1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <vector>
6 
7 #include "base/strings/string_util.h"
8 #include "build/build_config.h"
9 #include "content/browser/renderer_host/frame_tree_node.h"
10 #include "content/browser/renderer_host/render_frame_host_impl.h"
11 #include "content/browser/web_contents/web_contents_impl.h"
12 #include "content/public/browser/host_zoom_map.h"
13 #include "content/public/browser/navigation_entry.h"
14 #include "content/public/browser/notification_service.h"
15 #include "content/public/browser/notification_types.h"
16 #include "content/public/test/browser_test.h"
17 #include "content/public/test/browser_test_utils.h"
18 #include "content/public/test/content_browser_test.h"
19 #include "content/public/test/content_browser_test_utils.h"
20 #include "content/public/test/test_navigation_observer.h"
21 #include "content/shell/browser/shell.h"
22 #include "content/test/content_browser_test_utils_internal.h"
23 #include "net/dns/mock_host_resolver.h"
24 #include "testing/gtest/include/gtest/gtest.h"
25 #include "third_party/blink/public/common/page/page_zoom.h"
26 #include "url/gurl.h"
27 
28 namespace content {
29 
30 // This class contains basic tests of zoom functionality.
31 class ZoomBrowserTest : public ContentBrowserTest {
32  public:
ZoomBrowserTest()33   ZoomBrowserTest() {}
34 
35  protected:
SetUpOnMainThread()36   void SetUpOnMainThread() override {
37     host_resolver()->AddRule("*", "127.0.0.1");
38     SetupCrossSiteRedirector(embedded_test_server());
39     ASSERT_TRUE(embedded_test_server()->Start());
40   }
41 
web_contents()42   WebContentsImpl* web_contents() {
43     return static_cast<WebContentsImpl*>(shell()->web_contents());
44   }
45 };
46 
47 
48 // This class contains tests to make sure that subframes zoom in a manner
49 // consistent with the top-level frame, even when the subframes are cross-site.
50 // Particular things we want to make sure of:
51 //
52 // * Subframes should always have the same zoom level as their main frame, even
53 // if the subframe's domain has a different zoom level stored in HostZoomMap.
54 //
55 // * The condition above should continue to hold after a navigation of the
56 // subframe.
57 //
58 // * Zoom changes applied to the mainframe should propagate to all subframes,
59 // regardless of whether they are same site or cross-site to the frame they are
60 // children of.
61 //
62 // The tests in this file rely on the notion that, when a page zooms, that
63 // subframes have both (1) a change in their frame rect, and (2) a change in
64 // their frame's scale. Since the page should scale as a unit, this means the
65 // innerWidth value of any subframe should be the same before and after the
66 // zoom (though it may transiently take on a different value). The
67 // FrameSizeObserver serves to watch for onresize events, and observes when
68 // the innerWidth is correctly set.
69 class IFrameZoomBrowserTest : public ContentBrowserTest {
70  public:
IFrameZoomBrowserTest()71   IFrameZoomBrowserTest() {}
72 
73  protected:
SetUpOnMainThread()74   void SetUpOnMainThread() override {
75     host_resolver()->AddRule("*", "127.0.0.1");
76     SetupCrossSiteRedirector(embedded_test_server());
77     ASSERT_TRUE(embedded_test_server()->Start());
78   }
79 
web_contents()80   WebContentsImpl* web_contents() {
81     return static_cast<WebContentsImpl*>(shell()->web_contents());
82   }
83 };
84 
85 namespace {
86 
87 const double kTolerance = 0.1;  // In CSS pixels.
88 
GetMainframeWindowBorder(const ToRenderFrameHost & adapter)89 double GetMainframeWindowBorder(const ToRenderFrameHost& adapter) {
90   double border;
91   const char kGetMainframeBorder[] = "window.domAutomationController.send("
92       "window.outerWidth - window.innerWidth"
93       ");";
94   EXPECT_TRUE(
95       ExecuteScriptAndExtractDouble(adapter, kGetMainframeBorder, &border));
96   return border;
97 }
98 
GetMainFrameZoomFactor(const ToRenderFrameHost & adapter,double border)99 double GetMainFrameZoomFactor(const ToRenderFrameHost& adapter, double border) {
100   double zoom_factor;
101   EXPECT_TRUE(ExecuteScriptAndExtractDouble(
102       adapter,
103       JsReplace("window.domAutomationController.send("
104                 "   (window.outerWidth - $1) / window.innerWidth);",
105                 border),
106       &zoom_factor));
107   return zoom_factor;
108 }
109 
GetSubframeWidth(const ToRenderFrameHost & adapter)110 double GetSubframeWidth(const ToRenderFrameHost& adapter) {
111   double width;
112   EXPECT_TRUE(ExecuteScriptAndExtractDouble(
113       adapter, "window.domAutomationController.send(window.innerWidth);",
114       &width));
115   return width;
116 }
117 
118 // This struct is used to track changes to subframes after a main frame zoom
119 // change, so that we can test subframe inner widths with assurance that all the
120 // changes have finished propagating.
121 struct FrameResizeObserver {
FrameResizeObservercontent::__anon8d4a99be0111::FrameResizeObserver122   FrameResizeObserver(RenderFrameHost* host,
123                       std::string label,
124                       double inner_width,
125                       double tolerance)
126       : frame_host(host),
127         msg_label(std::move(label)),
128         zoomed_correctly(false),
129         expected_inner_width(inner_width),
130         tolerance(tolerance) {
131     SetupOnResizeCallback(host, msg_label);
132   }
133 
SetupOnResizeCallbackcontent::__anon8d4a99be0111::FrameResizeObserver134   void SetupOnResizeCallback(const ToRenderFrameHost& adapter,
135                              const std::string& label) {
136     const char kOnResizeCallbackSetup[] =
137         "document.body.onresize = function(){"
138         "  window.domAutomationController.send('%s ' + window.innerWidth);"
139         "};";
140     EXPECT_TRUE(ExecuteScript(
141         adapter, base::StringPrintf(kOnResizeCallbackSetup, label.c_str())));
142   }
143 
Checkcontent::__anon8d4a99be0111::FrameResizeObserver144   void Check(const std::string& status_msg) {
145     if (!base::StartsWith(status_msg, msg_label, base::CompareCase::SENSITIVE))
146       return;
147 
148     double inner_width = std::stod(status_msg.substr(msg_label.length() + 1));
149     zoomed_correctly = std::abs(expected_inner_width - inner_width) < tolerance;
150   }
151 
toThiscontent::__anon8d4a99be0111::FrameResizeObserver152   FrameResizeObserver* toThis() {return this;}
153 
154   RenderFrameHost* frame_host;
155   std::string msg_label;
156   bool zoomed_correctly;
157   double expected_inner_width;
158   double tolerance;
159 };
160 
161 // This struct is used to wait until a resize has occurred.
162 struct ResizeObserver {
ResizeObservercontent::__anon8d4a99be0111::ResizeObserver163   ResizeObserver(RenderFrameHost* host)
164       : frame_host(host) {
165     SetupOnResizeCallback(host);
166   }
167 
SetupOnResizeCallbackcontent::__anon8d4a99be0111::ResizeObserver168   void SetupOnResizeCallback(const ToRenderFrameHost& adapter) {
169     const char kOnResizeCallbackSetup[] =
170         "document.body.onresize = function(){"
171         "  window.domAutomationController.send('Resized');"
172         "};";
173     EXPECT_TRUE(ExecuteScript(
174         adapter, kOnResizeCallbackSetup));
175   }
176 
IsResizeCallbackcontent::__anon8d4a99be0111::ResizeObserver177   bool IsResizeCallback(const std::string& status_msg) {
178     return status_msg == "Resized";
179   }
180 
181   RenderFrameHost* frame_host;
182 };
183 
WaitForResize(DOMMessageQueue & msg_queue,ResizeObserver & observer)184 void WaitForResize(DOMMessageQueue& msg_queue, ResizeObserver& observer) {
185   std::string status;
186   while (msg_queue.WaitForMessage(&status)) {
187     // Strip the double quotes from the message.
188     status = status.substr(1, status.length() -2);
189     if (observer.IsResizeCallback(status))
190       break;
191   }
192 }
193 
WaitAndCheckFrameZoom(DOMMessageQueue & msg_queue,std::vector<FrameResizeObserver> & frame_observers)194 void WaitAndCheckFrameZoom(
195     DOMMessageQueue& msg_queue,
196     std::vector<FrameResizeObserver>& frame_observers) {
197   std::string status;
198   while (msg_queue.WaitForMessage(&status)) {
199     // Strip the double quotes from the message.
200     status = status.substr(1, status.length() -2);
201 
202     bool all_zoomed_correctly = true;
203 
204     // Use auto& to operate on a reference, and not a copy.
205     for (auto& observer : frame_observers) {
206       observer.Check(status);
207       all_zoomed_correctly = all_zoomed_correctly && observer.zoomed_correctly;
208     }
209 
210     if (all_zoomed_correctly)
211       break;
212   }
213 }
214 
215 }  // namespace
216 
IN_PROC_BROWSER_TEST_F(ZoomBrowserTest,ZoomPreservedOnReload)217 IN_PROC_BROWSER_TEST_F(ZoomBrowserTest, ZoomPreservedOnReload) {
218   std::string top_level_host("a.com");
219 
220   GURL main_url(embedded_test_server()->GetURL(
221       top_level_host, "/cross_site_iframe_factory.html?a(b(a))"));
222   EXPECT_TRUE(NavigateToURL(shell(), main_url));
223   NavigationEntry* entry =
224       web_contents()->GetController().GetLastCommittedEntry();
225   ASSERT_TRUE(entry);
226   GURL loaded_url = HostZoomMap::GetURLFromEntry(entry);
227   EXPECT_EQ(top_level_host, loaded_url.host());
228 
229   FrameTreeNode* root =
230       static_cast<WebContentsImpl*>(web_contents())->GetFrameTree()->root();
231   double main_frame_window_border = GetMainframeWindowBorder(web_contents());
232 
233   HostZoomMap* host_zoom_map = HostZoomMap::GetForWebContents(web_contents());
234   double default_zoom_level = host_zoom_map->GetDefaultZoomLevel();
235   EXPECT_EQ(0.0, default_zoom_level);
236 
237   EXPECT_DOUBLE_EQ(
238       1.0, GetMainFrameZoomFactor(web_contents(), main_frame_window_border));
239 
240   const double new_zoom_factor = 2.5;
241 
242   // Set the new zoom, wait for the page to be resized, and sanity-check that
243   // the zoom was applied.
244   {
245     DOMMessageQueue msg_queue;
246     ResizeObserver observer(root->current_frame_host());
247 
248     const double new_zoom_level =
249         default_zoom_level + blink::PageZoomFactorToZoomLevel(new_zoom_factor);
250     host_zoom_map->SetZoomLevelForHost(top_level_host, new_zoom_level);
251 
252     WaitForResize(msg_queue, observer);
253   }
254 
255   // Make this comparison approximate for Nexus5X test;
256   // https://crbug.com/622858.
257   EXPECT_NEAR(
258       new_zoom_factor,
259       GetMainFrameZoomFactor(web_contents(), main_frame_window_border),
260       0.01);
261 
262   // Now the actual test: Reload the page and check that the main frame is
263   // still properly zoomed.
264   WindowedNotificationObserver load_stop_observer(
265       NOTIFICATION_LOAD_STOP,
266       NotificationService::AllSources());
267   shell()->Reload();
268   load_stop_observer.Wait();
269 
270   EXPECT_NEAR(
271       new_zoom_factor,
272       GetMainFrameZoomFactor(web_contents(), main_frame_window_border),
273       0.01);
274 }
275 
IN_PROC_BROWSER_TEST_F(IFrameZoomBrowserTest,SubframesZoomProperly)276 IN_PROC_BROWSER_TEST_F(IFrameZoomBrowserTest, SubframesZoomProperly) {
277   std::string top_level_host("a.com");
278   GURL main_url(embedded_test_server()->GetURL(
279       top_level_host, "/cross_site_iframe_factory.html?a(b(a))"));
280   EXPECT_TRUE(NavigateToURL(shell(), main_url));
281   NavigationEntry* entry =
282       web_contents()->GetController().GetLastCommittedEntry();
283   ASSERT_TRUE(entry);
284   GURL loaded_url = HostZoomMap::GetURLFromEntry(entry);
285   EXPECT_EQ(top_level_host, loaded_url.host());
286 
287   FrameTreeNode* root =
288       static_cast<WebContentsImpl*>(web_contents())->GetFrameTree()->root();
289   RenderFrameHostImpl* child = root->child_at(0)->current_frame_host();
290   RenderFrameHostImpl* grandchild =
291       root->child_at(0)->child_at(0)->current_frame_host();
292 
293   // The following calls must be made when the page's scale factor = 1.0.
294   double scale_one_child_width = GetSubframeWidth(child);
295   double scale_one_grandchild_width = GetSubframeWidth(grandchild);
296   double main_frame_window_border = GetMainframeWindowBorder(web_contents());
297 
298   HostZoomMap* host_zoom_map = HostZoomMap::GetForWebContents(web_contents());
299   double default_zoom_level = host_zoom_map->GetDefaultZoomLevel();
300   EXPECT_EQ(0.0, default_zoom_level);
301 
302   EXPECT_DOUBLE_EQ(
303       1.0, GetMainFrameZoomFactor(web_contents(), main_frame_window_border));
304 
305   const double new_zoom_factor = 2.5;
306   {
307     DOMMessageQueue msg_queue;
308 
309     std::vector<FrameResizeObserver> frame_observers;
310     frame_observers.emplace_back(child, "child",
311                                  scale_one_child_width, kTolerance);
312     frame_observers.emplace_back(grandchild, "grandchild",
313                                  scale_one_grandchild_width, kTolerance);
314 
315     const double new_zoom_level =
316         default_zoom_level + blink::PageZoomFactorToZoomLevel(new_zoom_factor);
317     host_zoom_map->SetZoomLevelForHost(top_level_host, new_zoom_level);
318 
319     WaitAndCheckFrameZoom(msg_queue, frame_observers);
320   }
321 
322   // Make this comparison approximate for Nexus5X test;
323   // https://crbug.com/622858.
324   EXPECT_NEAR(
325       new_zoom_factor,
326       GetMainFrameZoomFactor(web_contents(), main_frame_window_border),
327       0.01);
328 }
329 
IN_PROC_BROWSER_TEST_F(IFrameZoomBrowserTest,SubframesDontZoomIndependently)330 IN_PROC_BROWSER_TEST_F(IFrameZoomBrowserTest, SubframesDontZoomIndependently) {
331   std::string top_level_host("a.com");
332   GURL main_url(embedded_test_server()->GetURL(
333       top_level_host, "/cross_site_iframe_factory.html?a(b(a))"));
334   EXPECT_TRUE(NavigateToURL(shell(), main_url));
335   NavigationEntry* entry =
336       web_contents()->GetController().GetLastCommittedEntry();
337   ASSERT_TRUE(entry);
338   GURL loaded_url = HostZoomMap::GetURLFromEntry(entry);
339   EXPECT_EQ(top_level_host, loaded_url.host());
340 
341   FrameTreeNode* root =
342       static_cast<WebContentsImpl*>(web_contents())->GetFrameTree()->root();
343   RenderFrameHostImpl* child = root->child_at(0)->current_frame_host();
344   RenderFrameHostImpl* grandchild =
345       root->child_at(0)->child_at(0)->current_frame_host();
346 
347   // The following calls must be made when the page's scale factor = 1.0.
348   double scale_one_child_width = GetSubframeWidth(child);
349   double scale_one_grandchild_width = GetSubframeWidth(grandchild);
350   double main_frame_window_border = GetMainframeWindowBorder(web_contents());
351 
352   HostZoomMap* host_zoom_map = HostZoomMap::GetForWebContents(web_contents());
353   double default_zoom_level = host_zoom_map->GetDefaultZoomLevel();
354   EXPECT_EQ(0.0, default_zoom_level);
355 
356   EXPECT_DOUBLE_EQ(
357       1.0, GetMainFrameZoomFactor(web_contents(), main_frame_window_border));
358 
359   const double new_zoom_factor = 2.0;
360   const double new_zoom_level =
361       default_zoom_level + blink::PageZoomFactorToZoomLevel(new_zoom_factor);
362 
363   // This should not cause the nested iframe to change its zoom.
364   host_zoom_map->SetZoomLevelForHost("b.com", new_zoom_level);
365 
366   EXPECT_DOUBLE_EQ(
367       1.0, GetMainFrameZoomFactor(web_contents(), main_frame_window_border));
368   EXPECT_EQ(scale_one_child_width, GetSubframeWidth(child));
369   EXPECT_EQ(scale_one_grandchild_width, GetSubframeWidth(grandchild));
370 
371   // When we navigate so that b.com is the top-level site, then it has the
372   // expected zoom.
373   GURL new_url = embedded_test_server()->GetURL("b.com", "/title1.html");
374   EXPECT_TRUE(NavigateToURL(shell(), new_url));
375   EXPECT_DOUBLE_EQ(
376       new_zoom_factor,
377       GetMainFrameZoomFactor(web_contents(), main_frame_window_border));
378 }
379 
IN_PROC_BROWSER_TEST_F(IFrameZoomBrowserTest,AllFramesGetDefaultZoom)380 IN_PROC_BROWSER_TEST_F(IFrameZoomBrowserTest, AllFramesGetDefaultZoom) {
381   std::string top_level_host("a.com");
382   GURL main_url(embedded_test_server()->GetURL(
383       top_level_host, "/cross_site_iframe_factory.html?a(b(a))"));
384   EXPECT_TRUE(NavigateToURL(shell(), main_url));
385   NavigationEntry* entry =
386       web_contents()->GetController().GetLastCommittedEntry();
387   ASSERT_TRUE(entry);
388   GURL loaded_url = HostZoomMap::GetURLFromEntry(entry);
389   EXPECT_EQ(top_level_host, loaded_url.host());
390 
391   FrameTreeNode* root =
392       static_cast<WebContentsImpl*>(web_contents())->GetFrameTree()->root();
393   RenderFrameHostImpl* child = root->child_at(0)->current_frame_host();
394   RenderFrameHostImpl* grandchild =
395       root->child_at(0)->child_at(0)->current_frame_host();
396 
397   // The following calls must be made when the page's scale factor = 1.0.
398   double scale_one_child_width = GetSubframeWidth(child);
399   double scale_one_grandchild_width = GetSubframeWidth(grandchild);
400   double main_frame_window_border = GetMainframeWindowBorder(web_contents());
401 
402   HostZoomMap* host_zoom_map = HostZoomMap::GetForWebContents(web_contents());
403   double default_zoom_level = host_zoom_map->GetDefaultZoomLevel();
404   EXPECT_EQ(0.0, default_zoom_level);
405 
406   EXPECT_DOUBLE_EQ(
407       1.0, GetMainFrameZoomFactor(web_contents(), main_frame_window_border));
408 
409   const double new_default_zoom_factor = 2.0;
410   {
411     DOMMessageQueue msg_queue;
412 
413     std::vector<FrameResizeObserver> frame_observers;
414     frame_observers.emplace_back(child, "child",
415                                  scale_one_child_width, kTolerance);
416     frame_observers.emplace_back(grandchild, "grandchild",
417                                  scale_one_grandchild_width, kTolerance);
418 
419     const double new_default_zoom_level =
420         default_zoom_level +
421         blink::PageZoomFactorToZoomLevel(new_default_zoom_factor);
422 
423     host_zoom_map->SetZoomLevelForHost("b.com", new_default_zoom_level + 1.0);
424     host_zoom_map->SetDefaultZoomLevel(new_default_zoom_level);
425 
426     WaitAndCheckFrameZoom(msg_queue, frame_observers);
427   }
428   // Make this comparison approximate for Nexus5X test;
429   // https://crbug.com/622858.
430   EXPECT_NEAR(
431       new_default_zoom_factor,
432       GetMainFrameZoomFactor(web_contents(), main_frame_window_border),
433       0.01
434   );
435 }
436 
437 // Flaky on mac, https://crbug.com/1055282
438 #if defined(OS_MAC)
439 #define MAYBE_SiblingFramesZoom DISABLED_SiblingFramesZoom
440 #else
441 #define MAYBE_SiblingFramesZoom SiblingFramesZoom
442 #endif
IN_PROC_BROWSER_TEST_F(IFrameZoomBrowserTest,MAYBE_SiblingFramesZoom)443 IN_PROC_BROWSER_TEST_F(IFrameZoomBrowserTest, MAYBE_SiblingFramesZoom) {
444   std::string top_level_host("a.com");
445   GURL main_url(embedded_test_server()->GetURL(
446       top_level_host, "/cross_site_iframe_factory.html?a(b,b)"));
447   EXPECT_TRUE(NavigateToURL(shell(), main_url));
448   NavigationEntry* entry =
449       web_contents()->GetController().GetLastCommittedEntry();
450   ASSERT_TRUE(entry);
451   GURL loaded_url = HostZoomMap::GetURLFromEntry(entry);
452   EXPECT_EQ(top_level_host, loaded_url.host());
453 
454   FrameTreeNode* root =
455       static_cast<WebContentsImpl*>(web_contents())->GetFrameTree()->root();
456   RenderFrameHostImpl* child1 = root->child_at(0)->current_frame_host();
457   RenderFrameHostImpl* child2 = root->child_at(1)->current_frame_host();
458 
459   // The following calls must be made when the page's scale factor = 1.0.
460   double scale_one_child1_width = GetSubframeWidth(child1);
461   double scale_one_child2_width = GetSubframeWidth(child2);
462   double main_frame_window_border = GetMainframeWindowBorder(web_contents());
463 
464   HostZoomMap* host_zoom_map = HostZoomMap::GetForWebContents(web_contents());
465   double default_zoom_level = host_zoom_map->GetDefaultZoomLevel();
466   EXPECT_EQ(0.0, default_zoom_level);
467 
468   EXPECT_DOUBLE_EQ(
469       1.0, GetMainFrameZoomFactor(web_contents(), main_frame_window_border));
470 
471   const double new_zoom_factor = 2.5;
472   {
473     DOMMessageQueue msg_queue;
474 
475     std::vector<FrameResizeObserver> frame_observers;
476     frame_observers.emplace_back(child1, "child1",
477                                  scale_one_child1_width, kTolerance);
478     frame_observers.emplace_back(child2, "child2",
479                                  scale_one_child2_width, kTolerance);
480 
481     const double new_zoom_level =
482         default_zoom_level + blink::PageZoomFactorToZoomLevel(new_zoom_factor);
483     host_zoom_map->SetZoomLevelForHost(top_level_host, new_zoom_level);
484 
485     WaitAndCheckFrameZoom(msg_queue, frame_observers);
486   }
487 
488   // Make this comparison approximate for Nexus5X test;
489   // https://crbug.com/622858.
490   EXPECT_NEAR(
491       new_zoom_factor,
492       GetMainFrameZoomFactor(web_contents(), main_frame_window_border),
493       0.01);
494 }
495 
IN_PROC_BROWSER_TEST_F(IFrameZoomBrowserTest,SubframeRetainsZoomOnNavigation)496 IN_PROC_BROWSER_TEST_F(IFrameZoomBrowserTest, SubframeRetainsZoomOnNavigation) {
497   std::string top_level_host("a.com");
498   GURL main_url(embedded_test_server()->GetURL(
499       top_level_host, "/cross_site_iframe_factory.html?a(b)"));
500   EXPECT_TRUE(NavigateToURL(shell(), main_url));
501   NavigationEntry* entry =
502       web_contents()->GetController().GetLastCommittedEntry();
503   ASSERT_TRUE(entry);
504   GURL loaded_url = HostZoomMap::GetURLFromEntry(entry);
505   EXPECT_EQ(top_level_host, loaded_url.host());
506 
507   FrameTreeNode* root =
508       static_cast<WebContentsImpl*>(web_contents())->GetFrameTree()->root();
509   RenderFrameHostImpl* child = root->child_at(0)->current_frame_host();
510 
511   // The following calls must be made when the page's scale factor = 1.0.
512   double scale_one_child_width = GetSubframeWidth(child);
513   double main_frame_window_border = GetMainframeWindowBorder(web_contents());
514 
515   HostZoomMap* host_zoom_map = HostZoomMap::GetForWebContents(web_contents());
516   double default_zoom_level = host_zoom_map->GetDefaultZoomLevel();
517   EXPECT_EQ(0.0, default_zoom_level);
518 
519   EXPECT_DOUBLE_EQ(
520       1.0, GetMainFrameZoomFactor(web_contents(), main_frame_window_border));
521 
522   const double new_zoom_factor = 0.5;
523   {
524     DOMMessageQueue msg_queue;
525 
526     std::vector<FrameResizeObserver> frame_observers;
527     frame_observers.emplace_back(child, "child",
528                                  scale_one_child_width, kTolerance);
529 
530     const double new_zoom_level =
531         default_zoom_level + blink::PageZoomFactorToZoomLevel(new_zoom_factor);
532     host_zoom_map->SetZoomLevelForHost(top_level_host, new_zoom_level);
533 
534     WaitAndCheckFrameZoom(msg_queue, frame_observers);
535   }
536 
537   // Make this comparison approximate for Nexus5X test;
538   // https://crbug.com/622858.
539   EXPECT_NEAR(
540       new_zoom_factor,
541       GetMainFrameZoomFactor(web_contents(), main_frame_window_border),
542       0.01
543   );
544 
545   // Navigate child frame cross site, and make sure zoom is the same.
546   TestNavigationObserver observer(web_contents());
547   GURL url = embedded_test_server()->GetURL("c.com", "/title1.html");
548   EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), url));
549   EXPECT_TRUE(observer.last_navigation_succeeded());
550   EXPECT_EQ(url, observer.last_navigation_url());
551 
552   // Check that the child frame maintained the same scale after navigating
553   // cross-site.
554   double new_child_width =
555       GetSubframeWidth(root->child_at(0)->current_frame_host());
556   EXPECT_EQ(scale_one_child_width, new_child_width);
557 }
558 
559 // http://crbug.com/609213
IN_PROC_BROWSER_TEST_F(IFrameZoomBrowserTest,RedirectToPageWithSubframeZoomsCorrectly)560 IN_PROC_BROWSER_TEST_F(IFrameZoomBrowserTest,
561                        RedirectToPageWithSubframeZoomsCorrectly) {
562   std::string initial_host("a.com");
563   std::string redirected_host("b.com");
564   EXPECT_TRUE(NavigateToURL(shell(), GURL(embedded_test_server()->GetURL(
565                                          initial_host, "/title2.html"))));
566   double main_frame_window_border = GetMainframeWindowBorder(web_contents());
567   EXPECT_DOUBLE_EQ(
568       1.0, GetMainFrameZoomFactor(web_contents(), main_frame_window_border));
569 
570   // Set a zoom level for b.com before we navigate to it.
571   const double kZoomFactorForRedirectedHost = 1.5;
572   HostZoomMap* host_zoom_map = HostZoomMap::GetForWebContents(web_contents());
573   host_zoom_map->SetZoomLevelForHost(
574       redirected_host,
575       blink::PageZoomFactorToZoomLevel(kZoomFactorForRedirectedHost));
576 
577   // Navigation to a.com doesn't change the zoom level, but when it redirects
578   // to b.com, and then a subframe loads, the zoom should change.
579   GURL redirect_url(embedded_test_server()->GetURL(
580       redirected_host, "/cross_site_iframe_factory.html?b(b)"));
581   GURL url(embedded_test_server()->GetURL(
582       initial_host, "/client-redirect?" + redirect_url.spec()));
583 
584   NavigateToURLBlockUntilNavigationsComplete(shell(), url, 2);
585   EXPECT_TRUE(IsLastCommittedEntryOfPageType(web_contents(), PAGE_TYPE_NORMAL));
586   EXPECT_EQ(redirect_url, web_contents()->GetLastCommittedURL());
587 
588   EXPECT_NEAR(kZoomFactorForRedirectedHost,
589               GetMainFrameZoomFactor(web_contents(), main_frame_window_border),
590               0.01);
591 }
592 
593 // Tests that on cross-site navigation from a page that has a subframe, the
594 // appropriate zoom is applied to the new page.
595 // crbug.com/673065
IN_PROC_BROWSER_TEST_F(IFrameZoomBrowserTest,SubframesDontBreakConnectionToRenderer)596 IN_PROC_BROWSER_TEST_F(IFrameZoomBrowserTest,
597                        SubframesDontBreakConnectionToRenderer) {
598   std::string top_level_host("a.com");
599   GURL main_url(embedded_test_server()->GetURL(
600       top_level_host, "/page_with_iframe_and_link.html"));
601   EXPECT_TRUE(NavigateToURL(shell(), main_url));
602   NavigationEntry* entry =
603       web_contents()->GetController().GetLastCommittedEntry();
604   ASSERT_TRUE(entry);
605   GURL loaded_url = HostZoomMap::GetURLFromEntry(entry);
606   EXPECT_EQ(top_level_host, loaded_url.host());
607 
608   // The following calls must be made when the page's scale factor = 1.0.
609   double main_frame_window_border = GetMainframeWindowBorder(web_contents());
610 
611   HostZoomMap* host_zoom_map = HostZoomMap::GetForWebContents(web_contents());
612   double default_zoom_level = host_zoom_map->GetDefaultZoomLevel();
613   EXPECT_EQ(0.0, default_zoom_level);
614   EXPECT_DOUBLE_EQ(
615       1.0, GetMainFrameZoomFactor(web_contents(), main_frame_window_border));
616 
617   // Set a zoom for a host that will be navigated to below.
618   const double new_zoom_factor = 2.0;
619   const double new_zoom_level =
620       default_zoom_level + blink::PageZoomFactorToZoomLevel(new_zoom_factor);
621   host_zoom_map->SetZoomLevelForHost("foo.com", new_zoom_level);
622 
623   // Navigate forward in the same RFH to a site with that host via a
624   // renderer-initiated navigation.
625   {
626     const char kReplacePortNumber[] =
627         "window.domAutomationController.send(setPortNumber(%d));";
628     uint16_t port_number = embedded_test_server()->port();
629     bool success = false;
630     EXPECT_TRUE(ExecuteScriptAndExtractBool(
631         shell(), base::StringPrintf(kReplacePortNumber, port_number),
632         &success));
633     TestNavigationObserver observer(shell()->web_contents());
634     GURL url = embedded_test_server()->GetURL("foo.com", "/title2.html");
635     success = false;
636     EXPECT_TRUE(ExecuteScriptAndExtractBool(
637         shell(), "window.domAutomationController.send(clickCrossSiteLink());",
638         &success));
639     EXPECT_TRUE(success);
640     EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
641     EXPECT_EQ(url, observer.last_navigation_url());
642     EXPECT_TRUE(observer.last_navigation_succeeded());
643   }
644 
645   // Check that the requested zoom has been applied to the new site.
646   // NOTE: Local observation on Linux has shown that this comparison has to be
647   // approximate. As the common failure mode would be that the zoom is ~1
648   // instead of ~2, this approximation shouldn't be problematic.
649   EXPECT_NEAR(
650       new_zoom_factor,
651       GetMainFrameZoomFactor(web_contents(), main_frame_window_border),
652       .1);
653 }
654 
655 }  // namespace content
656