Convert WidgetMsg_SetActive to FrameWidget mojom
This CL defines frame widget specific methods that will be invoked from
the browser process to activate/deactivate the corresponding
RenderWidget.
Bug: 1071565
Change-Id: I2866536547d9b58fb6bd9559329e3d35b577748d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2188013
Commit-Queue: Abhijeet Kandalkar <abhijeet@igalia.com>
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Reviewed-by: Avi Drissman <avi@chromium.org>
Reviewed-by: Dave Tapuska <dtapuska@chromium.org>
Cr-Commit-Position: refs/heads/master@{#803792}
diff --git a/chrome/browser/chrome_render_widget_host_browsertests.cc b/chrome/browser/chrome_render_widget_host_browsertests.cc
new file mode 100644
index 0000000..f6c04316
--- /dev/null
+++ b/chrome/browser/chrome_render_widget_host_browsertests.cc
@@ -0,0 +1,247 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/command_line.h"
+#include "base/run_loop.h"
+#include "base/test/test_timeouts.h"
+#include "build/build_config.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/location_bar/location_bar.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/interactive_test_utils.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/omnibox/browser/omnibox_view.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/focused_node_details.h"
+#include "content/public/browser/render_widget_host_view.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/content_browser_test.h"
+#include "content/public/test/content_browser_test_utils.h"
+#include "content/public/test/test_utils.h"
+#include "net/dns/mock_host_resolver.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "url/gurl.h"
+
+class ActiveRenderWidgetHostBrowserTest : public InProcessBrowserTest {
+ public:
+ ActiveRenderWidgetHostBrowserTest() = default;
+ ~ActiveRenderWidgetHostBrowserTest() override = default;
+
+ void SetUpCommandLine(base::CommandLine* command_line) override {
+ content::IsolateAllSitesForTesting(command_line);
+ }
+
+ void SetUpOnMainThread() override {
+ host_resolver()->AddRule("*", "127.0.0.1");
+
+ // Add content/test/data for cross_site_iframe_factory.html
+ embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");
+
+ ASSERT_TRUE(embedded_test_server()->Start());
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ActiveRenderWidgetHostBrowserTest);
+};
+
+IN_PROC_BROWSER_TEST_F(ActiveRenderWidgetHostBrowserTest,
+ DocumentIsActiveAndFocused) {
+ GURL main_url(embedded_test_server()->GetURL(
+ "a.com", "/cross_site_iframe_factory.html?a(b(c),d)"));
+
+ // Site A ------------ proxies for B C D
+ // |--Site B ------- proxies for A C D
+ // | +--Site C -- proxies for A B D
+ // +--Site D ------- proxies for A B C
+ // Where A = http://a.com/
+ // B = http://b.com/
+ // C = http://c.com/
+ // D = http://d.com/
+ ui_test_utils::NavigateToURL(browser(), main_url);
+
+ content::WebContents* web_contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+ content::RenderFrameHost* main_frame_a = web_contents->GetMainFrame();
+ content::RenderFrameHost* child_frame_b = ChildFrameAt(main_frame_a, 0);
+ ASSERT_NE(nullptr, child_frame_b);
+ content::RenderFrameHost* child_frame_d = ChildFrameAt(main_frame_a, 1);
+ ASSERT_NE(nullptr, child_frame_d);
+ content::RenderFrameHost* child_frame_c = ChildFrameAt(child_frame_b, 0);
+ ASSERT_NE(nullptr, child_frame_c);
+
+ EXPECT_NE(main_frame_a->GetSiteInstance(), child_frame_b->GetSiteInstance());
+ EXPECT_NE(main_frame_a->GetSiteInstance(), child_frame_d->GetSiteInstance());
+ EXPECT_NE(child_frame_b->GetSiteInstance(), child_frame_c->GetSiteInstance());
+
+ // Helper function to check document.hasFocus() for a given frame.
+ // hasFocus internally calls FocusController::IsDocumentFocused which
+ // return true only iff document is active and focused.
+ auto document_is_active_and_focused =
+ [](content::RenderFrameHost* rfh) -> bool {
+ bool has_focus = false;
+ EXPECT_TRUE(ExecuteScriptAndExtractBool(
+ rfh, "window.domAutomationController.send(document.hasFocus())",
+ &has_focus));
+ return has_focus;
+ };
+
+ // Helper function to check a property of document.activeElement in the
+ // specified frame.
+ auto verify_active_element_property = [](content::RenderFrameHost* rfh,
+ const std::string& property,
+ const std::string& expected_value) {
+ std::string script = base::StringPrintf(
+ "document.activeElement.%s.toLowerCase();", property.c_str());
+ EXPECT_EQ(expected_value, EvalJs(rfh, script));
+ };
+
+ // The main_frame_a should have a focus to start with.
+ EXPECT_EQ(main_frame_a, web_contents->GetFocusedFrame());
+ EXPECT_TRUE(document_is_active_and_focused(main_frame_a));
+ EXPECT_FALSE(document_is_active_and_focused(child_frame_b));
+ EXPECT_FALSE(document_is_active_and_focused(child_frame_c));
+ EXPECT_FALSE(document_is_active_and_focused(child_frame_d));
+ verify_active_element_property(main_frame_a, "tagName", "body");
+
+ // After focusing child_frame_b, document.hasFocus() should return
+ // true for child_frame_b and all its ancestor frames.
+ EXPECT_TRUE(ExecuteScript(child_frame_b, "window.focus();"));
+ EXPECT_EQ(child_frame_b, web_contents->GetFocusedFrame());
+ EXPECT_TRUE(document_is_active_and_focused(main_frame_a));
+ EXPECT_TRUE(document_is_active_and_focused(child_frame_b));
+ EXPECT_FALSE(document_is_active_and_focused(child_frame_c));
+ EXPECT_FALSE(document_is_active_and_focused(child_frame_d));
+ verify_active_element_property(main_frame_a, "tagName", "iframe");
+ verify_active_element_property(main_frame_a, "src",
+ child_frame_b->GetLastCommittedURL().spec());
+
+ // After focusing child_frame_c, document.hasFocus() should return
+ // true for child_frame_c and all its ancestor frames.
+ EXPECT_TRUE(ExecuteScript(child_frame_c, "window.focus();"));
+ EXPECT_EQ(child_frame_c, web_contents->GetFocusedFrame());
+ EXPECT_TRUE(document_is_active_and_focused(main_frame_a));
+ EXPECT_TRUE(document_is_active_and_focused(child_frame_b));
+ EXPECT_TRUE(document_is_active_and_focused(child_frame_c));
+ EXPECT_FALSE(document_is_active_and_focused(child_frame_d));
+ verify_active_element_property(main_frame_a, "tagName", "iframe");
+ // Check document.activeElement in main_frame_a. It should still
+ // point to <iframe> for the b.com frame, since Blink computes the
+ // focused iframe element by walking the parent chain of the focused
+ // frame until it hits the current frame. This logic should still
+ // work with remote frames.
+ verify_active_element_property(main_frame_a, "src",
+ child_frame_b->GetLastCommittedURL().spec());
+
+ // After focusing child_frame_d, document.hasFocus() should return
+ // true for child_frame_d and all its ancestor frames.
+ EXPECT_TRUE(ExecuteScript(child_frame_d, "window.focus();"));
+ EXPECT_EQ(child_frame_d, web_contents->GetFocusedFrame());
+ EXPECT_TRUE(document_is_active_and_focused(main_frame_a));
+ EXPECT_FALSE(document_is_active_and_focused(child_frame_b));
+ EXPECT_FALSE(document_is_active_and_focused(child_frame_c));
+ EXPECT_TRUE(document_is_active_and_focused(child_frame_d));
+ verify_active_element_property(main_frame_a, "tagName", "iframe");
+ verify_active_element_property(main_frame_a, "src",
+ child_frame_d->GetLastCommittedURL().spec());
+
+ // After focusing main_frame_a, document.hasFocus() should return
+ // true for main_frame_a and since it's a root of tree, all its
+ // descendants should return false. On the renderer side, both the
+ // 'active' and 'focus' states for blink::FocusController will be
+ // true.
+ EXPECT_TRUE(ExecuteScript(main_frame_a, "window.focus();"));
+ EXPECT_EQ(main_frame_a, web_contents->GetFocusedFrame());
+ EXPECT_TRUE(document_is_active_and_focused(main_frame_a));
+ EXPECT_FALSE(document_is_active_and_focused(child_frame_b));
+ EXPECT_FALSE(document_is_active_and_focused(child_frame_c));
+ EXPECT_FALSE(document_is_active_and_focused(child_frame_d));
+ verify_active_element_property(main_frame_a, "tagName", "body");
+
+ // Focus the URL bar.
+ OmniboxView* omnibox =
+ browser()->window()->GetLocationBar()->GetOmniboxView();
+ // Give the omnibox focus.
+ omnibox->SetFocus(/*is_user_initiated=*/true);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(main_frame_a, web_contents->GetFocusedFrame());
+
+ // `omnibox->SetFocus()` should call blur event on main_frame_a and
+ // deactivate the active render widget, but on Mac calling
+ // `omnibox->SetFocus()` function doesn't invoke
+ // RWHI::SetActive(false). As a result, `blink::FocusController`'s
+ // 'active' state maintains the previous value of false.
+ //
+ // This table sums up `blink::FocusController`'s 'active' and 'focus'
+ // states on different platforms after focusing the omnibox:
+ //
+ // | | Linux | Mac | Windows |
+ // | active | false | true | false |
+ // | focus | false | false | false |
+ //
+ // Since `document.hasFocus()` only returns true iff the document is
+ // both active and focus, the test still expects
+ // `document.hasFocus()` to be false on all platforms.
+ //
+ // Note that there is no separate API to test active state of the
+ // document. Instead, Mac's active behavior is separately tested in
+ // `ActiveRenderWidgetHostBrowserTest.FocusOmniBox`.
+ EXPECT_FALSE(document_is_active_and_focused(main_frame_a));
+ EXPECT_FALSE(document_is_active_and_focused(child_frame_b));
+ EXPECT_FALSE(document_is_active_and_focused(child_frame_c));
+ EXPECT_FALSE(document_is_active_and_focused(child_frame_d));
+ // body tag is active by default.
+ verify_active_element_property(main_frame_a, "tagName", "body");
+ verify_active_element_property(child_frame_b, "tagName", "body");
+ verify_active_element_property(child_frame_c, "tagName", "body");
+ verify_active_element_property(child_frame_d, "tagName", "body");
+}
+
+// This test verifies that on Mac, moving the focus from webcontents to Omnibox
+// doesn't change the 'active' state and old value of the active state is
+// retained.
+//
+// FakeFrameWidget has Optional<bool> 'active' state which is
+// uninitialised at the beginning. omnibox->SetFocus() invokes
+// RWHI::SetActive(false) for webcontents and there is a IPC call to
+// renderer which changes 'active' state to false.
+//
+// On Mac, calling omnibox->SetFocus function doesn't invoke
+// RWHI::SetActive(false). Hence there is no IPC call to renderer and
+// 'active' state maintains old value.
+IN_PROC_BROWSER_TEST_F(ActiveRenderWidgetHostBrowserTest, FocusOmniBox) {
+ GURL main_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
+ ui_test_utils::NavigateToURL(browser(), main_url);
+
+ content::WebContents* web_contents =
+ browser()->tab_strip_model()->GetActiveWebContents();
+
+ content::RenderFrameHost* main_frame = web_contents->GetMainFrame();
+ EXPECT_EQ(main_frame, web_contents->GetFocusedFrame());
+
+ mojo::PendingAssociatedReceiver<blink::mojom::FrameWidget>
+ blink_frame_widget_receiver =
+ content::BindFakeFrameWidgetInterfaces(main_frame);
+ content::FakeFrameWidget fake_frame_widget(
+ std::move(blink_frame_widget_receiver));
+
+ // Main frame is already focused at this point and now focus URL bar.
+ OmniboxView* omnibox =
+ browser()->window()->GetLocationBar()->GetOmniboxView();
+ // Give the omnibox focus.
+ omnibox->SetFocus(/*is_user_initiated=*/true);
+
+ base::RunLoop().RunUntilIdle();
+#if defined(OS_MAC)
+ // On MacOS, calling omnibox->SetFocus function doesn't invoke
+ // RWHI::SetActive. Hence there is no IPC call to renderer and
+ // FakeFrameWidget's 'active' state remains uninitialised.
+ EXPECT_EQ(fake_frame_widget.GetActive(), base::nullopt);
+#else
+ EXPECT_EQ(fake_frame_widget.GetActive(), false);
+#endif
+}
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 499156cc..12e4faf 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -6000,6 +6000,7 @@
"../browser/autofill/autofill_uitest.cc",
"../browser/autofill/autofill_uitest.h",
"../browser/browser_keyevents_browsertest.cc",
+ "../browser/chrome_render_widget_host_browsertests.cc",
"../browser/devtools/devtools_sanity_interactive_browsertest.cc",
"../browser/extensions/api/extension_action/browser_action_interactive_test.cc",
"../browser/extensions/api/extension_action/page_action_interactive_test.cc",
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
index ae13996e..3d48c475 100644
--- a/content/browser/renderer_host/render_widget_host_impl.cc
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -1231,7 +1231,9 @@
}
void RenderWidgetHostImpl::SetActive(bool active) {
- Send(new WidgetMsg_SetActive(routing_id_, active));
+ const bool is_frame_widget = owner_delegate_;
+ if (is_frame_widget)
+ blink_frame_widget_->SetActive(active);
}
void RenderWidgetHostImpl::LostMouseLock() {
diff --git a/content/browser/renderer_host/render_widget_host_view_aura_browsertest.cc b/content/browser/renderer_host/render_widget_host_view_aura_browsertest.cc
index 93acb8a5..dd608faa 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura_browsertest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura_browsertest.cc
@@ -22,6 +22,9 @@
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/shell/browser/shell.h"
+#include "content/shell/common/shell_switches.h"
+#include "net/dns/mock_host_resolver.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
#include "ui/events/event_utils.h"
namespace content {
@@ -299,4 +302,68 @@
ASSERT_TRUE(ExecuteScript(wc, "noop();"));
}
+class RenderWidgetHostViewAuraActiveWidgetTest : public ContentBrowserTest {
+ public:
+ RenderWidgetHostViewAuraActiveWidgetTest() = default;
+ ~RenderWidgetHostViewAuraActiveWidgetTest() override = default;
+
+ // Helper function to check |isActivated| for a given frame.
+ bool FrameIsActivated(content::RenderFrameHost* rfh) {
+ bool active = false;
+ EXPECT_TRUE(ExecuteScriptAndExtractBool(
+ rfh,
+ "window.domAutomationController.send(window.internals.isActivated())",
+ &active));
+ return active;
+ }
+
+ protected:
+ void SetUpCommandLine(base::CommandLine* command_line) override {
+ ContentBrowserTest::SetUpCommandLine(command_line);
+ command_line->AppendSwitch(switches::kExposeInternalsForTesting);
+ }
+
+ void SetUpOnMainThread() override {
+ host_resolver()->AddRule("*", "127.0.0.1");
+
+ // Add content/test/data for cross_site_iframe_factory.html
+ embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");
+
+ ASSERT_TRUE(embedded_test_server()->Start());
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewAuraActiveWidgetTest);
+};
+
+// In this test, toggling the value of 'active' state changes the
+// active state of frame on the renderer side.
+// SimulateActiveStateForWidget toggles the 'active' state of widget
+// over IPC.
+IN_PROC_BROWSER_TEST_F(RenderWidgetHostViewAuraActiveWidgetTest,
+ FocusIsInactive) {
+ GURL main_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
+ EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+ content::WebContents* web_contents = shell()->web_contents();
+
+ // The main_frame_a should have a focus to start with.
+ // On renderer side, blink::FocusController's both 'active' and
+ //'focus' states are set to true.
+ content::RenderFrameHost* main_frame = web_contents->GetMainFrame();
+ EXPECT_TRUE(FrameIsActivated(main_frame));
+
+ // After changing the 'active' state of main_frame to false
+ // blink::FocusController's 'active' set to false.
+ content::SimulateActiveStateForWidget(main_frame, false);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_FALSE(FrameIsActivated(main_frame));
+
+ // After changing the 'active' state of main_frame to true
+ // blink::FocusController's 'active' set to true.
+ content::SimulateActiveStateForWidget(main_frame, true);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(FrameIsActivated(main_frame));
+}
+
} // namespace content
diff --git a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
index f793384..7a18d871 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
@@ -551,6 +551,17 @@
delegates_.push_back(std::make_unique<MockRenderWidgetHostDelegate>());
parent_host_ = MockRenderWidgetHostImpl::Create(
delegates_.back().get(), *agent_scheduling_group_host_, routing_id);
+ mojo::AssociatedRemote<blink::mojom::FrameWidgetHost>
+ parent_frame_widget_host;
+ auto parent_frame_widget_host_receiver =
+ parent_frame_widget_host
+ .BindNewEndpointAndPassDedicatedReceiverForTesting();
+ mojo::AssociatedRemote<blink::mojom::FrameWidget> parent_frame_widget;
+ auto parent_frame_widget_receiver =
+ parent_frame_widget.BindNewEndpointAndPassDedicatedReceiverForTesting();
+ parent_host_->BindFrameWidgetInterfaces(
+ std::move(parent_frame_widget_host_receiver),
+ parent_frame_widget.Unbind());
delegates_.back()->set_widget_host(parent_host_);
delegates_.back()->set_frame_tree(GetFrameTree());
parent_view_ = new RenderWidgetHostViewAura(parent_host_);
@@ -560,6 +571,14 @@
gfx::Rect());
view_ = CreateView();
widget_host_ = static_cast<MockRenderWidgetHostImpl*>(view_->host());
+ mojo::AssociatedRemote<blink::mojom::FrameWidgetHost> frame_widget_host;
+ auto frame_widget_host_receiver =
+ frame_widget_host.BindNewEndpointAndPassDedicatedReceiverForTesting();
+ mojo::AssociatedRemote<blink::mojom::FrameWidget> frame_widget;
+ auto frame_widget_receiver =
+ frame_widget.BindNewEndpointAndPassDedicatedReceiverForTesting();
+ widget_host_->BindFrameWidgetInterfaces(
+ std::move(frame_widget_host_receiver), frame_widget.Unbind());
// Set the mouse_wheel_phase_handler_ timer timeout to 100ms.
view_->event_handler()->set_mouse_wheel_wheel_phase_handler_timeout(
base::TimeDelta::FromMilliseconds(100));
diff --git a/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm b/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
index 1af999b..5c4f7ee 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
+++ b/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
@@ -364,6 +364,15 @@
routing_id,
/*hidden=*/false,
std::make_unique<FrameTokenMessageQueue>()) {
+ mojo::AssociatedRemote<blink::mojom::FrameWidgetHost> frame_widget_host;
+ auto frame_widget_host_receiver =
+ frame_widget_host.BindNewEndpointAndPassDedicatedReceiverForTesting();
+ mojo::AssociatedRemote<blink::mojom::FrameWidget> frame_widget;
+ auto frame_widget_receiver =
+ frame_widget.BindNewEndpointAndPassDedicatedReceiverForTesting();
+ BindFrameWidgetInterfaces(std::move(frame_widget_host_receiver),
+ frame_widget.Unbind());
+
set_renderer_initialized(true);
lastWheelEventLatencyInfo = ui::LatencyInfo();
@@ -489,6 +498,14 @@
host_ = base::WrapUnique(MockRenderWidgetHostImpl::Create(
&delegate_, *agent_scheduling_group_host_,
process_host_->GetNextRoutingID()));
+ mojo::AssociatedRemote<blink::mojom::FrameWidgetHost> frame_widget_host;
+ auto frame_widget_host_receiver =
+ frame_widget_host.BindNewEndpointAndPassDedicatedReceiverForTesting();
+ mojo::AssociatedRemote<blink::mojom::FrameWidget> frame_widget;
+ auto frame_widget_receiver =
+ frame_widget.BindNewEndpointAndPassDedicatedReceiverForTesting();
+ host_->BindFrameWidgetInterfaces(std::move(frame_widget_host_receiver),
+ frame_widget.Unbind());
host_->set_owner_delegate(&mock_owner_delegate_);
rwhv_mac_ = new RenderWidgetHostViewMac(host_.get());
rwhv_cocoa_.reset([rwhv_mac_->GetInProcessNSView() retain]);
diff --git a/content/public/test/browser_test_utils.cc b/content/public/test/browser_test_utils.cc
index 2074bc1..7ace153 100644
--- a/content/public/test/browser_test_utils.cc
+++ b/content/public/test/browser_test_utils.cc
@@ -682,6 +682,31 @@
return "";
}
+mojo::PendingAssociatedReceiver<blink::mojom::FrameWidget>
+BindFakeFrameWidgetInterfaces(RenderFrameHost* frame) {
+ RenderWidgetHostImpl* render_widget_host_impl =
+ static_cast<RenderFrameHostImpl*>(frame)->GetRenderWidgetHost();
+
+ mojo::AssociatedRemote<blink::mojom::FrameWidgetHost> blink_frame_widget_host;
+ auto blink_frame_widget_host_receiver =
+ blink_frame_widget_host
+ .BindNewEndpointAndPassDedicatedReceiverForTesting();
+
+ mojo::AssociatedRemote<blink::mojom::FrameWidget> blink_frame_widget;
+ auto blink_frame_widget_receiver =
+ blink_frame_widget.BindNewEndpointAndPassDedicatedReceiverForTesting();
+
+ render_widget_host_impl->BindFrameWidgetInterfaces(
+ std::move(blink_frame_widget_host_receiver), blink_frame_widget.Unbind());
+
+ return blink_frame_widget_receiver;
+}
+
+void SimulateActiveStateForWidget(RenderFrameHost* frame, bool active) {
+ static_cast<RenderFrameHostImpl*>(frame)->GetRenderWidgetHost()->SetActive(
+ active);
+}
+
void WaitForLoadStopWithoutSuccessCheck(WebContents* web_contents) {
// In many cases, the load may have finished before we get here. Only wait if
// the tab still has a pending navigation.
diff --git a/content/public/test/browser_test_utils.h b/content/public/test/browser_test_utils.h
index 48cbc55..0ece6986 100644
--- a/content/public/test/browser_test_utils.h
+++ b/content/public/test/browser_test_utils.h
@@ -39,6 +39,7 @@
#include "content/public/common/isolated_world_ids.h"
#include "content/public/common/page_type.h"
#include "content/public/common/untrustworthy_context_menu_params.h"
+#include "content/public/test/fake_frame_widget.h"
#include "ipc/message_filter.h"
#include "net/base/load_flags.h"
#include "services/network/public/mojom/network_service.mojom.h"
@@ -350,6 +351,15 @@
std::string ReferrerPolicyToString(
network::mojom::ReferrerPolicy referrer_policy);
+// For testing, bind FakeFrameWidget to a RenderWidgetHost associated
+// with a given RenderFrameHost
+mojo::PendingAssociatedReceiver<blink::mojom::FrameWidget>
+BindFakeFrameWidgetInterfaces(RenderFrameHost* frame);
+
+// Set |active| state for a RenderWidgetHost associated with a given
+// RenderFrameHost
+void SimulateActiveStateForWidget(RenderFrameHost* frame, bool active);
+
// Holds down modifier keys for the duration of its lifetime and releases them
// upon destruction. This allows simulating multiple input events without
// simulating modifier key releases in between.
diff --git a/content/public/test/fake_frame_widget.cc b/content/public/test/fake_frame_widget.cc
index b41a5d2d..969f89a 100644
--- a/content/public/test/fake_frame_widget.cc
+++ b/content/public/test/fake_frame_widget.cc
@@ -27,4 +27,12 @@
}
#endif
+base::Optional<bool> FakeFrameWidget::GetActive() const {
+ return active_;
+}
+
+void FakeFrameWidget::SetActive(bool active) {
+ active_ = active;
+}
+
} // namespace content
diff --git a/content/public/test/fake_frame_widget.h b/content/public/test/fake_frame_widget.h
index 369d8fac..9576585 100644
--- a/content/public/test/fake_frame_widget.h
+++ b/content/public/test/fake_frame_widget.h
@@ -29,6 +29,7 @@
void operator=(const FakeFrameWidget&) = delete;
base::i18n::TextDirection GetTextDirection() const;
+ base::Optional<bool> GetActive() const;
private:
void DragTargetDragOver(const gfx::PointF& point_in_viewport,
@@ -48,6 +49,7 @@
void DragSourceSystemDragEnded() override {}
void SetBackgroundOpaque(bool value) override {}
void SetTextDirection(base::i18n::TextDirection direction) override;
+ void SetActive(bool active) override;
void SetInheritedEffectiveTouchActionForSubFrame(
const cc::TouchAction touch_action) override {}
void UpdateRenderThrottlingStatusForSubFrame(
@@ -70,6 +72,7 @@
mojo::AssociatedReceiver<blink::mojom::FrameWidget> receiver_;
base::i18n::TextDirection text_direction_ =
base::i18n::TextDirection::UNKNOWN_DIRECTION;
+ base::Optional<bool> active_;
};
} // namespace content
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index 17989ea..5b8818db 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -356,7 +356,6 @@
IPC_MESSAGE_HANDLER(WidgetMsg_Close, OnClose)
IPC_MESSAGE_HANDLER(WidgetMsg_WasHidden, OnWasHidden)
IPC_MESSAGE_HANDLER(WidgetMsg_WasShown, OnWasShown)
- IPC_MESSAGE_HANDLER(WidgetMsg_SetActive, OnSetActive)
IPC_MESSAGE_HANDLER(WidgetMsg_SetBounds_ACK, OnRequestSetBoundsAck)
IPC_MESSAGE_HANDLER(WidgetMsg_SetViewportIntersection,
OnSetViewportIntersection)
@@ -535,7 +534,7 @@
return GetFrameSinkId();
}
-void RenderWidget::OnSetActive(bool active) {
+void RenderWidget::SetActive(bool active) {
if (delegate())
delegate()->SetActiveForWidget(active);
}
diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h
index b49d995..376a2920 100644
--- a/content/renderer/render_widget.h
+++ b/content/renderer/render_widget.h
@@ -308,7 +308,7 @@
viz::FrameSinkId GetFrameSinkIdAtPoint(const gfx::PointF& point,
gfx::PointF* local_point);
- void OnSetActive(bool active);
+ void SetActive(bool active);
void UseSynchronousResizeModeForTesting(bool enable);
void SetDeviceScaleFactorForTesting(float factor);
diff --git a/content/shell/renderer/web_test/test_runner.cc b/content/shell/renderer/web_test/test_runner.cc
index cbce82c..24204f19 100644
--- a/content/shell/renderer/web_test/test_runner.cc
+++ b/content/shell/renderer/web_test/test_runner.cc
@@ -3116,7 +3116,7 @@
// This path simulates losing focus on the window, without moving it to
// another window.
if (widget->GetWebWidget()->HasFocus()) {
- widget->OnSetActive(false);
+ widget->SetActive(false);
widget->GetWebWidget()->SetFocus(false);
}
return;
@@ -3127,7 +3127,7 @@
if (other_main_frame != main_frame) {
RenderWidget* other_widget = other_main_frame->GetLocalRootRenderWidget();
if (other_widget->GetWebWidget()->HasFocus()) {
- other_widget->OnSetActive(false);
+ other_widget->SetActive(false);
other_widget->GetWebWidget()->SetFocus(false);
}
}
@@ -3135,7 +3135,7 @@
if (!widget->GetWebWidget()->HasFocus()) {
widget->GetWebWidget()->SetFocus(true);
- widget->OnSetActive(true);
+ widget->SetActive(true);
}
}
diff --git a/third_party/blink/public/mojom/page/widget.mojom b/third_party/blink/public/mojom/page/widget.mojom
index de9bc02..0774f572 100644
--- a/third_party/blink/public/mojom/page/widget.mojom
+++ b/third_party/blink/public/mojom/page/widget.mojom
@@ -61,6 +61,13 @@
// Changes the text direction of the currently selected input field (if any).
SetTextDirection(mojo_base.mojom.TextDirection direction);
+ // Activate/deactivate the Widget.
+ // Focused window is the window that receives keyboard input.
+ // The focused window is always or is always contained by the Active window.
+ // Active window is the one that contains the focused element.
+ // https://www.chromium.org/developers/design-documents/aura/focus-and-activation
+ SetActive(bool active);
+
// Only valid for sub frame local roots.
//
// Sets the inherited effective touch action on an out-of-process iframe.
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_base.cc b/third_party/blink/renderer/core/frame/web_frame_widget_base.cc
index 2206bdf4..d7014ea6 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_base.cc
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_base.cc
@@ -376,6 +376,10 @@
widget_base_->BindWidgetCompositor(std::move(receiver));
}
+void WebFrameWidgetBase::SetActive(bool active) {
+ View()->SetIsActive(active);
+}
+
void WebFrameWidgetBase::CancelDrag() {
// It's possible for this to be called while we're not doing a drag if
// it's from a previous page that got unloaded.
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_base.h b/third_party/blink/renderer/core/frame/web_frame_widget_base.h
index fac24ce..eaf1f69 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_base.h
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_base.h
@@ -381,6 +381,7 @@
WebDragOperation) override;
void DragSourceSystemDragEnded() override;
void SetBackgroundOpaque(bool opaque) override;
+ void SetActive(bool active) override;
// For both mainframe and childframe change the text direction of the
// currently selected input field (if any).
void SetTextDirection(base::i18n::TextDirection direction) override;
diff --git a/third_party/blink/renderer/core/testing/internals.cc b/third_party/blink/renderer/core/testing/internals.cc
index 0272f901..3fdb30f0 100644
--- a/third_party/blink/renderer/core/testing/internals.cc
+++ b/third_party/blink/renderer/core/testing/internals.cc
@@ -3164,6 +3164,13 @@
.GetInterestedElement();
}
+bool Internals::isActivated() {
+ if (!GetFrame())
+ return false;
+
+ return GetFrame()->GetPage()->GetFocusController().IsActive();
+}
+
bool Internals::isInCanvasFontCache(Document* document,
const String& font_string) {
return document->GetCanvasFontCache()->IsInCache(font_string);
diff --git a/third_party/blink/renderer/core/testing/internals.h b/third_party/blink/renderer/core/testing/internals.h
index f759f13..c0dcdd3 100644
--- a/third_party/blink/renderer/core/testing/internals.h
+++ b/third_party/blink/renderer/core/testing/internals.h
@@ -500,6 +500,10 @@
Element* interestedElement();
+ // Check if frame associated with current internals object is
+ // active or not.
+ bool isActivated();
+
bool isInCanvasFontCache(Document*, const String&);
unsigned canvasFontCacheMaxFonts();
diff --git a/third_party/blink/renderer/core/testing/internals.idl b/third_party/blink/renderer/core/testing/internals.idl
index c57453969..87e5ae0 100644
--- a/third_party/blink/renderer/core/testing/internals.idl
+++ b/third_party/blink/renderer/core/testing/internals.idl
@@ -321,6 +321,10 @@
// attribute returns the currently interested element on the page.
readonly attribute Element? interestedElement;
+ // Returns true if page associated with current internals object is
+ // active.
+ boolean isActivated();
+
boolean isInCanvasFontCache(Document document, DOMString fontString);
unsigned long canvasFontCacheMaxFonts();
diff --git a/third_party/blink/web_tests/fast/dom/Window/window-active-state.html b/third_party/blink/web_tests/fast/dom/Window/window-active-state.html
new file mode 100644
index 0000000..223735d
--- /dev/null
+++ b/third_party/blink/web_tests/fast/dom/Window/window-active-state.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<title>Check active state of windows</title>
+<script src="../../../resources/user-gesture-utils.js"></script>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script>
+ var popupWin = window.open('about:blank');
+ async_test((t) => {
+ window.addEventListener("blur", t.step_func(() => {
+ // Give a time to `popupWin` for activation.
+ setTimeout(() => {
+ assert_true(popupWin.internals.isActivated(), "Popup window should be activated");
+ assert_false(window.internals.isActivated(), "Parent window should be deactivated");
+ t.done();
+ }, 0);
+ }));
+
+ window.addEventListener("load", t.step_func(() => {
+ assert_true(window.internals.isActivated(), "Parent window should be activated");
+ focusWithUserGesture(popupWin);
+ }));
+ }, "This test passes if no crash");
+</script>