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