diff --git a/libcef/webwidget_host.cc b/libcef/webwidget_host.cc index 490c4b3c2..251d0853c 100644 --- a/libcef/webwidget_host.cc +++ b/libcef/webwidget_host.cc @@ -14,10 +14,31 @@ using webkit::npapi::WebPluginGeometry; using WebKit::WebSize; +void WebWidgetHost::ScheduleComposite() { + if (has_invalidate_task_) + return; + + has_invalidate_task_ = true; + + // Try to paint at 60fps. + static int64 kDesiredRate = 16; + + base::TimeDelta delta = base::TimeTicks::Now() - paint_last_call_; + int64 actualRate = delta.InMilliseconds(); + if (actualRate >= kDesiredRate) { + // Can't keep up so run as fast as possible. + MessageLoop::current()->PostTask(FROM_HERE, + base::Bind(&WebWidgetHost::Invalidate, weak_factory_.GetWeakPtr())); + } else { + // Maintain the desired rate. + MessageLoop::current()->PostDelayedTask(FROM_HERE, + base::Bind(&WebWidgetHost::Invalidate, weak_factory_.GetWeakPtr()), + kDesiredRate - actualRate); + } +} + void WebWidgetHost::ScheduleAnimation() { - MessageLoop::current()->PostDelayedTask(FROM_HERE, - base::Bind(&WebWidgetHost::ScheduleComposite, weak_factory_.GetWeakPtr()), - 10); + ScheduleComposite(); } void WebWidgetHost::UpdatePaintRect(const gfx::Rect& rect) { diff --git a/libcef/webwidget_host.h b/libcef/webwidget_host.h index 8d02495c7..36ebe1dc9 100644 --- a/libcef/webwidget_host.h +++ b/libcef/webwidget_host.h @@ -12,6 +12,7 @@ #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" #include "base/task.h" +#include "base/time.h" #include "skia/ext/platform_canvas.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebRect.h" @@ -73,8 +74,17 @@ class WebWidgetHost { void DidInvalidateRect(const gfx::Rect& rect); void DidScrollRect(int dx, int dy, const gfx::Rect& clip_rect); + + // Called for accelerated content like WebGL. void ScheduleComposite(); + + // Called for requestAnimationFrame animations. void ScheduleAnimation(); + + // Invalidate the complete client area. This is called at a reasonable frame + // rate by the Schedule*() methods. + void Invalidate(); + #if defined(OS_WIN) void SetCursor(HCURSOR cursor); #endif @@ -161,7 +171,7 @@ class WebWidgetHost { void OnInputLangChange(DWORD character_set, HKL input_language_id); void ImeUpdateTextInputState(WebKit::WebTextInputType type, const gfx::Rect& caret_rect); - static void UpdateInputMethod(HWND view); + void UpdateInputMethod(); #elif defined(OS_MACOSX) // These need to be called from a non-subclass, so they need to be public. public: @@ -225,6 +235,17 @@ class WebWidgetHost { // True if an update task is pending when window rendering is disabled. bool has_update_task_; + // True if an invalidate task is pending due to the Schedule*() methods. + bool has_invalidate_task_; + +#if defined(OS_WIN) + // True if an update input method task is pending due to DidInvalidateRect(). + bool has_update_input_method_task_; +#endif + + // When the Paint() method last completed. + base::TimeTicks paint_last_call_; + // Redraw rectangle requested by an explicit call to CefBrowser::Invalidate(). gfx::Rect redraw_rect_; diff --git a/libcef/webwidget_host_gtk.cc b/libcef/webwidget_host_gtk.cc index 97a2d845f..e4861b146 100644 --- a/libcef/webwidget_host_gtk.cc +++ b/libcef/webwidget_host_gtk.cc @@ -302,7 +302,7 @@ void WebWidgetHost::DidScrollRect(int dx, int dy, const gfx::Rect& clip_rect) { DidInvalidateRect(clip_rect); } -void WebWidgetHost::ScheduleComposite() { +void WebWidgetHost::Invalidate() { int width = logical_size_.width(); int height = logical_size_.height(); GdkRectangle grect = { @@ -323,6 +323,7 @@ WebWidgetHost::WebWidgetHost() canvas_h_(0), popup_(false), has_update_task_(false), + has_invalidate_task_(false), ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { set_painting(false); } @@ -414,6 +415,11 @@ void WebWidgetHost::Paint() { cairo_destroy(cairo_drawable); gdk_window_end_paint(window); + + // Used with scheduled invalidation to maintain a consistent frame rate. + paint_last_call_ = base::TimeTicks::Now(); + if (has_invalidate_task_) + has_invalidate_task_ = false; } void WebWidgetHost::SetTooltipText(const CefString& tooltip_text) diff --git a/libcef/webwidget_host_mac.mm b/libcef/webwidget_host_mac.mm index 64133d93f..6f94c7ab0 100644 --- a/libcef/webwidget_host_mac.mm +++ b/libcef/webwidget_host_mac.mm @@ -67,6 +67,7 @@ WebWidgetHost::WebWidgetHost() canvas_h_(0), popup_(false), has_update_task_(false), + has_invalidate_task_(false), ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { set_painting(false); } @@ -177,12 +178,13 @@ void WebWidgetHost::Paint(SkRegion& update_rgn) { paint_rgn_.setRect(convertToSkiaRect(client_rect)); } - // Animate the view and layout any views that have not been laid out yet. The - // latter may result in more invalidation. Keep track of the fact that we are - // laying out views, because this will sometimes cause ScrollRect to be called - // and we don't want to try to scrollRect:by: then. - layouting_ = true; webwidget_->animate(0.0); + + // Layout any views that have not been laid out yet. The layout may result in + // more invalidation. Keep track of the fact that we are laying out views, + // because this will sometimes cause ScrollRect to be called and we don't want + // to try to scrollRect:by: then. + layouting_ = true; webwidget_->layout(); layouting_ = false; @@ -225,9 +227,14 @@ void WebWidgetHost::Paint(SkRegion& update_rgn) { const float y = client_rect.height() - r.bottom(); skia::DrawToNativeContext(canvas_.get(), context, x, y, ©_rect); } + + // Used with scheduled invalidation to maintain a consistent frame rate. + paint_last_call_ = base::TimeTicks::Now(); + if (has_invalidate_task_) + has_invalidate_task_ = false; } -void WebWidgetHost::ScheduleComposite() { +void WebWidgetHost::Invalidate() { [view_ setNeedsDisplay:YES]; } diff --git a/libcef/webwidget_host_win.cc b/libcef/webwidget_host_win.cc index 6a841e3aa..43065528a 100644 --- a/libcef/webwidget_host_win.cc +++ b/libcef/webwidget_host_win.cc @@ -273,9 +273,15 @@ void WebWidgetHost::DidInvalidateRect(const gfx::Rect& damaged_rect) { UpdatePaintRect(damaged_rect); InvalidateRect(damaged_rect); - if (!popup_ && view_) { - CefThread::PostTask(CefThread::UI, FROM_HERE, NewRunnableFunction( - &WebWidgetHost::UpdateInputMethod, view_)); + if (!popup_ && view_ && webwidget_ && input_method_is_active_ && + !has_update_input_method_task_) { + has_update_input_method_task_ = true; + + // Call UpdateInputMethod() approximately every 100ms. + CefThread::PostDelayedTask(CefThread::UI, FROM_HERE, + base::Bind(&WebWidgetHost::UpdateInputMethod, + weak_factory_.GetWeakPtr()), + 100); } } @@ -308,7 +314,7 @@ void WebWidgetHost::DidScrollRect(int dx, int dy, const gfx::Rect& clip_rect) { InvalidateRect(clip_rect); } -void WebWidgetHost::ScheduleComposite() { +void WebWidgetHost::Invalidate() { if (!webwidget_) return; WebSize size = webwidget_->size(); @@ -331,6 +337,8 @@ WebWidgetHost::WebWidgetHost() popup_(false), track_mouse_leave_(false), has_update_task_(false), + has_invalidate_task_(false), + has_update_input_method_task_(false), tooltip_view_(NULL), tooltip_showing_(false), ime_notification_(false), @@ -403,12 +411,11 @@ void WebWidgetHost::Paint() { canvas_.reset(new skia::PlatformCanvas(canvas_w_, canvas_h_, true)); } - layouting_ = true; webwidget_->animate(0.0); // This may result in more invalidation. + layouting_ = true; webwidget_->layout(); - layouting_ = false; // Paint the canvas if necessary. Allow painting to generate extra rects the @@ -519,6 +526,11 @@ void WebWidgetHost::Paint() { paint_delegate_->Paint(popup_, damaged_rects, pixels); } + + // Used with scheduled invalidation to maintain a consistent frame rate. + paint_last_call_ = base::TimeTicks::Now(); + if (has_invalidate_task_) + has_invalidate_task_ = false; } void WebWidgetHost::InvalidateRect(const gfx::Rect& rect) @@ -1091,40 +1103,27 @@ void WebWidgetHost::ImeUpdateTextInputState(WebKit::WebTextInputType type, ime_input_.UpdateCaretRect(view_, caret_rect); } -/* static */ -void WebWidgetHost::UpdateInputMethod(HWND view) +void WebWidgetHost::UpdateInputMethod() { - // Since we call this function asynchronously (via PostTask), we - // must ensure that we haven't destroyed the window by the time this - // function executes - if (!::IsWindow(view)) + REQUIRE_UIT(); + + has_update_input_method_task_ = false; + + if (!input_method_is_active_ || !webwidget_) return; - WebWidgetHost* host = FromWindow(view); - - if (!host || !host->input_method_is_active_) - return; - - if (!host->webwidget_ || !CefThread::CurrentlyOn(CefThread::UI)) - return; - - WebKit::WebTextInputType new_type = WebKit::WebTextInputTypeNone; + WebKit::WebTextInputType new_type = webwidget_->textInputType(); WebKit::WebRect new_caret_bounds; - if (host->webwidget_) { - new_type = host->webwidget_->textInputType(); - - WebKit::WebRect startRect, endRect; - if (host->webwidget_->selectionBounds(startRect, endRect)) - new_caret_bounds = endRect; - } + WebKit::WebRect startRect, endRect; + if (webwidget_->selectionBounds(startRect, endRect)) + new_caret_bounds = endRect; // Only sends text input type and caret bounds to the browser process if they // are changed. - if (host->text_input_type_ != new_type || - host->caret_bounds_ != new_caret_bounds) { - host->text_input_type_ = new_type; - host->caret_bounds_ = new_caret_bounds; - host->ImeUpdateTextInputState(new_type, new_caret_bounds); + if (text_input_type_ != new_type || caret_bounds_ != new_caret_bounds) { + text_input_type_ = new_type; + caret_bounds_ = new_caret_bounds; + ImeUpdateTextInputState(new_type, new_caret_bounds); } }