blob: e3404c2f4072b6064bf7a19cd795b77687d1aa8b [file] [log] [blame]
// Copyright 2016 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 "chrome/browser/ui/cocoa/fullscreen_low_power_coordinator.h"
namespace {
// The minimum number of frames with valid low power contents that we need to
// receive in a row before showing the low power window.
const uint64_t kMinValidFrames = 15;
} // namespace
@interface FullscreenLowPowerWindow : NSWindow {
base::scoped_nsobject<NSWindow> eventTargetWindow_;
}
- (id)initWithEventTargetWindow:(NSWindow*)eventTargetWindow
withLayer:(CALayer*)layer;
@end
@implementation FullscreenLowPowerWindow
- (id)initWithEventTargetWindow:(NSWindow*)eventTargetWindow
withLayer:(CALayer*)layer {
if (self = [super
initWithContentRect:NSMakeRect(0, 0, 256, 256)
styleMask:NSTitledWindowMask | NSResizableWindowMask |
NSFullSizeContentViewWindowMask
backing:NSBackingStoreBuffered
defer:NO]) {
eventTargetWindow_.reset(eventTargetWindow, base::scoped_policy::RETAIN);
[self setCollectionBehavior:NSWindowCollectionBehaviorIgnoresCycle];
[self setExcludedFromWindowsMenu:YES];
[self setReleasedWhenClosed:NO];
[self setIgnoresMouseEvents:YES];
base::scoped_nsobject<CALayer> content_layer([[CALayer alloc] init]);
[content_layer setBackgroundColor:CGColorGetConstantColor(kCGColorBlack)];
NSView* content_view = [self contentView];
[content_view setLayer:content_layer];
[content_view setWantsLayer:YES];
[content_layer addSublayer:layer];
}
return self;
}
- (void)sendEvent:(NSEvent*)event {
[eventTargetWindow_ sendEvent:event];
}
@end
FullscreenLowPowerCoordinatorCocoa::FullscreenLowPowerCoordinatorCocoa(
NSWindow* content_window,
ui::AcceleratedWidgetMac* widget)
: content_window_(content_window, base::scoped_policy::RETAIN),
widget_(widget) {
if (widget_) {
widget_->SetFullscreenLowPowerCoordinator(this);
CALayer* fullscreen_low_power_layer = widget_->GetFullscreenLowPowerLayer();
if (fullscreen_low_power_layer) {
low_power_window_.reset([[FullscreenLowPowerWindow alloc]
initWithEventTargetWindow:content_window_
withLayer:fullscreen_low_power_layer]);
}
}
SetHasActiveSheet([content_window_ attachedSheet]);
ChildWindowsChanged();
}
FullscreenLowPowerCoordinatorCocoa::~FullscreenLowPowerCoordinatorCocoa() {
if (widget_)
widget_->ResetFullscreenLowPowerCoordinator();
EnterOrExitLowPowerModeIfNeeded();
[low_power_window_ close];
}
NSWindow* FullscreenLowPowerCoordinatorCocoa::GetFullscreenLowPowerWindow() {
return low_power_window_.get();
}
void FullscreenLowPowerCoordinatorCocoa::SetInFullscreenTransition(
bool in_fullscreen_transition) {
allowed_by_fullscreen_transition_ = !in_fullscreen_transition;
EnterOrExitLowPowerModeIfNeeded();
}
void FullscreenLowPowerCoordinatorCocoa::SetLayoutParameters(
const NSRect& toolbar_frame,
const NSRect& infobar_frame,
const NSRect& content_frame,
const NSRect& download_shelf_frame) {
NSRect screen_frame = [[content_window_ screen] frame];
allowed_by_nsview_layout_ = true;
// The toolbar and infobar must be above the top of the screen.
allowed_by_nsview_layout_ &=
toolbar_frame.origin.y >= screen_frame.size.height;
allowed_by_nsview_layout_ &=
infobar_frame.origin.y >= screen_frame.size.height;
// The content rect must equal the screen's rect.
allowed_by_nsview_layout_ &= NSEqualRects(content_frame, screen_frame);
// The download shelf must not extend on to the screen.
allowed_by_nsview_layout_ &=
download_shelf_frame.origin.y + download_shelf_frame.size.height <= 0;
EnterOrExitLowPowerModeIfNeeded();
}
void FullscreenLowPowerCoordinatorCocoa::SetHasActiveSheet(bool has_sheet) {
allowed_by_active_sheet_ = !has_sheet;
EnterOrExitLowPowerModeIfNeeded();
}
void FullscreenLowPowerCoordinatorCocoa::ChildWindowsChanged() {
allowed_by_child_windows_ = true;
for (NSWindow* child_window in [content_window_ childWindows]) {
// The toolbar correctly appears on top of the fullscreen low power window.
if ([child_window
isKindOfClass:NSClassFromString(@"NSToolbarFullScreenWindow")]) {
continue;
}
// There is a persistent 1x1 StatusBubbleWindow child window at 0,0.
if ([child_window isKindOfClass:NSClassFromString(@"StatusBubbleWindow")] &&
NSEqualRects([child_window frame], NSMakeRect(0, 0, 1, 1))) {
continue;
}
// Don't make any assumptions about other child windows.
allowed_by_child_windows_ = false;
break;
}
EnterOrExitLowPowerModeIfNeeded();
}
void FullscreenLowPowerCoordinatorCocoa::SetLowPowerLayerValid(bool valid) {
if (valid)
low_power_layer_valid_frame_count_ += 1;
else
low_power_layer_valid_frame_count_ = 0;
EnterOrExitLowPowerModeIfNeeded();
}
void FullscreenLowPowerCoordinatorCocoa::WillLoseAcceleratedWidget() {
DCHECK(widget_);
widget_->ResetFullscreenLowPowerCoordinator();
widget_ = nullptr;
EnterOrExitLowPowerModeIfNeeded();
}
void FullscreenLowPowerCoordinatorCocoa::EnterOrExitLowPowerModeIfNeeded() {
bool new_in_low_power_mode =
widget_ && low_power_window_ &&
low_power_layer_valid_frame_count_ > kMinValidFrames &&
allowed_by_fullscreen_transition_ && allowed_by_nsview_layout_ &&
allowed_by_child_windows_ && allowed_by_active_sheet_;
if (new_in_low_power_mode) {
// Update whether or not we are in low power mode based on whether or not
// the low power window is in front (we do not get notifications of window
// order change).
in_low_power_mode_ &=
[[NSApp orderedWindows] firstObject] == low_power_window_.get();
if (!in_low_power_mode_) {
[low_power_window_ setFrame:[content_window_ frame] display:YES];
[low_power_window_
setStyleMask:[low_power_window_ styleMask] | NSFullScreenWindowMask];
[low_power_window_ orderWindow:NSWindowAbove
relativeTo:[content_window_ windowNumber]];
in_low_power_mode_ = true;
}
} else {
if (in_low_power_mode_) {
[low_power_window_ orderWindow:NSWindowBelow
relativeTo:[content_window_ windowNumber]];
in_low_power_mode_ = false;
}
}
}