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