Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / content / browser / renderer_host / render_widget_host_view_mac.mm
index 4c53eb3..16002ed 100644 (file)
@@ -10,7 +10,6 @@
 #include "base/basictypes.h"
 #include "base/bind.h"
 #include "base/callback_helpers.h"
-#include "base/command_line.h"
 #include "base/debug/crash_logging.h"
 #include "base/debug/trace_event.h"
 #include "base/logging.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"
 #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 "skia/ext/platform_canvas.h"
 #include "third_party/WebKit/public/platform/WebScreenInfo.h"
 #include "third_party/WebKit/public/web/WebInputEvent.h"
@@ -81,19 +81,6 @@ using blink::WebInputEventFactory;
 using blink::WebMouseEvent;
 using blink::WebMouseWheelEvent;
 
-enum CoreAnimationStatus {
-  CORE_ANIMATION_DISABLED,
-  CORE_ANIMATION_ENABLED,
-};
-
-static CoreAnimationStatus GetCoreAnimationStatus() {
-  if (CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kUseCoreAnimation)) {
-    return CORE_ANIMATION_ENABLED;
-  }
-  return CORE_ANIMATION_DISABLED;
-}
-
 // These are not documented, so use only after checking -respondsToSelector:.
 @interface NSApplication (UndocumentedSpeechMethods)
 - (void)speakString:(NSString*)string;
@@ -206,7 +193,8 @@ static float ScaleFactor(NSView* view) {
                               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];
@@ -322,15 +310,35 @@ void DisablePasswordInput() {
   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;
 }
 
@@ -402,8 +410,13 @@ RenderWidgetHostViewMac::RenderWidgetHostViewMac(RenderWidgetHost* widget)
       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),
       is_loading_(false),
       weak_factory_(this),
       fullscreen_parent_host_view_(NULL),
@@ -492,7 +505,9 @@ void RenderWidgetHostViewMac::EnableCoreAnimation() {
 }
 
 bool RenderWidgetHostViewMac::CreateCompositedIOSurface() {
-  int current_window_number = window_number();
+  int current_window_number = use_core_animation_ ?
+      CompositingIOSurfaceContext::kOffscreenContextWindowNumber :
+      window_number();
   bool new_surface_needed = !compositing_iosurface_;
   bool new_context_needed =
     !compositing_iosurface_context_ ||
@@ -584,6 +599,9 @@ void RenderWidgetHostViewMac::DestroyCompositedIOSurfaceAndLayer(
 }
 
 void RenderWidgetHostViewMac::ClearBoundContextDrawable() {
+  if (use_core_animation_)
+    return;
+
   if (compositing_iosurface_context_ &&
       cocoa_view_ &&
       [[compositing_iosurface_context_->nsgl_context() view]
@@ -617,10 +635,7 @@ void RenderWidgetHostViewMac::InitAsPopup(
   [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,
@@ -782,12 +797,7 @@ void RenderWidgetHostViewMac::SetBounds(const gfx::Rect& rect) {
     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;
-    }
+    origin_global.y = FlipYFromRectToScreen(origin_global.y, size.height);
     [popup_window_ setFrame:NSMakeRect(origin_global.x, origin_global.y,
                                        size.width, size.height)
                     display:YES];
@@ -930,8 +940,7 @@ void RenderWidgetHostViewMac::DidUpdateBackingStore(
     const std::vector<ui::LatencyInfo>& latency_info) {
   GotSoftwareFrame();
 
-  for (size_t i = 0; i < latency_info.size(); i++)
-    software_latency_info_.push_back(latency_info[i]);
+  AddPendingLatencyInfo(latency_info);
 
   if (render_widget_host_->is_hidden())
     return;
@@ -1294,6 +1303,8 @@ void RenderWidgetHostViewMac::CompositorSwapBuffers(
   if (render_widget_host_->is_hidden())
     return;
 
+  AddPendingLatencyInfo(latency_info);
+
   NSWindow* window = [cocoa_view_ window];
   if (window_number() <= 0) {
     // There is no window to present so capturing during present won't work.
@@ -1304,10 +1315,11 @@ void RenderWidgetHostViewMac::CompositorSwapBuffers(
       RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback callback;
       if (frame_subscriber_->ShouldCaptureFrame(present_time,
                                                 &frame, &callback)) {
-        CGLSetCurrentContext(compositing_iosurface_context_->cgl_context());
+        gfx::ScopedCGLSetCurrentContext scoped_set_current_context(
+            compositing_iosurface_context_->cgl_context());
         compositing_iosurface_->SetIOSurfaceWithContextCurrent(
             compositing_iosurface_context_, surface_handle, size,
-            surface_scale_factor, latency_info);
+            surface_scale_factor);
         compositing_iosurface_->CopyToVideoFrame(
             gfx::Rect(size), frame,
             base::Bind(callback, present_time));
@@ -1346,10 +1358,11 @@ void RenderWidgetHostViewMac::CompositorSwapBuffers(
 
   // Make the context current and update the IOSurface with the handle
   // passed in by the swap command.
-  CGLSetCurrentContext(compositing_iosurface_context_->cgl_context());
+  gfx::ScopedCGLSetCurrentContext scoped_set_current_context(
+      compositing_iosurface_context_->cgl_context());
   if (!compositing_iosurface_->SetIOSurfaceWithContextCurrent(
           compositing_iosurface_context_, surface_handle, size,
-          surface_scale_factor, latency_info)) {
+          surface_scale_factor)) {
     LOG(ERROR) << "Failed SetIOSurface on CompositingIOSurfaceMac";
     GotAcceleratedCompositingError();
     return;
@@ -1370,7 +1383,8 @@ void RenderWidgetHostViewMac::CompositorSwapBuffers(
       compositing_iosurface_->CopyToVideoFrame(
           gfx::Rect(size), frame,
           base::Bind(callback, present_time));
-      CGLSetCurrentContext(compositing_iosurface_context_->cgl_context());
+      DCHECK_EQ(CGLGetCurrentContext(),
+                compositing_iosurface_context_->cgl_context());
     }
   }
 
@@ -1396,7 +1410,8 @@ void RenderWidgetHostViewMac::CompositorSwapBuffers(
   if (!about_to_validate_and_paint_) {
     if (use_core_animation_) {
       DCHECK(compositing_iosurface_layer_);
-      [compositing_iosurface_layer_ setNeedsDisplay];
+      compositing_iosurface_layer_async_timer_.Reset();
+      [compositing_iosurface_layer_ gotNewFrame];
     } else {
       if (!DrawIOSurfaceWithoutCoreAnimation()) {
         [cocoa_view_ setNeedsDisplay:YES];
@@ -1426,7 +1441,7 @@ bool RenderWidgetHostViewMac::DrawIOSurfaceWithoutCoreAnimation() {
   if (underlay_view_ &&
       underlay_view_->compositing_iosurface_ &&
       underlay_view_has_drawn_) {
-    [underlay_view_->cocoa_view() setNeedsDisplay:YES];
+    [underlay_view_->cocoa_view() setNeedsDisplayInRect:NSMakeRect(0, 0, 1, 1)];
     return true;
   }
 
@@ -1461,6 +1476,7 @@ bool RenderWidgetHostViewMac::DrawIOSurfaceWithoutCoreAnimation() {
     }
   }
 
+  SendPendingLatencyInfoToHost();
   return true;
 }
 
@@ -1479,6 +1495,9 @@ void RenderWidgetHostViewMac::GotAcceleratedCompositingError() {
 
 void RenderWidgetHostViewMac::SetOverlayView(
     RenderWidgetHostViewMac* overlay, const gfx::Point& offset) {
+  if (use_core_animation_)
+    return;
+
   if (overlay_view_)
     overlay_view_->underlay_view_.reset();
 
@@ -1492,6 +1511,9 @@ void RenderWidgetHostViewMac::SetOverlayView(
 }
 
 void RenderWidgetHostViewMac::RemoveOverlayView() {
+  if (use_core_animation_)
+    return;
+
   if (overlay_view_) {
     overlay_view_->underlay_view_.reset();
     overlay_view_.reset();
@@ -1714,20 +1736,38 @@ void RenderWidgetHostViewMac::OnSwapCompositorFrame(
     return;
   }
 
+  // Add latency info to report when the frame finishes drawing.
+  AddPendingLatencyInfo(frame->metadata.latency_info);
+  GotSoftwareFrame();
+
+  // 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];
+    if (!about_to_validate_and_paint_)
+      [software_layer_ displayIfNeeded];
+  } else {
+    [cocoa_view_ setNeedsDisplay:YES];
+    if (!about_to_validate_and_paint_)
+      [cocoa_view_ displayIfNeeded];
+  }
+
   cc::CompositorFrameAck ack;
   RenderWidgetHostImpl::SendSwapCompositorFrameAck(
       render_widget_host_->GetRoutingID(),
       software_frame_manager_->GetCurrentFrameOutputSurfaceId(),
       render_widget_host_->GetProcess()->GetID(),
       ack);
-  for (size_t i = 0; i < frame->metadata.latency_info.size(); i++) {
-    software_latency_info_.push_back(frame->metadata.latency_info[i]);
-  }
   software_frame_manager_->SwapToNewFrameComplete(
       !render_widget_host_->is_hidden());
 
-  GotSoftwareFrame();
-  [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() {
@@ -1837,6 +1877,16 @@ void RenderWidgetHostViewMac::ShutdownHost() {
 }
 
 void RenderWidgetHostViewMac::GotAcceleratedFrame() {
+  // Update the host with VSync parametrs.
+  base::TimeTicks timebase;
+  base::TimeDelta interval;
+  if (compositing_iosurface_context_ &&
+      compositing_iosurface_context_->display_link() &&
+      compositing_iosurface_context_->display_link()->GetVSyncParameters(
+          &timebase, &interval)) {
+    render_widget_host_->UpdateVSyncParameters(timebase, interval);
+  }
+
   // Update the scale factor of the layer to match the scale factor of the
   // IOSurface.
   [compositing_iosurface_layer_ updateScaleFactor];
@@ -1880,6 +1930,10 @@ void RenderWidgetHostViewMac::GotSoftwareFrame() {
   }
 }
 
+void RenderWidgetHostViewMac::TimerSinceGotAcceleratedFrameFired() {
+  [compositing_iosurface_layer_ timerSinceGotNewFrameFired];
+}
+
 void RenderWidgetHostViewMac::SetActive(bool active) {
   if (render_widget_host_) {
     render_widget_host_->SetActive(active);
@@ -1970,13 +2024,63 @@ gfx::Rect RenderWidgetHostViewMac::GetScaledOpenGLPixelRect(
                                              scale_factor()));
 }
 
-void RenderWidgetHostViewMac::SendSoftwareLatencyInfoToHost() {
-  for (size_t i = 0; i < software_latency_info_.size(); i++) {
-    software_latency_info_[i].AddLatencyNumber(
+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(software_latency_info_[i]);
+    render_widget_host_->FrameSwapped(pending_latency_info_[i]);
   }
-  software_latency_info_.clear();
+  pending_latency_info_.clear();
+}
+
+void RenderWidgetHostViewMac::TickPendingLatencyInfoDelay() {
+  // Keep calling setNeedsDisplay in a loop until enough display calls come in.
+  base::MessageLoop::current()->PostTask(FROM_HERE,
+      base::Bind(&RenderWidgetHostViewMac::TickPendingLatencyInfoDelay,
+                 pending_latency_info_delay_weak_ptr_factory_.GetWeakPtr()));
+  [software_layer_ setNeedsDisplay];
+  [compositing_iosurface_layer_ setNeedsDisplay];
 }
 
 }  // namespace content
@@ -2011,6 +2115,11 @@ void RenderWidgetHostViewMac::SendSoftwareLatencyInfoToHost() {
            selector:@selector(globalFrameDidChange:)
                name:NSViewGlobalFrameDidChangeNotification
              object:self];
+    [[NSNotificationCenter defaultCenter]
+        addObserver:self
+           selector:@selector(didChangeScreenParameters:)
+               name:NSApplicationDidChangeScreenParametersNotification
+             object:nil];
   }
   return self;
 }
@@ -2033,6 +2142,10 @@ void RenderWidgetHostViewMac::SendSoftwareLatencyInfoToHost() {
   [super dealloc];
 }
 
+- (void)didChangeScreenParameters:(NSNotification*)notify {
+  g_screen_info_up_to_date = false;
+}
+
 - (void)setResponderDelegate:
             (NSObject<RenderWidgetHostViewMacDelegate>*)delegate {
   DCHECK(!responderDelegate_);
@@ -2695,7 +2808,8 @@ void RenderWidgetHostViewMac::SendSoftwareLatencyInfoToHost() {
     return;
 
   handlingGlobalFrameDidChange_ = YES;
-  if (renderWidgetHostView_->compositing_iosurface_context_) {
+  if (!renderWidgetHostView_->use_core_animation_ &&
+      renderWidgetHostView_->compositing_iosurface_context_) {
     [renderWidgetHostView_->compositing_iosurface_context_->nsgl_context()
         update];
   }
@@ -2818,8 +2932,6 @@ void RenderWidgetHostViewMac::SendSoftwareLatencyInfoToHost() {
   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(),
@@ -2833,7 +2945,7 @@ void RenderWidgetHostViewMac::SendSoftwareLatencyInfoToHost() {
       NSRectFill(dirtyRect);
     }
 
-    CGLSetCurrentContext(
+    gfx::ScopedCGLSetCurrentContext scoped_set_current_context(
         renderWidgetHostView_->compositing_iosurface_context_->cgl_context());
     if (renderWidgetHostView_->DrawIOSurfaceWithoutCoreAnimation())
       return;
@@ -2919,7 +3031,7 @@ void RenderWidgetHostViewMac::SendSoftwareLatencyInfoToHost() {
       }
     }
 
-    renderWidgetHostView_->SendSoftwareLatencyInfoToHost();
+    renderWidgetHostView_->SendPendingLatencyInfoToHost();
 
     // Fill the remaining portion of the damagedRect with white
     [self fillBottomRightRemainderOfRect:bitmapRect