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 "base/bind.h" 6 #include "base/mac/mac_util.h" 7 #include "base/memory/weak_ptr.h" 8 #include "base/message_loop/message_loop.h" 9 #import "chrome/browser/app_controller_mac.h" 10 #include "chrome/browser/chrome_notification_types.h" 11 #include "chrome/browser/fullscreen.h" 12 #include "chrome/browser/ui/browser.h" 13 #include "chrome/browser/ui/browser_window.h" 14 #include "chrome/browser/ui/cocoa/last_active_browser_cocoa.h" 15 #include "chrome/browser/ui/panels/display_settings_provider.h" 16 #include "content/public/browser/notification_observer.h" 17 #include "content/public/browser/notification_registrar.h" 18 #include "content/public/browser/notification_service.h" 19 #include "content/public/browser/notification_source.h" 20 #include "ui/base/work_area_watcher_observer.h" 21 22 namespace { 23 24 // The time, in milliseconds, that a fullscreen check will be started after 25 // the active workspace change is notified. This value is from experiment. 26 const int kCheckFullScreenDelayTimeMs = 200; 27 28 class DisplaySettingsProviderCocoa : public DisplaySettingsProvider, 29 public ui::WorkAreaWatcherObserver, 30 public content::NotificationObserver { 31 public: 32 DisplaySettingsProviderCocoa(); 33 virtual ~DisplaySettingsProviderCocoa(); 34 35 void ActiveSpaceChanged(); 36 37 protected: 38 // Overridden from DisplaySettingsProvider: 39 virtual bool NeedsPeriodicFullScreenCheck() const OVERRIDE; 40 virtual bool IsFullScreen() OVERRIDE; 41 42 // Overridden from ui::WorkAreaWatcherObserver: 43 virtual void WorkAreaChanged() OVERRIDE; 44 45 // Overridden from content::NotificationObserver: 46 virtual void Observe(int type, 47 const content::NotificationSource& source, 48 const content::NotificationDetails& details) OVERRIDE; 49 50 private: 51 void ActiveWorkSpaceChanged(); 52 53 content::NotificationRegistrar registrar_; 54 id active_space_change_; 55 56 // Owned by MessageLoop after posting. 57 base::WeakPtrFactory<DisplaySettingsProviderCocoa> weak_factory_; 58 59 DISALLOW_COPY_AND_ASSIGN(DisplaySettingsProviderCocoa); 60 }; 61 62 DisplaySettingsProviderCocoa::DisplaySettingsProviderCocoa() 63 : active_space_change_(nil), 64 weak_factory_(this) { 65 AppController* appController = static_cast<AppController*>([NSApp delegate]); 66 [appController addObserverForWorkAreaChange:this]; 67 68 registrar_.Add( 69 this, 70 chrome::NOTIFICATION_FULLSCREEN_CHANGED, 71 content::NotificationService::AllSources()); 72 73 active_space_change_ = [[[NSWorkspace sharedWorkspace] notificationCenter] 74 addObserverForName:NSWorkspaceActiveSpaceDidChangeNotification 75 object:nil 76 queue:[NSOperationQueue mainQueue] 77 usingBlock:^(NSNotification* notification) { 78 ActiveWorkSpaceChanged(); 79 }]; 80 } 81 82 DisplaySettingsProviderCocoa::~DisplaySettingsProviderCocoa() { 83 AppController* appController = static_cast<AppController*>([NSApp delegate]); 84 [appController removeObserverForWorkAreaChange:this]; 85 86 [[[NSWorkspace sharedWorkspace] notificationCenter] 87 removeObserver:active_space_change_]; 88 } 89 90 bool DisplaySettingsProviderCocoa::NeedsPeriodicFullScreenCheck() const { 91 // Lion system introduces fullscreen support. When a window of an application 92 // enters fullscreen mode, the system will automatically hide all other 93 // windows, even including topmost windows that come from other applications. 94 // So we don't need to do anything when any other application enters 95 // fullscreen mode. We still need to handle the case when chrome enters 96 // fullscreen mode and our topmost windows will not get hided by the system. 97 return !chrome::mac::SupportsSystemFullscreen(); 98 } 99 100 bool DisplaySettingsProviderCocoa::IsFullScreen() { 101 // For Lion and later, we only need to check if chrome enters fullscreen mode 102 // (see detailed reason above in NeedsPeriodicFullScreenCheck). 103 if (!chrome::mac::SupportsSystemFullscreen()) 104 return DisplaySettingsProvider::IsFullScreen(); 105 106 Browser* browser = chrome::GetLastActiveBrowser(); 107 if (!browser) 108 return false; 109 BrowserWindow* browser_window = browser->window(); 110 if (!browser_window->IsFullscreen()) 111 return false; 112 113 // If the user switches to another space where the fullscreen browser window 114 // does not live, we do not call it fullscreen. 115 NSWindow* native_window = browser_window->GetNativeWindow(); 116 return [native_window isOnActiveSpace]; 117 } 118 119 void DisplaySettingsProviderCocoa::WorkAreaChanged() { 120 OnDisplaySettingsChanged(); 121 } 122 123 void DisplaySettingsProviderCocoa::Observe( 124 int type, 125 const content::NotificationSource& source, 126 const content::NotificationDetails& details) { 127 DCHECK_EQ(chrome::NOTIFICATION_FULLSCREEN_CHANGED, type); 128 // When we receive the fullscreen notification, the Chrome Window has not been 129 // put on the active space yet and thus IsFullScreen will return false. 130 // Since the fullscreen result is already known here, we can pass it dierctly 131 // to CheckFullScreenMode. 132 bool is_fullscreen = *(content::Details<bool>(details)).ptr(); 133 CheckFullScreenMode( 134 is_fullscreen ? ASSUME_FULLSCREEN_ON : ASSUME_FULLSCREEN_OFF); 135 } 136 137 void DisplaySettingsProviderCocoa::ActiveWorkSpaceChanged() { 138 // The active workspace notification might be received earlier than the 139 // browser window knows that it is not in active space. 140 base::MessageLoop::current()->PostDelayedTask( 141 FROM_HERE, 142 base::Bind(&DisplaySettingsProviderCocoa::CheckFullScreenMode, 143 weak_factory_.GetWeakPtr(), 144 PERFORM_FULLSCREEN_CHECK), 145 base::TimeDelta::FromMilliseconds(kCheckFullScreenDelayTimeMs)); 146 } 147 148 } // namespace 149 150 // static 151 DisplaySettingsProvider* DisplaySettingsProvider::Create() { 152 return new DisplaySettingsProviderCocoa(); 153 } 154