1// Copyright 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "content/renderer/browser_plugin/browser_plugin.h"
6
7#include "base/command_line.h"
8#include "base/message_loop/message_loop.h"
9#include "base/strings/string_number_conversions.h"
10#include "base/strings/utf_string_conversions.h"
11#include "content/common/browser_plugin/browser_plugin_constants.h"
12#include "content/common/browser_plugin/browser_plugin_messages.h"
13#include "content/common/view_messages.h"
14#include "content/public/common/content_client.h"
15#include "content/public/common/content_switches.h"
16#include "content/public/renderer/browser_plugin_delegate.h"
17#include "content/public/renderer/content_renderer_client.h"
18#include "content/renderer/browser_plugin/browser_plugin_manager.h"
19#include "content/renderer/child_frame_compositing_helper.h"
20#include "content/renderer/cursor_utils.h"
21#include "content/renderer/drop_data_builder.h"
22#include "content/renderer/render_thread_impl.h"
23#include "content/renderer/sad_plugin.h"
24#include "third_party/WebKit/public/platform/WebRect.h"
25#include "third_party/WebKit/public/web/WebElement.h"
26#include "third_party/WebKit/public/web/WebInputEvent.h"
27#include "third_party/WebKit/public/web/WebPluginContainer.h"
28#include "third_party/WebKit/public/web/WebView.h"
29#include "third_party/skia/include/core/SkCanvas.h"
30#include "ui/events/keycodes/keyboard_codes.h"
31
32using blink::WebCanvas;
33using blink::WebPluginContainer;
34using blink::WebPoint;
35using blink::WebRect;
36using blink::WebURL;
37using blink::WebVector;
38
39namespace {
40typedef std::map<blink::WebPluginContainer*, content::BrowserPlugin*>
41    PluginContainerMap;
42static base::LazyInstance<PluginContainerMap> g_plugin_container_map =
43    LAZY_INSTANCE_INITIALIZER;
44}  // namespace
45
46namespace content {
47
48// static
49BrowserPlugin* BrowserPlugin::GetFromNode(blink::WebNode& node) {
50  blink::WebPluginContainer* container = node.pluginContainer();
51  if (!container)
52    return NULL;
53
54  PluginContainerMap* browser_plugins = g_plugin_container_map.Pointer();
55  PluginContainerMap::iterator it = browser_plugins->find(container);
56  return it == browser_plugins->end() ? NULL : it->second;
57}
58
59BrowserPlugin::BrowserPlugin(RenderViewImpl* render_view,
60                             blink::WebFrame* frame,
61                             scoped_ptr<BrowserPluginDelegate> delegate)
62    : attached_(false),
63      attach_pending_(false),
64      render_view_(render_view->AsWeakPtr()),
65      render_view_routing_id_(render_view->GetRoutingID()),
66      container_(NULL),
67      last_device_scale_factor_(GetDeviceScaleFactor()),
68      sad_guest_(NULL),
69      guest_crashed_(false),
70      plugin_focused_(false),
71      visible_(true),
72      mouse_locked_(false),
73      browser_plugin_manager_(render_view->GetBrowserPluginManager()),
74      browser_plugin_instance_id_(browser_plugin::kInstanceIDNone),
75      contents_opaque_(true),
76      delegate_(delegate.Pass()),
77      weak_ptr_factory_(this) {
78  browser_plugin_instance_id_ = browser_plugin_manager()->GetNextInstanceID();
79
80  if (delegate_)
81    delegate_->SetElementInstanceID(browser_plugin_instance_id_);
82}
83
84BrowserPlugin::~BrowserPlugin() {
85  browser_plugin_manager()->RemoveBrowserPlugin(browser_plugin_instance_id_);
86
87  if (!ready())
88    return;
89
90  browser_plugin_manager()->Send(
91      new BrowserPluginHostMsg_PluginDestroyed(render_view_routing_id_,
92                                               browser_plugin_instance_id_));
93}
94
95bool BrowserPlugin::OnMessageReceived(const IPC::Message& message) {
96  bool handled = true;
97  IPC_BEGIN_MESSAGE_MAP(BrowserPlugin, message)
98    IPC_MESSAGE_HANDLER(BrowserPluginMsg_Attach_ACK, OnAttachACK)
99    IPC_MESSAGE_HANDLER(BrowserPluginMsg_AdvanceFocus, OnAdvanceFocus)
100    IPC_MESSAGE_HANDLER_GENERIC(BrowserPluginMsg_CompositorFrameSwapped,
101                                OnCompositorFrameSwapped(message))
102    IPC_MESSAGE_HANDLER(BrowserPluginMsg_CopyFromCompositingSurface,
103                        OnCopyFromCompositingSurface)
104    IPC_MESSAGE_HANDLER(BrowserPluginMsg_GuestGone, OnGuestGone)
105    IPC_MESSAGE_HANDLER(BrowserPluginMsg_SetContentsOpaque, OnSetContentsOpaque)
106    IPC_MESSAGE_HANDLER(BrowserPluginMsg_SetCursor, OnSetCursor)
107    IPC_MESSAGE_HANDLER(BrowserPluginMsg_SetMouseLock, OnSetMouseLock)
108    IPC_MESSAGE_HANDLER(BrowserPluginMsg_ShouldAcceptTouchEvents,
109                        OnShouldAcceptTouchEvents)
110    IPC_MESSAGE_UNHANDLED(handled = false)
111  IPC_END_MESSAGE_MAP()
112  return handled;
113}
114
115void BrowserPlugin::UpdateDOMAttribute(const std::string& attribute_name,
116                                       const std::string& attribute_value) {
117  if (!container())
118    return;
119
120  blink::WebElement element = container()->element();
121  blink::WebString web_attribute_name =
122      blink::WebString::fromUTF8(attribute_name);
123  element.setAttribute(web_attribute_name,
124      blink::WebString::fromUTF8(attribute_value));
125}
126
127void BrowserPlugin::Attach() {
128  if (ready()) {
129    attached_ = false;
130    guest_crashed_ = false;
131    EnableCompositing(false);
132    if (compositing_helper_.get()) {
133      compositing_helper_->OnContainerDestroy();
134      compositing_helper_ = NULL;
135    }
136  }
137
138  // TODO(fsamuel): Add support for reattachment.
139  BrowserPluginHostMsg_Attach_Params attach_params;
140  attach_params.focused = ShouldGuestBeFocused();
141  attach_params.visible = visible_;
142  attach_params.origin = plugin_rect().origin();
143  gfx::Size view_size(width(), height());
144  if (!view_size.IsEmpty()) {
145    PopulateResizeGuestParameters(view_size,
146                                  &attach_params.resize_guest_params);
147  }
148  browser_plugin_manager()->Send(new BrowserPluginHostMsg_Attach(
149      render_view_routing_id_,
150      browser_plugin_instance_id_,
151      attach_params));
152
153  attach_pending_ = true;
154}
155
156void BrowserPlugin::DidCommitCompositorFrame() {
157  if (compositing_helper_.get())
158    compositing_helper_->DidCommitCompositorFrame();
159}
160
161void BrowserPlugin::OnAdvanceFocus(int browser_plugin_instance_id,
162                                   bool reverse) {
163  DCHECK(render_view_);
164  render_view_->GetWebView()->advanceFocus(reverse);
165}
166
167void BrowserPlugin::OnAttachACK(int browser_plugin_instance_id) {
168  DCHECK(!attached());
169  attached_ = true;
170  attach_pending_ = false;
171}
172
173void BrowserPlugin::OnCompositorFrameSwapped(const IPC::Message& message) {
174  BrowserPluginMsg_CompositorFrameSwapped::Param param;
175  if (!BrowserPluginMsg_CompositorFrameSwapped::Read(&message, &param))
176    return;
177
178  // Note that there is no need to send ACK for this message.
179  // If the guest has updated pixels then it is no longer crashed.
180  guest_crashed_ = false;
181
182  scoped_ptr<cc::CompositorFrame> frame(new cc::CompositorFrame);
183  param.b.frame.AssignTo(frame.get());
184
185  EnableCompositing(true);
186  compositing_helper_->OnCompositorFrameSwapped(frame.Pass(),
187                                                param.b.producing_route_id,
188                                                param.b.output_surface_id,
189                                                param.b.producing_host_id,
190                                                param.b.shared_memory_handle);
191}
192
193void BrowserPlugin::OnCopyFromCompositingSurface(int browser_plugin_instance_id,
194                                                 int request_id,
195                                                 gfx::Rect source_rect,
196                                                 gfx::Size dest_size) {
197  if (!compositing_helper_.get()) {
198    browser_plugin_manager()->Send(
199        new BrowserPluginHostMsg_CopyFromCompositingSurfaceAck(
200            render_view_routing_id_,
201            browser_plugin_instance_id_,
202            request_id,
203            SkBitmap()));
204    return;
205  }
206  compositing_helper_->CopyFromCompositingSurface(request_id, source_rect,
207                                                  dest_size);
208}
209
210void BrowserPlugin::OnGuestGone(int browser_plugin_instance_id) {
211  guest_crashed_ = true;
212
213  // Turn off compositing so we can display the sad graphic. Changes to
214  // compositing state will show up at a later time after a layout and commit.
215  EnableCompositing(false);
216
217  // Queue up showing the sad graphic to give content embedders an opportunity
218  // to fire their listeners and potentially overlay the webview with custom
219  // behavior. If the BrowserPlugin is destroyed in the meantime, then the
220  // task will not be executed.
221  base::MessageLoop::current()->PostTask(
222      FROM_HERE,
223      base::Bind(&BrowserPlugin::ShowSadGraphic,
224                 weak_ptr_factory_.GetWeakPtr()));
225}
226
227void BrowserPlugin::OnSetContentsOpaque(int browser_plugin_instance_id,
228                                        bool opaque) {
229  if (contents_opaque_ == opaque)
230    return;
231  contents_opaque_ = opaque;
232  if (compositing_helper_.get())
233    compositing_helper_->SetContentsOpaque(opaque);
234}
235
236void BrowserPlugin::OnSetCursor(int browser_plugin_instance_id,
237                                const WebCursor& cursor) {
238  cursor_ = cursor;
239}
240
241void BrowserPlugin::OnSetMouseLock(int browser_plugin_instance_id,
242                                   bool enable) {
243  if (enable) {
244    if (mouse_locked_)
245      return;
246    render_view_->mouse_lock_dispatcher()->LockMouse(this);
247  } else {
248    if (!mouse_locked_) {
249      OnLockMouseACK(false);
250      return;
251    }
252    render_view_->mouse_lock_dispatcher()->UnlockMouse(this);
253  }
254}
255
256void BrowserPlugin::OnShouldAcceptTouchEvents(int browser_plugin_instance_id,
257                                              bool accept) {
258  if (container()) {
259    container()->requestTouchEventType(
260        accept ? WebPluginContainer::TouchEventRequestTypeRaw
261               : WebPluginContainer::TouchEventRequestTypeNone);
262  }
263}
264
265void BrowserPlugin::ShowSadGraphic() {
266  // If the BrowserPlugin is scheduled to be deleted, then container_ will be
267  // NULL so we shouldn't attempt to access it.
268  if (container_)
269    container_->invalidate();
270}
271
272float BrowserPlugin::GetDeviceScaleFactor() const {
273  if (!render_view_)
274    return 1.0f;
275  return render_view_->GetWebView()->deviceScaleFactor();
276}
277
278void BrowserPlugin::UpdateDeviceScaleFactor() {
279  if (last_device_scale_factor_ == GetDeviceScaleFactor())
280    return;
281
282  BrowserPluginHostMsg_ResizeGuest_Params params;
283  PopulateResizeGuestParameters(plugin_size(), &params);
284  browser_plugin_manager()->Send(new BrowserPluginHostMsg_ResizeGuest(
285      render_view_routing_id_,
286      browser_plugin_instance_id_,
287      params));
288}
289
290void BrowserPlugin::UpdateGuestFocusState() {
291  if (!ready())
292    return;
293  bool should_be_focused = ShouldGuestBeFocused();
294  browser_plugin_manager()->Send(new BrowserPluginHostMsg_SetFocus(
295      render_view_routing_id_,
296      browser_plugin_instance_id_,
297      should_be_focused));
298}
299
300bool BrowserPlugin::ShouldGuestBeFocused() const {
301  bool embedder_focused = false;
302  if (render_view_)
303    embedder_focused = render_view_->has_focus();
304  return plugin_focused_ && embedder_focused;
305}
306
307WebPluginContainer* BrowserPlugin::container() const {
308  return container_;
309}
310
311bool BrowserPlugin::initialize(WebPluginContainer* container) {
312  if (!container)
313    return false;
314
315  container_ = container;
316  container_->setWantsWheelEvents(true);
317
318  g_plugin_container_map.Get().insert(std::make_pair(container_, this));
319
320  // This is a way to notify observers of our attributes that this plugin is
321  // available in render tree.
322  // TODO(lazyboy): This should be done through the delegate instead. Perhaps
323  // by firing an event from there.
324  UpdateDOMAttribute("internalinstanceid",
325                     base::IntToString(browser_plugin_instance_id_));
326
327  browser_plugin_manager()->AddBrowserPlugin(browser_plugin_instance_id_, this);
328  return true;
329}
330
331void BrowserPlugin::EnableCompositing(bool enable) {
332  bool enabled = !!compositing_helper_.get();
333  if (enabled == enable)
334    return;
335
336  if (enable) {
337    DCHECK(!compositing_helper_.get());
338    if (!compositing_helper_.get()) {
339      compositing_helper_ = ChildFrameCompositingHelper::CreateForBrowserPlugin(
340          weak_ptr_factory_.GetWeakPtr());
341    }
342  }
343  compositing_helper_->EnableCompositing(enable);
344  compositing_helper_->SetContentsOpaque(contents_opaque_);
345
346  if (!enable) {
347    DCHECK(compositing_helper_.get());
348    compositing_helper_->OnContainerDestroy();
349    compositing_helper_ = NULL;
350  }
351}
352
353void BrowserPlugin::destroy() {
354  if (container_) {
355    //container_->clearScriptObjects();
356
357    // The BrowserPlugin's WebPluginContainer is deleted immediately after this
358    // call returns, so let's not keep a reference to it around.
359    g_plugin_container_map.Get().erase(container_);
360  }
361
362  if (compositing_helper_.get())
363    compositing_helper_->OnContainerDestroy();
364  container_ = NULL;
365  // Will be a no-op if the mouse is not currently locked.
366  if (render_view_)
367    render_view_->mouse_lock_dispatcher()->OnLockTargetDestroyed(this);
368  base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
369}
370
371bool BrowserPlugin::supportsKeyboardFocus() const {
372  return true;
373}
374
375bool BrowserPlugin::supportsEditCommands() const {
376  return true;
377}
378
379bool BrowserPlugin::supportsInputMethod() const {
380  return true;
381}
382
383bool BrowserPlugin::canProcessDrag() const {
384  return true;
385}
386
387void BrowserPlugin::paint(WebCanvas* canvas, const WebRect& rect) {
388  if (guest_crashed_) {
389    if (!sad_guest_)  // Lazily initialize bitmap.
390      sad_guest_ = content::GetContentClient()->renderer()->
391          GetSadWebViewBitmap();
392    // content_shell does not have the sad plugin bitmap, so we'll paint black
393    // instead to make it clear that something went wrong.
394    if (sad_guest_) {
395      PaintSadPlugin(canvas, plugin_rect_, *sad_guest_);
396      return;
397    }
398  }
399  SkAutoCanvasRestore auto_restore(canvas, true);
400  canvas->translate(plugin_rect_.x(), plugin_rect_.y());
401  SkRect image_data_rect = SkRect::MakeXYWH(
402      SkIntToScalar(0),
403      SkIntToScalar(0),
404      SkIntToScalar(plugin_rect_.width()),
405      SkIntToScalar(plugin_rect_.height()));
406  canvas->clipRect(image_data_rect);
407  // Paint black or white in case we have nothing in our backing store or we
408  // need to show a gutter.
409  SkPaint paint;
410  paint.setStyle(SkPaint::kFill_Style);
411  paint.setColor(guest_crashed_ ? SK_ColorBLACK : SK_ColorWHITE);
412  canvas->drawRect(image_data_rect, paint);
413}
414
415// static
416bool BrowserPlugin::ShouldForwardToBrowserPlugin(
417    const IPC::Message& message) {
418  switch (message.type()) {
419    case BrowserPluginMsg_Attach_ACK::ID:
420    case BrowserPluginMsg_AdvanceFocus::ID:
421    case BrowserPluginMsg_CompositorFrameSwapped::ID:
422    case BrowserPluginMsg_CopyFromCompositingSurface::ID:
423    case BrowserPluginMsg_GuestGone::ID:
424    case BrowserPluginMsg_SetContentsOpaque::ID:
425    case BrowserPluginMsg_SetCursor::ID:
426    case BrowserPluginMsg_SetMouseLock::ID:
427    case BrowserPluginMsg_ShouldAcceptTouchEvents::ID:
428      return true;
429    default:
430      break;
431  }
432  return false;
433}
434
435void BrowserPlugin::updateGeometry(
436    const WebRect& window_rect,
437    const WebRect& clip_rect,
438    const WebVector<WebRect>& cut_outs_rects,
439    bool is_visible) {
440  int old_width = width();
441  int old_height = height();
442  plugin_rect_ = window_rect;
443  if (!attached())
444    return;
445
446  if (old_width == window_rect.width && old_height == window_rect.height) {
447    // Let the browser know about the updated view rect.
448    browser_plugin_manager()->Send(new BrowserPluginHostMsg_UpdateGeometry(
449        render_view_routing_id_, browser_plugin_instance_id_, plugin_rect_));
450    return;
451  }
452
453  BrowserPluginHostMsg_ResizeGuest_Params params;
454  PopulateResizeGuestParameters(plugin_size(), &params);
455  browser_plugin_manager()->Send(new BrowserPluginHostMsg_ResizeGuest(
456      render_view_routing_id_,
457      browser_plugin_instance_id_,
458      params));
459}
460
461void BrowserPlugin::PopulateResizeGuestParameters(
462    const gfx::Size& view_size,
463    BrowserPluginHostMsg_ResizeGuest_Params* params) {
464  params->view_size = view_size;
465  params->scale_factor = GetDeviceScaleFactor();
466  if (last_device_scale_factor_ != params->scale_factor) {
467    last_device_scale_factor_ = params->scale_factor;
468    params->repaint = true;
469  }
470}
471
472void BrowserPlugin::updateFocus(bool focused) {
473  plugin_focused_ = focused;
474  UpdateGuestFocusState();
475}
476
477void BrowserPlugin::updateVisibility(bool visible) {
478  if (visible_ == visible)
479    return;
480
481  visible_ = visible;
482  if (!ready())
483    return;
484
485  if (compositing_helper_.get())
486    compositing_helper_->UpdateVisibility(visible);
487
488  browser_plugin_manager()->Send(new BrowserPluginHostMsg_SetVisibility(
489      render_view_routing_id_,
490      browser_plugin_instance_id_,
491      visible));
492}
493
494bool BrowserPlugin::acceptsInputEvents() {
495  return true;
496}
497
498bool BrowserPlugin::handleInputEvent(const blink::WebInputEvent& event,
499                                     blink::WebCursorInfo& cursor_info) {
500  if (guest_crashed_ || !ready())
501    return false;
502
503  if (event.type == blink::WebInputEvent::ContextMenu)
504    return true;
505
506  if (blink::WebInputEvent::isKeyboardEventType(event.type) &&
507      !edit_commands_.empty()) {
508    browser_plugin_manager()->Send(
509        new BrowserPluginHostMsg_SetEditCommandsForNextKeyEvent(
510            render_view_routing_id_,
511            browser_plugin_instance_id_,
512            edit_commands_));
513    edit_commands_.clear();
514  }
515
516  browser_plugin_manager()->Send(
517      new BrowserPluginHostMsg_HandleInputEvent(render_view_routing_id_,
518                                                browser_plugin_instance_id_,
519                                                plugin_rect_,
520                                                &event));
521  GetWebKitCursorInfo(cursor_, &cursor_info);
522  return true;
523}
524
525bool BrowserPlugin::handleDragStatusUpdate(blink::WebDragStatus drag_status,
526                                           const blink::WebDragData& drag_data,
527                                           blink::WebDragOperationsMask mask,
528                                           const blink::WebPoint& position,
529                                           const blink::WebPoint& screen) {
530  if (guest_crashed_ || !ready())
531    return false;
532  browser_plugin_manager()->Send(
533      new BrowserPluginHostMsg_DragStatusUpdate(
534        render_view_routing_id_,
535        browser_plugin_instance_id_,
536        drag_status,
537        DropDataBuilder::Build(drag_data),
538        mask,
539        position));
540  return true;
541}
542
543void BrowserPlugin::didReceiveResponse(
544    const blink::WebURLResponse& response) {
545}
546
547void BrowserPlugin::didReceiveData(const char* data, int data_length) {
548  if (delegate_)
549    delegate_->DidReceiveData(data, data_length);
550}
551
552void BrowserPlugin::didFinishLoading() {
553  if (delegate_)
554    delegate_->DidFinishLoading();
555}
556
557void BrowserPlugin::didFailLoading(const blink::WebURLError& error) {
558}
559
560void BrowserPlugin::didFinishLoadingFrameRequest(const blink::WebURL& url,
561                                                 void* notify_data) {
562}
563
564void BrowserPlugin::didFailLoadingFrameRequest(
565    const blink::WebURL& url,
566    void* notify_data,
567    const blink::WebURLError& error) {
568}
569
570bool BrowserPlugin::executeEditCommand(const blink::WebString& name) {
571  browser_plugin_manager()->Send(new BrowserPluginHostMsg_ExecuteEditCommand(
572      render_view_routing_id_,
573      browser_plugin_instance_id_,
574      name.utf8()));
575
576  // BrowserPlugin swallows edit commands.
577  return true;
578}
579
580bool BrowserPlugin::executeEditCommand(const blink::WebString& name,
581                                       const blink::WebString& value) {
582  edit_commands_.push_back(EditCommand(name.utf8(), value.utf8()));
583  // BrowserPlugin swallows edit commands.
584  return true;
585}
586
587bool BrowserPlugin::setComposition(
588    const blink::WebString& text,
589    const blink::WebVector<blink::WebCompositionUnderline>& underlines,
590    int selectionStart,
591    int selectionEnd) {
592  if (!ready())
593    return false;
594  std::vector<blink::WebCompositionUnderline> std_underlines;
595  for (size_t i = 0; i < underlines.size(); ++i) {
596    std_underlines.push_back(underlines[i]);
597  }
598  browser_plugin_manager()->Send(new BrowserPluginHostMsg_ImeSetComposition(
599      render_view_routing_id_,
600      browser_plugin_instance_id_,
601      text.utf8(),
602      std_underlines,
603      selectionStart,
604      selectionEnd));
605  // TODO(kochi): This assumes the IPC handling always succeeds.
606  return true;
607}
608
609bool BrowserPlugin::confirmComposition(
610    const blink::WebString& text,
611    blink::WebWidget::ConfirmCompositionBehavior selectionBehavior) {
612  if (!ready())
613    return false;
614  bool keep_selection = (selectionBehavior == blink::WebWidget::KeepSelection);
615  browser_plugin_manager()->Send(new BrowserPluginHostMsg_ImeConfirmComposition(
616      render_view_routing_id_,
617      browser_plugin_instance_id_,
618      text.utf8(),
619      keep_selection));
620  // TODO(kochi): This assumes the IPC handling always succeeds.
621  return true;
622}
623
624void BrowserPlugin::extendSelectionAndDelete(int before, int after) {
625  if (!ready())
626    return;
627  browser_plugin_manager()->Send(
628      new BrowserPluginHostMsg_ExtendSelectionAndDelete(
629          render_view_routing_id_,
630          browser_plugin_instance_id_,
631          before,
632          after));
633}
634
635void BrowserPlugin::OnLockMouseACK(bool succeeded) {
636  mouse_locked_ = succeeded;
637  browser_plugin_manager()->Send(new BrowserPluginHostMsg_LockMouse_ACK(
638      render_view_routing_id_,
639      browser_plugin_instance_id_,
640      succeeded));
641}
642
643void BrowserPlugin::OnMouseLockLost() {
644  mouse_locked_ = false;
645  browser_plugin_manager()->Send(new BrowserPluginHostMsg_UnlockMouse_ACK(
646      render_view_routing_id_,
647      browser_plugin_instance_id_));
648}
649
650bool BrowserPlugin::HandleMouseLockedInputEvent(
651    const blink::WebMouseEvent& event) {
652  browser_plugin_manager()->Send(
653      new BrowserPluginHostMsg_HandleInputEvent(render_view_routing_id_,
654                                                browser_plugin_instance_id_,
655                                                plugin_rect_,
656                                                &event));
657  return true;
658}
659
660}  // namespace content
661