// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/bind.h" #include "base/memory/weak_ptr.h" #include "base/message_loop/message_loop.h" #import "chrome/browser/app_controller_mac.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/fullscreen.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/cocoa/last_active_browser_cocoa.h" #include "chrome/browser/ui/panels/display_settings_provider.h" #include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_registrar.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/notification_source.h" #include "ui/base/work_area_watcher_observer.h" namespace { // The time, in milliseconds, that a fullscreen check will be started after // the active workspace change is notified. This value is from experiment. const int kCheckFullScreenDelayTimeMs = 200; class DisplaySettingsProviderCocoa : public DisplaySettingsProvider, public ui::WorkAreaWatcherObserver, public content::NotificationObserver { public: DisplaySettingsProviderCocoa(); ~DisplaySettingsProviderCocoa() override; void ActiveSpaceChanged(); protected: // Overridden from DisplaySettingsProvider: bool NeedsPeriodicFullScreenCheck() const override; bool IsFullScreen() override; // Overridden from ui::WorkAreaWatcherObserver: void WorkAreaChanged() override; // Overridden from content::NotificationObserver: void Observe(int type, const content::NotificationSource& source, const content::NotificationDetails& details) override; private: void ActiveWorkSpaceChanged(); content::NotificationRegistrar registrar_; id active_space_change_; // Owned by MessageLoop after posting. base::WeakPtrFactory weak_factory_; DISALLOW_COPY_AND_ASSIGN(DisplaySettingsProviderCocoa); }; DisplaySettingsProviderCocoa::DisplaySettingsProviderCocoa() : active_space_change_(nil), weak_factory_(this) { AppController* appController = static_cast([NSApp delegate]); [appController addObserverForWorkAreaChange:this]; registrar_.Add( this, chrome::NOTIFICATION_FULLSCREEN_CHANGED, content::NotificationService::AllSources()); active_space_change_ = [[[NSWorkspace sharedWorkspace] notificationCenter] addObserverForName:NSWorkspaceActiveSpaceDidChangeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification) { ActiveWorkSpaceChanged(); }]; } DisplaySettingsProviderCocoa::~DisplaySettingsProviderCocoa() { AppController* appController = static_cast([NSApp delegate]); [appController removeObserverForWorkAreaChange:this]; [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:active_space_change_]; } bool DisplaySettingsProviderCocoa::NeedsPeriodicFullScreenCheck() const { // Lion system introduces fullscreen support. When a window of an application // enters fullscreen mode, the system will automatically hide all other // windows, even including topmost windows that come from other applications. // So we don't need to do anything when any other application enters // fullscreen mode. We still need to handle the case when chrome enters // fullscreen mode and our topmost windows will not get hided by the system. return !chrome::mac::SupportsSystemFullscreen(); } bool DisplaySettingsProviderCocoa::IsFullScreen() { // For Lion and later, we only need to check if chrome enters fullscreen mode // (see detailed reason above in NeedsPeriodicFullScreenCheck). if (!chrome::mac::SupportsSystemFullscreen()) return DisplaySettingsProvider::IsFullScreen(); Browser* browser = chrome::GetLastActiveBrowser(); if (!browser) return false; BrowserWindow* browser_window = browser->window(); if (!browser_window->IsFullscreen()) return false; // If the user switches to another space where the fullscreen browser window // does not live, we do not call it fullscreen. NSWindow* native_window = browser_window->GetNativeWindow(); return [native_window isOnActiveSpace]; } void DisplaySettingsProviderCocoa::WorkAreaChanged() { OnDisplaySettingsChanged(); } void DisplaySettingsProviderCocoa::Observe( int type, const content::NotificationSource& source, const content::NotificationDetails& details) { DCHECK_EQ(chrome::NOTIFICATION_FULLSCREEN_CHANGED, type); // When we receive the fullscreen notification, the Chrome Window has not been // put on the active space yet and thus IsFullScreen will return false. // Since the fullscreen result is already known here, we can pass it dierctly // to CheckFullScreenMode. bool is_fullscreen = *(content::Details(details)).ptr(); CheckFullScreenMode( is_fullscreen ? ASSUME_FULLSCREEN_ON : ASSUME_FULLSCREEN_OFF); } void DisplaySettingsProviderCocoa::ActiveWorkSpaceChanged() { // The active workspace notification might be received earlier than the // browser window knows that it is not in active space. base::MessageLoop::current()->PostDelayedTask( FROM_HERE, base::Bind(&DisplaySettingsProviderCocoa::CheckFullScreenMode, weak_factory_.GetWeakPtr(), PERFORM_FULLSCREEN_CHECK), base::TimeDelta::FromMilliseconds(kCheckFullScreenDelayTimeMs)); } } // namespace // static DisplaySettingsProvider* DisplaySettingsProvider::Create() { return new DisplaySettingsProviderCocoa(); }