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