diff options
author | alexmos <alexmos@chromium.org> | 2015-12-06 02:07:39 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-12-06 10:09:28 +0000 |
commit | 401f0abaadb546a25d35c1a35c4557c30d736e06 (patch) | |
tree | 94c3ac2d744b94f15793a2a57cedf067db1d5bb0 | |
parent | 7a93db44208d1135755e6331310077cdd8bca098 (diff) | |
download | chromium_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}
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. |