Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / fullscreen / fullscreen_controller.cc
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.h"
6
7 #include "base/bind.h"
8 #include "base/command_line.h"
9 #include "base/message_loop/message_loop.h"
10 #include "chrome/browser/app_mode/app_mode_utils.h"
11 #include "chrome/browser/chrome_notification_types.h"
12 #include "chrome/browser/content_settings/host_content_settings_map.h"
13 #include "chrome/browser/download/download_shelf.h"
14 #include "chrome/browser/fullscreen.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/browser/ui/browser.h"
17 #include "chrome/browser/ui/browser_window.h"
18 #include "chrome/browser/ui/status_bubble.h"
19 #include "chrome/browser/ui/tabs/tab_strip_model.h"
20 #include "chrome/common/chrome_switches.h"
21 #include "content/public/browser/navigation_details.h"
22 #include "content/public/browser/navigation_entry.h"
23 #include "content/public/browser/notification_service.h"
24 #include "content/public/browser/render_view_host.h"
25 #include "content/public/browser/render_widget_host_view.h"
26 #include "content/public/browser/user_metrics.h"
27 #include "content/public/browser/web_contents.h"
28 #include "content/public/browser/web_contents_view.h"
29 #include "extensions/common/extension.h"
30
31 #if defined(OS_MACOSX)
32 #include "base/mac/mac_util.h"
33 #else
34 #include "base/prefs/pref_service.h"
35 #include "chrome/common/pref_names.h"
36 #endif
37
38 using base::UserMetricsAction;
39 using content::RenderViewHost;
40 using content::WebContents;
41
42 FullscreenController::FullscreenController(Browser* browser)
43     : browser_(browser),
44       window_(browser->window()),
45       profile_(browser->profile()),
46       fullscreened_tab_(NULL),
47       state_prior_to_tab_fullscreen_(STATE_INVALID),
48       tab_fullscreen_accepted_(false),
49       toggled_into_fullscreen_(false),
50       mouse_lock_tab_(NULL),
51       mouse_lock_state_(MOUSELOCK_NOT_REQUESTED),
52       reentrant_window_state_change_call_check_(false),
53       is_privileged_fullscreen_for_testing_(false),
54       ptr_factory_(this) {
55   DCHECK(window_);
56   DCHECK(profile_);
57 }
58
59 FullscreenController::~FullscreenController() {
60 }
61
62 bool FullscreenController::IsFullscreenForBrowser() const {
63   return window_->IsFullscreen() && !IsFullscreenCausedByTab();
64 }
65
66 void FullscreenController::ToggleFullscreenMode() {
67   extension_caused_fullscreen_ = GURL();
68   ToggleFullscreenModeInternal(BROWSER);
69 }
70
71 bool FullscreenController::IsFullscreenForTabOrPending() const {
72   return fullscreened_tab_ != NULL;
73 }
74
75 bool FullscreenController::IsFullscreenForTabOrPending(
76     const WebContents* web_contents) const {
77   if (web_contents == fullscreened_tab_) {
78     DCHECK(web_contents == browser_->tab_strip_model()->GetActiveWebContents());
79     DCHECK(!IsFullscreenWithinTabPossible() ||
80            web_contents->GetCapturerCount() == 0);
81     return true;
82   }
83   return IsFullscreenForCapturedTab(web_contents);
84 }
85
86 bool FullscreenController::IsFullscreenCausedByTab() const {
87   return state_prior_to_tab_fullscreen_ == STATE_NORMAL;
88 }
89
90 void FullscreenController::ToggleFullscreenModeForTab(WebContents* web_contents,
91                                                       bool enter_fullscreen) {
92   if (MaybeToggleFullscreenForCapturedTab(web_contents, enter_fullscreen)) {
93     // During tab capture of fullscreen-within-tab views, the browser window
94     // fullscreen state is unchanged, so return now.
95     return;
96   }
97   if (fullscreened_tab_) {
98     if (web_contents != fullscreened_tab_)
99       return;
100   } else if (
101       web_contents != browser_->tab_strip_model()->GetActiveWebContents()) {
102     return;
103   }
104   if (IsFullscreenForTabOrPending() == enter_fullscreen)
105     return;
106
107 #if defined(OS_WIN)
108   // For now, avoid breaking when initiating full screen tab mode while in
109   // a metro snap.
110   // TODO(robertshield): Find a way to reconcile tab-initiated fullscreen
111   //                     modes with metro snap.
112   if (IsInMetroSnapMode())
113     return;
114 #endif
115
116   bool in_browser_or_tab_fullscreen_mode = window_->IsFullscreen();
117   bool window_is_fullscreen_with_chrome = false;
118 #if defined(OS_MACOSX)
119   window_is_fullscreen_with_chrome = window_->IsFullscreenWithChrome();
120 #endif
121
122   if (enter_fullscreen) {
123     SetFullscreenedTab(web_contents);
124     if (!in_browser_or_tab_fullscreen_mode) {
125       state_prior_to_tab_fullscreen_ = STATE_NORMAL;
126       ToggleFullscreenModeInternal(TAB);
127     } else if (window_is_fullscreen_with_chrome) {
128 #if defined(OS_MACOSX)
129       state_prior_to_tab_fullscreen_ = STATE_BROWSER_FULLSCREEN_WITH_CHROME;
130       EnterFullscreenModeInternal(TAB);
131 #else
132       NOTREACHED();
133 #endif
134     } else {
135       state_prior_to_tab_fullscreen_ = STATE_BROWSER_FULLSCREEN_NO_CHROME;
136
137       // We need to update the fullscreen exit bubble, e.g., going from browser
138       // fullscreen to tab fullscreen will need to show different content.
139       const GURL& url = web_contents->GetURL();
140       if (!tab_fullscreen_accepted_) {
141         tab_fullscreen_accepted_ =
142             GetFullscreenSetting(url) == CONTENT_SETTING_ALLOW;
143       }
144       UpdateFullscreenExitBubbleContent();
145
146       // This is only a change between Browser and Tab fullscreen. We generate
147       // a fullscreen notification now because there is no window change.
148       PostFullscreenChangeNotification(true);
149     }
150   } else {
151     if (in_browser_or_tab_fullscreen_mode) {
152       if (IsFullscreenCausedByTab()) {
153         ToggleFullscreenModeInternal(TAB);
154       } else {
155 #if defined(OS_MACOSX)
156         if (state_prior_to_tab_fullscreen_ ==
157             STATE_BROWSER_FULLSCREEN_WITH_CHROME) {
158           EnterFullscreenModeInternal(BROWSER_WITH_CHROME);
159         } else {
160           // Clear the bubble URL, which forces the Mac UI to redraw.
161           UpdateFullscreenExitBubbleContent();
162         }
163 #endif
164         // If currently there is a tab in "tab fullscreen" mode and fullscreen
165         // was not caused by it (i.e., previously it was in "browser fullscreen"
166         // mode), we need to switch back to "browser fullscreen" mode. In this
167         // case, all we have to do is notifying the tab that it has exited "tab
168         // fullscreen" mode.
169         NotifyTabOfExitIfNecessary();
170
171         // This is only a change between Browser and Tab fullscreen. We generate
172         // a fullscreen notification now because there is no window change.
173         PostFullscreenChangeNotification(true);
174       }
175     }
176   }
177 }
178
179 void FullscreenController::ToggleFullscreenModeWithExtension(
180     const GURL& extension_url) {
181   // |extension_caused_fullscreen_| will be reset if this causes fullscreen to
182   // exit.
183   extension_caused_fullscreen_ = extension_url;
184   ToggleFullscreenModeInternal(BROWSER);
185 }
186
187 bool FullscreenController::IsInMetroSnapMode() {
188 #if defined(OS_WIN)
189   return window_->IsInMetroSnapMode();
190 #else
191   return false;
192 #endif
193 }
194
195 #if defined(OS_WIN)
196 void FullscreenController::SetMetroSnapMode(bool enable) {
197   reentrant_window_state_change_call_check_ = false;
198
199   toggled_into_fullscreen_ = false;
200   window_->SetMetroSnapMode(enable);
201
202   // FullscreenController unit tests for metro snap assume that on Windows calls
203   // to WindowFullscreenStateChanged are reentrant. If that assumption is
204   // invalidated, the tests must be updated to maintain coverage.
205   CHECK(reentrant_window_state_change_call_check_);
206 }
207 #endif  // defined(OS_WIN)
208
209 #if defined(OS_MACOSX)
210 void FullscreenController::ToggleFullscreenWithChrome() {
211   // This method cannot be called if simplified fullscreen is enabled.
212   const CommandLine* command_line = CommandLine::ForCurrentProcess();
213   DCHECK(!command_line->HasSwitch(switches::kEnableSimplifiedFullscreen));
214   ToggleFullscreenModeInternal(BROWSER_WITH_CHROME);
215 }
216 #endif
217
218 bool FullscreenController::IsMouseLockRequested() const {
219   return mouse_lock_state_ == MOUSELOCK_REQUESTED;
220 }
221
222 bool FullscreenController::IsMouseLocked() const {
223   return mouse_lock_state_ == MOUSELOCK_ACCEPTED ||
224          mouse_lock_state_ == MOUSELOCK_ACCEPTED_SILENTLY;
225 }
226
227 void FullscreenController::RequestToLockMouse(WebContents* web_contents,
228                                               bool user_gesture,
229                                               bool last_unlocked_by_target) {
230   DCHECK(!IsMouseLocked());
231   NotifyMouseLockChange();
232
233   // Must have a user gesture to prevent misbehaving sites from constantly
234   // re-locking the mouse. Exceptions are when the page has unlocked
235   // (i.e. not the user), or if we're in tab fullscreen (user gesture required
236   // for that)
237   if (!last_unlocked_by_target && !user_gesture &&
238       !IsFullscreenForTabOrPending(web_contents)) {
239     web_contents->GotResponseToLockMouseRequest(false);
240     return;
241   }
242   SetMouseLockTab(web_contents);
243   FullscreenExitBubbleType bubble_type = GetFullscreenExitBubbleType();
244
245   switch (GetMouseLockSetting(web_contents->GetURL())) {
246     case CONTENT_SETTING_ALLOW:
247       // If bubble already displaying buttons we must not lock the mouse yet,
248       // or it would prevent pressing those buttons. Instead, merge the request.
249       if (!IsPrivilegedFullscreenForTab() &&
250           fullscreen_bubble::ShowButtonsForType(bubble_type)) {
251         mouse_lock_state_ = MOUSELOCK_REQUESTED;
252       } else {
253         // Lock mouse.
254         if (web_contents->GotResponseToLockMouseRequest(true)) {
255           if (last_unlocked_by_target) {
256             mouse_lock_state_ = MOUSELOCK_ACCEPTED_SILENTLY;
257           } else {
258             mouse_lock_state_ = MOUSELOCK_ACCEPTED;
259           }
260         } else {
261           SetMouseLockTab(NULL);
262           mouse_lock_state_ = MOUSELOCK_NOT_REQUESTED;
263         }
264       }
265       break;
266     case CONTENT_SETTING_BLOCK:
267       web_contents->GotResponseToLockMouseRequest(false);
268       SetMouseLockTab(NULL);
269       mouse_lock_state_ = MOUSELOCK_NOT_REQUESTED;
270       break;
271     case CONTENT_SETTING_ASK:
272       mouse_lock_state_ = MOUSELOCK_REQUESTED;
273       break;
274     default:
275       NOTREACHED();
276   }
277   UpdateFullscreenExitBubbleContent();
278 }
279
280 void FullscreenController::OnTabDeactivated(WebContents* web_contents) {
281   if (web_contents == fullscreened_tab_ || web_contents == mouse_lock_tab_)
282     ExitTabFullscreenOrMouseLockIfNecessary();
283 }
284
285 void FullscreenController::OnTabDetachedFromView(WebContents* old_contents) {
286   if (!IsFullscreenForCapturedTab(old_contents))
287     return;
288
289   // A fullscreen-within-tab view undergoing screen capture has been detached
290   // and is no longer visible to the user. Set it to exactly the WebContents'
291   // preferred size. See 'FullscreenWithinTab Note'.
292   //
293   // When the user later selects the tab to show |old_contents| again, UI code
294   // elsewhere (e.g., views::WebView) will resize the view to fit within the
295   // browser window once again.
296   if (old_contents->GetCapturerCount() == 0 ||
297       old_contents->GetPreferredSize().IsEmpty()) {
298     // Do nothing if tab capture ended after toggling fullscreen, or a preferred
299     // size was never specified by the capturer.
300     return;
301   }
302   content::RenderWidgetHostView* const current_fs_view =
303       old_contents->GetFullscreenRenderWidgetHostView();
304   if (current_fs_view)
305     current_fs_view->SetSize(old_contents->GetPreferredSize());
306   old_contents->GetView()->SizeContents(old_contents->GetPreferredSize());
307 }
308
309 void FullscreenController::OnTabClosing(WebContents* web_contents) {
310   if (IsFullscreenForCapturedTab(web_contents)) {
311     RenderViewHost* const rvh = web_contents->GetRenderViewHost();
312     if (rvh)
313       rvh->ExitFullscreen();
314   } else if (web_contents == fullscreened_tab_ ||
315              web_contents == mouse_lock_tab_) {
316     ExitTabFullscreenOrMouseLockIfNecessary();
317     // The call to exit fullscreen may result in asynchronous notification of
318     // fullscreen state change (e.g., on Linux). We don't want to rely on it
319     // to call NotifyTabOfExitIfNecessary(), because at that point
320     // |fullscreened_tab_| may not be valid. Instead, we call it here to clean
321     // up tab fullscreen related state.
322     NotifyTabOfExitIfNecessary();
323   }
324 }
325
326 void FullscreenController::WindowFullscreenStateChanged() {
327   reentrant_window_state_change_call_check_ = true;
328
329   bool exiting_fullscreen = !window_->IsFullscreen();
330
331   PostFullscreenChangeNotification(!exiting_fullscreen);
332   if (exiting_fullscreen) {
333     toggled_into_fullscreen_ = false;
334     extension_caused_fullscreen_ = GURL();
335     NotifyTabOfExitIfNecessary();
336   }
337   if (exiting_fullscreen) {
338     window_->GetDownloadShelf()->Unhide();
339   } else {
340     window_->GetDownloadShelf()->Hide();
341     if (window_->GetStatusBubble())
342       window_->GetStatusBubble()->Hide();
343   }
344 }
345
346 bool FullscreenController::HandleUserPressedEscape() {
347   WebContents* const active_web_contents =
348       browser_->tab_strip_model()->GetActiveWebContents();
349   if (IsFullscreenForCapturedTab(active_web_contents)) {
350     RenderViewHost* const rvh = active_web_contents->GetRenderViewHost();
351     if (rvh)
352       rvh->ExitFullscreen();
353     return true;
354   } else if (IsFullscreenForTabOrPending() ||
355              IsMouseLocked() || IsMouseLockRequested()) {
356     ExitTabFullscreenOrMouseLockIfNecessary();
357     return true;
358   }
359
360   return false;
361 }
362
363 void FullscreenController::ExitTabOrBrowserFullscreenToPreviousState() {
364   if (IsFullscreenForTabOrPending())
365     ExitTabFullscreenOrMouseLockIfNecessary();
366   else if (IsFullscreenForBrowser())
367     ExitFullscreenModeInternal();
368 }
369
370 void FullscreenController::OnAcceptFullscreenPermission() {
371   FullscreenExitBubbleType bubble_type = GetFullscreenExitBubbleType();
372   bool mouse_lock = false;
373   bool fullscreen = false;
374   fullscreen_bubble::PermissionRequestedByType(bubble_type, &fullscreen,
375                                                &mouse_lock);
376   DCHECK(!(fullscreen && tab_fullscreen_accepted_));
377   DCHECK(!(mouse_lock && IsMouseLocked()));
378
379   HostContentSettingsMap* settings_map = profile_->GetHostContentSettingsMap();
380
381   GURL url = GetFullscreenExitBubbleURL();
382   ContentSettingsPattern pattern = ContentSettingsPattern::FromURL(url);
383
384   if (mouse_lock && !IsMouseLocked()) {
385     DCHECK(IsMouseLockRequested());
386     // TODO(markusheintz): We should allow patterns for all possible URLs here.
387     if (pattern.IsValid()) {
388       settings_map->SetContentSetting(
389           pattern, ContentSettingsPattern::Wildcard(),
390           CONTENT_SETTINGS_TYPE_MOUSELOCK, std::string(),
391           CONTENT_SETTING_ALLOW);
392     }
393
394     if (mouse_lock_tab_ &&
395         mouse_lock_tab_->GotResponseToLockMouseRequest(true)) {
396       mouse_lock_state_ = MOUSELOCK_ACCEPTED;
397     } else {
398       mouse_lock_state_ = MOUSELOCK_NOT_REQUESTED;
399       SetMouseLockTab(NULL);
400     }
401     NotifyMouseLockChange();
402   }
403
404   if (fullscreen && !tab_fullscreen_accepted_) {
405     DCHECK(fullscreened_tab_);
406     if (pattern.IsValid()) {
407       settings_map->SetContentSetting(
408           pattern, ContentSettingsPattern::Wildcard(),
409           CONTENT_SETTINGS_TYPE_FULLSCREEN, std::string(),
410           CONTENT_SETTING_ALLOW);
411     }
412     tab_fullscreen_accepted_ = true;
413   }
414   UpdateFullscreenExitBubbleContent();
415 }
416
417 void FullscreenController::OnDenyFullscreenPermission() {
418   if (!fullscreened_tab_ && !mouse_lock_tab_)
419     return;
420
421   if (IsMouseLockRequested()) {
422     mouse_lock_state_ = MOUSELOCK_NOT_REQUESTED;
423     if (mouse_lock_tab_)
424       mouse_lock_tab_->GotResponseToLockMouseRequest(false);
425     SetMouseLockTab(NULL);
426     NotifyMouseLockChange();
427
428     // UpdateFullscreenExitBubbleContent() must be called, but to avoid
429     // duplicate calls we do so only if not adjusting the fullscreen state
430     // below, which also calls UpdateFullscreenExitBubbleContent().
431     if (!IsFullscreenForTabOrPending())
432       UpdateFullscreenExitBubbleContent();
433   }
434
435   if (IsFullscreenForTabOrPending())
436     ExitTabFullscreenOrMouseLockIfNecessary();
437 }
438
439 void FullscreenController::LostMouseLock() {
440   mouse_lock_state_ = MOUSELOCK_NOT_REQUESTED;
441   SetMouseLockTab(NULL);
442   NotifyMouseLockChange();
443   UpdateFullscreenExitBubbleContent();
444 }
445
446 void FullscreenController::Observe(int type,
447     const content::NotificationSource& source,
448     const content::NotificationDetails& details) {
449   switch (type) {
450     case content::NOTIFICATION_NAV_ENTRY_COMMITTED:
451       if (content::Details<content::LoadCommittedDetails>(details)->
452               is_navigation_to_different_page()) {
453         ExitTabFullscreenOrMouseLockIfNecessary();
454       }
455       break;
456
457     default:
458       NOTREACHED() << "Got a notification we didn't register for.";
459   }
460 }
461
462 GURL FullscreenController::GetFullscreenExitBubbleURL() const {
463   if (fullscreened_tab_)
464     return fullscreened_tab_->GetURL();
465   else if (mouse_lock_tab_)
466     return mouse_lock_tab_->GetURL();
467   else if (!extension_caused_fullscreen_.is_empty())
468     return extension_caused_fullscreen_;
469   else
470     return GURL();
471 }
472
473 FullscreenExitBubbleType FullscreenController::GetFullscreenExitBubbleType()
474     const {
475   // In kiosk and exclusive app mode we always want to be fullscreen and do not
476   // want to show exit instructions for browser mode fullscreen.
477   bool app_mode = false;
478 #if !defined(OS_MACOSX)  // App mode (kiosk) is not available on Mac yet.
479   app_mode = chrome::IsRunningInAppMode();
480 #endif
481
482   if (mouse_lock_state_ == MOUSELOCK_ACCEPTED_SILENTLY) {
483     return FEB_TYPE_NONE;
484   }
485
486   if (fullscreened_tab_) {
487     if (tab_fullscreen_accepted_) {
488       if (IsPrivilegedFullscreenForTab()) {
489         return FEB_TYPE_NONE;
490       } else if (IsMouseLocked()) {
491         return FEB_TYPE_FULLSCREEN_MOUSELOCK_EXIT_INSTRUCTION;
492       } else if (IsMouseLockRequested()) {
493         return FEB_TYPE_MOUSELOCK_BUTTONS;
494       } else {
495         return FEB_TYPE_FULLSCREEN_EXIT_INSTRUCTION;
496       }
497     } else {  // Full screen not yet accepted.
498       if (IsMouseLockRequested()) {
499         return FEB_TYPE_FULLSCREEN_MOUSELOCK_BUTTONS;
500       } else {
501         return FEB_TYPE_FULLSCREEN_BUTTONS;
502       }
503     }
504   } else {  // Not tab full screen.
505     if (IsMouseLocked()) {
506       return FEB_TYPE_MOUSELOCK_EXIT_INSTRUCTION;
507     } else if (IsMouseLockRequested()) {
508       return FEB_TYPE_MOUSELOCK_BUTTONS;
509     } else {
510       if (!extension_caused_fullscreen_.is_empty()) {
511         return FEB_TYPE_BROWSER_EXTENSION_FULLSCREEN_EXIT_INSTRUCTION;
512       } else if (toggled_into_fullscreen_ && !app_mode) {
513         return FEB_TYPE_BROWSER_FULLSCREEN_EXIT_INSTRUCTION;
514       } else {
515         return FEB_TYPE_NONE;
516       }
517     }
518   }
519   NOTREACHED();
520   return FEB_TYPE_NONE;
521 }
522
523 void FullscreenController::UpdateNotificationRegistrations() {
524   if (fullscreened_tab_ && mouse_lock_tab_)
525     DCHECK(fullscreened_tab_ == mouse_lock_tab_);
526
527   WebContents* tab = fullscreened_tab_ ? fullscreened_tab_ : mouse_lock_tab_;
528
529   if (tab && registrar_.IsEmpty()) {
530     registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
531         content::Source<content::NavigationController>(&tab->GetController()));
532   } else if (!tab && !registrar_.IsEmpty()) {
533     registrar_.RemoveAll();
534   }
535 }
536
537 void FullscreenController::PostFullscreenChangeNotification(
538     bool is_fullscreen) {
539   base::MessageLoop::current()->PostTask(
540       FROM_HERE,
541       base::Bind(&FullscreenController::NotifyFullscreenChange,
542                  ptr_factory_.GetWeakPtr(),
543                  is_fullscreen));
544 }
545
546 void FullscreenController::NotifyFullscreenChange(bool is_fullscreen) {
547   content::NotificationService::current()->Notify(
548       chrome::NOTIFICATION_FULLSCREEN_CHANGED,
549       content::Source<FullscreenController>(this),
550       content::Details<bool>(&is_fullscreen));
551 }
552
553 void FullscreenController::NotifyTabOfExitIfNecessary() {
554   if (fullscreened_tab_) {
555     RenderViewHost* rvh = fullscreened_tab_->GetRenderViewHost();
556     SetFullscreenedTab(NULL);
557     state_prior_to_tab_fullscreen_ = STATE_INVALID;
558     tab_fullscreen_accepted_ = false;
559     if (rvh)
560       rvh->ExitFullscreen();
561   }
562
563   if (mouse_lock_tab_) {
564     if (IsMouseLockRequested()) {
565       mouse_lock_tab_->GotResponseToLockMouseRequest(false);
566       NotifyMouseLockChange();
567     } else {
568       UnlockMouse();
569     }
570     SetMouseLockTab(NULL);
571     mouse_lock_state_ = MOUSELOCK_NOT_REQUESTED;
572   }
573
574   UpdateFullscreenExitBubbleContent();
575 }
576
577 void FullscreenController::NotifyMouseLockChange() {
578   content::NotificationService::current()->Notify(
579       chrome::NOTIFICATION_MOUSE_LOCK_CHANGED,
580       content::Source<FullscreenController>(this),
581       content::NotificationService::NoDetails());
582 }
583
584 void FullscreenController::ToggleFullscreenModeInternal(
585     FullscreenInternalOption option) {
586 #if defined(OS_WIN)
587   // When in Metro snap mode, toggling in and out of fullscreen is prevented.
588   if (IsInMetroSnapMode())
589     return;
590 #endif
591
592   bool enter_fullscreen = !window_->IsFullscreen();
593 #if defined(OS_MACOSX)
594   // When a Mac user requests a toggle they may be toggling between
595   // FullscreenWithoutChrome and FullscreenWithChrome.
596   if (!IsFullscreenForTabOrPending()) {
597     if (option == BROWSER_WITH_CHROME)
598       enter_fullscreen |= window_->IsFullscreenWithoutChrome();
599     else
600       enter_fullscreen |= window_->IsFullscreenWithChrome();
601   }
602 #endif
603
604   // In kiosk mode, we always want to be fullscreen. When the browser first
605   // starts we're not yet fullscreen, so let the initial toggle go through.
606   if (chrome::IsRunningInAppMode() && window_->IsFullscreen())
607     return;
608
609 #if !defined(OS_MACOSX)
610   // Do not enter fullscreen mode if disallowed by pref. This prevents the user
611   // from manually entering fullscreen mode and also disables kiosk mode on
612   // desktop platforms.
613   if (enter_fullscreen &&
614       !profile_->GetPrefs()->GetBoolean(prefs::kFullscreenAllowed)) {
615     return;
616   }
617 #endif
618
619   if (enter_fullscreen)
620     EnterFullscreenModeInternal(option);
621   else
622     ExitFullscreenModeInternal();
623 }
624
625 void FullscreenController::EnterFullscreenModeInternal(
626     FullscreenInternalOption option) {
627   toggled_into_fullscreen_ = true;
628   GURL url;
629   if (option == TAB) {
630     url = browser_->tab_strip_model()->GetActiveWebContents()->GetURL();
631     tab_fullscreen_accepted_ =
632         GetFullscreenSetting(url) == CONTENT_SETTING_ALLOW;
633   } else {
634     if (!extension_caused_fullscreen_.is_empty())
635       url = extension_caused_fullscreen_;
636   }
637
638   if (option == BROWSER)
639     content::RecordAction(UserMetricsAction("ToggleFullscreen"));
640   // TODO(scheib): Record metrics for WITH_CHROME, without counting transitions
641   // from tab fullscreen out to browser with chrome.
642
643 #if defined(OS_MACOSX)
644   if (option == BROWSER_WITH_CHROME) {
645     CHECK(chrome::mac::SupportsSystemFullscreen());
646     window_->EnterFullscreenWithChrome();
647   } else {
648 #else
649   {
650 #endif
651     window_->EnterFullscreen(url, GetFullscreenExitBubbleType());
652   }
653
654   UpdateFullscreenExitBubbleContent();
655
656   // Once the window has become fullscreen it'll call back to
657   // WindowFullscreenStateChanged(). We don't do this immediately as
658   // BrowserWindow::EnterFullscreen() asks for bookmark_bar_state_, so we let
659   // the BrowserWindow invoke WindowFullscreenStateChanged when appropriate.
660 }
661
662 void FullscreenController::ExitFullscreenModeInternal() {
663   toggled_into_fullscreen_ = false;
664 #if defined(OS_MACOSX)
665   // Mac windows report a state change instantly, and so we must also clear
666   // state_prior_to_tab_fullscreen_ to match them else other logic using
667   // state_prior_to_tab_fullscreen_ will be incorrect.
668   NotifyTabOfExitIfNecessary();
669 #endif
670   window_->ExitFullscreen();
671   extension_caused_fullscreen_ = GURL();
672
673   UpdateFullscreenExitBubbleContent();
674 }
675
676 void FullscreenController::SetFullscreenedTab(WebContents* tab) {
677   fullscreened_tab_ = tab;
678   UpdateNotificationRegistrations();
679 }
680
681 void FullscreenController::SetMouseLockTab(WebContents* tab) {
682   mouse_lock_tab_ = tab;
683   UpdateNotificationRegistrations();
684 }
685
686 void FullscreenController::ExitTabFullscreenOrMouseLockIfNecessary() {
687   if (IsFullscreenForTabOrPending())
688     ToggleFullscreenModeForTab(fullscreened_tab_, false);
689   else
690     NotifyTabOfExitIfNecessary();
691 }
692
693 void FullscreenController::UpdateFullscreenExitBubbleContent() {
694   GURL url = GetFullscreenExitBubbleURL();
695   FullscreenExitBubbleType bubble_type = GetFullscreenExitBubbleType();
696
697   // If bubble displays buttons, unlock mouse to allow pressing them.
698   if (fullscreen_bubble::ShowButtonsForType(bubble_type) && IsMouseLocked())
699     UnlockMouse();
700
701   window_->UpdateFullscreenExitBubbleContent(url, bubble_type);
702 }
703
704 ContentSetting
705 FullscreenController::GetFullscreenSetting(const GURL& url) const {
706   if (IsPrivilegedFullscreenForTab() || url.SchemeIsFile())
707     return CONTENT_SETTING_ALLOW;
708
709   return profile_->GetHostContentSettingsMap()->GetContentSetting(url, url,
710       CONTENT_SETTINGS_TYPE_FULLSCREEN, std::string());
711 }
712
713 ContentSetting
714 FullscreenController::GetMouseLockSetting(const GURL& url) const {
715   if (IsPrivilegedFullscreenForTab() || url.SchemeIsFile())
716     return CONTENT_SETTING_ALLOW;
717
718   HostContentSettingsMap* settings_map = profile_->GetHostContentSettingsMap();
719   return settings_map->GetContentSetting(url, url,
720       CONTENT_SETTINGS_TYPE_MOUSELOCK, std::string());
721 }
722
723 bool FullscreenController::IsPrivilegedFullscreenForTab() const {
724   const bool embedded_widget_present =
725       fullscreened_tab_ &&
726       fullscreened_tab_->GetFullscreenRenderWidgetHostView() &&
727       IsFullscreenWithinTabPossible();
728   return embedded_widget_present || is_privileged_fullscreen_for_testing_;
729 }
730
731 void FullscreenController::SetPrivilegedFullscreenForTesting(
732     bool is_privileged) {
733   is_privileged_fullscreen_for_testing_ = is_privileged;
734 }
735
736 bool FullscreenController::IsFullscreenWithinTabPossible() const {
737   return implicit_cast<const content::WebContentsDelegate*>(browser_)->
738       EmbedsFullscreenWidget();
739 }
740
741 bool FullscreenController::MaybeToggleFullscreenForCapturedTab(
742     WebContents* web_contents, bool enter_fullscreen) {
743   if (!IsFullscreenWithinTabPossible())
744     return false;
745
746   if (enter_fullscreen) {
747     if (web_contents->GetCapturerCount() > 0) {
748       captured_tabs_.insert(web_contents);
749       return true;
750     }
751   } else {
752     const std::set<const WebContents*>::iterator it =
753         captured_tabs_.find(web_contents);
754     if (it != captured_tabs_.end()) {
755       captured_tabs_.erase(it);
756       return true;
757     }
758   }
759
760   return false;
761 }
762
763 bool FullscreenController::IsFullscreenForCapturedTab(
764     const WebContents* web_contents) const {
765   if (captured_tabs_.find(web_contents) != captured_tabs_.end()) {
766     DCHECK(IsFullscreenWithinTabPossible());
767     DCHECK_NE(fullscreened_tab_, web_contents);
768     return true;
769   }
770   return false;
771 }
772
773 void FullscreenController::UnlockMouse() {
774   if (!mouse_lock_tab_)
775     return;
776   content::RenderWidgetHostView* mouse_lock_view =
777       (fullscreened_tab_ == mouse_lock_tab_ && IsPrivilegedFullscreenForTab()) ?
778       mouse_lock_tab_->GetFullscreenRenderWidgetHostView() : NULL;
779   if (!mouse_lock_view) {
780     RenderViewHost* const rvh = mouse_lock_tab_->GetRenderViewHost();
781     if (rvh)
782       mouse_lock_view = rvh->GetView();
783   }
784   if (mouse_lock_view)
785     mouse_lock_view->UnlockMouse();
786 }