Home | History | Annotate | Download | only in fullscreen
      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