fix: offscreen mode under window.open creation (#48070)

fix: offscreen mode under window.open creation
This commit is contained in:
Shelley Vohr 2025-08-18 22:17:22 +02:00 committed by GitHub
commit beee3737a9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 124 additions and 18 deletions

View file

@ -21,10 +21,21 @@ index 23cd457563d7d534e924428ac6da2b475e579326..d8698f9f37eefa50bf4e29a164b2cc30
&no_javascript_access); &no_javascript_access);
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index f3204a39253652a906f8976c666395e8afa033a8..a45bf004f5096809b5fc7b70faa0b7fa7b257049 100644 index f3204a39253652a906f8976c666395e8afa033a8..d9b5a70e0c7acfaac312663ba336580eb062fae4 100644
--- a/content/browser/web_contents/web_contents_impl.cc --- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc
@@ -5293,6 +5293,12 @@ FrameTree* WebContentsImpl::CreateNewWindow( @@ -5238,6 +5238,10 @@ FrameTree* WebContentsImpl::CreateNewWindow(
create_params.initially_hidden = renderer_started_hidden;
create_params.initial_popup_url = params.target_url;
+ // Potentially allow the delegate to override the create_params.
+ if (delegate_)
+ delegate_->MaybeOverrideCreateParamsForNewWindow(&create_params);
+
// Even though all codepaths leading here are in response to a renderer
// trying to open a new window, if the new window ends up in a different
// browsing instance, then the RenderViewHost, RenderWidgetHost,
@@ -5293,6 +5297,12 @@ FrameTree* WebContentsImpl::CreateNewWindow(
// Sets the newly created WebContents WindowOpenDisposition. // Sets the newly created WebContents WindowOpenDisposition.
new_contents_impl->original_window_open_disposition_ = params.disposition; new_contents_impl->original_window_open_disposition_ = params.disposition;
@ -37,7 +48,7 @@ index f3204a39253652a906f8976c666395e8afa033a8..a45bf004f5096809b5fc7b70faa0b7fa
// If the new frame has a name, make sure any SiteInstances that can find // If the new frame has a name, make sure any SiteInstances that can find
// this named frame have proxies for it. Must be called after // this named frame have proxies for it. Must be called after
// SetSessionStorageNamespace, since this calls CreateRenderView, which uses // SetSessionStorageNamespace, since this calls CreateRenderView, which uses
@@ -5334,12 +5340,6 @@ FrameTree* WebContentsImpl::CreateNewWindow( @@ -5334,12 +5344,6 @@ FrameTree* WebContentsImpl::CreateNewWindow(
AddWebContentsDestructionObserver(new_contents_impl); AddWebContentsDestructionObserver(new_contents_impl);
} }
@ -122,7 +133,7 @@ index f42be2a1cc5ba3ccb52e48985e0532a34675e826..f6ab6ab2b036c7621b429181c3ff89d9
WebContents* source, WebContents* source,
const OpenURLParams& params, const OpenURLParams& params,
diff --git a/content/public/browser/web_contents_delegate.h b/content/public/browser/web_contents_delegate.h diff --git a/content/public/browser/web_contents_delegate.h b/content/public/browser/web_contents_delegate.h
index d33274984bf6523beeb3ab5ee586499d224bff3c..83bdd195339eb7d61ac88e2994fd8dabe93f6ecc 100644 index d33274984bf6523beeb3ab5ee586499d224bff3c..89c3f86bc828c5bf844bb103477c0772ba275dc5 100644
--- a/content/public/browser/web_contents_delegate.h --- a/content/public/browser/web_contents_delegate.h
+++ b/content/public/browser/web_contents_delegate.h +++ b/content/public/browser/web_contents_delegate.h
@@ -18,6 +18,7 @@ @@ -18,6 +18,7 @@
@ -133,7 +144,7 @@ index d33274984bf6523beeb3ab5ee586499d224bff3c..83bdd195339eb7d61ac88e2994fd8dab
#include "content/public/browser/eye_dropper.h" #include "content/public/browser/eye_dropper.h"
#include "content/public/browser/fullscreen_types.h" #include "content/public/browser/fullscreen_types.h"
#include "content/public/browser/invalidate_type.h" #include "content/public/browser/invalidate_type.h"
@@ -380,6 +381,13 @@ class CONTENT_EXPORT WebContentsDelegate { @@ -380,6 +381,16 @@ class CONTENT_EXPORT WebContentsDelegate {
const StoragePartitionConfig& partition_config, const StoragePartitionConfig& partition_config,
SessionStorageNamespace* session_storage_namespace); SessionStorageNamespace* session_storage_namespace);
@ -143,6 +154,9 @@ index d33274984bf6523beeb3ab5ee586499d224bff3c..83bdd195339eb7d61ac88e2994fd8dab
+ int opener_render_frame_id, + int opener_render_frame_id,
+ const mojom::CreateNewWindowParams& params, + const mojom::CreateNewWindowParams& params,
+ WebContents* new_contents); + WebContents* new_contents);
+
+ virtual void MaybeOverrideCreateParamsForNewWindow(
+ content::WebContents::CreateParams* create_params) {}
+ +
// Notifies the delegate about the creation of a new WebContents. This // Notifies the delegate about the creation of a new WebContents. This
// typically happens when popups are created. // typically happens when popups are created.

View file

@ -14,7 +14,7 @@ track down the source of this problem & figure out if we can fix it
by changing something in Electron. by changing something in Electron.
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 0fba6e8b5f1f9ecde06b9d846b4ace984cdfc943..b50e3c2ecb6f9f3322cfd16fc7bcbd8935f863a2 100644 index c2ce5a360607a304d8435d6e58a4f0895dd08056..71d85d2e3111457eca540e80bf8a68ebc548d440 100644
--- a/content/browser/web_contents/web_contents_impl.cc --- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc
@@ -5209,7 +5209,7 @@ FrameTree* WebContentsImpl::CreateNewWindow( @@ -5209,7 +5209,7 @@ FrameTree* WebContentsImpl::CreateNewWindow(

View file

@ -222,7 +222,7 @@ index b969f1d97b7e3396119b579cfbe61e19ff7d2dd4..b8d6169652da28266a514938b45b39c5
content::WebContents* AddNewContents( content::WebContents* AddNewContents(
content::WebContents* source, content::WebContents* source,
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index c034b546289ba069194ad65d3d3bc0703a3afe9c..e603c0fddbf4efaeb225686c1791ffb581e9e6c0 100644 index d57f205ea7dd4737126ddd7f2284d72eddae9e8f..bf6a239f566ec39efdacce5b74014d2995ba868e 100644
--- a/content/browser/web_contents/web_contents_impl.cc --- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc
@@ -5172,8 +5172,7 @@ FrameTree* WebContentsImpl::CreateNewWindow( @@ -5172,8 +5172,7 @@ FrameTree* WebContentsImpl::CreateNewWindow(
@ -250,7 +250,7 @@ index f6ab6ab2b036c7621b429181c3ff89d9f1ff77f9..d151ba757ae81c6f023ee08328ab1554
} }
diff --git a/content/public/browser/web_contents_delegate.h b/content/public/browser/web_contents_delegate.h diff --git a/content/public/browser/web_contents_delegate.h b/content/public/browser/web_contents_delegate.h
index 83bdd195339eb7d61ac88e2994fd8dabe93f6ecc..682e5eecb7ce514094f76253447aa7ac4b6f29b1 100644 index 89c3f86bc828c5bf844bb103477c0772ba275dc5..c3eef892eef2c42c46926be223fe950823e6943d 100644
--- a/content/public/browser/web_contents_delegate.h --- a/content/public/browser/web_contents_delegate.h
+++ b/content/public/browser/web_contents_delegate.h +++ b/content/public/browser/web_contents_delegate.h
@@ -359,8 +359,7 @@ class CONTENT_EXPORT WebContentsDelegate { @@ -359,8 +359,7 @@ class CONTENT_EXPORT WebContentsDelegate {

View file

@ -87,10 +87,10 @@ index 75df43e3cd2721a92c90c18154d53d5c203e2465..ce42c75c8face36d21f53f44c0201ac4
// The view with active text input state, i.e., a focused <input> element. // The view with active text input state, i.e., a focused <input> element.
// It will be nullptr if no such view exists. Note that the active view // It will be nullptr if no such view exists. Note that the active view
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 374d5f1a1685229865d0f1f1032f36bbcd54e92e..0fba6e8b5f1f9ecde06b9d846b4ace984cdfc943 100644 index 837913b6b82e473e8fa90209924b245d28e922e7..c2ce5a360607a304d8435d6e58a4f0895dd08056 100644
--- a/content/browser/web_contents/web_contents_impl.cc --- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc
@@ -10071,7 +10071,7 @@ void WebContentsImpl::OnFocusedElementChangedInFrame( @@ -10075,7 +10075,7 @@ void WebContentsImpl::OnFocusedElementChangedInFrame(
"WebContentsImpl::OnFocusedElementChangedInFrame", "WebContentsImpl::OnFocusedElementChangedInFrame",
"render_frame_host", frame); "render_frame_host", frame);
RenderWidgetHostViewBase* root_view = RenderWidgetHostViewBase* root_view =

View file

@ -44,10 +44,10 @@ index 624094ba6459f3663a12f868c4cb47dfa9f8dce1..eed851d277c5efa2d5ca594e18eaf7df
void RenderWidgetHostImpl::ShowContextMenuAtPoint( void RenderWidgetHostImpl::ShowContextMenuAtPoint(
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index a45bf004f5096809b5fc7b70faa0b7fa7b257049..c034b546289ba069194ad65d3d3bc0703a3afe9c 100644 index d9b5a70e0c7acfaac312663ba336580eb062fae4..d57f205ea7dd4737126ddd7f2284d72eddae9e8f 100644
--- a/content/browser/web_contents/web_contents_impl.cc --- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc
@@ -6045,6 +6045,11 @@ TextInputManager* WebContentsImpl::GetTextInputManager() { @@ -6049,6 +6049,11 @@ TextInputManager* WebContentsImpl::GetTextInputManager() {
return text_input_manager_.get(); return text_input_manager_.get();
} }

View file

@ -15,10 +15,10 @@ This CL removes these filters so the unresponsive event can still be
accessed from our JS event. The filtering is moved into Electron's code. accessed from our JS event. The filtering is moved into Electron's code.
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index b50e3c2ecb6f9f3322cfd16fc7bcbd8935f863a2..d7afac20523d2900cbefa5ab3ea9f0863780b704 100644 index 71d85d2e3111457eca540e80bf8a68ebc548d440..7be5466f9e81be22676d65bc04af6404c1da9f3b 100644
--- a/content/browser/web_contents/web_contents_impl.cc --- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc
@@ -10208,25 +10208,13 @@ void WebContentsImpl::RendererUnresponsive( @@ -10212,25 +10212,13 @@ void WebContentsImpl::RendererUnresponsive(
base::RepeatingClosure hang_monitor_restarter) { base::RepeatingClosure hang_monitor_restarter) {
OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::RendererUnresponsive", OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::RendererUnresponsive",
"render_widget_host", render_widget_host); "render_widget_host", render_widget_host);

View file

@ -9,7 +9,7 @@ is needed for OSR.
Originally landed in https://github.com/electron/libchromiumcontent/pull/226. Originally landed in https://github.com/electron/libchromiumcontent/pull/226.
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index e603c0fddbf4efaeb225686c1791ffb581e9e6c0..88d7b948d57f53fcd681856587f79ece991ee8fa 100644 index bf6a239f566ec39efdacce5b74014d2995ba868e..e233701dddea01dd727dcd02d096b331c8aeff97 100644
--- a/content/browser/web_contents/web_contents_impl.cc --- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc
@@ -4079,6 +4079,13 @@ void WebContentsImpl::Init(const WebContents::CreateParams& params, @@ -4079,6 +4079,13 @@ void WebContentsImpl::Init(const WebContents::CreateParams& params,

View file

@ -37,7 +37,7 @@ index d8698f9f37eefa50bf4e29a164b2cc302c32ecdf..3a8dc82b882aa00e9a5430bc8b7ba409
if (had_fullscreen_token && !GetView()->HasFocus()) if (had_fullscreen_token && !GetView()->HasFocus())
GetView()->Focus(); GetView()->Focus();
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 88d7b948d57f53fcd681856587f79ece991ee8fa..374d5f1a1685229865d0f1f1032f36bbcd54e92e 100644 index e233701dddea01dd727dcd02d096b331c8aeff97..837913b6b82e473e8fa90209924b245d28e922e7 100644
--- a/content/browser/web_contents/web_contents_impl.cc --- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc
@@ -4369,21 +4369,25 @@ KeyboardEventProcessingResult WebContentsImpl::PreHandleKeyboardEvent( @@ -4369,21 +4369,25 @@ KeyboardEventProcessingResult WebContentsImpl::PreHandleKeyboardEvent(

View file

@ -1209,6 +1209,31 @@ content::WebContents* WebContents::CreateCustomWebContents(
return nullptr; return nullptr;
} }
void WebContents::MaybeOverrideCreateParamsForNewWindow(
content::WebContents::CreateParams* create_params) {
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
v8::HandleScope handle_scope(isolate);
gin_helper::Dictionary dict;
gin::ConvertFromV8(isolate, pending_child_web_preferences_.Get(isolate),
&dict);
v8::Local<v8::Value> use_offscreen;
if (dict.Get(options::kOffscreen, &use_offscreen)) {
bool is_offscreen =
use_offscreen->IsObject() ||
(use_offscreen->IsBoolean() &&
dict.Get(options::kOffscreen, &is_offscreen) && is_offscreen);
if (is_offscreen) {
auto* view = new OffScreenWebContentsView(
false, offscreen_use_shared_texture_,
base::BindRepeating(&WebContents::OnPaint, base::Unretained(this)));
create_params->view = view;
create_params->delegate_view = view;
}
}
}
content::WebContents* WebContents::AddNewContents( content::WebContents* WebContents::AddNewContents(
content::WebContents* source, content::WebContents* source,
std::unique_ptr<content::WebContents> new_contents, std::unique_ptr<content::WebContents> new_contents,
@ -1222,9 +1247,13 @@ content::WebContents* WebContents::AddNewContents(
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate(); v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
Type type = Type::kBrowserWindow;
auto* web_preferences = WebContentsPreferences::From(new_contents.get());
if (web_preferences && web_preferences->IsOffscreen())
type = Type::kOffScreen;
v8::HandleScope handle_scope(isolate); v8::HandleScope handle_scope(isolate);
auto api_web_contents = auto api_web_contents = CreateAndTake(isolate, std::move(new_contents), type);
CreateAndTake(isolate, std::move(new_contents), Type::kBrowserWindow);
// We call RenderFrameCreated here as at this point the empty "about:blank" // We call RenderFrameCreated here as at this point the empty "about:blank"
// render frame has already been created. If the window never navigates again // render frame has already been created. If the window never navigates again

View file

@ -508,6 +508,8 @@ class WebContents final : public ExclusiveAccessContext,
int opener_render_frame_id, int opener_render_frame_id,
const content::mojom::CreateNewWindowParams& params, const content::mojom::CreateNewWindowParams& params,
content::WebContents* new_contents) override; content::WebContents* new_contents) override;
void MaybeOverrideCreateParamsForNewWindow(
content::WebContents::CreateParams* create_params) override;
content::WebContents* AddNewContents( content::WebContents* AddNewContents(
content::WebContents* source, content::WebContents* source,
std::unique_ptr<content::WebContents> new_contents, std::unique_ptr<content::WebContents> new_contents,

View file

@ -4,6 +4,7 @@ import { expect, assert } from 'chai';
import { once } from 'node:events'; import { once } from 'node:events';
import * as http from 'node:http'; import * as http from 'node:http';
import * as nodePath from 'node:path';
import { HexColors, ScreenCapture, hasCapturableScreen } from './lib/screen-helpers'; import { HexColors, ScreenCapture, hasCapturableScreen } from './lib/screen-helpers';
import { ifit, listen } from './lib/spec-helpers'; import { ifit, listen } from './lib/spec-helpers';
@ -203,6 +204,66 @@ describe('webContents.setWindowOpenHandler', () => {
expect(await browserWindow.webContents.executeJavaScript('42')).to.equal(42); expect(await browserWindow.webContents.executeJavaScript('42')).to.equal(42);
}); });
it('can open an offscreen child window from an onscreen parent', async () => {
browserWindow.webContents.setWindowOpenHandler(() => ({
action: 'allow',
overrideBrowserWindowOptions: {
webPreferences: {
offscreen: true
}
}
}));
const didCreateWindow = once(browserWindow.webContents, 'did-create-window');
const url = `file://${nodePath.join('fixtures', 'pages', 'content.html')}`;
browserWindow.webContents.executeJavaScript(`window.open('${JSON.stringify(url)}') && true`);
const [childWindow] = await didCreateWindow;
expect(childWindow.webContents.isOffscreen()).to.be.true('Child window should be offscreen');
});
it('can open an onscreen child window from an offscreen parent', async () => {
const obw = new BrowserWindow({
show: false,
webPreferences: {
offscreen: true
}
});
await obw.loadURL('about:blank');
obw.webContents.setWindowOpenHandler(() => ({ action: 'allow' }));
const didCreateWindow = once(obw.webContents, 'did-create-window');
const url = `file://${nodePath.join('fixtures', 'pages', 'content.html')}`;
obw.webContents.executeJavaScript(`window.open('${JSON.stringify(url)}') && true`);
const [childWindow] = await didCreateWindow;
expect(childWindow.webContents.isOffscreen()).to.be.false('Child window should not be offscreen');
});
it('can open an offscreen child window from an offscreen parent', async () => {
const obw = new BrowserWindow({
show: false,
webPreferences: {
offscreen: true
}
});
await obw.loadURL('about:blank');
obw.webContents.setWindowOpenHandler(() => ({
action: 'allow',
overrideBrowserWindowOptions: {
webPreferences: {
offscreen: true
}
}
}));
const didCreateWindow = once(obw.webContents, 'did-create-window');
const url = `file://${nodePath.join('fixtures', 'pages', 'content.html')}`;
obw.webContents.executeJavaScript(`window.open('${JSON.stringify(url)}') && true`);
const [childWindow] = await didCreateWindow;
expect(childWindow.webContents.isOffscreen()).to.be.true('Child window should be offscreen');
});
ifit(hasCapturableScreen())('should not make child window background transparent', async () => { ifit(hasCapturableScreen())('should not make child window background transparent', async () => {
browserWindow.webContents.setWindowOpenHandler(() => ({ action: 'allow' })); browserWindow.webContents.setWindowOpenHandler(() => ({ action: 'allow' }));
const didCreateWindow = once(browserWindow.webContents, 'did-create-window'); const didCreateWindow = once(browserWindow.webContents, 'did-create-window');