#include "base/sys_info.h"
#import "content/browser/accessibility/browser_accessibility_cocoa.h"
#include "content/browser/accessibility/browser_accessibility_manager_mac.h"
-#include "content/browser/renderer_host/backing_store_mac.h"
-#include "content/browser/renderer_host/backing_store_manager.h"
+#include "content/browser/frame_host/frame_tree.h"
+#include "content/browser/frame_host/frame_tree_node.h"
+#include "content/browser/frame_host/render_frame_host_impl.h"
#include "content/browser/renderer_host/compositing_iosurface_context_mac.h"
#include "content/browser/renderer_host/compositing_iosurface_layer_mac.h"
#include "content/browser/renderer_host/compositing_iosurface_mac.h"
#include "content/common/input_messages.h"
#include "content/common/view_messages.h"
#include "content/common/webplugin_geometry.h"
-#include "content/port/browser/render_widget_host_view_frame_subscriber.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/native_web_keyboard_event.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_types.h"
+#include "content/public/browser/render_widget_host_view_frame_subscriber.h"
#import "content/public/browser/render_widget_host_view_mac_delegate.h"
#include "content/public/browser/user_metrics.h"
-#include "content/public/common/content_switches.h"
+#include "content/public/browser/web_contents.h"
#include "skia/ext/platform_canvas.h"
+#include "third_party/WebKit/public/platform/WebScreenInfo.h"
#include "third_party/WebKit/public/web/WebInputEvent.h"
-#include "third_party/WebKit/public/web/WebScreenInfo.h"
#include "third_party/WebKit/public/web/mac/WebInputEventFactory.h"
#import "third_party/mozilla/ComplexTextInputPanel.h"
#include "ui/base/cocoa/animation_utils.h"
#include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
#include "ui/gfx/screen.h"
#include "ui/gfx/size_conversions.h"
+#include "ui/gl/gl_switches.h"
#include "ui/gl/io_surface_support_mac.h"
-using content::BackingStoreMac;
using content::BrowserAccessibility;
using content::BrowserAccessibilityManager;
using content::EditCommand;
+using content::FrameTreeNode;
using content::NativeWebKeyboardEvent;
+using content::RenderFrameHost;
+using content::RenderViewHost;
using content::RenderViewHostImpl;
using content::RenderWidgetHostImpl;
using content::RenderWidgetHostViewMac;
using content::RenderWidgetHostViewMacEditCommandHelper;
using content::TextInputClientMac;
-using WebKit::WebInputEvent;
-using WebKit::WebInputEventFactory;
-using WebKit::WebMouseEvent;
-using WebKit::WebMouseWheelEvent;
-
-enum CoreAnimationStatus {
- CORE_ANIMATION_DISABLED,
- CORE_ANIMATION_ENABLED_LAZY,
- CORE_ANIMATION_ENABLED_ALWAYS,
-};
-
-static CoreAnimationStatus GetCoreAnimationStatus() {
- // TODO(sail) Remove this.
- if (!CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kUseCoreAnimation)) {
- return CORE_ANIMATION_DISABLED;
- }
- if (CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
- switches::kUseCoreAnimation) == "lazy") {
- return CORE_ANIMATION_ENABLED_LAZY;
- }
- if (CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
- switches::kUseCoreAnimation) == "disabled") {
- return CORE_ANIMATION_DISABLED;
- }
- return CORE_ANIMATION_ENABLED_ALWAYS;
-}
+using content::WebContents;
+using blink::WebInputEvent;
+using blink::WebInputEventFactory;
+using blink::WebMouseEvent;
+using blink::WebMouseWheelEvent;
+using blink::WebGestureEvent;
// These are not documented, so use only after checking -respondsToSelector:.
@interface NSApplication (UndocumentedSpeechMethods)
static NSString* const NSWindowDidChangeBackingPropertiesNotification =
@"NSWindowDidChangeBackingPropertiesNotification";
-static NSString* const NSBackingPropertyOldScaleFactorKey =
- @"NSBackingPropertyOldScaleFactorKey";
-// Note: Apple's example code (linked from the comment above
-// -windowDidChangeBackingProperties:) uses
-// @"NSWindowBackingPropertiesChangeOldBackingScaleFactorKey", but that always
-// returns an old scale of 0. @"NSBackingPropertyOldScaleFactorKey" seems to
-// work in practice, and it's what's used in Apple's WebKit port
-// (WebKit/mac/WebView/WebView.mm).
#endif // 10.7
-static inline int ToWebKitModifiers(NSUInteger flags) {
- int modifiers = 0;
- if (flags & NSControlKeyMask) modifiers |= WebInputEvent::ControlKey;
- if (flags & NSShiftKeyMask) modifiers |= WebInputEvent::ShiftKey;
- if (flags & NSAlternateKeyMask) modifiers |= WebInputEvent::AltKey;
- if (flags & NSCommandKeyMask) modifiers |= WebInputEvent::MetaKey;
- return modifiers;
-}
-
// 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
return methodDescription.name != NULL || methodDescription.types != NULL;
}
-static float ScaleFactor(NSView* view) {
+static float ScaleFactorForView(NSView* view) {
return ui::GetImageScale(ui::GetScaleFactorForNativeView(view));
}
@interface RenderWidgetHostViewCocoa ()
@property(nonatomic, assign) NSRange selectedRange;
@property(nonatomic, assign) NSRange markedRange;
-@property(nonatomic, assign)
- NSObject<RenderWidgetHostViewMacDelegate>* delegate;
+ (BOOL)shouldAutohideCursorForEvent:(NSEvent*)event;
- (id)initWithRenderWidgetHostViewMac:(RenderWidgetHostViewMac*)r;
- (void)keyEvent:(NSEvent*)theEvent wasKeyEquivalent:(BOOL)equiv;
- (void)windowDidChangeBackingProperties:(NSNotification*)notification;
- (void)windowChangedGlobalFrame:(NSNotification*)notification;
-- (void)drawBackingStore:(BackingStoreMac*)backingStore
- dirtyRect:(CGRect)dirtyRect
- inContext:(CGContextRef)context;
-- (void)updateSoftwareLayerScaleFactor;
+- (void)drawWithDirtyRect:(CGRect)dirtyRect
+ inContext:(CGContextRef)context;
- (void)checkForPluginImeCancellation;
-- (void)updateTabBackingStoreScaleFactor;
+- (void)updateScreenProperties;
+- (void)setResponderDelegate:
+ (NSObject<RenderWidgetHostViewMacDelegate>*)delegate;
@end
// A window subclass that allows the fullscreen window to become main and gain
styleMask:windowStyle
backing:bufferingType
defer:deferCreation]) {
- CHECK_EQ(CORE_ANIMATION_DISABLED, GetCoreAnimationStatus());
+ DCHECK_EQ(content::CORE_ANIMATION_DISABLED,
+ content::GetCoreAnimationStatus());
[self setOpaque:NO];
[self setBackgroundColor:[NSColor clearColor]];
[self startObservingClicks];
const size_t kMaxTooltipLength = 1024;
// TODO(suzhe): Upstream this function.
-WebKit::WebColor WebColorFromNSColor(NSColor *color) {
+blink::WebColor WebColorFromNSColor(NSColor *color) {
CGFloat r, g, b, a;
[color getRed:&r green:&g blue:&b alpha:&a];
// third_party/WebKit/Source/WebKit/mac/WebView/WebHTMLView.mm
void ExtractUnderlines(
NSAttributedString* string,
- std::vector<WebKit::WebCompositionUnderline>* underlines) {
+ std::vector<blink::WebCompositionUnderline>* underlines) {
int length = [[string string] length];
int i = 0;
while (i < length) {
longestEffectiveRange:&range
inRange:NSMakeRange(i, length - i)];
if (NSNumber *style = [attrs objectForKey:NSUnderlineStyleAttributeName]) {
- WebKit::WebColor color = SK_ColorBLACK;
+ blink::WebColor color = SK_ColorBLACK;
if (NSColor *colorAttr =
[attrs objectForKey:NSUnderlineColorAttributeName]) {
color = WebColorFromNSColor(
[colorAttr colorUsingColorSpaceName:NSDeviceRGBColorSpace]);
}
- underlines->push_back(WebKit::WebCompositionUnderline(
+ underlines->push_back(blink::WebCompositionUnderline(
range.location, NSMaxRange(range), color, [style intValue] > 1));
}
i = range.location + range.length;
TSMRemoveDocumentProperty(0, kTSMDocumentEnabledInputSourcesPropertyTag);
}
+// Calls to [NSScreen screens], required by FlipYFromRectToScreen and
+// FlipNSRectToRectScreen, can take several milliseconds. Only re-compute this
+// value when screen info changes.
+// TODO(ccameron): An observer on every RWHVCocoa will set this to false
+// on NSApplicationDidChangeScreenParametersNotification. Only one observer
+// is necessary.
+bool g_screen_info_up_to_date = false;
+
+float FlipYFromRectToScreen(float y, float rect_height) {
+ TRACE_EVENT0("browser", "FlipYFromRectToScreen");
+ static CGFloat screen_zero_height = 0;
+ if (!g_screen_info_up_to_date) {
+ if ([[NSScreen screens] count] > 0) {
+ screen_zero_height =
+ [[[NSScreen screens] objectAtIndex:0] frame].size.height;
+ g_screen_info_up_to_date = true;
+ } else {
+ return y;
+ }
+ }
+ return screen_zero_height - y - rect_height;
+}
+
// Adjusts an NSRect in Cocoa screen coordinates to have an origin in the upper
// left of the primary screen (Carbon coordinates), and stuffs it into a
// gfx::Rect.
gfx::Rect FlipNSRectToRectScreen(const NSRect& rect) {
gfx::Rect new_rect(NSRectToCGRect(rect));
- if ([[NSScreen screens] count] > 0) {
- new_rect.set_y([[[NSScreen screens] objectAtIndex:0] frame].size.height -
- new_rect.y() - new_rect.height());
- }
+ new_rect.set_y(FlipYFromRectToScreen(new_rect.y(), new_rect.height()));
return new_rect;
}
return enclosing_window;
}
-WebKit::WebScreenInfo GetWebScreenInfo(NSView* view) {
+blink::WebScreenInfo GetWebScreenInfo(NSView* view) {
gfx::Display display =
gfx::Screen::GetNativeScreen()->GetDisplayNearestWindow(view);
NSScreen* screen = [NSScreen deepestScreen];
- WebKit::WebScreenInfo results;
+ blink::WebScreenInfo results;
results.deviceScaleFactor = static_cast<int>(display.device_scale_factor());
results.depth = NSBitsPerPixelFromDepth([screen depth]);
[[screen colorSpace] colorSpaceModel] == NSGrayColorSpaceModel;
results.rect = display.bounds();
results.availableRect = display.work_area();
+ results.orientationAngle = display.RotationAsDegree();
+
return results;
}
+void RemoveLayerFromSuperlayer(
+ base::scoped_nsobject<CompositingIOSurfaceLayer> layer) {
+ // Disable the fade-out animation as the layer is removed.
+ ScopedCAActionDisabler disabler;
+ [layer removeFromSuperlayer];
+}
+
} // namespace
namespace content {
///////////////////////////////////////////////////////////////////////////////
-// RenderWidgetHostView, public:
-
-// static
-RenderWidgetHostView* RenderWidgetHostView::CreateViewForWidget(
- RenderWidgetHost* widget) {
- return new RenderWidgetHostViewMac(widget);
-}
+// RenderWidgetHostViewBase, public:
// static
-void RenderWidgetHostViewPort::GetDefaultScreenInfo(
- WebKit::WebScreenInfo* results) {
+void RenderWidgetHostViewBase::GetDefaultScreenInfo(
+ blink::WebScreenInfo* results) {
*results = GetWebScreenInfo(NULL);
}
RenderWidgetHostViewMac::RenderWidgetHostViewMac(RenderWidgetHost* widget)
: render_widget_host_(RenderWidgetHostImpl::From(widget)),
- about_to_validate_and_paint_(false),
- call_set_needs_display_in_rect_pending_(false),
last_frame_was_accelerated_(false),
text_input_type_(ui::TEXT_INPUT_TYPE_NONE),
can_compose_inline_(true),
+ compositing_iosurface_layer_async_timer_(
+ FROM_HERE, base::TimeDelta::FromMilliseconds(250),
+ this, &RenderWidgetHostViewMac::TimerSinceGotAcceleratedFrameFired),
allow_overlapping_views_(false),
use_core_animation_(false),
+ pending_latency_info_delay_(0),
+ pending_latency_info_delay_weak_ptr_factory_(this),
+ backing_store_scale_factor_(1),
is_loading_(false),
weak_factory_(this),
fullscreen_parent_host_view_(NULL),
- pending_swap_buffers_acks_weak_factory_(this),
- next_swap_ack_time_(base::Time::Now()),
+ underlay_view_has_drawn_(false),
+ overlay_view_weak_factory_(this),
software_frame_weak_ptr_factory_(this) {
software_frame_manager_.reset(new SoftwareFrameManager(
software_frame_weak_ptr_factory_.GetWeakPtr()));
cocoa_view_ = [[[RenderWidgetHostViewCocoa alloc]
initWithRenderWidgetHostViewMac:this] autorelease];
- if (GetCoreAnimationStatus() == CORE_ANIMATION_ENABLED_ALWAYS) {
- EnableCoreAnimation();
+ if (GetCoreAnimationStatus() == CORE_ANIMATION_ENABLED) {
+ use_core_animation_ = true;
+ background_layer_.reset([[CALayer alloc] init]);
+ [background_layer_
+ setBackgroundColor:CGColorGetConstantColor(kCGColorWhite)];
+ [cocoa_view_ setLayer:background_layer_];
+ [cocoa_view_ setWantsLayer:YES];
}
render_widget_host_->SetView(this);
// pointer.
cocoa_view_ = nil;
- AckPendingSwapBuffers();
UnlockMouse();
// Make sure that the layer doesn't reach into the now-invalid object.
DestroyCompositedIOSurfaceAndLayer(kDestroyContext);
- software_layer_.reset();
+ DestroySoftwareLayer();
// We are owned by RenderWidgetHostViewCocoa, so if we go away before the
// RenderWidgetHost does we need to tell it not to hold a stale pointer to
void RenderWidgetHostViewMac::SetDelegate(
NSObject<RenderWidgetHostViewMacDelegate>* delegate) {
- [cocoa_view_ setDelegate:delegate];
+ [cocoa_view_ setResponderDelegate:delegate];
}
void RenderWidgetHostViewMac::SetAllowOverlappingViews(bool overlapping) {
- if (GetCoreAnimationStatus() == CORE_ANIMATION_ENABLED_LAZY) {
- if (overlapping) {
- ScopedCAActionDisabler disabler;
- EnableCoreAnimation();
- return;
- }
- }
-
if (allow_overlapping_views_ == overlapping)
return;
allow_overlapping_views_ = overlapping;
///////////////////////////////////////////////////////////////////////////////
// RenderWidgetHostViewMac, RenderWidgetHostView implementation:
-void RenderWidgetHostViewMac::EnableCoreAnimation() {
- if (use_core_animation_)
- return;
-
- use_core_animation_ = true;
-
- // Un-bind the GL context from this view because the CoreAnimation path will
- // not use explicit setView and clearDrawable calls.
- ClearBoundContextDrawable();
-
- software_layer_.reset([[CALayer alloc] init]);
- if (!software_layer_)
- LOG(ERROR) << "Failed to create CALayer for software rendering";
- [software_layer_ setBackgroundColor:CGColorGetConstantColor(kCGColorWhite)];
- [software_layer_ setDelegate:cocoa_view_];
- [software_layer_ setContentsGravity:kCAGravityTopLeft];
- [software_layer_ setFrame:NSRectToCGRect([cocoa_view_ bounds])];
- [software_layer_ setNeedsDisplay];
- [cocoa_view_ updateSoftwareLayerScaleFactor];
-
- [cocoa_view_ setLayer:software_layer_];
- [cocoa_view_ setWantsLayer:YES];
-
- if (compositing_iosurface_) {
- if (!CreateCompositedIOSurfaceLayer()) {
- LOG(ERROR) << "Failed to create CALayer for existing IOSurface";
- GotAcceleratedCompositingError();
- return;
- }
+bool RenderWidgetHostViewMac::EnsureCompositedIOSurface() {
+ // If the context or the IOSurface's context has had an error, re-build
+ // everything from scratch.
+ if (compositing_iosurface_context_ &&
+ compositing_iosurface_context_->HasBeenPoisoned()) {
+ LOG(ERROR) << "Failing EnsureCompositedIOSurface because "
+ << "context was poisoned";
+ return false;
+ }
+ if (compositing_iosurface_ &&
+ compositing_iosurface_->HasBeenPoisoned()) {
+ LOG(ERROR) << "Failing EnsureCompositedIOSurface because "
+ << "surface was poisoned";
+ return false;
}
-}
-bool RenderWidgetHostViewMac::CreateCompositedIOSurface() {
- if (compositing_iosurface_context_ && compositing_iosurface_)
- return true;
+ int current_window_number = use_core_animation_ ?
+ CompositingIOSurfaceContext::kOffscreenContextWindowNumber :
+ window_number();
+ bool new_surface_needed = !compositing_iosurface_;
+ bool new_context_needed =
+ !compositing_iosurface_context_ ||
+ (compositing_iosurface_context_ &&
+ compositing_iosurface_context_->window_number() !=
+ current_window_number);
- ScopedCAActionDisabler disabler;
+ if (!new_surface_needed && !new_context_needed)
+ return true;
// Create the GL context and shaders.
- if (!compositing_iosurface_context_) {
- compositing_iosurface_context_ =
- CompositingIOSurfaceContext::Get(window_number());
- if (!compositing_iosurface_context_) {
+ if (new_context_needed) {
+ scoped_refptr<CompositingIOSurfaceContext> new_context =
+ CompositingIOSurfaceContext::Get(current_window_number);
+ // Un-bind the GL context from this view before binding the new GL
+ // context. Having two GL contexts bound to a view will result in
+ // crashes and corruption.
+ // http://crbug.com/230883
+ ClearBoundContextDrawable();
+ if (!new_context) {
LOG(ERROR) << "Failed to create CompositingIOSurfaceContext";
return false;
}
+ compositing_iosurface_context_ = new_context;
}
+
// Create the IOSurface texture.
- if (!compositing_iosurface_) {
- compositing_iosurface_.reset(CompositingIOSurfaceMac::Create(
- compositing_iosurface_context_));
+ if (new_surface_needed) {
+ compositing_iosurface_.reset(CompositingIOSurfaceMac::Create());
if (!compositing_iosurface_) {
LOG(ERROR) << "Failed to create CompositingIOSurface";
return false;
}
}
- // Make sure that the IOSurface is updated to use the context that is owned
- // by the view.
- compositing_iosurface_->SetContext(compositing_iosurface_context_);
return true;
}
-bool RenderWidgetHostViewMac::CreateCompositedIOSurfaceLayer() {
- CHECK(compositing_iosurface_context_ && compositing_iosurface_);
+void RenderWidgetHostViewMac::EnsureSoftwareLayer() {
+ TRACE_EVENT0("browser", "RenderWidgetHostViewMac::EnsureSoftwareLayer");
+ if (software_layer_ || !use_core_animation_)
+ return;
+
+ software_layer_.reset([[SoftwareLayer alloc]
+ initWithRenderWidgetHostViewMac:this]);
+ DCHECK(software_layer_);
+
+ // Disable the fade-in animation as the layer is added.
+ ScopedCAActionDisabler disabler;
+ [background_layer_ addSublayer:software_layer_];
+}
+
+void RenderWidgetHostViewMac::DestroySoftwareLayer() {
+ if (!software_layer_)
+ return;
+
+ // Disable the fade-out animation as the layer is removed.
+ ScopedCAActionDisabler disabler;
+ [software_layer_ removeFromSuperlayer];
+ [software_layer_ disableRendering];
+ software_layer_.reset();
+}
+
+void RenderWidgetHostViewMac::EnsureCompositedIOSurfaceLayer() {
+ TRACE_EVENT0("browser",
+ "RenderWidgetHostViewMac::EnsureCompositedIOSurfaceLayer");
+ DCHECK(compositing_iosurface_context_);
if (compositing_iosurface_layer_ || !use_core_animation_)
- return true;
+ return;
+ compositing_iosurface_layer_.reset([[CompositingIOSurfaceLayer alloc]
+ initWithRenderWidgetHostViewMac:this]);
+ DCHECK(compositing_iosurface_layer_);
+
+ // Disable the fade-in animation as the layer is added.
ScopedCAActionDisabler disabler;
+ [background_layer_ addSublayer:compositing_iosurface_layer_];
+}
- // Create the GL CoreAnimation layer.
- if (!compositing_iosurface_layer_) {
- compositing_iosurface_layer_.reset([[CompositingIOSurfaceLayer alloc]
- initWithRenderWidgetHostViewMac:this]);
- if (!compositing_iosurface_layer_) {
- LOG(ERROR) << "Failed to create CALayer for IOSurface";
- return false;
- }
- [software_layer_ addSublayer:compositing_iosurface_layer_];
- }
+void RenderWidgetHostViewMac::DestroyCompositedIOSurfaceLayer(
+ DestroyCompositedIOSurfaceLayerBehavior destroy_layer_behavior) {
+ if (!compositing_iosurface_layer_)
+ return;
- // Creating the CompositingIOSurfaceLayer may attempt to draw in setLayer,
- // which, if it fails, will promptly tear down everything that was just
- // created. If that happened, return failure.
- return compositing_iosurface_context_ &&
- compositing_iosurface_ &&
- (compositing_iosurface_layer_ || !use_core_animation_);
+ if (destroy_layer_behavior == kRemoveLayerFromHierarchy) {
+ // Disable the fade-out animation as the layer is removed.
+ ScopedCAActionDisabler disabler;
+ [compositing_iosurface_layer_ removeFromSuperlayer];
+ }
+ [compositing_iosurface_layer_ disableCompositing];
+ compositing_iosurface_layer_.reset();
}
void RenderWidgetHostViewMac::DestroyCompositedIOSurfaceAndLayer(
DestroyContextBehavior destroy_context_behavior) {
- ScopedCAActionDisabler disabler;
+ // Any pending frames will not be displayed, so ack them now.
+ SendPendingSwapAck();
+ DestroyCompositedIOSurfaceLayer(kRemoveLayerFromHierarchy);
compositing_iosurface_.reset();
- if (compositing_iosurface_layer_) {
- [software_layer_ setNeedsDisplay];
- [compositing_iosurface_layer_ removeFromSuperlayer];
- [compositing_iosurface_layer_ disableCompositing];
- compositing_iosurface_layer_.reset();
- }
+
switch (destroy_context_behavior) {
case kLeaveContextBoundToView:
break;
}
void RenderWidgetHostViewMac::ClearBoundContextDrawable() {
+ if (use_core_animation_)
+ return;
+
if (compositing_iosurface_context_ &&
cocoa_view_ &&
[[compositing_iosurface_context_->nsgl_context() view]
isEqual:cocoa_view_]) {
+ // Disable screen updates because removing the GL context from below can
+ // cause flashes.
+ [[cocoa_view_ window] disableScreenUpdatesUntilFlush];
[compositing_iosurface_context_->nsgl_context() clearDrawable];
}
}
IPC_BEGIN_MESSAGE_MAP(RenderWidgetHostViewMac, message)
IPC_MESSAGE_HANDLER(ViewHostMsg_PluginFocusChanged, OnPluginFocusChanged)
IPC_MESSAGE_HANDLER(ViewHostMsg_StartPluginIme, OnStartPluginIme)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_DidChangeScrollbarsForMainFrame,
+ OnDidChangeScrollbarsForMainFrame)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
void RenderWidgetHostViewMac::InitAsPopup(
RenderWidgetHostView* parent_host_view,
const gfx::Rect& pos) {
- bool activatable = popup_type_ == WebKit::WebPopupTypeNone;
+ bool activatable = popup_type_ == blink::WebPopupTypeNone;
[cocoa_view_ setCloseOnDeactivate:YES];
[cocoa_view_ setCanBeKeyView:activatable ? YES : NO];
NSPoint origin_global = NSPointFromCGPoint(pos.origin().ToCGPoint());
- if ([[NSScreen screens] count] > 0) {
- origin_global.y = [[[NSScreen screens] objectAtIndex:0] frame].size.height -
- pos.height() - origin_global.y;
- }
+ origin_global.y = FlipYFromRectToScreen(origin_global.y, pos.height());
popup_window_.reset([[RenderWidgetPopupWindow alloc]
initWithContentRect:NSMakeRect(origin_global.x, origin_global.y,
return [window windowNumber];
}
-float RenderWidgetHostViewMac::scale_factor() const {
- return ScaleFactor(cocoa_view_);
+float RenderWidgetHostViewMac::ViewScaleFactor() const {
+ return ScaleFactorForView(cocoa_view_);
+}
+
+void RenderWidgetHostViewMac::UpdateDisplayLink() {
+ static bool is_vsync_disabled =
+ CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableGpuVsync);
+ if (is_vsync_disabled)
+ return;
+
+ NSScreen* screen = [[cocoa_view_ window] screen];
+ NSDictionary* screen_description = [screen deviceDescription];
+ NSNumber* screen_number = [screen_description objectForKey:@"NSScreenNumber"];
+ CGDirectDisplayID display_id = [screen_number unsignedIntValue];
+
+ display_link_ = DisplayLinkMac::GetForDisplay(display_id);
+ if (!display_link_) {
+ // Note that on some headless systems, the display link will fail to be
+ // created, so this should not be a fatal error.
+ LOG(ERROR) << "Failed to create display link.";
+ }
+}
+
+void RenderWidgetHostViewMac::SendVSyncParametersToRenderer() {
+ if (!render_widget_host_ || !display_link_)
+ return;
+
+ base::TimeTicks timebase;
+ base::TimeDelta interval;
+ if (!display_link_->GetVSyncParameters(&timebase, &interval))
+ return;
+
+ render_widget_host_->UpdateVSyncParameters(timebase, interval);
+}
+
+void RenderWidgetHostViewMac::UpdateBackingStoreScaleFactor() {
+ if (!render_widget_host_)
+ return;
+
+ float new_scale_factor = ScaleFactorForView(cocoa_view_);
+ if (new_scale_factor == backing_store_scale_factor_)
+ return;
+ backing_store_scale_factor_ = new_scale_factor;
+
+ render_widget_host_->NotifyScreenInfoChanged();
}
RenderWidgetHost* RenderWidgetHostViewMac::GetRenderWidgetHost() const {
render_widget_host_->WasShown();
software_frame_manager_->SetVisibility(true);
+ // Call setNeedsDisplay before pausing for new frames to come in -- if any
+ // do, and are drawn, then the needsDisplay bit will be cleared.
+ [software_layer_ setNeedsDisplay];
+ [compositing_iosurface_layer_ setNeedsDisplay];
+ PauseForPendingResizeOrRepaintsAndDraw();
+
// We're messing with the window, so do this to ensure no flashes.
if (!use_core_animation_)
[[cocoa_view_ window] disableScreenUpdatesUntilFlush];
-
- [compositing_iosurface_layer_ setNeedsDisplay];
}
void RenderWidgetHostViewMac::WasHidden() {
if (render_widget_host_->is_hidden())
return;
- // Send ACKs for any pending SwapBuffers (if any) since we won't be displaying
- // them and the GPU process is waiting.
- AckPendingSwapBuffers();
+ // Any pending frames will not be displayed until this is shown again. Ack
+ // them now.
+ SendPendingSwapAck();
// If we have a renderer, then inform it that we are being hidden so it can
// reduce its resource utilization.
// Ignore the position of |rect| for non-popup rwhvs. This is because
// background tabs do not have a window, but the window is required for the
// coordinate conversions. Popups are always for a visible tab.
- if (IsPopup()) {
+ //
+ // Note: If |cocoa_view_| has been removed from the view hierarchy, it's still
+ // valid for resizing to be requested (e.g., during tab capture, to size the
+ // view to screen-capture resolution). In this case, simply treat the view as
+ // relative to the screen.
+ BOOL isRelativeToScreen = IsPopup() ||
+ ![[cocoa_view_ superview] isKindOfClass:[BaseView class]];
+ if (isRelativeToScreen) {
// The position of |rect| is screen coordinate system and we have to
// consider Cocoa coordinate system is upside-down and also multi-screen.
NSPoint origin_global = NSPointFromCGPoint(rect.origin().ToCGPoint());
NSSize size = NSMakeSize(rect.width(), rect.height());
size = [cocoa_view_ convertSize:size toView:nil];
- if ([[NSScreen screens] count] > 0) {
- NSScreen* screen =
- static_cast<NSScreen*>([[NSScreen screens] objectAtIndex:0]);
- origin_global.y =
- NSHeight([screen frame]) - size.height - origin_global.y;
- }
- [popup_window_ setFrame:NSMakeRect(origin_global.x, origin_global.y,
- size.width, size.height)
- display:YES];
+ origin_global.y = FlipYFromRectToScreen(origin_global.y, size.height);
+ NSRect frame = NSMakeRect(origin_global.x, origin_global.y,
+ size.width, size.height);
+ if (IsPopup())
+ [popup_window_ setFrame:frame display:YES];
+ else
+ [cocoa_view_ setFrame:frame];
} else {
- DCHECK([[cocoa_view_ superview] isKindOfClass:[BaseView class]]);
BaseView* superview = static_cast<BaseView*>([cocoa_view_ superview]);
gfx::Rect rect2 = [superview flipNSRectToRect:[cocoa_view_ frame]];
rect2.set_width(rect.width());
}
void RenderWidgetHostViewMac::MovePluginWindows(
- const gfx::Vector2d& scroll_offset,
const std::vector<WebPluginGeometry>& moves) {
// Must be overridden, but unused on this platform. Core Animation
// plugins are drawn by the GPU process (through the compositor),
// and Core Graphics plugins are drawn by the renderer process.
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
}
void RenderWidgetHostViewMac::Focus() {
}
bool RenderWidgetHostViewMac::IsSurfaceAvailableForCopy() const {
- return !!render_widget_host_->GetBackingStore(false) ||
- (compositing_iosurface_ && compositing_iosurface_->HasIOSurface());
+ return software_frame_manager_->HasCurrentFrame() ||
+ (compositing_iosurface_ && compositing_iosurface_->HasIOSurface());
}
void RenderWidgetHostViewMac::Show() {
composition_bounds_ = character_bounds;
}
-void RenderWidgetHostViewMac::DidUpdateBackingStore(
- const gfx::Rect& scroll_rect,
- const gfx::Vector2d& scroll_delta,
- const std::vector<gfx::Rect>& copy_rects,
- const ui::LatencyInfo& latency_info) {
- GotSoftwareFrame();
-
- software_latency_info_.MergeWith(latency_info);
-
- if (render_widget_host_->is_hidden())
- return;
-
- std::vector<gfx::Rect> rects(copy_rects);
-
- // Because the findbar might be open, we cannot use scrollRect:by: here. For
- // now, simply mark all of scroll rect as dirty.
- if (!scroll_rect.IsEmpty())
- rects.push_back(scroll_rect);
-
- for (size_t i = 0; i < rects.size(); ++i) {
- NSRect ns_rect = [cocoa_view_ flipRectToNSRect:rects[i]];
-
- if (about_to_validate_and_paint_) {
- // As much as we'd like to use -setNeedsDisplayInRect: here, we can't.
- // We're in the middle of executing a -drawRect:, and as soon as it
- // returns Cocoa will clear its record of what needs display. We instead
- // use |performSelector:| to call |setNeedsDisplayInRect:| after returning
- // to the main loop, at which point |drawRect:| is no longer on the
- // stack.
- DCHECK([NSThread isMainThread]);
- if (!call_set_needs_display_in_rect_pending_) {
- [cocoa_view_ performSelector:@selector(callSetNeedsDisplayInRect)
- withObject:nil
- afterDelay:0];
- call_set_needs_display_in_rect_pending_ = true;
- invalid_rect_ = ns_rect;
- } else {
- // The old invalid rect is probably invalid now, since the view has most
- // likely been resized, but there's no harm in dirtying the union. In
- // the limit, this becomes equivalent to dirtying the whole view.
- invalid_rect_ = NSUnionRect(invalid_rect_, ns_rect);
- }
- } else {
- [cocoa_view_ setNeedsDisplayInRect:ns_rect];
- }
- }
-
- if (!about_to_validate_and_paint_)
- [cocoa_view_ displayIfNeeded];
-}
-
void RenderWidgetHostViewMac::RenderProcessGone(base::TerminationStatus status,
int error_code) {
Destroy();
}
void RenderWidgetHostViewMac::Destroy() {
- AckPendingSwapBuffers();
-
[[NSNotificationCenter defaultCenter]
removeObserver:cocoa_view_
name:NSWindowWillCloseNotification
// Called from the renderer to tell us what the tooltip text should be. It
// calls us frequently so we need to cache the value to prevent doing a lot
// of repeat work.
-void RenderWidgetHostViewMac::SetTooltipText(const string16& tooltip_text) {
+void RenderWidgetHostViewMac::SetTooltipText(
+ const base::string16& tooltip_text) {
if (tooltip_text != tooltip_text_ && [[cocoa_view_ window] isKeyWindow]) {
tooltip_text_ = tooltip_text;
// Windows; we're just trying to be polite. Don't persist the trimmed
// string, as then the comparison above will always fail and we'll try to
// set it again every single time the mouse moves.
- string16 display_text = tooltip_text_;
+ base::string16 display_text = tooltip_text_;
if (tooltip_text_.length() > kMaxTooltipLength)
display_text = tooltip_text_.substr(0, kMaxTooltipLength);
// RenderWidgetHostViewCocoa uses the stored selection text,
// which implements NSServicesRequests protocol.
//
-void RenderWidgetHostViewMac::SelectionChanged(const string16& text,
+void RenderWidgetHostViewMac::SelectionChanged(const base::string16& text,
size_t offset,
const gfx::Range& range) {
if (range.is_empty() || text.empty()) {
DCHECK(false) << "The text can not cover range.";
return;
}
- selected_text_ = UTF16ToUTF8(text.substr(pos, n));
+ selected_text_ = base::UTF16ToUTF8(text.substr(pos, n));
}
[cocoa_view_ setSelectedRange:range.ToNSRange()];
}
bool RenderWidgetHostViewMac::IsPopup() const {
- return popup_type_ != WebKit::WebPopupTypeNone;
-}
-
-BackingStore* RenderWidgetHostViewMac::AllocBackingStore(
- const gfx::Size& size) {
- float scale = ScaleFactor(cocoa_view_);
- return new BackingStoreMac(render_widget_host_, size, scale);
+ return popup_type_ != blink::WebPopupTypeNone;
}
void RenderWidgetHostViewMac::CopyFromCompositingSurface(
const gfx::Rect& src_subrect,
const gfx::Size& dst_size,
- const base::Callback<void(bool, const SkBitmap&)>& callback) {
+ const base::Callback<void(bool, const SkBitmap&)>& callback,
+ const SkBitmap::Config config) {
+ if (config != SkBitmap::kARGB_8888_Config) {
+ NOTIMPLEMENTED();
+ callback.Run(false, SkBitmap());
+ }
base::ScopedClosureRunner scoped_callback_runner(
base::Bind(callback, false, SkBitmap()));
- if (!compositing_iosurface_ ||
- !compositing_iosurface_->HasIOSurface())
- return;
-
- float scale = ScaleFactor(cocoa_view_);
+ float scale = ScaleFactorForView(cocoa_view_);
gfx::Size dst_pixel_size = gfx::ToFlooredSize(
gfx::ScaleSize(dst_size, scale));
+ if (compositing_iosurface_ && compositing_iosurface_->HasIOSurface()) {
+ ignore_result(scoped_callback_runner.Release());
+ compositing_iosurface_->CopyTo(GetScaledOpenGLPixelRect(src_subrect),
+ dst_pixel_size,
+ callback);
+ } else if (software_frame_manager_->HasCurrentFrame()) {
+ gfx::Rect src_pixel_rect = gfx::ToEnclosingRect(gfx::ScaleRect(
+ src_subrect,
+ software_frame_manager_->GetCurrentFrameDeviceScaleFactor()));
+ SkBitmap source_bitmap;
+ source_bitmap.setConfig(
+ SkBitmap::kARGB_8888_Config,
+ software_frame_manager_->GetCurrentFrameSizeInPixels().width(),
+ software_frame_manager_->GetCurrentFrameSizeInPixels().height(),
+ 0,
+ kOpaque_SkAlphaType);
+ source_bitmap.setPixels(software_frame_manager_->GetCurrentFramePixels());
+
+ SkBitmap target_bitmap;
+ target_bitmap.setConfig(
+ SkBitmap::kARGB_8888_Config,
+ dst_pixel_size.width(),
+ dst_pixel_size.height(),
+ 0,
+ kOpaque_SkAlphaType);
+ if (!target_bitmap.allocPixels())
+ return;
- ignore_result(scoped_callback_runner.Release());
+ SkCanvas target_canvas(target_bitmap);
+ SkRect src_pixel_skrect = SkRect::MakeXYWH(
+ src_pixel_rect.x(), src_pixel_rect.y(),
+ src_pixel_rect.width(), src_pixel_rect.height());
+ target_canvas.drawBitmapRectToRect(
+ source_bitmap,
+ &src_pixel_skrect,
+ SkRect::MakeXYWH(0, 0, dst_pixel_size.width(), dst_pixel_size.height()),
+ NULL,
+ SkCanvas::kNone_DrawBitmapRectFlag);
- compositing_iosurface_->CopyTo(GetScaledOpenGLPixelRect(src_subrect),
- dst_pixel_size,
- callback);
+ ignore_result(scoped_callback_runner.Release());
+ callback.Run(true, target_bitmap);
+ }
}
void RenderWidgetHostViewMac::CopyFromCompositingSurfaceToVideoFrame(
}
bool RenderWidgetHostViewMac::CanCopyToVideoFrame() const {
- return (!render_widget_host_->GetBackingStore(false) &&
+ return (!software_frame_manager_->HasCurrentFrame() &&
render_widget_host_->is_accelerated_compositing_active() &&
compositing_iosurface_ &&
compositing_iosurface_->HasIOSurface());
}
bool RenderWidgetHostViewMac::CanSubscribeFrame() const {
- return true;
+ return !software_frame_manager_->HasCurrentFrame();
}
void RenderWidgetHostViewMac::BeginFrameSubscription(
}
void RenderWidgetHostViewMac::PluginImeCompositionCompleted(
- const string16& text, int plugin_id) {
+ const base::string16& text, int plugin_id) {
if (render_widget_host_) {
render_widget_host_->Send(new ViewMsg_PluginImeCompositionCompleted(
render_widget_host_->GetRoutingID(), text, plugin_id));
uint64 surface_handle,
const gfx::Size& size,
float surface_scale_factor,
- const ui::LatencyInfo& latency_info) {
+ const std::vector<ui::LatencyInfo>& latency_info) {
+ // Ensure that the frame be acked unless it is explicitly passed to a
+ // display function.
+ base::ScopedClosureRunner scoped_ack(
+ base::Bind(&RenderWidgetHostViewMac::SendPendingSwapAck,
+ weak_factory_.GetWeakPtr()));
+
if (render_widget_host_->is_hidden())
return;
- NSWindow* window = [cocoa_view_ window];
- if (window_number() <= 0) {
- // There is no window to present so capturing during present won't work.
- // We check if frame subscriber wants this frame and capture manually.
- if (compositing_iosurface_ && frame_subscriber_) {
- const base::Time present_time = base::Time::Now();
- scoped_refptr<media::VideoFrame> frame;
- RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback callback;
- if (frame_subscriber_->ShouldCaptureFrame(present_time,
- &frame, &callback)) {
- compositing_iosurface_->SetIOSurface(
- surface_handle, size, surface_scale_factor, latency_info);
- compositing_iosurface_->CopyToVideoFrame(
- gfx::Rect(size), frame,
- base::Bind(callback, present_time));
- return;
- }
- }
+ // Ensure that if this function exits before the frame is set up (but not
+ // necessarily drawn) then it is treated as an error.
+ base::ScopedClosureRunner scoped_error(
+ base::Bind(&RenderWidgetHostViewMac::GotAcceleratedCompositingError,
+ weak_factory_.GetWeakPtr()));
- // TODO(shess) If the view does not have a window, or the window
- // does not have backing, the IOSurface will log "invalid drawable"
- // in -setView:. It is not clear how this code is reached with such
- // a case, so record some info into breakpad (some subset of
- // browsers are likely to crash later for unrelated reasons).
- // http://crbug.com/148882
- const char* const kCrashKey = "rwhvm_window";
- if (!window) {
- base::debug::SetCrashKeyValue(kCrashKey, "Missing window");
- } else {
- std::string value =
- base::StringPrintf("window %s delegate %s controller %s",
- object_getClassName(window),
- object_getClassName([window delegate]),
- object_getClassName([window windowController]));
- base::debug::SetCrashKeyValue(kCrashKey, value);
- }
+ AddPendingLatencyInfo(latency_info);
- return;
+ // If compositing_iosurface_ exists and has been poisoned, destroy it
+ // and allow EnsureCompositedIOSurface to recreate it below. Keep a
+ // reference to the destroyed layer around until after the below call
+ // to LayoutLayers, to avoid flickers.
+ base::ScopedClosureRunner scoped_layer_remover;
+ if (compositing_iosurface_context_ &&
+ compositing_iosurface_context_->HasBeenPoisoned()) {
+ scoped_layer_remover.Reset(
+ base::Bind(RemoveLayerFromSuperlayer, compositing_iosurface_layer_));
+ DestroyCompositedIOSurfaceLayer(kLeaveLayerInHierarchy);
+ DestroyCompositedIOSurfaceAndLayer(kDestroyContext);
}
- if (!CreateCompositedIOSurface()) {
- LOG(ERROR) << "Failed to create CompositingIOSurface";
- GotAcceleratedCompositingError();
+ // Ensure compositing_iosurface_ and compositing_iosurface_context_ be
+ // allocated.
+ if (!EnsureCompositedIOSurface()) {
+ LOG(ERROR) << "Failed EnsureCompositingIOSurface";
return;
}
- if (!compositing_iosurface_->SetIOSurface(
- surface_handle, size, surface_scale_factor, latency_info)) {
- LOG(ERROR) << "Failed SetIOSurface on CompositingIOSurfaceMac";
- GotAcceleratedCompositingError();
- return;
+ // Make the context current and update the IOSurface with the handle
+ // passed in by the swap command.
+ {
+ gfx::ScopedCGLSetCurrentContext scoped_set_current_context(
+ compositing_iosurface_context_->cgl_context());
+ if (!compositing_iosurface_->SetIOSurfaceWithContextCurrent(
+ compositing_iosurface_context_, surface_handle, size,
+ surface_scale_factor)) {
+ LOG(ERROR) << "Failed SetIOSurface on CompositingIOSurfaceMac";
+ return;
+ }
}
- // Create the layer for the composited content only after the IOSurface has
- // been initialized.
- if (!CreateCompositedIOSurfaceLayer()) {
- LOG(ERROR) << "Failed to create CompositingIOSurface layer";
- GotAcceleratedCompositingError();
- return;
+ // Grab video frames now that the IOSurface has been set up. Note that this
+ // will be done in an offscreen context, so it is necessary to re-set the
+ // current context afterward.
+ bool frame_was_captured = false;
+ if (frame_subscriber_) {
+ const base::TimeTicks present_time = base::TimeTicks::Now();
+ scoped_refptr<media::VideoFrame> frame;
+ RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback callback;
+ if (frame_subscriber_->ShouldCaptureFrame(present_time,
+ &frame, &callback)) {
+ // Flush the context that updated the IOSurface, to ensure that the
+ // context that does the copy picks up the correct version.
+ {
+ gfx::ScopedCGLSetCurrentContext scoped_set_current_context(
+ compositing_iosurface_context_->cgl_context());
+ glFlush();
+ }
+ compositing_iosurface_->CopyToVideoFrame(
+ gfx::Rect(size), frame,
+ base::Bind(callback, present_time));
+ frame_was_captured = true;
+ }
}
+ // At this point the surface, its context, and its layer have been set up, so
+ // don't generate an error (one may be generated when drawing).
+ ignore_result(scoped_error.Release());
+
GotAcceleratedFrame();
gfx::Size window_size(NSSizeToCGSize([cocoa_view_ frame].size));
// empty, so ack now and don't bother calling setNeedsDisplay below.
return;
}
+ if (window_number() <= 0) {
+ // It's normal for a backgrounded tab that is being captured to have no
+ // window but not be hidden. Immediately ack the frame, and don't try to
+ // draw it.
+ if (frame_was_captured)
+ return;
- // No need to draw the surface if we are inside a drawRect. It will be done
- // later.
- if (!about_to_validate_and_paint_) {
- if (use_core_animation_) {
- DCHECK(compositing_iosurface_layer_);
- [compositing_iosurface_layer_ setNeedsDisplay];
+ // If this frame was not captured, there is likely some sort of bug. Ack
+ // the frame and hope for the best. Because the IOSurface and layer are
+ // populated, it will likely be displayed when the view is added to a
+ // window's hierarchy.
+
+ // TODO(shess) If the view does not have a window, or the window
+ // does not have backing, the IOSurface will log "invalid drawable"
+ // in -setView:. It is not clear how this code is reached with such
+ // a case, so record some info into breakpad (some subset of
+ // browsers are likely to crash later for unrelated reasons).
+ // http://crbug.com/148882
+ const char* const kCrashKey = "rwhvm_window";
+ NSWindow* window = [cocoa_view_ window];
+ if (!window) {
+ base::debug::SetCrashKeyValue(kCrashKey, "Missing window");
} else {
- if (!DrawIOSurfaceWithoutCoreAnimation()) {
- [cocoa_view_ setNeedsDisplay:YES];
- GotAcceleratedCompositingError();
- return;
- }
+ std::string value =
+ base::StringPrintf("window %s delegate %s controller %s",
+ object_getClassName(window),
+ object_getClassName([window delegate]),
+ object_getClassName([window windowController]));
+ base::debug::SetCrashKeyValue(kCrashKey, value);
}
+ return;
}
-}
-void RenderWidgetHostViewMac::AckPendingSwapBuffers() {
- TRACE_EVENT0("browser", "RenderWidgetHostViewMac::AckPendingSwapBuffers");
-
- // Cancel any outstanding delayed calls to this function.
- pending_swap_buffers_acks_weak_factory_.InvalidateWeakPtrs();
-
- while (!pending_swap_buffers_acks_.empty()) {
- if (pending_swap_buffers_acks_.front().first != 0) {
- AcceleratedSurfaceMsg_BufferPresented_Params ack_params;
- ack_params.sync_point = 0;
- if (compositing_iosurface_)
- ack_params.renderer_id = compositing_iosurface_->GetRendererID();
- RenderWidgetHostImpl::AcknowledgeBufferPresent(
- pending_swap_buffers_acks_.front().first,
- pending_swap_buffers_acks_.front().second,
- ack_params);
- if (render_widget_host_) {
- render_widget_host_->AcknowledgeSwapBuffersToRenderer();
- }
- }
- pending_swap_buffers_acks_.erase(pending_swap_buffers_acks_.begin());
- }
-}
-
-void RenderWidgetHostViewMac::ThrottledAckPendingSwapBuffers() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
- // Send VSync parameters to the renderer's compositor thread.
- base::TimeTicks vsync_timebase;
- base::TimeDelta vsync_interval;
- GetVSyncParameters(&vsync_timebase, &vsync_interval);
- if (render_widget_host_ && compositing_iosurface_)
- render_widget_host_->UpdateVSyncParameters(vsync_timebase, vsync_interval);
-
- // If the render widget host is responsible for throttling swaps to vsync rate
- // then don't ack the swapbuffers until a full vsync has passed since the last
- // ack was sent.
- bool throttle_swap_ack =
- render_widget_host_ &&
- !render_widget_host_->is_threaded_compositing_enabled() &&
- compositing_iosurface_ &&
- !compositing_iosurface_->is_vsync_disabled();
- base::Time now = base::Time::Now();
- if (throttle_swap_ack && next_swap_ack_time_ > now) {
- base::TimeDelta next_swap_ack_delay = next_swap_ack_time_ - now;
- next_swap_ack_time_ += vsync_interval;
- base::MessageLoop::current()->PostDelayedTask(
- FROM_HERE,
- base::Bind(&RenderWidgetHostViewMac::AckPendingSwapBuffers,
- pending_swap_buffers_acks_weak_factory_.GetWeakPtr()),
- next_swap_ack_delay);
+ // If we reach here, then the frame will be displayed by a future draw
+ // call, so don't make the callback.
+ ignore_result(scoped_ack.Release());
+ if (use_core_animation_) {
+ DCHECK(compositing_iosurface_layer_);
+ compositing_iosurface_layer_async_timer_.Reset();
+ [compositing_iosurface_layer_ gotNewFrame];
} else {
- next_swap_ack_time_ = now + vsync_interval;
- AckPendingSwapBuffers();
+ gfx::ScopedCGLSetCurrentContext scoped_set_current_context(
+ compositing_iosurface_context_->cgl_context());
+ DrawIOSurfaceWithoutCoreAnimation();
}
+
+ // Try to finish previous copy requests after draw to get better pipelining.
+ if (compositing_iosurface_)
+ compositing_iosurface_->CheckIfAllCopiesAreFinished(false);
+
+ // The IOSurface's size may have changed, so re-layout the layers to take
+ // this into account. This may force an immediate draw.
+ LayoutLayers();
}
-bool RenderWidgetHostViewMac::DrawIOSurfaceWithoutCoreAnimation() {
+void RenderWidgetHostViewMac::DrawIOSurfaceWithoutCoreAnimation() {
CHECK(!use_core_animation_);
CHECK(compositing_iosurface_);
- CHECK(compositing_iosurface_context_ == compositing_iosurface_->context());
+
+ // If there is a pending frame, it should be acked by the end of this
+ // function. Note that the ack should happen only after all drawing is
+ // complete, so that the ack happens after any blocking due to vsync.
+ base::ScopedClosureRunner scoped_ack(
+ base::Bind(&RenderWidgetHostViewMac::SendPendingSwapAck,
+ weak_factory_.GetWeakPtr()));
GLint old_gl_surface_order = 0;
GLint new_gl_surface_order = allow_overlapping_views_ ? -1 : 1;
forParameter:NSOpenGLCPSurfaceOrder];
}
- CGLError cgl_error = CGLSetCurrentContext(
- compositing_iosurface_context_->cgl_context());
- if (cgl_error != kCGLNoError) {
- LOG(ERROR) << "CGLSetCurrentContext error in DrawIOSurface: " << cgl_error;
- return false;
+ // Instead of drawing, request that underlay view redraws.
+ if (underlay_view_ &&
+ underlay_view_->compositing_iosurface_ &&
+ underlay_view_has_drawn_) {
+ [underlay_view_->cocoa_view() setNeedsDisplayInRect:NSMakeRect(0, 0, 1, 1)];
+ return;
}
+ bool has_overlay = overlay_view_ && overlay_view_->compositing_iosurface_;
+ if (has_overlay) {
+ // Un-bind the overlay view's OpenGL context, since its content will be
+ // drawn by this context. Not doing this can result in corruption.
+ // http://crbug.com/330701
+ overlay_view_->ClearBoundContextDrawable();
+ }
[compositing_iosurface_context_->nsgl_context() setView:cocoa_view_];
- return compositing_iosurface_->DrawIOSurface(
- gfx::Size(NSSizeToCGSize([cocoa_view_ frame].size)),
- scale_factor(),
- frame_subscriber(),
- false);
+
+ gfx::Rect view_rect(NSRectToCGRect([cocoa_view_ frame]));
+ if (!compositing_iosurface_->DrawIOSurface(
+ compositing_iosurface_context_, view_rect,
+ ViewScaleFactor(), !has_overlay)) {
+ GotAcceleratedCompositingError();
+ return;
+ }
+
+ if (has_overlay) {
+ overlay_view_->underlay_view_has_drawn_ = true;
+ gfx::Rect overlay_view_rect(
+ NSRectToCGRect([overlay_view_->cocoa_view() frame]));
+ overlay_view_rect.set_x(overlay_view_offset_.x());
+ overlay_view_rect.set_y(view_rect.height() -
+ overlay_view_rect.height() -
+ overlay_view_offset_.y());
+ if (!overlay_view_->compositing_iosurface_->DrawIOSurface(
+ compositing_iosurface_context_, overlay_view_rect,
+ overlay_view_->ViewScaleFactor(), true)) {
+ GotAcceleratedCompositingError();
+ return;
+ }
+ }
+
+ SendPendingLatencyInfoToHost();
}
void RenderWidgetHostViewMac::GotAcceleratedCompositingError() {
- AckPendingSwapBuffers();
- DestroyCompositedIOSurfaceAndLayer(kDestroyContext);
+ LOG(ERROR) << "Encountered accelerated compositing error";
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&RenderWidgetHostViewMac::DestroyCompositingStateOnError,
+ weak_factory_.GetWeakPtr()));
+}
+
+void RenderWidgetHostViewMac::DestroyCompositingStateOnError() {
+ // This should be called with a clean stack. Make sure that no context is
+ // current.
+ DCHECK(!CGLGetCurrentContext());
+
// The existing GL contexts may be in a bad state, so don't re-use any of the
// existing ones anymore, rather, allocate new ones.
- CompositingIOSurfaceContext::MarkExistingContextsAsNotShareable();
- // Request that a new frame be generated.
+ if (compositing_iosurface_context_)
+ compositing_iosurface_context_->PoisonContextAndSharegroup();
+
+ DestroyCompositedIOSurfaceAndLayer(kDestroyContext);
+
+ // Request that a new frame be generated and dirty the view.
if (render_widget_host_)
render_widget_host_->ScheduleComposite();
+ [cocoa_view_ setNeedsDisplay:YES];
+
+ // Mark the last frame as not accelerated (so that the window is prepared for
+ // an underlay next time an accelerated frame comes in).
+ last_frame_was_accelerated_ = false;
+
// TODO(ccameron): It may be a good idea to request that the renderer recreate
// its GL context as well, and fall back to software if this happens
// repeatedly.
}
-void RenderWidgetHostViewMac::GetVSyncParameters(
- base::TimeTicks* timebase, base::TimeDelta* interval) {
- if (compositing_iosurface_) {
- uint32 numerator = 0;
- uint32 denominator = 0;
- compositing_iosurface_->GetVSyncParameters(
- timebase, &numerator, &denominator);
- if (numerator > 0 && denominator > 0) {
- int64 interval_micros =
- 1000000 * static_cast<int64>(numerator) / denominator;
- *interval = base::TimeDelta::FromMicroseconds(interval_micros);
- return;
- }
+void RenderWidgetHostViewMac::SetOverlayView(
+ RenderWidgetHostViewMac* overlay, const gfx::Point& offset) {
+ if (overlay_view_)
+ overlay_view_->underlay_view_.reset();
+
+ overlay_view_ = overlay->overlay_view_weak_factory_.GetWeakPtr();
+ overlay_view_->underlay_view_ = overlay_view_weak_factory_.GetWeakPtr();
+ if (use_core_animation_)
+ return;
+
+ overlay_view_offset_ = offset;
+ overlay_view_->underlay_view_has_drawn_ = false;
+
+ [cocoa_view_ setNeedsDisplay:YES];
+ [[cocoa_view_ window] disableScreenUpdatesUntilFlush];
+}
+
+void RenderWidgetHostViewMac::RemoveOverlayView() {
+ if (overlay_view_) {
+ overlay_view_->underlay_view_.reset();
+ overlay_view_.reset();
}
+ if (use_core_animation_)
+ return;
- // Pass reasonable default values if unable to get the actual ones
- // (e.g. CVDisplayLink failed to return them because the display is
- // in sleep mode).
- static const int64 kOneOverSixtyMicroseconds = 16669;
- *timebase = base::TimeTicks::Now(),
- *interval = base::TimeDelta::FromMicroseconds(kOneOverSixtyMicroseconds);
+ [cocoa_view_ setNeedsDisplay:YES];
+ [[cocoa_view_ window] disableScreenUpdatesUntilFlush];
}
bool RenderWidgetHostViewMac::GetLineBreakIndex(
request_range.end() - composition_range_.start());
}
+WebContents* RenderWidgetHostViewMac::GetWebContents() {
+ if (!render_widget_host_->IsRenderView())
+ return NULL;
+
+ return WebContents::FromRenderViewHost(
+ RenderViewHost::From(render_widget_host_));
+}
+
bool RenderWidgetHostViewMac::GetCachedFirstRectForCharacterRange(
NSRange range,
NSRect* rect,
int gpu_host_id) {
TRACE_EVENT0("browser",
"RenderWidgetHostViewMac::AcceleratedSurfaceBuffersSwapped");
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
- pending_swap_buffers_acks_.push_back(std::make_pair(params.route_id,
- gpu_host_id));
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ AddPendingSwapAck(params.route_id,
+ gpu_host_id,
+ compositing_iosurface_ ?
+ compositing_iosurface_->GetRendererID() : 0);
CompositorSwapBuffers(params.surface_handle,
params.size,
params.scale_factor,
params.latency_info);
-
- ThrottledAckPendingSwapBuffers();
}
void RenderWidgetHostViewMac::AcceleratedSurfacePostSubBuffer(
int gpu_host_id) {
TRACE_EVENT0("browser",
"RenderWidgetHostViewMac::AcceleratedSurfacePostSubBuffer");
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
- pending_swap_buffers_acks_.push_back(std::make_pair(params.route_id,
- gpu_host_id));
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ AddPendingSwapAck(params.route_id,
+ gpu_host_id,
+ compositing_iosurface_ ?
+ compositing_iosurface_->GetRendererID() : 0);
CompositorSwapBuffers(params.surface_handle,
params.surface_size,
params.surface_scale_factor,
params.latency_info);
-
- ThrottledAckPendingSwapBuffers();
}
void RenderWidgetHostViewMac::AcceleratedSurfaceSuspend() {
return false;
}
-void RenderWidgetHostViewMac::AboutToWaitForBackingStoreMsg() {
- AckPendingSwapBuffers();
-}
-
void RenderWidgetHostViewMac::OnSwapCompositorFrame(
uint32 output_surface_id, scoped_ptr<cc::CompositorFrame> frame) {
// Only software compositor frames are accepted.
if (!frame->software_frame_data) {
DLOG(ERROR) << "Received unexpected frame type.";
RecordAction(
- UserMetricsAction("BadMessageTerminate_UnexpectedFrameType"));
+ base::UserMetricsAction("BadMessageTerminate_UnexpectedFrameType"));
render_widget_host_->GetProcess()->ReceivedBadMessage();
return;
}
- GotSoftwareFrame();
if (!software_frame_manager_->SwapToNewFrame(
output_surface_id,
frame->software_frame_data.get(),
render_widget_host_->GetProcess()->ReceivedBadMessage();
return;
}
+
+ // Add latency info to report when the frame finishes drawing.
+ AddPendingLatencyInfo(frame->metadata.latency_info);
+ GotSoftwareFrame();
+
+ cc::CompositorFrameAck ack;
+ RenderWidgetHostImpl::SendSwapCompositorFrameAck(
+ render_widget_host_->GetRoutingID(),
+ software_frame_manager_->GetCurrentFrameOutputSurfaceId(),
+ render_widget_host_->GetProcess()->GetID(),
+ ack);
software_frame_manager_->SwapToNewFrameComplete(
!render_widget_host_->is_hidden());
- [cocoa_view_ setNeedsDisplay:YES];
+ // Notify observers, tab capture observers in particular, that a new software
+ // frame has come in.
+ NotificationService::current()->Notify(
+ NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE,
+ Source<RenderWidgetHost>(render_widget_host_),
+ NotificationService::NoDetails());
}
void RenderWidgetHostViewMac::OnAcceleratedCompositingStateChange() {
}
-void RenderWidgetHostViewMac::GetScreenInfo(WebKit::WebScreenInfo* results) {
+void RenderWidgetHostViewMac::AcceleratedSurfaceInitialized(int host_id,
+ int route_id) {
+}
+
+void RenderWidgetHostViewMac::GetScreenInfo(blink::WebScreenInfo* results) {
*results = GetWebScreenInfo(GetNativeView());
}
return gfx::GLSurfaceHandle(gfx::kNullPluginWindow, gfx::NATIVE_TRANSPORT);
}
-void RenderWidgetHostViewMac::SetHasHorizontalScrollbar(
- bool has_horizontal_scrollbar) {
- [cocoa_view_ setHasHorizontalScrollbar:has_horizontal_scrollbar];
-}
-
void RenderWidgetHostViewMac::SetScrollOffsetPinning(
bool is_pinned_to_left, bool is_pinned_to_right) {
[cocoa_view_ scrollOffsetPinnedToLeft:is_pinned_to_left
[NSCursor hide];
// Clear the tooltip window.
- SetTooltipText(string16());
+ SetTooltipText(base::string16());
return true;
}
}
void RenderWidgetHostViewMac::UnhandledWheelEvent(
- const WebKit::WebMouseWheelEvent& event) {
+ const blink::WebMouseWheelEvent& event) {
// Only record a wheel event as unhandled if JavaScript handlers got a chance
// to see it (no-op wheel events are ignored by the event dispatcher)
if (event.deltaX || event.deltaY)
void RenderWidgetHostViewMac::SoftwareFrameWasFreed(
uint32 output_surface_id, unsigned frame_id) {
+ if (!render_widget_host_)
+ return;
cc::CompositorFrameAck ack;
ack.last_software_frame_id = frame_id;
RenderWidgetHostImpl::SendReclaimCompositorResources(
}
void RenderWidgetHostViewMac::ReleaseReferencesToSoftwareFrame() {
+ DestroySoftwareLayer();
}
void RenderWidgetHostViewMac::ShutdownHost() {
}
void RenderWidgetHostViewMac::GotAcceleratedFrame() {
- // Update the scale factor of the layer to match the scale factor of the
- // IOSurface.
- [compositing_iosurface_layer_ updateScaleFactor];
-
+ EnsureCompositedIOSurfaceLayer();
+ SendVSyncParametersToRenderer();
if (!last_frame_was_accelerated_) {
last_frame_was_accelerated_ = true;
[cocoa_view_ setNeedsDisplay:YES];
}
- // Delete software backingstore.
- BackingStoreManager::RemoveBackingStore(render_widget_host_);
+ // Delete software backingstore and layer.
software_frame_manager_->DiscardCurrentFrame();
+ DestroySoftwareLayer();
}
}
void RenderWidgetHostViewMac::GotSoftwareFrame() {
+ if (!render_widget_host_)
+ return;
+
+ EnsureSoftwareLayer();
+ LayoutLayers();
+ SendVSyncParametersToRenderer();
+
+ // Draw the contents of the frame immediately. It is critical that this
+ // happen before the frame be acked, otherwise the new frame will likely be
+ // ready before the drawing is complete, thrashing the browser main thread.
+ if (use_core_animation_) {
+ [software_layer_ setNeedsDisplay];
+ [software_layer_ displayIfNeeded];
+ } else {
+ [cocoa_view_ setNeedsDisplay:YES];
+ [cocoa_view_ displayIfNeeded];
+ }
+
if (last_frame_was_accelerated_) {
last_frame_was_accelerated_ = false;
- AckPendingSwapBuffers();
-
// If overlapping views are allowed, then don't unbind the context
// from the view (that is, don't call clearDrawble -- just delete the
// texture and IOSurface). Rather, let it sit behind the software frame
}
}
+void RenderWidgetHostViewMac::TimerSinceGotAcceleratedFrameFired() {
+ [compositing_iosurface_layer_ timerSinceGotNewFrameFired];
+}
+
void RenderWidgetHostViewMac::SetActive(bool active) {
if (render_widget_host_) {
render_widget_host_->SetActive(active);
GetViewBounds()));
}
- if (compositing_iosurface_ && use_core_animation_) {
- scoped_refptr<CompositingIOSurfaceContext> new_context =
- CompositingIOSurfaceContext::Get(window_number());
- if (new_context) {
- // Un-bind the GL context from this view before binding the new GL
- // context. Having two GL contexts bound to a view will result in
- // crashes and corruption.
- // http://crbug.com/230883
- ClearBoundContextDrawable();
- compositing_iosurface_context_ = new_context;
- compositing_iosurface_->SetContext(compositing_iosurface_context_);
- }
+ if (compositing_iosurface_ && !use_core_animation_) {
+ // This will migrate the context to the appropriate window.
+ if (!EnsureCompositedIOSurface())
+ GotAcceleratedCompositingError();
}
}
render_widget_host_->GetRoutingID(), background));
}
-void RenderWidgetHostViewMac::OnAccessibilityEvents(
- const std::vector<AccessibilityHostMsg_EventParams>& params) {
+void RenderWidgetHostViewMac::CreateBrowserAccessibilityManagerIfNeeded() {
if (!GetBrowserAccessibilityManager()) {
SetBrowserAccessibilityManager(
new BrowserAccessibilityManagerMac(
cocoa_view_,
BrowserAccessibilityManagerMac::GetEmptyDocument(),
- NULL));
+ render_widget_host_));
}
- GetBrowserAccessibilityManager()->OnAccessibilityEvents(params);
}
+gfx::Point RenderWidgetHostViewMac::AccessibilityOriginInScreen(
+ const gfx::Rect& bounds) {
+ NSPoint origin = NSMakePoint(bounds.x(), bounds.y());
+ NSSize size = NSMakeSize(bounds.width(), bounds.height());
+ origin.y = NSHeight([cocoa_view_ bounds]) - origin.y;
+ NSPoint originInWindow = [cocoa_view_ convertPoint:origin toView:nil];
+ NSPoint originInScreen =
+ [[cocoa_view_ window] convertBaseToScreen:originInWindow];
+ originInScreen.y = originInScreen.y - size.height;
+ return gfx::Point(originInScreen.x, originInScreen.y);
+}
+
+void RenderWidgetHostViewMac::OnAccessibilitySetFocus(int accObjId) {
+ // Immediately set the focused item even though we have not officially set
+ // focus on it as VoiceOver expects to get the focused item after this
+ // method returns.
+ BrowserAccessibilityManager* manager = GetBrowserAccessibilityManager();
+ if (manager)
+ manager->SetFocus(manager->GetFromID(accObjId), false);
+}
+
+void RenderWidgetHostViewMac::AccessibilityShowMenu(int accObjId) {
+ BrowserAccessibilityManager* manager = GetBrowserAccessibilityManager();
+ if (!manager)
+ return;
+ BrowserAccessibilityCocoa* obj =
+ manager->GetFromID(accObjId)->ToBrowserAccessibilityCocoa();
+
+ // Performs a right click copying WebKit's
+ // accessibilityPerformShowMenuAction.
+ NSPoint objOrigin = [obj origin];
+ NSSize size = [[obj size] sizeValue];
+ gfx::Point origin = AccessibilityOriginInScreen(
+ gfx::Rect(objOrigin.x, objOrigin.y, size.width, size.height));
+ NSPoint location = NSMakePoint(origin.x(), origin.y());
+ location = [[cocoa_view_ window] convertScreenToBase:location];
+ location.x += size.width/2;
+ location.y += size.height/2;
+
+ NSEvent* fakeRightClick = [NSEvent
+ mouseEventWithType:NSRightMouseDown
+ location:location
+ modifierFlags:0
+ timestamp:0
+ windowNumber:[[cocoa_view_ window] windowNumber]
+ context:[NSGraphicsContext currentContext]
+ eventNumber:0
+ clickCount:1
+ pressure:0];
+
+ [cocoa_view_ mouseEvent:fakeRightClick];
+}
+
+
+
void RenderWidgetHostViewMac::SetTextInputActive(bool active) {
if (active) {
if (text_input_type_ == ui::TEXT_INPUT_TYPE_PASSWORD)
[cocoa_view_ setPluginImeActive:YES];
}
+void RenderWidgetHostViewMac::OnDidChangeScrollbarsForMainFrame(
+ bool has_horizontal_scrollbar, bool has_vertical_scrollbar) {
+ [cocoa_view_ setHasHorizontalScrollbar:has_horizontal_scrollbar];
+}
+
gfx::Rect RenderWidgetHostViewMac::GetScaledOpenGLPixelRect(
const gfx::Rect& rect) {
gfx::Rect src_gl_subrect = rect;
src_gl_subrect.set_y(GetViewBounds().height() - rect.bottom());
return gfx::ToEnclosingRect(gfx::ScaleRect(src_gl_subrect,
- scale_factor()));
+ ViewScaleFactor()));
+}
+
+void RenderWidgetHostViewMac::AddPendingLatencyInfo(
+ const std::vector<ui::LatencyInfo>& latency_info) {
+ // If a screenshot is being taken when using CoreAnimation, send a few extra
+ // calls to setNeedsDisplay and wait for their resulting display calls,
+ // before reporting that the frame has reached the screen.
+ if (use_core_animation_) {
+ bool should_defer = false;
+ for (size_t i = 0; i < latency_info.size(); i++) {
+ if (latency_info[i].FindLatency(
+ ui::WINDOW_SNAPSHOT_FRAME_NUMBER_COMPONENT,
+ render_widget_host_->GetLatencyComponentId(),
+ NULL)) {
+ should_defer = true;
+ }
+ }
+ if (should_defer) {
+ // Multiple pending screenshot requests will work, but if every frame
+ // requests a screenshot, then the delay will never expire. Assert this
+ // here to avoid this.
+ CHECK_EQ(pending_latency_info_delay_, 0u);
+ // Wait a fixed number of frames (calls to CALayer::display) before
+ // claiming that the screenshot has reached the screen. This number
+ // comes from taking the first number where tests didn't fail (six),
+ // and doubling it.
+ const uint32 kScreenshotLatencyDelayInFrames = 12;
+ pending_latency_info_delay_ = kScreenshotLatencyDelayInFrames;
+ TickPendingLatencyInfoDelay();
+ }
+ }
+
+ for (size_t i = 0; i < latency_info.size(); i++) {
+ pending_latency_info_.push_back(latency_info[i]);
+ }
+}
+
+void RenderWidgetHostViewMac::SendPendingLatencyInfoToHost() {
+ if (pending_latency_info_delay_) {
+ pending_latency_info_delay_ -= 1;
+ return;
+ }
+ pending_latency_info_delay_weak_ptr_factory_.InvalidateWeakPtrs();
+
+ for (size_t i = 0; i < pending_latency_info_.size(); i++) {
+ pending_latency_info_[i].AddLatencyNumber(
+ ui::INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT, 0, 0);
+ render_widget_host_->FrameSwapped(pending_latency_info_[i]);
+ }
+ pending_latency_info_.clear();
+}
+
+void RenderWidgetHostViewMac::TickPendingLatencyInfoDelay() {
+ if (compositing_iosurface_layer_) {
+ // Keep calling gotNewFrame in a loop until enough display calls come in.
+ // Each call will be separated by about a vsync.
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&RenderWidgetHostViewMac::TickPendingLatencyInfoDelay,
+ pending_latency_info_delay_weak_ptr_factory_.GetWeakPtr()));
+ [compositing_iosurface_layer_ gotNewFrame];
+ }
+ if (software_layer_) {
+ // In software mode, setNeedsDisplay will almost immediately result in the
+ // layer's draw function being called, so manually insert a pretend-vsync
+ // at 60 Hz.
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&RenderWidgetHostViewMac::TickPendingLatencyInfoDelay,
+ pending_latency_info_delay_weak_ptr_factory_.GetWeakPtr()),
+ base::TimeDelta::FromMilliseconds(1000/60));
+ [software_layer_ setNeedsDisplay];
+ }
}
-void RenderWidgetHostViewMac::FrameSwapped() {
- software_latency_info_.AddLatencyNumber(
- ui::INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT, 0, 0);
- render_widget_host_->FrameSwapped(software_latency_info_);
- software_latency_info_.Clear();
+void RenderWidgetHostViewMac::AddPendingSwapAck(
+ int32 route_id, int gpu_host_id, int32 renderer_id) {
+ // Note that multiple un-acked swaps can come in the event of a GPU process
+ // loss. Drop the old acks.
+ pending_swap_ack_.reset(new PendingSwapAck(
+ route_id, gpu_host_id, renderer_id));
+
+ // A trace value of 2 indicates that there is a pending swap ack. See
+ // CompositingIOSurfaceLayer's canDrawInCGLContext for other value meanings.
+ TRACE_COUNTER_ID1("browser", "PendingSwapAck", this, 2);
+}
+
+void RenderWidgetHostViewMac::SendPendingSwapAck() {
+ if (!pending_swap_ack_)
+ return;
+
+ AcceleratedSurfaceMsg_BufferPresented_Params ack_params;
+ ack_params.sync_point = 0;
+ ack_params.renderer_id = pending_swap_ack_->renderer_id;
+ RenderWidgetHostImpl::AcknowledgeBufferPresent(pending_swap_ack_->route_id,
+ pending_swap_ack_->gpu_host_id,
+ ack_params);
+ pending_swap_ack_.reset();
+ TRACE_COUNTER_ID1("browser", "PendingSwapAck", this, 0);
+}
+
+void RenderWidgetHostViewMac::PauseForPendingResizeOrRepaintsAndDraw() {
+ if (!render_widget_host_ || render_widget_host_->is_hidden())
+ return;
+
+ // Pausing for the overlay view prevents the underlay from receiving
+ // frames. This may lead to large delays, causing overlaps. If both
+ // overlay and underlay resize at the same time, let them both to have
+ // some time waiting. See crbug.com/352020.
+ if (underlay_view_ &&
+ underlay_view_->render_widget_host_ &&
+ !underlay_view_->render_widget_host_->
+ CanPauseForPendingResizeOrRepaints())
+ return;
+
+ // Ensure that all frames are acked before waiting for a frame to come in.
+ // Note that we will draw a frame at the end of this function, so it is safe
+ // to ack a never-drawn frame here.
+ SendPendingSwapAck();
+
+ // Wait for a frame of the right size to come in.
+ render_widget_host_->PauseForPendingResizeOrRepaints();
+
+ // Immediately draw any frames that haven't been drawn yet. This is necessary
+ // to keep the window and the window's contents in sync.
+ [cocoa_view_ displayIfNeeded];
+ [software_layer_ displayIfNeeded];
+ [compositing_iosurface_layer_ displayIfNeeded];
+}
+
+void RenderWidgetHostViewMac::LayoutLayers() {
+ if (!use_core_animation_)
+ return;
+
+ // Disable animation of the layer's resizing or change in contents scale.
+ ScopedCAActionDisabler disabler;
+
+ CGRect new_background_frame = NSRectToCGRect([cocoa_view() bounds]);
+
+ // Dynamically calling setContentsScale on a CAOpenGLLayer for which
+ // setAsynchronous is dynamically toggled can result in flashes of corrupt
+ // content. Work around this by replacing the entire layer when the scale
+ // factor changes.
+ if (compositing_iosurface_ &&
+ [compositing_iosurface_layer_
+ respondsToSelector:(@selector(contentsScale))]) {
+ if (compositing_iosurface_->scale_factor() !=
+ [compositing_iosurface_layer_ contentsScale]) {
+ DestroyCompositedIOSurfaceLayer(kRemoveLayerFromHierarchy);
+ EnsureCompositedIOSurfaceLayer();
+ }
+ }
+ if (compositing_iosurface_ &&
+ compositing_iosurface_->HasIOSurface() &&
+ compositing_iosurface_layer_) {
+ CGRect layer_bounds = CGRectMake(
+ 0,
+ 0,
+ compositing_iosurface_->dip_io_surface_size().width(),
+ compositing_iosurface_->dip_io_surface_size().height());
+ CGPoint layer_position = CGPointMake(
+ 0,
+ CGRectGetHeight(new_background_frame) - CGRectGetHeight(layer_bounds));
+ bool bounds_changed = !CGRectEqualToRect(
+ layer_bounds, [compositing_iosurface_layer_ bounds]);
+ [compositing_iosurface_layer_ setPosition:layer_position];
+ [compositing_iosurface_layer_ setBounds:layer_bounds];
+
+ // If the bounds changed, then draw the frame immediately, to ensure that
+ // content displayed is in sync with the window size.
+ if (bounds_changed) {
+ // Also, sometimes, especially when infobars are being removed, the
+ // setNeedsDisplay calls are dropped on the floor, and stale content is
+ // displayed. Calling displayIfNeeded will ensure that the right size
+ // frame is drawn to the screen.
+ // http://crbug.com/350817
+ [compositing_iosurface_layer_ setNeedsDisplay];
+ [compositing_iosurface_layer_ displayIfNeeded];
+ }
+ }
+
+ // Dynamically update the software layer's contents scale to match the
+ // software frame.
+ if (software_frame_manager_->HasCurrentFrame() &&
+ [software_layer_ respondsToSelector:(@selector(contentsScale))] &&
+ [software_layer_ respondsToSelector:(@selector(setContentsScale:))]) {
+ if (software_frame_manager_->GetCurrentFrameDeviceScaleFactor() !=
+ [software_layer_ contentsScale]) {
+ [software_layer_ setContentsScale:
+ software_frame_manager_->GetCurrentFrameDeviceScaleFactor()];
+ }
+ }
+ // Changing the software layer's bounds and position doesn't always result
+ // in the layer being anchored to the top-left. Set the layer's frame
+ // explicitly, since this is more reliable in practice.
+ if (software_layer_) {
+ bool frame_changed = !CGRectEqualToRect(
+ new_background_frame, [software_layer_ frame]);
+ if (frame_changed) {
+ [software_layer_ setFrame:new_background_frame];
+ [software_layer_ setNeedsDisplay];
+ }
+ }
+}
+
+SkBitmap::Config RenderWidgetHostViewMac::PreferredReadbackFormat() {
+ return SkBitmap::kARGB_8888_Config;
}
} // namespace content
// RenderWidgetHostViewCocoa ---------------------------------------------------
@implementation RenderWidgetHostViewCocoa
-
@synthesize selectedRange = selectedRange_;
@synthesize suppressNextEscapeKeyUp = suppressNextEscapeKeyUp_;
@synthesize markedRange = markedRange_;
-@synthesize delegate = delegate_;
- (id)initWithRenderWidgetHostViewMac:(RenderWidgetHostViewMac*)r {
self = [super initWithFrame:NSZeroRect];
if (self) {
+ self.acceptsTouchEvents = YES;
editCommand_helper_.reset(new RenderWidgetHostViewMacEditCommandHelper);
editCommand_helper_->AddEditingSelectorsToClass([self class]);
renderWidgetHostView_.reset(r);
canBeKeyView_ = YES;
focusedPluginIdentifier_ = -1;
- deviceScaleFactor_ = ScaleFactor(self);
+ renderWidgetHostView_->backing_store_scale_factor_ =
+ ScaleFactorForView(self);
// OpenGL support:
if ([self respondsToSelector:
selector:@selector(globalFrameDidChange:)
name:NSViewGlobalFrameDidChangeNotification
object:self];
+ [[NSNotificationCenter defaultCenter]
+ addObserver:self
+ selector:@selector(didChangeScreenParameters:)
+ name:NSApplicationDidChangeScreenParametersNotification
+ object:nil];
}
return self;
}
if (renderWidgetHostView_)
renderWidgetHostView_->AcceleratedSurfaceRelease();
- if (delegate_ && [delegate_ respondsToSelector:@selector(viewGone:)])
- [delegate_ viewGone:self];
+ if (responderDelegate_ &&
+ [responderDelegate_ respondsToSelector:@selector(viewGone:)])
+ [responderDelegate_ viewGone:self];
+ responderDelegate_.reset();
+
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
+- (void)didChangeScreenParameters:(NSNotification*)notify {
+ g_screen_info_up_to_date = false;
+}
+
+- (void)setResponderDelegate:
+ (NSObject<RenderWidgetHostViewMacDelegate>*)delegate {
+ DCHECK(!responderDelegate_);
+ responderDelegate_.reset([delegate retain]);
+}
+
- (void)resetCursorRects {
if (currentCursor_) {
[self addCursorRect:[self visibleRect] cursor:currentCursor_];
}
- (void)gotUnhandledWheelEvent {
- if (delegate_ &&
- [delegate_ respondsToSelector:@selector(gotUnhandledWheelEvent)]) {
- [delegate_ gotUnhandledWheelEvent];
+ if (responderDelegate_ &&
+ [responderDelegate_
+ respondsToSelector:@selector(gotUnhandledWheelEvent)]) {
+ [responderDelegate_ gotUnhandledWheelEvent];
}
}
- (void)scrollOffsetPinnedToLeft:(BOOL)left toRight:(BOOL)right {
- if (delegate_ && [delegate_ respondsToSelector:
- @selector(scrollOffsetPinnedToLeft:toRight:)]) {
- [delegate_ scrollOffsetPinnedToLeft:left toRight:right];
+ if (responderDelegate_ &&
+ [responderDelegate_
+ respondsToSelector:@selector(scrollOffsetPinnedToLeft:toRight:)]) {
+ [responderDelegate_ scrollOffsetPinnedToLeft:left toRight:right];
}
}
- (void)setHasHorizontalScrollbar:(BOOL)has_horizontal_scrollbar {
- if (delegate_ &&
- [delegate_ respondsToSelector:@selector(setHasHorizontalScrollbar:)]) {
- [delegate_ setHasHorizontalScrollbar:has_horizontal_scrollbar];
+ if (responderDelegate_ &&
+ [responderDelegate_
+ respondsToSelector:@selector(setHasHorizontalScrollbar:)]) {
+ [responderDelegate_ setHasHorizontalScrollbar:has_horizontal_scrollbar];
}
}
if ([super respondsToSelector:selector])
return YES;
- if (delegate_)
- return [delegate_ respondsToSelector:selector];
+ if (responderDelegate_)
+ return [responderDelegate_ respondsToSelector:selector];
return NO;
}
- (id)forwardingTargetForSelector:(SEL)selector {
- if ([delegate_ respondsToSelector:selector])
- return delegate_;
+ if ([responderDelegate_ respondsToSelector:selector])
+ return responderDelegate_.get();
return [super forwardingTargetForSelector:selector];
}
// The cursor is over a nonWebContentView - ignore this mouse event.
return YES;
}
- if ([view isKindOfClass:[self class]] && ![view isEqual:self]) {
+ if ([view isKindOfClass:[self class]] && ![view isEqual:self] &&
+ !hasOpenMouseDown_) {
// The cursor is over an overlapping render widget. This check is done by
// both views so the one that's returned by -hitTest: will end up
// processing the event.
+ // Note that while dragging, we only get events for the render view where
+ // drag started, even if mouse is actually over another view or outside
+ // the window. Cocoa does this for us. We should handle these events and
+ // not ignore (since there is no other render view to handle them). Thus
+ // the |!hasOpenMouseDown_| check above.
return YES;
}
view = [view superview];
- (void)mouseEvent:(NSEvent*)theEvent {
TRACE_EVENT0("browser", "RenderWidgetHostViewCocoa::mouseEvent");
- if (delegate_ && [delegate_ respondsToSelector:@selector(handleEvent:)]) {
- BOOL handled = [delegate_ handleEvent:theEvent];
+ if (responderDelegate_ &&
+ [responderDelegate_ respondsToSelector:@selector(handleEvent:)]) {
+ BOOL handled = [responderDelegate_ handleEvent:theEvent];
if (handled)
return;
}
}
- (EventHandled)keyEvent:(NSEvent*)theEvent {
- if (delegate_ && [delegate_ respondsToSelector:@selector(handleEvent:)]) {
- BOOL handled = [delegate_ handleEvent:theEvent];
+ if (responderDelegate_ &&
+ [responderDelegate_ respondsToSelector:@selector(handleEvent:)]) {
+ BOOL handled = [responderDelegate_ handleEvent:theEvent];
if (handled)
return kEventHandled;
}
} else if (oldHasMarkedText && !hasMarkedText_ && !textInserted) {
if (unmarkTextCalled_) {
widgetHost->ImeConfirmComposition(
- string16(), gfx::Range::InvalidRange(), false);
+ base::string16(), gfx::Range::InvalidRange(), false);
} else {
widgetHost->ImeCancelComposition();
}
// So before sending the real key down event, we need to send a fake key up
// event to balance it.
NativeWebKeyboardEvent fakeEvent = event;
- fakeEvent.type = WebKit::WebInputEvent::KeyUp;
+ fakeEvent.type = blink::WebInputEvent::KeyUp;
fakeEvent.skip_in_browser = true;
widgetHost->ForwardKeyboardEvent(fakeEvent);
// Not checking |renderWidgetHostView_->render_widget_host_| here because
if (!textInserted && textToBeInserted_.length() == 1) {
// If a single character was inserted, then we just send it as a keypress
// event.
- event.type = WebKit::WebInputEvent::Char;
+ event.type = blink::WebInputEvent::Char;
event.text[0] = textToBeInserted_[0];
event.text[1] = 0;
event.skip_in_browser = true;
// We don't get insertText: calls if ctrl or cmd is down, or the key event
// generates an insert command. So synthesize a keypress event for these
// cases, unless the key event generated any other command.
- event.type = WebKit::WebInputEvent::Char;
+ event.type = blink::WebInputEvent::Char;
event.skip_in_browser = true;
widgetHost->ForwardKeyboardEvent(event);
}
}
if (renderWidgetHostView_->render_widget_host_) {
- const WebMouseWheelEvent& webEvent =
- WebInputEventFactory::mouseWheelEvent(event, self);
+ // History-swiping is not possible if the logic reaches this point.
+ // Allow rubber-banding in both directions.
+ bool canRubberbandLeft = true;
+ bool canRubberbandRight = true;
+ const WebMouseWheelEvent webEvent = WebInputEventFactory::mouseWheelEvent(
+ event, self, canRubberbandLeft, canRubberbandRight);
renderWidgetHostView_->render_widget_host_->ForwardWheelEvent(webEvent);
}
}
}
+- (void)beginGestureWithEvent:(NSEvent*)event {
+ [responderDelegate_ beginGestureWithEvent:event];
+}
+- (void)endGestureWithEvent:(NSEvent*)event {
+ [responderDelegate_ endGestureWithEvent:event];
+}
+- (void)touchesMovedWithEvent:(NSEvent*)event {
+ [responderDelegate_ touchesMovedWithEvent:event];
+}
+- (void)touchesBeganWithEvent:(NSEvent*)event {
+ [responderDelegate_ touchesBeganWithEvent:event];
+}
+- (void)touchesCancelledWithEvent:(NSEvent*)event {
+ [responderDelegate_ touchesCancelledWithEvent:event];
+}
+- (void)touchesEndedWithEvent:(NSEvent*)event {
+ [responderDelegate_ touchesEndedWithEvent:event];
+}
+
+// This is invoked only on 10.8 or newer when the user taps a word using
+// three fingers.
+- (void)quickLookWithEvent:(NSEvent*)event {
+ NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
+ TextInputClientMac::GetInstance()->GetStringAtPoint(
+ renderWidgetHostView_->render_widget_host_,
+ gfx::Point(point.x, NSHeight([self frame]) - point.y),
+ ^(NSAttributedString* string, NSPoint baselinePoint) {
+ if (string && [string length] > 0) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [self showDefinitionForAttributedString:string
+ atPoint:baselinePoint];
+ });
+ }
+ }
+ );
+}
+
+// This method handles 2 different types of hardware events.
+// (Apple does not distinguish between them).
+// a. Scrolling the middle wheel of a mouse.
+// b. Swiping on the track pad.
+//
+// This method is responsible for 2 types of behavior:
+// a. Scrolling the content of window.
+// b. Navigating forwards/backwards in history.
+//
+// This is a brief description of the logic:
+// 1. If the content can be scrolled, scroll the content.
+// (This requires a roundtrip to blink to determine whether the content
+// can be scrolled.)
+// Once this logic is triggered, the navigate logic cannot be triggered
+// until the gesture finishes.
+// 2. If the user is making a horizontal swipe, start the navigate
+// forward/backwards UI.
+// Once this logic is triggered, the user can either cancel or complete
+// the gesture. If the user completes the gesture, all remaining touches
+// are swallowed, and not allowed to scroll the content. If the user
+// cancels the gesture, all remaining touches are forwarded to the content
+// scroll logic. The user cannot trigger the navigation logic again.
- (void)scrollWheel:(NSEvent*)event {
- if (delegate_ && [delegate_ respondsToSelector:@selector(handleEvent:)]) {
- BOOL handled = [delegate_ handleEvent:event];
+ if (responderDelegate_ &&
+ [responderDelegate_ respondsToSelector:@selector(handleEvent:)]) {
+ BOOL handled = [responderDelegate_ handleEvent:event];
if (handled)
return;
}
if (base::mac::IsOSLionOrLater() && [event phase] == NSEventPhaseBegan &&
!endWheelMonitor_) {
endWheelMonitor_ =
- [NSEvent addLocalMonitorForEventsMatchingMask:NSScrollWheelMask
- handler:^(NSEvent* blockEvent) {
- [self shortCircuitScrollWheelEvent:blockEvent];
- return blockEvent;
- }];
+ [NSEvent addLocalMonitorForEventsMatchingMask:NSScrollWheelMask
+ handler:^(NSEvent* blockEvent) {
+ [self shortCircuitScrollWheelEvent:blockEvent];
+ return blockEvent;
+ }];
}
+ // This is responsible for content scrolling!
if (renderWidgetHostView_->render_widget_host_) {
- const WebMouseWheelEvent& webEvent =
- WebInputEventFactory::mouseWheelEvent(event, self);
+ BOOL canRubberbandLeft = [responderDelegate_ canRubberbandLeft:self];
+ BOOL canRubberbandRight = [responderDelegate_ canRubberbandRight:self];
+ const WebMouseWheelEvent webEvent = WebInputEventFactory::mouseWheelEvent(
+ event, self, canRubberbandLeft, canRubberbandRight);
renderWidgetHostView_->render_widget_host_->ForwardWheelEvent(webEvent);
}
}
+// Called repeatedly during a pinch gesture, with incremental change values.
+- (void)magnifyWithEvent:(NSEvent*)event {
+ if (renderWidgetHostView_->render_widget_host_) {
+ // Send a GesturePinchUpdate event.
+ // Note that we don't attempt to bracket these by GesturePinchBegin/End (or
+ // GestureSrollBegin/End) as is done for touchscreen. Keeping track of when
+ // a pinch is active would take a little more work here, and we don't need
+ // it for anything yet.
+ const WebGestureEvent& webEvent =
+ WebInputEventFactory::gestureEvent(event, self);
+ renderWidgetHostView_->render_widget_host_->ForwardGestureEvent(webEvent);
+ }
+}
+
- (void)viewWillMoveToWindow:(NSWindow*)newWindow {
NSWindow* oldWindow = [self window];
if (!renderWidgetHostView_->use_core_animation_)
[oldWindow disableScreenUpdatesUntilFlush];
- // If the new window for this view is using CoreAnimation then enable
- // CoreAnimation on this view.
- if (GetCoreAnimationStatus() == CORE_ANIMATION_ENABLED_LAZY &&
- [[newWindow contentView] wantsLayer]) {
- renderWidgetHostView_->EnableCoreAnimation();
- }
-
NSNotificationCenter* notificationCenter =
[NSNotificationCenter defaultCenter];
}
}
-- (void)updateTabBackingStoreScaleFactor {
- if (!renderWidgetHostView_->render_widget_host_)
- return;
-
- float scaleFactor = ScaleFactor(self);
- if (scaleFactor == deviceScaleFactor_)
- return;
- deviceScaleFactor_ = scaleFactor;
-
- BackingStoreMac* backingStore = static_cast<BackingStoreMac*>(
- renderWidgetHostView_->render_widget_host_->GetBackingStore(false));
- if (backingStore) // NULL in hardware path.
- backingStore->ScaleFactorChanged(scaleFactor);
-
- [self updateSoftwareLayerScaleFactor];
- renderWidgetHostView_->render_widget_host_->NotifyScreenInfoChanged();
+- (void)updateScreenProperties{
+ renderWidgetHostView_->UpdateBackingStoreScaleFactor();
+ renderWidgetHostView_->UpdateDisplayLink();
}
// http://developer.apple.com/library/mac/#documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/CapturingScreenContents/CapturingScreenContents.html#//apple_ref/doc/uid/TP40012302-CH10-SW4
- (void)windowDidChangeBackingProperties:(NSNotification*)notification {
- NSWindow* window = (NSWindow*)[notification object];
+ // Background tabs check if their scale factor or vsync properties changed
+ // when they are added to a window.
- CGFloat newBackingScaleFactor = [window backingScaleFactor];
- CGFloat oldBackingScaleFactor = [base::mac::ObjCCast<NSNumber>(
- [[notification userInfo] objectForKey:NSBackingPropertyOldScaleFactorKey])
- doubleValue];
- if (newBackingScaleFactor != oldBackingScaleFactor) {
- // Background tabs check if their scale factor changed when they are added
- // to a window.
-
- // Allocating a CGLayerRef with the current scale factor immediately from
- // this handler doesn't work. Schedule the backing store update on the
- // next runloop cycle, then things are read for CGLayerRef allocations to
- // work.
- [self performSelector:@selector(updateTabBackingStoreScaleFactor)
- withObject:nil
- afterDelay:0];
- }
+ // Allocating a CGLayerRef with the current scale factor immediately from
+ // this handler doesn't work. Schedule the backing store update on the
+ // next runloop cycle, then things are read for CGLayerRef allocations to
+ // work.
+ [self performSelector:@selector(updateScreenProperties)
+ withObject:nil
+ afterDelay:0];
}
- (void)globalFrameDidChange:(NSNotification*)notification {
return;
handlingGlobalFrameDidChange_ = YES;
- if (renderWidgetHostView_->compositing_iosurface_context_) {
+ if (!renderWidgetHostView_->use_core_animation_ &&
+ renderWidgetHostView_->compositing_iosurface_context_) {
[renderWidgetHostView_->compositing_iosurface_context_->nsgl_context()
update];
}
// NB: -[NSView setFrame:] calls through -setFrameSize:, so overriding
// -setFrame: isn't neccessary.
[super setFrameSize:newSize];
- if (renderWidgetHostView_->render_widget_host_) {
- renderWidgetHostView_->render_widget_host_->SendScreenRects();
- renderWidgetHostView_->render_widget_host_->WasResized();
- }
- // This call is necessary to make the window wait for a new frame at the new
- // size to be available before the resize completes. Calling only
- // setLayerContentsRedrawPolicy:NSViewLayerContentsRedrawOnSetNeedsDisplay on
- // this is not sufficient.
- ScopedCAActionDisabler disabler;
- CGRect frame = NSRectToCGRect([renderWidgetHostView_->cocoa_view() bounds]);
- [renderWidgetHostView_->software_layer_ setFrame:frame];
- [renderWidgetHostView_->software_layer_ setNeedsDisplay];
- [renderWidgetHostView_->compositing_iosurface_layer_ setFrame:frame];
- [renderWidgetHostView_->compositing_iosurface_layer_ setNeedsDisplay];
-}
+ if (!renderWidgetHostView_->render_widget_host_)
+ return;
+
+ // Move the CALayers to their positions in the new view size. Note that
+ // this will not draw anything because the non-background layers' sizes
+ // didn't actually change.
+ renderWidgetHostView_->LayoutLayers();
-- (void)callSetNeedsDisplayInRect {
- DCHECK([NSThread isMainThread]);
- DCHECK(renderWidgetHostView_->call_set_needs_display_in_rect_pending_);
- [self setNeedsDisplayInRect:renderWidgetHostView_->invalid_rect_];
- renderWidgetHostView_->call_set_needs_display_in_rect_pending_ = false;
- renderWidgetHostView_->invalid_rect_ = NSZeroRect;
+ renderWidgetHostView_->render_widget_host_->SendScreenRects();
+ renderWidgetHostView_->render_widget_host_->WasResized();
- if (renderWidgetHostView_->compositing_iosurface_layer_)
- [renderWidgetHostView_->compositing_iosurface_layer_ setNeedsDisplay];
+ // Wait for the frame that WasResize might have requested. If the view is
+ // being made visible at a new size, then this call will have no effect
+ // because the view widget is still hidden, and the pause call in WasShown
+ // will have this effect for us.
+ renderWidgetHostView_->PauseForPendingResizeOrRepaintsAndDraw();
}
// Fills with white the parts of the area to the right and bottom for |rect|
- (void)drawRect:(NSRect)dirtyRect {
TRACE_EVENT0("browser", "RenderWidgetHostViewCocoa::drawRect");
- CHECK(!renderWidgetHostView_->use_core_animation_);
+ DCHECK(!renderWidgetHostView_->use_core_animation_);
if (!renderWidgetHostView_->render_widget_host_) {
- // TODO(shess): Consider using something more noticable?
+ // When using CoreAnimation, this path is used to paint the contents area
+ // white before any frames come in. When layers to draw frames exist, this
+ // is not hit.
[[NSColor whiteColor] set];
NSRectFill(dirtyRect);
return;
}
- DCHECK(!renderWidgetHostView_->about_to_validate_and_paint_);
-
- // GetBackingStore works for both software and accelerated frames. If a
- // SwapBuffers occurs while GetBackingStore is blocking, we will continue to
- // blit the IOSurface below.
- renderWidgetHostView_->about_to_validate_and_paint_ = true;
- BackingStoreMac* backingStore = static_cast<BackingStoreMac*>(
- renderWidgetHostView_->render_widget_host_->GetBackingStore(true));
- renderWidgetHostView_->about_to_validate_and_paint_ = false;
-
const gfx::Rect damagedRect([self flipNSRectToRect:dirtyRect]);
if (renderWidgetHostView_->last_frame_was_accelerated_ &&
renderWidgetHostView_->compositing_iosurface_) {
if (renderWidgetHostView_->allow_overlapping_views_) {
- CHECK_EQ(CORE_ANIMATION_DISABLED, GetCoreAnimationStatus());
-
// If overlapping views need to be allowed, punch a hole in the window
// to expose the GL underlay.
TRACE_EVENT2("gpu", "NSRectFill clear", "w", damagedRect.width(),
NSRectFill(dirtyRect);
}
- if (renderWidgetHostView_->DrawIOSurfaceWithoutCoreAnimation())
- return;
-
- // On error, fall back to software and fall through to the non-accelerated
- // drawing path.
- renderWidgetHostView_->GotAcceleratedCompositingError();
+ gfx::ScopedCGLSetCurrentContext scoped_set_current_context(
+ renderWidgetHostView_->compositing_iosurface_context_->cgl_context());
+ renderWidgetHostView_->DrawIOSurfaceWithoutCoreAnimation();
+ return;
}
CGContextRef context = static_cast<CGContextRef>(
[[NSGraphicsContext currentContext] graphicsPort]);
- [self drawBackingStore:backingStore
- dirtyRect:NSRectToCGRect(dirtyRect)
- inContext:context];
+ [self drawWithDirtyRect:NSRectToCGRect(dirtyRect)
+ inContext:context];
}
-- (void)drawBackingStore:(BackingStoreMac*)backingStore
- dirtyRect:(CGRect)dirtyRect
- inContext:(CGContextRef)context {
+- (void)drawWithDirtyRect:(CGRect)dirtyRect
+ inContext:(CGContextRef)context {
content::SoftwareFrameManager* software_frame_manager =
renderWidgetHostView_->software_frame_manager_.get();
- // There should never be both a legacy software and software composited
- // frame.
- DCHECK(!backingStore || !software_frame_manager->HasCurrentFrame());
-
- if (backingStore || software_frame_manager->HasCurrentFrame()) {
+ if (software_frame_manager->HasCurrentFrame()) {
// Note: All coordinates are in view units, not pixels.
gfx::Rect bitmapRect(
- software_frame_manager->HasCurrentFrame() ?
- software_frame_manager->GetCurrentFrameSizeInDIP() :
- backingStore->size());
+ software_frame_manager->GetCurrentFrameSizeInDIP());
// Specify the proper y offset to ensure that the view is rooted to the
// upper left corner. This can be negative, if the window was resized
gfx::Rect paintRect = gfx::IntersectRects(bitmapRect, damagedRect);
if (!paintRect.IsEmpty()) {
- if (software_frame_manager->HasCurrentFrame()) {
- // If a software compositor framebuffer is present, draw using that.
- gfx::Size sizeInPixels =
- software_frame_manager->GetCurrentFrameSizeInPixels();
- base::ScopedCFTypeRef<CGDataProviderRef> dataProvider(
- CGDataProviderCreateWithData(
- NULL,
- software_frame_manager->GetCurrentFramePixels(),
- 4 * sizeInPixels.width() * sizeInPixels.height(),
- NULL));
- base::ScopedCFTypeRef<CGImageRef> image(
- CGImageCreate(
- sizeInPixels.width(),
- sizeInPixels.height(),
- 8,
- 32,
- 4 * sizeInPixels.width(),
- base::mac::GetSystemColorSpace(),
- kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host,
- dataProvider,
- NULL,
- false,
- kCGRenderingIntentDefault));
- CGRect imageRect = bitmapRect.ToCGRect();
- imageRect.origin.y = yOffset;
- CGContextDrawImage(context, imageRect, image);
- } else if (backingStore->cg_layer()) {
- // If we have a CGLayer, draw that into the window
- // TODO: add clipping to dirtyRect if it improves drawing performance.
- CGContextDrawLayerAtPoint(context, CGPointMake(0.0, yOffset),
- backingStore->cg_layer());
- } else {
- // If we haven't created a layer yet, draw the cached bitmap into
- // the window. The CGLayer will be created the next time the renderer
- // paints.
- base::ScopedCFTypeRef<CGImageRef> image(
- CGBitmapContextCreateImage(backingStore->cg_bitmap()));
- CGRect imageRect = bitmapRect.ToCGRect();
- imageRect.origin.y = yOffset;
- CGContextDrawImage(context, imageRect, image);
- }
+ gfx::Size sizeInPixels =
+ software_frame_manager->GetCurrentFrameSizeInPixels();
+ base::ScopedCFTypeRef<CGDataProviderRef> dataProvider(
+ CGDataProviderCreateWithData(
+ NULL,
+ software_frame_manager->GetCurrentFramePixels(),
+ 4 * sizeInPixels.width() * sizeInPixels.height(),
+ NULL));
+ base::ScopedCFTypeRef<CGImageRef> image(
+ CGImageCreate(
+ sizeInPixels.width(),
+ sizeInPixels.height(),
+ 8,
+ 32,
+ 4 * sizeInPixels.width(),
+ base::mac::GetSystemColorSpace(),
+ kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host,
+ dataProvider,
+ NULL,
+ false,
+ kCGRenderingIntentDefault));
+ CGRect imageRect = bitmapRect.ToCGRect();
+ imageRect.origin.y = yOffset;
+ CGContextDrawImage(context, imageRect, image);
}
- renderWidgetHostView_->FrameSwapped();
+ renderWidgetHostView_->SendPendingLatencyInfoToHost();
// Fill the remaining portion of the damagedRect with white
[self fillBottomRightRemainderOfRect:bitmapRect
}
- (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item {
- if (delegate_ && [delegate_ respondsToSelector:
- @selector(validateUserInterfaceItem:isValidItem:)]) {
+ if (responderDelegate_ &&
+ [responderDelegate_
+ respondsToSelector:@selector(validateUserInterfaceItem:
+ isValidItem:)]) {
BOOL valid;
- BOOL known = [delegate_ validateUserInterfaceItem:item
- isValidItem:&valid];
+ BOOL known =
+ [responderDelegate_ validateUserInterfaceItem:item isValidItem:&valid];
if (known)
return valid;
}
return [super accessibilityFocusedUIElement];
}
-- (void)doDefaultAction:(int32)accessibilityObjectId {
- RenderWidgetHostImpl* rwh = renderWidgetHostView_->render_widget_host_;
- rwh->Send(new AccessibilityMsg_DoDefaultAction(
- rwh->GetRoutingID(), accessibilityObjectId));
-}
-
-// VoiceOver uses this method to move the caret to the beginning of the next
-// word in a text field.
-- (void)accessibilitySetTextSelection:(int32)accId
- startOffset:(int32)startOffset
- endOffset:(int32)endOffset {
- RenderWidgetHostImpl* rwh = renderWidgetHostView_->render_widget_host_;
- rwh->AccessibilitySetTextSelection(accId, startOffset, endOffset);
-}
-
-// Convert a web accessibility's location in web coordinates into a cocoa
-// screen coordinate.
-- (NSPoint)accessibilityPointInScreen:
- (BrowserAccessibilityCocoa*)accessibility {
- NSPoint origin = [accessibility origin];
- NSSize size = [[accessibility size] sizeValue];
- origin.y = NSHeight([self bounds]) - origin.y;
- NSPoint originInWindow = [self convertPoint:origin toView:nil];
- NSPoint originInScreen = [[self window] convertBaseToScreen:originInWindow];
- originInScreen.y = originInScreen.y - size.height;
- return originInScreen;
-}
-
-- (void)setAccessibilityFocus:(BOOL)focus
- accessibilityId:(int32)accessibilityObjectId {
- if (focus) {
- RenderWidgetHostImpl* rwh = renderWidgetHostView_->render_widget_host_;
- rwh->Send(new AccessibilityMsg_SetFocus(
- rwh->GetRoutingID(), accessibilityObjectId));
-
- // Immediately set the focused item even though we have not officially set
- // focus on it as VoiceOver expects to get the focused item after this
- // method returns.
- BrowserAccessibilityManager* manager =
- renderWidgetHostView_->GetBrowserAccessibilityManager();
- manager->SetFocus(manager->GetFromRendererID(accessibilityObjectId), false);
- }
-}
-
-- (void)performShowMenuAction:(BrowserAccessibilityCocoa*)accessibility {
- // Performs a right click copying WebKit's
- // accessibilityPerformShowMenuAction.
- NSPoint location = [self accessibilityPointInScreen:accessibility];
- NSSize size = [[accessibility size] sizeValue];
- location = [[self window] convertScreenToBase:location];
- location.x += size.width/2;
- location.y += size.height/2;
-
- NSEvent* fakeRightClick = [NSEvent
- mouseEventWithType:NSRightMouseDown
- location:location
- modifierFlags:0
- timestamp:0
- windowNumber:[[self window] windowNumber]
- context:[NSGraphicsContext currentContext]
- eventNumber:0
- clickCount:1
- pressure:0];
-
- [self mouseEvent:fakeRightClick];
-}
-
// Below is the nasty tooltip stuff -- copied from WebKit's WebHTMLView.mm
// with minor modifications for code style and commenting.
//
// called in keyEvent: method.
if (!handlingKeyDown_) {
renderWidgetHostView_->render_widget_host_->ImeConfirmComposition(
- string16(), gfx::Range::InvalidRange(), false);
+ base::string16(), gfx::Range::InvalidRange(), false);
} else {
unmarkTextCalled_ = YES;
}
} else {
// Use a thin black underline by default.
underlines_.push_back(
- WebKit::WebCompositionUnderline(0, length, SK_ColorBLACK, false));
+ blink::WebCompositionUnderline(0, length, SK_ColorBLACK, false));
}
// If we are handling a key down event, then SetComposition() will be
- (void)viewDidMoveToWindow {
if ([self window])
- [self updateTabBackingStoreScaleFactor];
+ [self updateScreenProperties];
if (canBeKeyView_) {
NSWindow* newWindow = [self window];
hasOpenMouseDown_ = NO;
}
-
- // Resize the view's layers to match the new window size.
- ScopedCAActionDisabler disabler;
- [renderWidgetHostView_->software_layer_
- setFrame:NSRectToCGRect([self bounds])];
- [renderWidgetHostView_->compositing_iosurface_layer_
- setFrame:NSRectToCGRect([self bounds])];
}
- (void)undo:(id)sender {
- if (renderWidgetHostView_->render_widget_host_->IsRenderView()) {
- static_cast<RenderViewHostImpl*>(
- renderWidgetHostView_->render_widget_host_)->Undo();
- }
+ WebContents* web_contents = renderWidgetHostView_->GetWebContents();
+ if (web_contents)
+ web_contents->Undo();
}
- (void)redo:(id)sender {
- if (renderWidgetHostView_->render_widget_host_->IsRenderView()) {
- static_cast<RenderViewHostImpl*>(
- renderWidgetHostView_->render_widget_host_)->Redo();
- }
+ WebContents* web_contents = renderWidgetHostView_->GetWebContents();
+ if (web_contents)
+ web_contents->Redo();
}
- (void)cut:(id)sender {
- if (renderWidgetHostView_->render_widget_host_->IsRenderView()) {
- static_cast<RenderViewHostImpl*>(
- renderWidgetHostView_->render_widget_host_)->Cut();
- }
+ WebContents* web_contents = renderWidgetHostView_->GetWebContents();
+ if (web_contents)
+ web_contents->Cut();
}
- (void)copy:(id)sender {
- if (renderWidgetHostView_->render_widget_host_->IsRenderView()) {
- static_cast<RenderViewHostImpl*>(
- renderWidgetHostView_->render_widget_host_)->Copy();
- }
+ WebContents* web_contents = renderWidgetHostView_->GetWebContents();
+ if (web_contents)
+ web_contents->Copy();
}
- (void)copyToFindPboard:(id)sender {
- if (renderWidgetHostView_->render_widget_host_->IsRenderView()) {
- static_cast<RenderViewHostImpl*>(
- renderWidgetHostView_->render_widget_host_)->CopyToFindPboard();
- }
+ WebContents* web_contents = renderWidgetHostView_->GetWebContents();
+ if (web_contents)
+ web_contents->CopyToFindPboard();
}
- (void)paste:(id)sender {
- if (renderWidgetHostView_->render_widget_host_->IsRenderView()) {
- static_cast<RenderViewHostImpl*>(
- renderWidgetHostView_->render_widget_host_)->Paste();
- }
+ WebContents* web_contents = renderWidgetHostView_->GetWebContents();
+ if (web_contents)
+ web_contents->Paste();
}
- (void)pasteAndMatchStyle:(id)sender {
- if (renderWidgetHostView_->render_widget_host_->IsRenderView()) {
- static_cast<RenderViewHostImpl*>(
- renderWidgetHostView_->render_widget_host_)->PasteAndMatchStyle();
- }
+ WebContents* web_contents = renderWidgetHostView_->GetWebContents();
+ if (web_contents)
+ web_contents->PasteAndMatchStyle();
}
- (void)selectAll:(id)sender {
// menu handler, neither is true.
// Explicitly call SelectAll() here to make sure the renderer returns
// selection results.
- if (renderWidgetHostView_->render_widget_host_->IsRenderView()) {
- static_cast<RenderViewHostImpl*>(
- renderWidgetHostView_->render_widget_host_)->SelectAll();
- }
+ WebContents* web_contents = renderWidgetHostView_->GetWebContents();
+ if (web_contents)
+ web_contents->SelectAll();
}
- (void)startSpeaking:(id)sender {
if (renderWidgetHostView_->render_widget_host_)
renderWidgetHostView_->render_widget_host_->ImeConfirmComposition(
- string16(), gfx::Range::InvalidRange(), false);
+ base::string16(), gfx::Range::InvalidRange(), false);
[self cancelComposition];
}
if (!active) {
[[ComplexTextInputPanel sharedComplexTextInputPanel] cancelComposition];
renderWidgetHostView_->PluginImeCompositionCompleted(
- string16(), focusedPluginIdentifier_);
+ base::string16(), focusedPluginIdentifier_);
}
}
if (pluginImeActive_ &&
![[ComplexTextInputPanel sharedComplexTextInputPanel] inComposition]) {
renderWidgetHostView_->PluginImeCompositionCompleted(
- string16(), focusedPluginIdentifier_);
+ base::string16(), focusedPluginIdentifier_);
pluginImeActive_ = NO;
}
}
renderWidgetHostView_->KillSelf();
}
-- (void)updateSoftwareLayerScaleFactor {
- if (![renderWidgetHostView_->software_layer_
- respondsToSelector:@selector(setContentsScale:)])
- return;
-
- ScopedCAActionDisabler disabler;
- [renderWidgetHostView_->software_layer_ setContentsScale:deviceScaleFactor_];
-}
-
-// Delegate methods for the software CALayer
-- (void)drawLayer:(CALayer*)layer
- inContext:(CGContextRef)context {
- TRACE_EVENT0("browser", "CompositingIOSurfaceLayer::drawLayer");
-
- DCHECK(renderWidgetHostView_->use_core_animation_);
- DCHECK([layer isEqual:renderWidgetHostView_->software_layer_]);
-
- CGRect clipRect = CGContextGetClipBoundingBox(context);
-
- if (!renderWidgetHostView_->render_widget_host_ ||
- renderWidgetHostView_->render_widget_host_->is_hidden()) {
- CGContextSetFillColorWithColor(context,
- CGColorGetConstantColor(kCGColorWhite));
- CGContextFillRect(context, clipRect);
- return;
- }
-
- renderWidgetHostView_->about_to_validate_and_paint_ = true;
- BackingStoreMac* backingStore = static_cast<BackingStoreMac*>(
- renderWidgetHostView_->render_widget_host_->GetBackingStore(true));
- renderWidgetHostView_->about_to_validate_and_paint_ = false;
-
- [self drawBackingStore:backingStore
- dirtyRect:clipRect
- inContext:context];
-}
-
-- (void)setNeedsDisplay:(BOOL)flag {
- [renderWidgetHostView_->software_layer_ setNeedsDisplay];
- [super setNeedsDisplay:flag];
-}
-
-- (void)setNeedsDisplayInRect:(NSRect)rect {
- [renderWidgetHostView_->software_layer_
- setNeedsDisplayInRect:NSRectToCGRect(rect)];
- [super setNeedsDisplayInRect:rect];
-}
-
@end
//
return [super isOpaque];
}
+// "-webkit-app-region: drag | no-drag" is implemented on Mac by excluding
+// regions that are not draggable. (See ControlRegionView in
+// native_app_window_cocoa.mm). This requires the render host view to be
+// draggable by default.
+- (BOOL)mouseDownCanMoveWindow {
+ return YES;
+}
+
@end
+
+@implementation SoftwareLayer
+
+- (id)initWithRenderWidgetHostViewMac:(content::RenderWidgetHostViewMac*)r {
+ if (self = [super init]) {
+ renderWidgetHostView_ = r;
+
+ [self setBackgroundColor:CGColorGetConstantColor(kCGColorWhite)];
+ [self setAnchorPoint:CGPointMake(0, 0)];
+ // Setting contents gravity is necessary to prevent the layer from being
+ // scaled during dyanmic resizes (especially with devtools open).
+ [self setContentsGravity:kCAGravityTopLeft];
+ if (renderWidgetHostView_->software_frame_manager_->HasCurrentFrame() &&
+ [self respondsToSelector:(@selector(setContentsScale:))]) {
+ [self setContentsScale:renderWidgetHostView_->software_frame_manager_->
+ GetCurrentFrameDeviceScaleFactor()];
+ }
+
+ // Ensure that the transition between frames not be animated.
+ [self setActions:@{ @"contents" : [NSNull null] }];
+ }
+ return self;
+}
+
+- (void)drawInContext:(CGContextRef)context {
+ TRACE_EVENT0("browser", "SoftwareLayer::drawInContext");
+
+ CGRect clipRect = CGContextGetClipBoundingBox(context);
+ if (renderWidgetHostView_) {
+ [renderWidgetHostView_->cocoa_view() drawWithDirtyRect:clipRect
+ inContext:context];
+ } else {
+ CGContextSetFillColorWithColor(context,
+ CGColorGetConstantColor(kCGColorWhite));
+ CGContextFillRect(context, clipRect);
+ }
+}
+
+- (void)disableRendering {
+ // Disable the fade-out animation as the layer is removed.
+ ScopedCAActionDisabler disabler;
+ [self removeFromSuperlayer];
+ renderWidgetHostView_ = nil;
+}
+
+@end // implementation SoftwareLayer