blob: c5db8055ce6828903086e37f7bc2478d6d6cd880 [file] [log] [blame]
initial.commit09911bf2008-07-26 23:55:291// Copyright 2008, Google Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8// * Redistributions of source code must retain the above copyright
9// notice, this list of conditions and the following disclaimer.
10// * Redistributions in binary form must reproduce the above
11// copyright notice, this list of conditions and the following disclaimer
12// in the documentation and/or other materials provided with the
13// distribution.
14// * Neither the name of Google Inc. nor the names of its
15// contributors may be used to endorse or promote products derived from
16// this software without specific prior written permission.
17//
18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30#include "chrome/renderer/render_widget.h"
31
32#include <windows.h>
33
34#include "base/gfx/point.h"
35#include "base/gfx/size.h"
36#include "base/logging.h"
37#include "base/message_loop.h"
38#include "base/gfx/platform_canvas.h"
39#include "base/scoped_ptr.h"
40#include "webkit/glue/webinputevent.h"
41#include "webkit/glue/webwidget.h"
42
43///////////////////////////////////////////////////////////////////////////////
44
45namespace {
46
47// This class is used to defer calling RenderWidget::Close() while the current
48// thread is inside RenderThread::Send(), which in some cases can result in a
49// nested MessageLoop being run.
50class DeferredCloses : public Task {
51 public:
52 // Called to queue a deferred close for the given widget.
53 static void Push(RenderWidget* widget) {
54 if (!current_)
55 current_ = new DeferredCloses();
56 current_->queue_.push(widget);
57 }
58
59 // Called to trigger any deferred closes to be run.
60 static void Post() {
61 DCHECK(!RenderThread::current()->in_send());
62 if (current_) {
63 MessageLoop::current()->PostTask(FROM_HERE, current_);
64 current_ = NULL;
65 }
66 }
67
68 private:
69 virtual void Run() {
70 // Maybe we are being run from within another RenderWidget::Send call. If
71 // that is true, then we need to re-queue the widgets to be closed and try
72 // again later.
73 while (!queue_.empty()) {
74 if (RenderThread::current()->in_send()) {
75 Push(queue_.front());
76 } else {
77 queue_.front()->Close();
78 }
79 queue_.pop();
80 }
81 }
82
83 // The current DeferredCloses object.
84 static DeferredCloses* current_;
85
86 typedef std::queue< scoped_refptr<RenderWidget> > WidgetQueue;
87 WidgetQueue queue_;
88};
89
90DeferredCloses* DeferredCloses::current_ = NULL;
91
92} // namespace
93
94///////////////////////////////////////////////////////////////////////////////
95
96RenderWidget::RenderWidget()
97 : routing_id_(MSG_ROUTING_NONE),
98 opener_id_(MSG_ROUTING_NONE),
99 host_window_(NULL),
100 current_paint_buf_(NULL),
101 current_scroll_buf_(NULL),
102 next_paint_flags_(0),
103 paint_reply_pending_(false),
104 did_show_(false),
105 closing_(false),
106 is_hidden_(false),
107 needs_repainting_on_restore_(false),
108 has_focus_(false),
109 ime_is_active_(false),
110 ime_control_enable_ime_(true),
111 ime_control_x_(-1),
112 ime_control_y_(-1),
113 ime_control_new_state_(false),
114 ime_control_updated_(false) {
115 RenderProcess::AddRefProcess();
116}
117
118RenderWidget::~RenderWidget() {
119 if (current_paint_buf_) {
120 RenderProcess::FreeSharedMemory(current_paint_buf_);
121 current_paint_buf_ = NULL;
122 }
123 if (current_scroll_buf_) {
124 RenderProcess::FreeSharedMemory(current_scroll_buf_);
125 current_scroll_buf_ = NULL;
126 }
127 RenderProcess::ReleaseProcess();
128}
129
130/*static*/
131RenderWidget* RenderWidget::Create(int32 opener_id) {
132 DCHECK(opener_id != MSG_ROUTING_NONE);
133 scoped_refptr<RenderWidget> widget = new RenderWidget();
134 widget->Init(opener_id); // adds reference
135 return widget;
136}
137
138void RenderWidget::Init(int32 opener_id) {
139 DCHECK(!webwidget_);
140
141 if (opener_id != MSG_ROUTING_NONE)
142 opener_id_ = opener_id;
143
144 // Avoid a leak here by not assigning, since WebWidget::Create addrefs for us.
145 WebWidget* webwidget = WebWidget::Create(this);
146 webwidget_.swap(&webwidget);
147
148 bool result = RenderThread::current()->Send(
149 new ViewHostMsg_CreateWidget(opener_id, &routing_id_));
150 if (result) {
151 RenderThread::current()->AddRoute(routing_id_, this);
152 // Take a reference on behalf of the RenderThread. This will be balanced
153 // when we receive ViewMsg_Close.
154 AddRef();
155 } else {
156 DCHECK(false);
157 }
158}
159
160// This is used to complete pending inits and non-pending inits. For non-
161// pending cases, the parent will be the same as the current parent. This
162// indicates we do not need to reparent or anything.
163void RenderWidget::CompleteInit(HWND parent_hwnd) {
164 DCHECK(routing_id_ != MSG_ROUTING_NONE);
165 DCHECK(parent_hwnd);
166
167 host_window_ = parent_hwnd;
168
169 Send(new ViewHostMsg_RendererReady(routing_id_));
170}
171
172IPC_DEFINE_MESSAGE_MAP(RenderWidget)
173 IPC_MESSAGE_HANDLER(ViewMsg_Close, OnClose)
174 IPC_MESSAGE_HANDLER(ViewMsg_CreatingNew_ACK, OnCreatingNewAck)
175 IPC_MESSAGE_HANDLER(ViewMsg_Resize, OnResize)
176 IPC_MESSAGE_HANDLER(ViewMsg_WasHidden, OnWasHidden)
177 IPC_MESSAGE_HANDLER(ViewMsg_WasRestored, OnWasRestored)
178 IPC_MESSAGE_HANDLER(ViewMsg_PaintRect_ACK, OnPaintRectAck)
179 IPC_MESSAGE_HANDLER(ViewMsg_ScrollRect_ACK, OnScrollRectAck)
180 IPC_MESSAGE_HANDLER(ViewMsg_HandleInputEvent, OnHandleInputEvent)
181 IPC_MESSAGE_HANDLER(ViewMsg_MouseCaptureLost, OnMouseCaptureLost)
182 IPC_MESSAGE_HANDLER(ViewMsg_SetFocus, OnSetFocus)
183 IPC_MESSAGE_HANDLER(ViewMsg_ImeSetInputMode, OnImeSetInputMode)
184 IPC_MESSAGE_HANDLER(ViewMsg_ImeSetComposition, OnImeSetComposition)
185 IPC_MESSAGE_UNHANDLED_ERROR()
186IPC_END_MESSAGE_MAP()
187
188bool RenderWidget::Send(IPC::Message* message) {
189 // Don't send any messages after the browser has told us to close.
190 if (closing_) {
191 delete message;
192 return false;
193 }
194
195 // If given a messsage without a routing ID, then assign our routing ID.
196 if (message->routing_id() == MSG_ROUTING_NONE)
197 message->set_routing_id(routing_id_);
198
199 bool rv = RenderThread::current()->Send(message);
200
201 // If there aren't any more RenderThread::Send calls on the stack, then we
202 // can go ahead and schedule Close to be called on any RenderWidget objects
203 // that received a ViewMsg_Close while we were inside Send.
204 if (!RenderThread::current()->in_send())
205 DeferredCloses::Post();
206
207 return rv;
208}
209
210// Got a response from the browser after the renderer decided to create a new
211// view.
212void RenderWidget::OnCreatingNewAck(HWND parent) {
213 DCHECK(routing_id_ != MSG_ROUTING_NONE);
214
215 CompleteInit(parent);
216}
217
218void RenderWidget::OnClose() {
219 if (closing_)
220 return;
221 closing_ = true;
222
223 // Browser correspondence is no longer needed at this point.
224 if (routing_id_ != MSG_ROUTING_NONE)
225 RenderThread::current()->RemoveRoute(routing_id_);
226
227 // Balances the AddRef taken when we called AddRoute. This release happens
228 // via the MessageLoop since it may cause our destruction.
229 MessageLoop::current()->ReleaseSoon(FROM_HERE, this);
230
231 // If there is a Send call on the stack, then it could be dangerous to close
232 // now. Instead, we wait until we get out of Send.
233 if (RenderThread::current()->in_send()) {
234 DeferredCloses::Push(this);
235 } else {
236 Close();
237 }
238}
239
240void RenderWidget::OnResize(const gfx::Size& new_size) {
241 // During shutdown we can just ignore this message.
242 if (!webwidget_)
243 return;
244
245 // TODO(darin): We should not need to reset this here.
246 is_hidden_ = false;
247 needs_repainting_on_restore_ = false;
248
249 // We shouldn't be asked to resize to our current size.
250 DCHECK(size_ != new_size);
251 size_ = new_size;
252
253 // We should not be sent a Resize message if we have not ACK'd the previous
254 DCHECK(!next_paint_is_resize_ack());
255
256 // When resizing, we want to wait to paint before ACK'ing the resize. This
257 // ensures that we only resize as fast as we can paint. We only need to send
258 // an ACK if we are resized to a non-empty rect.
259 webwidget_->Resize(new_size);
260 if (!new_size.IsEmpty()) {
261 DCHECK(!paint_rect_.IsEmpty());
262
263 // This should have caused an invalidation of the entire view. The damaged
264 // rect could be larger than new_size if we are being made smaller.
265 DCHECK_GE(paint_rect_.width(), new_size.width());
266 DCHECK_GE(paint_rect_.height(), new_size.height());
267
268 // We will send the Resize_ACK flag once we paint again.
269 set_next_paint_is_resize_ack();
270 }
271}
272
273void RenderWidget::OnWasHidden() {
274 // Go into a mode where we stop generating paint and scrolling events.
275 is_hidden_ = true;
276}
277
278void RenderWidget::OnWasRestored(bool needs_repainting) {
279 // During shutdown we can just ignore this message.
280 if (!webwidget_)
281 return;
282
283 // See OnWasHidden
284 is_hidden_ = false;
285
286 if (!needs_repainting && !needs_repainting_on_restore_)
287 return;
288 needs_repainting_on_restore_ = false;
289
290 // Tag the next paint as a restore ack, which is picked up by DoDeferredPaint
291 // when it sends out the next PaintRect message.
292 set_next_paint_is_restore_ack();
293
294 // Generate a full repaint.
295 DidInvalidateRect(webwidget_, gfx::Rect(size_.width(), size_.height()));
296}
297
298void RenderWidget::OnPaintRectAck() {
299 DCHECK(paint_reply_pending());
300 paint_reply_pending_ = false;
301
302 // If we sent a PaintRect message with a zero-sized bitmap, then
303 // we should have no current paint buf.
304 if (current_paint_buf_) {
305 RenderProcess::FreeSharedMemory(current_paint_buf_);
306 current_paint_buf_ = NULL;
307 }
308
309 // Continue painting if necessary...
310 DoDeferredPaint();
311}
312
313void RenderWidget::OnScrollRectAck() {
314 DCHECK(scroll_reply_pending());
315
316 RenderProcess::FreeSharedMemory(current_scroll_buf_);
317 current_scroll_buf_ = NULL;
318
319 // Continue scrolling if necessary...
320 DoDeferredScroll();
321}
322
323void RenderWidget::OnHandleInputEvent(const IPC::Message& message) {
324 void* iter = NULL;
325
326 const char* data;
327 int data_length;
328 if (!message.ReadData(&iter, &data, &data_length))
329 return;
330
331 const WebInputEvent* input_event =
332 reinterpret_cast<const WebInputEvent*>(data);
333 bool processed = false;
334 if (webwidget_)
335 processed = webwidget_->HandleInputEvent(input_event);
336
337 IPC::Message* response = new ViewHostMsg_HandleInputEvent_ACK(routing_id_);
338 response->WriteInt(input_event->type);
339 if (!processed) {
340 // If the event was not processed we send it back.
341 response->WriteData(data, data_length);
342 }
343 Send(response);
344}
345
346void RenderWidget::OnMouseCaptureLost() {
347 if (webwidget_)
348 webwidget_->MouseCaptureLost();
349}
350
351void RenderWidget::OnSetFocus(bool enable) {
352 has_focus_ = enable;
353 if (webwidget_)
354 webwidget_->SetFocus(enable);
355 if (enable) {
356 // Force to retrieve the state of the focused widget to determine if we
357 // should activate IMEs next time when this process calls the UpdateIME()
358 // function.
359 ime_control_updated_ = true;
360 ime_control_new_state_ = true;
361 }
362}
363
364void RenderWidget::ClearFocus() {
365 // We may have got the focus from the browser before this gets processed, in
366 // which case we do not want to unfocus ourself.
367 if (!has_focus_ && webwidget_)
368 webwidget_->SetFocus(false);
369}
370
371void RenderWidget::PaintRect(const gfx::Rect& rect, SharedMemory* paint_buf) {
372 gfx::PlatformCanvas canvas(rect.width(), rect.height(), true,
373 paint_buf->handle());
374 // Bring the canvas into the coordinate system of the paint rect
375 canvas.translate(static_cast<SkScalar>(-rect.x()),
376 static_cast<SkScalar>(-rect.y()));
377
378 webwidget_->Paint(&canvas, rect);
379
380 // Flush to underlying bitmap. TODO(darin): is this needed?
381 canvas.getTopPlatformDevice().accessBitmap(false);
382
383 // Let the subclass observe this paint operations.
384 DidPaint();
385}
386
387size_t RenderWidget::GetPaintBufSize(const gfx::Rect& rect) {
388 // TODO(darin): protect against overflow
389 return 4 * rect.width() * rect.height();
390}
391
392void RenderWidget::DoDeferredPaint() {
393 if (!webwidget_ || paint_reply_pending() || paint_rect_.IsEmpty())
394 return;
395
396 // When we are hidden, we want to suppress painting, but we still need to
397 // mark this DoDeferredPaint as complete.
398 if (is_hidden_ || size_.IsEmpty()) {
399 paint_rect_ = gfx::Rect();
400 needs_repainting_on_restore_ = true;
401 return;
402 }
403
404 // Layout may generate more invalidation...
405 webwidget_->Layout();
406
407 // OK, save the current paint_rect to a local since painting may cause more
408 // invalidation. Some WebCore rendering objects only layout when painted.
409 gfx::Rect damaged_rect = paint_rect_;
410 paint_rect_ = gfx::Rect();
411
412 // Compute a buffer for painting and cache it.
413 current_paint_buf_ =
414 RenderProcess::AllocSharedMemory(GetPaintBufSize(damaged_rect));
415 if (!current_paint_buf_) {
416 NOTREACHED();
417 return;
418 }
419
420 PaintRect(damaged_rect, current_paint_buf_);
421
422 ViewHostMsg_PaintRect_Params params;
423 params.bitmap = current_paint_buf_->handle();
424 params.bitmap_rect = damaged_rect;
425 params.view_size = size_;
426 params.plugin_window_moves = plugin_window_moves_;
427 params.flags = next_paint_flags_;
428
429 plugin_window_moves_.clear();
430
431 paint_reply_pending_ = true;
432 Send(new ViewHostMsg_PaintRect(routing_id_, params));
433 next_paint_flags_ = 0;
434
435 UpdateIME();
436}
437
438void RenderWidget::DoDeferredScroll() {
439 if (!webwidget_ || scroll_reply_pending() || scroll_rect_.IsEmpty())
440 return;
441
442 // When we are hidden, we want to suppress scrolling, but we still need to
443 // mark this DoDeferredScroll as complete.
444 if (is_hidden_ || size_.IsEmpty()) {
445 scroll_rect_ = gfx::Rect();
446 needs_repainting_on_restore_ = true;
447 return;
448 }
449
450 // Layout may generate more invalidation, so we might have to bail on
451 // optimized scrolling...
452 webwidget_->Layout();
453
454 if (scroll_rect_.IsEmpty())
455 return;
456
457 gfx::Rect damaged_rect;
458
459 // Compute the region we will expose by scrolling, and paint that into a
460 // shared memory section.
461 if (scroll_delta_.x()) {
462 int dx = scroll_delta_.x();
463 damaged_rect.set_y(scroll_rect_.y());
464 damaged_rect.set_height(scroll_rect_.height());
465 if (dx > 0) {
466 damaged_rect.set_x(scroll_rect_.x());
467 damaged_rect.set_width(dx);
468 } else {
469 damaged_rect.set_x(scroll_rect_.right() + dx);
470 damaged_rect.set_width(-dx);
471 }
472 } else {
473 int dy = scroll_delta_.y();
474 damaged_rect.set_x(scroll_rect_.x());
475 damaged_rect.set_width(scroll_rect_.width());
476 if (dy > 0) {
477 damaged_rect.set_y(scroll_rect_.y());
478 damaged_rect.set_height(dy);
479 } else {
480 damaged_rect.set_y(scroll_rect_.bottom() + dy);
481 damaged_rect.set_height(-dy);
482 }
483 }
484
485 // In case the scroll offset exceeds the width/height of the scroll rect
486 damaged_rect = scroll_rect_.Intersect(damaged_rect);
487
488 current_scroll_buf_ =
489 RenderProcess::AllocSharedMemory(GetPaintBufSize(damaged_rect));
490 if (!current_scroll_buf_) {
491 NOTREACHED();
492 return;
493 }
494
495 // Set these parameters before calling Paint, since that could result in
496 // further invalidates (uncommon).
497 ViewHostMsg_ScrollRect_Params params;
498 params.bitmap = current_scroll_buf_->handle();
499 params.bitmap_rect = damaged_rect;
500 params.dx = scroll_delta_.x();
501 params.dy = scroll_delta_.y();
502 params.clip_rect = scroll_rect_;
503 params.view_size = size_;
504 params.plugin_window_moves = plugin_window_moves_;
505
506 plugin_window_moves_.clear();
507
508 // Mark the scroll operation as no longer pending.
509 scroll_rect_ = gfx::Rect();
510
511 PaintRect(damaged_rect, current_scroll_buf_);
512 Send(new ViewHostMsg_ScrollRect(routing_id_, params));
513 UpdateIME();
514}
515
516///////////////////////////////////////////////////////////////////////////////
517// WebWidgetDelegate
518
519HWND RenderWidget::GetContainingWindow(WebWidget* webwidget) {
520 return host_window_;
521}
522
523void RenderWidget::DidInvalidateRect(WebWidget* webwidget,
524 const gfx::Rect& rect) {
525 // We only want one pending DoDeferredPaint call at any time...
526 bool paint_pending = !paint_rect_.IsEmpty();
527
528 // If this invalidate overlaps with a pending scroll, then we have to
529 // downgrade to invalidating the scroll rect.
530 if (rect.Intersects(scroll_rect_)) {
531 paint_rect_ = paint_rect_.Union(scroll_rect_);
532 scroll_rect_ = gfx::Rect();
533 }
534
535 gfx::Rect view_rect(0, 0, size_.width(), size_.height());
536 // TODO(iyengar) Investigate why we have painting issues when
537 // we ignore invalid regions outside the view.
538 // Ignore invalidates that occur outside the bounds of the view
539 // TODO(darin): maybe this should move into the paint code?
540 // paint_rect_ = view_rect.Intersect(paint_rect_.Union(rect));
541 paint_rect_ = paint_rect_.Union(view_rect.Intersect(rect));
542
543 if (paint_rect_.IsEmpty() || paint_reply_pending() || paint_pending)
544 return;
545
546 // Perform painting asynchronously. This serves two purposes:
547 // 1) Ensures that we call WebView::Paint without a bunch of other junk
548 // on the call stack.
549 // 2) Allows us to collect more damage rects before painting to help coalesce
550 // the work that we will need to do.
551 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
552 this, &RenderWidget::DoDeferredPaint));
553}
554
555void RenderWidget::DidScrollRect(WebWidget* webwidget, int dx, int dy,
556 const gfx::Rect& clip_rect) {
557 // We only support scrolling along one axis at a time.
558 DCHECK((dx && !dy) || (!dx && dy));
559
560 bool intersects_with_painting = paint_rect_.Intersects(clip_rect);
561
562 // If we already have a pending scroll operation or if this scroll operation
563 // intersects the existing paint region, then just failover to invalidating.
564 if (!scroll_rect_.IsEmpty() || intersects_with_painting) {
565 if (!intersects_with_painting && scroll_rect_ == clip_rect) {
566 // OK, we can just update the scroll delta (requires same scrolling axis)
567 if (!dx && !scroll_delta_.x()) {
568 scroll_delta_.set_y(scroll_delta_.y() + dy);
569 return;
570 }
571 if (!dy && !scroll_delta_.y()) {
572 scroll_delta_.set_x(scroll_delta_.x() + dx);
573 return;
574 }
575 }
576 DidInvalidateRect(webwidget_, scroll_rect_);
577 DCHECK(scroll_rect_.IsEmpty());
578 DidInvalidateRect(webwidget_, clip_rect);
579 return;
580 }
581
582 // We only want one pending DoDeferredScroll call at any time...
583 bool scroll_pending = !scroll_rect_.IsEmpty();
584
585 scroll_rect_ = clip_rect;
586 scroll_delta_.SetPoint(dx, dy);
587
588 if (scroll_pending)
589 return;
590
591 // Perform scrolling asynchronously since we need to call WebView::Paint
592 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
593 this, &RenderWidget::DoDeferredScroll));
594}
595
596void RenderWidget::SetCursor(WebWidget* webwidget, const WebCursor& cursor) {
597 // Only send a SetCursor message if we need to make a change.
598 if (!current_cursor_.IsEqual(cursor)) {
599 current_cursor_ = cursor;
600 Send(new ViewHostMsg_SetCursor(routing_id_, cursor));
601 }
602}
603
604// We are supposed to get a single call to Show for a newly created RenderWidget
605// that was created via RenderWidget::CreateWebView. So, we wait until this
606// point to dispatch the ShowWidget message.
607//
608// This method provides us with the information about how to display the newly
609// created RenderWidget (i.e., as a constrained popup or as a new tab).
610//
611void RenderWidget::Show(WebWidget* webwidget,
612 WindowOpenDisposition disposition) {
613 DCHECK(!did_show_) << "received extraneous Show call";
614 DCHECK(routing_id_ != MSG_ROUTING_NONE);
615 DCHECK(opener_id_ != MSG_ROUTING_NONE);
616
617 if (!did_show_) {
618 did_show_ = true;
619 // NOTE: initial_pos_ may still have its default values at this point, but
620 // that's okay. It'll be ignored if as_popup is false, or the browser
621 // process will impose a default position otherwise.
622 RenderThread::current()->Send(new ViewHostMsg_ShowWidget(
623 opener_id_, routing_id_, initial_pos_));
624 }
625}
626
627void RenderWidget::Focus(WebWidget* webwidget) {
628 // Prevent the widget from stealing the focus if it does not have focus
629 // already. We do this by explicitely setting the focus to false again.
630 // We only let the browser focus the renderer.
631 if (!has_focus_ && webwidget_) {
632 MessageLoop::current()->PostTask(FROM_HERE,
633 NewRunnableMethod(this, &RenderWidget::ClearFocus));
634 }
635}
636
637void RenderWidget::Blur(WebWidget* webwidget) {
638 Send(new ViewHostMsg_Blur(routing_id_));
639}
640
641void RenderWidget::CloseWidgetSoon(WebWidget* webwidget) {
642 // If a page calls window.close() twice, we'll end up here twice, but that's
643 // OK. It is safe to send multiple Close messages.
644
645 // Ask the RenderWidgetHost to initiate close.
646 Send(new ViewHostMsg_Close(routing_id_));
647}
648
649void RenderWidget::Close() {
650 if (webwidget_) {
651 webwidget_->Close();
652 webwidget_ = NULL;
653 }
654}
655
656void RenderWidget::GetWindowLocation(WebWidget* webwidget, gfx::Point* origin) {
657 gfx::Rect rect;
658 Send(new ViewHostMsg_GetWindowRect(routing_id_, host_window_, &rect));
659 *origin = rect.origin();
660}
661
662void RenderWidget::SetWindowRect(WebWidget* webwidget, const gfx::Rect& pos) {
663 if (did_show_) {
664 Send(new ViewHostMsg_RequestMove(routing_id_, pos));
665 } else {
666 initial_pos_ = pos;
667 }
668}
669
670void RenderWidget::OnImeSetInputMode(bool is_active) {
671 // A renderer process may move its input focus and the caret position
672 // while a browser process stop receiving IPC messages.
673 // Thus, when a browser process requests for a renderer process to send
674 // IPC messages, it has to check whether or not a renderer process moves
675 // its input focus and send an IPC message if they are updated.
676 ime_is_active_ = is_active;
677 ime_control_updated_ = true;
678 ime_control_new_state_ = true;
679}
680
681void RenderWidget::OnImeSetComposition(int string_type,
682 int cursor_position,
683 int target_start, int target_end,
684 const std::wstring& ime_string) {
685 if (webwidget_) {
686 int string_length = static_cast<int>(ime_string.length());
687 const wchar_t* string_data = ime_string.data();
688 webwidget_->ImeSetComposition(string_type, cursor_position,
689 target_start, target_end,
690 string_length, string_data);
691 }
692}
693
694void RenderWidget::UpdateIME() {
695 // If a browser process does not have IMEs, its IMEs are not active, or there
696 // are not any attached widgets.
697 // a renderer process does not have to retrieve information of the focused
698 // control or send notification messages to a browser process.
699 if (!ime_is_active_) {
700 return;
701 }
702 // Retrieve the caret position from the focused widget.
703 bool enable_ime;
704 int x, y;
705 const void *id;
706 if (!webwidget_ || !webwidget_->ImeUpdateStatus(&enable_ime, &id, &x, &y)) {
707 // There are not any editable widgets attached to this process.
708 // We should disable the IME to prevent it from sending CJK strings to
709 // non-editable widgets.
710 ime_control_updated_ = true;
711 ime_control_new_state_ = false;
712 }
713 if (ime_control_updated_) {
714 // The input focus has been changed.
715 // Compare the current state with the updated state and choose actions.
716 if (ime_control_enable_ime_) {
717 if (ime_control_new_state_) {
718 // Case 1: a text input -> another text input
719 // Complete the current composition and notify the caret position.
720 enum ViewHostMsg_ImeControl control = IME_COMPLETE_COMPOSITION;
721 Send(new ViewHostMsg_ImeUpdateStatus(routing_id(), control, x, y));
722 } else {
723 // Case 2: a text input -> a password input (or a static control)
724 // Complete the current composition and disable the IME.
725 enum ViewHostMsg_ImeControl control = IME_DISABLE;
726 Send(new ViewHostMsg_ImeUpdateStatus(routing_id(), control, x, y));
727 }
728 } else {
729 if (ime_control_new_state_) {
730 // Case 3: a password input (or a static control) -> a text input
731 // Enable the IME and notify the caret position.
732 enum ViewHostMsg_ImeControl control = IME_COMPLETE_COMPOSITION;
733 Send(new ViewHostMsg_ImeUpdateStatus(routing_id(), control, x, y));
734 } else {
735 // Case 4: a password input (or a static contol) -> another password
736 // input (or another static control).
737 // The IME has been already disabled and we don't have to do anything.
738 }
739 }
740 } else {
741 // The input focus is not changed.
742 // Notify the caret position to a browser process only if it is changed.
743 if (ime_control_enable_ime_) {
744 if (x != ime_control_x_ || y != ime_control_y_) {
745 enum ViewHostMsg_ImeControl control = IME_MOVE_WINDOWS;
746 Send(new ViewHostMsg_ImeUpdateStatus(routing_id(), control, x, y));
747 }
748 }
749 }
750 // Save the updated IME status to prevent from sending the same IPC messages.
751 ime_control_updated_ = false;
752 ime_control_enable_ime_ = ime_control_new_state_;
753 ime_control_x_ = x;
754 ime_control_y_ = y;
755}
756
757void RenderWidget::DidMove(WebWidget* webwidget,
758 const WebPluginGeometry& move) {
759 size_t i = 0;
760 for (; i < plugin_window_moves_.size(); ++i) {
761 if (plugin_window_moves_[i].window == move.window) {
762 plugin_window_moves_[i] = move;
763 break;
764 }
765 }
766
767 if (i == plugin_window_moves_.size())
768 plugin_window_moves_.push_back(move);
769}