diff --git a/docs/api/webview-tag.md b/docs/api/webview-tag.md index d34026e97fa..eecd2e86f90 100644 --- a/docs/api/webview-tag.md +++ b/docs/api/webview-tag.md @@ -221,7 +221,9 @@ windows. Popups are disabled by default. ``` A `string` which is a comma separated list of strings which specifies the web preferences to be set on the webview. -The full list of supported preference strings can be found in [BrowserWindow](browser-window.md#new-browserwindowoptions). +The full list of supported preference strings can be found in [BrowserWindow](browser-window.md#new-browserwindowoptions). In addition, webview supports the following preferences: + +* `transparent` boolean (optional) - Whether to enable background transparency for the guest page. Default is `true`. **Note:** The guest page's text and background colors are derived from the [color scheme](https://developer.mozilla.org/en-US/docs/Web/CSS/color-scheme) of its root element. When transparency is enabled, the text color will still change accordingly but the background will remain transparent. The string follows the same format as the features string in `window.open`. A name by itself is given a `true` boolean value. diff --git a/shell/browser/api/electron_api_web_contents.cc b/shell/browser/api/electron_api_web_contents.cc index 29cfc8537bc..c24d3288831 100644 --- a/shell/browser/api/electron_api_web_contents.cc +++ b/shell/browser/api/electron_api_web_contents.cc @@ -822,6 +822,9 @@ WebContents::WebContents(v8::Isolate* isolate, // Get type options.Get("type", &type_); + // Get transparent for guest view + options.Get("transparent", &guest_transparent_); + bool b = false; if (options.Get(options::kOffscreen, &b) && b) type_ = Type::kOffScreen; @@ -3778,7 +3781,7 @@ void WebContents::SetImageAnimationPolicy(const std::string& new_policy) { } void WebContents::SetBackgroundColor(absl::optional maybe_color) { - SkColor color = maybe_color.value_or(type_ == Type::kWebView || + SkColor color = maybe_color.value_or((IsGuest() && guest_transparent_) || type_ == Type::kBrowserView ? SK_ColorTRANSPARENT : SK_ColorWHITE); diff --git a/shell/browser/api/electron_api_web_contents.h b/shell/browser/api/electron_api_web_contents.h index 49c1a1f81ad..00c3ad8648c 100644 --- a/shell/browser/api/electron_api_web_contents.h +++ b/shell/browser/api/electron_api_web_contents.h @@ -801,6 +801,9 @@ class WebContents : public ExclusiveAccessContext, // The type of current WebContents. Type type_ = Type::kBrowserWindow; + // Weather the guest view should be transparent + bool guest_transparent_ = true; + int32_t id_; // Request id used for findInPage request. diff --git a/spec/fixtures/pages/flex-webview.html b/spec/fixtures/pages/flex-webview.html new file mode 100644 index 00000000000..7d5369946c6 --- /dev/null +++ b/spec/fixtures/pages/flex-webview.html @@ -0,0 +1,15 @@ + diff --git a/spec/webview-spec.ts b/spec/webview-spec.ts index 57770e797ca..3e4dca739f2 100644 --- a/spec/webview-spec.ts +++ b/spec/webview-spec.ts @@ -1,6 +1,6 @@ import * as path from 'node:path'; import * as url from 'node:url'; -import { BrowserWindow, session, ipcMain, app, WebContents } from 'electron/main'; +import { BrowserWindow, session, ipcMain, app, WebContents, screen } from 'electron/main'; import { closeAllWindows } from './lib/window-helpers'; import { emittedUntil } from './lib/events-helpers'; import { ifit, ifdescribe, defer, itremote, useRemoteContext, listen } from './lib/spec-helpers'; @@ -9,6 +9,7 @@ import * as http from 'node:http'; import * as auth from 'basic-auth'; import { once } from 'node:events'; import { setTimeout } from 'node:timers/promises'; +import { areColorsSimilar, captureScreen, HexColors, getPixelColor } from './lib/screen-helpers'; declare let WebView: any; const features = process._linkedBinding('electron_common_features'); @@ -773,6 +774,85 @@ describe(' tag', function () { }); }); + describe('webpreferences attribute', () => { + const WINDOW_BACKGROUND_COLOR = '#55ccbb'; + + let w: BrowserWindow; + before(async () => { + w = new BrowserWindow({ + webPreferences: { + webviewTag: true, + nodeIntegration: true, + contextIsolation: false + } + }); + await w.loadURL(`file://${fixtures}/pages/flex-webview.html`); + w.setBackgroundColor(WINDOW_BACKGROUND_COLOR); + }); + afterEach(async () => { + await w.webContents.executeJavaScript(`{ + for (const el of document.querySelectorAll('webview')) el.remove(); + }`); + }); + after(() => w.close()); + + // Linux and arm64 platforms (WOA and macOS) do not return any capture sources + ifit(process.platform === 'darwin' && process.arch === 'x64')('is transparent by default', async () => { + await loadWebView(w.webContents, { + src: 'data:text/html,foo' + }); + + await setTimeout(1000); + + const display = screen.getPrimaryDisplay(); + const screenCapture = await captureScreen(); + const centerColor = getPixelColor(screenCapture, { + x: display.size.width / 2, + y: display.size.height / 2 + }); + + expect(areColorsSimilar(centerColor, WINDOW_BACKGROUND_COLOR)).to.be.true(); + }); + + // Linux and arm64 platforms (WOA and macOS) do not return any capture sources + ifit(process.platform === 'darwin' && process.arch === 'x64')('remains transparent when set', async () => { + await loadWebView(w.webContents, { + src: 'data:text/html,foo', + webpreferences: 'transparent=yes' + }); + + await setTimeout(1000); + + const display = screen.getPrimaryDisplay(); + const screenCapture = await captureScreen(); + const centerColor = getPixelColor(screenCapture, { + x: display.size.width / 2, + y: display.size.height / 2 + }); + + expect(areColorsSimilar(centerColor, WINDOW_BACKGROUND_COLOR)).to.be.true(); + }); + + // Linux and arm64 platforms (WOA and macOS) do not return any capture sources + ifit(process.platform === 'darwin' && process.arch === 'x64')('can disable transparency', async () => { + await loadWebView(w.webContents, { + src: 'data:text/html,foo', + webpreferences: 'transparent=no' + }); + + await setTimeout(1000); + + const display = screen.getPrimaryDisplay(); + const screenCapture = await captureScreen(); + const centerColor = getPixelColor(screenCapture, { + x: display.size.width / 2, + y: display.size.height / 2 + }); + + expect(areColorsSimilar(centerColor, HexColors.WHITE)).to.be.true(); + }); + }); + describe('permission request handlers', () => { let w: BrowserWindow; beforeEach(async () => {