summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoralexmos <alexmos@chromium.org>2015-12-06 02:07:39 -0800
committerCommit bot <commit-bot@chromium.org>2015-12-06 10:09:28 +0000
commit401f0abaadb546a25d35c1a35c4557c30d736e06 (patch)
tree94c3ac2d744b94f15793a2a57cedf067db1d5bb0
parent7a93db44208d1135755e6331310077cdd8bca098 (diff)
downloadchromium_src-401f0abaadb546a25d35c1a35c4557c30d736e06.zip
chromium_src-401f0abaadb546a25d35c1a35c4557c30d736e06.tar.gz
chromium_src-401f0abaadb546a25d35c1a35c4557c30d736e06.tar.bz2
Implement sequential focus navigation for OOPIF.
Pressing <tab> or <shift-tab> traverses through all focusable elements on a page, descending into the content of any frames that it encounters along the way, including cross-origin frames. This behavior is currently broken with out-of-process frames: remote frames are skipped entirely during focus traversal. This CL puts in the initial plumbing to fix this and allow advancing focus across frames in different processes. When the search for focusable elements in FocusController::advanceFocusInDocumentOrder encounters an <iframe> element for a remote frame, it will instruct that frame to continue the search in its process via the new AdvanceFocus IPCs. Once the frame has finished advancing through its focusable elements, it will use the same IPCs to transfer the search for focusable elements to its parent frame. To allow the parent to resume search from the right place, the IPCs include the source frame (from which the search is being transferred). BUG=341918,339659 Review URL: https://codereview.chromium.org/1500873002 Cr-Commit-Position: refs/heads/master@{#363362}
-rw-r--r--content/browser/frame_host/render_frame_proxy_host.cc26
-rw-r--r--content/browser/frame_host/render_frame_proxy_host.h2
-rw-r--r--content/browser/site_per_process_browsertest.cc85
-rw-r--r--content/common/browser_plugin/browser_plugin_messages.h1
-rw-r--r--content/common/frame_messages.h19
-rw-r--r--content/renderer/render_frame_impl.cc12
-rw-r--r--content/renderer/render_frame_impl.h2
-rw-r--r--content/renderer/render_frame_proxy.cc6
-rw-r--r--content/renderer/render_frame_proxy.h3
-rw-r--r--third_party/WebKit/Source/core/frame/RemoteFrame.cpp6
-rw-r--r--third_party/WebKit/Source/core/frame/RemoteFrame.h4
-rw-r--r--third_party/WebKit/Source/core/frame/RemoteFrameClient.h3
-rw-r--r--third_party/WebKit/Source/core/page/FocusController.cpp57
-rw-r--r--third_party/WebKit/Source/core/page/FocusController.h4
-rw-r--r--third_party/WebKit/Source/web/RemoteFrameClientImpl.cpp5
-rw-r--r--third_party/WebKit/Source/web/RemoteFrameClientImpl.h1
-rw-r--r--third_party/WebKit/Source/web/WebViewImpl.cpp7
-rw-r--r--third_party/WebKit/Source/web/WebViewImpl.h3
-rw-r--r--third_party/WebKit/public/web/WebRemoteFrameClient.h6
-rw-r--r--third_party/WebKit/public/web/WebView.h8
20 files changed, 246 insertions, 14 deletions
diff --git a/content/browser/frame_host/render_frame_proxy_host.cc b/content/browser/frame_host/render_frame_proxy_host.cc
index c921e04..1f0c182 100644
--- a/content/browser/frame_host/render_frame_proxy_host.cc
+++ b/content/browser/frame_host/render_frame_proxy_host.cc
@@ -146,6 +146,7 @@ bool RenderFrameProxyHost::OnMessageReceived(const IPC::Message& msg) {
IPC_MESSAGE_HANDLER(FrameHostMsg_OpenURL, OnOpenURL)
IPC_MESSAGE_HANDLER(FrameHostMsg_RouteMessageEvent, OnRouteMessageEvent)
IPC_MESSAGE_HANDLER(FrameHostMsg_DidChangeOpener, OnDidChangeOpener)
+ IPC_MESSAGE_HANDLER(FrameHostMsg_AdvanceFocus, OnAdvanceFocus)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
@@ -325,4 +326,29 @@ void RenderFrameProxyHost::OnDidChangeOpener(int32 opener_routing_id) {
GetSiteInstance());
}
+void RenderFrameProxyHost::OnAdvanceFocus(blink::WebFocusType type,
+ int32_t source_routing_id) {
+ RenderFrameHostImpl* target_rfh =
+ frame_tree_node_->render_manager()->current_frame_host();
+
+ // Translate the source RenderFrameHost in this process to its equivalent
+ // RenderFrameProxyHost in the target process. This is needed for continuing
+ // the focus traversal from correct place in a parent frame after one of its
+ // child frames finishes its traversal.
+ RenderFrameHostImpl* source_rfh =
+ RenderFrameHostImpl::FromID(GetProcess()->GetID(), source_routing_id);
+ int32_t source_proxy_routing_id = MSG_ROUTING_NONE;
+ if (source_rfh) {
+ RenderFrameProxyHost* source_proxy =
+ source_rfh->frame_tree_node()
+ ->render_manager()
+ ->GetRenderFrameProxyHost(target_rfh->GetSiteInstance());
+ if (source_proxy)
+ source_proxy_routing_id = source_proxy->GetRoutingID();
+ }
+
+ target_rfh->Send(new FrameMsg_AdvanceFocus(target_rfh->GetRoutingID(), type,
+ source_proxy_routing_id));
+}
+
} // namespace content
diff --git a/content/browser/frame_host/render_frame_proxy_host.h b/content/browser/frame_host/render_frame_proxy_host.h
index fd5fc6c..c8b0db0 100644
--- a/content/browser/frame_host/render_frame_proxy_host.h
+++ b/content/browser/frame_host/render_frame_proxy_host.h
@@ -10,6 +10,7 @@
#include "content/browser/site_instance_impl.h"
#include "ipc/ipc_listener.h"
#include "ipc/ipc_sender.h"
+#include "third_party/WebKit/public/platform/WebFocusType.h"
struct FrameMsg_PostMessage_Params;
@@ -129,6 +130,7 @@ class RenderFrameProxyHost
void OnOpenURL(const FrameHostMsg_OpenURL_Params& params);
void OnRouteMessageEvent(const FrameMsg_PostMessage_Params& params);
void OnDidChangeOpener(int32 opener_routing_id);
+ void OnAdvanceFocus(blink::WebFocusType type, int32_t source_routing_id);
// This RenderFrameProxyHost's routing id.
int routing_id_;
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index 007c6cd..cd46ef8 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -4069,6 +4069,91 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
EXPECT_EQ("FOO", result);
}
+// Ensure that sequential focus navigation (advancing focused elements with
+// <tab> and <shift-tab>) works across cross-process subframes.
+// The test sets up six inputs fields in a page with two cross-process
+// subframes:
+// child1 child2
+// /------------\ /------------\.
+// | 2. <input> | | 4. <input> |
+// 1. <input> | 3. <input> | | 5. <input> | 6. <input>
+// \------------/ \------------/.
+//
+// The test then presses <tab> six times to cycle through focused elements 1-6.
+// The test then repeats this with <shift-tab> to cycle in reverse order.
+IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, SequentialFocusNavigation) {
+ GURL main_url(embedded_test_server()->GetURL(
+ "a.com", "/cross_site_iframe_factory.html?a(b,c)"));
+ EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+ WebContents* contents = shell()->web_contents();
+ FrameTreeNode* root =
+ static_cast<WebContentsImpl*>(contents)->GetFrameTree()->root();
+
+ // Assign a name to each frame. This will be sent along in test messages
+ // from focus events.
+ EXPECT_TRUE(ExecuteScript(root->current_frame_host(),
+ "window.name = 'root';"));
+ EXPECT_TRUE(ExecuteScript(root->child_at(0)->current_frame_host(),
+ "window.name = 'child1';"));
+ EXPECT_TRUE(ExecuteScript(root->child_at(1)->current_frame_host(),
+ "window.name = 'child2';"));
+
+ // This script will insert two <input> fields in the document, one at the
+ // beginning and one at the end. For root frame, this means that we will
+ // have an <input>, then two <iframe> elements, then another <input>.
+ std::string script =
+ "function onFocus(e) {"
+ " domAutomationController.setAutomationId(0);"
+ " domAutomationController.send(window.name + '-focused-' + e.target.id);"
+ "}"
+ "var input1 = document.createElement('input');"
+ "input1.id = 'input1';"
+ "var input2 = document.createElement('input');"
+ "input2.id = 'input2';"
+ "document.body.insertBefore(input1, document.body.firstChild);"
+ "document.body.appendChild(input2);"
+ "input1.addEventListener('focus', onFocus, false);"
+ "input2.addEventListener('focus', onFocus, false);";
+
+ // Add two input fields to each of the three frames.
+ EXPECT_TRUE(ExecuteScript(root->current_frame_host(), script));
+ EXPECT_TRUE(ExecuteScript(root->child_at(0)->current_frame_host(), script));
+ EXPECT_TRUE(ExecuteScript(root->child_at(1)->current_frame_host(), script));
+
+ // Helper to simulate a tab press and wait for a focus message.
+ auto press_tab_and_wait_for_message = [contents](bool reverse) {
+ DOMMessageQueue msg_queue;
+ std::string reply;
+ SimulateKeyPress(contents, ui::VKEY_TAB, false, reverse /* shift */, false,
+ false);
+ EXPECT_TRUE(msg_queue.WaitForMessage(&reply));
+ return reply;
+ };
+
+ // Press <tab> six times to focus each of the <input> elements in turn.
+ EXPECT_EQ("\"root-focused-input1\"", press_tab_and_wait_for_message(false));
+ EXPECT_EQ(root, root->frame_tree()->GetFocusedFrame());
+ EXPECT_EQ("\"child1-focused-input1\"", press_tab_and_wait_for_message(false));
+ EXPECT_EQ(root->child_at(0), root->frame_tree()->GetFocusedFrame());
+ EXPECT_EQ("\"child1-focused-input2\"", press_tab_and_wait_for_message(false));
+ EXPECT_EQ("\"child2-focused-input1\"", press_tab_and_wait_for_message(false));
+ EXPECT_EQ(root->child_at(1), root->frame_tree()->GetFocusedFrame());
+ EXPECT_EQ("\"child2-focused-input2\"", press_tab_and_wait_for_message(false));
+ EXPECT_EQ("\"root-focused-input2\"", press_tab_and_wait_for_message(false));
+ EXPECT_EQ(root, root->frame_tree()->GetFocusedFrame());
+
+ // Now, press <shift-tab> to navigate focus in the reverse direction.
+ EXPECT_EQ("\"child2-focused-input2\"", press_tab_and_wait_for_message(true));
+ EXPECT_EQ(root->child_at(1), root->frame_tree()->GetFocusedFrame());
+ EXPECT_EQ("\"child2-focused-input1\"", press_tab_and_wait_for_message(true));
+ EXPECT_EQ("\"child1-focused-input2\"", press_tab_and_wait_for_message(true));
+ EXPECT_EQ(root->child_at(0), root->frame_tree()->GetFocusedFrame());
+ EXPECT_EQ("\"child1-focused-input1\"", press_tab_and_wait_for_message(true));
+ EXPECT_EQ("\"root-focused-input1\"", press_tab_and_wait_for_message(true));
+ EXPECT_EQ(root, root->frame_tree()->GetFocusedFrame());
+}
+
// A WebContentsDelegate to capture ContextMenu creation events.
class ContextMenuObserverDelegate : public WebContentsDelegate {
public:
diff --git a/content/common/browser_plugin/browser_plugin_messages.h b/content/common/browser_plugin/browser_plugin_messages.h
index ca06826..91f5f60 100644
--- a/content/common/browser_plugin/browser_plugin_messages.h
+++ b/content/common/browser_plugin/browser_plugin_messages.h
@@ -37,7 +37,6 @@
IPC_ENUM_TRAITS_MAX_VALUE(blink::WebDragStatus, blink::WebDragStatusLast)
-IPC_ENUM_TRAITS_MAX_VALUE(blink::WebFocusType, blink::WebFocusTypeLast)
IPC_STRUCT_BEGIN(BrowserPluginHostMsg_Attach_Params)
IPC_STRUCT_MEMBER(bool, focused)
diff --git a/content/common/frame_messages.h b/content/common/frame_messages.h
index f4fc8b5..01d5fdf 100644
--- a/content/common/frame_messages.h
+++ b/content/common/frame_messages.h
@@ -29,6 +29,7 @@
#include "content/public/common/three_d_api_types.h"
#include "content/public/common/transition_element.h"
#include "ipc/ipc_message_macros.h"
+#include "third_party/WebKit/public/platform/WebFocusType.h"
#include "third_party/WebKit/public/web/WebFrameOwnerProperties.h"
#include "third_party/WebKit/public/web/WebTreeScopeType.h"
#include "ui/gfx/ipc/gfx_param_traits.h"
@@ -58,6 +59,7 @@ IPC_ENUM_TRAITS_MAX_VALUE(blink::WebContextMenuData::MediaType,
blink::WebContextMenuData::MediaTypeLast)
IPC_ENUM_TRAITS_MAX_VALUE(blink::WebContextMenuData::InputFieldType,
blink::WebContextMenuData::InputFieldTypeLast)
+IPC_ENUM_TRAITS_MAX_VALUE(blink::WebFocusType, blink::WebFocusTypeLast)
IPC_ENUM_TRAITS_MAX_VALUE(blink::WebFrameOwnerProperties::ScrollingMode,
blink::WebFrameOwnerProperties::ScrollingMode::Last)
IPC_ENUM_TRAITS(blink::WebSandboxFlags) // Bitmask.
@@ -720,6 +722,14 @@ IPC_MESSAGE_ROUTED3(FrameMsg_GetSerializedHtmlWithLocalLinks,
IPC_MESSAGE_ROUTED1(FrameMsg_SetFrameOwnerProperties,
blink::WebFrameOwnerProperties /* frame_owner_properties */)
+// Request to continue running the sequential focus navigation algorithm in
+// this frame. |source_routing_id| identifies the frame that issued this
+// request. This message is sent when pressing <tab> or <shift-tab> needs to
+// find the next focusable element in a cross-process frame.
+IPC_MESSAGE_ROUTED2(FrameMsg_AdvanceFocus,
+ blink::WebFocusType /* type */,
+ int32_t /* source_routing_id */)
+
#if defined(ENABLE_PLUGINS)
// Notifies the renderer of updates to the Plugin Power Saver origin whitelist.
IPC_MESSAGE_ROUTED1(FrameMsg_UpdatePluginContentOriginWhitelist,
@@ -1250,6 +1260,15 @@ IPC_MESSAGE_ROUTED2(FrameHostMsg_SerializedHtmlWithLocalLinksResponse,
IPC_MESSAGE_ROUTED1(FrameHostMsg_UpdatePageImportanceSignals,
content::PageImportanceSignals)
+// This message is sent from a RenderFrameProxy when sequential focus
+// navigation needs to advance into its actual frame. |source_routing_id|
+// identifies the frame that issued this request. This is used when pressing
+// <tab> or <shift-tab> hits an out-of-process iframe when searching for the
+// next focusable element.
+IPC_MESSAGE_ROUTED2(FrameHostMsg_AdvanceFocus,
+ blink::WebFocusType /* type */,
+ int32_t /* source_routing_id */)
+
#if defined(OS_MACOSX) || defined(OS_ANDROID)
// Message to show/hide a popup menu using native controls.
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 2ea7dca..860e3d5 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -1243,6 +1243,7 @@ bool RenderFrameImpl::OnMessageReceived(const IPC::Message& msg) {
IPC_MESSAGE_HANDLER(FrameMsg_DidUpdateSandboxFlags, OnDidUpdateSandboxFlags)
IPC_MESSAGE_HANDLER(FrameMsg_SetFrameOwnerProperties,
OnSetFrameOwnerProperties)
+ IPC_MESSAGE_HANDLER(FrameMsg_AdvanceFocus, OnAdvanceFocus)
IPC_MESSAGE_HANDLER(FrameMsg_SetTextTrackSettings,
OnTextTrackSettingsChanged)
IPC_MESSAGE_HANDLER(FrameMsg_PostMessageEvent, OnPostMessageEvent)
@@ -1801,6 +1802,17 @@ void RenderFrameImpl::OnSetFrameOwnerProperties(
frame_->setFrameOwnerProperties(frame_owner_properties);
}
+void RenderFrameImpl::OnAdvanceFocus(blink::WebFocusType type,
+ int32_t source_routing_id) {
+ RenderFrameProxy* source_frame =
+ RenderFrameProxy::FromRoutingID(source_routing_id);
+ if (!source_frame)
+ return;
+
+ render_view_->webview()->advanceFocusAcrossFrames(
+ type, source_frame->web_frame(), frame_);
+}
+
void RenderFrameImpl::OnTextTrackSettingsChanged(
const FrameMsg_TextTrackSettings_Params& params) {
DCHECK(!frame_->parent());
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index b9c33b7..bae9b33 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -29,6 +29,7 @@
#include "media/blink/webmediaplayer_params.h"
#include "mojo/application/public/interfaces/service_provider.mojom.h"
#include "mojo/application/public/interfaces/shell.mojom.h"
+#include "third_party/WebKit/public/platform/WebFocusType.h"
#include "third_party/WebKit/public/platform/modules/app_banner/WebAppBannerClient.h"
#include "third_party/WebKit/public/web/WebAXObject.h"
#include "third_party/WebKit/public/web/WebDataSource.h"
@@ -746,6 +747,7 @@ class CONTENT_EXPORT RenderFrameImpl
void OnDidUpdateSandboxFlags(blink::WebSandboxFlags flags);
void OnSetFrameOwnerProperties(
const blink::WebFrameOwnerProperties& frame_owner_properties);
+ void OnAdvanceFocus(blink::WebFocusType type, int32_t source_routing_id);
void OnTextTrackSettingsChanged(
const FrameMsg_TextTrackSettings_Params& params);
void OnPostMessageEvent(const FrameMsg_PostMessage_Params& params);
diff --git a/content/renderer/render_frame_proxy.cc b/content/renderer/render_frame_proxy.cc
index 600d478..f753af4 100644
--- a/content/renderer/render_frame_proxy.cc
+++ b/content/renderer/render_frame_proxy.cc
@@ -444,4 +444,10 @@ void RenderFrameProxy::didChangeOpener(blink::WebFrame* opener) {
Send(new FrameHostMsg_DidChangeOpener(routing_id_, opener_routing_id));
}
+void RenderFrameProxy::advanceFocus(blink::WebFocusType type,
+ blink::WebLocalFrame* source) {
+ int source_routing_id = RenderFrameImpl::FromWebFrame(source)->GetRoutingID();
+ Send(new FrameHostMsg_AdvanceFocus(routing_id_, type, source_routing_id));
+}
+
} // namespace
diff --git a/content/renderer/render_frame_proxy.h b/content/renderer/render_frame_proxy.h
index d54e15e..1e4c288 100644
--- a/content/renderer/render_frame_proxy.h
+++ b/content/renderer/render_frame_proxy.h
@@ -10,6 +10,7 @@
#include "content/common/content_export.h"
#include "ipc/ipc_listener.h"
#include "ipc/ipc_sender.h"
+#include "third_party/WebKit/public/platform/WebFocusType.h"
#include "third_party/WebKit/public/web/WebRemoteFrame.h"
#include "third_party/WebKit/public/web/WebRemoteFrameClient.h"
#include "url/origin.h"
@@ -132,6 +133,8 @@ class CONTENT_EXPORT RenderFrameProxy
void forwardInputEvent(const blink::WebInputEvent* event) override;
void frameRectsChanged(const blink::WebRect& frame_rect) override;
void didChangeOpener(blink::WebFrame* opener) override;
+ void advanceFocus(blink::WebFocusType type,
+ blink::WebLocalFrame* source) override;
// IPC handlers
void OnDidStartLoading();
diff --git a/third_party/WebKit/Source/core/frame/RemoteFrame.cpp b/third_party/WebKit/Source/core/frame/RemoteFrame.cpp
index 444e796..5d7965a 100644
--- a/third_party/WebKit/Source/core/frame/RemoteFrame.cpp
+++ b/third_party/WebKit/Source/core/frame/RemoteFrame.cpp
@@ -8,6 +8,7 @@
#include "bindings/core/v8/WindowProxy.h"
#include "bindings/core/v8/WindowProxyManager.h"
#include "core/dom/RemoteSecurityContext.h"
+#include "core/frame/LocalFrame.h"
#include "core/frame/RemoteDOMWindow.h"
#include "core/frame/RemoteFrameClient.h"
#include "core/frame/RemoteFrameView.h"
@@ -187,4 +188,9 @@ void RemoteFrame::setRemotePlatformLayer(WebLayer* layer)
toHTMLFrameOwnerElement(owner())->setNeedsCompositingUpdate();
}
+void RemoteFrame::advanceFocus(WebFocusType type, LocalFrame* source)
+{
+ remoteFrameClient()->advanceFocus(type, source);
+}
+
} // namespace blink
diff --git a/third_party/WebKit/Source/core/frame/RemoteFrame.h b/third_party/WebKit/Source/core/frame/RemoteFrame.h
index c12ec4b..21e0c4e 100644
--- a/third_party/WebKit/Source/core/frame/RemoteFrame.h
+++ b/third_party/WebKit/Source/core/frame/RemoteFrame.h
@@ -8,11 +8,13 @@
#include "core/CoreExport.h"
#include "core/dom/RemoteSecurityContext.h"
#include "core/frame/Frame.h"
+#include "public/platform/WebFocusType.h"
namespace blink {
class Event;
class IntRect;
+class LocalFrame;
class RemoteDOMWindow;
class RemoteFrameClient;
class RemoteFrameView;
@@ -50,6 +52,8 @@ public:
void setRemotePlatformLayer(WebLayer*);
WebLayer* remotePlatformLayer() const { return m_remotePlatformLayer; }
+ void advanceFocus(WebFocusType, LocalFrame* source);
+
void setView(PassRefPtrWillBeRawPtr<RemoteFrameView>);
void createView();
diff --git a/third_party/WebKit/Source/core/frame/RemoteFrameClient.h b/third_party/WebKit/Source/core/frame/RemoteFrameClient.h
index 50d88f8..7beb80a 100644
--- a/third_party/WebKit/Source/core/frame/RemoteFrameClient.h
+++ b/third_party/WebKit/Source/core/frame/RemoteFrameClient.h
@@ -8,6 +8,7 @@
#include "core/frame/FrameClient.h"
#include "core/frame/FrameTypes.h"
#include "core/loader/FrameLoaderTypes.h"
+#include "public/platform/WebFocusType.h"
namespace blink {
@@ -28,6 +29,8 @@ public:
virtual void forwardInputEvent(Event*) = 0;
virtual void frameRectsChanged(const IntRect& frameRect) = 0;
+
+ virtual void advanceFocus(WebFocusType, LocalFrame* source) = 0;
};
} // namespace blink
diff --git a/third_party/WebKit/Source/core/page/FocusController.cpp b/third_party/WebKit/Source/core/page/FocusController.cpp
index 4bde2a9..dc9b4ac 100644
--- a/third_party/WebKit/Source/core/page/FocusController.cpp
+++ b/third_party/WebKit/Source/core/page/FocusController.cpp
@@ -43,6 +43,7 @@
#include "core/frame/FrameView.h"
#include "core/frame/LocalDOMWindow.h"
#include "core/frame/LocalFrame.h"
+#include "core/frame/RemoteFrame.h"
#include "core/frame/Settings.h"
#include "core/html/HTMLAreaElement.h"
#include "core/html/HTMLImageElement.h"
@@ -690,8 +691,13 @@ bool FocusController::advanceFocus(WebFocusType type, bool initialFocus, InputDe
{
switch (type) {
case WebFocusTypeForward:
- case WebFocusTypeBackward:
- return advanceFocusInDocumentOrder(type, initialFocus, sourceCapabilities);
+ case WebFocusTypeBackward: {
+ // We should never hit this when a RemoteFrame is focused, since the key
+ // event that initiated focus advancement should've been routed to that
+ // frame's process from the beginning.
+ LocalFrame* startingFrame = toLocalFrame(focusedOrMainFrame());
+ return advanceFocusInDocumentOrder(startingFrame, nullptr, type, initialFocus, sourceCapabilities);
+ }
case WebFocusTypeLeft:
case WebFocusTypeRight:
case WebFocusTypeUp:
@@ -704,17 +710,30 @@ bool FocusController::advanceFocus(WebFocusType type, bool initialFocus, InputDe
return false;
}
-bool FocusController::advanceFocusInDocumentOrder(WebFocusType type, bool initialFocus, InputDeviceCapabilities* sourceCapabilities)
+bool FocusController::advanceFocusAcrossFrames(WebFocusType type, RemoteFrame* from, LocalFrame* to, InputDeviceCapabilities* sourceCapabilities)
+{
+ // If we are shifting focus from a child frame to its parent, the
+ // child frame has no more focusable elements, and we should continue
+ // looking for focusable elements in the parent, starting from the <iframe>
+ // element of the child frame.
+ Node* startingNode = nullptr;
+ if (from->tree().parent() == to) {
+ ASSERT(from->owner()->isLocal());
+ startingNode = toHTMLFrameOwnerElement(from->owner());
+ }
+
+ return advanceFocusInDocumentOrder(to, startingNode, type, false, sourceCapabilities);
+}
+
+bool FocusController::advanceFocusInDocumentOrder(LocalFrame* frame, Node* startingNode, WebFocusType type, bool initialFocus, InputDeviceCapabilities* sourceCapabilities)
{
- // FIXME: Focus advancement won't work with externally rendered frames until after
- // inter-frame focus control is moved out of Blink.
- if (!focusedOrMainFrame()->isLocalFrame())
- return false;
- LocalFrame* frame = toLocalFrame(focusedOrMainFrame());
ASSERT(frame);
Document* document = frame->document();
- Node* currentNode = document->focusedElement();
+ Node* currentNode = startingNode;
+ if (!currentNode)
+ currentNode = document->focusedElement();
+
// FIXME: Not quite correct when it comes to focus transitions leaving/entering the WebView itself
bool caretBrowsing = frame->settings() && frame->settings()->caretBrowsingEnabled();
@@ -726,6 +745,14 @@ bool FocusController::advanceFocusInDocumentOrder(WebFocusType type, bool initia
RefPtrWillBeRawPtr<Element> element = findFocusableElementAcrossFocusScopes(type, FocusNavigationScope::focusNavigationScopeOf(currentNode ? *currentNode : *document), currentNode);
if (!element) {
+ // If there's a RemoteFrame on the ancestor chain, we need to continue
+ // searching for focusable elements there.
+ if (frame->localFrameRoot() != frame->tree().top()) {
+ document->clearFocusedElement();
+ toRemoteFrame(frame->localFrameRoot()->tree().parent())->advanceFocus(type, frame->localFrameRoot());
+ return true;
+ }
+
// We didn't find an element to focus, so we should try to pass focus to Chrome.
if (!initialFocus && m_page->chromeClient().canTakeFocus(type)) {
document->clearFocusedElement();
@@ -735,9 +762,7 @@ bool FocusController::advanceFocusInDocumentOrder(WebFocusType type, bool initia
}
// Chrome doesn't want focus, so we should wrap focus.
- if (!m_page->mainFrame()->isLocalFrame())
- return false;
- element = findFocusableElementRecursively(type, FocusNavigationScope::focusNavigationScopeOf(*m_page->deprecatedLocalMainFrame()->document()), nullptr);
+ element = findFocusableElementRecursively(type, FocusNavigationScope::focusNavigationScopeOf(*toLocalFrame(m_page->mainFrame())->document()), nullptr);
element = findFocusableElementDescendingDownIntoFrameDocument(type, element.get());
if (!element)
@@ -760,6 +785,14 @@ bool FocusController::advanceFocusInDocumentOrder(WebFocusType type, bool initia
document->clearFocusedElement();
setFocusedFrame(owner->contentFrame());
+
+ // If contentFrame is remote, continue the search for focusable
+ // elements in that frame's process.
+ // clearFocusedElement() fires events that might detach the
+ // contentFrame, hence the need to null-check it again.
+ if (owner->contentFrame() && owner->contentFrame()->isRemoteFrame())
+ toRemoteFrame(owner->contentFrame())->advanceFocus(type, frame);
+
return true;
}
diff --git a/third_party/WebKit/Source/core/page/FocusController.h b/third_party/WebKit/Source/core/page/FocusController.h
index 40cf228..1bc31f4 100644
--- a/third_party/WebKit/Source/core/page/FocusController.h
+++ b/third_party/WebKit/Source/core/page/FocusController.h
@@ -46,6 +46,7 @@ class InputDeviceCapabilities;
class LocalFrame;
class Node;
class Page;
+class RemoteFrame;
class CORE_EXPORT FocusController final : public NoBaseWillBeGarbageCollectedFinalized<FocusController> {
WTF_MAKE_NONCOPYABLE(FocusController); USING_FAST_MALLOC_WILL_BE_REMOVED(FocusController);
@@ -68,6 +69,7 @@ public:
bool setInitialFocus(WebFocusType);
bool advanceFocus(WebFocusType type, InputDeviceCapabilities* sourceCapabilities = nullptr) { return advanceFocus(type, false, sourceCapabilities); }
+ bool advanceFocusAcrossFrames(WebFocusType, RemoteFrame* from, LocalFrame* to, InputDeviceCapabilities* sourceCapabilities = nullptr);
Element* findFocusableElement(WebFocusType, Node&);
bool setFocusedElement(Element*, PassRefPtrWillBeRawPtr<Frame>, const FocusParams&);
@@ -88,7 +90,7 @@ private:
bool advanceFocus(WebFocusType, bool initialFocus, InputDeviceCapabilities* sourceCapabilities = nullptr);
bool advanceFocusDirectionally(WebFocusType);
- bool advanceFocusInDocumentOrder(WebFocusType, bool initialFocus, InputDeviceCapabilities* sourceCapabilities);
+ bool advanceFocusInDocumentOrder(LocalFrame*, Node* startingNode, WebFocusType, bool initialFocus, InputDeviceCapabilities* sourceCapabilities);
bool advanceFocusDirectionallyInContainer(Node* container, const LayoutRect& startingRect, WebFocusType);
void findFocusCandidateInContainer(Node& container, const LayoutRect& startingRect, WebFocusType, FocusCandidate& closest);
diff --git a/third_party/WebKit/Source/web/RemoteFrameClientImpl.cpp b/third_party/WebKit/Source/web/RemoteFrameClientImpl.cpp
index 9250810..fde5147 100644
--- a/third_party/WebKit/Source/web/RemoteFrameClientImpl.cpp
+++ b/third_party/WebKit/Source/web/RemoteFrameClientImpl.cpp
@@ -188,4 +188,9 @@ void RemoteFrameClientImpl::frameRectsChanged(const IntRect& frameRect)
m_webFrame->client()->frameRectsChanged(frameRect);
}
+void RemoteFrameClientImpl::advanceFocus(WebFocusType type, LocalFrame* source)
+{
+ m_webFrame->client()->advanceFocus(type, WebLocalFrameImpl::fromFrame(source));
+}
+
} // namespace blink
diff --git a/third_party/WebKit/Source/web/RemoteFrameClientImpl.h b/third_party/WebKit/Source/web/RemoteFrameClientImpl.h
index eb2342d..388c808 100644
--- a/third_party/WebKit/Source/web/RemoteFrameClientImpl.h
+++ b/third_party/WebKit/Source/web/RemoteFrameClientImpl.h
@@ -36,6 +36,7 @@ public:
unsigned backForwardLength() override;
void forwardInputEvent(Event*) override;
void frameRectsChanged(const IntRect& frameRect) override;
+ void advanceFocus(WebFocusType, LocalFrame*) override;
WebRemoteFrameImpl* webFrame() const { return m_webFrame; }
diff --git a/third_party/WebKit/Source/web/WebViewImpl.cpp b/third_party/WebKit/Source/web/WebViewImpl.cpp
index b4ad9dc..e6417fa 100644
--- a/third_party/WebKit/Source/web/WebViewImpl.cpp
+++ b/third_party/WebKit/Source/web/WebViewImpl.cpp
@@ -2968,6 +2968,13 @@ void WebViewImpl::advanceFocus(bool reverse)
page()->focusController().advanceFocus(reverse ? WebFocusTypeBackward : WebFocusTypeForward);
}
+void WebViewImpl::advanceFocusAcrossFrames(WebFocusType type, WebRemoteFrame* from, WebLocalFrame* to)
+{
+ // TODO(alexmos): Pass in proper with sourceCapabilities.
+ page()->focusController().advanceFocusAcrossFrames(
+ type, toWebRemoteFrameImpl(from)->frame(), toWebLocalFrameImpl(to)->frame());
+}
+
double WebViewImpl::zoomLevel()
{
return m_zoomLevel;
diff --git a/third_party/WebKit/Source/web/WebViewImpl.h b/third_party/WebKit/Source/web/WebViewImpl.h
index 4ced199..c4453c1 100644
--- a/third_party/WebKit/Source/web/WebViewImpl.h
+++ b/third_party/WebKit/Source/web/WebViewImpl.h
@@ -82,10 +82,12 @@ class WebActiveGestureAnimation;
class WebDevToolsAgentImpl;
class WebElement;
class WebLayerTreeView;
+class WebLocalFrame;
class WebLocalFrameImpl;
class WebImage;
class WebPagePopupImpl;
class WebPlugin;
+class WebRemoteFrame;
class WebSelection;
class WebSettingsImpl;
class WebViewScheduler;
@@ -189,6 +191,7 @@ public:
void smoothScroll(int targetX, int targetY, long durationMs) override;
void zoomToFindInPageRect(const WebRect&);
void advanceFocus(bool reverse) override;
+ void advanceFocusAcrossFrames(WebFocusType, WebRemoteFrame* from, WebLocalFrame* to) override;
double zoomLevel() override;
double setZoomLevel(double) override;
void zoomLimitsChanged(double minimumZoomLevel, double maximumZoomLevel) override;
diff --git a/third_party/WebKit/public/web/WebRemoteFrameClient.h b/third_party/WebKit/public/web/WebRemoteFrameClient.h
index 703943b..5a6a824 100644
--- a/third_party/WebKit/public/web/WebRemoteFrameClient.h
+++ b/third_party/WebKit/public/web/WebRemoteFrameClient.h
@@ -5,6 +5,7 @@
#ifndef WebRemoteFrameClient_h
#define WebRemoteFrameClient_h
+#include "public/platform/WebFocusType.h"
#include "public/platform/WebSecurityOrigin.h"
#include "public/web/WebDOMMessageEvent.h"
@@ -48,6 +49,11 @@ public:
// This frame updated its opener to another frame.
virtual void didChangeOpener(WebFrame* opener) { }
+
+ // Continue sequential focus navigation in this frame. This is called when
+ // the |source| frame is searching for the next focusable element (e.g., in
+ // response to <tab>) and encounters a remote frame.
+ virtual void advanceFocus(WebFocusType type, WebLocalFrame* source) { }
};
} // namespace blink
diff --git a/third_party/WebKit/public/web/WebView.h b/third_party/WebKit/public/web/WebView.h
index e0fd1bf..843f633 100644
--- a/third_party/WebKit/public/web/WebView.h
+++ b/third_party/WebKit/public/web/WebView.h
@@ -33,6 +33,7 @@
#include "../platform/WebColor.h"
#include "../platform/WebDisplayMode.h"
+#include "../platform/WebFocusType.h"
#include "../platform/WebPageVisibilityState.h"
#include "../platform/WebString.h"
#include "../platform/WebVector.h"
@@ -50,9 +51,11 @@ class WebCredentialManagerClient;
class WebDragData;
class WebFrame;
class WebHitTestResult;
+class WebLocalFrame;
class WebPageImportanceSignals;
class WebPageOverlay;
class WebPrerendererClient;
+class WebRemoteFrame;
class WebSettings;
class WebSpellCheckClient;
class WebString;
@@ -191,6 +194,11 @@ public:
// previous element in the tab sequence (if reverse is true).
virtual void advanceFocus(bool reverse) { }
+ // Advance the focus from the frame |from| to the next in sequence
+ // (determined by WebFocusType) focusable element in frame |to|. Used when
+ // focus needs to advance to/from a cross-process frame.
+ virtual void advanceFocusAcrossFrames(WebFocusType, WebRemoteFrame* from, WebLocalFrame* to) { }
+
// Animate a scale into the specified rect where multiple targets were
// found from previous tap gesture.
// Returns false if it doesn't do any zooming.