Prevent an inner WebContents from stealing focus from an outer WebContents.
An inner WebContents will only allow itself to be focused in two cases:
- When the outer WebContents has focus and advances focus to the inner
WebContents.
- When an outer WebContents has focus and focus the inner WebContents
through window.focus() API.
Bug: 725622
Cq-Include-Trybots: master.tryserver.chromium.linux:linux_site_isolation
Change-Id: If8ef4ed3386eadc2bb22c3afa7701d627c13eb39
Reviewed-on: https://chromium-review.googlesource.com/636624
Reviewed-by: Alex Moshchuk <[email protected]>
Reviewed-by: James MacLean <[email protected]>
Commit-Queue: Lucas Gadani <[email protected]>
Cr-Commit-Position: refs/heads/master@{#498203}
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 959ff29a..cd0e39e 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -1649,7 +1649,8 @@
if (outer_web_contents_impl->frame_tree_.GetFocusedFrame() ==
outer_contents_frame_impl->frame_tree_node()) {
- SetFocusedFrame(frame_tree_.root(), nullptr);
+ SetFocusedFrame(frame_tree_.root(),
+ outer_contents_frame->GetSiteInstance());
}
// Set up the the guest's AX tree to point back at the embedder's AX tree.
@@ -5268,14 +5269,36 @@
void WebContentsImpl::SetFocusedFrame(FrameTreeNode* node,
SiteInstance* source) {
- SetAsFocusedWebContentsIfNecessary();
-
frame_tree_.SetFocusedFrame(node, source);
- WebContentsImpl* inner_contents = node_.GetInnerWebContentsInFrame(node);
-
- WebContentsImpl* contents_to_focus = inner_contents ? inner_contents : this;
- contents_to_focus->SetAsFocusedWebContentsIfNecessary();
+ if (auto* inner_contents = node_.GetInnerWebContentsInFrame(node)) {
+ // |this| is an outer WebContents and |node| represents an inner
+ // WebContents. Transfer the focus to the inner contents if |this| is
+ // focused.
+ if (GetFocusedWebContents() == this)
+ inner_contents->SetAsFocusedWebContentsIfNecessary();
+ } else if (node_.OuterContentsFrameTreeNode() &&
+ node_.OuterContentsFrameTreeNode()
+ ->current_frame_host()
+ ->GetSiteInstance() == source) {
+ // |this| is an inner WebContents, |node| is its main FrameTreeNode and
+ // the outer WebContents FrameTreeNode is at |source|'s SiteInstance.
+ // Transfer the focus to the inner WebContents if the outer WebContents is
+ // focused. This branch is used when an inner WebContents is focused through
+ // its RenderFrameProxyHost (via FrameHostMsg_FrameFocused IPC, used to
+ // implement the window.focus() API).
+ if (GetFocusedWebContents() == GetOuterWebContents())
+ SetAsFocusedWebContentsIfNecessary();
+ } else if (!GetOuterWebContents()) {
+ // This is an outermost WebContents.
+ SetAsFocusedWebContentsIfNecessary();
+ } else if (!GuestMode::IsCrossProcessFrameGuest(this) &&
+ GetOuterWebContents()) {
+ // TODO(lfg, paulmeyer): Allows BrowserPlugins to set themselves as the
+ // focused WebContents. This works around a bug in FindRequestManager that
+ // doesn't support properly traversing BrowserPlugins.
+ SetAsFocusedWebContentsIfNecessary();
+ }
}
RenderFrameHost* WebContentsImpl::GetFocusedFrameIncludingInnerWebContents() {
@@ -5302,6 +5325,18 @@
}
}
+void WebContentsImpl::OnAdvanceFocus(RenderFrameHostImpl* source_rfh) {
+ // When a RenderFrame needs to advance focus to a RenderFrameProxy (by hitting
+ // TAB), the RenderFrameProxy sends an IPC to RenderFrameProxyHost. When this
+ // RenderFrameProxyHost represents an inner WebContents, the outer WebContents
+ // needs to focus the inner WebContents.
+ if (GetOuterWebContents() &&
+ GetOuterWebContents() == source_rfh->delegate()->GetAsWebContents() &&
+ GetFocusedWebContents() == GetOuterWebContents()) {
+ SetAsFocusedWebContentsIfNecessary();
+ }
+}
+
void WebContentsImpl::OnFocusedElementChangedInFrame(
RenderFrameHostImpl* frame,
const gfx::Rect& bounds_in_root_view) {