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