Commit 044d85df authored by ccameron's avatar ccameron Committed by Commit bot

Mac: Use Mavericks occlusion APIs for power savings.

Use NSWindowDidChangeOcclusionStateNotification (new in 10.9
Mavericks) to detect when the application is hidden, the window is
minimized or obscured by another window.

Send the WasHidden to the WebContentsImpl, because this will
take into account tab capture.

When RenderWidgetHostViewMac::WasHidden is called, the previous
behavior was to destroy the BrowserCompositorViewMac. This has
the undesirable effect of creating a white flash when a hidden
window is re-exposed.

To prevent this, only destroy the BrowserCompositorViewMac when
the the RenderWidgetHostViewMac is removed from all NSWindows.
Manage the state of BrowserCompositorViewMac explicitly as being
Active (previously whenever it existed), Suspended (the new state
where it is kept around, but the RWH and DFH are hidden), and
Destroyed (previously wherever it did not exist).

Move some 10.7 APIs to base because they belong there, not in
content.

BUG=310374

Review URL: https://codereview.chromium.org/684343002

Cr-Commit-Position: refs/heads/master@{#302160}
parent dfad17b4
......@@ -210,6 +210,8 @@ BASE_EXPORT extern "C" NSString* const NSWindowWillEnterFullScreenNotification;
BASE_EXPORT extern "C" NSString* const NSWindowWillExitFullScreenNotification;
BASE_EXPORT extern "C" NSString* const NSWindowDidEnterFullScreenNotification;
BASE_EXPORT extern "C" NSString* const NSWindowDidExitFullScreenNotification;
BASE_EXPORT extern "C" NSString* const
NSWindowDidChangeBackingPropertiesNotification;
@protocol NSWindowDelegateFullScreenAdditions
- (void)windowDidFailToEnterFullScreen:(NSWindow*)window;
......@@ -293,6 +295,10 @@ typedef NSUInteger NSWindowOcclusionState;
- (NSWindowOcclusionState)occlusionState;
@end
BASE_EXPORT extern "C" NSString* const
NSWindowDidChangeOcclusionStateNotification;
enum {
NSWorkspaceLaunchWithErrorPresentation = 0x00000040
};
......
......@@ -20,8 +20,20 @@ NSString* const NSWindowDidEnterFullScreenNotification =
NSString* const NSWindowDidExitFullScreenNotification =
@"NSWindowDidExitFullScreenNotification";
NSString* const NSWindowDidChangeBackingPropertiesNotification =
@"NSWindowDidChangeBackingPropertiesNotification";
#endif // MAC_OS_X_VERSION_10_7
// Replicate specific 10.9 SDK declarations for building with prior SDKs.
#if !defined(MAC_OS_X_VERSION_10_9) || \
MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_9
NSString* const NSWindowDidChangeOcclusionStateNotification =
@"NSWindowDidChangeOcclusionStateNotification";
#endif // MAC_OS_X_VERSION_10_9
// Replicate specific 10.10 SDK declarations for building with prior SDKs.
#if !defined(MAC_OS_X_VERSION_10_10) || \
MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_10
......
......@@ -377,11 +377,43 @@ class CONTENT_EXPORT RenderWidgetHostViewMac
// The background CoreAnimation layer which is hosted by |cocoa_view_|.
base::scoped_nsobject<CALayer> background_layer_;
// The state of |delegated_frame_host_| and |browser_compositor_view_| to
// manage being visible, hidden, or occluded.
enum BrowserCompositorViewState {
// Effects:
// - |browser_compositor_view_| exists and |delegated_frame_host_| is
// visible.
// Happens when:
// - |render_widet_host_| is in the visible state (this includes when
// the tab isn't visible, but tab capture is enabled).
BrowserCompositorActive,
// Effects:
// - |browser_compositor_view_| exists, but |delegated_frame_host_| has
// been hidden.
// Happens when:
// - The |render_widget_host_| is hidden, but |cocoa_view_| is still in the
// NSWindow hierarchy.
// - This happens when |cocoa_view_| is hidden (minimized, on another
// occluded by other windows, etc). The |browser_compositor_view_| and
// its CALayers are kept around so that we will have content to show when
// we are un-occluded.
BrowserCompositorSuspended,
// Effects:
// - |browser_compositor_view_| has been destroyed and
// |delegated_frame_host_| has been hidden.
// Happens when:
// - The |render_widget_host_| is hidden or dead, and |cocoa_view_| is not
// attached to a NSWindow.
// - This happens for backgrounded tabs.
BrowserCompositorDestroyed,
};
BrowserCompositorViewState browser_compositor_state_;
// Delegated frame management and compositior.
scoped_ptr<DelegatedFrameHost> delegated_frame_host_;
scoped_ptr<ui::Layer> root_layer_;
// Container for the NSView drawn by the browser compositor.
// Container for the CALayer tree drawn by the browser compositor.
scoped_ptr<BrowserCompositorViewMac> browser_compositor_view_;
// Placeholder that is allocated while browser_compositor_view_ is NULL,
......@@ -429,6 +461,10 @@ class CONTENT_EXPORT RenderWidgetHostViewMac
void BrowserCompositorViewFrameSwapped(
const std::vector<ui::LatencyInfo>& latency_info) override;
// Transition from being in the Suspended state to being in the Destroyed
// state, if appropriate (see BrowserCompositorViewState for details).
void DestroySuspendedBrowserCompositorViewIfNeeded();
private:
friend class RenderWidgetHostViewMacTest;
......@@ -443,7 +479,10 @@ class CONTENT_EXPORT RenderWidgetHostViewMac
// ensure no dangling references.
void ShutdownBrowserCompositor();
// The state of the the browser compositor and delegated frame host. See
// BrowserCompositorViewState for details.
void EnsureBrowserCompositorView();
void SuspendBrowserCompositorView();
void DestroyBrowserCompositorView();
// IPC message handlers.
......
......@@ -116,15 +116,6 @@ BOOL EventIsReservedBySystem(NSEvent* event) {
- (BOOL)isSpeaking;
@end
// Declare things that are part of the 10.7 SDK.
#if !defined(MAC_OS_X_VERSION_10_7) || \
MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
static NSString* const NSWindowDidChangeBackingPropertiesNotification =
@"NSWindowDidChangeBackingPropertiesNotification";
#endif // 10.7
// This method will return YES for OS X versions 10.7.3 and later, and NO
// otherwise.
// Used to prevent a crash when building with the 10.7 SDK and accessing the
......@@ -404,7 +395,9 @@ namespace content {
// DelegatedFrameHost, public:
ui::Compositor* RenderWidgetHostViewMac::GetCompositor() const {
if (browser_compositor_view_)
// When |browser_compositor_view_| is suspended or destroyed, the connection
// between its ui::Compositor and |delegated_frame_host_| has been severed.
if (browser_compositor_state_ == BrowserCompositorActive)
return browser_compositor_view_->GetCompositor();
return NULL;
}
......@@ -519,6 +512,7 @@ RenderWidgetHostViewMac::RenderWidgetHostViewMac(RenderWidgetHost* widget,
: render_widget_host_(RenderWidgetHostImpl::From(widget)),
text_input_type_(ui::TEXT_INPUT_TYPE_NONE),
can_compose_inline_(true),
browser_compositor_state_(BrowserCompositorDestroyed),
browser_compositor_view_placeholder_(
new BrowserCompositorViewPlaceholderMac),
is_loading_(false),
......@@ -588,29 +582,66 @@ void RenderWidgetHostViewMac::SetAllowPauseForResizeOrRepaint(bool allow) {
// RenderWidgetHostViewMac, RenderWidgetHostView implementation:
void RenderWidgetHostViewMac::EnsureBrowserCompositorView() {
if (browser_compositor_view_)
return;
TRACE_EVENT0("browser",
"RenderWidgetHostViewMac::EnsureBrowserCompositorView");
browser_compositor_view_.reset(
new BrowserCompositorViewMac(this, cocoa_view_, root_layer_.get()));
delegated_frame_host_->AddedToWindow();
delegated_frame_host_->WasShown(ui::LatencyInfo());
// Create the view, to transition from Destroyed -> Suspended.
if (browser_compositor_state_ == BrowserCompositorDestroyed) {
browser_compositor_view_.reset(
new BrowserCompositorViewMac(this, cocoa_view_, root_layer_.get()));
browser_compositor_state_ = BrowserCompositorSuspended;
}
// Show the DelegatedFrameHost to transition from Suspended -> Active.
if (browser_compositor_state_ == BrowserCompositorSuspended) {
delegated_frame_host_->AddedToWindow();
delegated_frame_host_->WasShown(ui::LatencyInfo());
browser_compositor_state_ = BrowserCompositorActive;
}
}
void RenderWidgetHostViewMac::SuspendBrowserCompositorView() {
TRACE_EVENT0("browser",
"RenderWidgetHostViewMac::SuspendBrowserCompositorView");
// Hide the DelegatedFrameHost to transition from Active -> Suspended.
if (browser_compositor_state_ == BrowserCompositorActive) {
// Marking the DelegatedFrameHost as removed from the window hierarchy is
// necessary to remove all connections to its old ui::Compositor.
delegated_frame_host_->WasHidden();
delegated_frame_host_->RemovingFromWindow();
browser_compositor_state_ = BrowserCompositorSuspended;
}
}
void RenderWidgetHostViewMac::DestroyBrowserCompositorView() {
TRACE_EVENT0("browser",
"RenderWidgetHostViewMac::DestroyBrowserCompositorView");
if (!browser_compositor_view_)
// Transition from Active -> Suspended if need be.
SuspendBrowserCompositorView();
// Destroy the BrowserCompositorView to transition Suspended -> Destroyed.
if (browser_compositor_state_ == BrowserCompositorSuspended) {
browser_compositor_view_.reset();
browser_compositor_state_ = BrowserCompositorDestroyed;
}
}
void RenderWidgetHostViewMac::DestroySuspendedBrowserCompositorViewIfNeeded() {
if (browser_compositor_state_ != BrowserCompositorSuspended)
return;
// If this view is in a window that is visible, keep around the suspended
// BrowserCompositorView in case |cocoa_view_| is suddenly revealed (so that
// we don't flash white).
NSWindow* window = [cocoa_view_ window];
if (window)
return;
// Marking the DelegatedFrameHost as removed from the window hierarchy is
// necessary to remove all connections to its old ui::Compositor.
delegated_frame_host_->WasHidden();
delegated_frame_host_->RemovingFromWindow();
browser_compositor_view_.reset();
// This should only be reached if |render_widget_host_| is hidden, destroyed,
// or in the process of being destroyed.
DestroyBrowserCompositorView();
}
bool RenderWidgetHostViewMac::OnMessageReceived(const IPC::Message& message) {
......@@ -796,11 +827,12 @@ void RenderWidgetHostViewMac::WasHidden() {
if (render_widget_host_->is_hidden())
return;
DestroyBrowserCompositorView();
// If we have a renderer, then inform it that we are being hidden so it can
// reduce its resource utilization.
render_widget_host_->WasHidden();
SuspendBrowserCompositorView();
DestroySuspendedBrowserCompositorViewIfNeeded();
}
void RenderWidgetHostViewMac::SetSize(const gfx::Size& size) {
......@@ -3053,8 +3085,13 @@ extern NSString *NSTextInputReplacementRangeAttributeName;
}
- (void)viewDidMoveToWindow {
if ([self window])
if ([self window]) {
[self updateScreenProperties];
} else {
// If the RenderWidgetHostViewCocoa is being removed from its window, tear
// down its browser compositor resources, if needed.
renderWidgetHostView_->DestroySuspendedBrowserCompositorViewIfNeeded();
}
if (canBeKeyView_) {
NSWindow* newWindow = [self window];
......
......@@ -8,7 +8,9 @@
#include <string>
#import "base/mac/mac_util.h"
#import "base/mac/scoped_sending_event.h"
#include "base/mac/sdk_forward_declarations.h"
#include "base/message_loop/message_loop.h"
#import "base/message_loop/message_pump_mac.h"
#include "content/browser/frame_host/popup_menu_helper_mac.h"
......@@ -594,4 +596,45 @@ void WebContentsViewMac::CloseTab() {
[subview setFrame:self.bounds];
}
- (void)viewWillMoveToWindow:(NSWindow*)newWindow {
NSWindow* oldWindow = [self window];
NSNotificationCenter* notificationCenter =
[NSNotificationCenter defaultCenter];
// Occlusion notification APIs are new in Mavericks.
bool supportsOcclusionAPIs = base::mac::IsOSMavericksOrLater();
if (supportsOcclusionAPIs) {
if (oldWindow) {
[notificationCenter
removeObserver:self
name:NSWindowDidChangeOcclusionStateNotification
object:oldWindow];
}
if (newWindow) {
[notificationCenter
addObserver:self
selector:@selector(windowChangedOcclusionState:)
name:NSWindowDidChangeOcclusionStateNotification
object:newWindow];
}
}
}
- (void)windowChangedOcclusionState:(NSNotification*)notification {
DCHECK(base::mac::IsOSMavericksOrLater());
NSWindow* window = [notification object];
WebContentsImpl* webContents = [self webContents];
if (window && webContents) {
if ([window occlusionState] & NSWindowOcclusionStateVisible) {
if (!webContents->should_normally_be_visible())
webContents->WasShown();
} else {
if (webContents->should_normally_be_visible())
webContents->WasHidden();
}
}
}
@end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment