Reland "Display Cutout: Notify the browser process on fullscreen frame change"
This is a reland of 326c044a30ccc53226a008a26500785dfa0d6ef9
This is unchanged except for a test fix to web_contents_impl_browsertest.cc.
[email protected],[email protected],[email protected],[email protected]
Original change's description:
> Display Cutout: Notify the browser process on fullscreen frame change
>
> Notify the browser process when the frame owning the current
> fullscreen element is changed.
>
> Design: https://docs.google.com/document/d/1j3jqmGRXAHzpeKeS_tLlOo4C9DsrvPOf_-PMnFzTmeE/edit#heading=h.wozo6ynsdlp3
>
> BUG=838400
>
> Change-Id: I7771f7b58a8c1cdb7ec0b89b30db1de1fe708b32
> Reviewed-on: https://chromium-review.googlesource.com/1054441
> Reviewed-by: Scott Violet <[email protected]>
> Reviewed-by: Alex Moshchuk <[email protected]>
> Reviewed-by: Kinuko Yasuda <[email protected]>
> Reviewed-by: Mounir Lamouri <[email protected]>
> Reviewed-by: Philip Jägenstedt <[email protected]>
> Commit-Queue: Becca Hughes <[email protected]>
> Cr-Commit-Position: refs/heads/master@{#567162}
Bug: 838400
Change-Id: I72c8dff2b4b1d9df5410c3fa14ea32eb5d606ca9
Reviewed-on: https://chromium-review.googlesource.com/1101140
Reviewed-by: Alex Moshchuk <[email protected]>
Commit-Queue: Becca Hughes <[email protected]>
Cr-Commit-Position: refs/heads/master@{#567351}
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 07724d7..c586ac2 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -2311,6 +2311,74 @@
}
}
+void WebContentsImpl::FullscreenStateChanged(RenderFrameHost* rfh,
+ bool is_fullscreen) {
+ int frame_tree_node_id = rfh->GetFrameTreeNodeId();
+ auto it = fullscreen_frame_tree_nodes_.find(frame_tree_node_id);
+ bool changed = false;
+
+ if (is_fullscreen) {
+ // If we are fullscreen then add the FrameTreeNode ID to the set.
+ if (it == fullscreen_frame_tree_nodes_.end()) {
+ fullscreen_frame_tree_nodes_.insert(frame_tree_node_id);
+ changed = true;
+ }
+ } else {
+ FrameTreeNode* ancestor =
+ static_cast<RenderFrameHostImpl*>(rfh)->frame_tree_node();
+ DCHECK(ancestor);
+
+ // If we are not fullscreen then remove this frame and any descendants
+ // from the set.
+ for (it = fullscreen_frame_tree_nodes_.begin();
+ it != fullscreen_frame_tree_nodes_.end();) {
+ FrameTreeNode* node = FrameTreeNode::GloballyFindByID(*it);
+
+ if (!node || frame_tree_node_id == *it ||
+ node->IsDescendantOf(ancestor)) {
+ it = fullscreen_frame_tree_nodes_.erase(it);
+ changed = true;
+ } else {
+ ++it;
+ }
+ }
+ }
+
+ // If we have changed then find the current fullscreen FrameTreeNode
+ // and call the observers. If we have exited fullscreen then this
+ // will be the last frame that was fullscreen.
+ if (changed && fullscreen_frame_tree_nodes_.size() > 0) {
+ unsigned int max_depth = 0;
+ RenderFrameHost* max_depth_rfh = nullptr;
+
+ for (auto node_id : fullscreen_frame_tree_nodes_) {
+ FrameTreeNode* fullscreen_node = FrameTreeNode::GloballyFindByID(node_id);
+ DCHECK(fullscreen_node);
+
+ if (max_depth_rfh == nullptr || fullscreen_node->depth() > max_depth) {
+ max_depth = fullscreen_node->depth();
+ max_depth_rfh = fullscreen_node->current_frame_host();
+ }
+ }
+
+ // If we have already notified observers about this frame then we should not
+ // fire the observers again.
+ DCHECK(max_depth_rfh);
+ if (max_depth_rfh->GetFrameTreeNodeId() ==
+ current_fullscreen_frame_tree_node_id_)
+ return;
+
+ current_fullscreen_frame_tree_node_id_ =
+ max_depth_rfh->GetFrameTreeNodeId();
+
+ for (auto& observer : observers_)
+ observer.DidAcquireFullscreen(max_depth_rfh);
+ } else if (fullscreen_frame_tree_nodes_.size() == 0) {
+ current_fullscreen_frame_tree_node_id_ =
+ RenderFrameHost::kNoFrameTreeNodeId;
+ }
+}
+
bool WebContentsImpl::IsFullscreenForCurrentTab() const {
return delegate_ ? delegate_->IsFullscreenForTabOrPending(this) : false;
}
@@ -4895,6 +4963,9 @@
pepper_playback_observer_->RenderFrameDeleted(render_frame_host);
#endif
display_cutout_host_impl_->RenderFrameDeleted(render_frame_host);
+
+ // Remove any fullscreen state that the frame has stored.
+ FullscreenStateChanged(render_frame_host, false /* is_fullscreen */);
}
void WebContentsImpl::ShowContextMenu(RenderFrameHost* render_frame_host,
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index 93919c36..0b541ff 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -539,6 +539,8 @@
void EnterFullscreenMode(const GURL& origin,
const blink::WebFullscreenOptions& options) override;
void ExitFullscreenMode(bool will_cause_resize) override;
+ void FullscreenStateChanged(RenderFrameHost* rfh,
+ bool is_fullscreen) override;
bool ShouldRouteMessageEvent(
RenderFrameHost* target_rfh,
SiteInstance* source_site_instance) const override;
@@ -961,6 +963,8 @@
friend class WebContentsObserver;
friend class WebContents; // To implement factory methods.
+ friend class WebContentsImplBrowserTest;
+
FRIEND_TEST_ALL_PREFIXES(WebContentsImplTest, NoJSMessageOnInterstitials);
FRIEND_TEST_ALL_PREFIXES(WebContentsImplTest, UpdateTitle);
FRIEND_TEST_ALL_PREFIXES(WebContentsImplTest, FindOpenerRVHWhenPending);
@@ -977,6 +981,12 @@
FRIEND_TEST_ALL_PREFIXES(WebContentsImplTest,
ResetJavaScriptDialogOnUserNavigate);
FRIEND_TEST_ALL_PREFIXES(WebContentsImplTest, ParseDownloadHeaders);
+ FRIEND_TEST_ALL_PREFIXES(WebContentsImplBrowserTest,
+ NotifyFullscreenAcquired);
+ FRIEND_TEST_ALL_PREFIXES(WebContentsImplBrowserTest,
+ NotifyFullscreenAcquired_Navigate);
+ FRIEND_TEST_ALL_PREFIXES(WebContentsImplBrowserTest,
+ NotifyFullscreenAcquired_SameOrigin);
FRIEND_TEST_ALL_PREFIXES(FormStructureBrowserTest, HTMLFiles);
FRIEND_TEST_ALL_PREFIXES(NavigationControllerTest, HistoryNavigate);
FRIEND_TEST_ALL_PREFIXES(RenderFrameHostManagerTest, PageDoesBackAndReload);
@@ -1731,6 +1741,15 @@
class DisplayCutoutHostImpl;
std::unique_ptr<DisplayCutoutHostImpl> display_cutout_host_impl_;
+ // Stores a set of FrameTreeNode ids that are fullscreen.
+ using FullscreenFrameNodes = std::set<int>;
+ FullscreenFrameNodes fullscreen_frame_tree_nodes_;
+
+ // Stores the ID of the current fullscreen |FrameTreeNode| or
+ // |kNoFrameTreeNodeId| if the tab is not currently fullscreen.
+ int current_fullscreen_frame_tree_node_id_ =
+ RenderFrameHost::kNoFrameTreeNodeId;
+
base::WeakPtrFactory<WebContentsImpl> loading_weak_factory_;
base::WeakPtrFactory<WebContentsImpl> weak_factory_;
diff --git a/content/browser/web_contents/web_contents_impl_browsertest.cc b/content/browser/web_contents/web_contents_impl_browsertest.cc
index 9767bed5..4286d9a5c 100644
--- a/content/browser/web_contents/web_contents_impl_browsertest.cc
+++ b/content/browser/web_contents/web_contents_impl_browsertest.cc
@@ -34,9 +34,11 @@
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_types.h"
+#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/resource_dispatcher_host.h"
+#include "content/public/browser/site_isolation_policy.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_delegate.h"
#include "content/public/browser/web_contents_observer.h"
@@ -121,6 +123,13 @@
host_resolver()->AddRule("*", "127.0.0.1");
}
+ bool CurrentFullscreenFrameTreeNodeIsEmpty() {
+ WebContentsImpl* web_contents =
+ static_cast<WebContentsImpl*>(shell()->web_contents());
+ return web_contents->current_fullscreen_frame_tree_node_id_ ==
+ RenderFrameHost::kNoFrameTreeNodeId;
+ }
+
private:
DISALLOW_COPY_AND_ASSIGN(WebContentsImplBrowserTest);
};
@@ -2523,4 +2532,218 @@
EXPECT_TRUE(new_contents->GetLastCommittedURL().SchemeIs("file"));
}
+namespace {
+
+class FullscreenWebContentsObserver : public WebContentsObserver {
+ public:
+ FullscreenWebContentsObserver(WebContents* web_contents,
+ RenderFrameHost* wanted_rfh)
+ : WebContentsObserver(web_contents), wanted_rfh_(wanted_rfh) {}
+
+ // WebContentsObserver override.
+ void DidAcquireFullscreen(RenderFrameHost* rfh) override {
+ EXPECT_EQ(wanted_rfh_, rfh);
+ EXPECT_FALSE(found_value_);
+
+ if (rfh == wanted_rfh_) {
+ found_value_ = true;
+ run_loop_.Quit();
+ }
+ }
+
+ void Wait() {
+ if (!found_value_)
+ run_loop_.Run();
+ }
+
+ private:
+ base::RunLoop run_loop_;
+ bool found_value_ = false;
+ RenderFrameHost* wanted_rfh_;
+
+ DISALLOW_COPY_AND_ASSIGN(FullscreenWebContentsObserver);
+};
+
+} // namespace
+
+IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, NotifyFullscreenAcquired) {
+ ASSERT_TRUE(embedded_test_server()->Start());
+ WebContentsImpl* web_contents =
+ static_cast<WebContentsImpl*>(shell()->web_contents());
+ TestWCDelegateForDialogsAndFullscreen test_delegate;
+ web_contents->SetDelegate(&test_delegate);
+
+ GURL url = embedded_test_server()->GetURL(
+ "a.com", "/cross_site_iframe_factory.html?a(b{allowfullscreen})");
+ EXPECT_TRUE(NavigateToURL(shell(), url));
+ RenderFrameHost* main_frame = web_contents->GetMainFrame();
+ int main_frame_id = main_frame->GetFrameTreeNodeId();
+
+ RenderFrameHost* child_frame = ChildFrameAt(main_frame, 0);
+ int child_frame_id = child_frame->GetFrameTreeNodeId();
+
+ WebContentsImpl::FullscreenFrameNodes nodes;
+ EXPECT_EQ(nodes, web_contents->fullscreen_frame_tree_nodes_);
+ EXPECT_TRUE(CurrentFullscreenFrameTreeNodeIsEmpty());
+
+ // Make the top page fullscreen.
+ {
+ FullscreenWebContentsObserver observer(web_contents, main_frame);
+ EXPECT_TRUE(
+ ExecuteScript(main_frame, "document.body.webkitRequestFullscreen();"));
+ observer.Wait();
+ }
+
+ nodes.insert(main_frame_id);
+ EXPECT_EQ(nodes, web_contents->fullscreen_frame_tree_nodes_);
+ EXPECT_EQ(main_frame_id,
+ web_contents->current_fullscreen_frame_tree_node_id_);
+
+ // Make the child frame fullscreen.
+ {
+ FullscreenWebContentsObserver observer(web_contents, child_frame);
+ EXPECT_TRUE(
+ ExecuteScript(child_frame, "document.body.webkitRequestFullscreen();"));
+ observer.Wait();
+ }
+
+ nodes.insert(child_frame_id);
+ EXPECT_EQ(nodes, web_contents->fullscreen_frame_tree_nodes_);
+ EXPECT_EQ(child_frame_id,
+ web_contents->current_fullscreen_frame_tree_node_id_);
+
+ // Exit fullscreen on the child frame.
+ // This will not work with --site-per-process until crbug.com/617369
+ // is fixed.
+ if (!SiteIsolationPolicy::UseDedicatedProcessesForAllSites()) {
+ {
+ FullscreenWebContentsObserver observer(web_contents, main_frame);
+ EXPECT_TRUE(
+ ExecuteScript(child_frame, "document.webkitExitFullscreen();"));
+ observer.Wait();
+ }
+
+ nodes.erase(child_frame_id);
+ EXPECT_EQ(nodes, web_contents->fullscreen_frame_tree_nodes_);
+ EXPECT_EQ(main_frame_id,
+ web_contents->current_fullscreen_frame_tree_node_id_);
+ }
+}
+
+IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest,
+ NotifyFullscreenAcquired_Navigate) {
+ ASSERT_TRUE(embedded_test_server()->Start());
+ WebContentsImpl* web_contents =
+ static_cast<WebContentsImpl*>(shell()->web_contents());
+ TestWCDelegateForDialogsAndFullscreen test_delegate;
+ test_delegate.WillWaitForFullscreenExit();
+ web_contents->SetDelegate(&test_delegate);
+
+ GURL url = embedded_test_server()->GetURL(
+ "a.com", "/cross_site_iframe_factory.html?a(b{allowfullscreen})");
+ EXPECT_TRUE(NavigateToURL(shell(), url));
+ RenderFrameHost* main_frame = web_contents->GetMainFrame();
+ int main_frame_id = main_frame->GetFrameTreeNodeId();
+
+ RenderFrameHost* child_frame = ChildFrameAt(main_frame, 0);
+ int child_frame_id = child_frame->GetFrameTreeNodeId();
+
+ WebContentsImpl::FullscreenFrameNodes nodes;
+ EXPECT_EQ(nodes, web_contents->fullscreen_frame_tree_nodes_);
+ EXPECT_TRUE(CurrentFullscreenFrameTreeNodeIsEmpty());
+
+ // Make the top page fullscreen.
+ {
+ FullscreenWebContentsObserver observer(web_contents, main_frame);
+ EXPECT_TRUE(
+ ExecuteScript(main_frame, "document.body.webkitRequestFullscreen();"));
+ observer.Wait();
+ }
+
+ nodes.insert(main_frame_id);
+ EXPECT_EQ(nodes, web_contents->fullscreen_frame_tree_nodes_);
+ EXPECT_EQ(main_frame_id,
+ web_contents->current_fullscreen_frame_tree_node_id_);
+
+ // Make the child frame fullscreen.
+ {
+ FullscreenWebContentsObserver observer(web_contents, child_frame);
+ EXPECT_TRUE(
+ ExecuteScript(child_frame, "document.body.webkitRequestFullscreen();"));
+ observer.Wait();
+ }
+
+ nodes.insert(child_frame_id);
+ EXPECT_EQ(nodes, web_contents->fullscreen_frame_tree_nodes_);
+ EXPECT_EQ(child_frame_id,
+ web_contents->current_fullscreen_frame_tree_node_id_);
+
+ // Perform a cross origin navigation on the main frame.
+ EXPECT_TRUE(
+ NavigateToURL(shell(), embedded_test_server()->GetURL(
+ "c.com", "/cross_site_iframe_factory.html")));
+ EXPECT_EQ(0u, web_contents->fullscreen_frame_tree_nodes_.size());
+ EXPECT_TRUE(CurrentFullscreenFrameTreeNodeIsEmpty());
+}
+
+IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest,
+ NotifyFullscreenAcquired_SameOrigin) {
+ ASSERT_TRUE(embedded_test_server()->Start());
+ WebContentsImpl* web_contents =
+ static_cast<WebContentsImpl*>(shell()->web_contents());
+ TestWCDelegateForDialogsAndFullscreen test_delegate;
+ web_contents->SetDelegate(&test_delegate);
+
+ GURL url = embedded_test_server()->GetURL(
+ "a.com", "/cross_site_iframe_factory.html?a(a{allowfullscreen})");
+ EXPECT_TRUE(NavigateToURL(shell(), url));
+ RenderFrameHost* main_frame = web_contents->GetMainFrame();
+ int main_frame_id = main_frame->GetFrameTreeNodeId();
+
+ RenderFrameHost* child_frame = ChildFrameAt(main_frame, 0);
+ int child_frame_id = child_frame->GetFrameTreeNodeId();
+
+ WebContentsImpl::FullscreenFrameNodes nodes;
+ EXPECT_EQ(nodes, web_contents->fullscreen_frame_tree_nodes_);
+ EXPECT_TRUE(CurrentFullscreenFrameTreeNodeIsEmpty());
+
+ // Make the top page fullscreen.
+ {
+ FullscreenWebContentsObserver observer(web_contents, main_frame);
+ EXPECT_TRUE(
+ ExecuteScript(main_frame, "document.body.webkitRequestFullscreen();"));
+ observer.Wait();
+ }
+
+ nodes.insert(main_frame_id);
+ EXPECT_EQ(nodes, web_contents->fullscreen_frame_tree_nodes_);
+ EXPECT_EQ(main_frame_id,
+ web_contents->current_fullscreen_frame_tree_node_id_);
+
+ // Make the child frame fullscreen.
+ {
+ FullscreenWebContentsObserver observer(web_contents, child_frame);
+ EXPECT_TRUE(
+ ExecuteScript(child_frame, "document.body.webkitRequestFullscreen();"));
+ observer.Wait();
+ }
+
+ nodes.insert(child_frame_id);
+ EXPECT_EQ(nodes, web_contents->fullscreen_frame_tree_nodes_);
+ EXPECT_EQ(child_frame_id,
+ web_contents->current_fullscreen_frame_tree_node_id_);
+
+ // Exit fullscreen on the child frame.
+ {
+ FullscreenWebContentsObserver observer(web_contents, main_frame);
+ EXPECT_TRUE(ExecuteScript(child_frame, "document.webkitExitFullscreen();"));
+ observer.Wait();
+ }
+
+ nodes.erase(child_frame_id);
+ EXPECT_EQ(nodes, web_contents->fullscreen_frame_tree_nodes_);
+ EXPECT_EQ(main_frame_id,
+ web_contents->current_fullscreen_frame_tree_node_id_);
+}
+
} // namespace content