blob: 88cd8fd9c005886729539400d6b7c777dbb1240a [file] [log] [blame]
// 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/mac/mac_util.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<DisplaySettingsProviderCocoa> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(DisplaySettingsProviderCocoa);
};
DisplaySettingsProviderCocoa::DisplaySettingsProviderCocoa()
: active_space_change_(nil),
weak_factory_(this) {
AppController* appController = static_cast<AppController*>([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<AppController*>([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<bool>(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();
}