Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(84)

Side by Side Diff: content/browser/iframe_zoom_browsertest.cc

Issue 2608213003: Add browsertest that zoom is preserved on reload (Closed)
Patch Set: Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | content/browser/zoom_browsertest.cc » ('j') | content/browser/zoom_browsertest.cc » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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/common/page_zoom.h"
14 #include "content/public/test/browser_test_utils.h"
15 #include "content/public/test/content_browser_test.h"
16 #include "content/public/test/content_browser_test_utils.h"
17 #include "content/public/test/test_navigation_observer.h"
18 #include "content/shell/browser/shell.h"
19 #include "content/test/content_browser_test_utils_internal.h"
20 #include "net/dns/mock_host_resolver.h"
21 #include "testing/gtest/include/gtest/gtest.h"
22 #include "url/gurl.h"
23
24 namespace content {
25
26 // This file contains tests to make sure that subframes zoom in a manner
27 // consistent with the top-level frame, even when the subframes are cross-site.
28 // Particular things we want to make sure of:
29 //
30 // * Subframes should always have the same zoom level as their main frame, even
31 // if the subframe's domain has a different zoom level stored in HostZoomMap.
32 //
33 // * The condition above should continue to hold after a navigation of the
34 // subframe.
35 //
36 // * Zoom changes applied to the mainframe should propagate to all subframes,
37 // regardless of whether they are same site or cross-site to the frame they are
38 // children of.
39 //
40 // The tests in this file rely on the notion that, when a page zooms, that
41 // subframes have both (1) a change in their frame rect, and (2) a change in
42 // their frame's scale. Since the page should scale as a unit, this means the
43 // innerWidth value of any subframe should be the same before and after the
44 // zoom (though it may transiently take on a different value). The
45 // FrameSizeObserver serves to watch for onresize events, and observes when
46 // the innerWidth is correctly set.
47 class IFrameZoomBrowserTest : public ContentBrowserTest {
48 public:
49 IFrameZoomBrowserTest() {}
50
51 protected:
52 void SetUpOnMainThread() override {
53 host_resolver()->AddRule("*", "127.0.0.1");
54 SetupCrossSiteRedirector(embedded_test_server());
55 ASSERT_TRUE(embedded_test_server()->Start());
56 }
57
58 WebContentsImpl* web_contents() {
59 return static_cast<WebContentsImpl*>(shell()->web_contents());
60 }
61 };
62
63 namespace {
64
65 const double kTolerance = 0.1; // In CSS pixels.
66
67 double GetMainframeWindowBorder(const ToRenderFrameHost& adapter) {
68 double border;
69 const char kGetMainframeBorder[] = "window.domAutomationController.send("
70 "window.outerWidth - window.innerWidth"
71 ");";
72 EXPECT_TRUE(
73 ExecuteScriptAndExtractDouble(adapter, kGetMainframeBorder, &border));
74 return border;
75 }
76
77 double GetMainFrameZoomFactor(const ToRenderFrameHost& adapter, double border) {
78 const char kGetMainFrameZoomLevel[] =
79 "window.domAutomationController.send("
80 "(window.outerWidth - %f)/window.innerWidth"
81 ");";
82 double zoom_factor;
83 EXPECT_TRUE(ExecuteScriptAndExtractDouble(
84 adapter, base::StringPrintf(kGetMainFrameZoomLevel, border),
85 &zoom_factor));
86 return zoom_factor;
87 }
88
89 double GetSubframeWidth(const ToRenderFrameHost& adapter) {
90 double width;
91 EXPECT_TRUE(ExecuteScriptAndExtractDouble(
92 adapter, "window.domAutomationController.send(window.innerWidth);",
93 &width));
94 return width;
95 }
96
97 // This struct is used to track changes to subframes after a main frame zoom
98 // change, so that we can test subframe inner widths with assurance that all the
99 // changes have finished propagating.
100 struct FrameResizeObserver {
101 FrameResizeObserver(RenderFrameHost* host,
102 std::string label,
103 double inner_width,
104 double tolerance)
105 : frame_host(host),
106 msg_label(std::move(label)),
107 zoomed_correctly(false),
108 expected_inner_width(inner_width),
109 tolerance(tolerance) {
110 SetupOnResizeCallback(host, msg_label);
111 }
112
113 void SetupOnResizeCallback(const ToRenderFrameHost& adapter,
114 const std::string& label) {
115 const char kOnResizeCallbackSetup[] =
116 "document.body.onresize = function(){"
117 " window.domAutomationController.setAutomationId(0);"
118 " window.domAutomationController.send('%s ' + window.innerWidth);"
119 "};";
120 EXPECT_TRUE(ExecuteScript(
121 adapter, base::StringPrintf(kOnResizeCallbackSetup, label.c_str())));
122 }
123
124 void Check(const std::string& status_msg) {
125 if (!base::StartsWith(status_msg, msg_label, base::CompareCase::SENSITIVE))
126 return;
127
128 double inner_width = std::stod(status_msg.substr(msg_label.length() + 1));
129 zoomed_correctly = std::abs(expected_inner_width - inner_width) < tolerance;
130 }
131
132 FrameResizeObserver* toThis() {return this;}
133
134 RenderFrameHost* frame_host;
135 std::string msg_label;
136 bool zoomed_correctly;
137 double expected_inner_width;
138 double tolerance;
139 };
140
141 void WaitAndCheckFrameZoom(
142 DOMMessageQueue& msg_queue,
143 std::vector<FrameResizeObserver>& frame_observers) {
144 std::string status;
145 while (msg_queue.WaitForMessage(&status)) {
146 // Strip the double quotes from the message.
147 status = status.substr(1, status.length() -2);
148
149 bool all_zoomed_correctly = true;
150
151 // Use auto& to operate on a reference, and not a copy.
152 for (auto& observer : frame_observers) {
153 observer.Check(status);
154 all_zoomed_correctly = all_zoomed_correctly && observer.zoomed_correctly;
155 }
156
157 if (all_zoomed_correctly)
158 break;
159 }
160 }
161
162 } // namespace
163
164 IN_PROC_BROWSER_TEST_F(IFrameZoomBrowserTest, SubframesZoomProperly) {
165 std::string top_level_host("a.com");
166 GURL main_url(embedded_test_server()->GetURL(
167 top_level_host, "/cross_site_iframe_factory.html?a(b(a))"));
168 EXPECT_TRUE(NavigateToURL(shell(), main_url));
169 NavigationEntry* entry =
170 web_contents()->GetController().GetLastCommittedEntry();
171 ASSERT_TRUE(entry);
172 GURL loaded_url = HostZoomMap::GetURLFromEntry(entry);
173 EXPECT_EQ(top_level_host, loaded_url.host());
174
175 FrameTreeNode* root =
176 static_cast<WebContentsImpl*>(web_contents())->GetFrameTree()->root();
177 RenderFrameHostImpl* child = root->child_at(0)->current_frame_host();
178 RenderFrameHostImpl* grandchild =
179 root->child_at(0)->child_at(0)->current_frame_host();
180
181 // The following calls must be made when the page's scale factor = 1.0.
182 double scale_one_child_width = GetSubframeWidth(child);
183 double scale_one_grandchild_width = GetSubframeWidth(grandchild);
184 double main_frame_window_border = GetMainframeWindowBorder(web_contents());
185
186 HostZoomMap* host_zoom_map = HostZoomMap::GetForWebContents(web_contents());
187 double default_zoom_level = host_zoom_map->GetDefaultZoomLevel();
188 EXPECT_EQ(0.0, default_zoom_level);
189
190 EXPECT_DOUBLE_EQ(
191 1.0, GetMainFrameZoomFactor(web_contents(), main_frame_window_border));
192
193 const double new_zoom_factor = 2.5;
194 {
195 DOMMessageQueue msg_queue;
196
197 std::vector<FrameResizeObserver> frame_observers;
198 frame_observers.emplace_back(child, "child",
199 scale_one_child_width, kTolerance);
200 frame_observers.emplace_back(grandchild, "grandchild",
201 scale_one_grandchild_width, kTolerance);
202
203 const double new_zoom_level =
204 default_zoom_level + ZoomFactorToZoomLevel(new_zoom_factor);
205 host_zoom_map->SetZoomLevelForHost(top_level_host, new_zoom_level);
206
207 WaitAndCheckFrameZoom(msg_queue, frame_observers);
208 }
209
210 // Make this comparison approximate for Nexus5X test;
211 // https://crbug.com/622858.
212 EXPECT_NEAR(
213 new_zoom_factor,
214 GetMainFrameZoomFactor(web_contents(), main_frame_window_border),
215 0.01);
216 }
217
218 IN_PROC_BROWSER_TEST_F(IFrameZoomBrowserTest, SubframesDontZoomIndependently) {
219 std::string top_level_host("a.com");
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 RenderFrameHostImpl* child = root->child_at(0)->current_frame_host();
232 RenderFrameHostImpl* grandchild =
233 root->child_at(0)->child_at(0)->current_frame_host();
234
235 // The following calls must be made when the page's scale factor = 1.0.
236 double scale_one_child_width = GetSubframeWidth(child);
237 double scale_one_grandchild_width = GetSubframeWidth(grandchild);
238 double main_frame_window_border = GetMainframeWindowBorder(web_contents());
239
240 HostZoomMap* host_zoom_map = HostZoomMap::GetForWebContents(web_contents());
241 double default_zoom_level = host_zoom_map->GetDefaultZoomLevel();
242 EXPECT_EQ(0.0, default_zoom_level);
243
244 EXPECT_DOUBLE_EQ(
245 1.0, GetMainFrameZoomFactor(web_contents(), main_frame_window_border));
246
247 const double new_zoom_factor = 2.0;
248 const double new_zoom_level =
249 default_zoom_level + ZoomFactorToZoomLevel(new_zoom_factor);
250
251 // This should not cause the nested iframe to change its zoom.
252 host_zoom_map->SetZoomLevelForHost("b.com", new_zoom_level);
253
254 EXPECT_DOUBLE_EQ(
255 1.0, GetMainFrameZoomFactor(web_contents(), main_frame_window_border));
256 EXPECT_EQ(scale_one_child_width, GetSubframeWidth(child));
257 EXPECT_EQ(scale_one_grandchild_width, GetSubframeWidth(grandchild));
258
259 // We exclude the remainder of this test on Android since Android does not
260 // set page zoom levels for loading pages.
261 // See RenderViewImpl::OnSetZoomLevelForLoadingURL().
262 #if !defined(OS_ANDROID)
263 // When we navigate so that b.com is the top-level site, then it has the
264 // expected zoom.
265 GURL new_url = embedded_test_server()->GetURL("b.com", "/title1.html");
266 EXPECT_TRUE(NavigateToURL(shell(), new_url));
267 EXPECT_DOUBLE_EQ(
268 new_zoom_factor,
269 GetMainFrameZoomFactor(web_contents(), main_frame_window_border));
270 #endif
271 }
272
273 IN_PROC_BROWSER_TEST_F(IFrameZoomBrowserTest, AllFramesGetDefaultZoom) {
274 std::string top_level_host("a.com");
275 GURL main_url(embedded_test_server()->GetURL(
276 top_level_host, "/cross_site_iframe_factory.html?a(b(a))"));
277 EXPECT_TRUE(NavigateToURL(shell(), main_url));
278 NavigationEntry* entry =
279 web_contents()->GetController().GetLastCommittedEntry();
280 ASSERT_TRUE(entry);
281 GURL loaded_url = HostZoomMap::GetURLFromEntry(entry);
282 EXPECT_EQ(top_level_host, loaded_url.host());
283
284 FrameTreeNode* root =
285 static_cast<WebContentsImpl*>(web_contents())->GetFrameTree()->root();
286 RenderFrameHostImpl* child = root->child_at(0)->current_frame_host();
287 RenderFrameHostImpl* grandchild =
288 root->child_at(0)->child_at(0)->current_frame_host();
289
290 // The following calls must be made when the page's scale factor = 1.0.
291 double scale_one_child_width = GetSubframeWidth(child);
292 double scale_one_grandchild_width = GetSubframeWidth(grandchild);
293 double main_frame_window_border = GetMainframeWindowBorder(web_contents());
294
295 HostZoomMap* host_zoom_map = HostZoomMap::GetForWebContents(web_contents());
296 double default_zoom_level = host_zoom_map->GetDefaultZoomLevel();
297 EXPECT_EQ(0.0, default_zoom_level);
298
299 EXPECT_DOUBLE_EQ(
300 1.0, GetMainFrameZoomFactor(web_contents(), main_frame_window_border));
301
302 const double new_default_zoom_factor = 2.0;
303 {
304 DOMMessageQueue msg_queue;
305
306 std::vector<FrameResizeObserver> frame_observers;
307 frame_observers.emplace_back(child, "child",
308 scale_one_child_width, kTolerance);
309 frame_observers.emplace_back(grandchild, "grandchild",
310 scale_one_grandchild_width, kTolerance);
311
312 const double new_default_zoom_level =
313 default_zoom_level + ZoomFactorToZoomLevel(new_default_zoom_factor);
314
315 host_zoom_map->SetZoomLevelForHost("b.com", new_default_zoom_level + 1.0);
316 host_zoom_map->SetDefaultZoomLevel(new_default_zoom_level);
317
318 WaitAndCheckFrameZoom(msg_queue, frame_observers);
319 }
320 // Make this comparison approximate for Nexus5X test;
321 // https://crbug.com/622858.
322 EXPECT_NEAR(
323 new_default_zoom_factor,
324 GetMainFrameZoomFactor(web_contents(), main_frame_window_border),
325 0.01
326 );
327 }
328
329 IN_PROC_BROWSER_TEST_F(IFrameZoomBrowserTest, SiblingFramesZoom) {
330 std::string top_level_host("a.com");
331 GURL main_url(embedded_test_server()->GetURL(
332 top_level_host, "/cross_site_iframe_factory.html?a(b,b)"));
333 EXPECT_TRUE(NavigateToURL(shell(), main_url));
334 NavigationEntry* entry =
335 web_contents()->GetController().GetLastCommittedEntry();
336 ASSERT_TRUE(entry);
337 GURL loaded_url = HostZoomMap::GetURLFromEntry(entry);
338 EXPECT_EQ(top_level_host, loaded_url.host());
339
340 FrameTreeNode* root =
341 static_cast<WebContentsImpl*>(web_contents())->GetFrameTree()->root();
342 RenderFrameHostImpl* child1 = root->child_at(0)->current_frame_host();
343 RenderFrameHostImpl* child2 = root->child_at(1)->current_frame_host();
344
345 // The following calls must be made when the page's scale factor = 1.0.
346 double scale_one_child1_width = GetSubframeWidth(child1);
347 double scale_one_child2_width = GetSubframeWidth(child2);
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.5;
358 {
359 DOMMessageQueue msg_queue;
360
361 std::vector<FrameResizeObserver> frame_observers;
362 frame_observers.emplace_back(child1, "child1",
363 scale_one_child1_width, kTolerance);
364 frame_observers.emplace_back(child2, "child2",
365 scale_one_child2_width, kTolerance);
366
367 const double new_zoom_level =
368 default_zoom_level + ZoomFactorToZoomLevel(new_zoom_factor);
369 host_zoom_map->SetZoomLevelForHost(top_level_host, new_zoom_level);
370
371 WaitAndCheckFrameZoom(msg_queue, frame_observers);
372 }
373
374 // Make this comparison approximate for Nexus5X test;
375 // https://crbug.com/622858.
376 EXPECT_NEAR(
377 new_zoom_factor,
378 GetMainFrameZoomFactor(web_contents(), main_frame_window_border),
379 0.01);
380 }
381
382 IN_PROC_BROWSER_TEST_F(IFrameZoomBrowserTest, SubframeRetainsZoomOnNavigation) {
383 std::string top_level_host("a.com");
384 GURL main_url(embedded_test_server()->GetURL(
385 top_level_host, "/cross_site_iframe_factory.html?a(b)"));
386 EXPECT_TRUE(NavigateToURL(shell(), main_url));
387 NavigationEntry* entry =
388 web_contents()->GetController().GetLastCommittedEntry();
389 ASSERT_TRUE(entry);
390 GURL loaded_url = HostZoomMap::GetURLFromEntry(entry);
391 EXPECT_EQ(top_level_host, loaded_url.host());
392
393 FrameTreeNode* root =
394 static_cast<WebContentsImpl*>(web_contents())->GetFrameTree()->root();
395 RenderFrameHostImpl* child = root->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 main_frame_window_border = GetMainframeWindowBorder(web_contents());
400
401 HostZoomMap* host_zoom_map = HostZoomMap::GetForWebContents(web_contents());
402 double default_zoom_level = host_zoom_map->GetDefaultZoomLevel();
403 EXPECT_EQ(0.0, default_zoom_level);
404
405 EXPECT_DOUBLE_EQ(
406 1.0, GetMainFrameZoomFactor(web_contents(), main_frame_window_border));
407
408 const double new_zoom_factor = 0.5;
409 {
410 DOMMessageQueue msg_queue;
411
412 std::vector<FrameResizeObserver> frame_observers;
413 frame_observers.emplace_back(child, "child",
414 scale_one_child_width, kTolerance);
415
416 const double new_zoom_level =
417 default_zoom_level + ZoomFactorToZoomLevel(new_zoom_factor);
418 host_zoom_map->SetZoomLevelForHost(top_level_host, new_zoom_level);
419
420 WaitAndCheckFrameZoom(msg_queue, frame_observers);
421 }
422
423 // Make this comparison approximate for Nexus5X test;
424 // https://crbug.com/622858.
425 EXPECT_NEAR(
426 new_zoom_factor,
427 GetMainFrameZoomFactor(web_contents(), main_frame_window_border),
428 0.01
429 );
430
431 // Navigate child frame cross site, and make sure zoom is the same.
432 TestNavigationObserver observer(web_contents());
433 GURL url = embedded_test_server()->GetURL("c.com", "/title1.html");
434 NavigateFrameToURL(root->child_at(0), url);
435 EXPECT_TRUE(observer.last_navigation_succeeded());
436 EXPECT_EQ(url, observer.last_navigation_url());
437
438 // Check that the child frame maintained the same scale after navigating
439 // cross-site.
440 double new_child_width =
441 GetSubframeWidth(root->child_at(0)->current_frame_host());
442 EXPECT_EQ(scale_one_child_width, new_child_width);
443 }
444
445 // http://crbug.com/609213
446 #if !defined(OS_ANDROID)
447 IN_PROC_BROWSER_TEST_F(IFrameZoomBrowserTest,
448 RedirectToPageWithSubframeZoomsCorrectly) {
449 std::string initial_host("a.com");
450 std::string redirected_host("b.com");
451 EXPECT_TRUE(NavigateToURL(shell(), GURL(embedded_test_server()->GetURL(
452 initial_host, "/title2.html"))));
453 double main_frame_window_border = GetMainframeWindowBorder(web_contents());
454 EXPECT_DOUBLE_EQ(
455 1.0, GetMainFrameZoomFactor(web_contents(), main_frame_window_border));
456
457 // Set a zoom level for b.com before we navigate to it.
458 const double kZoomFactorForRedirectedHost = 1.5;
459 HostZoomMap* host_zoom_map = HostZoomMap::GetForWebContents(web_contents());
460 host_zoom_map->SetZoomLevelForHost(
461 redirected_host, ZoomFactorToZoomLevel(kZoomFactorForRedirectedHost));
462
463 // Navigation to a.com doesn't change the zoom level, but when it redirects
464 // to b.com, and then a subframe loads, the zoom should change.
465 GURL redirect_url(embedded_test_server()->GetURL(
466 redirected_host, "/cross_site_iframe_factory.html?b(b)"));
467 GURL url(embedded_test_server()->GetURL(
468 initial_host, "/client-redirect?" + redirect_url.spec()));
469
470 NavigateToURLBlockUntilNavigationsComplete(shell(), url, 2);
471 EXPECT_TRUE(IsLastCommittedEntryOfPageType(web_contents(), PAGE_TYPE_NORMAL));
472 EXPECT_EQ(redirect_url, web_contents()->GetLastCommittedURL());
473
474 EXPECT_NEAR(
475 kZoomFactorForRedirectedHost,
476 GetMainFrameZoomFactor(web_contents(), main_frame_window_border),
477 0.001);
478 }
479 #endif
480
481 } // namespace content
OLDNEW
« no previous file with comments | « no previous file | content/browser/zoom_browsertest.cc » ('j') | content/browser/zoom_browsertest.cc » ('J')

Powered by Google App Engine
This is Rietveld 408576698