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