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