summaryrefslogtreecommitdiffstats
path: root/chrome/renderer/render_widget.cc
diff options
context:
space:
mode:
authorinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 23:55:29 +0000
committerinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 23:55:29 +0000
commit09911bf300f1a419907a9412154760efd0b7abc3 (patch)
treef131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/renderer/render_widget.cc
parent586acc5fe142f498261f52c66862fa417c3d52d2 (diff)
downloadchromium_src-09911bf300f1a419907a9412154760efd0b7abc3.zip
chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.gz
chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.bz2
Add chrome to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/renderer/render_widget.cc')
-rw-r--r--chrome/renderer/render_widget.cc769
1 files changed, 769 insertions, 0 deletions
diff --git a/chrome/renderer/render_widget.cc b/chrome/renderer/render_widget.cc
new file mode 100644
index 0000000..c5db805
--- /dev/null
+++ b/chrome/renderer/render_widget.cc
@@ -0,0 +1,769 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "chrome/renderer/render_widget.h"
+
+#include <windows.h>
+
+#include "base/gfx/point.h"
+#include "base/gfx/size.h"
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/gfx/platform_canvas.h"
+#include "base/scoped_ptr.h"
+#include "webkit/glue/webinputevent.h"
+#include "webkit/glue/webwidget.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+namespace {
+
+// This class is used to defer calling RenderWidget::Close() while the current
+// thread is inside RenderThread::Send(), which in some cases can result in a
+// nested MessageLoop being run.
+class DeferredCloses : public Task {
+ public:
+ // Called to queue a deferred close for the given widget.
+ static void Push(RenderWidget* widget) {
+ if (!current_)
+ current_ = new DeferredCloses();
+ current_->queue_.push(widget);
+ }
+
+ // Called to trigger any deferred closes to be run.
+ static void Post() {
+ DCHECK(!RenderThread::current()->in_send());
+ if (current_) {
+ MessageLoop::current()->PostTask(FROM_HERE, current_);
+ current_ = NULL;
+ }
+ }
+
+ private:
+ virtual void Run() {
+ // Maybe we are being run from within another RenderWidget::Send call. If
+ // that is true, then we need to re-queue the widgets to be closed and try
+ // again later.
+ while (!queue_.empty()) {
+ if (RenderThread::current()->in_send()) {
+ Push(queue_.front());
+ } else {
+ queue_.front()->Close();
+ }
+ queue_.pop();
+ }
+ }
+
+ // The current DeferredCloses object.
+ static DeferredCloses* current_;
+
+ typedef std::queue< scoped_refptr<RenderWidget> > WidgetQueue;
+ WidgetQueue queue_;
+};
+
+DeferredCloses* DeferredCloses::current_ = NULL;
+
+} // namespace
+
+///////////////////////////////////////////////////////////////////////////////
+
+RenderWidget::RenderWidget()
+ : routing_id_(MSG_ROUTING_NONE),
+ opener_id_(MSG_ROUTING_NONE),
+ host_window_(NULL),
+ current_paint_buf_(NULL),
+ current_scroll_buf_(NULL),
+ next_paint_flags_(0),
+ paint_reply_pending_(false),
+ did_show_(false),
+ closing_(false),
+ is_hidden_(false),
+ needs_repainting_on_restore_(false),
+ has_focus_(false),
+ ime_is_active_(false),
+ ime_control_enable_ime_(true),
+ ime_control_x_(-1),
+ ime_control_y_(-1),
+ ime_control_new_state_(false),
+ ime_control_updated_(false) {
+ RenderProcess::AddRefProcess();
+}
+
+RenderWidget::~RenderWidget() {
+ if (current_paint_buf_) {
+ RenderProcess::FreeSharedMemory(current_paint_buf_);
+ current_paint_buf_ = NULL;
+ }
+ if (current_scroll_buf_) {
+ RenderProcess::FreeSharedMemory(current_scroll_buf_);
+ current_scroll_buf_ = NULL;
+ }
+ RenderProcess::ReleaseProcess();
+}
+
+/*static*/
+RenderWidget* RenderWidget::Create(int32 opener_id) {
+ DCHECK(opener_id != MSG_ROUTING_NONE);
+ scoped_refptr<RenderWidget> widget = new RenderWidget();
+ widget->Init(opener_id); // adds reference
+ return widget;
+}
+
+void RenderWidget::Init(int32 opener_id) {
+ DCHECK(!webwidget_);
+
+ if (opener_id != MSG_ROUTING_NONE)
+ opener_id_ = opener_id;
+
+ // Avoid a leak here by not assigning, since WebWidget::Create addrefs for us.
+ WebWidget* webwidget = WebWidget::Create(this);
+ webwidget_.swap(&webwidget);
+
+ bool result = RenderThread::current()->Send(
+ new ViewHostMsg_CreateWidget(opener_id, &routing_id_));
+ if (result) {
+ RenderThread::current()->AddRoute(routing_id_, this);
+ // Take a reference on behalf of the RenderThread. This will be balanced
+ // when we receive ViewMsg_Close.
+ AddRef();
+ } else {
+ DCHECK(false);
+ }
+}
+
+// This is used to complete pending inits and non-pending inits. For non-
+// pending cases, the parent will be the same as the current parent. This
+// indicates we do not need to reparent or anything.
+void RenderWidget::CompleteInit(HWND parent_hwnd) {
+ DCHECK(routing_id_ != MSG_ROUTING_NONE);
+ DCHECK(parent_hwnd);
+
+ host_window_ = parent_hwnd;
+
+ Send(new ViewHostMsg_RendererReady(routing_id_));
+}
+
+IPC_DEFINE_MESSAGE_MAP(RenderWidget)
+ IPC_MESSAGE_HANDLER(ViewMsg_Close, OnClose)
+ IPC_MESSAGE_HANDLER(ViewMsg_CreatingNew_ACK, OnCreatingNewAck)
+ IPC_MESSAGE_HANDLER(ViewMsg_Resize, OnResize)
+ IPC_MESSAGE_HANDLER(ViewMsg_WasHidden, OnWasHidden)
+ IPC_MESSAGE_HANDLER(ViewMsg_WasRestored, OnWasRestored)
+ IPC_MESSAGE_HANDLER(ViewMsg_PaintRect_ACK, OnPaintRectAck)
+ IPC_MESSAGE_HANDLER(ViewMsg_ScrollRect_ACK, OnScrollRectAck)
+ IPC_MESSAGE_HANDLER(ViewMsg_HandleInputEvent, OnHandleInputEvent)
+ IPC_MESSAGE_HANDLER(ViewMsg_MouseCaptureLost, OnMouseCaptureLost)
+ IPC_MESSAGE_HANDLER(ViewMsg_SetFocus, OnSetFocus)
+ IPC_MESSAGE_HANDLER(ViewMsg_ImeSetInputMode, OnImeSetInputMode)
+ IPC_MESSAGE_HANDLER(ViewMsg_ImeSetComposition, OnImeSetComposition)
+ IPC_MESSAGE_UNHANDLED_ERROR()
+IPC_END_MESSAGE_MAP()
+
+bool RenderWidget::Send(IPC::Message* message) {
+ // Don't send any messages after the browser has told us to close.
+ if (closing_) {
+ delete message;
+ return false;
+ }
+
+ // If given a messsage without a routing ID, then assign our routing ID.
+ if (message->routing_id() == MSG_ROUTING_NONE)
+ message->set_routing_id(routing_id_);
+
+ bool rv = RenderThread::current()->Send(message);
+
+ // If there aren't any more RenderThread::Send calls on the stack, then we
+ // can go ahead and schedule Close to be called on any RenderWidget objects
+ // that received a ViewMsg_Close while we were inside Send.
+ if (!RenderThread::current()->in_send())
+ DeferredCloses::Post();
+
+ return rv;
+}
+
+// Got a response from the browser after the renderer decided to create a new
+// view.
+void RenderWidget::OnCreatingNewAck(HWND parent) {
+ DCHECK(routing_id_ != MSG_ROUTING_NONE);
+
+ CompleteInit(parent);
+}
+
+void RenderWidget::OnClose() {
+ if (closing_)
+ return;
+ closing_ = true;
+
+ // Browser correspondence is no longer needed at this point.
+ if (routing_id_ != MSG_ROUTING_NONE)
+ RenderThread::current()->RemoveRoute(routing_id_);
+
+ // Balances the AddRef taken when we called AddRoute. This release happens
+ // via the MessageLoop since it may cause our destruction.
+ MessageLoop::current()->ReleaseSoon(FROM_HERE, this);
+
+ // If there is a Send call on the stack, then it could be dangerous to close
+ // now. Instead, we wait until we get out of Send.
+ if (RenderThread::current()->in_send()) {
+ DeferredCloses::Push(this);
+ } else {
+ Close();
+ }
+}
+
+void RenderWidget::OnResize(const gfx::Size& new_size) {
+ // During shutdown we can just ignore this message.
+ if (!webwidget_)
+ return;
+
+ // TODO(darin): We should not need to reset this here.
+ is_hidden_ = false;
+ needs_repainting_on_restore_ = false;
+
+ // We shouldn't be asked to resize to our current size.
+ DCHECK(size_ != new_size);
+ size_ = new_size;
+
+ // We should not be sent a Resize message if we have not ACK'd the previous
+ DCHECK(!next_paint_is_resize_ack());
+
+ // When resizing, we want to wait to paint before ACK'ing the resize. This
+ // ensures that we only resize as fast as we can paint. We only need to send
+ // an ACK if we are resized to a non-empty rect.
+ webwidget_->Resize(new_size);
+ if (!new_size.IsEmpty()) {
+ DCHECK(!paint_rect_.IsEmpty());
+
+ // This should have caused an invalidation of the entire view. The damaged
+ // rect could be larger than new_size if we are being made smaller.
+ DCHECK_GE(paint_rect_.width(), new_size.width());
+ DCHECK_GE(paint_rect_.height(), new_size.height());
+
+ // We will send the Resize_ACK flag once we paint again.
+ set_next_paint_is_resize_ack();
+ }
+}
+
+void RenderWidget::OnWasHidden() {
+ // Go into a mode where we stop generating paint and scrolling events.
+ is_hidden_ = true;
+}
+
+void RenderWidget::OnWasRestored(bool needs_repainting) {
+ // During shutdown we can just ignore this message.
+ if (!webwidget_)
+ return;
+
+ // See OnWasHidden
+ is_hidden_ = false;
+
+ if (!needs_repainting && !needs_repainting_on_restore_)
+ return;
+ needs_repainting_on_restore_ = false;
+
+ // Tag the next paint as a restore ack, which is picked up by DoDeferredPaint
+ // when it sends out the next PaintRect message.
+ set_next_paint_is_restore_ack();
+
+ // Generate a full repaint.
+ DidInvalidateRect(webwidget_, gfx::Rect(size_.width(), size_.height()));
+}
+
+void RenderWidget::OnPaintRectAck() {
+ DCHECK(paint_reply_pending());
+ paint_reply_pending_ = false;
+
+ // If we sent a PaintRect message with a zero-sized bitmap, then
+ // we should have no current paint buf.
+ if (current_paint_buf_) {
+ RenderProcess::FreeSharedMemory(current_paint_buf_);
+ current_paint_buf_ = NULL;
+ }
+
+ // Continue painting if necessary...
+ DoDeferredPaint();
+}
+
+void RenderWidget::OnScrollRectAck() {
+ DCHECK(scroll_reply_pending());
+
+ RenderProcess::FreeSharedMemory(current_scroll_buf_);
+ current_scroll_buf_ = NULL;
+
+ // Continue scrolling if necessary...
+ DoDeferredScroll();
+}
+
+void RenderWidget::OnHandleInputEvent(const IPC::Message& message) {
+ void* iter = NULL;
+
+ const char* data;
+ int data_length;
+ if (!message.ReadData(&iter, &data, &data_length))
+ return;
+
+ const WebInputEvent* input_event =
+ reinterpret_cast<const WebInputEvent*>(data);
+ bool processed = false;
+ if (webwidget_)
+ processed = webwidget_->HandleInputEvent(input_event);
+
+ IPC::Message* response = new ViewHostMsg_HandleInputEvent_ACK(routing_id_);
+ response->WriteInt(input_event->type);
+ if (!processed) {
+ // If the event was not processed we send it back.
+ response->WriteData(data, data_length);
+ }
+ Send(response);
+}
+
+void RenderWidget::OnMouseCaptureLost() {
+ if (webwidget_)
+ webwidget_->MouseCaptureLost();
+}
+
+void RenderWidget::OnSetFocus(bool enable) {
+ has_focus_ = enable;
+ if (webwidget_)
+ webwidget_->SetFocus(enable);
+ if (enable) {
+ // Force to retrieve the state of the focused widget to determine if we
+ // should activate IMEs next time when this process calls the UpdateIME()
+ // function.
+ ime_control_updated_ = true;
+ ime_control_new_state_ = true;
+ }
+}
+
+void RenderWidget::ClearFocus() {
+ // We may have got the focus from the browser before this gets processed, in
+ // which case we do not want to unfocus ourself.
+ if (!has_focus_ && webwidget_)
+ webwidget_->SetFocus(false);
+}
+
+void RenderWidget::PaintRect(const gfx::Rect& rect, SharedMemory* paint_buf) {
+ gfx::PlatformCanvas canvas(rect.width(), rect.height(), true,
+ paint_buf->handle());
+ // Bring the canvas into the coordinate system of the paint rect
+ canvas.translate(static_cast<SkScalar>(-rect.x()),
+ static_cast<SkScalar>(-rect.y()));
+
+ webwidget_->Paint(&canvas, rect);
+
+ // Flush to underlying bitmap. TODO(darin): is this needed?
+ canvas.getTopPlatformDevice().accessBitmap(false);
+
+ // Let the subclass observe this paint operations.
+ DidPaint();
+}
+
+size_t RenderWidget::GetPaintBufSize(const gfx::Rect& rect) {
+ // TODO(darin): protect against overflow
+ return 4 * rect.width() * rect.height();
+}
+
+void RenderWidget::DoDeferredPaint() {
+ if (!webwidget_ || paint_reply_pending() || paint_rect_.IsEmpty())
+ return;
+
+ // When we are hidden, we want to suppress painting, but we still need to
+ // mark this DoDeferredPaint as complete.
+ if (is_hidden_ || size_.IsEmpty()) {
+ paint_rect_ = gfx::Rect();
+ needs_repainting_on_restore_ = true;
+ return;
+ }
+
+ // Layout may generate more invalidation...
+ webwidget_->Layout();
+
+ // OK, save the current paint_rect to a local since painting may cause more
+ // invalidation. Some WebCore rendering objects only layout when painted.
+ gfx::Rect damaged_rect = paint_rect_;
+ paint_rect_ = gfx::Rect();
+
+ // Compute a buffer for painting and cache it.
+ current_paint_buf_ =
+ RenderProcess::AllocSharedMemory(GetPaintBufSize(damaged_rect));
+ if (!current_paint_buf_) {
+ NOTREACHED();
+ return;
+ }
+
+ PaintRect(damaged_rect, current_paint_buf_);
+
+ ViewHostMsg_PaintRect_Params params;
+ params.bitmap = current_paint_buf_->handle();
+ params.bitmap_rect = damaged_rect;
+ params.view_size = size_;
+ params.plugin_window_moves = plugin_window_moves_;
+ params.flags = next_paint_flags_;
+
+ plugin_window_moves_.clear();
+
+ paint_reply_pending_ = true;
+ Send(new ViewHostMsg_PaintRect(routing_id_, params));
+ next_paint_flags_ = 0;
+
+ UpdateIME();
+}
+
+void RenderWidget::DoDeferredScroll() {
+ if (!webwidget_ || scroll_reply_pending() || scroll_rect_.IsEmpty())
+ return;
+
+ // When we are hidden, we want to suppress scrolling, but we still need to
+ // mark this DoDeferredScroll as complete.
+ if (is_hidden_ || size_.IsEmpty()) {
+ scroll_rect_ = gfx::Rect();
+ needs_repainting_on_restore_ = true;
+ return;
+ }
+
+ // Layout may generate more invalidation, so we might have to bail on
+ // optimized scrolling...
+ webwidget_->Layout();
+
+ if (scroll_rect_.IsEmpty())
+ return;
+
+ gfx::Rect damaged_rect;
+
+ // Compute the region we will expose by scrolling, and paint that into a
+ // shared memory section.
+ if (scroll_delta_.x()) {
+ int dx = scroll_delta_.x();
+ damaged_rect.set_y(scroll_rect_.y());
+ damaged_rect.set_height(scroll_rect_.height());
+ if (dx > 0) {
+ damaged_rect.set_x(scroll_rect_.x());
+ damaged_rect.set_width(dx);
+ } else {
+ damaged_rect.set_x(scroll_rect_.right() + dx);
+ damaged_rect.set_width(-dx);
+ }
+ } else {
+ int dy = scroll_delta_.y();
+ damaged_rect.set_x(scroll_rect_.x());
+ damaged_rect.set_width(scroll_rect_.width());
+ if (dy > 0) {
+ damaged_rect.set_y(scroll_rect_.y());
+ damaged_rect.set_height(dy);
+ } else {
+ damaged_rect.set_y(scroll_rect_.bottom() + dy);
+ damaged_rect.set_height(-dy);
+ }
+ }
+
+ // In case the scroll offset exceeds the width/height of the scroll rect
+ damaged_rect = scroll_rect_.Intersect(damaged_rect);
+
+ current_scroll_buf_ =
+ RenderProcess::AllocSharedMemory(GetPaintBufSize(damaged_rect));
+ if (!current_scroll_buf_) {
+ NOTREACHED();
+ return;
+ }
+
+ // Set these parameters before calling Paint, since that could result in
+ // further invalidates (uncommon).
+ ViewHostMsg_ScrollRect_Params params;
+ params.bitmap = current_scroll_buf_->handle();
+ params.bitmap_rect = damaged_rect;
+ params.dx = scroll_delta_.x();
+ params.dy = scroll_delta_.y();
+ params.clip_rect = scroll_rect_;
+ params.view_size = size_;
+ params.plugin_window_moves = plugin_window_moves_;
+
+ plugin_window_moves_.clear();
+
+ // Mark the scroll operation as no longer pending.
+ scroll_rect_ = gfx::Rect();
+
+ PaintRect(damaged_rect, current_scroll_buf_);
+ Send(new ViewHostMsg_ScrollRect(routing_id_, params));
+ UpdateIME();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// WebWidgetDelegate
+
+HWND RenderWidget::GetContainingWindow(WebWidget* webwidget) {
+ return host_window_;
+}
+
+void RenderWidget::DidInvalidateRect(WebWidget* webwidget,
+ const gfx::Rect& rect) {
+ // We only want one pending DoDeferredPaint call at any time...
+ bool paint_pending = !paint_rect_.IsEmpty();
+
+ // If this invalidate overlaps with a pending scroll, then we have to
+ // downgrade to invalidating the scroll rect.
+ if (rect.Intersects(scroll_rect_)) {
+ paint_rect_ = paint_rect_.Union(scroll_rect_);
+ scroll_rect_ = gfx::Rect();
+ }
+
+ gfx::Rect view_rect(0, 0, size_.width(), size_.height());
+ // TODO(iyengar) Investigate why we have painting issues when
+ // we ignore invalid regions outside the view.
+ // Ignore invalidates that occur outside the bounds of the view
+ // TODO(darin): maybe this should move into the paint code?
+ // paint_rect_ = view_rect.Intersect(paint_rect_.Union(rect));
+ paint_rect_ = paint_rect_.Union(view_rect.Intersect(rect));
+
+ if (paint_rect_.IsEmpty() || paint_reply_pending() || paint_pending)
+ return;
+
+ // Perform painting asynchronously. This serves two purposes:
+ // 1) Ensures that we call WebView::Paint without a bunch of other junk
+ // on the call stack.
+ // 2) Allows us to collect more damage rects before painting to help coalesce
+ // the work that we will need to do.
+ MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &RenderWidget::DoDeferredPaint));
+}
+
+void RenderWidget::DidScrollRect(WebWidget* webwidget, int dx, int dy,
+ const gfx::Rect& clip_rect) {
+ // We only support scrolling along one axis at a time.
+ DCHECK((dx && !dy) || (!dx && dy));
+
+ bool intersects_with_painting = paint_rect_.Intersects(clip_rect);
+
+ // If we already have a pending scroll operation or if this scroll operation
+ // intersects the existing paint region, then just failover to invalidating.
+ if (!scroll_rect_.IsEmpty() || intersects_with_painting) {
+ if (!intersects_with_painting && scroll_rect_ == clip_rect) {
+ // OK, we can just update the scroll delta (requires same scrolling axis)
+ if (!dx && !scroll_delta_.x()) {
+ scroll_delta_.set_y(scroll_delta_.y() + dy);
+ return;
+ }
+ if (!dy && !scroll_delta_.y()) {
+ scroll_delta_.set_x(scroll_delta_.x() + dx);
+ return;
+ }
+ }
+ DidInvalidateRect(webwidget_, scroll_rect_);
+ DCHECK(scroll_rect_.IsEmpty());
+ DidInvalidateRect(webwidget_, clip_rect);
+ return;
+ }
+
+ // We only want one pending DoDeferredScroll call at any time...
+ bool scroll_pending = !scroll_rect_.IsEmpty();
+
+ scroll_rect_ = clip_rect;
+ scroll_delta_.SetPoint(dx, dy);
+
+ if (scroll_pending)
+ return;
+
+ // Perform scrolling asynchronously since we need to call WebView::Paint
+ MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &RenderWidget::DoDeferredScroll));
+}
+
+void RenderWidget::SetCursor(WebWidget* webwidget, const WebCursor& cursor) {
+ // Only send a SetCursor message if we need to make a change.
+ if (!current_cursor_.IsEqual(cursor)) {
+ current_cursor_ = cursor;
+ Send(new ViewHostMsg_SetCursor(routing_id_, cursor));
+ }
+}
+
+// We are supposed to get a single call to Show for a newly created RenderWidget
+// that was created via RenderWidget::CreateWebView. So, we wait until this
+// point to dispatch the ShowWidget message.
+//
+// This method provides us with the information about how to display the newly
+// created RenderWidget (i.e., as a constrained popup or as a new tab).
+//
+void RenderWidget::Show(WebWidget* webwidget,
+ WindowOpenDisposition disposition) {
+ DCHECK(!did_show_) << "received extraneous Show call";
+ DCHECK(routing_id_ != MSG_ROUTING_NONE);
+ DCHECK(opener_id_ != MSG_ROUTING_NONE);
+
+ if (!did_show_) {
+ did_show_ = true;
+ // NOTE: initial_pos_ may still have its default values at this point, but
+ // that's okay. It'll be ignored if as_popup is false, or the browser
+ // process will impose a default position otherwise.
+ RenderThread::current()->Send(new ViewHostMsg_ShowWidget(
+ opener_id_, routing_id_, initial_pos_));
+ }
+}
+
+void RenderWidget::Focus(WebWidget* webwidget) {
+ // Prevent the widget from stealing the focus if it does not have focus
+ // already. We do this by explicitely setting the focus to false again.
+ // We only let the browser focus the renderer.
+ if (!has_focus_ && webwidget_) {
+ MessageLoop::current()->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &RenderWidget::ClearFocus));
+ }
+}
+
+void RenderWidget::Blur(WebWidget* webwidget) {
+ Send(new ViewHostMsg_Blur(routing_id_));
+}
+
+void RenderWidget::CloseWidgetSoon(WebWidget* webwidget) {
+ // If a page calls window.close() twice, we'll end up here twice, but that's
+ // OK. It is safe to send multiple Close messages.
+
+ // Ask the RenderWidgetHost to initiate close.
+ Send(new ViewHostMsg_Close(routing_id_));
+}
+
+void RenderWidget::Close() {
+ if (webwidget_) {
+ webwidget_->Close();
+ webwidget_ = NULL;
+ }
+}
+
+void RenderWidget::GetWindowLocation(WebWidget* webwidget, gfx::Point* origin) {
+ gfx::Rect rect;
+ Send(new ViewHostMsg_GetWindowRect(routing_id_, host_window_, &rect));
+ *origin = rect.origin();
+}
+
+void RenderWidget::SetWindowRect(WebWidget* webwidget, const gfx::Rect& pos) {
+ if (did_show_) {
+ Send(new ViewHostMsg_RequestMove(routing_id_, pos));
+ } else {
+ initial_pos_ = pos;
+ }
+}
+
+void RenderWidget::OnImeSetInputMode(bool is_active) {
+ // A renderer process may move its input focus and the caret position
+ // while a browser process stop receiving IPC messages.
+ // Thus, when a browser process requests for a renderer process to send
+ // IPC messages, it has to check whether or not a renderer process moves
+ // its input focus and send an IPC message if they are updated.
+ ime_is_active_ = is_active;
+ ime_control_updated_ = true;
+ ime_control_new_state_ = true;
+}
+
+void RenderWidget::OnImeSetComposition(int string_type,
+ int cursor_position,
+ int target_start, int target_end,
+ const std::wstring& ime_string) {
+ if (webwidget_) {
+ int string_length = static_cast<int>(ime_string.length());
+ const wchar_t* string_data = ime_string.data();
+ webwidget_->ImeSetComposition(string_type, cursor_position,
+ target_start, target_end,
+ string_length, string_data);
+ }
+}
+
+void RenderWidget::UpdateIME() {
+ // If a browser process does not have IMEs, its IMEs are not active, or there
+ // are not any attached widgets.
+ // a renderer process does not have to retrieve information of the focused
+ // control or send notification messages to a browser process.
+ if (!ime_is_active_) {
+ return;
+ }
+ // Retrieve the caret position from the focused widget.
+ bool enable_ime;
+ int x, y;
+ const void *id;
+ if (!webwidget_ || !webwidget_->ImeUpdateStatus(&enable_ime, &id, &x, &y)) {
+ // There are not any editable widgets attached to this process.
+ // We should disable the IME to prevent it from sending CJK strings to
+ // non-editable widgets.
+ ime_control_updated_ = true;
+ ime_control_new_state_ = false;
+ }
+ if (ime_control_updated_) {
+ // The input focus has been changed.
+ // Compare the current state with the updated state and choose actions.
+ if (ime_control_enable_ime_) {
+ if (ime_control_new_state_) {
+ // Case 1: a text input -> another text input
+ // Complete the current composition and notify the caret position.
+ enum ViewHostMsg_ImeControl control = IME_COMPLETE_COMPOSITION;
+ Send(new ViewHostMsg_ImeUpdateStatus(routing_id(), control, x, y));
+ } else {
+ // Case 2: a text input -> a password input (or a static control)
+ // Complete the current composition and disable the IME.
+ enum ViewHostMsg_ImeControl control = IME_DISABLE;
+ Send(new ViewHostMsg_ImeUpdateStatus(routing_id(), control, x, y));
+ }
+ } else {
+ if (ime_control_new_state_) {
+ // Case 3: a password input (or a static control) -> a text input
+ // Enable the IME and notify the caret position.
+ enum ViewHostMsg_ImeControl control = IME_COMPLETE_COMPOSITION;
+ Send(new ViewHostMsg_ImeUpdateStatus(routing_id(), control, x, y));
+ } else {
+ // Case 4: a password input (or a static contol) -> another password
+ // input (or another static control).
+ // The IME has been already disabled and we don't have to do anything.
+ }
+ }
+ } else {
+ // The input focus is not changed.
+ // Notify the caret position to a browser process only if it is changed.
+ if (ime_control_enable_ime_) {
+ if (x != ime_control_x_ || y != ime_control_y_) {
+ enum ViewHostMsg_ImeControl control = IME_MOVE_WINDOWS;
+ Send(new ViewHostMsg_ImeUpdateStatus(routing_id(), control, x, y));
+ }
+ }
+ }
+ // Save the updated IME status to prevent from sending the same IPC messages.
+ ime_control_updated_ = false;
+ ime_control_enable_ime_ = ime_control_new_state_;
+ ime_control_x_ = x;
+ ime_control_y_ = y;
+}
+
+void RenderWidget::DidMove(WebWidget* webwidget,
+ const WebPluginGeometry& move) {
+ size_t i = 0;
+ for (; i < plugin_window_moves_.size(); ++i) {
+ if (plugin_window_moves_[i].window == move.window) {
+ plugin_window_moves_[i] = move;
+ break;
+ }
+ }
+
+ if (i == plugin_window_moves_.size())
+ plugin_window_moves_.push_back(move);
+}