Merge revision 736 and revision 737 changes:

- Fix crashes/assertions when CefBrowserImpl is destroyed on a non-UI thread (issue #694).
- Improve the handling of invalidation/painting for off-screen rendering (issue #695).

git-svn-id: https://chromiumembedded.googlecode.com/svn/branches/1025@738 5089003a-bbd8-11dd-ad1f-f1f9622dbc98
This commit is contained in:
Marshall Greenblatt
2012-08-13 16:36:57 +00:00
parent be1fd6d3c3
commit f0e11cc5df
10 changed files with 162 additions and 234 deletions

View File

@ -771,6 +771,9 @@ void CefBrowserImpl::UIT_DestroyBrowser() {
dev_tools_agent_.reset(); dev_tools_agent_.reset();
} }
if (frame_objects_.size() > 0)
frame_objects_.clear();
// Clean up anything associated with the WebViewHost widget. // Clean up anything associated with the WebViewHost widget.
if (webviewhost_.get()) { if (webviewhost_.get()) {
if (webviewhost_->webwidget()) if (webviewhost_->webwidget())
@ -778,6 +781,13 @@ void CefBrowserImpl::UIT_DestroyBrowser() {
webviewhost_.reset(); webviewhost_.reset();
} }
delegate_.reset(NULL);
popup_delegate_.reset(NULL);
nav_controller_.reset(NULL);
if (paint_delegate_.get())
paint_delegate_.reset(NULL);
// Remove the reference to the window handle. // Remove the reference to the window handle.
UIT_ClearMainWndHandle(); UIT_ClearMainWndHandle();
@ -789,11 +799,11 @@ void CefBrowserImpl::UIT_DestroyBrowser() {
request_context_proxy_.release()); request_context_proxy_.release());
} }
// Remove the reference added in UIT_CreateBrowser().
Release();
// Remove the browser from the list maintained by the context. // Remove the browser from the list maintained by the context.
_Context->RemoveBrowser(this); _Context->RemoveBrowser(this);
// Remove the reference added in UIT_CreateBrowser().
Release();
} }
void CefBrowserImpl::UIT_CloseBrowser() { void CefBrowserImpl::UIT_CloseBrowser() {

View File

@ -57,7 +57,7 @@ BrowserNavigationController::~BrowserNavigationController() {
} }
void BrowserNavigationController::Reset() { void BrowserNavigationController::Reset() {
entries_.clear(); entries_.reset();
DiscardPendingEntry(); DiscardPendingEntry();
last_committed_entry_index_ = -1; last_committed_entry_index_ = -1;
@ -111,7 +111,8 @@ BrowserNavigationEntry* BrowserNavigationController::GetLastCommittedEntry()
const { const {
if (last_committed_entry_index_ == -1) if (last_committed_entry_index_ == -1)
return NULL; return NULL;
return entries_[last_committed_entry_index_].get(); return const_cast<BrowserNavigationEntry*>(
entries_[last_committed_entry_index_]);
} }
BrowserNavigationEntry* BrowserNavigationController::GetActiveEntry() const { BrowserNavigationEntry* BrowserNavigationController::GetActiveEntry() const {
@ -133,13 +134,14 @@ BrowserNavigationEntry* BrowserNavigationController::GetEntryAtIndex(
if (index < 0 || index >= GetEntryCount()) if (index < 0 || index >= GetEntryCount())
return NULL; return NULL;
return entries_[index].get(); return const_cast<BrowserNavigationEntry*>(entries_[index]);
} }
BrowserNavigationEntry* BrowserNavigationController::GetEntryWithPageID( BrowserNavigationEntry* BrowserNavigationController::GetEntryWithPageID(
int32 page_id) const { int32 page_id) const {
int index = GetEntryIndexWithPageID(page_id); int index = GetEntryIndexWithPageID(page_id);
return (index != -1) ? entries_[index].get() : NULL; return (index != -1) ?
const_cast<BrowserNavigationEntry*>(entries_[index]) : NULL;
} }
void BrowserNavigationController::DidNavigateToEntry( void BrowserNavigationController::DidNavigateToEntry(
@ -158,7 +160,7 @@ void BrowserNavigationController::DidNavigateToEntry(
int existing_entry_index = GetEntryIndexWithPageID(entry->GetPageID()); int existing_entry_index = GetEntryIndexWithPageID(entry->GetPageID());
BrowserNavigationEntry* existing_entry = (existing_entry_index != -1) ? BrowserNavigationEntry* existing_entry = (existing_entry_index != -1) ?
entries_[existing_entry_index].get() : NULL; entries_[existing_entry_index] : NULL;
if (!existing_entry) { if (!existing_entry) {
// No existing entry, then simply ignore this navigation! // No existing entry, then simply ignore this navigation!
DLOG(WARNING) << "ignoring navigation for page: " << entry->GetPageID(); DLOG(WARNING) << "ignoring navigation for page: " << entry->GetPageID();
@ -191,7 +193,7 @@ void BrowserNavigationController::DidNavigateToEntry(
} }
void BrowserNavigationController::DiscardPendingEntry() { void BrowserNavigationController::DiscardPendingEntry() {
if (pending_entry_index_ == -1) if (pending_entry_index_ == -1 && pending_entry_)
delete pending_entry_; delete pending_entry_;
pending_entry_ = NULL; pending_entry_ = NULL;
pending_entry_index_ = -1; pending_entry_index_ = -1;
@ -204,19 +206,19 @@ void BrowserNavigationController::InsertEntry(BrowserNavigationEntry* entry) {
if (settings.history_disabled) { if (settings.history_disabled) {
// History is disabled. Remove any existing entries. // History is disabled. Remove any existing entries.
if (entries_.size() > 0) if (entries_.size() > 0)
entries_.clear(); entries_.reset();
} else { } else {
// Prune any entry which are in front of the current entry. // Prune any entry which are in front of the current entry.
int current_size = static_cast<int>(entries_.size()); int current_size = static_cast<int>(entries_.size());
if (current_size > 0) { if (current_size > 0) {
while (last_committed_entry_index_ < (current_size - 1)) { while (last_committed_entry_index_ < (current_size - 1)) {
entries_.pop_back(); entries_.erase(entries_.end() - 1);
current_size--; current_size--;
} }
} }
} }
entries_.push_back(linked_ptr<BrowserNavigationEntry>(entry)); entries_.push_back(entry);
last_committed_entry_index_ = static_cast<int>(entries_.size()) - 1; last_committed_entry_index_ = static_cast<int>(entries_.size()) - 1;
UpdateMaxPageID(); UpdateMaxPageID();
} }
@ -234,7 +236,7 @@ void BrowserNavigationController::NavigateToPendingEntry(bool reload,
// For session history navigations only the pending_entry_index_ is set. // For session history navigations only the pending_entry_index_ is set.
if (!pending_entry_) { if (!pending_entry_) {
DCHECK_NE(pending_entry_index_, -1); DCHECK_NE(pending_entry_index_, -1);
pending_entry_ = entries_[pending_entry_index_].get(); pending_entry_ = entries_[pending_entry_index_];
} }
if (browser_->UIT_Navigate(*pending_entry_, reload, ignoreCache)) { if (browser_->UIT_Navigate(*pending_entry_, reload, ignoreCache)) {

View File

@ -11,8 +11,8 @@
#include <vector> #include <vector>
#include "base/basictypes.h" #include "base/basictypes.h"
#include "base/memory/linked_ptr.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/memory/scoped_vector.h"
#include "googleurl/src/gurl.h" #include "googleurl/src/gurl.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebDataSource.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebDataSource.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebHTTPBody.h" #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebHTTPBody.h"
@ -187,8 +187,7 @@ class BrowserNavigationController {
void UpdateMaxPageID(); void UpdateMaxPageID();
// List of NavigationEntry for this tab // List of NavigationEntry for this tab
typedef std::vector< linked_ptr<BrowserNavigationEntry> > NavigationEntryList; typedef ScopedVector<BrowserNavigationEntry> NavigationEntryList;
typedef NavigationEntryList::iterator NavigationEntryListIterator;
NavigationEntryList entries_; NavigationEntryList entries_;
// An entry we haven't gotten a response for yet. This will be discarded // An entry we haven't gotten a response for yet. This will be discarded
@ -210,4 +209,3 @@ class BrowserNavigationController {
}; };
#endif // CEF_LIBCEF_BROWSER_NAVIGATION_CONTROLLER_H_ #endif // CEF_LIBCEF_BROWSER_NAVIGATION_CONTROLLER_H_

View File

@ -1225,8 +1225,8 @@ void BrowserWebViewDelegate::UpdateSessionHistory(WebFrame* frame) {
if (page_id_ == -1) if (page_id_ == -1)
return; return;
BrowserNavigationEntry* entry = static_cast<BrowserNavigationEntry*>( BrowserNavigationEntry* entry =
browser_->UIT_GetNavigationController()->GetEntryWithPageID(page_id_)); browser_->UIT_GetNavigationController()->GetEntryWithPageID(page_id_);
if (!entry) if (!entry)
return; return;

View File

@ -11,6 +11,7 @@
#include "base/logging.h" #include "base/logging.h"
#include "base/string_util.h" #include "base/string_util.h"
#include "base/threading/non_thread_safe.h"
#include "base/utf_string_conversions.h" #include "base/utf_string_conversions.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebAttribute.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebAttribute.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
@ -42,7 +43,8 @@ namespace {
// Wrapper implementation for WebDOMEventListener. // Wrapper implementation for WebDOMEventListener.
class CefDOMEventListenerWrapper : public WebDOMEventListener, class CefDOMEventListenerWrapper : public WebDOMEventListener,
public CefTrackNode { public CefTrackNode,
public base::NonThreadSafe {
public: public:
CefDOMEventListenerWrapper(CefBrowserImpl* browser, WebFrame* frame, CefDOMEventListenerWrapper(CefBrowserImpl* browser, WebFrame* frame,
CefRefPtr<CefDOMEventListener> listener) CefRefPtr<CefDOMEventListener> listener)
@ -53,7 +55,6 @@ class CefDOMEventListenerWrapper : public WebDOMEventListener,
browser->UIT_AddFrameObject(frame, this); browser->UIT_AddFrameObject(frame, this);
} }
virtual ~CefDOMEventListenerWrapper() { virtual ~CefDOMEventListenerWrapper() {
REQUIRE_UIT();
} }
virtual void handleEvent(const WebDOMEvent& event) { virtual void handleEvent(const WebDOMEvent& event) {

View File

@ -6,6 +6,7 @@
#include "libcef/cef_thread.h" #include "libcef/cef_thread.h"
#include "base/bind.h" #include "base/bind.h"
#include "base/logging.h"
#include "base/message_loop.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/platform/WebSize.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebWidget.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebWidget.h"
@ -15,26 +16,18 @@ using WebKit::WebSize;
void WebWidgetHost::ScheduleComposite() { void WebWidgetHost::ScheduleComposite() {
if (has_invalidate_task_) if (invalidate_timer_.IsRunning())
return; return;
has_invalidate_task_ = true;
// Try to paint at 60fps. // Try to paint at 60fps.
static int64 kDesiredRate = 16; static int64 kDesiredRate = 16;
base::TimeDelta delta = base::TimeTicks::Now() - paint_last_call_; // Maintain the desired rate.
int64 actualRate = delta.InMilliseconds(); invalidate_timer_.Start(
if (actualRate >= kDesiredRate) { FROM_HERE,
// Can't keep up so run as fast as possible. base::TimeDelta::FromMilliseconds(kDesiredRate),
MessageLoop::current()->PostTask(FROM_HERE, this,
base::Bind(&WebWidgetHost::Invalidate, weak_factory_.GetWeakPtr())); &WebWidgetHost::Invalidate);
} else {
// Maintain the desired rate.
MessageLoop::current()->PostDelayedTask(FROM_HERE,
base::Bind(&WebWidgetHost::Invalidate, weak_factory_.GetWeakPtr()),
kDesiredRate - actualRate);
}
} }
void WebWidgetHost::ScheduleAnimation() { void WebWidgetHost::ScheduleAnimation() {
@ -105,9 +98,19 @@ gfx::PluginWindowHandle WebWidgetHost::GetWindowedPluginAt(int x, int y) {
return gfx::kNullPluginWindow; 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() { void WebWidgetHost::DoPaint() {
if (MessageLoop::current()->IsIdle()) { if (MessageLoop::current()->IsIdle()) {
has_update_task_ = false;
// Paint to the delegate. // Paint to the delegate.
#if defined(OS_MACOSX) #if defined(OS_MACOSX)
SkRegion region; SkRegion region;
@ -117,7 +120,6 @@ void WebWidgetHost::DoPaint() {
#endif #endif
} else { } else {
// Try again later. // Try again later.
CefThread::PostTask(CefThread::UI, FROM_HERE, SchedulePaintTimer();
base::Bind(&WebWidgetHost::DoPaint, weak_factory_.GetWeakPtr()));
} }
} }

View File

@ -15,8 +15,8 @@
#include "base/basictypes.h" #include "base/basictypes.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/time.h" #include "base/time.h"
#include "base/timer.h"
#include "skia/ext/platform_canvas.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/WebInputEvent.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebRect.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); void MoveWindowedPlugin(const webkit::npapi::WebPluginGeometry& geometry);
gfx::PluginWindowHandle GetWindowedPluginAt(int x, int y); 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; } void set_popup(bool popup) { popup_ = popup; }
bool popup() { return popup_; } bool popup() { return popup_; }
@ -151,6 +147,11 @@ class WebWidgetHost {
protected: protected:
WebWidgetHost(); 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) #if defined(OS_WIN)
// Per-class wndproc. Returns true if the event should be swallowed. // Per-class wndproc. Returns true if the event should be swallowed.
virtual bool WndProc(UINT message, WPARAM wparam, LPARAM lparam); virtual bool WndProc(UINT message, WPARAM wparam, LPARAM lparam);
@ -173,9 +174,8 @@ class WebWidgetHost {
LRESULT OnImeEndComposition(UINT message, WPARAM wparam, LPARAM lparam, LRESULT OnImeEndComposition(UINT message, WPARAM wparam, LPARAM lparam,
BOOL& handled); BOOL& handled);
void OnInputLangChange(DWORD character_set, HKL input_language_id); void OnInputLangChange(DWORD character_set, HKL input_language_id);
void ImeUpdateTextInputState(WebKit::WebTextInputType type, void UpdateImeInputState();
const gfx::Rect& caret_rect); void ToggleImeTimer();
void UpdateInputMethod();
#elif defined(OS_MACOSX) #elif defined(OS_MACOSX)
// These need to be called from a non-subclass, so they need to be public. // These need to be called from a non-subclass, so they need to be public.
public: public:
@ -237,20 +237,18 @@ class WebWidgetHost {
gfx::Rect paint_rect_; gfx::Rect paint_rect_;
#endif #endif
// True if an update task is pending when window rendering is disabled. // Used to coalesce DidInvalidateRect() events into a single DoPaint() call.
bool has_update_task_; // Used when window rendering is disabled.
base::OneShotTimer<WebWidgetHost> paint_timer_;
// True if an invalidate task is pending due to the Schedule*() methods. // Used to coalesce Schedule*() events into a single Invalidate() call.
bool has_invalidate_task_; base::OneShotTimer<WebWidgetHost> invalidate_timer_;
#if defined(OS_WIN) #if defined(OS_WIN)
// True if an update input method task is pending due to DidInvalidateRect(). // Used to call UpdateImeInputState() while IME is active.
bool has_update_input_method_task_; base::RepeatingTimer<WebWidgetHost> ime_timer_;
#endif #endif
// When the Paint() method last completed.
base::TimeTicks paint_last_call_;
// Redraw rectangle requested by an explicit call to CefBrowser::Invalidate(). // Redraw rectangle requested by an explicit call to CefBrowser::Invalidate().
gfx::Rect redraw_rect_; gfx::Rect redraw_rect_;
@ -269,20 +267,16 @@ class WebWidgetHost {
// Wrapper class for IME input. // Wrapper class for IME input.
ui::ImeInput ime_input_; ui::ImeInput ime_input_;
// Represents whether or not this browser process is receiving status // Represents whether or not this browser process is receiving status messages
// messages about the focused edit control from a renderer process. // about the focused edit control from a renderer process.
bool ime_notification_; bool ime_notification_;
// Represents whether or not the IME of a browser process is active. // Stores the current text input type.
bool input_method_is_active_;
// Stores the current text input type received by ImeUpdateTextInputState()
// method.
WebKit::WebTextInputType text_input_type_; WebKit::WebTextInputType text_input_type_;
// Stores the current caret bounds of input focus. // Stores the current caret bounds of input focus.
WebKit::WebRect caret_bounds_; WebKit::WebRect caret_bounds_;
#endif #endif // OS_WIN
#if defined(OS_MACOSX) #if defined(OS_MACOSX)
int mouse_modifiers_; int mouse_modifiers_;
@ -299,9 +293,6 @@ class WebWidgetHost {
bool painting_; bool painting_;
bool layouting_; bool layouting_;
private:
base::WeakPtrFactory<WebWidgetHost> weak_factory_;
}; };
#endif // CEF_LIBCEF_WEBWIDGET_HOST_H_ #endif // CEF_LIBCEF_WEBWIDGET_HOST_H_

View File

@ -309,10 +309,7 @@ WebWidgetHost::WebWidgetHost()
webwidget_(NULL), webwidget_(NULL),
canvas_w_(0), canvas_w_(0),
canvas_h_(0), canvas_h_(0),
popup_(false), popup_(false) {
has_update_task_(false),
has_invalidate_task_(false),
ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
set_painting(false); set_painting(false);
} }
@ -403,11 +400,6 @@ void WebWidgetHost::Paint() {
cairo_destroy(cairo_drawable); cairo_destroy(cairo_drawable);
gdk_window_end_paint(window); 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) { void WebWidgetHost::SetTooltipText(const CefString& tooltip_text) {

View File

@ -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/WebInputEvent.h"
#import "third_party/WebKit/Source/WebKit/chromium/public/WebPopupMenu.h" #import "third_party/WebKit/Source/WebKit/chromium/public/WebPopupMenu.h"
#import "third_party/WebKit/Source/WebKit/chromium/public/WebScreenInfo.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/WebKit/Source/WebKit/chromium/public/platform/WebSize.h"
#import "third_party/skia/include/core/SkRegion.h" #import "third_party/skia/include/core/SkRegion.h"
#import "ui/gfx/rect.h" #import "ui/gfx/rect.h"
@ -82,12 +82,9 @@ WebWidgetHost::WebWidgetHost()
canvas_w_(0), canvas_w_(0),
canvas_h_(0), canvas_h_(0),
popup_(false), popup_(false),
has_update_task_(false),
has_invalidate_task_(false),
mouse_modifiers_(0), mouse_modifiers_(0),
painting_(false), painting_(false),
layouting_(false), layouting_(false) {
ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
set_painting(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 client_rect(width, height);
const gfx::Rect damaged_rect_in_client = client_rect.Intersect(damaged_rect); 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_) { if (view_) {
NSRect cocoa_rect = NSRectFromCGRect(damaged_rect_in_client.ToCGRect()); NSRect cocoa_rect = NSRectFromCGRect(damaged_rect_in_client.ToCGRect());
cocoa_rect.origin.y = client_rect.height() - NSMaxY(cocoa_rect); cocoa_rect.origin.y = client_rect.height() - NSMaxY(cocoa_rect);
[view_ setNeedsDisplayInRect:cocoa_rect]; [view_ setNeedsDisplayInRect:cocoa_rect];
} else { } else {
// Don't post a paint task if this invalidation occurred during layout or SchedulePaintTimer();
// 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()));
}
}
} }
} }
@ -309,6 +299,9 @@ void WebWidgetHost::Paint(SkRegion& update_rgn) {
skia::DrawToNativeContext(canvas_.get(), context, x, y, &copy_rect); skia::DrawToNativeContext(canvas_.get(), context, x, y, &copy_rect);
} }
} else { } else {
if (damaged_rgn.isEmpty())
return;
// Paint to the delegate. // Paint to the delegate.
DCHECK(paint_delegate_); DCHECK(paint_delegate_);
const SkBitmap& bitmap = canvas_->getDevice()->accessBitmap(false); const SkBitmap& bitmap = canvas_->getDevice()->accessBitmap(false);
@ -325,20 +318,14 @@ void WebWidgetHost::Paint(SkRegion& update_rgn) {
paint_delegate_->Paint(popup_, damaged_rects, pixels); 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() { void WebWidgetHost::Invalidate() {
if (view_) { if (!webwidget_)
[view_ setNeedsDisplay:YES]; return;
} else if (webwidget_) {
WebSize size = webwidget_->size(); WebSize size = webwidget_->size();
InvalidateRect(gfx::Rect(0, 0, size.width, size.height)); InvalidateRect(gfx::Rect(0, 0, size.width, size.height));
}
} }
void WebWidgetHost::SetTooltipText(const CefString& tooltip_text) { void WebWidgetHost::SetTooltipText(const CefString& tooltip_text) {
@ -349,17 +336,7 @@ void WebWidgetHost::InvalidateRect(const gfx::Rect& rect) {
if (rect.IsEmpty()) if (rect.IsEmpty())
return; return;
if (!view_) { DidInvalidateRect(rect);
// 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();
}
} }
bool WebWidgetHost::GetImage(int width, int height, void* rgba_buffer) { bool WebWidgetHost::GetImage(int width, int height, void* rgba_buffer) {

View File

@ -102,7 +102,6 @@ WebWidgetHost* WebWidgetHost::Create(HWND parent_view,
0, 0, 0, 0, 0, 0, 0, 0,
parent_view, NULL, GetModuleHandle(NULL), parent_view, NULL, GetModuleHandle(NULL),
NULL); NULL);
ui::SetWindowUserData(host->view_, host); ui::SetWindowUserData(host->view_, host);
} else { } else {
host->paint_delegate_ = paint_delegate; 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) { 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 const gfx::Rect damaged_rect_in_client = client_rect.Intersect(damaged_rect);
// to invalidating the scroll rect. if (damaged_rect_in_client.IsEmpty())
UpdatePaintRect(damaged_rect); return;
InvalidateRect(damaged_rect);
if (!popup_ && view_ && webwidget_ && input_method_is_active_ && UpdatePaintRect(damaged_rect_in_client);
!has_update_input_method_task_) {
has_update_input_method_task_ = true;
// Call UpdateInputMethod() approximately every 100ms. if (view_) {
CefThread::PostDelayedTask(CefThread::UI, FROM_HERE, RECT r = damaged_rect_in_client.ToRECT();
base::Bind(&WebWidgetHost::UpdateInputMethod, ::InvalidateRect(view_, &r, FALSE);
weak_factory_.GetWeakPtr()), } else {
100); 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 // Invalidate the scroll rect. It will be drawn from the canvas bitmap on the
// next WM_PAINT call. // next WM_PAINT call.
InvalidateRect(clip_rect); RECT r = clip_rect.ToRECT();
::InvalidateRect(view_, &r, FALSE);
} }
void WebWidgetHost::Invalidate() { void WebWidgetHost::Invalidate() {
@ -339,16 +338,11 @@ WebWidgetHost::WebWidgetHost()
canvas_h_(0), canvas_h_(0),
popup_(false), popup_(false),
track_mouse_leave_(false), track_mouse_leave_(false),
has_update_task_(false),
has_invalidate_task_(false),
has_update_input_method_task_(false),
tooltip_view_(NULL), tooltip_view_(NULL),
tooltip_showing_(false), tooltip_showing_(false),
ime_notification_(false), ime_notification_(false),
input_method_is_active_(false),
layouting_(false), layouting_(false),
text_input_type_(WebKit::WebTextInputTypeNone), text_input_type_(WebKit::WebTextInputTypeNone) {
ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
set_painting(false); set_painting(false);
} }
@ -515,6 +509,9 @@ void WebWidgetHost::Paint() {
// Draw children // Draw children
UpdateWindow(view_); UpdateWindow(view_);
} else { } else {
if (damaged_rgn.isEmpty())
return;
// Paint to the delegate. // Paint to the delegate.
DCHECK(paint_delegate_); DCHECK(paint_delegate_);
const SkBitmap& bitmap = canvas_->getDevice()->accessBitmap(false); const SkBitmap& bitmap = canvas_->getDevice()->accessBitmap(false);
@ -531,30 +528,13 @@ void WebWidgetHost::Paint() {
paint_delegate_->Paint(popup_, damaged_rects, pixels); 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) { void WebWidgetHost::InvalidateRect(const gfx::Rect& rect) {
if (rect.IsEmpty()) if (rect.IsEmpty())
return; return;
if (view_) { DidInvalidateRect(rect);
// 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()));
}
}
} }
bool WebWidgetHost::GetImage(int width, int height, void* buffer) { bool WebWidgetHost::GetImage(int width, int height, void* buffer) {
@ -947,26 +927,22 @@ LRESULT WebWidgetHost::OnImeSetContext(UINT message, WPARAM wparam,
if (!webwidget_) if (!webwidget_)
return 0; return 0;
// We need status messages about the focused input control from a // We need to start the IME timer when:
// renderer process when:
// * the current input context has IMEs, and; // * the current input context has IMEs, and;
// * an application is activated. // * an application is activated.
// This seems to tell we should also check if the current input context has // This seems to indicate that we should also check if the current input
// IMEs before sending a request, however, this WM_IME_SETCONTEXT is // context has IMEs before starting the timer, however, this WM_IME_SETCONTEXT
// fortunately sent to an application only while the input context has IMEs. // is fortunately sent to an application only while the input context has
// Therefore, we just start/stop status messages according to the activation // IMEs. Therefore, we just start/stop the timer according to the activation
// status of this application without checks. // status of this application without checks.
bool activated = (wparam == TRUE); ime_notification_ = (wparam == TRUE);
if (webwidget_) {
input_method_is_active_ = activated;
ime_notification_ = activated;
}
if (ime_notification_) if (ime_notification_)
ime_input_.CreateImeWindow(view_); ime_input_.CreateImeWindow(view_);
ime_input_.CleanupComposition(view_); ime_input_.CleanupComposition(view_);
ime_input_.SetImeWindowStyle(view_, message, wparam, lparam, &handled); ime_input_.SetImeWindowStyle(view_, message, wparam, lparam, &handled);
ToggleImeTimer();
return 0; return 0;
} }
@ -1000,7 +976,7 @@ LRESULT WebWidgetHost::OnImeComposition(UINT message, WPARAM wparam,
ui_CompositionUnderline__WebKit_WebCompositionUnderline_diff); ui_CompositionUnderline__WebKit_WebCompositionUnderline_diff);
// Retrieve the result string and its attributes of the ongoing composition // 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; ui::CompositionText composition;
if (ime_input_.GetResult(view_, lparam, &composition.text)) { if (ime_input_.GetResult(view_, lparam, &composition.text)) {
webwidget_->setComposition(composition.text, webwidget_->setComposition(composition.text,
@ -1014,14 +990,12 @@ LRESULT WebWidgetHost::OnImeComposition(UINT message, WPARAM wparam,
// by the start of another composition. // by the start of another composition.
} }
// Retrieve the composition string and its attributes of the ongoing // 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)) { if (ime_input_.GetComposition(view_, lparam, &composition)) {
// TODO(suzhe): due to a bug of webkit, we can't use selection range with // 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 string. See: https://bugs.webkit.org/show_bug.cgi?id=37788
composition.selection = ui::Range(composition.selection.end()); composition.selection = ui::Range(composition.selection.end());
// TODO(suzhe): convert both renderer_host and renderer to use
// ui::CompositionText.
const std::vector<WebKit::WebCompositionUnderline>& underlines = const std::vector<WebKit::WebCompositionUnderline>& underlines =
reinterpret_cast<const std::vector<WebKit::WebCompositionUnderline>&>( reinterpret_cast<const std::vector<WebKit::WebCompositionUnderline>&>(
composition.underlines); composition.underlines);
@ -1043,8 +1017,8 @@ LRESULT WebWidgetHost::OnImeEndComposition(UINT message, WPARAM wparam,
if (ime_input_.is_composing()) { if (ime_input_.is_composing()) {
// A composition has been ended while there is an ongoing composition, // A composition has been ended while there is an ongoing composition,
// i.e. the ongoing composition has been canceled. // i.e. the ongoing composition has been canceled.
// We need to reset the composition status both of the ImeInput object and // Reset the composition status of both of the ImeInput object and the
// of the renderer process. // window.
ime_input_.CancelIME(view_); ime_input_.CancelIME(view_);
ime_input_.ResetComposition(view_); ime_input_.ResetComposition(view_);
} }
@ -1056,44 +1030,32 @@ LRESULT WebWidgetHost::OnImeEndComposition(UINT message, WPARAM wparam,
void WebWidgetHost::OnInputLangChange(DWORD character_set, void WebWidgetHost::OnInputLangChange(DWORD character_set,
HKL input_language_id) { HKL input_language_id) {
if (!webwidget_)
return;
// Send the given Locale ID to the ImeInput object and retrieves whether // Send the given Locale ID to the ImeInput object and retrieves whether
// or not the current input context has IMEs. // or not the current input context has IMEs. Toggle the timer based on
// If the current input context has IMEs, a browser process has to send a // whether the current input context has IMEs.
// 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_.)
bool ime_status = ime_input_.SetInputLanguage(); bool ime_status = ime_input_.SetInputLanguage();
if (ime_status != ime_notification_ && webwidget_) { if (ime_status != ime_notification_) {
input_method_is_active_ = ime_status;
ime_notification_ = ime_status; ime_notification_ = ime_status;
ToggleImeTimer();
} }
} }
void WebWidgetHost::ImeUpdateTextInputState(WebKit::WebTextInputType type, void WebWidgetHost::UpdateImeInputState() {
const gfx::Rect& caret_rect) { 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) { if (text_input_type_ != type) {
text_input_type_ = type; text_input_type_ = type;
if (type == WebKit::WebTextInputTypeText) if (type == WebKit::WebTextInputTypeText)
@ -1102,31 +1064,24 @@ void WebWidgetHost::ImeUpdateTextInputState(WebKit::WebTextInputType type,
ime_input_.DisableIME(view_); ime_input_.DisableIME(view_);
} }
// Only update caret position if the input method is enabled. // Only update caret position if the input method is enabled and the caret
if (type == WebKit::WebTextInputTypeText) // position has changed.
ime_input_.UpdateCaretRect(view_, caret_rect); if (type == WebKit::WebTextInputTypeText && caret_bounds != caret_bounds_) {
} caret_bounds_ = caret_bounds;
ime_input_.UpdateCaretRect(view_, caret_bounds);
void WebWidgetHost::UpdateInputMethod() { }
REQUIRE_UIT(); }
has_update_input_method_task_ = false; void WebWidgetHost::ToggleImeTimer() {
if (view_ && !popup_ && ime_notification_ && webwidget_) {
if (!input_method_is_active_ || !webwidget_) if (!ime_timer_.IsRunning()) {
return; ime_timer_.Start(
FROM_HERE,
WebKit::WebTextInputType new_type = webwidget_->textInputType(); base::TimeDelta::FromMilliseconds(200),
WebKit::WebRect new_caret_bounds; this,
&WebWidgetHost::UpdateImeInputState);
WebKit::WebRect startRect, endRect; }
if (webwidget_->selectionBounds(startRect, endRect)) } else if (ime_timer_.IsRunning()) {
new_caret_bounds = endRect; ime_timer_.Stop();
// 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);
} }
} }