Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: webview should maximize on requestFullscreen #29989

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions patches/chromium/.patches
Expand Up @@ -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
Expand Down
35 changes: 35 additions & 0 deletions patches/chromium/webview_fullscreen.patch
@@ -0,0 +1,35 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Cheng Zhao <zcbenz@gmail.com>
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));
38 changes: 35 additions & 3 deletions shell/browser/api/electron_api_web_contents.cc
Expand Up @@ -99,6 +99,7 @@
#include "shell/browser/web_contents_zoom_controller.h"
#include "shell/browser/web_dialog_helper.h"
#include "shell/browser/web_view_guest_delegate.h"
#include "shell/browser/web_view_manager.h"
#include "shell/common/api/electron_api_native_image.h"
#include "shell/common/color_util.h"
#include "shell/common/electron_constants.h"
Expand Down Expand Up @@ -3552,13 +3553,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;
}

Expand All @@ -3573,10 +3574,41 @@ 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;

html_fullscreen_ = fullscreen;

// Notify renderer of the html fullscreen change.
web_contents()
->GetRenderViewHost()
->GetWidget()
->SynchronizeVisualProperties();

// The embedder WebContents is spearated from the frame tree of webview, so
// we must manually sync their fullscreen states.
if (embedder_)
embedder_->SetHtmlApiFullscreen(fullscreen);

// Make sure all child webviews quit html fullscreen.
if (!fullscreen && !IsGuest()) {
auto* manager = WebViewManager::GetWebViewManager(web_contents());
manager->ForEachGuest(
web_contents(), base::BindRepeating([](content::WebContents* guest) {
WebContents* api_web_contents = WebContents::From(guest);
// Use UpdateHtmlApiFullscreen instead of SetXXX becuase there is no
// need to interact with the owner window.
api_web_contents->UpdateHtmlApiFullscreen(false);
return false;
}));
}
}

// static
v8::Local<v8::ObjectTemplate> WebContents::FillObjectTemplate(
v8::Isolate* isolate,
Expand Down
2 changes: 2 additions & 0 deletions shell/browser/api/electron_api_web_contents.h
Expand Up @@ -685,6 +685,8 @@ class WebContents : public gin::Wrappable<WebContents>,

// 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<v8::Value> session_;
v8::Global<v8::Value> devtools_web_contents_;
Expand Down
1 change: 0 additions & 1 deletion shell/browser/web_view_manager.h
Expand Up @@ -24,7 +24,6 @@ class WebViewManager : public content::BrowserPluginGuestManager {

static WebViewManager* GetWebViewManager(content::WebContents* web_contents);

protected:
// content::BrowserPluginGuestManager:
bool ForEachGuest(content::WebContents* embedder,
const GuestCallback& callback) override;
Expand Down
12 changes: 12 additions & 0 deletions spec-main/fixtures/webview/fullscreen/frame.html
@@ -0,0 +1,12 @@
<body>
<div id="div">
WebView
</div>
<script type="text/javascript" charset="utf-8">
const {ipcRenderer} = require('electron')
ipcRenderer.send('webview-ready')
document.addEventListener('fullscreenchange', () => {
ipcRenderer.send('webview-fullscreenchange')
})
</script>
</body>
12 changes: 12 additions & 0 deletions spec-main/fixtures/webview/fullscreen/main.html
@@ -0,0 +1,12 @@
<body>
<webview id="webview" nodeintegration="on" webpreferences="contextIsolation=no" src="frame.html"/>
<script type="text/javascript" charset="utf-8">
document.addEventListener('fullscreenchange', () => {
require('electron').ipcRenderer.send('fullscreenchange')
})

function isIframeFullscreen() {
return document.getElementById('webview').shadowRoot.lastElementChild.matches(':fullscreen')
}
</script>
</body>
30 changes: 30 additions & 0 deletions spec-main/webview-spec.ts
Expand Up @@ -378,6 +378,36 @@ describe('<webview> 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();
});
});

describe('nativeWindowOpen option', () => {
let w: BrowserWindow;
beforeEach(async () => {
Expand Down