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(); 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); 58 WindowState state() const { return state_; } 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 78 FullscreenControllerTestWindow::FullscreenControllerTestWindow() 79 : state_(NORMAL), 80 mac_with_chrome_mode_(false), 81 browser_(NULL) { 82 } 83 84 void FullscreenControllerTestWindow::EnterFullscreen( 85 const GURL& url, FullscreenExitBubbleType type) { 86 EnterFullscreen(false); 87 } 88 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 99 bool FullscreenControllerTestWindow::ShouldHideUIForFullscreen() const { 100 return IsFullscreen(); 101 } 102 103 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) 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 120 bool FullscreenControllerTestWindow::IsInMetroSnapMode() const { 121 return state_ == METRO_SNAP; 122 } 123 #endif 124 125 #if defined(OS_MACOSX) 126 void FullscreenControllerTestWindow::EnterFullscreenWithChrome() { 127 EnterFullscreen(true); 128 } 129 130 bool FullscreenControllerTestWindow::IsFullscreenWithChrome() { 131 return IsFullscreen() && mac_with_chrome_mode_; 132 } 133 134 bool FullscreenControllerTestWindow::IsFullscreenWithoutChrome() { 135 return IsFullscreen() && !mac_with_chrome_mode_; 136 } 137 #endif 138 139 // static 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 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 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 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 229 FullscreenControllerStateUnitTest::FullscreenControllerStateUnitTest () 230 : window_(NULL) { 231 } 232 233 void FullscreenControllerStateUnitTest::SetUp() { 234 BrowserWithTestWindowTest::SetUp(); 235 window_->set_browser(browser()); 236 } 237 238 BrowserWindow* FullscreenControllerStateUnitTest::CreateBrowserWindow() { 239 window_ = new FullscreenControllerTestWindow(); 240 return window_; // BrowserWithTestWindowTest takes ownership. 241 } 242 243 void FullscreenControllerStateUnitTest::ChangeWindowFullscreenState() { 244 window_->ChangeWindowFullscreenState(); 245 } 246 247 const char* FullscreenControllerStateUnitTest::GetWindowStateString() { 248 return FullscreenControllerTestWindow::GetWindowStateString(window_->state()); 249 } 250 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 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 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. 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 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 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 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. 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. 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. 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. 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. 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. 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. 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. 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. 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