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/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