• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
SupportsMacSystemFullscreen()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 
FullscreenControllerStateTest()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 
~FullscreenControllerStateTest()230 FullscreenControllerStateTest::~FullscreenControllerStateTest() {
231 }
232 
233 // static
GetStateString(State state)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
GetEventString(Event event)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
IsWindowFullscreenStateChangedReentrant()275 bool FullscreenControllerStateTest::IsWindowFullscreenStateChangedReentrant() {
276 #if defined(TOOLKIT_VIEWS)
277   return true;
278 #else
279   return false;
280 #endif
281 }
282 
283 // static
IsPersistentState(State state)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 
TransitionToState(State final_state)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 
TransitionAStepTowardState(State destination_state)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 
GetWindowStateString()340 const char* FullscreenControllerStateTest::GetWindowStateString() {
341   return NULL;
342 }
343 
InvokeEvent(Event event)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 
VerifyWindowState()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 
MaybeWaitForNotification()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 
TestTransitionsForEachState()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
NextTransitionInShortestPath(State source,State destination,int search_limit)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 
GetAndClearDebugLog()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 
ShouldSkipStateAndEventPair(State state,Event event)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 
ShouldSkipTest(State state,Event event)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 
TestStateAndEvent(State state,Event event)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 
VerifyWindowStateExpectations(FullscreenWithChromeExpectation fullscreen_with_chrome,FullscreenWithoutChromeExpectation fullscreen_without_chrome,FullscreenForBrowserExpectation fullscreen_for_browser,FullscreenForTabExpectation fullscreen_for_tab,InMetroSnapExpectation in_metro_snap)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 
GetFullscreenController()774 FullscreenController* FullscreenControllerStateTest::GetFullscreenController() {
775     return GetBrowser()->fullscreen_controller();
776 }
777 
GetTransitionTableAsString() const778 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 
GetStateTransitionsAsString() const800 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