Index: content/browser/iframe_zoom_browsertest.cc |
diff --git a/content/browser/iframe_zoom_browsertest.cc b/content/browser/iframe_zoom_browsertest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..4cd8a922ca4a3e78b42f6879ec40f00cf80d143f |
--- /dev/null |
+++ b/content/browser/iframe_zoom_browsertest.cc |
@@ -0,0 +1,358 @@ |
+// Copyright 2016 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 <vector> |
+ |
+#include "content/browser/frame_host/frame_tree_node.h" |
+#include "content/browser/frame_host/render_frame_host_impl.h" |
+#include "content/browser/web_contents/web_contents_impl.h" |
+#include "content/public/browser/host_zoom_map.h" |
+#include "content/public/browser/navigation_entry.h" |
+#include "content/public/common/page_zoom.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_navigation_observer.h" |
+#include "content/shell/browser/shell.h" |
+#include "content/test/content_browser_test_utils_internal.h" |
+#include "net/dns/mock_host_resolver.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+#include "url/gurl.h" |
+ |
+namespace content { |
+ |
+class IFrameZoomBrowserTest : public ContentBrowserTest { |
alexmos
2016/04/25 22:43:27
A comment with a high-level overview of how these
wjmaclean
2016/04/27 13:43:57
I've tried to improve this ... let me know what yo
alexmos
2016/04/27 23:39:51
This is great - thanks for adding this!
|
+ public: |
+ IFrameZoomBrowserTest() {} |
+ |
+ protected: |
+ void SetUpOnMainThread() override { |
+ host_resolver()->AddRule("*", "127.0.0.1"); |
+ ASSERT_TRUE(embedded_test_server()->Start()); |
+ SetupCrossSiteRedirector(embedded_test_server()); |
+ } |
+ |
+ WebContentsImpl* web_contents() { |
+ return static_cast<WebContentsImpl*>(shell()->web_contents()); |
+ } |
+}; |
+ |
+double GetMainframeWindowBorder(const ToRenderFrameHost& adapter) { |
+ double border; |
+ const char kGetMainframeBorder[] = "window.domAutomationController.send(" |
+ "window.outerWidth - window.innerWidth" |
+ ");"; |
+ EXPECT_TRUE( |
+ ExecuteScriptAndExtractDouble(adapter, kGetMainframeBorder, &border)); |
+ return border; |
+} |
+ |
+double GetMainframeZoomFactor(const ToRenderFrameHost& adapter, double border) { |
alexmos
2016/04/25 22:43:27
nit: s/GetMainframeZoomFactor/GetMainFrameZoomFact
wjmaclean
2016/04/27 13:43:57
Done. I had used 'Mainframe' to match 'Subframe',
|
+ const char kGetMainframeZoomLevel[] = |
+ "window.domAutomationController.send(" |
+ "(window.outerWidth - %f)/window.innerWidth" |
+ ");"; |
+ double zoom_factor; |
+ EXPECT_TRUE(ExecuteScriptAndExtractDouble( |
+ adapter, base::StringPrintf(kGetMainframeZoomLevel, border), |
+ &zoom_factor)); |
+ return zoom_factor; |
+} |
+ |
+double GetSubframeWidth(const ToRenderFrameHost& adapter) { |
+ double width; |
+ EXPECT_TRUE(ExecuteScriptAndExtractDouble( |
+ adapter, "window.domAutomationController.send(window.innerWidth);", |
+ &width)); |
+ return width; |
+} |
+ |
+#define SETUP_A_B_A_NESTED_FRAMES() \ |
alexmos
2016/04/25 22:43:27
I'm not sure how I feel about this macro. :) I'm
wjmaclean
2016/04/27 13:43:57
I've replicated the macro, and moved the helpers i
|
+ std::string top_level_host("a.com"); \ |
+ GURL main_url(embedded_test_server()->GetURL( \ |
+ top_level_host, "/cross_site_iframe_factory.html?a(b(a))")); \ |
+ EXPECT_TRUE(NavigateToURL(shell(), main_url)); \ |
+ NavigationEntry* entry = \ |
+ web_contents()->GetController().GetLastCommittedEntry(); \ |
+ ASSERT_TRUE(entry); \ |
+ GURL loaded_url = HostZoomMap::GetURLFromEntry(entry); \ |
+ EXPECT_EQ(top_level_host, loaded_url.host()); \ |
+ \ |
+ FrameTreeNode* root = \ |
+ static_cast<WebContentsImpl*>(web_contents())->GetFrameTree()->root(); \ |
+ RenderFrameHostImpl* child = root->child_at(0)->current_frame_host(); \ |
+ RenderFrameHostImpl* grandchild = \ |
+ root->child_at(0)->child_at(0)->current_frame_host(); \ |
+ \ |
+ /* The following calls must be made when the page's scale factor = 1.0.*/ \ |
+ double scale_one_child_width = GetSubframeWidth(child); \ |
+ double scale_one_grandchild_width = GetSubframeWidth(grandchild); \ |
+ double main_frame_window_border = GetMainframeWindowBorder(web_contents()); \ |
+ \ |
+ HostZoomMap* host_zoom_map = HostZoomMap::GetForWebContents(web_contents()); \ |
+ double default_zoom_level = host_zoom_map->GetDefaultZoomLevel(); \ |
+ EXPECT_EQ(0.0, default_zoom_level); \ |
+ \ |
+ EXPECT_DOUBLE_EQ( \ |
+ 1.0, GetMainframeZoomFactor(web_contents(), main_frame_window_border)); |
+ |
+// The tests in this file rely on the notion that, when a page zooms, that |
+// subframes both (1) a change in their frame rect, and (2) a change in their |
+// frame's scale. Since the page should scale as a unit, this means the |
+// innerWidth value of any subframe should be the same before and after the |
+// zoom (though it may transiently take on a different value). The |
+// FrameSizeObserver serves to watch for onresize events, and observes when |
+// the innerWidth is correctly set. |
+struct FrameResizeObserver { |
+ FrameResizeObserver(RenderFrameHost* host, |
+ std::string label, |
+ double inner_width, |
+ double tolerance) |
+ : frame_host(host), |
+ msg_label(std::move(label)), |
+ zoomed_correctly(false), |
+ expected_inner_width(inner_width), |
+ tolerance(tolerance) { |
+ SetupOnResizeCallback(host, msg_label); |
+ } |
+ |
+ void SetupOnResizeCallback(const ToRenderFrameHost& adapter, |
+ const std::string& label) { |
+ const char kOnResizeCallbackSetup[] = |
+ "document.body.onresize = function(){" |
+ " window.domAutomationController.setAutomationId(0);" |
+ " window.domAutomationController.send('%s ' + window.innerWidth);" |
+ "};"; |
+ EXPECT_TRUE(ExecuteScript( |
+ adapter, base::StringPrintf(kOnResizeCallbackSetup, label.c_str()))); |
+ } |
+ |
+ void Check(const std::string& status_msg) { |
+ if (status_msg.find(msg_label) != 0) |
+ return; |
+ |
+ double inner_width = std::stod(status_msg.substr(msg_label.length() + 1)); |
+ zoomed_correctly = std::abs(expected_inner_width - inner_width) < tolerance; |
+ } |
+ |
+ FrameResizeObserver* toThis() {return this;} |
+ |
+ RenderFrameHost* frame_host; |
+ std::string msg_label; |
+ bool zoomed_correctly; |
+ double expected_inner_width; |
+ double tolerance; |
+}; |
+ |
+void WaitAndCheckFrameZoom( |
+ DOMMessageQueue& msg_queue, |
+ std::vector<FrameResizeObserver>& frame_observers) { |
+ std::string status; |
+ while (msg_queue.WaitForMessage(&status)) { |
+ // Strip the double quotes from the message. |
+ status = status.substr(1, status.length() -2); |
+ |
+ bool all_zoomed_correctly = true; |
+ |
+ // Use auto& to operate on a reference, and not a copy. |
+ for (auto& observer : frame_observers) { |
+ observer.Check(status); |
+ all_zoomed_correctly = all_zoomed_correctly && observer.zoomed_correctly; |
+ } |
+ |
+ if (all_zoomed_correctly) |
+ break; |
+ } |
+} |
+ |
+IN_PROC_BROWSER_TEST_F(IFrameZoomBrowserTest, SubframesZoomProperly) { |
+ SETUP_A_B_A_NESTED_FRAMES(); |
+ |
+ const double new_zoom_factor = 2.5; |
+ { |
+ DOMMessageQueue msg_queue; |
+ |
+ const double kTolerance = 0.1; |
+ std::vector<FrameResizeObserver> frame_observers; |
+ frame_observers.emplace_back(child, "child", |
+ scale_one_child_width, kTolerance); |
+ frame_observers.emplace_back(grandchild, "grandchild", |
+ scale_one_grandchild_width, kTolerance); |
+ |
+ const double new_zoom_level = |
+ default_zoom_level + ZoomFactorToZoomLevel(new_zoom_factor); |
+ host_zoom_map->SetZoomLevelForHost(top_level_host, new_zoom_level); |
+ |
+ WaitAndCheckFrameZoom(msg_queue, frame_observers); |
+ } |
+ |
+ EXPECT_DOUBLE_EQ( |
+ new_zoom_factor, |
+ GetMainframeZoomFactor(web_contents(), main_frame_window_border)); |
+} |
+ |
+IN_PROC_BROWSER_TEST_F(IFrameZoomBrowserTest, SubframesDontZoomIndependently) { |
+ SETUP_A_B_A_NESTED_FRAMES(); |
+ |
+ const double new_zoom_factor = 2.0; |
+ const double new_zoom_level = |
+ default_zoom_level + ZoomFactorToZoomLevel(new_zoom_factor); |
+ |
+ // This should not cause the nested iframe to change its zoom. |
+ host_zoom_map->SetZoomLevelForHost("b.com", new_zoom_level); |
+ |
+ EXPECT_DOUBLE_EQ( |
+ 1.0, GetMainframeZoomFactor(web_contents(), main_frame_window_border)); |
+ EXPECT_EQ(scale_one_child_width, GetSubframeWidth(child)); |
+ EXPECT_EQ(scale_one_grandchild_width, GetSubframeWidth(grandchild)); |
+ |
+ // When we navigate so that b.com is the top-level site, then it has the |
+ // expected zoom. |
+ EXPECT_TRUE(NavigateToURL( |
+ shell(), embedded_test_server()->GetURL( |
+ "b.com", "/cross_site_iframe_factory.html?b"))); |
+ EXPECT_DOUBLE_EQ( |
+ new_zoom_factor, |
+ GetMainframeZoomFactor(web_contents(), main_frame_window_border)); |
+} |
+ |
+IN_PROC_BROWSER_TEST_F(IFrameZoomBrowserTest, AllFramesGetDefaultZoom) { |
+ SETUP_A_B_A_NESTED_FRAMES(); |
+ |
+ const double new_default_zoom_factor = 2.0; |
+ { |
+ DOMMessageQueue msg_queue; |
+ |
+ const double kTolerance = 0.1; |
+ std::vector<FrameResizeObserver> frame_observers; |
+ frame_observers.emplace_back(child, "child", |
+ scale_one_child_width, kTolerance); |
+ frame_observers.emplace_back(grandchild, "grandchild", |
+ scale_one_grandchild_width, kTolerance); |
+ |
+ const double new_default_zoom_level = |
+ default_zoom_level + ZoomFactorToZoomLevel(new_default_zoom_factor); |
+ |
+ host_zoom_map->SetZoomLevelForHost("b.com", new_default_zoom_level + 1.0); |
+ host_zoom_map->SetDefaultZoomLevel(new_default_zoom_level); |
+ |
+ WaitAndCheckFrameZoom(msg_queue, frame_observers); |
+ } |
+ EXPECT_DOUBLE_EQ( |
+ new_default_zoom_factor, |
+ GetMainframeZoomFactor(web_contents(), main_frame_window_border)); |
+} |
+ |
+IN_PROC_BROWSER_TEST_F(IFrameZoomBrowserTest, SiblingFramesZoom) { |
+ std::string top_level_host("a.com"); |
+ GURL main_url(embedded_test_server()->GetURL( |
+ top_level_host, "/cross_site_iframe_factory.html?a(b,b)")); |
+ EXPECT_TRUE(NavigateToURL(shell(), main_url)); |
+ NavigationEntry* entry = |
+ web_contents()->GetController().GetLastCommittedEntry(); |
+ ASSERT_TRUE(entry); |
+ GURL loaded_url = HostZoomMap::GetURLFromEntry(entry); |
+ EXPECT_EQ(top_level_host, loaded_url.host()); |
alexmos
2016/04/25 22:43:27
Maybe it's enough to have coverage for lines 252-2
alexmos
2016/04/27 23:39:51
Just making sure you saw this -- though it's also
|
+ |
+ FrameTreeNode* root = |
+ static_cast<WebContentsImpl*>(web_contents())->GetFrameTree()->root(); |
+ RenderFrameHostImpl* child1 = root->child_at(0)->current_frame_host(); |
+ RenderFrameHostImpl* child2 = root->child_at(1)->current_frame_host(); |
+ |
+ /* The following calls must be made when the page's scale factor = 1.0.*/ |
alexmos
2016/04/25 22:43:27
Any reason not to use // instead of /* */ here for
wjmaclean
2016/04/27 13:43:57
This migrated from a macro, hence the old-style co
|
+ double scale_one_child1_width = GetSubframeWidth(child1); |
+ double scale_one_child2_width = GetSubframeWidth(child2); |
+ double main_frame_window_border = GetMainframeWindowBorder(web_contents()); |
+ |
+ HostZoomMap* host_zoom_map = HostZoomMap::GetForWebContents(web_contents()); |
+ double default_zoom_level = host_zoom_map->GetDefaultZoomLevel(); |
+ EXPECT_EQ(0.0, default_zoom_level); |
+ |
+ EXPECT_DOUBLE_EQ( |
+ 1.0, GetMainframeZoomFactor(web_contents(), main_frame_window_border)); |
+ |
+ const double new_zoom_factor = 2.5; |
+ { |
+ DOMMessageQueue msg_queue; |
+ |
+ const double kTolerance = 0.1; |
alexmos
2016/04/25 22:43:27
How about moving this to be declared only once som
wjmaclean
2016/04/27 13:43:57
Done.
|
+ std::vector<FrameResizeObserver> frame_observers; |
+ frame_observers.emplace_back(child1, "child1", |
+ scale_one_child1_width, kTolerance); |
+ frame_observers.emplace_back(child2, "child2", |
+ scale_one_child2_width, kTolerance); |
+ |
+ const double new_zoom_level = |
+ default_zoom_level + ZoomFactorToZoomLevel(new_zoom_factor); |
+ host_zoom_map->SetZoomLevelForHost(top_level_host, new_zoom_level); |
+ |
+ WaitAndCheckFrameZoom(msg_queue, frame_observers); |
+ } |
+ |
+ EXPECT_DOUBLE_EQ( |
+ new_zoom_factor, |
+ GetMainframeZoomFactor(web_contents(), main_frame_window_border)); |
+} |
+ |
+IN_PROC_BROWSER_TEST_F(IFrameZoomBrowserTest, SubframeRetainsZoomOnNavigation) { |
+ std::string top_level_host("a.com"); |
+ GURL main_url(embedded_test_server()->GetURL( |
+ top_level_host, "/cross_site_iframe_factory.html?a(b)")); |
+ EXPECT_TRUE(NavigateToURL(shell(), main_url)); |
+ NavigationEntry* entry = |
+ web_contents()->GetController().GetLastCommittedEntry(); |
+ ASSERT_TRUE(entry); |
+ GURL loaded_url = HostZoomMap::GetURLFromEntry(entry); |
+ EXPECT_EQ(top_level_host, loaded_url.host()); |
+ |
+ FrameTreeNode* root = |
+ static_cast<WebContentsImpl*>(web_contents())->GetFrameTree()->root(); |
+ RenderFrameHostImpl* child = root->child_at(0)->current_frame_host(); |
+ |
+ /* The following calls must be made when the page's scale factor = 1.0.*/ |
+ double scale_one_child_width = GetSubframeWidth(child); |
+ double main_frame_window_border = GetMainframeWindowBorder(web_contents()); |
+ |
+ HostZoomMap* host_zoom_map = HostZoomMap::GetForWebContents(web_contents()); |
+ double default_zoom_level = host_zoom_map->GetDefaultZoomLevel(); |
+ EXPECT_EQ(0.0, default_zoom_level); |
+ |
+ EXPECT_DOUBLE_EQ( |
+ 1.0, GetMainframeZoomFactor(web_contents(), main_frame_window_border)); |
+ |
+ const double new_zoom_factor = 2.5; |
alexmos
2016/04/25 22:43:27
It probably doesn't make any difference, but maybe
wjmaclean
2016/04/27 13:43:57
Actually, I don't think it makes any difference, a
|
+ { |
+ DOMMessageQueue msg_queue; |
+ |
+ const double kTolerance = 0.1; |
+ std::vector<FrameResizeObserver> frame_observers; |
+ frame_observers.emplace_back(child, "child", |
+ scale_one_child_width, kTolerance); |
+ |
+ const double new_zoom_level = |
+ default_zoom_level + ZoomFactorToZoomLevel(new_zoom_factor); |
+ host_zoom_map->SetZoomLevelForHost(top_level_host, new_zoom_level); |
+ |
+ WaitAndCheckFrameZoom(msg_queue, frame_observers); |
+ } |
+ |
+ EXPECT_DOUBLE_EQ( |
+ new_zoom_factor, |
+ GetMainframeZoomFactor(web_contents(), main_frame_window_border)); |
+ |
+ // Navigate child frame, and make sure zoom is the same. |
alexmos
2016/04/25 22:43:27
nit: "Navigate the child frame cross-site"
wjmaclean
2016/04/27 13:43:57
Done.
|
+ TestNavigationObserver observer(web_contents()); |
+ GURL url = embedded_test_server()->GetURL("c.com", "/title1.html"); |
+ NavigateFrameToURL(root->child_at(0), url); |
+ EXPECT_TRUE(observer.last_navigation_succeeded()); |
+ EXPECT_EQ(url, observer.last_navigation_url()); |
+ |
+ // After the navigation, we expect the child sub-frame to have the same scale, |
+ // even if its domain has a different value specified. |
alexmos
2016/04/25 22:43:27
nit: "Check that the child frame maintained the sa
wjmaclean
2016/04/27 13:43:57
Done.
|
+ double new_child_width = |
+ GetSubframeWidth(root->child_at(0)->current_frame_host()); |
+ EXPECT_EQ(scale_one_child_width, new_child_width); |
+} |
+ |
+} // namespace content |