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 "chrome/browser/ui/fullscreen/fullscreen_controller_state_test.h" 6 7 #include <memory.h> 8 9 #include <iomanip> 10 #include <iostream> 11 12 #include "chrome/browser/fullscreen.h" 13 #include "chrome/browser/ui/browser.h" 14 #include "chrome/browser/ui/browser_window.h" 15 #include "chrome/browser/ui/fullscreen/fullscreen_controller.h" 16 #include "chrome/browser/ui/fullscreen/fullscreen_controller_test.h" 17 #include "chrome/browser/ui/tabs/tab_strip_model.h" 18 #include "content/public/browser/web_contents.h" 19 #include "content/public/common/url_constants.h" 20 #include "testing/gtest/include/gtest/gtest.h" 21 22 #if defined(OS_MACOSX) 23 #include "base/mac/mac_util.h" 24 #endif 25 26 namespace { 27 28 bool SupportsMacSystemFullscreen() { 29 #if defined(OS_MACOSX) 30 return chrome::mac::SupportsSystemFullscreen(); 31 #else 32 return false; 33 #endif 34 } 35 36 } // namespace 37 38 FullscreenControllerStateTest::FullscreenControllerStateTest() 39 : state_(STATE_NORMAL), 40 last_notification_received_state_(STATE_NORMAL) { 41 // Human specified state machine data. 42 // For each state, for each event, define the resulting state. 43 State transition_table_data[][NUM_EVENTS] = { 44 { // STATE_NORMAL: 45 STATE_TO_BROWSER_FULLSCREEN_NO_CHROME, // Event TOGGLE_FULLSCREEN 46 STATE_TO_BROWSER_FULLSCREEN_WITH_CHROME,// Event TOGGLE_FULLSCREEN_CHROME 47 STATE_TO_TAB_FULLSCREEN, // Event TAB_FULLSCREEN_TRUE 48 STATE_NORMAL, // Event TAB_FULLSCREEN_FALSE 49 STATE_METRO_SNAP, // Event METRO_SNAP_TRUE 50 STATE_NORMAL, // Event METRO_SNAP_FALSE 51 STATE_NORMAL, // Event BUBBLE_EXIT_LINK 52 STATE_NORMAL, // Event BUBBLE_ALLOW 53 STATE_NORMAL, // Event BUBBLE_DENY 54 STATE_NORMAL, // Event WINDOW_CHANGE 55 }, 56 { // STATE_BROWSER_FULLSCREEN_NO_CHROME: 57 STATE_TO_NORMAL, // Event TOGGLE_FULLSCREEN 58 STATE_BROWSER_FULLSCREEN_WITH_CHROME, // Event TOGGLE_FULLSCREEN_CHROME 59 STATE_TAB_BROWSER_FULLSCREEN, // Event TAB_FULLSCREEN_TRUE 60 STATE_BROWSER_FULLSCREEN_NO_CHROME, // Event TAB_FULLSCREEN_FALSE 61 STATE_METRO_SNAP, // Event METRO_SNAP_TRUE 62 STATE_BROWSER_FULLSCREEN_NO_CHROME, // Event METRO_SNAP_FALSE 63 STATE_TO_NORMAL, // Event BUBBLE_EXIT_LINK 64 STATE_BROWSER_FULLSCREEN_NO_CHROME, // Event BUBBLE_ALLOW 65 STATE_BROWSER_FULLSCREEN_NO_CHROME, // Event BUBBLE_DENY 66 STATE_BROWSER_FULLSCREEN_NO_CHROME, // Event WINDOW_CHANGE 67 }, 68 { // STATE_BROWSER_FULLSCREEN_WITH_CHROME: 69 STATE_BROWSER_FULLSCREEN_NO_CHROME, // Event TOGGLE_FULLSCREEN 70 STATE_TO_NORMAL, // Event TOGGLE_FULLSCREEN_CHROME 71 STATE_TAB_BROWSER_FULLSCREEN_CHROME, // Event TAB_FULLSCREEN_TRUE 72 STATE_BROWSER_FULLSCREEN_WITH_CHROME, // Event TAB_FULLSCREEN_FALSE 73 STATE_BROWSER_FULLSCREEN_WITH_CHROME, // Event METRO_SNAP_TRUE 74 STATE_BROWSER_FULLSCREEN_WITH_CHROME, // Event METRO_SNAP_FALSE 75 STATE_TO_NORMAL, // Event BUBBLE_EXIT_LINK 76 STATE_BROWSER_FULLSCREEN_WITH_CHROME, // Event BUBBLE_ALLOW 77 STATE_BROWSER_FULLSCREEN_WITH_CHROME, // Event BUBBLE_DENY 78 STATE_BROWSER_FULLSCREEN_WITH_CHROME, // Event WINDOW_CHANGE 79 }, 80 { // STATE_METRO_SNAP: 81 STATE_METRO_SNAP, // Event TOGGLE_FULLSCREEN 82 STATE_METRO_SNAP, // Event TOGGLE_FULLSCREEN_CHROME 83 STATE_METRO_SNAP, // Event TAB_FULLSCREEN_TRUE 84 STATE_METRO_SNAP, // Event TAB_FULLSCREEN_FALSE 85 STATE_METRO_SNAP, // Event METRO_SNAP_TRUE 86 STATE_NORMAL, // Event METRO_SNAP_FALSE 87 STATE_METRO_SNAP, // Event BUBBLE_EXIT_LINK 88 STATE_METRO_SNAP, // Event BUBBLE_ALLOW 89 STATE_METRO_SNAP, // Event BUBBLE_DENY 90 STATE_METRO_SNAP, // Event WINDOW_CHANGE 91 }, 92 { // STATE_TAB_FULLSCREEN: 93 STATE_TO_NORMAL, // Event TOGGLE_FULLSCREEN 94 STATE_TO_NORMAL, // Event TOGGLE_FULLSCREEN_CHROME 95 STATE_TAB_FULLSCREEN, // Event TAB_FULLSCREEN_TRUE 96 STATE_TO_NORMAL, // Event TAB_FULLSCREEN_FALSE 97 STATE_METRO_SNAP, // Event METRO_SNAP_TRUE 98 STATE_TAB_FULLSCREEN, // Event METRO_SNAP_FALSE 99 STATE_TO_NORMAL, // Event BUBBLE_EXIT_LINK 100 STATE_TAB_FULLSCREEN, // Event BUBBLE_ALLOW 101 STATE_TO_NORMAL, // Event BUBBLE_DENY 102 STATE_TAB_FULLSCREEN, // Event WINDOW_CHANGE 103 }, 104 { // STATE_TAB_BROWSER_FULLSCREEN: 105 STATE_TO_NORMAL, // Event TOGGLE_FULLSCREEN 106 STATE_TO_NORMAL, // Event TOGGLE_FULLSCREEN_CHROME 107 STATE_TAB_BROWSER_FULLSCREEN, // Event TAB_FULLSCREEN_TRUE 108 STATE_BROWSER_FULLSCREEN_NO_CHROME, // Event TAB_FULLSCREEN_FALSE 109 STATE_METRO_SNAP, // Event METRO_SNAP_TRUE 110 STATE_TAB_BROWSER_FULLSCREEN, // Event METRO_SNAP_FALSE 111 STATE_BROWSER_FULLSCREEN_NO_CHROME, // Event BUBBLE_EXIT_LINK 112 STATE_TAB_BROWSER_FULLSCREEN, // Event BUBBLE_ALLOW 113 STATE_BROWSER_FULLSCREEN_NO_CHROME, // Event BUBBLE_DENY 114 STATE_TAB_BROWSER_FULLSCREEN, // Event WINDOW_CHANGE 115 }, 116 { // STATE_TAB_BROWSER_FULLSCREEN_CHROME: 117 STATE_TO_NORMAL, // Event TOGGLE_FULLSCREEN 118 STATE_TO_NORMAL, // Event TOGGLE_FULLSCREEN_CHROME 119 STATE_TAB_BROWSER_FULLSCREEN_CHROME, // Event TAB_FULLSCREEN_TRUE 120 STATE_BROWSER_FULLSCREEN_WITH_CHROME, // Event TAB_FULLSCREEN_FALSE 121 STATE_METRO_SNAP, // Event METRO_SNAP_TRUE 122 STATE_TAB_BROWSER_FULLSCREEN_CHROME, // Event METRO_SNAP_FALSE 123 STATE_BROWSER_FULLSCREEN_WITH_CHROME, // Event BUBBLE_EXIT_LINK 124 STATE_TAB_BROWSER_FULLSCREEN_CHROME, // Event BUBBLE_ALLOW 125 STATE_BROWSER_FULLSCREEN_WITH_CHROME, // Event BUBBLE_DENY 126 STATE_TAB_BROWSER_FULLSCREEN_CHROME, // Event WINDOW_CHANGE 127 }, 128 { // STATE_TO_NORMAL: 129 STATE_TO_NORMAL, // Event TOGGLE_FULLSCREEN 130 STATE_TO_BROWSER_FULLSCREEN_WITH_CHROME,// Event TOGGLE_FULLSCREEN_CHROME 131 // TODO(scheib) Should be a route back to TAB. http://crbug.com/154196 132 STATE_TO_NORMAL, // Event TAB_FULLSCREEN_TRUE 133 STATE_TO_NORMAL, // Event TAB_FULLSCREEN_FALSE 134 STATE_METRO_SNAP, // Event METRO_SNAP_TRUE 135 STATE_TO_NORMAL, // Event METRO_SNAP_FALSE 136 STATE_TO_NORMAL, // Event BUBBLE_EXIT_LINK 137 STATE_TO_NORMAL, // Event BUBBLE_ALLOW 138 STATE_TO_NORMAL, // Event BUBBLE_DENY 139 STATE_NORMAL, // Event WINDOW_CHANGE 140 }, 141 { // STATE_TO_BROWSER_FULLSCREEN_NO_CHROME: 142 STATE_TO_BROWSER_FULLSCREEN_NO_CHROME, // Event TOGGLE_FULLSCREEN 143 STATE_TO_BROWSER_FULLSCREEN_WITH_CHROME,// Event TOGGLE_FULLSCREEN_CHROME 144 // TODO(scheib) Should be a route to TAB_BROWSER http://crbug.com/154196 145 STATE_TO_BROWSER_FULLSCREEN_NO_CHROME, // Event TAB_FULLSCREEN_TRUE 146 STATE_TO_BROWSER_FULLSCREEN_NO_CHROME, // Event TAB_FULLSCREEN_FALSE 147 STATE_METRO_SNAP, // Event METRO_SNAP_TRUE 148 STATE_TO_BROWSER_FULLSCREEN_NO_CHROME, // Event METRO_SNAP_FALSE 149 #if defined(OS_MACOSX) 150 // Mac window reports fullscreen immediately and an exit triggers exit. 151 STATE_TO_NORMAL, // Event BUBBLE_EXIT_LINK 152 #else 153 STATE_TO_BROWSER_FULLSCREEN_NO_CHROME, // Event BUBBLE_EXIT_LINK 154 #endif 155 STATE_TO_BROWSER_FULLSCREEN_NO_CHROME, // Event BUBBLE_ALLOW 156 STATE_TO_BROWSER_FULLSCREEN_NO_CHROME, // Event BUBBLE_DENY 157 STATE_BROWSER_FULLSCREEN_NO_CHROME, // Event WINDOW_CHANGE 158 }, 159 { // STATE_TO_BROWSER_FULLSCREEN_WITH_CHROME: 160 STATE_TO_BROWSER_FULLSCREEN_NO_CHROME, // Event TOGGLE_FULLSCREEN 161 STATE_TO_NORMAL, // Event TOGGLE_FULLSCREEN_CHROME 162 // TODO(scheib) Should be a route to TAB_BROWSER http://crbug.com/154196 163 STATE_TAB_BROWSER_FULLSCREEN, // Event TAB_FULLSCREEN_TRUE 164 STATE_TO_BROWSER_FULLSCREEN_WITH_CHROME,// Event TAB_FULLSCREEN_FALSE 165 STATE_TO_BROWSER_FULLSCREEN_WITH_CHROME,// Event METRO_SNAP_TRUE 166 STATE_TO_BROWSER_FULLSCREEN_WITH_CHROME,// Event METRO_SNAP_FALSE 167 STATE_TO_NORMAL, // Event BUBBLE_EXIT_LINK 168 STATE_TO_BROWSER_FULLSCREEN_WITH_CHROME,// Event BUBBLE_ALLOW 169 STATE_TO_BROWSER_FULLSCREEN_WITH_CHROME,// Event BUBBLE_DENY 170 STATE_BROWSER_FULLSCREEN_WITH_CHROME, // Event WINDOW_CHANGE 171 }, 172 { // STATE_TO_TAB_FULLSCREEN: 173 // TODO(scheib) Should be a route to TAB_BROWSER http://crbug.com/154196 174 STATE_TO_TAB_FULLSCREEN, // Event TOGGLE_FULLSCREEN 175 STATE_TO_NORMAL, // Event TOGGLE_FULLSCREEN_CHROME 176 STATE_TO_TAB_FULLSCREEN, // Event TAB_FULLSCREEN_TRUE 177 #if defined(OS_MACOSX) 178 // Mac runs as expected due to a forced NotifyTabOfExitIfNecessary(); 179 STATE_TO_NORMAL, // Event TAB_FULLSCREEN_FALSE 180 #else 181 // TODO(scheib) Should be a route back to NORMAL. http://crbug.com/154196 182 STATE_TO_BROWSER_FULLSCREEN_NO_CHROME, // Event TAB_FULLSCREEN_FALSE 183 #endif 184 STATE_METRO_SNAP, // Event METRO_SNAP_TRUE 185 STATE_TO_TAB_FULLSCREEN, // Event METRO_SNAP_FALSE 186 #if defined(OS_MACOSX) 187 // Mac window reports fullscreen immediately and an exit triggers exit. 188 STATE_TO_NORMAL, // Event BUBBLE_EXIT_LINK 189 #else 190 STATE_TO_TAB_FULLSCREEN, // Event BUBBLE_EXIT_LINK 191 #endif 192 STATE_TO_TAB_FULLSCREEN, // Event BUBBLE_ALLOW 193 #if defined(OS_MACOSX) 194 // Mac window reports fullscreen immediately and an exit triggers exit. 195 STATE_TO_NORMAL, // Event BUBBLE_DENY 196 #else 197 STATE_TO_TAB_FULLSCREEN, // Event BUBBLE_DENY 198 #endif 199 STATE_TAB_FULLSCREEN, // Event WINDOW_CHANGE 200 }, 201 }; 202 COMPILE_ASSERT(sizeof(transition_table_data) == sizeof(transition_table_), 203 transition_table_incorrect_size); 204 memcpy(transition_table_, transition_table_data, 205 sizeof(transition_table_data)); 206 207 // Verify that transition_table_ has been completely defined. 208 for (int source = 0; source < NUM_STATES; ++source) { 209 for (int event = 0; event < NUM_EVENTS; ++event) { 210 EXPECT_NE(transition_table_[source][event], STATE_INVALID); 211 EXPECT_GE(transition_table_[source][event], 0); 212 EXPECT_LT(transition_table_[source][event], NUM_STATES); 213 } 214 } 215 216 // Copy transition_table_ data into state_transitions_ table. 217 for (int source = 0; source < NUM_STATES; ++source) { 218 for (int event = 0; event < NUM_EVENTS; ++event) { 219 if (ShouldSkipStateAndEventPair(static_cast<State>(source), 220 static_cast<Event>(event))) 221 continue; 222 State destination = transition_table_[source][event]; 223 state_transitions_[source][destination].event = static_cast<Event>(event); 224 state_transitions_[source][destination].state = destination; 225 state_transitions_[source][destination].distance = 1; 226 } 227 } 228 } 229 230 FullscreenControllerStateTest::~FullscreenControllerStateTest() { 231 } 232 233 // static 234 const char* FullscreenControllerStateTest::GetStateString(State state) { 235 switch (state) { 236 ENUM_TO_STRING(STATE_NORMAL); 237 ENUM_TO_STRING(STATE_BROWSER_FULLSCREEN_NO_CHROME); 238 ENUM_TO_STRING(STATE_BROWSER_FULLSCREEN_WITH_CHROME); 239 ENUM_TO_STRING(STATE_METRO_SNAP); 240 ENUM_TO_STRING(STATE_TAB_FULLSCREEN); 241 ENUM_TO_STRING(STATE_TAB_BROWSER_FULLSCREEN); 242 ENUM_TO_STRING(STATE_TAB_BROWSER_FULLSCREEN_CHROME); 243 ENUM_TO_STRING(STATE_TO_NORMAL); 244 ENUM_TO_STRING(STATE_TO_BROWSER_FULLSCREEN_NO_CHROME); 245 ENUM_TO_STRING(STATE_TO_BROWSER_FULLSCREEN_WITH_CHROME); 246 ENUM_TO_STRING(STATE_TO_TAB_FULLSCREEN); 247 ENUM_TO_STRING(STATE_INVALID); 248 default: 249 NOTREACHED() << "No string for state " << state; 250 return "State-Unknown"; 251 } 252 } 253 254 // static 255 const char* FullscreenControllerStateTest::GetEventString(Event event) { 256 switch (event) { 257 ENUM_TO_STRING(TOGGLE_FULLSCREEN); 258 ENUM_TO_STRING(TOGGLE_FULLSCREEN_CHROME); 259 ENUM_TO_STRING(TAB_FULLSCREEN_TRUE); 260 ENUM_TO_STRING(TAB_FULLSCREEN_FALSE); 261 ENUM_TO_STRING(METRO_SNAP_TRUE); 262 ENUM_TO_STRING(METRO_SNAP_FALSE); 263 ENUM_TO_STRING(BUBBLE_EXIT_LINK); 264 ENUM_TO_STRING(BUBBLE_ALLOW); 265 ENUM_TO_STRING(BUBBLE_DENY); 266 ENUM_TO_STRING(WINDOW_CHANGE); 267 ENUM_TO_STRING(EVENT_INVALID); 268 default: 269 NOTREACHED() << "No string for event " << event; 270 return "Event-Unknown"; 271 } 272 } 273 274 // static 275 bool FullscreenControllerStateTest::IsWindowFullscreenStateChangedReentrant() { 276 #if defined(TOOLKIT_VIEWS) 277 return true; 278 #else 279 return false; 280 #endif 281 } 282 283 // static 284 bool FullscreenControllerStateTest::IsPersistentState(State state) { 285 switch (state) { 286 case STATE_NORMAL: 287 case STATE_BROWSER_FULLSCREEN_NO_CHROME: 288 case STATE_BROWSER_FULLSCREEN_WITH_CHROME: 289 case STATE_METRO_SNAP: 290 case STATE_TAB_FULLSCREEN: 291 case STATE_TAB_BROWSER_FULLSCREEN: 292 case STATE_TAB_BROWSER_FULLSCREEN_CHROME: 293 return true; 294 295 case STATE_TO_NORMAL: 296 case STATE_TO_BROWSER_FULLSCREEN_NO_CHROME: 297 case STATE_TO_BROWSER_FULLSCREEN_WITH_CHROME: 298 case STATE_TO_TAB_FULLSCREEN: 299 return false; 300 301 default: 302 NOTREACHED(); 303 return false; 304 } 305 } 306 307 void FullscreenControllerStateTest::TransitionToState(State final_state) { 308 int max_steps = NUM_STATES; 309 while (max_steps-- && TransitionAStepTowardState(final_state)) 310 continue; 311 ASSERT_GE(max_steps, 0) << "TransitionToState was unable to achieve desired " 312 << "target state. TransitionAStepTowardState iterated too many times." 313 << GetAndClearDebugLog(); 314 ASSERT_EQ(final_state, state_) << "TransitionToState was unable to achieve " 315 << "desired target state. TransitionAStepTowardState returned false." 316 << GetAndClearDebugLog(); 317 } 318 319 bool FullscreenControllerStateTest::TransitionAStepTowardState( 320 State destination_state) { 321 State source_state = state_; 322 if (source_state == destination_state) 323 return false; 324 325 StateTransitionInfo next = NextTransitionInShortestPath(source_state, 326 destination_state, 327 NUM_STATES); 328 if (next.state == STATE_INVALID) { 329 NOTREACHED() << "TransitionAStepTowardState unable to transition. " 330 << "NextTransitionInShortestPath(" 331 << GetStateString(source_state) << ", " 332 << GetStateString(destination_state) << ") returned STATE_INVALID." 333 << GetAndClearDebugLog(); 334 return false; 335 } 336 337 return InvokeEvent(next.event); 338 } 339 340 const char* FullscreenControllerStateTest::GetWindowStateString() { 341 return NULL; 342 } 343 344 bool FullscreenControllerStateTest::InvokeEvent(Event event) { 345 if (!fullscreen_notification_observer_.get()) { 346 // Start observing NOTIFICATION_FULLSCREEN_CHANGED. Construct the 347 // notification observer here instead of in 348 // FullscreenControllerStateTest::FullscreenControllerStateTest() so that we 349 // listen to notifications on the proper thread. 350 fullscreen_notification_observer_.reset( 351 new FullscreenNotificationObserver()); 352 } 353 354 State source_state = state_; 355 State next_state = transition_table_[source_state][event]; 356 357 EXPECT_FALSE(ShouldSkipStateAndEventPair(source_state, event)) 358 << GetAndClearDebugLog(); 359 360 // When simulating reentrant window change calls, expect the next state 361 // automatically. 362 if (IsWindowFullscreenStateChangedReentrant()) 363 next_state = transition_table_[next_state][WINDOW_CHANGE]; 364 365 debugging_log_ << " InvokeEvent(" << std::left 366 << std::setw(kMaxStateNameLength) << GetEventString(event) 367 << ") to " 368 << std::setw(kMaxStateNameLength) << GetStateString(next_state); 369 370 state_ = next_state; 371 372 switch (event) { 373 case TOGGLE_FULLSCREEN: 374 GetFullscreenController()->ToggleBrowserFullscreenMode(); 375 break; 376 377 case TOGGLE_FULLSCREEN_CHROME: 378 #if defined(OS_MACOSX) 379 if (chrome::mac::SupportsSystemFullscreen()) { 380 GetFullscreenController()->ToggleBrowserFullscreenWithChrome(); 381 break; 382 } 383 #endif 384 NOTREACHED() << GetAndClearDebugLog(); 385 break; 386 387 case TAB_FULLSCREEN_TRUE: 388 case TAB_FULLSCREEN_FALSE: { 389 content::WebContents* const active_tab = 390 GetBrowser()->tab_strip_model()->GetActiveWebContents(); 391 GetFullscreenController()-> 392 ToggleFullscreenModeForTab(active_tab, event == TAB_FULLSCREEN_TRUE); 393 // Activating/Deactivating tab fullscreen on a captured tab should not 394 // evoke a state change in the browser window. 395 if (active_tab->GetCapturerCount() > 0) 396 state_ = source_state; 397 break; 398 } 399 400 case METRO_SNAP_TRUE: 401 #if defined(OS_WIN) 402 GetFullscreenController()->SetMetroSnapMode(true); 403 #else 404 NOTREACHED() << GetAndClearDebugLog(); 405 #endif 406 break; 407 408 case METRO_SNAP_FALSE: 409 #if defined(OS_WIN) 410 GetFullscreenController()->SetMetroSnapMode(false); 411 #else 412 NOTREACHED() << GetAndClearDebugLog(); 413 #endif 414 break; 415 416 case BUBBLE_EXIT_LINK: 417 GetFullscreenController()->ExitTabOrBrowserFullscreenToPreviousState(); 418 break; 419 420 case BUBBLE_ALLOW: 421 GetFullscreenController()->OnAcceptFullscreenPermission(); 422 break; 423 424 case BUBBLE_DENY: 425 GetFullscreenController()->OnDenyFullscreenPermission(); 426 break; 427 428 case WINDOW_CHANGE: 429 ChangeWindowFullscreenState(); 430 break; 431 432 default: 433 NOTREACHED() << "InvokeEvent needs a handler for event " 434 << GetEventString(event) << GetAndClearDebugLog(); 435 return false; 436 } 437 438 if (GetWindowStateString()) 439 debugging_log_ << " Window state now " << GetWindowStateString() << "\n"; 440 else 441 debugging_log_ << "\n"; 442 443 MaybeWaitForNotification(); 444 VerifyWindowState(); 445 446 return true; 447 } 448 449 void FullscreenControllerStateTest::VerifyWindowState() { 450 switch (state_) { 451 case STATE_NORMAL: 452 VerifyWindowStateExpectations(FULLSCREEN_WITH_CHROME_FALSE, 453 FULLSCREEN_WITHOUT_CHROME_FALSE, 454 FULLSCREEN_FOR_BROWSER_FALSE, 455 FULLSCREEN_FOR_TAB_FALSE, 456 IN_METRO_SNAP_FALSE); 457 break; 458 case STATE_BROWSER_FULLSCREEN_NO_CHROME: 459 VerifyWindowStateExpectations(FULLSCREEN_WITH_CHROME_FALSE, 460 FULLSCREEN_WITHOUT_CHROME_TRUE, 461 FULLSCREEN_FOR_BROWSER_TRUE, 462 FULLSCREEN_FOR_TAB_FALSE, 463 IN_METRO_SNAP_FALSE); 464 break; 465 case STATE_BROWSER_FULLSCREEN_WITH_CHROME: 466 VerifyWindowStateExpectations(FULLSCREEN_WITH_CHROME_TRUE, 467 FULLSCREEN_WITHOUT_CHROME_FALSE, 468 FULLSCREEN_FOR_BROWSER_TRUE, 469 FULLSCREEN_FOR_TAB_FALSE, 470 IN_METRO_SNAP_FALSE); 471 break; 472 case STATE_METRO_SNAP: 473 VerifyWindowStateExpectations(FULLSCREEN_WITH_CHROME_NO_EXPECTATION, 474 FULLSCREEN_WITHOUT_CHROME_NO_EXPECTATION, 475 FULLSCREEN_FOR_BROWSER_NO_EXPECTATION, 476 FULLSCREEN_FOR_TAB_NO_EXPECTATION, 477 IN_METRO_SNAP_TRUE); 478 break; 479 case STATE_TAB_FULLSCREEN: 480 VerifyWindowStateExpectations(FULLSCREEN_WITH_CHROME_FALSE, 481 FULLSCREEN_WITHOUT_CHROME_TRUE, 482 FULLSCREEN_FOR_BROWSER_FALSE, 483 FULLSCREEN_FOR_TAB_TRUE, 484 IN_METRO_SNAP_FALSE); 485 break; 486 case STATE_TAB_BROWSER_FULLSCREEN: 487 VerifyWindowStateExpectations(FULLSCREEN_WITH_CHROME_FALSE, 488 FULLSCREEN_WITHOUT_CHROME_TRUE, 489 FULLSCREEN_FOR_BROWSER_TRUE, 490 FULLSCREEN_FOR_TAB_TRUE, 491 IN_METRO_SNAP_FALSE); 492 break; 493 case STATE_TAB_BROWSER_FULLSCREEN_CHROME: 494 VerifyWindowStateExpectations(FULLSCREEN_WITH_CHROME_FALSE, 495 FULLSCREEN_WITHOUT_CHROME_TRUE, 496 FULLSCREEN_FOR_BROWSER_TRUE, 497 FULLSCREEN_FOR_TAB_TRUE, 498 IN_METRO_SNAP_FALSE); 499 break; 500 case STATE_TO_NORMAL: 501 VerifyWindowStateExpectations(FULLSCREEN_WITH_CHROME_FALSE, 502 FULLSCREEN_WITHOUT_CHROME_FALSE, 503 FULLSCREEN_FOR_BROWSER_NO_EXPECTATION, 504 FULLSCREEN_FOR_TAB_NO_EXPECTATION, 505 IN_METRO_SNAP_FALSE); 506 break; 507 508 case STATE_TO_BROWSER_FULLSCREEN_NO_CHROME: 509 VerifyWindowStateExpectations(FULLSCREEN_WITH_CHROME_FALSE, 510 FULLSCREEN_WITHOUT_CHROME_TRUE, 511 #if defined(OS_MACOSX) 512 FULLSCREEN_FOR_BROWSER_TRUE, 513 #else 514 FULLSCREEN_FOR_BROWSER_FALSE, 515 #endif 516 FULLSCREEN_FOR_TAB_NO_EXPECTATION, 517 IN_METRO_SNAP_FALSE); 518 break; 519 520 case STATE_TO_BROWSER_FULLSCREEN_WITH_CHROME: 521 VerifyWindowStateExpectations(FULLSCREEN_WITH_CHROME_TRUE, 522 FULLSCREEN_WITHOUT_CHROME_FALSE, 523 #if defined(OS_MACOSX) 524 FULLSCREEN_FOR_BROWSER_TRUE, 525 #else 526 FULLSCREEN_FOR_BROWSER_FALSE, 527 #endif 528 FULLSCREEN_FOR_TAB_NO_EXPECTATION, 529 IN_METRO_SNAP_FALSE); 530 break; 531 532 case STATE_TO_TAB_FULLSCREEN: 533 #if defined(OS_MACOSX) 534 // TODO(scheib) InPresentationMode returns false when invoking events: 535 // TAB_FULLSCREEN_TRUE, TOGGLE_FULLSCREEN. http://crbug.com/156645 536 // It may be that a new testing state TO_TAB_BROWSER_FULLSCREEN 537 // would help work around this http://crbug.com/154196 538 // Test with: STATE_TO_TAB_FULLSCREEN__TOGGLE_FULLSCREEN 539 // 540 // EXPECT_TRUE(GetBrowser()->window()->InPresentationMode()) 541 // << GetAndClearDebugLog(); 542 #endif 543 VerifyWindowStateExpectations(FULLSCREEN_WITH_CHROME_NO_EXPECTATION, 544 FULLSCREEN_WITHOUT_CHROME_NO_EXPECTATION, 545 FULLSCREEN_FOR_BROWSER_FALSE, 546 FULLSCREEN_FOR_TAB_TRUE, 547 IN_METRO_SNAP_FALSE); 548 break; 549 550 default: 551 NOTREACHED() << GetAndClearDebugLog(); 552 } 553 } 554 555 void FullscreenControllerStateTest::MaybeWaitForNotification() { 556 // We should get a fullscreen notification each time we get to a new 557 // persistent state. If we don't get a notification, the test will 558 // fail by timing out. 559 if (state_ != last_notification_received_state_ && 560 IsPersistentState(state_)) { 561 fullscreen_notification_observer_->Wait(); 562 last_notification_received_state_ = state_; 563 fullscreen_notification_observer_.reset( 564 new FullscreenNotificationObserver()); 565 } 566 } 567 568 void FullscreenControllerStateTest::TestTransitionsForEachState() { 569 for (int source_int = 0; source_int < NUM_STATES; ++source_int) { 570 for (int event1_int = 0; event1_int < NUM_EVENTS; ++event1_int) { 571 State state = static_cast<State>(source_int); 572 Event event1 = static_cast<Event>(event1_int); 573 574 // Early out if skipping all tests for this state, reduces log noise. 575 if (ShouldSkipTest(state, event1)) 576 continue; 577 578 for (int event2_int = 0; event2_int < NUM_EVENTS; ++event2_int) { 579 for (int event3_int = 0; event3_int < NUM_EVENTS; ++event3_int) { 580 Event event2 = static_cast<Event>(event2_int); 581 Event event3 = static_cast<Event>(event3_int); 582 583 // Test each state and each event. 584 ASSERT_NO_FATAL_FAILURE(TestStateAndEvent(state, event1)) 585 << GetAndClearDebugLog(); 586 587 // Then, add an additional event to the sequence. 588 if (ShouldSkipStateAndEventPair(state_, event2)) 589 continue; 590 ASSERT_TRUE(InvokeEvent(event2)) << GetAndClearDebugLog(); 591 592 // Then, add an additional event to the sequence. 593 if (ShouldSkipStateAndEventPair(state_, event3)) 594 continue; 595 ASSERT_TRUE(InvokeEvent(event3)) << GetAndClearDebugLog(); 596 } 597 } 598 } 599 } 600 } 601 602 FullscreenControllerStateTest::StateTransitionInfo 603 FullscreenControllerStateTest::NextTransitionInShortestPath( 604 State source, 605 State destination, 606 int search_limit) { 607 if (search_limit <= 0) 608 return StateTransitionInfo(); // Return a default (invalid) state. 609 610 if (state_transitions_[source][destination].state == STATE_INVALID) { 611 // Don't know the next state yet, do a depth first search. 612 StateTransitionInfo result; 613 614 // Consider all states reachable via each event from the source state. 615 for (int event_int = 0; event_int < NUM_EVENTS; ++event_int) { 616 Event event = static_cast<Event>(event_int); 617 State next_state_candidate = transition_table_[source][event]; 618 619 if (ShouldSkipStateAndEventPair(source, event)) 620 continue; 621 622 // Recurse. 623 StateTransitionInfo candidate = NextTransitionInShortestPath( 624 next_state_candidate, destination, search_limit - 1); 625 626 if (candidate.distance + 1 < result.distance) { 627 result.event = event; 628 result.state = next_state_candidate; 629 result.distance = candidate.distance + 1; 630 } 631 } 632 633 // Cache result so that a search is not required next time. 634 state_transitions_[source][destination] = result; 635 } 636 637 return state_transitions_[source][destination]; 638 } 639 640 std::string FullscreenControllerStateTest::GetAndClearDebugLog() { 641 debugging_log_ << "(End of Debugging Log)\n"; 642 std::string output_log = "\nDebugging Log:\n" + debugging_log_.str(); 643 debugging_log_.str(std::string()); 644 return output_log; 645 } 646 647 bool FullscreenControllerStateTest::ShouldSkipStateAndEventPair(State state, 648 Event event) { 649 // TODO(scheib) Toggling Tab fullscreen while pending Tab or 650 // Browser fullscreen is broken currently http://crbug.com/154196 651 if ((state == STATE_TO_BROWSER_FULLSCREEN_NO_CHROME || 652 state == STATE_TO_BROWSER_FULLSCREEN_WITH_CHROME || 653 state == STATE_TO_TAB_FULLSCREEN) && 654 (event == TAB_FULLSCREEN_TRUE || event == TAB_FULLSCREEN_FALSE)) 655 return true; 656 if (state == STATE_TO_NORMAL && event == TAB_FULLSCREEN_TRUE) 657 return true; 658 659 // Skip metro snap state and events when not on windows. 660 #if !defined(OS_WIN) 661 if (state == STATE_METRO_SNAP || 662 event == METRO_SNAP_TRUE || 663 event == METRO_SNAP_FALSE) 664 return true; 665 #endif 666 667 // Skip Mac Lion Fullscreen state and events when not on OSX 10.7+. 668 if (!SupportsMacSystemFullscreen()) { 669 if (state == STATE_BROWSER_FULLSCREEN_WITH_CHROME || 670 state == STATE_TAB_BROWSER_FULLSCREEN_CHROME || 671 state == STATE_TO_BROWSER_FULLSCREEN_WITH_CHROME || 672 event == TOGGLE_FULLSCREEN_CHROME) { 673 return true; 674 } 675 } 676 677 return false; 678 } 679 680 bool FullscreenControllerStateTest::ShouldSkipTest(State state, Event event) { 681 // Quietly skip metro snap tests when not on windows. 682 #if !defined(OS_WIN) 683 if (state == STATE_METRO_SNAP || 684 event == METRO_SNAP_TRUE || 685 event == METRO_SNAP_FALSE) { 686 debugging_log_ << "\nSkipping metro snap test on non-Windows.\n"; 687 return true; 688 } 689 #endif 690 691 // Quietly skip Mac Lion Fullscreen tests when not on OSX 10.7+. 692 if (!SupportsMacSystemFullscreen()) { 693 if (state == STATE_BROWSER_FULLSCREEN_WITH_CHROME || 694 event == TOGGLE_FULLSCREEN_CHROME) { 695 debugging_log_ << "\nSkipping Lion Fullscreen test on non-OSX 10.7+.\n"; 696 return true; 697 } 698 } 699 700 // When testing reentrancy there are states the fullscreen controller 701 // will be unable to remain in, as they will progress due to the 702 // reentrant window change call. Skip states that will be instantly 703 // exited by the reentrant call. 704 if (IsWindowFullscreenStateChangedReentrant() && 705 (transition_table_[state][WINDOW_CHANGE] != state)) { 706 debugging_log_ << "\nSkipping reentrant test for transitory source state " 707 << GetStateString(state) << ".\n"; 708 return true; 709 } 710 711 if (ShouldSkipStateAndEventPair(state, event)) { 712 debugging_log_ << "\nSkipping test due to ShouldSkipStateAndEventPair(" 713 << GetStateString(state) << ", " 714 << GetEventString(event) << ").\n"; 715 LOG(INFO) << "Skipping test due to ShouldSkipStateAndEventPair(" 716 << GetStateString(state) << ", " 717 << GetEventString(event) << ")."; 718 return true; 719 } 720 721 return false; 722 } 723 724 void FullscreenControllerStateTest::TestStateAndEvent(State state, 725 Event event) { 726 if (ShouldSkipTest(state, event)) 727 return; 728 729 debugging_log_ << "\nTest transition from state " 730 << GetStateString(state) 731 << (IsWindowFullscreenStateChangedReentrant() ? 732 " with reentrant calls.\n" : ".\n"); 733 734 // Spaced out text to line up with columns printed in InvokeEvent(). 735 debugging_log_ << "First, from " 736 << GetStateString(state_) << "\n"; 737 ASSERT_NO_FATAL_FAILURE(TransitionToState(state)) 738 << GetAndClearDebugLog(); 739 740 debugging_log_ << " Then,\n"; 741 ASSERT_TRUE(InvokeEvent(event)) << GetAndClearDebugLog(); 742 } 743 744 void FullscreenControllerStateTest::VerifyWindowStateExpectations( 745 FullscreenWithChromeExpectation fullscreen_with_chrome, 746 FullscreenWithoutChromeExpectation fullscreen_without_chrome, 747 FullscreenForBrowserExpectation fullscreen_for_browser, 748 FullscreenForTabExpectation fullscreen_for_tab, 749 InMetroSnapExpectation in_metro_snap) { 750 #if defined(OS_MACOSX) 751 if (fullscreen_with_chrome != FULLSCREEN_WITH_CHROME_NO_EXPECTATION) { 752 EXPECT_EQ(GetBrowser()->window()->IsFullscreenWithChrome(), 753 !!fullscreen_with_chrome) << GetAndClearDebugLog(); 754 } 755 if (fullscreen_without_chrome != FULLSCREEN_WITHOUT_CHROME_NO_EXPECTATION) { 756 EXPECT_EQ(GetBrowser()->window()->IsFullscreenWithoutChrome(), 757 !!fullscreen_without_chrome) << GetAndClearDebugLog(); 758 } 759 #endif 760 if (fullscreen_for_browser != FULLSCREEN_FOR_BROWSER_NO_EXPECTATION) { 761 EXPECT_EQ(GetFullscreenController()->IsFullscreenForBrowser(), 762 !!fullscreen_for_browser) << GetAndClearDebugLog(); 763 } 764 if (fullscreen_for_tab != FULLSCREEN_FOR_TAB_NO_EXPECTATION) { 765 EXPECT_EQ(GetFullscreenController()->IsWindowFullscreenForTabOrPending(), 766 !!fullscreen_for_tab) << GetAndClearDebugLog(); 767 } 768 if (in_metro_snap != IN_METRO_SNAP_NO_EXPECTATION) { 769 EXPECT_EQ(GetFullscreenController()->IsInMetroSnapMode(), 770 !!in_metro_snap) << GetAndClearDebugLog(); 771 } 772 } 773 774 FullscreenController* FullscreenControllerStateTest::GetFullscreenController() { 775 return GetBrowser()->fullscreen_controller(); 776 } 777 778 std::string FullscreenControllerStateTest::GetTransitionTableAsString() const { 779 std::ostringstream output; 780 output << "transition_table_[NUM_STATES = " << NUM_STATES 781 << "][NUM_EVENTS = " << NUM_EVENTS 782 << "] =\n"; 783 for (int state_int = 0; state_int < NUM_STATES; ++state_int) { 784 State state = static_cast<State>(state_int); 785 output << " { // " << GetStateString(state) << ":\n"; 786 for (int event_int = 0; event_int < NUM_EVENTS; ++event_int) { 787 Event event = static_cast<Event>(event_int); 788 output << " " 789 << std::left << std::setw(kMaxStateNameLength+1) 790 << std::string(GetStateString(transition_table_[state][event])) + "," 791 << "// Event " 792 << GetEventString(event) << "\n"; 793 } 794 output << " },\n"; 795 } 796 output << " };\n"; 797 return output.str(); 798 } 799 800 std::string FullscreenControllerStateTest::GetStateTransitionsAsString() const { 801 std::ostringstream output; 802 output << "state_transitions_[NUM_STATES = " << NUM_STATES 803 << "][NUM_STATES = " << NUM_STATES << "] =\n"; 804 for (int state1_int = 0; state1_int < NUM_STATES; ++state1_int) { 805 State state1 = static_cast<State>(state1_int); 806 output << "{ // " << GetStateString(state1) << ":\n"; 807 for (int state2_int = 0; state2_int < NUM_STATES; ++state2_int) { 808 State state2 = static_cast<State>(state2_int); 809 const StateTransitionInfo& info = state_transitions_[state1][state2]; 810 output << " { " 811 << std::left << std::setw(kMaxStateNameLength+1) 812 << std::string(GetEventString(info.event)) + "," 813 << std::left << std::setw(kMaxStateNameLength+1) 814 << std::string(GetStateString(info.state)) + "," 815 << std::right << std::setw(2) 816 << info.distance 817 << " }, // " 818 << GetStateString(state2) << "\n"; 819 } 820 output << "},\n"; 821 } 822 output << "};"; 823 return output.str(); 824 } 825