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 <zcbenz@gmail.com>
This commit is contained in:
Brandon Fowler 2024-01-04 23:00:27 -05:00 committed by GitHub
parent 8c71e2adc9
commit 5086071294
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 106 additions and 3 deletions

View file

@ -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.

View file

@ -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<SkColor> 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);

View file

@ -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.

15
spec/fixtures/pages/flex-webview.html vendored Normal file
View file

@ -0,0 +1,15 @@
<style>
html,
body {
height: 100%;
}
body {
display: flex;
margin: 0;
}
webview {
flex: 1;
}
</style>

View file

@ -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('<webview> 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 () => {