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