summaryrefslogtreecommitdiffstats
path: root/chrome/browser/ui/fullscreen/fullscreen_controller_state_unittest.cc
blob: d37b165b55697a42b87d2be26f8991e6ac40ca37 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/command_line.h"
#include "build/build_config.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_tabstrip.h"
#include "chrome/browser/ui/fullscreen/fullscreen_controller.h"
#include "chrome/browser/ui/fullscreen/fullscreen_controller_state_test.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/test/base/browser_with_test_window_test.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/url_constants.h"
#include "testing/gtest/include/gtest/gtest.h"

// The FullscreenControllerStateUnitTest unit test suite exhastively tests
// the FullscreenController through all permutations of events. The behavior
// of the BrowserWindow is mocked via FullscreenControllerTestWindow.


// FullscreenControllerTestWindow ----------------------------------------------

// A BrowserWindow used for testing FullscreenController. The behavior of this
// mock is verfied manually by running FullscreenControllerStateInteractiveTest.
class FullscreenControllerTestWindow : public TestBrowserWindow {
 public:
  // Simulate the window state with an enumeration.
  enum WindowState {
    NORMAL,
    FULLSCREEN,
    // No TO_ state for METRO_SNAP, the windows implementation is synchronous.
    METRO_SNAP,
    TO_NORMAL,
    TO_FULLSCREEN,
  };

  FullscreenControllerTestWindow();
  virtual ~FullscreenControllerTestWindow() {}

  // BrowserWindow Interface:
  virtual void EnterFullscreen(const GURL& url,
                               FullscreenExitBubbleType type) OVERRIDE;
  virtual void ExitFullscreen() OVERRIDE;
  virtual bool ShouldHideUIForFullscreen() const OVERRIDE;
  virtual bool IsFullscreen() const OVERRIDE;
#if defined(OS_WIN)
  virtual void SetMetroSnapMode(bool enable) OVERRIDE;
  virtual bool IsInMetroSnapMode() const OVERRIDE;
#endif
#if defined(OS_MACOSX)
  virtual void EnterFullscreenWithChrome() OVERRIDE;
  virtual bool IsFullscreenWithChrome() OVERRIDE;
  virtual bool IsFullscreenWithoutChrome() OVERRIDE;
#endif

  static const char* GetWindowStateString(WindowState state);
  WindowState state() const { return state_; }
  void set_browser(Browser* browser) { browser_ = browser; }

  // Simulates the window changing state.
  void ChangeWindowFullscreenState();

 private:
  // Enters fullscreen with |new_mac_with_chrome_mode|.
  void EnterFullscreen(bool new_mac_with_chrome_mode);

  // Returns true if ChangeWindowFullscreenState() should be called as a result
  // of updating the current fullscreen state to the passed in state.
  bool IsTransitionReentrant(bool new_fullscreen,
                             bool new_mac_with_chrome_mode);

  WindowState state_;
  bool mac_with_chrome_mode_;
  Browser* browser_;
};

FullscreenControllerTestWindow::FullscreenControllerTestWindow()
    : state_(NORMAL),
      mac_with_chrome_mode_(false),
      browser_(NULL) {
}

void FullscreenControllerTestWindow::EnterFullscreen(
    const GURL& url, FullscreenExitBubbleType type) {
  EnterFullscreen(false);
}

void FullscreenControllerTestWindow::ExitFullscreen() {
  if (IsFullscreen()) {
    state_ = TO_NORMAL;
    mac_with_chrome_mode_ = false;

    if (IsTransitionReentrant(false, false))
      ChangeWindowFullscreenState();
  }
}

bool FullscreenControllerTestWindow::ShouldHideUIForFullscreen() const {
  return IsFullscreen();
}

bool FullscreenControllerTestWindow::IsFullscreen() const {
#if defined(OS_MACOSX)
  return state_ == FULLSCREEN || state_ == TO_FULLSCREEN;
#else
  return state_ == FULLSCREEN || state_ == TO_NORMAL;
#endif
}

#if defined(OS_WIN)
void FullscreenControllerTestWindow::SetMetroSnapMode(bool enable) {
  if (enable != IsInMetroSnapMode())
    state_ = enable ? METRO_SNAP : NORMAL;

  if (FullscreenControllerStateTest::IsWindowFullscreenStateChangedReentrant())
    ChangeWindowFullscreenState();
}

bool FullscreenControllerTestWindow::IsInMetroSnapMode() const {
  return state_ == METRO_SNAP;
}
#endif

#if defined(OS_MACOSX)
void FullscreenControllerTestWindow::EnterFullscreenWithChrome() {
  EnterFullscreen(true);
}

bool FullscreenControllerTestWindow::IsFullscreenWithChrome() {
  return IsFullscreen() && mac_with_chrome_mode_;
}

bool FullscreenControllerTestWindow::IsFullscreenWithoutChrome() {
  return IsFullscreen() && !mac_with_chrome_mode_;
}
#endif

// static
const char* FullscreenControllerTestWindow::GetWindowStateString(
    WindowState state) {
  switch (state) {
    ENUM_TO_STRING(NORMAL);
    ENUM_TO_STRING(FULLSCREEN);
    ENUM_TO_STRING(METRO_SNAP);
    ENUM_TO_STRING(TO_FULLSCREEN);
    ENUM_TO_STRING(TO_NORMAL);
    default:
      NOTREACHED() << "No string for state " << state;
      return "WindowState-Unknown";
  }
}

void FullscreenControllerTestWindow::ChangeWindowFullscreenState() {
  // Most states result in "no operation" intentionally. The tests
  // assume that all possible states and event pairs can be tested, even
  // though window managers will not generate all of these.
  if (state_ == TO_FULLSCREEN)
      state_ = FULLSCREEN;
  else if (state_ == TO_NORMAL)
      state_ = NORMAL;

  // Emit a change event from every state to ensure the Fullscreen Controller
  // handles it in all circumstances.
  browser_->WindowFullscreenStateChanged();
}

void FullscreenControllerTestWindow::EnterFullscreen(
    bool new_mac_with_chrome_mode) {
  bool reentrant = IsTransitionReentrant(true, new_mac_with_chrome_mode);

  mac_with_chrome_mode_ = new_mac_with_chrome_mode;
  if (!IsFullscreen())
    state_ = TO_FULLSCREEN;

  if (reentrant)
    ChangeWindowFullscreenState();
}

bool FullscreenControllerTestWindow::IsTransitionReentrant(
    bool new_fullscreen,
    bool new_mac_with_chrome_mode) {
#if defined(OS_MACOSX)
  bool mac_with_chrome_mode_changed = new_mac_with_chrome_mode ?
      IsFullscreenWithoutChrome() : IsFullscreenWithChrome();
#else
  bool mac_with_chrome_mode_changed = false;
#endif
  bool fullscreen_changed = (new_fullscreen != IsFullscreen());

  if (!fullscreen_changed && !mac_with_chrome_mode_changed)
    return false;

  if (FullscreenControllerStateTest::IsWindowFullscreenStateChangedReentrant())
    return true;

  // BrowserWindowCocoa::EnterFullscreen() and
  // BrowserWindowCocoa::EnterFullscreenWithChrome() are reentrant when
  // switching between fullscreen with chrome and fullscreen without chrome.
  return state_ == FULLSCREEN &&
      !fullscreen_changed &&
      mac_with_chrome_mode_changed;
}


// FullscreenControllerStateUnitTest -------------------------------------------

// Unit test fixture testing Fullscreen Controller through its states. Most of
// the test logic comes from FullscreenControllerStateTest.
class FullscreenControllerStateUnitTest : public BrowserWithTestWindowTest,
                                          public FullscreenControllerStateTest {
 public:
  FullscreenControllerStateUnitTest();

  // FullscreenControllerStateTest:
  virtual void SetUp() OVERRIDE;
  virtual BrowserWindow* CreateBrowserWindow() OVERRIDE;
  virtual void ChangeWindowFullscreenState() OVERRIDE;
  virtual const char* GetWindowStateString() OVERRIDE;
  virtual void VerifyWindowState() OVERRIDE;

 protected:
  // FullscreenControllerStateTest:
  virtual bool ShouldSkipStateAndEventPair(State state, Event event) OVERRIDE;
  virtual Browser* GetBrowser() OVERRIDE;
  FullscreenControllerTestWindow* window_;
};

FullscreenControllerStateUnitTest::FullscreenControllerStateUnitTest ()
    : window_(NULL) {
}

void FullscreenControllerStateUnitTest::SetUp() {
  BrowserWithTestWindowTest::SetUp();
  window_->set_browser(browser());
}

BrowserWindow* FullscreenControllerStateUnitTest::CreateBrowserWindow() {
  window_ = new FullscreenControllerTestWindow();
  return window_;  // BrowserWithTestWindowTest takes ownership.
}

void FullscreenControllerStateUnitTest::ChangeWindowFullscreenState() {
  window_->ChangeWindowFullscreenState();
}

const char* FullscreenControllerStateUnitTest::GetWindowStateString() {
  return FullscreenControllerTestWindow::GetWindowStateString(window_->state());
}

void FullscreenControllerStateUnitTest::VerifyWindowState() {
  switch (state()) {
    case STATE_NORMAL:
      EXPECT_EQ(FullscreenControllerTestWindow::NORMAL,
                window_->state()) << GetAndClearDebugLog();
      break;

    case STATE_BROWSER_FULLSCREEN_NO_CHROME:
    case STATE_BROWSER_FULLSCREEN_WITH_CHROME:
    case STATE_TAB_FULLSCREEN:
    case STATE_TAB_BROWSER_FULLSCREEN:
    case STATE_TAB_BROWSER_FULLSCREEN_CHROME:
      EXPECT_EQ(FullscreenControllerTestWindow::FULLSCREEN,
                window_->state()) << GetAndClearDebugLog();
      break;

#if defined(OS_WIN)
    case STATE_METRO_SNAP:
      EXPECT_EQ(FullscreenControllerTestWindow::METRO_SNAP,
                window_->state()) << GetAndClearDebugLog();
      break;
#endif

    case STATE_TO_NORMAL:
      EXPECT_EQ(FullscreenControllerTestWindow::TO_NORMAL,
                window_->state()) << GetAndClearDebugLog();
      break;

    case STATE_TO_BROWSER_FULLSCREEN_NO_CHROME:
    case STATE_TO_BROWSER_FULLSCREEN_WITH_CHROME:
    case STATE_TO_TAB_FULLSCREEN:
      EXPECT_EQ(FullscreenControllerTestWindow::TO_FULLSCREEN,
                window_->state()) << GetAndClearDebugLog();
      break;

    default:
      NOTREACHED() << GetAndClearDebugLog();
  }

  FullscreenControllerStateTest::VerifyWindowState();
}

bool FullscreenControllerStateUnitTest::ShouldSkipStateAndEventPair(
    State state, Event event) {
#if defined(OS_MACOSX)
  // TODO(scheib) Toggle, Window Event, Toggle, Toggle on Mac as exposed by
  // test *.STATE_TO_NORMAL__TOGGLE_FULLSCREEN runs interactively and exits to
  // Normal. This doesn't appear to be the desired result, and would add
  // too much complexity to mimic in our simple FullscreenControllerTestWindow.
  // http://crbug.com/156968
  if ((state == STATE_TO_NORMAL ||
       state == STATE_TO_BROWSER_FULLSCREEN_NO_CHROME ||
       state == STATE_TO_TAB_FULLSCREEN) &&
      event == TOGGLE_FULLSCREEN)
    return true;
#endif

  return FullscreenControllerStateTest::ShouldSkipStateAndEventPair(state,
                                                                    event);
}

Browser* FullscreenControllerStateUnitTest::GetBrowser() {
  return BrowserWithTestWindowTest::browser();
}


// Soak tests ------------------------------------------------------------------

// Tests all states with all permutations of multiple events to detect lingering
// state issues that would bleed over to other states.
// I.E. for each state test all combinations of events E1, E2, E3.
//
// This produces coverage for event sequences that may happen normally but
// would not be exposed by traversing to each state via TransitionToState().
// TransitionToState() always takes the same path even when multiple paths
// exist.
TEST_F(FullscreenControllerStateUnitTest, TransitionsForEachState) {
  // A tab is needed for tab fullscreen.
  AddTab(browser(), GURL(url::kAboutBlankURL));
  TestTransitionsForEachState();
  // Progress of test can be examined via LOG(INFO) << GetAndClearDebugLog();
}


// Individual tests for each pair of state and event ---------------------------

#define TEST_EVENT(state, event)                                \
  TEST_F(FullscreenControllerStateUnitTest, state##__##event) { \
    AddTab(browser(), GURL(url::kAboutBlankURL));               \
    ASSERT_NO_FATAL_FAILURE(TestStateAndEvent(state, event))    \
        << GetAndClearDebugLog();                               \
  }
    // Progress of tests can be examined by inserting the following line:
    // LOG(INFO) << GetAndClearDebugLog(); }

#include "chrome/browser/ui/fullscreen/fullscreen_controller_state_tests.h"


// Specific one-off tests for known issues -------------------------------------

// TODO(scheib) Toggling Tab fullscreen while pending Tab or
// Browser fullscreen is broken currently http://crbug.com/154196
TEST_F(FullscreenControllerStateUnitTest,
       DISABLED_ToggleTabWhenPendingBrowser) {
  // Only possible without reentrancy.
  if (FullscreenControllerStateTest::IsWindowFullscreenStateChangedReentrant())
    return;
  AddTab(browser(), GURL(url::kAboutBlankURL));
  ASSERT_NO_FATAL_FAILURE(
      TransitionToState(STATE_TO_BROWSER_FULLSCREEN_NO_CHROME))
      << GetAndClearDebugLog();

  ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_TRUE)) << GetAndClearDebugLog();
  ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_FALSE)) << GetAndClearDebugLog();
  ASSERT_TRUE(InvokeEvent(WINDOW_CHANGE)) << GetAndClearDebugLog();
}

// TODO(scheib) Toggling Tab fullscreen while pending Tab or
// Browser fullscreen is broken currently http://crbug.com/154196
TEST_F(FullscreenControllerStateUnitTest, DISABLED_ToggleTabWhenPendingTab) {
  // Only possible without reentrancy.
  if (FullscreenControllerStateTest::IsWindowFullscreenStateChangedReentrant())
    return;
  AddTab(browser(), GURL(url::kAboutBlankURL));
  ASSERT_NO_FATAL_FAILURE(
      TransitionToState(STATE_TO_TAB_FULLSCREEN))
      << GetAndClearDebugLog();

  ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_TRUE)) << GetAndClearDebugLog();
  ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_FALSE)) << GetAndClearDebugLog();
  ASSERT_TRUE(InvokeEvent(WINDOW_CHANGE)) << GetAndClearDebugLog();
}

// Debugging utility: Display the transition tables. Intentionally disabled
TEST_F(FullscreenControllerStateUnitTest, DISABLED_DebugLogStateTables) {
  std::ostringstream output;
  output << "\n\nTransition Table:";
  output << GetTransitionTableAsString();

  output << "\n\nInitial transitions:";
  output << GetStateTransitionsAsString();

  // Calculate all transition pairs.
  for (int state1_int = 0; state1_int < NUM_STATES; ++state1_int) {
    State state1 = static_cast<State>(state1_int);
    for (int state2_int = 0; state2_int < NUM_STATES; ++state2_int) {
      State state2 = static_cast<State>(state2_int);
      if (ShouldSkipStateAndEventPair(state1, EVENT_INVALID) ||
          ShouldSkipStateAndEventPair(state2, EVENT_INVALID))
        continue;
      // Compute the transition
      if (NextTransitionInShortestPath(state1, state2, NUM_STATES).state ==
          STATE_INVALID) {
        LOG(ERROR) << "Should be skipping state transitions for: "
            << GetStateString(state1) << " " << GetStateString(state2);
      }
    }
  }

  output << "\n\nAll transitions:";
  output << GetStateTransitionsAsString();
  LOG(INFO) << output.str();
}

// Test that the fullscreen exit bubble is closed by
// WindowFullscreenStateChanged() if fullscreen is exited via BrowserWindow.
// This currently occurs when an extension exits fullscreen via changing the
// browser bounds.
TEST_F(FullscreenControllerStateUnitTest, ExitFullscreenViaBrowserWindow) {
  AddTab(browser(), GURL(url::kAboutBlankURL));
  ASSERT_TRUE(InvokeEvent(TOGGLE_FULLSCREEN));
  ASSERT_TRUE(InvokeEvent(WINDOW_CHANGE));
  ASSERT_TRUE(browser()->window()->IsFullscreen());
  // Exit fullscreen without going through fullscreen controller.
  browser()->window()->ExitFullscreen();
  ChangeWindowFullscreenState();
  EXPECT_EQ(FEB_TYPE_NONE,
            browser()->fullscreen_controller()->GetFullscreenExitBubbleType());
}

// Test that switching tabs takes the browser out of tab fullscreen.
TEST_F(FullscreenControllerStateUnitTest, ExitTabFullscreenViaSwitchingTab) {
  AddTab(browser(), GURL(url::kAboutBlankURL));
  AddTab(browser(), GURL(url::kAboutBlankURL));
  ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_TRUE));
  ASSERT_TRUE(InvokeEvent(WINDOW_CHANGE));
  ASSERT_TRUE(browser()->window()->IsFullscreen());

  browser()->tab_strip_model()->SelectNextTab();
  ChangeWindowFullscreenState();
  EXPECT_FALSE(browser()->window()->IsFullscreen());
}

// Test that switching tabs via detaching the active tab (which is in tab
// fullscreen) takes the browser out of tab fullscreen. This case can
// occur if the user is in both tab fullscreen and immersive browser fullscreen.
TEST_F(FullscreenControllerStateUnitTest, ExitTabFullscreenViaDetachingTab) {
  AddTab(browser(), GURL(url::kAboutBlankURL));
  AddTab(browser(), GURL(url::kAboutBlankURL));
  ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_TRUE));
  ASSERT_TRUE(InvokeEvent(WINDOW_CHANGE));
  ASSERT_TRUE(browser()->window()->IsFullscreen());

  scoped_ptr<content::WebContents> web_contents(
      browser()->tab_strip_model()->DetachWebContentsAt(0));
  ChangeWindowFullscreenState();
  EXPECT_FALSE(browser()->window()->IsFullscreen());
}

// Test that replacing the web contents for a tab which is in tab fullscreen
// takes the browser out of tab fullscreen. This can occur if the user
// navigates to a prerendered page from a page which is tab fullscreen.
TEST_F(FullscreenControllerStateUnitTest, ExitTabFullscreenViaReplacingTab) {
  AddTab(browser(), GURL(url::kAboutBlankURL));
  ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_TRUE));
  ASSERT_TRUE(InvokeEvent(WINDOW_CHANGE));
  ASSERT_TRUE(browser()->window()->IsFullscreen());

  content::WebContents* new_web_contents = content::WebContents::Create(
      content::WebContents::CreateParams(profile()));
  scoped_ptr<content::WebContents> old_web_contents(
      browser()->tab_strip_model()->ReplaceWebContentsAt(
          0, new_web_contents));
  ChangeWindowFullscreenState();
  EXPECT_FALSE(browser()->window()->IsFullscreen());
}

// Tests that, in a browser configured for Fullscreen-Within-Tab mode,
// fullscreening a screen-captured tab will NOT cause any fullscreen state
// change to the browser window. Furthermore, the test switches between tabs to
// confirm a captured tab will be resized by FullscreenController to the capture
// video resolution once the widget is detached from the UI.
//
// See 'FullscreenWithinTab Note' in fullscreen_controller.h.
TEST_F(FullscreenControllerStateUnitTest, OneCapturedFullscreenedTab) {
  content::WebContentsDelegate* const wc_delegate =
      static_cast<content::WebContentsDelegate*>(browser());
  ASSERT_TRUE(wc_delegate->EmbedsFullscreenWidget());

  AddTab(browser(), GURL(url::kAboutBlankURL));
  AddTab(browser(), GURL(url::kAboutBlankURL));
  content::WebContents* const first_tab =
      browser()->tab_strip_model()->GetWebContentsAt(0);
  content::WebContents* const second_tab =
      browser()->tab_strip_model()->GetWebContentsAt(1);

  // Activate the first tab and tell its WebContents it is being captured.
  browser()->tab_strip_model()->ActivateTabAt(0, true);
  const gfx::Size kCaptureSize(1280, 720);
  first_tab->IncrementCapturerCount(kCaptureSize);
  ASSERT_FALSE(browser()->window()->IsFullscreen());
  ASSERT_FALSE(wc_delegate->IsFullscreenForTabOrPending(first_tab));
  ASSERT_FALSE(wc_delegate->IsFullscreenForTabOrPending(second_tab));
  ASSERT_FALSE(GetFullscreenController()->IsWindowFullscreenForTabOrPending());

  // Enter tab fullscreen.  Since the tab is being captured, the browser window
  // should not expand to fill the screen.
  ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_TRUE));
  EXPECT_FALSE(browser()->window()->IsFullscreen());
  EXPECT_TRUE(wc_delegate->IsFullscreenForTabOrPending(first_tab));
  EXPECT_FALSE(wc_delegate->IsFullscreenForTabOrPending(second_tab));
  EXPECT_FALSE(GetFullscreenController()->IsWindowFullscreenForTabOrPending());

  // Switch to the other tab.  Check that the first tab was resized to the
  // WebContents' preferred size.
  browser()->tab_strip_model()->ActivateTabAt(1, true);
  EXPECT_FALSE(browser()->window()->IsFullscreen());
  EXPECT_TRUE(wc_delegate->IsFullscreenForTabOrPending(first_tab));
  EXPECT_FALSE(wc_delegate->IsFullscreenForTabOrPending(second_tab));
  EXPECT_FALSE(GetFullscreenController()->IsWindowFullscreenForTabOrPending());
  // TODO(miu): Need to make an adjustment to content::WebContentsViewMac for
  // the following to work:
#if !defined(OS_MACOSX)
  EXPECT_EQ(kCaptureSize, first_tab->GetViewBounds().size());
#endif

  // Switch back to the first tab and exit fullscreen.
  browser()->tab_strip_model()->ActivateTabAt(0, true);
  EXPECT_FALSE(browser()->window()->IsFullscreen());
  EXPECT_TRUE(wc_delegate->IsFullscreenForTabOrPending(first_tab));
  EXPECT_FALSE(wc_delegate->IsFullscreenForTabOrPending(second_tab));
  EXPECT_FALSE(GetFullscreenController()->IsWindowFullscreenForTabOrPending());
  ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_FALSE));
  EXPECT_FALSE(browser()->window()->IsFullscreen());
  EXPECT_FALSE(wc_delegate->IsFullscreenForTabOrPending(first_tab));
  EXPECT_FALSE(wc_delegate->IsFullscreenForTabOrPending(second_tab));
  EXPECT_FALSE(GetFullscreenController()->IsWindowFullscreenForTabOrPending());
}

// Tests that, in a browser configured for Fullscreen-Within-Tab mode, more than
// one tab can be in fullscreen mode at the same time without interfering with
// each other.  One tab is being screen-captured and is toggled into fullscreen
// mode, and then the user switches to another tab not being screen-captured and
// fullscreens it.  The first tab's fullscreen toggle does not affect the
// browser window fullscreen, while the second one's does.  Then, the order of
// operations is reversed.
//
// See 'FullscreenWithinTab Note' in fullscreen_controller.h.
TEST_F(FullscreenControllerStateUnitTest, TwoFullscreenedTabsOneCaptured) {
  content::WebContentsDelegate* const wc_delegate =
      static_cast<content::WebContentsDelegate*>(browser());
  ASSERT_TRUE(wc_delegate->EmbedsFullscreenWidget());

  AddTab(browser(), GURL(url::kAboutBlankURL));
  AddTab(browser(), GURL(url::kAboutBlankURL));
  content::WebContents* const first_tab =
      browser()->tab_strip_model()->GetWebContentsAt(0);
  content::WebContents* const second_tab =
      browser()->tab_strip_model()->GetWebContentsAt(1);

  // Start capturing the first tab, fullscreen it, then switch to the second tab
  // and fullscreen that.  The second tab will cause the browser window to
  // expand to fill the screen.
  browser()->tab_strip_model()->ActivateTabAt(0, true);
  const gfx::Size kCaptureSize(1280, 720);
  first_tab->IncrementCapturerCount(kCaptureSize);
  ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_TRUE));
  EXPECT_FALSE(browser()->window()->IsFullscreen());
  EXPECT_TRUE(wc_delegate->IsFullscreenForTabOrPending(first_tab));
  EXPECT_FALSE(wc_delegate->IsFullscreenForTabOrPending(second_tab));
  EXPECT_FALSE(GetFullscreenController()->IsWindowFullscreenForTabOrPending());
  browser()->tab_strip_model()->ActivateTabAt(1, true);
  ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_TRUE));
  ASSERT_TRUE(InvokeEvent(WINDOW_CHANGE));
  EXPECT_TRUE(browser()->window()->IsFullscreen());
  EXPECT_TRUE(wc_delegate->IsFullscreenForTabOrPending(first_tab));
  EXPECT_TRUE(wc_delegate->IsFullscreenForTabOrPending(second_tab));
  EXPECT_TRUE(GetFullscreenController()->IsWindowFullscreenForTabOrPending());

  // Now exit fullscreen while still in the second tab.  The browser window
  // should no longer be fullscreened.
  ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_FALSE));
  ASSERT_TRUE(InvokeEvent(WINDOW_CHANGE));
  EXPECT_FALSE(browser()->window()->IsFullscreen());
  EXPECT_TRUE(wc_delegate->IsFullscreenForTabOrPending(first_tab));
  EXPECT_FALSE(wc_delegate->IsFullscreenForTabOrPending(second_tab));
  EXPECT_FALSE(GetFullscreenController()->IsWindowFullscreenForTabOrPending());

  // Finally, exit fullscreen on the captured tab.
  browser()->tab_strip_model()->ActivateTabAt(0, true);
  ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_FALSE));
  EXPECT_FALSE(browser()->window()->IsFullscreen());
  EXPECT_FALSE(wc_delegate->IsFullscreenForTabOrPending(first_tab));
  EXPECT_FALSE(wc_delegate->IsFullscreenForTabOrPending(second_tab));
  EXPECT_FALSE(GetFullscreenController()->IsWindowFullscreenForTabOrPending());
}

// Tests that, in a browser configured for Fullscreen-Within-Tab mode, more than
// one tab can be in fullscreen mode at the same time.  This is like the
// TwoFullscreenedTabsOneCaptured test above, except that the screen-captured
// tab exits fullscreen mode while the second tab is still in the foreground.
// When the first tab exits fullscreen, the fullscreen state of the second tab
// and the browser window should remain unchanged.
//
// See 'FullscreenWithinTab Note' in fullscreen_controller.h.
TEST_F(FullscreenControllerStateUnitTest,
       BackgroundCapturedTabExitsFullscreen) {
  content::WebContentsDelegate* const wc_delegate =
      static_cast<content::WebContentsDelegate*>(browser());
  ASSERT_TRUE(wc_delegate->EmbedsFullscreenWidget());

  AddTab(browser(), GURL(url::kAboutBlankURL));
  AddTab(browser(), GURL(url::kAboutBlankURL));
  content::WebContents* const first_tab =
      browser()->tab_strip_model()->GetWebContentsAt(0);
  content::WebContents* const second_tab =
      browser()->tab_strip_model()->GetWebContentsAt(1);

  // Start capturing the first tab, fullscreen it, then switch to the second tab
  // and fullscreen that.  The second tab will cause the browser window to
  // expand to fill the screen.
  browser()->tab_strip_model()->ActivateTabAt(0, true);
  const gfx::Size kCaptureSize(1280, 720);
  first_tab->IncrementCapturerCount(kCaptureSize);
  ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_TRUE));
  EXPECT_FALSE(browser()->window()->IsFullscreen());
  EXPECT_TRUE(wc_delegate->IsFullscreenForTabOrPending(first_tab));
  EXPECT_FALSE(wc_delegate->IsFullscreenForTabOrPending(second_tab));
  EXPECT_FALSE(GetFullscreenController()->IsWindowFullscreenForTabOrPending());
  browser()->tab_strip_model()->ActivateTabAt(1, true);
  ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_TRUE));
  ASSERT_TRUE(InvokeEvent(WINDOW_CHANGE));
  EXPECT_TRUE(browser()->window()->IsFullscreen());
  EXPECT_TRUE(wc_delegate->IsFullscreenForTabOrPending(first_tab));
  EXPECT_TRUE(wc_delegate->IsFullscreenForTabOrPending(second_tab));
  EXPECT_TRUE(GetFullscreenController()->IsWindowFullscreenForTabOrPending());

  // Now, the first tab (backgrounded) exits fullscreen.  This should not affect
  // the second tab's fullscreen, nor the state of the browser window.
  GetFullscreenController()->ToggleFullscreenModeForTab(first_tab, false);
  EXPECT_TRUE(browser()->window()->IsFullscreen());
  EXPECT_FALSE(wc_delegate->IsFullscreenForTabOrPending(first_tab));
  EXPECT_TRUE(wc_delegate->IsFullscreenForTabOrPending(second_tab));
  EXPECT_TRUE(GetFullscreenController()->IsWindowFullscreenForTabOrPending());

  // Finally, exit fullscreen on the second tab.
  ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_FALSE));
  ASSERT_TRUE(InvokeEvent(WINDOW_CHANGE));
  EXPECT_FALSE(browser()->window()->IsFullscreen());
  EXPECT_FALSE(wc_delegate->IsFullscreenForTabOrPending(first_tab));
  EXPECT_FALSE(wc_delegate->IsFullscreenForTabOrPending(second_tab));
  EXPECT_FALSE(GetFullscreenController()->IsWindowFullscreenForTabOrPending());
}

// Tests that, in a browser configured for Fullscreen-Within-Tab mode,
// fullscreening a screen-captured tab will NOT cause any fullscreen state
// change to the browser window. Then, toggling Browser Fullscreen mode should
// fullscreen the browser window, but this should behave fully independently of
// the tab's fullscreen state.
//
// See 'FullscreenWithinTab Note' in fullscreen_controller.h.
TEST_F(FullscreenControllerStateUnitTest,
       OneCapturedTabFullscreenedBeforeBrowserFullscreen) {
  content::WebContentsDelegate* const wc_delegate =
      static_cast<content::WebContentsDelegate*>(browser());
  ASSERT_TRUE(wc_delegate->EmbedsFullscreenWidget());

  AddTab(browser(), GURL(url::kAboutBlankURL));
  content::WebContents* const tab =
      browser()->tab_strip_model()->GetWebContentsAt(0);

  // Start capturing the tab and fullscreen it.  The state of the browser window
  // should remain unchanged.
  browser()->tab_strip_model()->ActivateTabAt(0, true);
  const gfx::Size kCaptureSize(1280, 720);
  tab->IncrementCapturerCount(kCaptureSize);
  ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_TRUE));
  EXPECT_TRUE(wc_delegate->IsFullscreenForTabOrPending(tab));
  EXPECT_FALSE(GetFullscreenController()->IsWindowFullscreenForTabOrPending());
  EXPECT_FALSE(GetFullscreenController()->IsFullscreenForBrowser());

  // Now, toggle into Browser Fullscreen mode.  The browser window should now be
  // fullscreened.
  ASSERT_TRUE(InvokeEvent(TOGGLE_FULLSCREEN));
  ASSERT_TRUE(InvokeEvent(WINDOW_CHANGE));
  EXPECT_TRUE(wc_delegate->IsFullscreenForTabOrPending(tab));
  EXPECT_FALSE(GetFullscreenController()->IsWindowFullscreenForTabOrPending());
  EXPECT_TRUE(GetFullscreenController()->IsFullscreenForBrowser());

  // Now, toggle back out of Browser Fullscreen mode.  The browser window exits
  // fullscreen mode, but the tab stays in fullscreen mode.
  ASSERT_TRUE(InvokeEvent(TOGGLE_FULLSCREEN));
  ASSERT_TRUE(InvokeEvent(WINDOW_CHANGE));
  EXPECT_TRUE(wc_delegate->IsFullscreenForTabOrPending(tab));
  EXPECT_FALSE(GetFullscreenController()->IsWindowFullscreenForTabOrPending());
  EXPECT_FALSE(GetFullscreenController()->IsFullscreenForBrowser());

  // Finally, toggle back into Browser Fullscreen mode and then toggle out of
  // tab fullscreen mode.  The browser window should stay fullscreened, while
  // the tab exits fullscreen mode.
  ASSERT_TRUE(InvokeEvent(TOGGLE_FULLSCREEN));
  ASSERT_TRUE(InvokeEvent(WINDOW_CHANGE));
  ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_FALSE));
  EXPECT_FALSE(wc_delegate->IsFullscreenForTabOrPending(tab));
  EXPECT_FALSE(GetFullscreenController()->IsWindowFullscreenForTabOrPending());
  EXPECT_TRUE(GetFullscreenController()->IsFullscreenForBrowser());
}

// Tests that the state of a fullscreened, screen-captured tab is preserved if
// the tab is detached from one Browser window and attached to another.
//
// See 'FullscreenWithinTab Note' in fullscreen_controller.h.
TEST_F(FullscreenControllerStateUnitTest,
       CapturedFullscreenedTabTransferredBetweenBrowserWindows) {
  content::WebContentsDelegate* const wc_delegate =
      static_cast<content::WebContentsDelegate*>(browser());
  ASSERT_TRUE(wc_delegate->EmbedsFullscreenWidget());

  AddTab(browser(), GURL(url::kAboutBlankURL));
  AddTab(browser(), GURL(url::kAboutBlankURL));
  content::WebContents* const tab =
      browser()->tab_strip_model()->GetWebContentsAt(0);

  // Activate the first tab and tell its WebContents it is being captured.
  browser()->tab_strip_model()->ActivateTabAt(0, true);
  const gfx::Size kCaptureSize(1280, 720);
  tab->IncrementCapturerCount(kCaptureSize);
  ASSERT_FALSE(browser()->window()->IsFullscreen());
  ASSERT_FALSE(wc_delegate->IsFullscreenForTabOrPending(tab));
  ASSERT_FALSE(GetFullscreenController()->IsWindowFullscreenForTabOrPending());

  // Enter tab fullscreen.  Since the tab is being captured, the browser window
  // should not expand to fill the screen.
  ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_TRUE));
  EXPECT_FALSE(browser()->window()->IsFullscreen());
  EXPECT_TRUE(wc_delegate->IsFullscreenForTabOrPending(tab));
  EXPECT_FALSE(GetFullscreenController()->IsWindowFullscreenForTabOrPending());

  // Create the second browser window.
  const scoped_ptr<BrowserWindow> second_browser_window(CreateBrowserWindow());
  const scoped_ptr<Browser> second_browser(CreateBrowser(
      browser()->profile(),
      browser()->type(),
      false,
      browser()->host_desktop_type(),
      second_browser_window.get()));
  AddTab(second_browser.get(), GURL(url::kAboutBlankURL));
  content::WebContentsDelegate* const second_wc_delegate =
      static_cast<content::WebContentsDelegate*>(second_browser.get());

  // Detach the tab from the first browser window and attach it to the second.
  // The tab should remain in fullscreen mode and neither browser window should
  // have expanded. It is correct for both FullscreenControllers to agree the
  // tab is in fullscreen mode.
  browser()->tab_strip_model()->DetachWebContentsAt(0);
  second_browser->tab_strip_model()->
      InsertWebContentsAt(0, tab, TabStripModel::ADD_ACTIVE);
  EXPECT_FALSE(browser()->window()->IsFullscreen());
  EXPECT_FALSE(second_browser->window()->IsFullscreen());
  EXPECT_TRUE(wc_delegate->IsFullscreenForTabOrPending(tab));
  EXPECT_TRUE(second_wc_delegate->IsFullscreenForTabOrPending(tab));
  EXPECT_FALSE(GetFullscreenController()->IsWindowFullscreenForTabOrPending());
  EXPECT_FALSE(second_browser->fullscreen_controller()->
                   IsWindowFullscreenForTabOrPending());

  // Now, detach and reattach it back to the first browser window.  Again, the
  // tab should remain in fullscreen mode and neither browser window should have
  // expanded.
  second_browser->tab_strip_model()->DetachWebContentsAt(0);
  browser()->tab_strip_model()->
      InsertWebContentsAt(0, tab, TabStripModel::ADD_ACTIVE);
  EXPECT_FALSE(browser()->window()->IsFullscreen());
  EXPECT_FALSE(second_browser->window()->IsFullscreen());
  EXPECT_TRUE(wc_delegate->IsFullscreenForTabOrPending(tab));
  EXPECT_TRUE(second_wc_delegate->IsFullscreenForTabOrPending(tab));
  EXPECT_FALSE(GetFullscreenController()->IsWindowFullscreenForTabOrPending());
  EXPECT_FALSE(second_browser->fullscreen_controller()->
                   IsWindowFullscreenForTabOrPending());

  // Exit fullscreen.
  ASSERT_TRUE(InvokeEvent(TAB_FULLSCREEN_FALSE));
  EXPECT_FALSE(browser()->window()->IsFullscreen());
  EXPECT_FALSE(wc_delegate->IsFullscreenForTabOrPending(tab));
  EXPECT_FALSE(second_wc_delegate->IsFullscreenForTabOrPending(tab));
  EXPECT_FALSE(GetFullscreenController()->IsWindowFullscreenForTabOrPending());
  EXPECT_FALSE(second_browser->fullscreen_controller()->
                   IsWindowFullscreenForTabOrPending());

  // Required tear-down specific to this test.
  second_browser->tab_strip_model()->CloseAllTabs();
}