1 // Copyright 2014 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 <stdlib.h>
6
7 #include "base/command_line.h"
8 #include "base/macros.h"
9 #include "base/strings/stringprintf.h"
10 #include "build/build_config.h"
11 #include "content/browser/renderer_host/frame_tree.h"
12 #include "content/browser/renderer_host/render_frame_host_impl.h"
13 #include "content/browser/renderer_host/render_widget_host_impl.h"
14 #include "content/browser/web_contents/web_contents_impl.h"
15 #include "content/public/browser/render_widget_host.h"
16 #include "content/public/browser/render_widget_host_view.h"
17 #include "content/public/browser/web_contents.h"
18 #include "content/public/common/content_switches.h"
19 #include "content/public/test/browser_test.h"
20 #include "content/public/test/browser_test_utils.h"
21 #include "content/public/test/content_browser_test.h"
22 #include "content/public/test/content_browser_test_utils.h"
23 #include "content/public/test/test_navigation_observer.h"
24 #include "content/public/test/test_utils.h"
25 #include "content/shell/browser/shell.h"
26 #include "content/shell/common/shell_switches.h"
27 #include "net/dns/mock_host_resolver.h"
28 #include "ui/compositor/compositor_switches.h"
29 #include "ui/display/screen.h"
30
31 namespace content {
32
33 class ScreenOrientationBrowserTest : public ContentBrowserTest {
34 public:
ScreenOrientationBrowserTest()35 ScreenOrientationBrowserTest() {
36 }
37
web_contents()38 WebContentsImpl* web_contents() {
39 return static_cast<WebContentsImpl*>(shell()->web_contents());
40 }
41
42 protected:
SendFakeScreenOrientation(unsigned angle,const std::string & str_type)43 void SendFakeScreenOrientation(unsigned angle, const std::string& str_type) {
44 RenderWidgetHostImpl* main_frame_rwh = static_cast<RenderWidgetHostImpl*>(
45 web_contents()->GetMainFrame()->GetRenderWidgetHost());
46
47 blink::mojom::ScreenOrientation type =
48 blink::mojom::ScreenOrientation::kUndefined;
49 if (str_type == "portrait-primary") {
50 type = blink::mojom::ScreenOrientation::kPortraitPrimary;
51 } else if (str_type == "portrait-secondary") {
52 type = blink::mojom::ScreenOrientation::kPortraitSecondary;
53 } else if (str_type == "landscape-primary") {
54 type = blink::mojom::ScreenOrientation::kLandscapePrimary;
55 } else if (str_type == "landscape-secondary") {
56 type = blink::mojom::ScreenOrientation::kLandscapeSecondary;
57 }
58 ASSERT_NE(blink::mojom::ScreenOrientation::kUndefined, type);
59
60 std::set<RenderWidgetHost*> rwhs;
61 for (RenderFrameHost* rfh : web_contents()->GetAllFrames()) {
62 if (rfh == web_contents()->GetMainFrame())
63 continue;
64
65 rwhs.insert(static_cast<RenderFrameHostImpl*>(rfh)
66 ->frame_tree_node()
67 ->render_manager()
68 ->GetRenderWidgetHostView()
69 ->GetRenderWidgetHost());
70 }
71
72 // This simulates what the browser process does when the screen orientation
73 // is changed.
74 main_frame_rwh->SetScreenOrientationForTesting(angle, type);
75 }
76
GetOrientationAngle()77 int GetOrientationAngle() {
78 int angle =
79 ExecuteScriptAndGetValue(shell()->web_contents()->GetMainFrame(),
80 "screen.orientation.angle")
81 .GetInt();
82 return angle;
83 }
84
GetOrientationType()85 std::string GetOrientationType() {
86 std::string type =
87 ExecuteScriptAndGetValue(shell()->web_contents()->GetMainFrame(),
88 "screen.orientation.type")
89 .GetString();
90 return type;
91 }
92
ScreenOrientationSupported()93 bool ScreenOrientationSupported() {
94 bool support =
95 ExecuteScriptAndGetValue(shell()->web_contents()->GetMainFrame(),
96 "'orientation' in screen")
97 .GetBool();
98 return support;
99 }
100
WindowOrientationSupported()101 bool WindowOrientationSupported() {
102 bool support =
103 ExecuteScriptAndGetValue(shell()->web_contents()->GetMainFrame(),
104 "'orientation' in window")
105 .GetBool();
106 return support;
107 }
108
GetWindowOrientationAngle()109 int GetWindowOrientationAngle() {
110 int angle =
111 ExecuteScriptAndGetValue(shell()->web_contents()->GetMainFrame(),
112 "window.orientation")
113 .GetInt();
114 return angle;
115 }
116
117 private:
118 DISALLOW_COPY_AND_ASSIGN(ScreenOrientationBrowserTest);
119 };
120
121 class ScreenOrientationOOPIFBrowserTest : public ScreenOrientationBrowserTest {
122 public:
ScreenOrientationOOPIFBrowserTest()123 ScreenOrientationOOPIFBrowserTest() {}
124
SetUpCommandLine(base::CommandLine * command_line)125 void SetUpCommandLine(base::CommandLine* command_line) override {
126 IsolateAllSitesForTesting(command_line);
127 }
128
SetUpOnMainThread()129 void SetUpOnMainThread() override {
130 host_resolver()->AddRule("*", "127.0.0.1");
131 SetupCrossSiteRedirector(embedded_test_server());
132 ASSERT_TRUE(embedded_test_server()->Start());
133 }
134
135 private:
136 DISALLOW_COPY_AND_ASSIGN(ScreenOrientationOOPIFBrowserTest);
137 };
138
139 // This test doesn't work on MacOS X but the reason is mostly because it is not
140 // used Aura. It could be set as !defined(OS_MAC) but the rule below will
141 // actually support MacOS X if and when it switches to Aura.
142 #if defined(USE_AURA) || defined(OS_ANDROID)
143 // Flaky on Chrome OS: http://crbug.com/468259
144 #if defined(OS_CHROMEOS)
145 #define MAYBE_ScreenOrientationChange DISABLED_ScreenOrientationChange
146 #else
147 #define MAYBE_ScreenOrientationChange ScreenOrientationChange
148 #endif
IN_PROC_BROWSER_TEST_F(ScreenOrientationBrowserTest,MAYBE_ScreenOrientationChange)149 IN_PROC_BROWSER_TEST_F(ScreenOrientationBrowserTest,
150 MAYBE_ScreenOrientationChange) {
151 std::string types[] = { "portrait-primary",
152 "portrait-secondary",
153 "landscape-primary",
154 "landscape-secondary" };
155 GURL test_url = GetTestUrl("screen_orientation",
156 "screen_orientation_screenorientationchange.html");
157
158 TestNavigationObserver navigation_observer(shell()->web_contents(), 1);
159 shell()->LoadURL(test_url);
160 navigation_observer.Wait();
161 WaitForResizeComplete(shell()->web_contents());
162
163 int angle = GetOrientationAngle();
164
165 for (int i = 0; i < 4; ++i) {
166 angle = (angle + 90) % 360;
167 SendFakeScreenOrientation(angle, types[i]);
168
169 TestNavigationObserver navigation_observer(shell()->web_contents());
170 navigation_observer.Wait();
171 EXPECT_EQ(angle, GetOrientationAngle());
172 EXPECT_EQ(types[i], GetOrientationType());
173 }
174 }
175 #endif // defined(USE_AURA) || defined(OS_ANDROID)
176
177 // Flaky on Chrome OS: http://crbug.com/468259
178 #if defined(OS_CHROMEOS)
179 #define MAYBE_WindowOrientationChange DISABLED_WindowOrientationChange
180 #else
181 #define MAYBE_WindowOrientationChange WindowOrientationChange
182 #endif
IN_PROC_BROWSER_TEST_F(ScreenOrientationBrowserTest,MAYBE_WindowOrientationChange)183 IN_PROC_BROWSER_TEST_F(ScreenOrientationBrowserTest,
184 MAYBE_WindowOrientationChange) {
185 GURL test_url = GetTestUrl("screen_orientation",
186 "screen_orientation_windoworientationchange.html");
187
188 TestNavigationObserver navigation_observer(shell()->web_contents(), 1);
189 shell()->LoadURL(test_url);
190 navigation_observer.Wait();
191 #if USE_AURA || defined(OS_ANDROID)
192 WaitForResizeComplete(shell()->web_contents());
193 #endif // USE_AURA || defined(OS_ANDROID)
194
195 if (!WindowOrientationSupported())
196 return;
197
198 int angle = GetWindowOrientationAngle();
199
200 for (int i = 0; i < 4; ++i) {
201 angle = (angle + 90) % 360;
202 SendFakeScreenOrientation(angle, "portrait-primary");
203
204 TestNavigationObserver navigation_observer(shell()->web_contents(), 1);
205 navigation_observer.Wait();
206 EXPECT_EQ(angle == 270 ? -90 : angle, GetWindowOrientationAngle());
207 }
208 }
209
210 // LockSmoke test seems to have become flaky on all non-ChromeOS platforms.
211 // The cause is unfortunately unknown. See https://crbug.com/448876
212 // Chromium Android does not support fullscreen
IN_PROC_BROWSER_TEST_F(ScreenOrientationBrowserTest,DISABLED_LockSmoke)213 IN_PROC_BROWSER_TEST_F(ScreenOrientationBrowserTest, DISABLED_LockSmoke) {
214 GURL test_url = GetTestUrl("screen_orientation",
215 "screen_orientation_lock_smoke.html");
216
217 TestNavigationObserver navigation_observer(shell()->web_contents(), 2);
218 shell()->LoadURL(test_url);
219
220 navigation_observer.Wait();
221 #if USE_AURA || defined(OS_ANDROID)
222 WaitForResizeComplete(shell()->web_contents());
223 #endif // USE_AURA || defined(OS_ANDROID)
224
225 std::string expected =
226 #if defined(OS_ANDROID)
227 "SecurityError"; // WebContents need to be fullscreen.
228 #else
229 "NotSupportedError"; // Locking isn't supported.
230 #endif
231
232 EXPECT_EQ(expected, shell()->web_contents()->GetLastCommittedURL().ref());
233 }
234
235 // Check that using screen orientation after a frame is detached doesn't crash
236 // the renderer process.
237 // This could be a web test if they were not using a mock screen orientation
238 // controller.
IN_PROC_BROWSER_TEST_F(ScreenOrientationBrowserTest,CrashTest_UseAfterDetach)239 IN_PROC_BROWSER_TEST_F(ScreenOrientationBrowserTest, CrashTest_UseAfterDetach) {
240 ASSERT_TRUE(embedded_test_server()->Start());
241 GURL test_url(embedded_test_server()->GetURL(
242 "/screen_orientation/screen_orientation_use_after_detach.html"));
243
244 TestNavigationObserver navigation_observer(shell()->web_contents(), 2);
245 shell()->LoadURL(test_url);
246
247 navigation_observer.Wait();
248
249 // This is a success if the renderer process did not crash, thus, we end up
250 // here.
251 }
252
253 #if defined(OS_ANDROID)
254 class ScreenOrientationLockDisabledBrowserTest : public ContentBrowserTest {
255 public:
ScreenOrientationLockDisabledBrowserTest()256 ScreenOrientationLockDisabledBrowserTest() {}
~ScreenOrientationLockDisabledBrowserTest()257 ~ScreenOrientationLockDisabledBrowserTest() override {}
258
SetUpCommandLine(base::CommandLine * command_line)259 void SetUpCommandLine(base::CommandLine* command_line) override {
260 command_line->AppendSwitch(switches::kDisableScreenOrientationLock);
261 }
262 };
263
264 // Check that when --disable-screen-orientation-lock is passed to the command
265 // line, screen.orientation.lock() correctly reports to not be supported.
IN_PROC_BROWSER_TEST_F(ScreenOrientationLockDisabledBrowserTest,DISABLED_NotSupported)266 IN_PROC_BROWSER_TEST_F(ScreenOrientationLockDisabledBrowserTest,
267 DISABLED_NotSupported) {
268 GURL test_url = GetTestUrl("screen_orientation",
269 "screen_orientation_lock_disabled.html");
270
271 TestNavigationObserver navigation_observer(shell()->web_contents(), 1);
272 shell()->LoadURL(test_url);
273 navigation_observer.Wait();
274
275 {
276 ASSERT_TRUE(ExecuteScript(shell(), "run();"));
277
278 TestNavigationObserver navigation_observer(shell()->web_contents(), 1);
279 navigation_observer.Wait();
280 EXPECT_EQ("NotSupportedError",
281 shell()->web_contents()->GetLastCommittedURL().ref());
282 }
283 }
284 #endif // defined(OS_ANDROID)
285
IN_PROC_BROWSER_TEST_F(ScreenOrientationOOPIFBrowserTest,ScreenOrientation)286 IN_PROC_BROWSER_TEST_F(ScreenOrientationOOPIFBrowserTest, ScreenOrientation) {
287 GURL main_url(embedded_test_server()->GetURL(
288 "a.com", "/cross_site_iframe_factory.html?a(b)"));
289 EXPECT_TRUE(NavigateToURL(shell(), main_url));
290 #if USE_AURA || defined(OS_ANDROID)
291 WaitForResizeComplete(shell()->web_contents());
292 #endif // USE_AURA || defined(OS_ANDROID)
293
294 std::string types[] = {"portrait-primary", "portrait-secondary",
295 "landscape-primary", "landscape-secondary"};
296
297 int angle = GetOrientationAngle();
298
299 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
300 FrameTreeNode* child = root->child_at(0);
301 MainThreadFrameObserver root_observer(
302 root->current_frame_host()->GetRenderWidgetHost());
303 MainThreadFrameObserver child_observer(
304 child->current_frame_host()->GetRenderWidgetHost());
305 for (int i = 0; i < 4; ++i) {
306 angle = (angle + 90) % 360;
307 SendFakeScreenOrientation(angle, types[i]);
308
309 root_observer.Wait();
310 child_observer.Wait();
311
312 int orientation_angle;
313 std::string orientation_type;
314
315 EXPECT_TRUE(ExecuteScriptAndExtractInt(
316 root->current_frame_host(),
317 "window.domAutomationController.send(screen.orientation.angle)",
318 &orientation_angle));
319 EXPECT_EQ(angle, orientation_angle);
320 EXPECT_TRUE(ExecuteScriptAndExtractInt(
321 child->current_frame_host(),
322 "window.domAutomationController.send(screen.orientation.angle)",
323 &orientation_angle));
324 EXPECT_EQ(angle, orientation_angle);
325
326 EXPECT_TRUE(ExecuteScriptAndExtractString(
327 root->current_frame_host(),
328 "window.domAutomationController.send(screen.orientation.type)",
329 &orientation_type));
330 EXPECT_EQ(types[i], orientation_type);
331 EXPECT_TRUE(ExecuteScriptAndExtractString(
332 child->current_frame_host(),
333 "window.domAutomationController.send(screen.orientation.type)",
334 &orientation_type));
335 EXPECT_EQ(types[i], orientation_type);
336 }
337 }
338
339 // Regression test for triggering a screen orientation change for a pending
340 // main frame RenderFrameHost. See https://crbug.com/764202. In the bug, this
341 // was triggered via the DevTools audit panel and
342 // blink::mojom::FrameWidget::EnableDeviceEmulation, which calls
343 // RenderWidget::Resize on the renderer side. The test fakes this by directly
344 // sending the resize message to the widget.
IN_PROC_BROWSER_TEST_F(ScreenOrientationOOPIFBrowserTest,ScreenOrientationInPendingMainFrame)345 IN_PROC_BROWSER_TEST_F(ScreenOrientationOOPIFBrowserTest,
346 ScreenOrientationInPendingMainFrame) {
347 GURL main_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
348 EXPECT_TRUE(NavigateToURL(shell(), main_url));
349 #if USE_AURA || defined(OS_ANDROID)
350 WaitForResizeComplete(shell()->web_contents());
351 #endif // USE_AURA || defined(OS_ANDROID)
352
353 // Set up a fake Resize message with a screen orientation change.
354 RenderWidgetHost* main_frame_rwh =
355 web_contents()->GetMainFrame()->GetRenderWidgetHost();
356 blink::ScreenInfo screen_info;
357 main_frame_rwh->GetScreenInfo(&screen_info);
358 int expected_angle = (screen_info.orientation_angle + 90) % 360;
359
360 // Start a cross-site navigation, but don't commit yet.
361 GURL second_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
362 TestNavigationManager delayer(shell()->web_contents(), second_url);
363 shell()->LoadURL(second_url);
364 EXPECT_TRUE(delayer.WaitForRequestStart());
365
366 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
367 RenderFrameHostImpl* pending_rfh =
368 root->render_manager()->speculative_frame_host();
369
370 // Send the orientation change to the pending RFH's widget.
371 static_cast<RenderWidgetHostImpl*>(pending_rfh->GetRenderWidgetHost())
372 ->SetScreenOrientationForTesting(expected_angle,
373 screen_info.orientation_type);
374
375 // Let the navigation finish and make sure it succeeded.
376 delayer.WaitForNavigationFinished();
377 EXPECT_EQ(second_url, web_contents()->GetMainFrame()->GetLastCommittedURL());
378
379 #if USE_AURA || defined(OS_ANDROID)
380 WaitForResizeComplete(shell()->web_contents());
381 #endif // USE_AURA || defined(OS_ANDROID)
382
383 int orientation_angle;
384 EXPECT_TRUE(ExecuteScriptAndExtractInt(
385 root->current_frame_host(),
386 "window.domAutomationController.send(screen.orientation.angle)",
387 &orientation_angle));
388 EXPECT_EQ(expected_angle, orientation_angle);
389 }
390
391 #ifdef OS_ANDROID
392 // This test is disabled because th trybots run in system portrait lock, which
393 // prevents the test from changing the screen orientation.
IN_PROC_BROWSER_TEST_F(ScreenOrientationOOPIFBrowserTest,DISABLED_ScreenOrientationLock)394 IN_PROC_BROWSER_TEST_F(ScreenOrientationOOPIFBrowserTest,
395 DISABLED_ScreenOrientationLock) {
396 GURL main_url(embedded_test_server()->GetURL(
397 "a.com", "/cross_site_iframe_factory.html?a(b)"));
398 EXPECT_TRUE(NavigateToURL(shell(), main_url));
399 WaitForResizeComplete(shell()->web_contents());
400
401 const char* types[] = {"portrait-primary", "portrait-secondary",
402 "landscape-primary", "landscape-secondary"};
403
404 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
405 FrameTreeNode* child = root->child_at(0);
406 RenderFrameHostImpl* frames[] = {root->current_frame_host(),
407 child->current_frame_host()};
408
409 EXPECT_TRUE(ExecuteScript(root->current_frame_host(),
410 "document.body.webkitRequestFullscreen()"));
411 for (const char* type : types) {
412 std::string script =
413 base::StringPrintf("screen.orientation.lock('%s')", type);
414 EXPECT_TRUE(ExecuteScript(child->current_frame_host(), script));
415
416 for (auto* frame : frames) {
417 std::string orientation_type;
418 while (type != orientation_type) {
419 EXPECT_TRUE(ExecuteScriptAndExtractString(
420 frame,
421 "window.domAutomationController.send(screen.orientation.type)",
422 &orientation_type));
423 }
424 }
425
426 EXPECT_TRUE(ExecuteScript(child->current_frame_host(),
427 "screen.orientation.unlock()"));
428 }
429 }
430 #endif // OS_ANDROID
431
432 } // namespace content
433