From 50860712943da0feabcb668b264c35b7837286c0 Mon Sep 17 00:00:00 2001 From: Brandon Fowler <42590338+BrandonXLF@users.noreply.github.com> Date: Thu, 4 Jan 2024 23:00:27 -0500 Subject: [PATCH] feat: add `transparent` webpreference to webview (#40301) * feat: add transparent option to WebContents * feat: add transparent attribute to webview * test: add tests for webview transparent attribute * docs: add transparent attribute to webview docs * fix: run tests on macOS only * refactor: remove unneeded html tag * fix: only apply transparent option to guests * refactor: correct comment * refactor: use opaque instead Retains current webview behaviour by default. * fix: correct variable name to guest_opaque_ * refactor: use transparent webpreference * docs: remove unused web preference * fix: uncomment condition for transparency test * docs: converted to list format and linked to MDN * fix: make webviews transparent by default again * fix: rebase error --------- Co-authored-by: Cheng Zhao --- docs/api/webview-tag.md | 4 +- .../browser/api/electron_api_web_contents.cc | 5 +- shell/browser/api/electron_api_web_contents.h | 3 + spec/fixtures/pages/flex-webview.html | 15 ++++ spec/webview-spec.ts | 82 ++++++++++++++++++- 5 files changed, 106 insertions(+), 3 deletions(-) create mode 100644 spec/fixtures/pages/flex-webview.html diff --git a/docs/api/webview-tag.md b/docs/api/webview-tag.md index d34026e97fae..eecd2e86f90f 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 29cfc8537bc3..c24d3288831f 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 49c1a1f81ada..00c3ad8648c7 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 000000000000..7d5369946c68 --- /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 57770e797ca7..3e4dca739f29 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 () => {