1 // Copyright (c) 2012 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 "base/command_line.h"
6 #include "build/build_config.h"
7 #include "chrome/browser/ui/browser.h"
8 #include "chrome/browser/ui/browser_tabstrip.h"
9 #include "chrome/browser/ui/fullscreen/fullscreen_controller.h"
10 #include "chrome/browser/ui/fullscreen/fullscreen_controller_state_test.h"
11 #include "chrome/browser/ui/tabs/tab_strip_model.h"
12 #include "chrome/test/base/browser_with_test_window_test.h"
13 #include "content/public/browser/web_contents.h"
14 #include "content/public/common/url_constants.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16
17 // The FullscreenControllerStateUnitTest unit test suite exhastively tests
18 // the FullscreenController through all permutations of events. The behavior
19 // of the BrowserWindow is mocked via FullscreenControllerTestWindow.
20
21
22 // FullscreenControllerTestWindow ----------------------------------------------
23
24 // A BrowserWindow used for testing FullscreenController. The behavior of this
25 // mock is verfied manually by running FullscreenControllerStateInteractiveTest.
26 class FullscreenControllerTestWindow : public TestBrowserWindow {
27 public:
28 // Simulate the window state with an enumeration.
29 enum WindowState {
30 NORMAL,
31 FULLSCREEN,
32 // No TO_ state for METRO_SNAP, the windows implementation is synchronous.
33 METRO_SNAP,
34 TO_NORMAL,
35 TO_FULLSCREEN,
36 };
37
38 FullscreenControllerTestWindow();
~FullscreenControllerTestWindow()39 virtual ~FullscreenControllerTestWindow() {}
40
41 // BrowserWindow Interface:
42 virtual void EnterFullscreen(const GURL& url,
43 FullscreenExitBubbleType type) OVERRIDE;
44 virtual void ExitFullscreen() OVERRIDE;
45 virtual bool ShouldHideUIForFullscreen() const OVERRIDE;
46 virtual bool IsFullscreen() const OVERRIDE;
47 #if defined(OS_WIN)
48 virtual void SetMetroSnapMode(bool enable) OVERRIDE;
49 virtual bool IsInMetroSnapMode() const OVERRIDE;
50 #endif
51 #if defined(OS_MACOSX)
52 virtual void EnterFullscreenWithChrome() OVERRIDE;
53 virtual bool IsFullscreenWithChrome() OVERRIDE;
54 virtual bool IsFullscreenWithoutChrome() OVERRIDE;
55 #endif
56
57 static const char* GetWindowStateString(WindowState state);
state() const58 WindowState state() const { return state_; }
set_browser(Browser * browser)59 void set_browser(Browser* browser) { browser_ = browser; }
60
61 // Simulates the window changing state.
62 void ChangeWindowFullscreenState();
63
64 private:
65 // Enters fullscreen with |new_mac_with_chrome_mode|.
66 void EnterFullscreen(bool new_mac_with_chrome_mode);
67
68 // Returns true if ChangeWindowFullscreenState() should be called as a result
69 // of updating the current fullscreen state to the passed in state.
70 bool IsTransitionReentrant(bool new_fullscreen,
71 bool new_mac_with_chrome_mode);
72
73 WindowState state_;
74 bool mac_with_chrome_mode_;
75 Browser* browser_;
76 };
77
FullscreenControllerTestWindow()78 FullscreenControllerTestWindow::FullscreenControllerTestWindow()
79 : state_(NORMAL),
80 mac_with_chrome_mode_(false),
81 browser_(NULL) {
82 }
83
EnterFullscreen(const GURL & url,FullscreenExitBubbleType type)84 void FullscreenControllerTestWindow::EnterFullscreen(
85 const GURL& url, FullscreenExitBubbleType type) {
86 EnterFullscreen(false);
87 }
88
ExitFullscreen()89 void FullscreenControllerTestWindow::ExitFullscreen() {
90 if (IsFullscreen()) {
91 state_ = TO_NORMAL;
92 mac_with_chrome_mode_ = false;
93
94 if (IsTransitionReentrant(false, false))
95 ChangeWindowFullscreenState();
96 }
97 }
98
ShouldHideUIForFullscreen() const99 bool FullscreenControllerTestWindow::ShouldHideUIForFullscreen() const {
100 return IsFullscreen();
101 }
102
IsFullscreen() const103 bool FullscreenControllerTestWindow::IsFullscreen() const {
104 #if defined(OS_MACOSX)
105 return state_ == FULLSCREEN || state_ == TO_FULLSCREEN;
106 #else
107 return state_ == FULLSCREEN || state_ == TO_NORMAL;
108 #endif
109 }
110
111 #if defined(OS_WIN)
SetMetroSnapMode(bool enable)112 void FullscreenControllerTestWindow::SetMetroSnapMode(bool enable) {
113 if (enable != IsInMetroSnapMode())
114 state_ = enable ? METRO_SNAP : NORMAL;
115
116 if (FullscreenControllerStateTest::IsWindowFullscreenStateChangedReentrant())
117 ChangeWindowFullscreenState();
118 }
119
IsInMetroSnapMode() const120 bool FullscreenControllerTestWindow::IsInMetroSnapMode() const {
121 return state_ == METRO_SNAP;
122 }
123 #endif
124
125 #if defined(OS_MACOSX)
EnterFullscreenWithChrome()126 void FullscreenControllerTestWindow::EnterFullscreenWithChrome() {
127 EnterFullscreen(true);
128 }
129
IsFullscreenWithChrome()130 bool FullscreenControllerTestWindow::IsFullscreenWithChrome() {
131 return IsFullscreen() && mac_with_chrome_mode_;
132 }
133
IsFullscreenWithoutChrome()134 bool FullscreenControllerTestWindow::IsFullscreenWithoutChrome() {
135 return IsFullscreen() && !mac_with_chrome_mode_;
136 }
137 #endif
138
139 // static
GetWindowStateString(WindowState state)140 const char* FullscreenControllerTestWindow::GetWindowStateString(
141 WindowState state) {
142 switch (state) {
143 ENUM_TO_STRING(NORMAL);
144 ENUM_TO_STRING(FULLSCREEN);
145 ENUM_TO_STRING(METRO_SNAP);
146 ENUM_TO_STRING(TO_FULLSCREEN);
147 ENUM_TO_STRING(TO_NORMAL);
148 default:
149 NOTREACHED() << "No string for state " << state;
150 return "WindowState-Unknown";
151 }
152 }
153
ChangeWindowFullscreenState()154 void FullscreenControllerTestWindow::ChangeWindowFullscreenState() {
155 // Most states result in "no operation" intentionally. The tests
156 // assume that all possible states and event pairs can be tested, even
157 // though window managers will not generate all of these.
158 if (state_ == TO_FULLSCREEN)
159 state_ = FULLSCREEN;
160 else if (state_ == TO_NORMAL)
161 state_ = NORMAL;
162
163 // Emit a change event from every state to ensure the Fullscreen Controller
164 // handles it in all circumstances.
165 browser_->WindowFullscreenStateChanged();
166 }
167
EnterFullscreen(bool new_mac_with_chrome_mode)168 void FullscreenControllerTestWindow::EnterFullscreen(
169 bool new_mac_with_chrome_mode) {
170 bool reentrant = IsTransitionReentrant(true, new_mac_with_chrome_mode);
171
172 mac_with_chrome_mode_ = new_mac_with_chrome_mode;
173 if (!IsFullscreen())
174 state_ = TO_FULLSCREEN;
175
176 if (reentrant)
177 ChangeWindowFullscreenState();
178 }
179
IsTransitionReentrant(bool new_fullscreen,bool new_mac_with_chrome_mode)180 bool FullscreenControllerTestWindow::IsTransitionReentrant(
181 bool new_fullscreen,
182 bool new_mac_with_chrome_mode) {
183 #if defined(OS_MACOSX)
184 bool mac_with_chrome_mode_changed = new_mac_with_chrome_mode ?
185 IsFullscreenWithoutChrome() : IsFullscreenWithChrome();
186 #else
187 bool mac_with_chrome_mode_changed = false;
188 #endif
189 bool fullscreen_changed = (new_fullscreen != IsFullscreen());
190
191 if (!fullscreen_changed && !mac_with_chrome_mode_changed)
192 return false;
193
194 if (FullscreenControllerStateTest::IsWindowFullscreenStateChangedReentrant())
195 return true;
196
197 // BrowserWindowCocoa::EnterFullscreen() and
198 // BrowserWindowCocoa::EnterFullscreenWithChrome() are reentrant when
199 // switching between fullscreen with chrome and fullscreen without chrome.
200 return state_ == FULLSCREEN &&
201 !fullscreen_changed &&
202 mac_with_chrome_mode_changed;
203 }
204
205
206 // FullscreenControllerStateUnitTest -------------------------------------------
207
208 // Unit test fixture testing Fullscreen Controller through its states. Most of
209 // the test logic comes from FullscreenControllerStateTest.
210 class FullscreenControllerStateUnitTest : public BrowserWithTestWindowTest,
211 public FullscreenControllerStateTest {
212 public:
213 FullscreenControllerStateUnitTest();
214
215 // FullscreenControllerStateTest:
216 virtual void SetUp() OVERRIDE;
217 virtual BrowserWindow* CreateBrowserWindow() OVERRIDE;
218 virtual void ChangeWindowFullscreenState() OVERRIDE;
219 virtual const char* GetWindowStateString() OVERRIDE;
220 virtual void VerifyWindowState() OVERRIDE;
221
222 protected:
223 // FullscreenControllerStateTest:
224 virtual bool ShouldSkipStateAndEventPair(State state, Event event) OVERRIDE;
225 virtual Browser* GetBrowser() OVERRIDE;
226 FullscreenControllerTestWindow* window_;
227 };
228
FullscreenControllerStateUnitTest()229 FullscreenControllerStateUnitTest::FullscreenControllerStateUnitTest ()
230 : window_(NULL) {
231 }
232
SetUp()233 void FullscreenControllerStateUnitTest::SetUp() {
234 BrowserWithTestWindowTest::SetUp();
235 window_->set_browser(browser());
236 }
237
CreateBrowserWindow()238 BrowserWindow* FullscreenControllerStateUnitTest::CreateBrowserWindow() {
239 window_ = new FullscreenControllerTestWindow();
240 return window_; // BrowserWithTestWindowTest takes ownership.
241 }
242
ChangeWindowFullscreenState()243 void FullscreenControllerStateUnitTest::ChangeWindowFullscreenState() {
244 window_->ChangeWindowFullscreenState();
245 }
246
GetWindowStateString()247 const char* FullscreenControllerStateUnitTest::GetWindowStateString() {
248 return FullscreenControllerTestWindow::GetWindowStateString(window_->state());
249 }
250
VerifyWindowState()251 void FullscreenControllerStateUnitTest::VerifyWindowState() {
252 switch (state()) {
253 case STATE_NORMAL:
254 EXPECT_EQ(FullscreenControllerTestWindow::NORMAL,
255 window_->state()) << GetAndClearDebugLog();
256 break;
257
258 case STATE_BROWSER_FULLSCREEN_NO_CHROME:
259 case STATE_BROWSER_FULLSCREEN_WITH_CHROME:
260 case STATE_TAB_FULLSCREEN:
261 case STATE_TAB_BROWSER_FULLSCREEN:
262 case STATE_TAB_BROWSER_FULLSCREEN_CHROME:
263 EXPECT_EQ(FullscreenControllerTestWindow::FULLSCREEN,
264 window_->state()) << GetAndClearDebugLog();
265 break;
266
267 #if defined(OS_WIN)
268 case STATE_METRO_SNAP:
269 EXPECT_EQ(FullscreenControllerTestWindow::METRO_SNAP,
270 window_->state()) << GetAndClearDebugLog();
271 break;
272 #endif
273
274 case STATE_TO_NORMAL:
275 EXPECT_EQ(FullscreenControllerTestWindow::TO_NORMAL,
276 window_->state()) << GetAndClearDebugLog();
277 break;
278
279 case STATE_TO_BROWSER_FULLSCREEN_NO_CHROME:
280 case STATE_TO_BROWSER_FULLSCREEN_WITH_CHROME:
281 case STATE_TO_TAB_FULLSCREEN:
282 EXPECT_EQ(FullscreenControllerTestWindow::TO_FULLSCREEN,
283 window_->state()) << GetAndClearDebugLog();
284 break;
285
286 default:
287 NOTREACHED() << GetAndClearDebugLog();
288 }
289
290 FullscreenControllerStateTest::VerifyWindowState();
291 }
292
ShouldSkipStateAndEventPair(State state,Event event)293 bool FullscreenControllerStateUnitTest::ShouldSkipStateAndEventPair(
294 State state, Event event) {
295 #if defined(OS_MACOSX)
296 // TODO(scheib) Toggle, Window Event, Toggle, Toggle on Mac as exposed by
297 // test *.STATE_TO_NORMAL__TOGGLE_FULLSCREEN runs interactively and exits to
298 // Normal. This doesn't appear to be the desired result, and would add
299 // too much complexity to mimic in our simple FullscreenControllerTestWindow.
300 // http://crbug.com/156968
301 if ((state == STATE_TO_NORMAL ||
302 state == STATE_TO_BROWSER_FULLSCREEN_NO_CHROME ||
303 state == STATE_TO_TAB_FULLSCREEN) &&
304 event == TOGGLE_FULLSCREEN)
305 return true;
306 #endif
307
308 return FullscreenControllerStateTest::ShouldSkipStateAndEventPair(state,
309 event);
310 }
311
GetBrowser()312 Browser* FullscreenControllerStateUnitTest::GetBrowser() {
313 return BrowserWithTestWindowTest::browser();
314 }
315
316
317 // Soak tests ------------------------------------------------------------------
318
319 // Tests all states with all permutations of multiple events to detect lingering
320 // state issues that would bleed over to other states.
321 // I.E. for each state test all combinations of events E1, E2, E3.
322 //
323 // This produces coverage for event sequences that may happen normally but
324 // would not be exposed by traversing to each state via TransitionToState().
325 // TransitionToState() always takes the same path even when multiple paths
326 // exist.
TEST_F(FullscreenControllerStateUnitTest,TransitionsForEachState)327 TEST_F(FullscreenControllerStateUnitTest, TransitionsForEachState) {
328 // A tab is needed for tab fullscreen.
329 AddTab(browser(), GURL(url::kAboutBlankURL));
330 TestTransitionsForEachState();
331 // Progress of test can be examined via LOG(INFO) << GetAndClearDebugLog();
332 }
333
334
335 // Individual tests for each pair of state and event ---------------------------
336
337 #define TEST_EVENT(state, event) \
338 TEST_F(FullscreenControllerStateUnitTest, state##__##event) { \
339 AddTab(browser(), GURL(url::kAboutBlankURL)); \
340 ASSERT_NO_FATAL_FAILURE(TestStateAndEvent(state, event)) \
341 << GetAndClearDebugLog(); \
342 }
343 // Progress of tests can be examined by inserting the following line:
344 // LOG(INFO) << GetAndClearDebugLog(); }
345
346 #include "chrome/browser/ui/fullscreen/fullscreen_controller_state_tests.h"
347
348
349 // Specific one-off tests for known issues -------------------------------------
350
351 // TODO(scheib) Toggling Tab fullscreen while pending Tab or
352 // Browser fullscreen is broken currently http://crbug.com/154196
TEST_F(FullscreenControllerStateUnitTest,DISABLED_ToggleTabWhenPendingBrowser)353 TEST_F(FullscreenControllerStateUnitTest,
354 DISABLED_ToggleTabWhenPendingBrowser) {
355 // Only possible without reentrancy.
356 if (FullscreenControllerStateTest::IsWindowFullscreenStateChangedReentrant())
357 return;
358 AddTab(browser(), GURL(url::kAboutBlankURL));
359 ASSERT_NO_FATAL_FAILURE(
360 TransitionToState(STATE_TO_BROWSER_FULLSCREEN_NO_CHROME))
361 << GetAndClearDebugLog();
362
363 ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_TRUE)) << GetAndClearDebugLog();
364 ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_FALSE)) << GetAndClearDebugLog();
365 ASSERT_TRUE(InvokeEvent(WINDOW_CHANGE)) << GetAndClearDebugLog();
366 }
367
368 // TODO(scheib) Toggling Tab fullscreen while pending Tab or
369 // Browser fullscreen is broken currently http://crbug.com/154196
TEST_F(FullscreenControllerStateUnitTest,DISABLED_ToggleTabWhenPendingTab)370 TEST_F(FullscreenControllerStateUnitTest, DISABLED_ToggleTabWhenPendingTab) {
371 // Only possible without reentrancy.
372 if (FullscreenControllerStateTest::IsWindowFullscreenStateChangedReentrant())
373 return;
374 AddTab(browser(), GURL(url::kAboutBlankURL));
375 ASSERT_NO_FATAL_FAILURE(
376 TransitionToState(STATE_TO_TAB_FULLSCREEN))
377 << GetAndClearDebugLog();
378
379 ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_TRUE)) << GetAndClearDebugLog();
380 ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_FALSE)) << GetAndClearDebugLog();
381 ASSERT_TRUE(InvokeEvent(WINDOW_CHANGE)) << GetAndClearDebugLog();
382 }
383
384 // Debugging utility: Display the transition tables. Intentionally disabled
TEST_F(FullscreenControllerStateUnitTest,DISABLED_DebugLogStateTables)385 TEST_F(FullscreenControllerStateUnitTest, DISABLED_DebugLogStateTables) {
386 std::ostringstream output;
387 output << "\n\nTransition Table:";
388 output << GetTransitionTableAsString();
389
390 output << "\n\nInitial transitions:";
391 output << GetStateTransitionsAsString();
392
393 // Calculate all transition pairs.
394 for (int state1_int = 0; state1_int < NUM_STATES; ++state1_int) {
395 State state1 = static_cast<State>(state1_int);
396 for (int state2_int = 0; state2_int < NUM_STATES; ++state2_int) {
397 State state2 = static_cast<State>(state2_int);
398 if (ShouldSkipStateAndEventPair(state1, EVENT_INVALID) ||
399 ShouldSkipStateAndEventPair(state2, EVENT_INVALID))
400 continue;
401 // Compute the transition
402 if (NextTransitionInShortestPath(state1, state2, NUM_STATES).state ==
403 STATE_INVALID) {
404 LOG(ERROR) << "Should be skipping state transitions for: "
405 << GetStateString(state1) << " " << GetStateString(state2);
406 }
407 }
408 }
409
410 output << "\n\nAll transitions:";
411 output << GetStateTransitionsAsString();
412 LOG(INFO) << output.str();
413 }
414
415 // Test that the fullscreen exit bubble is closed by
416 // WindowFullscreenStateChanged() if fullscreen is exited via BrowserWindow.
417 // This currently occurs when an extension exits fullscreen via changing the
418 // browser bounds.
TEST_F(FullscreenControllerStateUnitTest,ExitFullscreenViaBrowserWindow)419 TEST_F(FullscreenControllerStateUnitTest, ExitFullscreenViaBrowserWindow) {
420 AddTab(browser(), GURL(url::kAboutBlankURL));
421 ASSERT_TRUE(InvokeEvent(TOGGLE_FULLSCREEN));
422 ASSERT_TRUE(InvokeEvent(WINDOW_CHANGE));
423 ASSERT_TRUE(browser()->window()->IsFullscreen());
424 // Exit fullscreen without going through fullscreen controller.
425 browser()->window()->ExitFullscreen();
426 ChangeWindowFullscreenState();
427 EXPECT_EQ(FEB_TYPE_NONE,
428 browser()->fullscreen_controller()->GetFullscreenExitBubbleType());
429 }
430
431 // Test that switching tabs takes the browser out of tab fullscreen.
TEST_F(FullscreenControllerStateUnitTest,ExitTabFullscreenViaSwitchingTab)432 TEST_F(FullscreenControllerStateUnitTest, ExitTabFullscreenViaSwitchingTab) {
433 AddTab(browser(), GURL(url::kAboutBlankURL));
434 AddTab(browser(), GURL(url::kAboutBlankURL));
435 ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_TRUE));
436 ASSERT_TRUE(InvokeEvent(WINDOW_CHANGE));
437 ASSERT_TRUE(browser()->window()->IsFullscreen());
438
439 browser()->tab_strip_model()->SelectNextTab();
440 ChangeWindowFullscreenState();
441 EXPECT_FALSE(browser()->window()->IsFullscreen());
442 }
443
444 // Test that switching tabs via detaching the active tab (which is in tab
445 // fullscreen) takes the browser out of tab fullscreen. This case can
446 // occur if the user is in both tab fullscreen and immersive browser fullscreen.
TEST_F(FullscreenControllerStateUnitTest,ExitTabFullscreenViaDetachingTab)447 TEST_F(FullscreenControllerStateUnitTest, ExitTabFullscreenViaDetachingTab) {
448 AddTab(browser(), GURL(url::kAboutBlankURL));
449 AddTab(browser(), GURL(url::kAboutBlankURL));
450 ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_TRUE));
451 ASSERT_TRUE(InvokeEvent(WINDOW_CHANGE));
452 ASSERT_TRUE(browser()->window()->IsFullscreen());
453
454 scoped_ptr<content::WebContents> web_contents(
455 browser()->tab_strip_model()->DetachWebContentsAt(0));
456 ChangeWindowFullscreenState();
457 EXPECT_FALSE(browser()->window()->IsFullscreen());
458 }
459
460 // Test that replacing the web contents for a tab which is in tab fullscreen
461 // takes the browser out of tab fullscreen. This can occur if the user
462 // navigates to a prerendered page from a page which is tab fullscreen.
TEST_F(FullscreenControllerStateUnitTest,ExitTabFullscreenViaReplacingTab)463 TEST_F(FullscreenControllerStateUnitTest, ExitTabFullscreenViaReplacingTab) {
464 AddTab(browser(), GURL(url::kAboutBlankURL));
465 ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_TRUE));
466 ASSERT_TRUE(InvokeEvent(WINDOW_CHANGE));
467 ASSERT_TRUE(browser()->window()->IsFullscreen());
468
469 content::WebContents* new_web_contents = content::WebContents::Create(
470 content::WebContents::CreateParams(profile()));
471 scoped_ptr<content::WebContents> old_web_contents(
472 browser()->tab_strip_model()->ReplaceWebContentsAt(
473 0, new_web_contents));
474 ChangeWindowFullscreenState();
475 EXPECT_FALSE(browser()->window()->IsFullscreen());
476 }
477
478 // Tests that, in a browser configured for Fullscreen-Within-Tab mode,
479 // fullscreening a screen-captured tab will NOT cause any fullscreen state
480 // change to the browser window. Furthermore, the test switches between tabs to
481 // confirm a captured tab will be resized by FullscreenController to the capture
482 // video resolution once the widget is detached from the UI.
483 //
484 // See 'FullscreenWithinTab Note' in fullscreen_controller.h.
TEST_F(FullscreenControllerStateUnitTest,OneCapturedFullscreenedTab)485 TEST_F(FullscreenControllerStateUnitTest, OneCapturedFullscreenedTab) {
486 content::WebContentsDelegate* const wc_delegate =
487 static_cast<content::WebContentsDelegate*>(browser());
488 if (!wc_delegate->EmbedsFullscreenWidget()) {
489 LOG(WARNING) << "Skipping test since fullscreen-within-tab is disabled.";
490 return;
491 }
492
493 AddTab(browser(), GURL(url::kAboutBlankURL));
494 AddTab(browser(), GURL(url::kAboutBlankURL));
495 content::WebContents* const first_tab =
496 browser()->tab_strip_model()->GetWebContentsAt(0);
497 content::WebContents* const second_tab =
498 browser()->tab_strip_model()->GetWebContentsAt(1);
499
500 // Activate the first tab and tell its WebContents it is being captured.
501 browser()->tab_strip_model()->ActivateTabAt(0, true);
502 const gfx::Size kCaptureSize(1280, 720);
503 first_tab->IncrementCapturerCount(kCaptureSize);
504 ASSERT_FALSE(browser()->window()->IsFullscreen());
505 ASSERT_FALSE(wc_delegate->IsFullscreenForTabOrPending(first_tab));
506 ASSERT_FALSE(wc_delegate->IsFullscreenForTabOrPending(second_tab));
507 ASSERT_FALSE(GetFullscreenController()->IsWindowFullscreenForTabOrPending());
508
509 // Enter tab fullscreen. Since the tab is being captured, the browser window
510 // should not expand to fill the screen.
511 ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_TRUE));
512 EXPECT_FALSE(browser()->window()->IsFullscreen());
513 EXPECT_TRUE(wc_delegate->IsFullscreenForTabOrPending(first_tab));
514 EXPECT_FALSE(wc_delegate->IsFullscreenForTabOrPending(second_tab));
515 EXPECT_FALSE(GetFullscreenController()->IsWindowFullscreenForTabOrPending());
516
517 // Switch to the other tab. Check that the first tab was resized to the
518 // WebContents' preferred size.
519 browser()->tab_strip_model()->ActivateTabAt(1, true);
520 EXPECT_FALSE(browser()->window()->IsFullscreen());
521 EXPECT_TRUE(wc_delegate->IsFullscreenForTabOrPending(first_tab));
522 EXPECT_FALSE(wc_delegate->IsFullscreenForTabOrPending(second_tab));
523 EXPECT_FALSE(GetFullscreenController()->IsWindowFullscreenForTabOrPending());
524 // TODO(miu): Need to make an adjustment to content::WebContentsViewMac for
525 // the following to work:
526 #if !defined(OS_MACOSX)
527 EXPECT_EQ(kCaptureSize, first_tab->GetViewBounds().size());
528 #endif
529
530 // Switch back to the first tab and exit fullscreen.
531 browser()->tab_strip_model()->ActivateTabAt(0, true);
532 EXPECT_FALSE(browser()->window()->IsFullscreen());
533 EXPECT_TRUE(wc_delegate->IsFullscreenForTabOrPending(first_tab));
534 EXPECT_FALSE(wc_delegate->IsFullscreenForTabOrPending(second_tab));
535 EXPECT_FALSE(GetFullscreenController()->IsWindowFullscreenForTabOrPending());
536 ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_FALSE));
537 EXPECT_FALSE(browser()->window()->IsFullscreen());
538 EXPECT_FALSE(wc_delegate->IsFullscreenForTabOrPending(first_tab));
539 EXPECT_FALSE(wc_delegate->IsFullscreenForTabOrPending(second_tab));
540 EXPECT_FALSE(GetFullscreenController()->IsWindowFullscreenForTabOrPending());
541 }
542
543 // Tests that, in a browser configured for Fullscreen-Within-Tab mode, more than
544 // one tab can be in fullscreen mode at the same time without interfering with
545 // each other. One tab is being screen-captured and is toggled into fullscreen
546 // mode, and then the user switches to another tab not being screen-captured and
547 // fullscreens it. The first tab's fullscreen toggle does not affect the
548 // browser window fullscreen, while the second one's does. Then, the order of
549 // operations is reversed.
550 //
551 // See 'FullscreenWithinTab Note' in fullscreen_controller.h.
TEST_F(FullscreenControllerStateUnitTest,TwoFullscreenedTabsOneCaptured)552 TEST_F(FullscreenControllerStateUnitTest, TwoFullscreenedTabsOneCaptured) {
553 content::WebContentsDelegate* const wc_delegate =
554 static_cast<content::WebContentsDelegate*>(browser());
555 if (!wc_delegate->EmbedsFullscreenWidget()) {
556 LOG(WARNING) << "Skipping test since fullscreen-within-tab is disabled.";
557 return;
558 }
559
560 AddTab(browser(), GURL(url::kAboutBlankURL));
561 AddTab(browser(), GURL(url::kAboutBlankURL));
562 content::WebContents* const first_tab =
563 browser()->tab_strip_model()->GetWebContentsAt(0);
564 content::WebContents* const second_tab =
565 browser()->tab_strip_model()->GetWebContentsAt(1);
566
567 // Start capturing the first tab, fullscreen it, then switch to the second tab
568 // and fullscreen that. The second tab will cause the browser window to
569 // expand to fill the screen.
570 browser()->tab_strip_model()->ActivateTabAt(0, true);
571 const gfx::Size kCaptureSize(1280, 720);
572 first_tab->IncrementCapturerCount(kCaptureSize);
573 ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_TRUE));
574 EXPECT_FALSE(browser()->window()->IsFullscreen());
575 EXPECT_TRUE(wc_delegate->IsFullscreenForTabOrPending(first_tab));
576 EXPECT_FALSE(wc_delegate->IsFullscreenForTabOrPending(second_tab));
577 EXPECT_FALSE(GetFullscreenController()->IsWindowFullscreenForTabOrPending());
578 browser()->tab_strip_model()->ActivateTabAt(1, true);
579 ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_TRUE));
580 ASSERT_TRUE(InvokeEvent(WINDOW_CHANGE));
581 EXPECT_TRUE(browser()->window()->IsFullscreen());
582 EXPECT_TRUE(wc_delegate->IsFullscreenForTabOrPending(first_tab));
583 EXPECT_TRUE(wc_delegate->IsFullscreenForTabOrPending(second_tab));
584 EXPECT_TRUE(GetFullscreenController()->IsWindowFullscreenForTabOrPending());
585
586 // Now exit fullscreen while still in the second tab. The browser window
587 // should no longer be fullscreened.
588 ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_FALSE));
589 ASSERT_TRUE(InvokeEvent(WINDOW_CHANGE));
590 EXPECT_FALSE(browser()->window()->IsFullscreen());
591 EXPECT_TRUE(wc_delegate->IsFullscreenForTabOrPending(first_tab));
592 EXPECT_FALSE(wc_delegate->IsFullscreenForTabOrPending(second_tab));
593 EXPECT_FALSE(GetFullscreenController()->IsWindowFullscreenForTabOrPending());
594
595 // Finally, exit fullscreen on the captured tab.
596 browser()->tab_strip_model()->ActivateTabAt(0, true);
597 ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_FALSE));
598 EXPECT_FALSE(browser()->window()->IsFullscreen());
599 EXPECT_FALSE(wc_delegate->IsFullscreenForTabOrPending(first_tab));
600 EXPECT_FALSE(wc_delegate->IsFullscreenForTabOrPending(second_tab));
601 EXPECT_FALSE(GetFullscreenController()->IsWindowFullscreenForTabOrPending());
602 }
603
604 // Tests that, in a browser configured for Fullscreen-Within-Tab mode, more than
605 // one tab can be in fullscreen mode at the same time. This is like the
606 // TwoFullscreenedTabsOneCaptured test above, except that the screen-captured
607 // tab exits fullscreen mode while the second tab is still in the foreground.
608 // When the first tab exits fullscreen, the fullscreen state of the second tab
609 // and the browser window should remain unchanged.
610 //
611 // See 'FullscreenWithinTab Note' in fullscreen_controller.h.
TEST_F(FullscreenControllerStateUnitTest,BackgroundCapturedTabExitsFullscreen)612 TEST_F(FullscreenControllerStateUnitTest,
613 BackgroundCapturedTabExitsFullscreen) {
614 content::WebContentsDelegate* const wc_delegate =
615 static_cast<content::WebContentsDelegate*>(browser());
616 if (!wc_delegate->EmbedsFullscreenWidget()) {
617 LOG(WARNING) << "Skipping test since fullscreen-within-tab is disabled.";
618 return;
619 }
620
621 AddTab(browser(), GURL(url::kAboutBlankURL));
622 AddTab(browser(), GURL(url::kAboutBlankURL));
623 content::WebContents* const first_tab =
624 browser()->tab_strip_model()->GetWebContentsAt(0);
625 content::WebContents* const second_tab =
626 browser()->tab_strip_model()->GetWebContentsAt(1);
627
628 // Start capturing the first tab, fullscreen it, then switch to the second tab
629 // and fullscreen that. The second tab will cause the browser window to
630 // expand to fill the screen.
631 browser()->tab_strip_model()->ActivateTabAt(0, true);
632 const gfx::Size kCaptureSize(1280, 720);
633 first_tab->IncrementCapturerCount(kCaptureSize);
634 ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_TRUE));
635 EXPECT_FALSE(browser()->window()->IsFullscreen());
636 EXPECT_TRUE(wc_delegate->IsFullscreenForTabOrPending(first_tab));
637 EXPECT_FALSE(wc_delegate->IsFullscreenForTabOrPending(second_tab));
638 EXPECT_FALSE(GetFullscreenController()->IsWindowFullscreenForTabOrPending());
639 browser()->tab_strip_model()->ActivateTabAt(1, true);
640 ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_TRUE));
641 ASSERT_TRUE(InvokeEvent(WINDOW_CHANGE));
642 EXPECT_TRUE(browser()->window()->IsFullscreen());
643 EXPECT_TRUE(wc_delegate->IsFullscreenForTabOrPending(first_tab));
644 EXPECT_TRUE(wc_delegate->IsFullscreenForTabOrPending(second_tab));
645 EXPECT_TRUE(GetFullscreenController()->IsWindowFullscreenForTabOrPending());
646
647 // Now, the first tab (backgrounded) exits fullscreen. This should not affect
648 // the second tab's fullscreen, nor the state of the browser window.
649 GetFullscreenController()->ToggleFullscreenModeForTab(first_tab, false);
650 EXPECT_TRUE(browser()->window()->IsFullscreen());
651 EXPECT_FALSE(wc_delegate->IsFullscreenForTabOrPending(first_tab));
652 EXPECT_TRUE(wc_delegate->IsFullscreenForTabOrPending(second_tab));
653 EXPECT_TRUE(GetFullscreenController()->IsWindowFullscreenForTabOrPending());
654
655 // Finally, exit fullscreen on the second tab.
656 ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_FALSE));
657 ASSERT_TRUE(InvokeEvent(WINDOW_CHANGE));
658 EXPECT_FALSE(browser()->window()->IsFullscreen());
659 EXPECT_FALSE(wc_delegate->IsFullscreenForTabOrPending(first_tab));
660 EXPECT_FALSE(wc_delegate->IsFullscreenForTabOrPending(second_tab));
661 EXPECT_FALSE(GetFullscreenController()->IsWindowFullscreenForTabOrPending());
662 }
663
664 // Tests that, in a browser configured for Fullscreen-Within-Tab mode,
665 // fullscreening a screen-captured tab will NOT cause any fullscreen state
666 // change to the browser window. Then, toggling Browser Fullscreen mode should
667 // fullscreen the browser window, but this should behave fully independently of
668 // the tab's fullscreen state.
669 //
670 // See 'FullscreenWithinTab Note' in fullscreen_controller.h.
TEST_F(FullscreenControllerStateUnitTest,OneCapturedTabFullscreenedBeforeBrowserFullscreen)671 TEST_F(FullscreenControllerStateUnitTest,
672 OneCapturedTabFullscreenedBeforeBrowserFullscreen) {
673 content::WebContentsDelegate* const wc_delegate =
674 static_cast<content::WebContentsDelegate*>(browser());
675 if (!wc_delegate->EmbedsFullscreenWidget()) {
676 LOG(WARNING) << "Skipping test since fullscreen-within-tab is disabled.";
677 return;
678 }
679
680 AddTab(browser(), GURL(url::kAboutBlankURL));
681 content::WebContents* const tab =
682 browser()->tab_strip_model()->GetWebContentsAt(0);
683
684 // Start capturing the tab and fullscreen it. The state of the browser window
685 // should remain unchanged.
686 browser()->tab_strip_model()->ActivateTabAt(0, true);
687 const gfx::Size kCaptureSize(1280, 720);
688 tab->IncrementCapturerCount(kCaptureSize);
689 ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_TRUE));
690 EXPECT_TRUE(wc_delegate->IsFullscreenForTabOrPending(tab));
691 EXPECT_FALSE(GetFullscreenController()->IsWindowFullscreenForTabOrPending());
692 EXPECT_FALSE(GetFullscreenController()->IsFullscreenForBrowser());
693
694 // Now, toggle into Browser Fullscreen mode. The browser window should now be
695 // fullscreened.
696 ASSERT_TRUE(InvokeEvent(TOGGLE_FULLSCREEN));
697 ASSERT_TRUE(InvokeEvent(WINDOW_CHANGE));
698 EXPECT_TRUE(wc_delegate->IsFullscreenForTabOrPending(tab));
699 EXPECT_FALSE(GetFullscreenController()->IsWindowFullscreenForTabOrPending());
700 EXPECT_TRUE(GetFullscreenController()->IsFullscreenForBrowser());
701
702 // Now, toggle back out of Browser Fullscreen mode. The browser window exits
703 // fullscreen mode, but the tab stays in fullscreen mode.
704 ASSERT_TRUE(InvokeEvent(TOGGLE_FULLSCREEN));
705 ASSERT_TRUE(InvokeEvent(WINDOW_CHANGE));
706 EXPECT_TRUE(wc_delegate->IsFullscreenForTabOrPending(tab));
707 EXPECT_FALSE(GetFullscreenController()->IsWindowFullscreenForTabOrPending());
708 EXPECT_FALSE(GetFullscreenController()->IsFullscreenForBrowser());
709
710 // Finally, toggle back into Browser Fullscreen mode and then toggle out of
711 // tab fullscreen mode. The browser window should stay fullscreened, while
712 // the tab exits fullscreen mode.
713 ASSERT_TRUE(InvokeEvent(TOGGLE_FULLSCREEN));
714 ASSERT_TRUE(InvokeEvent(WINDOW_CHANGE));
715 ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_FALSE));
716 EXPECT_FALSE(wc_delegate->IsFullscreenForTabOrPending(tab));
717 EXPECT_FALSE(GetFullscreenController()->IsWindowFullscreenForTabOrPending());
718 EXPECT_TRUE(GetFullscreenController()->IsFullscreenForBrowser());
719 }
720
721 // Tests that the state of a fullscreened, screen-captured tab is preserved if
722 // the tab is detached from one Browser window and attached to another.
723 //
724 // See 'FullscreenWithinTab Note' in fullscreen_controller.h.
TEST_F(FullscreenControllerStateUnitTest,CapturedFullscreenedTabTransferredBetweenBrowserWindows)725 TEST_F(FullscreenControllerStateUnitTest,
726 CapturedFullscreenedTabTransferredBetweenBrowserWindows) {
727 content::WebContentsDelegate* const wc_delegate =
728 static_cast<content::WebContentsDelegate*>(browser());
729 if (!wc_delegate->EmbedsFullscreenWidget()) {
730 LOG(WARNING) << "Skipping test since fullscreen-within-tab is disabled.";
731 return;
732 }
733
734 AddTab(browser(), GURL(url::kAboutBlankURL));
735 AddTab(browser(), GURL(url::kAboutBlankURL));
736 content::WebContents* const tab =
737 browser()->tab_strip_model()->GetWebContentsAt(0);
738
739 // Activate the first tab and tell its WebContents it is being captured.
740 browser()->tab_strip_model()->ActivateTabAt(0, true);
741 const gfx::Size kCaptureSize(1280, 720);
742 tab->IncrementCapturerCount(kCaptureSize);
743 ASSERT_FALSE(browser()->window()->IsFullscreen());
744 ASSERT_FALSE(wc_delegate->IsFullscreenForTabOrPending(tab));
745 ASSERT_FALSE(GetFullscreenController()->IsWindowFullscreenForTabOrPending());
746
747 // Enter tab fullscreen. Since the tab is being captured, the browser window
748 // should not expand to fill the screen.
749 ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_TRUE));
750 EXPECT_FALSE(browser()->window()->IsFullscreen());
751 EXPECT_TRUE(wc_delegate->IsFullscreenForTabOrPending(tab));
752 EXPECT_FALSE(GetFullscreenController()->IsWindowFullscreenForTabOrPending());
753
754 // Create the second browser window.
755 const scoped_ptr<BrowserWindow> second_browser_window(CreateBrowserWindow());
756 const scoped_ptr<Browser> second_browser(CreateBrowser(
757 browser()->profile(),
758 browser()->type(),
759 false,
760 browser()->host_desktop_type(),
761 second_browser_window.get()));
762 AddTab(second_browser.get(), GURL(url::kAboutBlankURL));
763 content::WebContentsDelegate* const second_wc_delegate =
764 static_cast<content::WebContentsDelegate*>(second_browser.get());
765
766 // Detach the tab from the first browser window and attach it to the second.
767 // The tab should remain in fullscreen mode and neither browser window should
768 // have expanded. It is correct for both FullscreenControllers to agree the
769 // tab is in fullscreen mode.
770 browser()->tab_strip_model()->DetachWebContentsAt(0);
771 second_browser->tab_strip_model()->
772 InsertWebContentsAt(0, tab, TabStripModel::ADD_ACTIVE);
773 EXPECT_FALSE(browser()->window()->IsFullscreen());
774 EXPECT_FALSE(second_browser->window()->IsFullscreen());
775 EXPECT_TRUE(wc_delegate->IsFullscreenForTabOrPending(tab));
776 EXPECT_TRUE(second_wc_delegate->IsFullscreenForTabOrPending(tab));
777 EXPECT_FALSE(GetFullscreenController()->IsWindowFullscreenForTabOrPending());
778 EXPECT_FALSE(second_browser->fullscreen_controller()->
779 IsWindowFullscreenForTabOrPending());
780
781 // Now, detach and reattach it back to the first browser window. Again, the
782 // tab should remain in fullscreen mode and neither browser window should have
783 // expanded.
784 second_browser->tab_strip_model()->DetachWebContentsAt(0);
785 browser()->tab_strip_model()->
786 InsertWebContentsAt(0, tab, TabStripModel::ADD_ACTIVE);
787 EXPECT_FALSE(browser()->window()->IsFullscreen());
788 EXPECT_FALSE(second_browser->window()->IsFullscreen());
789 EXPECT_TRUE(wc_delegate->IsFullscreenForTabOrPending(tab));
790 EXPECT_TRUE(second_wc_delegate->IsFullscreenForTabOrPending(tab));
791 EXPECT_FALSE(GetFullscreenController()->IsWindowFullscreenForTabOrPending());
792 EXPECT_FALSE(second_browser->fullscreen_controller()->
793 IsWindowFullscreenForTabOrPending());
794
795 // Exit fullscreen.
796 ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_FALSE));
797 EXPECT_FALSE(browser()->window()->IsFullscreen());
798 EXPECT_FALSE(wc_delegate->IsFullscreenForTabOrPending(tab));
799 EXPECT_FALSE(second_wc_delegate->IsFullscreenForTabOrPending(tab));
800 EXPECT_FALSE(GetFullscreenController()->IsWindowFullscreenForTabOrPending());
801 EXPECT_FALSE(second_browser->fullscreen_controller()->
802 IsWindowFullscreenForTabOrPending());
803
804 // Required tear-down specific to this test.
805 second_browser->tab_strip_model()->CloseAllTabs();
806 }
807