diff --git a/patches/chromium/.patches b/patches/chromium/.patches index 20d3efd486dd3..df67145245418 100644 --- a/patches/chromium/.patches +++ b/patches/chromium/.patches @@ -89,6 +89,7 @@ export_gin_v8platform_pageallocator_for_usage_outside_of_the_gin.patch fix_export_zlib_symbols.patch don_t_use_potentially_null_getwebframe_-_view_when_get_blink.patch web_contents.patch +webview_fullscreen.patch disable_unload_metrics.patch fix_add_check_for_sandbox_then_result.patch extend_apply_webpreferences.patch diff --git a/patches/chromium/webview_fullscreen.patch b/patches/chromium/webview_fullscreen.patch new file mode 100644 index 0000000000000..44483526b7d84 --- /dev/null +++ b/patches/chromium/webview_fullscreen.patch @@ -0,0 +1,35 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Cheng Zhao +Date: Thu, 4 Oct 2018 14:57:02 -0700 +Subject: fix: also propagate fullscreen state for outer frame + +When entering fullscreen with Element.requestFullscreen in child frames, +the parent frame should also enter fullscreen mode too. Chromium handles +this for iframes, but not for webviews as they are essentially main +frames instead of child frames. + +This patch makes webviews propagate the fullscreen state to embedder. + +Note that we also need to manually update embedder's +`api::WebContents::IsFullscreenForTabOrPending` value. + +diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc +index 5c16a9ee9bbf461c24456613ff709f4f608e3441..4e4d1fdf50e2d480e099c9af71a45fc864d2cf56 100644 +--- a/content/browser/renderer_host/render_frame_host_impl.cc ++++ b/content/browser/renderer_host/render_frame_host_impl.cc +@@ -5520,6 +5520,15 @@ void RenderFrameHostImpl::EnterFullscreen( + notified_instances.insert(parent_site_instance); + } + ++ // Entering fullscreen from webview should also notify its outer frame. ++ if (frame_tree_node()->render_manager()->IsMainFrameForInnerDelegate()) { ++ RenderFrameProxyHost* outer_proxy = ++ frame_tree_node()->render_manager()->GetProxyToOuterDelegate(); ++ DCHECK(outer_proxy); ++ outer_proxy->GetAssociatedRemoteFrame()->WillEnterFullscreen( ++ options.Clone()); ++ } ++ + delegate_->EnterFullscreenMode(this, *options); + delegate_->FullscreenStateChanged(this, true /* is_fullscreen */, + std::move(options)); diff --git a/shell/browser/api/electron_api_web_contents.cc b/shell/browser/api/electron_api_web_contents.cc index 566515804e9db..204b2237bfcf6 100644 --- a/shell/browser/api/electron_api_web_contents.cc +++ b/shell/browser/api/electron_api_web_contents.cc @@ -3562,13 +3562,13 @@ void WebContents::SetHtmlApiFullscreen(bool enter_fullscreen) { // Window is already in fullscreen mode, save the state. if (enter_fullscreen && owner_window_->IsFullscreen()) { native_fullscreen_ = true; - html_fullscreen_ = true; + UpdateHtmlApiFullscreen(true); return; } // Exit html fullscreen state but not window's fullscreen mode. if (!enter_fullscreen && native_fullscreen_) { - html_fullscreen_ = false; + UpdateHtmlApiFullscreen(false); return; } @@ -3583,10 +3583,47 @@ void WebContents::SetHtmlApiFullscreen(bool enter_fullscreen) { owner_window_->SetFullScreen(enter_fullscreen); } - html_fullscreen_ = enter_fullscreen; + UpdateHtmlApiFullscreen(enter_fullscreen); native_fullscreen_ = false; } +void WebContents::UpdateHtmlApiFullscreen(bool fullscreen) { + if (fullscreen == html_fullscreen_) + return; + + // The embedder WebContents is spearated from the frame tree of webview, so + // we must manually sync their fullscreen states. + if (embedder_) { + if (fullscreen) { + embedder_->fullscreen_webviews_.insert(this); + embedder_->SetHtmlApiFullscreen(true); + } else if (!fullscreen && + // do not update embedder if this update is initiated by it. + !embedder_->fullscreen_webviews_.empty()) { + embedder_->fullscreen_webviews_.erase(this); + embedder_->SetHtmlApiFullscreen(false); + } + } + + // Make sure all child webviews quit html fullscreen. + if (!fullscreen) { + // Clear the original container before iteration, so the iterated webviews + // won't call back to enter infinite recursion. + auto copied = fullscreen_webviews_; + fullscreen_webviews_.clear(); + for (WebContents* webview : copied) + webview->SetHtmlApiFullscreen(false); + } + + html_fullscreen_ = fullscreen; + + // Notify renderer of the html fullscreen change. + web_contents() + ->GetRenderViewHost() + ->GetWidget() + ->SynchronizeVisualProperties(); +} + // static v8::Local WebContents::FillObjectTemplate( v8::Isolate* isolate, diff --git a/shell/browser/api/electron_api_web_contents.h b/shell/browser/api/electron_api_web_contents.h index 59761657d6088..d741682369ae4 100644 --- a/shell/browser/api/electron_api_web_contents.h +++ b/shell/browser/api/electron_api_web_contents.h @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -686,6 +687,8 @@ class WebContents : public gin::Wrappable, // Set fullscreen mode triggered by html api. void SetHtmlApiFullscreen(bool enter_fullscreen); + // Update the html fullscreen flag in both browser and renderer. + void UpdateHtmlApiFullscreen(bool fullscreen); v8::Global session_; v8::Global devtools_web_contents_; @@ -771,6 +774,8 @@ class WebContents : public gin::Wrappable, // Stores the frame thats currently in fullscreen, nullptr if there is none. content::RenderFrameHost* fullscreen_frame_ = nullptr; + // The webviews that entered HTML5 fullscreen. + std::set fullscreen_webviews_; service_manager::BinderRegistryWithArgs registry_; diff --git a/spec-main/fixtures/webview/fullscreen/frame.html b/spec-main/fixtures/webview/fullscreen/frame.html new file mode 100644 index 0000000000000..c92571eef4a8b --- /dev/null +++ b/spec-main/fixtures/webview/fullscreen/frame.html @@ -0,0 +1,12 @@ + +
+ WebView +
+ + diff --git a/spec-main/fixtures/webview/fullscreen/main.html b/spec-main/fixtures/webview/fullscreen/main.html new file mode 100644 index 0000000000000..aeb460578f97d --- /dev/null +++ b/spec-main/fixtures/webview/fullscreen/main.html @@ -0,0 +1,12 @@ + + + + diff --git a/spec-main/webview-spec.ts b/spec-main/webview-spec.ts index d89adb141df4a..321c9226104f1 100644 --- a/spec-main/webview-spec.ts +++ b/spec-main/webview-spec.ts @@ -378,6 +378,51 @@ describe(' tag', function () { }); }); + describe('requestFullscreen from webview', () => { + const loadWebViewWindow = async () => { + const w = new BrowserWindow({ + webPreferences: { + webviewTag: true, + nodeIntegration: true, + contextIsolation: false + } + }); + const attachPromise = emittedOnce(w.webContents, 'did-attach-webview'); + const readyPromise = emittedOnce(ipcMain, 'webview-ready'); + w.loadFile(path.join(__dirname, 'fixtures', 'webview', 'fullscreen', 'main.html')); + const [, webview] = await attachPromise; + await readyPromise; + return [w, webview]; + }; + + afterEach(closeAllWindows); + + it('should make parent frame element fullscreen too', async () => { + const [w, webview] = await loadWebViewWindow(); + expect(await w.webContents.executeJavaScript('isIframeFullscreen()')).to.be.false(); + + const parentFullscreen = emittedOnce(ipcMain, 'fullscreenchange'); + await webview.executeJavaScript('document.getElementById("div").requestFullscreen()', true); + await parentFullscreen; + expect(await w.webContents.executeJavaScript('isIframeFullscreen()')).to.be.true(); + }); + + it('can be exited from parent frame', async () => { + const [w, webview] = await loadWebViewWindow(); + expect(await webview.executeJavaScript('document.getElementById("div").matches(":fullscreen")')).to.be.false(); + + const parentFullscreen = emittedOnce(ipcMain, 'fullscreenchange'); + await webview.executeJavaScript('document.getElementById("div").requestFullscreen()', true); + await parentFullscreen; + expect(await webview.executeJavaScript('document.getElementById("div").matches(":fullscreen")')).to.be.true(); + + const webviewFullscreen = emittedOnce(ipcMain, 'webview-fullscreenchange'); + await w.webContents.executeJavaScript('document.exitFullscreen()', true); + await webviewFullscreen; + expect(await webview.executeJavaScript('document.getElementById("div").matches(":fullscreen")')).to.be.false(); + }); + }); + describe('nativeWindowOpen option', () => { let w: BrowserWindow; beforeEach(async () => {