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.It also handles a
DCHECK preventing guest webcontents from becoming the focused webContents.

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 048321f3142ad1c6252a1d72735d3ee5dd1a1591..a4fd7f1370aeb113a0b07ba0e4504adf7cc1848c 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -7702,6 +7702,17 @@ void RenderFrameHostImpl::EnterFullscreen(
     }
   }
 
+  // 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);
+    if (outer_proxy->is_render_frame_proxy_live()) {
+      outer_proxy->GetAssociatedRemoteFrame()->WillEnterFullscreen(
+          options.Clone());
+    }
+  }
+
   // Focus the window if another frame may have delegated the capability.
   if (had_fullscreen_token && !GetView()->HasFocus())
     GetView()->Focus();
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index bfef0d4ad7d543bd0f7cc5b0e57d067c1d8fc944..f8ca743c5629c8e62a16cad3e2f7f76e17826751 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -3864,21 +3864,25 @@ KeyboardEventProcessingResult WebContentsImpl::PreHandleKeyboardEvent(
     const input::NativeWebKeyboardEvent& event) {
   OPTIONAL_TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("content.verbose"),
                         "WebContentsImpl::PreHandleKeyboardEvent");
-  auto* outermost_contents = GetOutermostWebContents();
-  // TODO(wjmaclean): Generalize this to forward all key events to the outermost
-  // delegate's handler.
-  if (outermost_contents != this && IsFullscreen() &&
-      event.windows_key_code == ui::VKEY_ESCAPE) {
-    // When an inner WebContents has focus and is fullscreen, redirect <esc>
-    // key events to the outermost WebContents so it can be handled by that
-    // WebContents' delegate.
-    if (outermost_contents->PreHandleKeyboardEvent(event) ==
-        KeyboardEventProcessingResult::HANDLED) {
+
+  auto handled = delegate_ ? delegate_->PreHandleKeyboardEvent(this, event)
+                   : KeyboardEventProcessingResult::NOT_HANDLED;
+
+  if (IsFullscreen() && event.windows_key_code == ui::VKEY_ESCAPE) {
+    if (handled == KeyboardEventProcessingResult::HANDLED)
       return KeyboardEventProcessingResult::HANDLED;
+
+    // When an inner WebContents has focus and is fullscreen, traverse through
+    // containing webcontents to any that may handle the escape key.
+    while (auto* outer_web_contents = GetOuterWebContents()) {
+      auto result = outer_web_contents->PreHandleKeyboardEvent(event);
+      if (result == KeyboardEventProcessingResult::HANDLED) {
+        return KeyboardEventProcessingResult::HANDLED;
+      }
     }
   }
-  return delegate_ ? delegate_->PreHandleKeyboardEvent(this, event)
-                   : KeyboardEventProcessingResult::NOT_HANDLED;
+
+  return handled;
 }
 
 bool WebContentsImpl::HandleMouseEvent(const blink::WebMouseEvent& event) {
@@ -4035,7 +4039,7 @@ void WebContentsImpl::EnterFullscreenMode(
   OPTIONAL_TRACE_EVENT0("content", "WebContentsImpl::EnterFullscreenMode");
   DCHECK(CanEnterFullscreenMode(requesting_frame));
   DCHECK(requesting_frame->IsActive());
-  DCHECK(ContainsOrIsFocusedWebContents());
+  DCHECK(ContainsOrIsFocusedWebContents() || IsGuest());
   if (base::FeatureList::IsEnabled(
           features::kAutomaticFullscreenContentSetting)) {
     // Ensure the window is made active to take input focus. The user may have
diff --git a/third_party/blink/renderer/core/fullscreen/fullscreen.cc b/third_party/blink/renderer/core/fullscreen/fullscreen.cc
index 7c69e3d4fc1a115f0d2b0d318566653279fa2741..f6a7ab2e25bf3c8345c82648efaa80983b425c27 100644
--- a/third_party/blink/renderer/core/fullscreen/fullscreen.cc
+++ b/third_party/blink/renderer/core/fullscreen/fullscreen.cc
@@ -112,7 +112,7 @@ void FullscreenElementChanged(Document& document,
     // is the iframe element for the out-of-process frame that contains the
     // fullscreen element. Hence, it must match :-webkit-full-screen-ancestor.
     if (new_request_type & FullscreenRequestType::kForCrossProcessDescendant) {
-      DCHECK(IsA<HTMLIFrameElement>(new_element));
+      // DCHECK(IsA<HTMLIFrameElement>(new_element));
       new_element->SetContainsFullScreenElement(true);
     }
     new_element->SetContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(
