diff --git a/cef1/libcef/webwidget_host.cc b/cef1/libcef/webwidget_host.cc index a1ddc399f..5b03d0236 100644 --- a/cef1/libcef/webwidget_host.cc +++ b/cef1/libcef/webwidget_host.cc @@ -6,6 +6,7 @@ #include "libcef/cef_thread.h" #include "base/bind.h" +#include "base/logging.h" #include "base/message_loop.h" #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebSize.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebWidget.h" @@ -15,26 +16,18 @@ using WebKit::WebSize; void WebWidgetHost::ScheduleComposite() { - if (has_invalidate_task_) + if (invalidate_timer_.IsRunning()) 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()), - base::TimeDelta::FromMilliseconds(kDesiredRate - actualRate)); - } + // Maintain the desired rate. + invalidate_timer_.Start( + FROM_HERE, + base::TimeDelta::FromMilliseconds(kDesiredRate), + this, + &WebWidgetHost::Invalidate); } void WebWidgetHost::ScheduleAnimation() { @@ -105,9 +98,19 @@ gfx::PluginWindowHandle WebWidgetHost::GetWindowedPluginAt(int x, int y) { return gfx::kNullPluginWindow; } +void WebWidgetHost::SchedulePaintTimer() { + if (layouting_ || paint_timer_.IsRunning()) + return; + + paint_timer_.Start( + FROM_HERE, + base::TimeDelta::FromMilliseconds(0), // Fire immediately. + this, + &WebWidgetHost::DoPaint); +} + void WebWidgetHost::DoPaint() { if (MessageLoop::current()->IsIdle()) { - has_update_task_ = false; // Paint to the delegate. #if defined(OS_MACOSX) SkRegion region; @@ -117,7 +120,6 @@ void WebWidgetHost::DoPaint() { #endif } else { // Try again later. - CefThread::PostTask(CefThread::UI, FROM_HERE, - base::Bind(&WebWidgetHost::DoPaint, weak_factory_.GetWeakPtr())); + SchedulePaintTimer(); } } diff --git a/cef1/libcef/webwidget_host.h b/cef1/libcef/webwidget_host.h index b5dfec428..369ee36ff 100644 --- a/cef1/libcef/webwidget_host.h +++ b/cef1/libcef/webwidget_host.h @@ -15,8 +15,8 @@ #include "base/basictypes.h" #include "base/memory/scoped_ptr.h" -#include "base/memory/weak_ptr.h" #include "base/time.h" +#include "base/timer.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/platform/WebRect.h" @@ -139,10 +139,6 @@ class WebWidgetHost { void MoveWindowedPlugin(const webkit::npapi::WebPluginGeometry& geometry); gfx::PluginWindowHandle GetWindowedPluginAt(int x, int y); - // If window rendering is disabled paint messages are generated after all - // other pending messages have been processed. - void DoPaint(); - void set_popup(bool popup) { popup_ = popup; } bool popup() { return popup_; } @@ -151,6 +147,11 @@ class WebWidgetHost { protected: WebWidgetHost(); + // If window rendering is disabled paint messages are generated after all + // other pending messages have been processed. + void SchedulePaintTimer(); + void DoPaint(); + #if defined(OS_WIN) // Per-class wndproc. Returns true if the event should be swallowed. virtual bool WndProc(UINT message, WPARAM wparam, LPARAM lparam); @@ -173,9 +174,8 @@ class WebWidgetHost { LRESULT OnImeEndComposition(UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled); void OnInputLangChange(DWORD character_set, HKL input_language_id); - void ImeUpdateTextInputState(WebKit::WebTextInputType type, - const gfx::Rect& caret_rect); - void UpdateInputMethod(); + void UpdateImeInputState(); + void ToggleImeTimer(); #elif defined(OS_MACOSX) // These need to be called from a non-subclass, so they need to be public. public: @@ -237,20 +237,18 @@ class WebWidgetHost { gfx::Rect paint_rect_; #endif - // True if an update task is pending when window rendering is disabled. - bool has_update_task_; + // Used to coalesce DidInvalidateRect() events into a single DoPaint() call. + // Used when window rendering is disabled. + base::OneShotTimer paint_timer_; - // True if an invalidate task is pending due to the Schedule*() methods. - bool has_invalidate_task_; + // Used to coalesce Schedule*() events into a single Invalidate() call. + base::OneShotTimer invalidate_timer_; #if defined(OS_WIN) - // True if an update input method task is pending due to DidInvalidateRect(). - bool has_update_input_method_task_; + // Used to call UpdateImeInputState() while IME is active. + base::RepeatingTimer ime_timer_; #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_; @@ -269,20 +267,16 @@ class WebWidgetHost { // Wrapper class for IME input. ui::ImeInput ime_input_; - // Represents whether or not this browser process is receiving status - // messages about the focused edit control from a renderer process. + // Represents whether or not this browser process is receiving status messages + // about the focused edit control from a renderer process. bool ime_notification_; - // Represents whether or not the IME of a browser process is active. - bool input_method_is_active_; - - // Stores the current text input type received by ImeUpdateTextInputState() - // method. + // Stores the current text input type. WebKit::WebTextInputType text_input_type_; // Stores the current caret bounds of input focus. WebKit::WebRect caret_bounds_; -#endif +#endif // OS_WIN #if defined(OS_MACOSX) int mouse_modifiers_; @@ -299,9 +293,6 @@ class WebWidgetHost { bool painting_; bool layouting_; - - private: - base::WeakPtrFactory weak_factory_; }; #endif // CEF_LIBCEF_WEBWIDGET_HOST_H_ diff --git a/cef1/libcef/webwidget_host_gtk.cc b/cef1/libcef/webwidget_host_gtk.cc index 38b1658b9..9d07401e3 100644 --- a/cef1/libcef/webwidget_host_gtk.cc +++ b/cef1/libcef/webwidget_host_gtk.cc @@ -309,10 +309,7 @@ WebWidgetHost::WebWidgetHost() webwidget_(NULL), canvas_w_(0), canvas_h_(0), - popup_(false), - has_update_task_(false), - has_invalidate_task_(false), - ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { + popup_(false) { set_painting(false); } @@ -403,11 +400,6 @@ 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/cef1/libcef/webwidget_host_mac.mm b/cef1/libcef/webwidget_host_mac.mm index 4de7cf7dd..3d3b255e2 100644 --- a/cef1/libcef/webwidget_host_mac.mm +++ b/cef1/libcef/webwidget_host_mac.mm @@ -22,7 +22,7 @@ MSVC_POP_WARNING(); #import "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h" #import "third_party/WebKit/Source/WebKit/chromium/public/WebPopupMenu.h" #import "third_party/WebKit/Source/WebKit/chromium/public/WebScreenInfo.h" -#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" +#import "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" #import "third_party/WebKit/Source/WebKit/chromium/public/platform/WebSize.h" #import "third_party/skia/include/core/SkRegion.h" #import "ui/gfx/rect.h" @@ -82,12 +82,9 @@ WebWidgetHost::WebWidgetHost() canvas_w_(0), canvas_h_(0), popup_(false), - has_update_task_(false), - has_invalidate_task_(false), mouse_modifiers_(0), painting_(false), - layouting_(false), - ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { + layouting_(false) { set_painting(false); } @@ -100,24 +97,17 @@ void WebWidgetHost::DidInvalidateRect(const gfx::Rect& damaged_rect) { const gfx::Rect client_rect(width, height); const gfx::Rect damaged_rect_in_client = client_rect.Intersect(damaged_rect); + if (damaged_rect_in_client.IsEmpty()) + return; - if (!damaged_rect_in_client.IsEmpty()) { - UpdatePaintRect(damaged_rect_in_client); + UpdatePaintRect(damaged_rect_in_client); - if (view_) { - NSRect cocoa_rect = NSRectFromCGRect(damaged_rect_in_client.ToCGRect()); - cocoa_rect.origin.y = client_rect.height() - NSMaxY(cocoa_rect); - [view_ setNeedsDisplayInRect:cocoa_rect]; - } else { - // Don't post a paint task if this invalidation occurred during layout or - // if a paint task is already pending. Paint() will be called by - // DoPaint(). - if (!layouting_ && !has_update_task_) { - has_update_task_ = true; - CefThread::PostTask(CefThread::UI, FROM_HERE, - base::Bind(&WebWidgetHost::DoPaint, weak_factory_.GetWeakPtr())); - } - } + if (view_) { + NSRect cocoa_rect = NSRectFromCGRect(damaged_rect_in_client.ToCGRect()); + cocoa_rect.origin.y = client_rect.height() - NSMaxY(cocoa_rect); + [view_ setNeedsDisplayInRect:cocoa_rect]; + } else { + SchedulePaintTimer(); } } @@ -309,6 +299,9 @@ void WebWidgetHost::Paint(SkRegion& update_rgn) { skia::DrawToNativeContext(canvas_.get(), context, x, y, ©_rect); } } else { + if (damaged_rgn.isEmpty()) + return; + // Paint to the delegate. DCHECK(paint_delegate_); const SkBitmap& bitmap = canvas_->getDevice()->accessBitmap(false); @@ -325,20 +318,14 @@ void WebWidgetHost::Paint(SkRegion& update_rgn) { 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::Invalidate() { - if (view_) { - [view_ setNeedsDisplay:YES]; - } else if (webwidget_) { - WebSize size = webwidget_->size(); - InvalidateRect(gfx::Rect(0, 0, size.width, size.height)); - } + if (!webwidget_) + return; + + WebSize size = webwidget_->size(); + InvalidateRect(gfx::Rect(0, 0, size.width, size.height)); } void WebWidgetHost::SetTooltipText(const CefString& tooltip_text) { @@ -349,17 +336,7 @@ void WebWidgetHost::InvalidateRect(const gfx::Rect& rect) { if (rect.IsEmpty()) return; - if (!view_) { - // Don't post a paint task if this invalidation occurred during layout or if - // a paint task is already pending. Paint() will be called by DoPaint(). - if (!layouting_ && !has_update_task_) { - has_update_task_ = true; - CefThread::PostTask(CefThread::UI, FROM_HERE, - base::Bind(&WebWidgetHost::DoPaint, weak_factory_.GetWeakPtr())); - } - } else { - NOTIMPLEMENTED(); - } + DidInvalidateRect(rect); } bool WebWidgetHost::GetImage(int width, int height, void* rgba_buffer) { diff --git a/cef1/libcef/webwidget_host_win.cc b/cef1/libcef/webwidget_host_win.cc index 023ac1c22..cb16771ea 100644 --- a/cef1/libcef/webwidget_host_win.cc +++ b/cef1/libcef/webwidget_host_win.cc @@ -102,7 +102,6 @@ WebWidgetHost* WebWidgetHost::Create(HWND parent_view, 0, 0, 0, 0, parent_view, NULL, GetModuleHandle(NULL), NULL); - ui::SetWindowUserData(host->view_, host); } else { host->paint_delegate_ = paint_delegate; @@ -268,22 +267,21 @@ LRESULT CALLBACK WebWidgetHost::WndProc(HWND hwnd, UINT message, WPARAM wparam, } void WebWidgetHost::DidInvalidateRect(const gfx::Rect& damaged_rect) { - DLOG_IF(WARNING, painting_) << "unexpected invalidation while painting"; + int width, height; + GetSize(width, height); + const gfx::Rect client_rect(width, height); - // If this invalidate overlaps with a pending scroll then we have to downgrade - // to invalidating the scroll rect. - UpdatePaintRect(damaged_rect); - InvalidateRect(damaged_rect); + const gfx::Rect damaged_rect_in_client = client_rect.Intersect(damaged_rect); + if (damaged_rect_in_client.IsEmpty()) + return; - if (!popup_ && view_ && webwidget_ && input_method_is_active_ && - !has_update_input_method_task_) { - has_update_input_method_task_ = true; + UpdatePaintRect(damaged_rect_in_client); - // Call UpdateInputMethod() approximately every 100ms. - CefThread::PostDelayedTask(CefThread::UI, FROM_HERE, - base::Bind(&WebWidgetHost::UpdateInputMethod, - weak_factory_.GetWeakPtr()), - base::TimeDelta::FromMilliseconds(100)); + if (view_) { + RECT r = damaged_rect_in_client.ToRECT(); + ::InvalidateRect(view_, &r, FALSE); + } else { + SchedulePaintTimer(); } } @@ -314,7 +312,8 @@ void WebWidgetHost::DidScrollRect(int dx, int dy, const gfx::Rect& clip_rect) { // Invalidate the scroll rect. It will be drawn from the canvas bitmap on the // next WM_PAINT call. - InvalidateRect(clip_rect); + RECT r = clip_rect.ToRECT(); + ::InvalidateRect(view_, &r, FALSE); } void WebWidgetHost::Invalidate() { @@ -339,16 +338,11 @@ WebWidgetHost::WebWidgetHost() canvas_h_(0), 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), - input_method_is_active_(false), layouting_(false), - text_input_type_(WebKit::WebTextInputTypeNone), - ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { + text_input_type_(WebKit::WebTextInputTypeNone) { set_painting(false); } @@ -515,6 +509,9 @@ void WebWidgetHost::Paint() { // Draw children UpdateWindow(view_); } else { + if (damaged_rgn.isEmpty()) + return; + // Paint to the delegate. DCHECK(paint_delegate_); const SkBitmap& bitmap = canvas_->getDevice()->accessBitmap(false); @@ -531,30 +528,13 @@ 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) { if (rect.IsEmpty()) return; - if (view_) { - // Let the window handle painting. - RECT r = rect.ToRECT(); - ::InvalidateRect(view_, &r, FALSE); - } else { - // Don't post a paint task if this invalidation occurred during layout or if - // a paint task is already pending. Paint() will be called by DoPaint(). - if (!layouting_ && !has_update_task_) { - has_update_task_ = true; - CefThread::PostTask(CefThread::UI, FROM_HERE, - base::Bind(&WebWidgetHost::DoPaint, weak_factory_.GetWeakPtr())); - } - } + DidInvalidateRect(rect); } bool WebWidgetHost::GetImage(int width, int height, void* buffer) { @@ -947,26 +927,22 @@ LRESULT WebWidgetHost::OnImeSetContext(UINT message, WPARAM wparam, if (!webwidget_) return 0; - // We need status messages about the focused input control from a - // renderer process when: + // We need to start the IME timer when: // * the current input context has IMEs, and; // * an application is activated. - // This seems to tell we should also check if the current input context has - // IMEs before sending a request, however, this WM_IME_SETCONTEXT is - // fortunately sent to an application only while the input context has IMEs. - // Therefore, we just start/stop status messages according to the activation + // This seems to indicate that we should also check if the current input + // context has IMEs before starting the timer, however, this WM_IME_SETCONTEXT + // is fortunately sent to an application only while the input context has + // IMEs. Therefore, we just start/stop the timer according to the activation // status of this application without checks. - bool activated = (wparam == TRUE); - if (webwidget_) { - input_method_is_active_ = activated; - ime_notification_ = activated; - } - + ime_notification_ = (wparam == TRUE); if (ime_notification_) ime_input_.CreateImeWindow(view_); ime_input_.CleanupComposition(view_); ime_input_.SetImeWindowStyle(view_, message, wparam, lparam, &handled); + + ToggleImeTimer(); return 0; } @@ -1000,7 +976,7 @@ LRESULT WebWidgetHost::OnImeComposition(UINT message, WPARAM wparam, ui_CompositionUnderline__WebKit_WebCompositionUnderline_diff); // Retrieve the result string and its attributes of the ongoing composition - // and send it to a renderer process. + // and send to WebKit. ui::CompositionText composition; if (ime_input_.GetResult(view_, lparam, &composition.text)) { webwidget_->setComposition(composition.text, @@ -1014,14 +990,12 @@ LRESULT WebWidgetHost::OnImeComposition(UINT message, WPARAM wparam, // by the start of another composition. } // Retrieve the composition string and its attributes of the ongoing - // composition and send it to a renderer process. + // composition and send to WebKit. if (ime_input_.GetComposition(view_, lparam, &composition)) { // TODO(suzhe): due to a bug of webkit, we can't use selection range with // composition string. See: https://bugs.webkit.org/show_bug.cgi?id=37788 composition.selection = ui::Range(composition.selection.end()); - // TODO(suzhe): convert both renderer_host and renderer to use - // ui::CompositionText. const std::vector& underlines = reinterpret_cast&>( composition.underlines); @@ -1043,8 +1017,8 @@ LRESULT WebWidgetHost::OnImeEndComposition(UINT message, WPARAM wparam, if (ime_input_.is_composing()) { // A composition has been ended while there is an ongoing composition, // i.e. the ongoing composition has been canceled. - // We need to reset the composition status both of the ImeInput object and - // of the renderer process. + // Reset the composition status of both of the ImeInput object and the + // window. ime_input_.CancelIME(view_); ime_input_.ResetComposition(view_); } @@ -1056,44 +1030,32 @@ LRESULT WebWidgetHost::OnImeEndComposition(UINT message, WPARAM wparam, void WebWidgetHost::OnInputLangChange(DWORD character_set, HKL input_language_id) { + if (!webwidget_) + return; + // Send the given Locale ID to the ImeInput object and retrieves whether - // or not the current input context has IMEs. - // If the current input context has IMEs, a browser process has to send a - // request to a renderer process that it needs status messages about - // the focused edit control from the renderer process. - // On the other hand, if the current input context does not have IMEs, the - // browser process also has to send a request to the renderer process that - // it does not need the status messages any longer. - // To minimize the number of this notification request, we should check if - // the browser process is actually retrieving the status messages (this - // state is stored in ime_notification_) and send a request only if the - // browser process has to update this status, its details are listed below: - // * If a browser process is not retrieving the status messages, - // (i.e. ime_notification_ == false), - // send this request only if the input context does have IMEs, - // (i.e. ime_status == true); - // When it successfully sends the request, toggle its notification status, - // (i.e.ime_notification_ = !ime_notification_ = true). - // * If a browser process is retrieving the status messages - // (i.e. ime_notification_ == true), - // send this request only if the input context does not have IMEs, - // (i.e. ime_status == false). - // When it successfully sends the request, toggle its notification status, - // (i.e.ime_notification_ = !ime_notification_ = false). - // To analyze the above actions, we can optimize them into the ones - // listed below: - // 1 Sending a request only if ime_status_ != ime_notification_, and; - // 2 Copying ime_status to ime_notification_ if it sends the request - // successfully (because Action 1 shows ime_status = !ime_notification_.) + // or not the current input context has IMEs. Toggle the timer based on + // whether the current input context has IMEs. bool ime_status = ime_input_.SetInputLanguage(); - if (ime_status != ime_notification_ && webwidget_) { - input_method_is_active_ = ime_status; + if (ime_status != ime_notification_) { ime_notification_ = ime_status; + ToggleImeTimer(); } } -void WebWidgetHost::ImeUpdateTextInputState(WebKit::WebTextInputType type, - const gfx::Rect& caret_rect) { +void WebWidgetHost::UpdateImeInputState() { + if (!webwidget_ || !ime_notification_) { + ToggleImeTimer(); + return; + } + + WebKit::WebTextInputType type = webwidget_->textInputType(); + WebKit::WebRect caret_bounds; + + WebKit::WebRect startRect, endRect; + if (webwidget_->selectionBounds(startRect, endRect)) + caret_bounds = endRect; + if (text_input_type_ != type) { text_input_type_ = type; if (type == WebKit::WebTextInputTypeText) @@ -1102,31 +1064,24 @@ void WebWidgetHost::ImeUpdateTextInputState(WebKit::WebTextInputType type, ime_input_.DisableIME(view_); } - // Only update caret position if the input method is enabled. - if (type == WebKit::WebTextInputTypeText) - ime_input_.UpdateCaretRect(view_, caret_rect); -} - -void WebWidgetHost::UpdateInputMethod() { - REQUIRE_UIT(); - - has_update_input_method_task_ = false; - - if (!input_method_is_active_ || !webwidget_) - return; - - WebKit::WebTextInputType new_type = webwidget_->textInputType(); - WebKit::WebRect new_caret_bounds; - - 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 (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); + // Only update caret position if the input method is enabled and the caret + // position has changed. + if (type == WebKit::WebTextInputTypeText && caret_bounds != caret_bounds_) { + caret_bounds_ = caret_bounds; + ime_input_.UpdateCaretRect(view_, caret_bounds); + } +} + +void WebWidgetHost::ToggleImeTimer() { + if (view_ && !popup_ && ime_notification_ && webwidget_) { + if (!ime_timer_.IsRunning()) { + ime_timer_.Start( + FROM_HERE, + base::TimeDelta::FromMilliseconds(200), + this, + &WebWidgetHost::UpdateImeInputState); + } + } else if (ime_timer_.IsRunning()) { + ime_timer_.Stop(); } }