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