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

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

fix: offscreen mode under new window creation

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
This commit is contained in:
Calvin 2025-08-10 13:55:40 -06:00 committed by GitHub
commit bc56c6987f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 133 additions and 19 deletions

View file

@ -21,10 +21,21 @@ index 378e3eb2f8b9d4daaf39ef213dec88d86cf90a5c..0ace2e0c7073ee97ebb274db4b184a07
&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 5c319d0b7c81cb7335170a23e2863750ae9c6aa0..336bc9c329aed83293e6a802c8504a73cdf15b20 100644 index 5c319d0b7c81cb7335170a23e2863750ae9c6aa0..ec2989ca2c736140c9be6b78591798ac733e752e 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
@@ -5336,6 +5336,12 @@ FrameTree* WebContentsImpl::CreateNewWindow( @@ -5281,6 +5281,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,
@@ -5336,6 +5340,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 5c319d0b7c81cb7335170a23e2863750ae9c6aa0..336bc9c329aed83293e6a802c8504a73
// 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
@@ -5377,12 +5383,6 @@ FrameTree* WebContentsImpl::CreateNewWindow( @@ -5377,12 +5387,6 @@ FrameTree* WebContentsImpl::CreateNewWindow(
AddWebContentsDestructionObserver(new_contents_impl); AddWebContentsDestructionObserver(new_contents_impl);
} }
@ -122,7 +133,7 @@ index ca92e2ddf78d8f386b5ab23a09876d3b44e21334..33be50ce93dd998df5244f9ade391943
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 a4b8c5f950549e018c0d09522ff8890a1a774966..e364ae0f9bfa6321f3a3be598b36eb07fb5bca7a 100644 index a4b8c5f950549e018c0d09522ff8890a1a774966..6c4c6265c26f5304b8f77d7fc5a4fad5dffc831d 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,15 @@ index a4b8c5f950549e018c0d09522ff8890a1a774966..e364ae0f9bfa6321f3a3be598b36eb07
#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"
@@ -384,6 +385,13 @@ class CONTENT_EXPORT WebContentsDelegate { @@ -29,6 +30,7 @@
#include "content/public/browser/select_audio_output_request.h"
#include "content/public/browser/serial_chooser.h"
#include "content/public/browser/storage_partition_config.h"
+#include "content/public/browser/web_contents.h"
#include "content/public/common/window_container_type.mojom-forward.h"
#include "third_party/blink/public/common/input/web_mouse_event.h"
#include "third_party/blink/public/common/mediastream/media_stream_request.h"
@@ -384,6 +386,16 @@ class CONTENT_EXPORT WebContentsDelegate {
const StoragePartitionConfig& partition_config, const StoragePartitionConfig& partition_config,
SessionStorageNamespace* session_storage_namespace); SessionStorageNamespace* session_storage_namespace);
@ -143,6 +162,9 @@ index a4b8c5f950549e018c0d09522ff8890a1a774966..e364ae0f9bfa6321f3a3be598b36eb07
+ 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 cc7042ef657b5b248869c9fe560fbece07e1d7d6..b286ce081a19a3ca02cfffaa8ac32e407bbff02a 100644 index 65d5726b06a24d6ffca55413fd767cb46c87a137..3f68fa06dd2864e047d12f0c59a3c649494f0bf9 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
@@ -5252,7 +5252,7 @@ FrameTree* WebContentsImpl::CreateNewWindow( @@ -5252,7 +5252,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 c093f8b4ba872969a88923f2f96597bb8ffc8817..c1c1b4974339e1f08845815ef2d87b213e8597d9 100644 index e1f9c3070f0de3ee2d54791bd18fcdf35de6b7b5..2004582d7041b94a3232c3c160b2bfd9351f6658 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
@@ -5215,8 +5215,7 @@ FrameTree* WebContentsImpl::CreateNewWindow( @@ -5215,8 +5215,7 @@ FrameTree* WebContentsImpl::CreateNewWindow(
@ -250,10 +250,10 @@ index 33be50ce93dd998df5244f9ade391943f06978ad..3bb9baf76d331351d23d59fc2b9eb82d
} }
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 e364ae0f9bfa6321f3a3be598b36eb07fb5bca7a..c75fdf6bd7cb6b4d6bcfbb23da952adce4dd90ac 100644 index 6c4c6265c26f5304b8f77d7fc5a4fad5dffc831d..d0842904102fee982bc8502478a0a9067bb77904 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
@@ -363,8 +363,7 @@ class CONTENT_EXPORT WebContentsDelegate { @@ -364,8 +364,7 @@ class CONTENT_EXPORT WebContentsDelegate {
SiteInstance* source_site_instance, SiteInstance* source_site_instance,
mojom::WindowContainerType window_container_type, mojom::WindowContainerType window_container_type,
const GURL& opener_url, const GURL& opener_url,

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 7aa1e2d3b0cbc8055cd60987ad3cbc96e09c25f4..cc7042ef657b5b248869c9fe560fbece07e1d7d6 100644 index f49eefa91c94e85091879c0e521a5e27d1165d74..65d5726b06a24d6ffca55413fd767cb46c87a137 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
@@ -10068,7 +10068,7 @@ void WebContentsImpl::OnFocusedElementChangedInFrame( @@ -10072,7 +10072,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 0c5aa1c0e4c344f807cf0fcb7cc3cf532c1eaf23..ecbfaf2e7fd842a6f55002975e5bb4c4
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 336bc9c329aed83293e6a802c8504a73cdf15b20..c093f8b4ba872969a88923f2f96597bb8ffc8817 100644 index ec2989ca2c736140c9be6b78591798ac733e752e..e1f9c3070f0de3ee2d54791bd18fcdf35de6b7b5 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
@@ -6089,6 +6089,11 @@ TextInputManager* WebContentsImpl::GetTextInputManager() { @@ -6093,6 +6093,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 b286ce081a19a3ca02cfffaa8ac32e407bbff02a..e17f75be3878b9ba7a7a33babc88fcc105ddc267 100644 index 3f68fa06dd2864e047d12f0c59a3c649494f0bf9..4cd0c02dda57e1d718dbac953c937b492d06150c 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
@@ -10205,25 +10205,13 @@ void WebContentsImpl::RendererUnresponsive( @@ -10209,25 +10209,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 c1c1b4974339e1f08845815ef2d87b213e8597d9..4b309a0af2a747e46a9231eb35c00a825f4a0712 100644 index 2004582d7041b94a3232c3c160b2bfd9351f6658..2a70e261f9ec17be3d666a06b7dad3abae093aaf 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
@@ -4133,6 +4133,13 @@ void WebContentsImpl::Init(const WebContents::CreateParams& params, @@ -4133,6 +4133,13 @@ void WebContentsImpl::Init(const WebContents::CreateParams& params,

View file

@ -37,7 +37,7 @@ index 0ace2e0c7073ee97ebb274db4b184a074f6ae544..ee3015214724be708ca15d480d105bbf
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 4b309a0af2a747e46a9231eb35c00a825f4a0712..7aa1e2d3b0cbc8055cd60987ad3cbc96e09c25f4 100644 index 2a70e261f9ec17be3d666a06b7dad3abae093aaf..f49eefa91c94e85091879c0e521a5e27d1165d74 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
@@ -4422,21 +4422,25 @@ KeyboardEventProcessingResult WebContentsImpl::PreHandleKeyboardEvent( @@ -4422,21 +4422,25 @@ KeyboardEventProcessingResult WebContentsImpl::PreHandleKeyboardEvent(

View file

@ -1211,6 +1211,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,
@ -1224,9 +1249,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

@ -510,6 +510,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');