browser_plugin.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
1// Copyright (c) 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/json/json_string_value_serializer.h"
9#include "base/message_loop.h"
10#include "base/string_number_conversions.h"
11#include "base/string_util.h"
12#include "base/utf_string_conversions.h"
13#include "content/common/browser_plugin/browser_plugin_constants.h"
14#include "content/common/browser_plugin/browser_plugin_messages.h"
15#include "content/common/view_messages.h"
16#include "content/public/common/content_client.h"
17#include "content/public/common/content_switches.h"
18#include "content/public/renderer/content_renderer_client.h"
19#include "content/renderer/browser_plugin/browser_plugin_bindings.h"
20#include "content/renderer/browser_plugin/browser_plugin_compositing_helper.h"
21#include "content/renderer/browser_plugin/browser_plugin_manager.h"
22#include "content/renderer/render_process_impl.h"
23#include "content/renderer/render_thread_impl.h"
24#include "content/renderer/v8_value_converter_impl.h"
25#include "skia/ext/platform_canvas.h"
26#include "third_party/WebKit/Source/Platform/chromium/public/WebRect.h"
27#include "third_party/WebKit/Source/WebKit/chromium/public/WebBindings.h"
28#include "third_party/WebKit/Source/WebKit/chromium/public/WebDOMCustomEvent.h"
29#include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
30#include "third_party/WebKit/Source/WebKit/chromium/public/WebElement.h"
31#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
32#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h"
33#include "third_party/WebKit/Source/WebKit/chromium/public/WebPluginContainer.h"
34#include "third_party/WebKit/Source/WebKit/chromium/public/WebPluginParams.h"
35#include "third_party/WebKit/Source/WebKit/chromium/public/WebScriptSource.h"
36#include "ui/base/keycodes/keyboard_codes.h"
37#include "webkit/plugins/sad_plugin.h"
38
39#if defined (OS_WIN)
40#include "base/sys_info.h"
41#endif
42
43using WebKit::WebCanvas;
44using WebKit::WebPluginContainer;
45using WebKit::WebPluginParams;
46using WebKit::WebPoint;
47using WebKit::WebRect;
48using WebKit::WebURL;
49using WebKit::WebVector;
50
51namespace content {
52
53namespace {
54
55static std::string TerminationStatusToString(base::TerminationStatus status) {
56  switch (status) {
57    case base::TERMINATION_STATUS_NORMAL_TERMINATION:
58      return "normal";
59    case base::TERMINATION_STATUS_ABNORMAL_TERMINATION:
60    case base::TERMINATION_STATUS_STILL_RUNNING:
61      return "abnormal";
62    case base::TERMINATION_STATUS_PROCESS_WAS_KILLED:
63      return "killed";
64    case base::TERMINATION_STATUS_PROCESS_CRASHED:
65      return "crashed";
66    case base::TERMINATION_STATUS_MAX_ENUM:
67      break;
68  }
69  NOTREACHED() << "Unknown Termination Status.";
70  return "unknown";
71}
72
73static std::string GetInternalEventName(const char* event_name) {
74  return base::StringPrintf("-internal-%s", event_name);
75}
76
77static std::string PermissionTypeToString(BrowserPluginPermissionType type) {
78  switch (type) {
79    case BrowserPluginPermissionTypeDownload:
80      return browser_plugin::kPermissionTypeDownload;
81    case BrowserPluginPermissionTypeGeolocation:
82      return browser_plugin::kPermissionTypeGeolocation;
83    case BrowserPluginPermissionTypeMedia:
84      return browser_plugin::kPermissionTypeMedia;
85    case BrowserPluginPermissionTypeNewWindow:
86      return browser_plugin::kPermissionTypeNewWindow;
87    case BrowserPluginPermissionTypePointerLock:
88      return browser_plugin::kPermissionTypePointerLock;
89    case BrowserPluginPermissionTypeUnknown:
90    default:
91      NOTREACHED();
92      break;
93  }
94  return std::string();
95}
96
97typedef std::map<WebKit::WebPluginContainer*,
98                 BrowserPlugin*> PluginContainerMap;
99static base::LazyInstance<PluginContainerMap> g_plugin_container_map =
100    LAZY_INSTANCE_INITIALIZER;
101
102}  // namespace
103
104BrowserPlugin::BrowserPlugin(
105    RenderViewImpl* render_view,
106    WebKit::WebFrame* frame,
107    const WebPluginParams& params)
108    : instance_id_(browser_plugin::kInstanceIDNone),
109      render_view_(render_view->AsWeakPtr()),
110      render_view_routing_id_(render_view->GetRoutingID()),
111      container_(NULL),
112      damage_buffer_sequence_id_(0),
113      resize_ack_received_(true),
114      last_device_scale_factor_(1.0f),
115      sad_guest_(NULL),
116      guest_crashed_(false),
117      auto_size_ack_pending_(false),
118      guest_process_id_(-1),
119      guest_route_id_(-1),
120      persist_storage_(false),
121      valid_partition_id_(true),
122      content_window_routing_id_(MSG_ROUTING_NONE),
123      plugin_focused_(false),
124      visible_(true),
125      size_changed_in_flight_(false),
126      before_first_navigation_(true),
127      browser_plugin_manager_(render_view->GetBrowserPluginManager()),
128      current_nav_entry_index_(0),
129      nav_entry_count_(0),
130      compositing_enabled_(false),
131      weak_ptr_factory_(this) {
132}
133
134BrowserPlugin::~BrowserPlugin() {
135  // If the BrowserPlugin has never navigated then the browser process and
136  // BrowserPluginManager don't know about it and so there is nothing to do
137  // here.
138  if (!HasGuest())
139    return;
140  browser_plugin_manager()->RemoveBrowserPlugin(instance_id_);
141  browser_plugin_manager()->Send(
142      new BrowserPluginHostMsg_PluginDestroyed(render_view_routing_id_,
143                                               instance_id_));
144}
145
146/*static*/
147BrowserPlugin* BrowserPlugin::FromContainer(
148    WebKit::WebPluginContainer* container) {
149  PluginContainerMap* browser_plugins = g_plugin_container_map.Pointer();
150  PluginContainerMap::iterator it = browser_plugins->find(container);
151  return it == browser_plugins->end() ? NULL : it->second;
152}
153
154bool BrowserPlugin::OnMessageReceived(const IPC::Message& message) {
155  bool handled = true;
156  IPC_BEGIN_MESSAGE_MAP(BrowserPlugin, message)
157    IPC_MESSAGE_HANDLER(BrowserPluginMsg_AddMessageToConsole,
158                        OnAddMessageToConsole)
159    IPC_MESSAGE_HANDLER(BrowserPluginMsg_AdvanceFocus, OnAdvanceFocus)
160    IPC_MESSAGE_HANDLER(BrowserPluginMsg_Attach_ACK, OnAttachACK)
161    IPC_MESSAGE_HANDLER(BrowserPluginMsg_BuffersSwapped, OnBuffersSwapped)
162    IPC_MESSAGE_HANDLER(BrowserPluginMsg_Close, OnClose)
163    IPC_MESSAGE_HANDLER_GENERIC(BrowserPluginMsg_CompositorFrameSwapped,
164                                OnCompositorFrameSwapped(message))
165    IPC_MESSAGE_HANDLER(BrowserPluginMsg_GuestContentWindowReady,
166                        OnGuestContentWindowReady)
167    IPC_MESSAGE_HANDLER(BrowserPluginMsg_GuestGone, OnGuestGone)
168    IPC_MESSAGE_HANDLER(BrowserPluginMsg_GuestResponsive, OnGuestResponsive)
169    IPC_MESSAGE_HANDLER(BrowserPluginMsg_GuestUnresponsive, OnGuestUnresponsive)
170    IPC_MESSAGE_HANDLER(BrowserPluginMsg_LoadAbort, OnLoadAbort)
171    IPC_MESSAGE_HANDLER(BrowserPluginMsg_LoadCommit, OnLoadCommit)
172    IPC_MESSAGE_HANDLER(BrowserPluginMsg_LoadRedirect, OnLoadRedirect)
173    IPC_MESSAGE_HANDLER(BrowserPluginMsg_LoadStart, OnLoadStart)
174    IPC_MESSAGE_HANDLER(BrowserPluginMsg_LoadStop, OnLoadStop)
175    IPC_MESSAGE_HANDLER(BrowserPluginMsg_RequestPermission, OnRequestPermission)
176    IPC_MESSAGE_HANDLER(BrowserPluginMsg_SetCursor, OnSetCursor)
177    IPC_MESSAGE_HANDLER(BrowserPluginMsg_ShouldAcceptTouchEvents,
178                        OnShouldAcceptTouchEvents)
179    IPC_MESSAGE_HANDLER(BrowserPluginMsg_UnlockMouse, OnUnlockMouse)
180    IPC_MESSAGE_HANDLER(BrowserPluginMsg_UpdatedName, OnUpdatedName)
181    IPC_MESSAGE_HANDLER(BrowserPluginMsg_UpdateRect, OnUpdateRect)
182    IPC_MESSAGE_UNHANDLED(handled = false)
183  IPC_END_MESSAGE_MAP()
184  return handled;
185}
186
187void BrowserPlugin::UpdateDOMAttribute(const std::string& attribute_name,
188                                       const std::string& attribute_value) {
189  if (!container())
190    return;
191
192  WebKit::WebElement element = container()->element();
193  WebKit::WebString web_attribute_name =
194      WebKit::WebString::fromUTF8(attribute_name);
195  if (!HasDOMAttribute(attribute_name) ||
196      (std::string(element.getAttribute(web_attribute_name).utf8()) !=
197          attribute_value)) {
198    element.setAttribute(web_attribute_name,
199        WebKit::WebString::fromUTF8(attribute_value));
200  }
201}
202
203void BrowserPlugin::RemoveDOMAttribute(const std::string& attribute_name) {
204  if (!container())
205    return;
206
207  container()->element().removeAttribute(
208      WebKit::WebString::fromUTF8(attribute_name));
209}
210
211std::string BrowserPlugin::GetDOMAttributeValue(
212    const std::string& attribute_name) const {
213  if (!container())
214    return std::string();
215
216  return container()->element().getAttribute(
217      WebKit::WebString::fromUTF8(attribute_name)).utf8();
218}
219
220bool BrowserPlugin::HasDOMAttribute(const std::string& attribute_name) const {
221  if (!container())
222    return false;
223
224  return container()->element().hasAttribute(
225      WebKit::WebString::fromUTF8(attribute_name));
226}
227
228std::string BrowserPlugin::GetNameAttribute() const {
229  return GetDOMAttributeValue(browser_plugin::kAttributeName);
230}
231
232std::string BrowserPlugin::GetSrcAttribute() const {
233  return GetDOMAttributeValue(browser_plugin::kAttributeSrc);
234}
235
236bool BrowserPlugin::GetAutoSizeAttribute() const {
237  return HasDOMAttribute(browser_plugin::kAttributeAutoSize);
238}
239
240int BrowserPlugin::GetMaxHeightAttribute() const {
241  int max_height;
242  base::StringToInt(GetDOMAttributeValue(browser_plugin::kAttributeMaxHeight),
243                    &max_height);
244  return max_height;
245}
246
247int BrowserPlugin::GetMaxWidthAttribute() const {
248  int max_width;
249  base::StringToInt(GetDOMAttributeValue(browser_plugin::kAttributeMaxWidth),
250                    &max_width);
251  return max_width;
252}
253
254int BrowserPlugin::GetMinHeightAttribute() const {
255  int min_height;
256  base::StringToInt(GetDOMAttributeValue(browser_plugin::kAttributeMinHeight),
257                    &min_height);
258  return min_height;
259}
260
261int BrowserPlugin::GetMinWidthAttribute() const {
262  int min_width;
263  base::StringToInt(GetDOMAttributeValue(browser_plugin::kAttributeMinWidth),
264                    &min_width);
265  return min_width;
266}
267
268int BrowserPlugin::GetAdjustedMaxHeight() const {
269  int max_height = GetMaxHeightAttribute();
270  return max_height ? max_height : height();
271}
272
273int BrowserPlugin::GetAdjustedMaxWidth() const {
274  int max_width = GetMaxWidthAttribute();
275  return max_width ? max_width : width();
276}
277
278int BrowserPlugin::GetAdjustedMinHeight() const {
279  int min_height = GetMinHeightAttribute();
280  // FrameView.cpp does not allow this value to be <= 0, so when the value is
281  // unset (or set to 0), we set it to the container size.
282  min_height = min_height ? min_height : height();
283  // For autosize, minHeight should not be bigger than maxHeight.
284  return std::min(min_height, GetAdjustedMaxHeight());
285}
286
287int BrowserPlugin::GetAdjustedMinWidth() const {
288  int min_width = GetMinWidthAttribute();
289  // FrameView.cpp does not allow this value to be <= 0, so when the value is
290  // unset (or set to 0), we set it to the container size.
291  min_width = min_width ? min_width : width();
292  // For autosize, minWidth should not be bigger than maxWidth.
293  return std::min(min_width, GetAdjustedMaxWidth());
294}
295
296std::string BrowserPlugin::GetPartitionAttribute() const {
297  return GetDOMAttributeValue(browser_plugin::kAttributePartition);
298}
299
300void BrowserPlugin::ParseNameAttribute() {
301  if (!HasGuest())
302    return;
303  browser_plugin_manager()->Send(
304      new BrowserPluginHostMsg_SetName(render_view_routing_id_,
305                                       instance_id_,
306                                       GetNameAttribute()));
307}
308
309bool BrowserPlugin::ParseSrcAttribute(std::string* error_message) {
310  if (!valid_partition_id_) {
311    *error_message = browser_plugin::kErrorInvalidPartition;
312    return false;
313  }
314  std::string src = GetSrcAttribute();
315  if (src.empty())
316    return true;
317
318  // If we haven't created the guest yet, do so now. We will navigate it right
319  // after creation. If |src| is empty, we can delay the creation until we
320  // actually need it.
321  if (!HasGuest()) {
322    // On initial navigation, we request an instance ID from the browser
323    // process. We essentially ignore all subsequent calls to SetSrcAttribute
324    // until we receive an instance ID. |before_first_navigation_|
325    // prevents BrowserPlugin from allocating more than one instance ID.
326    // Upon receiving an instance ID from the browser process, we continue
327    // the process of navigation by populating the
328    // BrowserPluginHostMsg_Attach_Params with the current state of
329    // BrowserPlugin and sending a BrowserPluginHostMsg_CreateGuest to the
330    // browser process in order to create a new guest.
331    if (before_first_navigation_) {
332      browser_plugin_manager()->AllocateInstanceID(this);
333      before_first_navigation_ = false;
334    }
335    return true;
336  }
337
338  browser_plugin_manager()->Send(
339      new BrowserPluginHostMsg_NavigateGuest(render_view_routing_id_,
340                                             instance_id_,
341                                             src));
342  return true;
343}
344
345void BrowserPlugin::ParseAutoSizeAttribute() {
346  auto_size_ack_pending_ = true;
347  last_view_size_ = plugin_rect_.size();
348  UpdateGuestAutoSizeState(GetAutoSizeAttribute());
349}
350
351void BrowserPlugin::PopulateAutoSizeParameters(
352    BrowserPluginHostMsg_AutoSize_Params* params, bool current_auto_size) {
353  params->enable = current_auto_size;
354  // No need to populate the params if autosize is off.
355  if (current_auto_size) {
356    params->max_size = gfx::Size(GetAdjustedMaxWidth(), GetAdjustedMaxHeight());
357    params->min_size = gfx::Size(GetAdjustedMinWidth(), GetAdjustedMinHeight());
358  }
359}
360
361void BrowserPlugin::UpdateGuestAutoSizeState(bool current_auto_size) {
362  // If we haven't yet heard back from the guest about the last resize request,
363  // then we don't issue another request until we do in
364  // BrowserPlugin::UpdateRect.
365  if (!HasGuest() || !resize_ack_received_)
366    return;
367  BrowserPluginHostMsg_AutoSize_Params auto_size_params;
368  BrowserPluginHostMsg_ResizeGuest_Params resize_guest_params;
369  if (current_auto_size) {
370    GetDamageBufferWithSizeParams(&auto_size_params, &resize_guest_params);
371  } else {
372    GetDamageBufferWithSizeParams(NULL, &resize_guest_params);
373  }
374  resize_ack_received_ = false;
375  browser_plugin_manager()->Send(
376      new BrowserPluginHostMsg_SetAutoSize(render_view_routing_id_,
377                                           instance_id_,
378                                           auto_size_params,
379                                           resize_guest_params));
380}
381
382void BrowserPlugin::SizeChangedDueToAutoSize(const gfx::Size& old_view_size) {
383  size_changed_in_flight_ = false;
384
385  std::map<std::string, base::Value*> props;
386  props[browser_plugin::kOldHeight] =
387      new base::FundamentalValue(old_view_size.height());
388  props[browser_plugin::kOldWidth] =
389      new base::FundamentalValue(old_view_size.width());
390  props[browser_plugin::kNewHeight] =
391      new base::FundamentalValue(last_view_size_.height());
392  props[browser_plugin::kNewWidth] =
393      new base::FundamentalValue(last_view_size_.width());
394  TriggerEvent(browser_plugin::kEventSizeChanged, &props);
395}
396
397// static
398bool BrowserPlugin::UsesDamageBuffer(
399    const BrowserPluginMsg_UpdateRect_Params& params) {
400  return params.damage_buffer_sequence_id != 0 || params.needs_ack;
401}
402
403bool BrowserPlugin::UsesPendingDamageBuffer(
404    const BrowserPluginMsg_UpdateRect_Params& params) {
405  if (!pending_damage_buffer_)
406    return false;
407  return damage_buffer_sequence_id_ == params.damage_buffer_sequence_id;
408}
409
410void BrowserPlugin::Attach(int instance_id) {
411  CHECK(instance_id != browser_plugin::kInstanceIDNone);
412  before_first_navigation_ = false;
413  instance_id_ = instance_id;
414  browser_plugin_manager()->AddBrowserPlugin(instance_id, this);
415
416  BrowserPluginHostMsg_Attach_Params create_guest_params;
417  create_guest_params.focused = ShouldGuestBeFocused();
418  create_guest_params.visible = visible_;
419  create_guest_params.name = GetNameAttribute();
420  create_guest_params.storage_partition_id = storage_partition_id_;
421  create_guest_params.persist_storage = persist_storage_;
422  create_guest_params.src = GetSrcAttribute();
423  GetDamageBufferWithSizeParams(&create_guest_params.auto_size_params,
424                                &create_guest_params.resize_guest_params);
425
426  browser_plugin_manager()->Send(
427      new BrowserPluginHostMsg_Attach(render_view_routing_id_,
428                                      instance_id_, create_guest_params));
429}
430
431void BrowserPlugin::DidCommitCompositorFrame() {
432  if (compositing_helper_)
433    compositing_helper_->DidCommitCompositorFrame();
434}
435
436void BrowserPlugin::OnAddMessageToConsole(
437    int instance_id, const base::DictionaryValue& message_info) {
438  std::map<std::string, base::Value*> props;
439  // Fill in the info provided by the browser.
440  for (DictionaryValue::Iterator iter(message_info); !iter.IsAtEnd();
441           iter.Advance()) {
442    props[iter.key()] = iter.value().DeepCopy();
443  }
444  TriggerEvent(browser_plugin::kEventConsoleMessage, &props);
445}
446
447void BrowserPlugin::OnAdvanceFocus(int instance_id, bool reverse) {
448  DCHECK(render_view_);
449  render_view_->GetWebView()->advanceFocus(reverse);
450}
451
452void BrowserPlugin::OnAttachACK(
453    int instance_id,
454    const BrowserPluginMsg_Attach_ACK_Params& params) {
455  // Update BrowserPlugin attributes to match the state of the guest.
456  if (!params.name.empty())
457    OnUpdatedName(instance_id, params.name);
458  if (!params.storage_partition_id.empty()) {
459    std::string partition_name =
460        (params.persist_storage ? browser_plugin::kPersistPrefix : "") +
461            params.storage_partition_id;
462    UpdateDOMAttribute(browser_plugin::kAttributePartition, partition_name);
463  }
464}
465
466void BrowserPlugin::OnBuffersSwapped(int instance_id,
467                                     const gfx::Size& size,
468                                     std::string mailbox_name,
469                                     int gpu_route_id,
470                                     int gpu_host_id) {
471  DCHECK(instance_id == instance_id_);
472  EnableCompositing(true);
473
474  compositing_helper_->OnBuffersSwapped(size,
475                                        mailbox_name,
476                                        gpu_route_id,
477                                        gpu_host_id,
478                                        GetDeviceScaleFactor());
479}
480
481void BrowserPlugin::OnClose(int instance_id) {
482  TriggerEvent(browser_plugin::kEventClose, NULL);
483}
484
485void BrowserPlugin::OnCompositorFrameSwapped(const IPC::Message& message) {
486  BrowserPluginMsg_CompositorFrameSwapped::Param param;
487  if (!BrowserPluginMsg_CompositorFrameSwapped::Read(&message, &param))
488    return;
489  scoped_ptr<cc::CompositorFrame> frame(new cc::CompositorFrame);
490  param.b.AssignTo(frame.get());
491
492  EnableCompositing(true);
493  compositing_helper_->OnCompositorFrameSwapped(frame.Pass(),
494                                                param.c /* route_id */,
495                                                param.d /* host_id */);
496}
497
498void BrowserPlugin::OnGuestContentWindowReady(int instance_id,
499                                              int content_window_routing_id) {
500  DCHECK(content_window_routing_id != MSG_ROUTING_NONE);
501  content_window_routing_id_ = content_window_routing_id;
502}
503
504void BrowserPlugin::OnGuestGone(int instance_id, int process_id, int status) {
505  // Set the BrowserPlugin in a crashed state before firing event listeners so
506  // that operations on the BrowserPlugin within listeners are aware that
507  // BrowserPlugin is in a crashed state.
508  guest_crashed_ = true;
509
510  // We fire the event listeners before painting the sad graphic to give the
511  // developer an opportunity to display an alternative overlay image on crash.
512  std::string termination_status = TerminationStatusToString(
513      static_cast<base::TerminationStatus>(status));
514  std::map<std::string, base::Value*> props;
515  props[browser_plugin::kProcessId] = new base::FundamentalValue(process_id);
516  props[browser_plugin::kReason] = new base::StringValue(termination_status);
517
518  // Event listeners may remove the BrowserPlugin from the document. If that
519  // happens, the BrowserPlugin will be scheduled for later deletion (see
520  // BrowserPlugin::destroy()). That will clear the container_ reference,
521  // but leave other member variables valid below.
522  TriggerEvent(browser_plugin::kEventExit, &props);
523
524  // We won't paint the contents of the current backing store again so we might
525  // as well toss it out and save memory.
526  backing_store_.reset();
527  // If the BrowserPlugin is scheduled to be deleted, then container_ will be
528  // NULL so we shouldn't attempt to access it.
529  if (container_)
530    container_->invalidate();
531  // Turn off compositing so we can display the sad graphic.
532  EnableCompositing(false);
533}
534
535void BrowserPlugin::OnGuestResponsive(int instance_id, int process_id) {
536  std::map<std::string, base::Value*> props;
537  props[browser_plugin::kProcessId] = new base::FundamentalValue(process_id);
538  TriggerEvent(browser_plugin::kEventResponsive, &props);
539}
540
541void BrowserPlugin::OnGuestUnresponsive(int instance_id, int process_id) {
542  std::map<std::string, base::Value*> props;
543  props[browser_plugin::kProcessId] = new base::FundamentalValue(process_id);
544  TriggerEvent(browser_plugin::kEventUnresponsive, &props);
545}
546
547void BrowserPlugin::OnLoadAbort(int instance_id,
548                                const GURL& url,
549                                bool is_top_level,
550                                const std::string& type) {
551  std::map<std::string, base::Value*> props;
552  props[browser_plugin::kURL] = new base::StringValue(url.spec());
553  props[browser_plugin::kIsTopLevel] = new base::FundamentalValue(is_top_level);
554  props[browser_plugin::kReason] = new base::StringValue(type);
555  TriggerEvent(browser_plugin::kEventLoadAbort, &props);
556}
557
558void BrowserPlugin::OnLoadCommit(
559    int instance_id,
560    const BrowserPluginMsg_LoadCommit_Params& params) {
561  // If the guest has just committed a new navigation then it is no longer
562  // crashed.
563  guest_crashed_ = false;
564  if (params.is_top_level)
565    UpdateDOMAttribute(browser_plugin::kAttributeSrc, params.url.spec());
566
567  guest_process_id_ = params.process_id;
568  guest_route_id_ = params.route_id;
569  current_nav_entry_index_ = params.current_entry_index;
570  nav_entry_count_ = params.entry_count;
571
572  std::map<std::string, base::Value*> props;
573  props[browser_plugin::kURL] = new base::StringValue(params.url.spec());
574  props[browser_plugin::kIsTopLevel] =
575      new base::FundamentalValue(params.is_top_level);
576  TriggerEvent(browser_plugin::kEventLoadCommit, &props);
577}
578
579void BrowserPlugin::OnLoadRedirect(int instance_id,
580                                   const GURL& old_url,
581                                   const GURL& new_url,
582                                   bool is_top_level) {
583  std::map<std::string, base::Value*> props;
584  props[browser_plugin::kOldURL] = new base::StringValue(old_url.spec());
585  props[browser_plugin::kNewURL] = new base::StringValue(new_url.spec());
586  props[browser_plugin::kIsTopLevel] = new base::FundamentalValue(is_top_level);
587  TriggerEvent(browser_plugin::kEventLoadRedirect, &props);
588}
589
590void BrowserPlugin::OnLoadStart(int instance_id,
591                                const GURL& url,
592                                bool is_top_level) {
593  std::map<std::string, base::Value*> props;
594  props[browser_plugin::kURL] = new base::StringValue(url.spec());
595  props[browser_plugin::kIsTopLevel] = new base::FundamentalValue(is_top_level);
596
597  TriggerEvent(browser_plugin::kEventLoadStart, &props);
598}
599
600void BrowserPlugin::OnLoadStop(int instance_id) {
601  TriggerEvent(browser_plugin::kEventLoadStop, NULL);
602}
603
604void BrowserPlugin::OnRequestPermission(
605    int instance_id,
606    BrowserPluginPermissionType permission_type,
607    int request_id,
608    const base::DictionaryValue& request_info) {
609  // The New Window API is very similiar to the permission API in structure,
610  // but exposes a slightly different interface to the developer and so we put
611  // it in a separate event.
612  const char* event_name =
613      (permission_type == BrowserPluginPermissionTypeNewWindow) ?
614          browser_plugin::kEventNewWindow :
615              browser_plugin::kEventRequestPermission;
616
617  AddPermissionRequestToMap(request_id, permission_type);
618
619  std::map<std::string, base::Value*> props;
620  props[browser_plugin::kPermission] =
621      base::Value::CreateStringValue(PermissionTypeToString(permission_type));
622  props[browser_plugin::kRequestId] =
623      base::Value::CreateIntegerValue(request_id);
624
625  // Fill in the info provided by the browser.
626  for (DictionaryValue::Iterator iter(request_info); !iter.IsAtEnd();
627           iter.Advance()) {
628    props[iter.key()] = iter.value().DeepCopy();
629  }
630  TriggerEvent(event_name, &props);
631}
632
633void BrowserPlugin::OnSetCursor(int instance_id, const WebCursor& cursor) {
634  cursor_ = cursor;
635}
636
637void BrowserPlugin::OnShouldAcceptTouchEvents(int instance_id, bool accept) {
638  if (container()) {
639    container()->requestTouchEventType(accept ?
640        WebKit::WebPluginContainer::TouchEventRequestTypeRaw :
641        WebKit::WebPluginContainer::TouchEventRequestTypeNone);
642  }
643}
644
645void BrowserPlugin::OnUnlockMouse(int instance_id) {
646  render_view_->mouse_lock_dispatcher()->UnlockMouse(this);
647}
648
649void BrowserPlugin::OnUpdatedName(int instance_id, const std::string& name) {
650  UpdateDOMAttribute(browser_plugin::kAttributeName, name);
651}
652
653void BrowserPlugin::AddPermissionRequestToMap(int request_id,
654    BrowserPluginPermissionType type) {
655  DCHECK(!pending_permission_requests_.count(request_id));
656  pending_permission_requests_.insert(std::make_pair(request_id, type));
657}
658
659void BrowserPlugin::OnUpdateRect(
660    int instance_id,
661    const BrowserPluginMsg_UpdateRect_Params& params) {
662  bool use_new_damage_buffer = !backing_store_;
663  BrowserPluginHostMsg_AutoSize_Params auto_size_params;
664  BrowserPluginHostMsg_ResizeGuest_Params resize_guest_params;
665  // If we have a pending damage buffer, and the guest has begun to use the
666  // damage buffer then we know the guest will no longer use the current
667  // damage buffer. At this point, we drop the current damage buffer, and
668  // mark the pending damage buffer as the current damage buffer.
669  if (UsesPendingDamageBuffer(params)) {
670    SwapDamageBuffers();
671    use_new_damage_buffer = true;
672  }
673
674  bool auto_size = GetAutoSizeAttribute();
675  // We receive a resize ACK in regular mode, but not in autosize.
676  // In SW, |resize_ack_received_| is reset in SwapDamageBuffers().
677  // In HW mode, we need to do it here so we can continue sending
678  // resize messages when needed.
679  if (params.is_resize_ack ||
680      (!params.needs_ack && (auto_size || auto_size_ack_pending_)))
681    resize_ack_received_ = true;
682
683  auto_size_ack_pending_ = false;
684
685  if ((!auto_size && (width() != params.view_size.width() ||
686                      height() != params.view_size.height())) ||
687      (auto_size && (!InAutoSizeBounds(params.view_size))) ||
688      GetDeviceScaleFactor() != params.scale_factor) {
689    // We are HW accelerated, render widget does not expect an ack,
690    // but we still need to update the size.
691    if (!params.needs_ack) {
692      UpdateGuestAutoSizeState(auto_size);
693      return;
694    }
695
696    if (!resize_ack_received_) {
697      // The guest has not yet responded to the last resize request, and
698      // so we don't want to do anything at this point other than ACK the guest.
699      if (auto_size)
700        PopulateAutoSizeParameters(&auto_size_params, auto_size);
701    } else {
702      // If we have no pending damage buffer, then the guest has not caught up
703      // with the BrowserPlugin container. We now tell the guest about the new
704      // container size.
705      if (auto_size) {
706        GetDamageBufferWithSizeParams(&auto_size_params,
707                                      &resize_guest_params);
708      } else {
709        GetDamageBufferWithSizeParams(NULL, &resize_guest_params);
710      }
711    }
712    browser_plugin_manager()->Send(new BrowserPluginHostMsg_UpdateRect_ACK(
713        render_view_routing_id_,
714        instance_id_,
715        auto_size_params,
716        resize_guest_params));
717    return;
718  }
719
720  if (auto_size && (params.view_size != last_view_size_)) {
721    if (backing_store_)
722      backing_store_->Clear(SK_ColorWHITE);
723    gfx::Size old_view_size = last_view_size_;
724    last_view_size_ = params.view_size;
725    // Schedule a SizeChanged instead of calling it directly to ensure that
726    // the backing store has been updated before the developer attempts to
727    // resize to avoid flicker. |size_changed_in_flight_| acts as a form of
728    // flow control for SizeChanged events. If the guest's view size is changing
729    // rapidly before a SizeChanged event fires, then we avoid scheduling
730    // another SizeChanged event. SizeChanged reads the new size from
731    // |last_view_size_| so we can be sure that it always fires an event
732    // with the last seen view size.
733    if (container_ && !size_changed_in_flight_) {
734      size_changed_in_flight_ = true;
735      base::MessageLoop::current()->PostTask(
736          FROM_HERE,
737          base::Bind(&BrowserPlugin::SizeChangedDueToAutoSize,
738                     base::Unretained(this),
739                     old_view_size));
740    }
741  }
742
743  // No more work to do since the guest is no longer using a damage buffer.
744  if (!UsesDamageBuffer(params))
745    return;
746
747  // If we are seeing damage buffers, HW compositing should be turned off.
748  EnableCompositing(false);
749
750  // If we are now using a new damage buffer, then that means that the guest
751  // has updated its size state in response to a resize request. We change
752  // the backing store's size to accomodate the new damage buffer size.
753  if (use_new_damage_buffer) {
754    int backing_store_width = auto_size ? GetAdjustedMaxWidth() : width();
755    int backing_store_height = auto_size ? GetAdjustedMaxHeight(): height();
756    backing_store_.reset(
757        new BrowserPluginBackingStore(
758            gfx::Size(backing_store_width, backing_store_height),
759            params.scale_factor));
760  }
761
762  // Update the backing store.
763  if (!params.scroll_rect.IsEmpty()) {
764    backing_store_->ScrollBackingStore(params.scroll_delta,
765                                       params.scroll_rect,
766                                       params.view_size);
767  }
768  backing_store_->PaintToBackingStore(params.bitmap_rect,
769                                      params.copy_rects,
770                                      current_damage_buffer_->memory());
771
772  // Invalidate the container.
773  // If the BrowserPlugin is scheduled to be deleted, then container_ will be
774  // NULL so we shouldn't attempt to access it.
775  if (container_)
776    container_->invalidate();
777  if (auto_size)
778    PopulateAutoSizeParameters(&auto_size_params, auto_size);
779  browser_plugin_manager()->Send(new BrowserPluginHostMsg_UpdateRect_ACK(
780      render_view_routing_id_,
781      instance_id_,
782      auto_size_params,
783      resize_guest_params));
784}
785
786void BrowserPlugin::ParseSizeContraintsChanged() {
787  bool auto_size = GetAutoSizeAttribute();
788  if (auto_size)
789    UpdateGuestAutoSizeState(true);
790}
791
792bool BrowserPlugin::InAutoSizeBounds(const gfx::Size& size) const {
793  return size.width() <= GetAdjustedMaxWidth() &&
794      size.height() <= GetAdjustedMaxHeight();
795}
796
797NPObject* BrowserPlugin::GetContentWindow() const {
798  if (content_window_routing_id_ == MSG_ROUTING_NONE)
799    return NULL;
800  RenderViewImpl* guest_render_view = RenderViewImpl::FromRoutingID(
801      content_window_routing_id_);
802  if (!guest_render_view)
803    return NULL;
804  WebKit::WebFrame* guest_frame = guest_render_view->GetWebView()->mainFrame();
805  return guest_frame->windowObject();
806}
807
808// static
809bool BrowserPlugin::AttachWindowTo(const WebKit::WebNode& node, int window_id) {
810  if (node.isNull())
811    return false;
812
813  if (!node.isElementNode())
814    return false;
815
816  WebKit::WebElement shim_element = node.toConst<WebKit::WebElement>();
817  // The shim containing the BrowserPlugin must be attached to a document.
818  if (shim_element.document().isNull())
819    return false;
820
821  WebKit::WebNode shadow_root = shim_element.shadowRoot();
822  if (shadow_root.isNull() || !shadow_root.hasChildNodes())
823    return false;
824
825  WebKit::WebNode plugin_element = shadow_root.firstChild();
826  WebKit::WebPluginContainer* plugin_container =
827      plugin_element.pluginContainer();
828  if (!plugin_container)
829    return false;
830
831  BrowserPlugin* browser_plugin =
832      BrowserPlugin::FromContainer(plugin_container);
833  if (!browser_plugin)
834    return false;
835
836  // If the BrowserPlugin already has a guest attached to it then we probably
837  // shouldn't allow attaching a different guest.
838  // TODO(fsamuel): We may wish to support reattaching guests in the future:
839  // http://crbug.com/156219.
840  if (browser_plugin->HasGuest())
841    return false;
842
843  browser_plugin->Attach(window_id);
844  return true;
845}
846
847bool BrowserPlugin::HasGuest() const {
848  return instance_id_ != browser_plugin::kInstanceIDNone;
849}
850
851bool BrowserPlugin::CanGoBack() const {
852  return nav_entry_count_ > 1 && current_nav_entry_index_ > 0;
853}
854
855bool BrowserPlugin::CanGoForward() const {
856  return current_nav_entry_index_ >= 0 &&
857      current_nav_entry_index_ < (nav_entry_count_ - 1);
858}
859
860bool BrowserPlugin::ParsePartitionAttribute(std::string* error_message) {
861  if (!before_first_navigation_) {
862    *error_message = browser_plugin::kErrorAlreadyNavigated;
863    return false;
864  }
865
866  std::string input = GetPartitionAttribute();
867
868  // Since the "persist:" prefix is in ASCII, StartsWith will work fine on
869  // UTF-8 encoded |partition_id|. If the prefix is a match, we can safely
870  // remove the prefix without splicing in the middle of a multi-byte codepoint.
871  // We can use the rest of the string as UTF-8 encoded one.
872  if (StartsWithASCII(input, browser_plugin::kPersistPrefix, true)) {
873    size_t index = input.find(":");
874    CHECK(index != std::string::npos);
875    // It is safe to do index + 1, since we tested for the full prefix above.
876    input = input.substr(index + 1);
877    if (input.empty()) {
878      valid_partition_id_ = false;
879      *error_message = browser_plugin::kErrorInvalidPartition;
880      return false;
881    }
882    persist_storage_ = true;
883  } else {
884    persist_storage_ = false;
885  }
886
887  valid_partition_id_ = true;
888  storage_partition_id_ = input;
889  return true;
890}
891
892bool BrowserPlugin::CanRemovePartitionAttribute(std::string* error_message) {
893  if (HasGuest())
894    *error_message = browser_plugin::kErrorCannotRemovePartition;
895  return !HasGuest();
896}
897
898void BrowserPlugin::ParseAttributes() {
899  // TODO(mthiesse): Handle errors here?
900  std::string error;
901  ParsePartitionAttribute(&error);
902
903  // Parse the 'src' attribute last, as it will set the has_navigated_ flag to
904  // true, which prevents changing the 'partition' attribute.
905  ParseSrcAttribute(&error);
906}
907
908float BrowserPlugin::GetDeviceScaleFactor() const {
909  if (!render_view_)
910    return 1.0f;
911  return render_view_->GetWebView()->deviceScaleFactor();
912}
913
914void BrowserPlugin::UpdateDeviceScaleFactor(float device_scale_factor) {
915  if (last_device_scale_factor_ == device_scale_factor || !resize_ack_received_)
916    return;
917
918  BrowserPluginHostMsg_ResizeGuest_Params params;
919  PopulateResizeGuestParameters(&params, gfx::Size(width(), height()));
920  browser_plugin_manager()->Send(new BrowserPluginHostMsg_ResizeGuest(
921      render_view_routing_id_,
922      instance_id_,
923      params));
924}
925
926void BrowserPlugin::TriggerEvent(const std::string& event_name,
927                                 std::map<std::string, base::Value*>* props) {
928  if (!container() || !container()->element().document().frame())
929    return;
930  v8::HandleScope handle_scope;
931  std::string json_string;
932  if (props) {
933    base::DictionaryValue dict;
934    for (std::map<std::string, base::Value*>::iterator iter = props->begin(),
935             end = props->end(); iter != end; ++iter) {
936      dict.Set(iter->first, iter->second);
937    }
938
939    JSONStringValueSerializer serializer(&json_string);
940    if (!serializer.Serialize(dict))
941      return;
942  }
943
944  WebKit::WebFrame* frame = container()->element().document().frame();
945  WebKit::WebDOMEvent dom_event = frame->document().createEvent("CustomEvent");
946  WebKit::WebDOMCustomEvent event = dom_event.to<WebKit::WebDOMCustomEvent>();
947
948  // The events triggered directly from the plugin <object> are internal events
949  // whose implementation details can (and likely will) change over time. The
950  // wrapper/shim (e.g. <webview> tag) should receive these events, and expose a
951  // more appropriate (and stable) event to the consumers as part of the API.
952  event.initCustomEvent(
953      WebKit::WebString::fromUTF8(GetInternalEventName(event_name.c_str())),
954      false, false,
955      WebKit::WebSerializedScriptValue::serialize(
956          v8::String::New(json_string.c_str(), json_string.size())));
957  container()->element().dispatchEvent(event);
958}
959
960void BrowserPlugin::OnRequestObjectGarbageCollected(int request_id) {
961  // Remove from alive objects.
962  std::map<int, AliveV8PermissionRequestItem*>::iterator iter =
963      alive_v8_permission_request_objects_.find(request_id);
964  if (iter != alive_v8_permission_request_objects_.end())
965    alive_v8_permission_request_objects_.erase(iter);
966
967  // If a decision has not been made for this request yet, deny it.
968  RespondPermissionIfRequestIsPending(request_id, false /*allow*/);
969}
970
971void BrowserPlugin::PersistRequestObject(
972    const NPVariant* request, const std::string& type, int id) {
973  CHECK(alive_v8_permission_request_objects_.find(id) ==
974        alive_v8_permission_request_objects_.end());
975  if (pending_permission_requests_.find(id) ==
976      pending_permission_requests_.end()) {
977    return;
978  }
979
980  v8::Isolate* isolate = v8::Isolate::GetCurrent();
981  v8::Persistent<v8::Value> weak_request(
982      isolate, WebKit::WebBindings::toV8Value(request));
983
984  AliveV8PermissionRequestItem* new_item =
985      new std::pair<int, base::WeakPtr<BrowserPlugin> >(
986          id, weak_ptr_factory_.GetWeakPtr());
987
988  std::pair<std::map<int, AliveV8PermissionRequestItem*>::iterator, bool>
989      result = alive_v8_permission_request_objects_.insert(
990          std::make_pair(id, new_item));
991  CHECK(result.second);  // Inserted in the map.
992  AliveV8PermissionRequestItem* request_item = result.first->second;
993  weak_request.MakeWeak(isolate, request_item, WeakCallbackForPersistObject);
994}
995
996// static
997void BrowserPlugin::WeakCallbackForPersistObject(
998    v8::Isolate* isolate, v8::Persistent<v8::Value> object, void* param) {
999  v8::Persistent<v8::Object> persistent_object =
1000      v8::Persistent<v8::Object>::Cast(object);
1001
1002  AliveV8PermissionRequestItem* item_ptr =
1003      static_cast<AliveV8PermissionRequestItem*>(param);
1004  int request_id = item_ptr->first;
1005  base::WeakPtr<BrowserPlugin> plugin = item_ptr->second;
1006  delete item_ptr;
1007
1008  persistent_object.Dispose(isolate);
1009  persistent_object.Clear();
1010
1011  if (plugin) {
1012    // Asynchronously remove item from |alive_v8_permission_request_objects_|.
1013    // Note that we are using weak pointer for the following PostTask, so we
1014    // don't need to worry about BrowserPlugin going away.
1015    base::MessageLoop::current()->PostTask(
1016        FROM_HERE,
1017        base::Bind(&BrowserPlugin::OnRequestObjectGarbageCollected,
1018                   plugin,
1019                   request_id));
1020  }
1021}
1022
1023void BrowserPlugin::Back() {
1024  if (!HasGuest())
1025    return;
1026  browser_plugin_manager()->Send(
1027      new BrowserPluginHostMsg_Go(render_view_routing_id_,
1028                                  instance_id_, -1));
1029}
1030
1031void BrowserPlugin::Forward() {
1032  if (!HasGuest())
1033    return;
1034  browser_plugin_manager()->Send(
1035      new BrowserPluginHostMsg_Go(render_view_routing_id_,
1036                                  instance_id_, 1));
1037}
1038
1039void BrowserPlugin::Go(int relative_index) {
1040  if (!HasGuest())
1041    return;
1042  browser_plugin_manager()->Send(
1043      new BrowserPluginHostMsg_Go(render_view_routing_id_,
1044                                  instance_id_,
1045                                  relative_index));
1046}
1047
1048void BrowserPlugin::TerminateGuest() {
1049  if (!HasGuest() || guest_crashed_)
1050    return;
1051  browser_plugin_manager()->Send(
1052      new BrowserPluginHostMsg_TerminateGuest(render_view_routing_id_,
1053                                              instance_id_));
1054}
1055
1056void BrowserPlugin::Stop() {
1057  if (!HasGuest())
1058    return;
1059  browser_plugin_manager()->Send(
1060      new BrowserPluginHostMsg_Stop(render_view_routing_id_,
1061                                    instance_id_));
1062}
1063
1064void BrowserPlugin::Reload() {
1065  if (!HasGuest())
1066    return;
1067  browser_plugin_manager()->Send(
1068      new BrowserPluginHostMsg_Reload(render_view_routing_id_,
1069                                      instance_id_));
1070}
1071
1072void BrowserPlugin::UpdateGuestFocusState() {
1073  if (!HasGuest())
1074    return;
1075  bool should_be_focused = ShouldGuestBeFocused();
1076  browser_plugin_manager()->Send(new BrowserPluginHostMsg_SetFocus(
1077      render_view_routing_id_,
1078      instance_id_,
1079      should_be_focused));
1080}
1081
1082bool BrowserPlugin::ShouldGuestBeFocused() const {
1083  bool embedder_focused = false;
1084  if (render_view_)
1085    embedder_focused = render_view_->has_focus();
1086  return plugin_focused_ && embedder_focused;
1087}
1088
1089WebKit::WebPluginContainer* BrowserPlugin::container() const {
1090  return container_;
1091}
1092
1093void BrowserPlugin::RespondPermission(
1094    BrowserPluginPermissionType permission_type, int request_id, bool allow) {
1095  if (permission_type == BrowserPluginPermissionTypePointerLock)
1096      RespondPermissionPointerLock(allow);
1097  else
1098    browser_plugin_manager()->Send(
1099        new BrowserPluginHostMsg_RespondPermission(
1100            render_view_->GetRoutingID(), instance_id_, permission_type,
1101            request_id, allow));
1102}
1103
1104void BrowserPlugin::RespondPermissionPointerLock(bool allow) {
1105  if (allow)
1106    render_view_->mouse_lock_dispatcher()->LockMouse(this);
1107  else
1108    OnLockMouseACK(false);
1109}
1110
1111void BrowserPlugin::RespondPermissionIfRequestIsPending(
1112    int request_id, bool allow) {
1113  PendingPermissionRequests::iterator iter =
1114      pending_permission_requests_.find(request_id);
1115  if (iter == pending_permission_requests_.end())
1116    return;
1117
1118  BrowserPluginPermissionType permission_type = iter->second;
1119  pending_permission_requests_.erase(iter);
1120  RespondPermission(permission_type, request_id, allow);
1121}
1122
1123void BrowserPlugin::OnEmbedderDecidedPermission(int request_id, bool allow) {
1124  RespondPermissionIfRequestIsPending(request_id, allow);
1125}
1126
1127bool BrowserPlugin::initialize(WebPluginContainer* container) {
1128  if (!container)
1129    return false;
1130
1131  if (!GetContentClient()->renderer()->AllowBrowserPlugin(container))
1132    return false;
1133
1134  bindings_.reset(new BrowserPluginBindings(this));
1135  container_ = container;
1136  container_->setWantsWheelEvents(true);
1137  ParseAttributes();
1138  g_plugin_container_map.Get().insert(std::make_pair(container_, this));
1139  return true;
1140}
1141
1142void BrowserPlugin::EnableCompositing(bool enable) {
1143  if (compositing_enabled_ == enable)
1144    return;
1145
1146  compositing_enabled_ = enable;
1147  if (enable) {
1148    // No need to keep the backing store and damage buffer around if we're now
1149    // compositing.
1150    backing_store_.reset();
1151    current_damage_buffer_.reset();
1152    if (!compositing_helper_) {
1153      compositing_helper_ = new BrowserPluginCompositingHelper(
1154          container_,
1155          browser_plugin_manager(),
1156          instance_id_,
1157          render_view_routing_id_);
1158    }
1159  } else {
1160    // We're switching back to the software path. We create a new damage
1161    // buffer that can accommodate the current size of the container.
1162    BrowserPluginHostMsg_ResizeGuest_Params params;
1163    PopulateResizeGuestParameters(&params, gfx::Size(width(), height()));
1164    // Request a full repaint from the guest even if its size is not actually
1165    // changing.
1166    params.repaint = true;
1167    resize_ack_received_ = false;
1168    browser_plugin_manager()->Send(new BrowserPluginHostMsg_ResizeGuest(
1169        render_view_routing_id_,
1170        instance_id_,
1171        params));
1172  }
1173  compositing_helper_->EnableCompositing(enable);
1174}
1175
1176void BrowserPlugin::destroy() {
1177  // The BrowserPlugin's WebPluginContainer is deleted immediately after this
1178  // call returns, so let's not keep a reference to it around.
1179  g_plugin_container_map.Get().erase(container_);
1180  container_ = NULL;
1181  if (compositing_helper_)
1182    compositing_helper_->OnContainerDestroy();
1183  // Will be a no-op if the mouse is not currently locked.
1184  if (render_view_)
1185    render_view_->mouse_lock_dispatcher()->OnLockTargetDestroyed(this);
1186  base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
1187}
1188
1189NPObject* BrowserPlugin::scriptableObject() {
1190  if (!bindings_)
1191    return NULL;
1192
1193  NPObject* browser_plugin_np_object(bindings_->np_object());
1194  // The object is expected to be retained before it is returned.
1195  WebKit::WebBindings::retainObject(browser_plugin_np_object);
1196  return browser_plugin_np_object;
1197}
1198
1199bool BrowserPlugin::supportsKeyboardFocus() const {
1200  return true;
1201}
1202
1203bool BrowserPlugin::canProcessDrag() const {
1204  return true;
1205}
1206
1207void BrowserPlugin::paint(WebCanvas* canvas, const WebRect& rect) {
1208  if (guest_crashed_) {
1209    if (!sad_guest_)  // Lazily initialize bitmap.
1210      sad_guest_ = content::GetContentClient()->renderer()->
1211          GetSadWebViewBitmap();
1212    // content_shell does not have the sad plugin bitmap, so we'll paint black
1213    // instead to make it clear that something went wrong.
1214    if (sad_guest_) {
1215      webkit::PaintSadPlugin(canvas, plugin_rect_, *sad_guest_);
1216      return;
1217    }
1218  }
1219  SkAutoCanvasRestore auto_restore(canvas, true);
1220  canvas->translate(plugin_rect_.x(), plugin_rect_.y());
1221  SkRect image_data_rect = SkRect::MakeXYWH(
1222      SkIntToScalar(0),
1223      SkIntToScalar(0),
1224      SkIntToScalar(plugin_rect_.width()),
1225      SkIntToScalar(plugin_rect_.height()));
1226  canvas->clipRect(image_data_rect);
1227  // Paint black or white in case we have nothing in our backing store or we
1228  // need to show a gutter.
1229  SkPaint paint;
1230  paint.setStyle(SkPaint::kFill_Style);
1231  paint.setColor(guest_crashed_ ? SK_ColorBLACK : SK_ColorWHITE);
1232  canvas->drawRect(image_data_rect, paint);
1233  // Stay a solid color if we have never set a non-empty src, or we don't have a
1234  // backing store.
1235  if (!backing_store_.get() || !HasGuest())
1236    return;
1237  float inverse_scale_factor =  1.0f / backing_store_->GetScaleFactor();
1238  canvas->scale(inverse_scale_factor, inverse_scale_factor);
1239  canvas->drawBitmap(backing_store_->GetBitmap(), 0, 0);
1240}
1241
1242bool BrowserPlugin::InBounds(const gfx::Point& position) const {
1243  // Note that even for plugins that are rotated using rotate transformations,
1244  // we use the the |plugin_rect_| provided by updateGeometry, which means we
1245  // will be off if |position| is within the plugin rect but does not fall
1246  // within the actual plugin boundary. Not supporting such edge case is OK
1247  // since this function should not be used for making security-sensitive
1248  // decisions.
1249  // This also does not take overlapping plugins into account.
1250  bool result = position.x() >= plugin_rect_.x() &&
1251      position.x() < plugin_rect_.x() + plugin_rect_.width() &&
1252      position.y() >= plugin_rect_.y() &&
1253      position.y() < plugin_rect_.y() + plugin_rect_.height();
1254  return result;
1255}
1256
1257gfx::Point BrowserPlugin::ToLocalCoordinates(const gfx::Point& point) const {
1258  if (container_)
1259    return container_->windowToLocalPoint(WebKit::WebPoint(point));
1260  return gfx::Point(point.x() - plugin_rect_.x(), point.y() - plugin_rect_.y());
1261}
1262
1263// static
1264bool BrowserPlugin::ShouldForwardToBrowserPlugin(
1265    const IPC::Message& message) {
1266  switch (message.type()) {
1267    case BrowserPluginMsg_AddMessageToConsole::ID:
1268    case BrowserPluginMsg_AdvanceFocus::ID:
1269    case BrowserPluginMsg_Attach_ACK::ID:
1270    case BrowserPluginMsg_BuffersSwapped::ID:
1271    case BrowserPluginMsg_Close::ID:
1272    case BrowserPluginMsg_CompositorFrameSwapped::ID:
1273    case BrowserPluginMsg_GuestContentWindowReady::ID:
1274    case BrowserPluginMsg_GuestGone::ID:
1275    case BrowserPluginMsg_GuestResponsive::ID:
1276    case BrowserPluginMsg_GuestUnresponsive::ID:
1277    case BrowserPluginMsg_LoadAbort::ID:
1278    case BrowserPluginMsg_LoadCommit::ID:
1279    case BrowserPluginMsg_LoadRedirect::ID:
1280    case BrowserPluginMsg_LoadStart::ID:
1281    case BrowserPluginMsg_LoadStop::ID:
1282    case BrowserPluginMsg_RequestPermission::ID:
1283    case BrowserPluginMsg_SetCursor::ID:
1284    case BrowserPluginMsg_ShouldAcceptTouchEvents::ID:
1285    case BrowserPluginMsg_UnlockMouse::ID:
1286    case BrowserPluginMsg_UpdatedName::ID:
1287    case BrowserPluginMsg_UpdateRect::ID:
1288      return true;
1289    default:
1290      break;
1291  }
1292  return false;
1293}
1294
1295void BrowserPlugin::updateGeometry(
1296    const WebRect& window_rect,
1297    const WebRect& clip_rect,
1298    const WebVector<WebRect>& cut_outs_rects,
1299    bool is_visible) {
1300  int old_width = width();
1301  int old_height = height();
1302  plugin_rect_ = window_rect;
1303  // In AutoSize mode, guests don't care when the BrowserPlugin container is
1304  // resized. If |!resize_ack_received_|, then we are still waiting on a
1305  // previous resize to be ACK'ed and so we don't issue additional resizes
1306  // until the previous one is ACK'ed.
1307  // TODO(mthiesse): Assess the performance of calling GetAutoSizeAttribute() on
1308  // resize.
1309  if (!HasGuest() ||
1310      !resize_ack_received_ ||
1311      (old_width == window_rect.width && old_height == window_rect.height) ||
1312      GetAutoSizeAttribute()) {
1313    return;
1314  }
1315
1316  BrowserPluginHostMsg_ResizeGuest_Params params;
1317  PopulateResizeGuestParameters(&params, gfx::Size(width(), height()));
1318  resize_ack_received_ = false;
1319  browser_plugin_manager()->Send(new BrowserPluginHostMsg_ResizeGuest(
1320      render_view_routing_id_,
1321      instance_id_,
1322      params));
1323}
1324
1325void BrowserPlugin::SwapDamageBuffers() {
1326  current_damage_buffer_.reset(pending_damage_buffer_.release());
1327  resize_ack_received_ = true;
1328}
1329
1330void BrowserPlugin::PopulateResizeGuestParameters(
1331    BrowserPluginHostMsg_ResizeGuest_Params* params,
1332    const gfx::Size& view_size) {
1333  params->view_size = view_size;
1334  params->scale_factor = GetDeviceScaleFactor();
1335  if (last_device_scale_factor_ != params->scale_factor){
1336    params->repaint = true;
1337    last_device_scale_factor_ = params->scale_factor;
1338  }
1339
1340  // In HW compositing mode, we do not need a damage buffer.
1341  if (compositing_enabled_)
1342    return;
1343
1344  const size_t stride = skia::PlatformCanvasStrideForWidth(view_size.width());
1345  // Make sure the size of the damage buffer is at least four bytes so that we
1346  // can fit in a magic word to verify that the memory is shared correctly.
1347  size_t size =
1348      std::max(sizeof(unsigned int),
1349               static_cast<size_t>(view_size.height() *
1350                                   stride *
1351                                   GetDeviceScaleFactor() *
1352                                   GetDeviceScaleFactor()));
1353
1354  params->damage_buffer_size = size;
1355  pending_damage_buffer_.reset(
1356      CreateDamageBuffer(size, &params->damage_buffer_handle));
1357  if (!pending_damage_buffer_)
1358    NOTREACHED();
1359  params->damage_buffer_sequence_id = ++damage_buffer_sequence_id_;
1360}
1361
1362void BrowserPlugin::GetDamageBufferWithSizeParams(
1363    BrowserPluginHostMsg_AutoSize_Params* auto_size_params,
1364    BrowserPluginHostMsg_ResizeGuest_Params* resize_guest_params) {
1365  if (auto_size_params)
1366    PopulateAutoSizeParameters(auto_size_params, GetAutoSizeAttribute());
1367  gfx::Size view_size = (auto_size_params && auto_size_params->enable) ?
1368      auto_size_params->max_size : gfx::Size(width(), height());
1369  if (view_size.IsEmpty())
1370    return;
1371  resize_ack_received_ = false;
1372  PopulateResizeGuestParameters(resize_guest_params, view_size);
1373}
1374
1375#if defined(OS_POSIX)
1376base::SharedMemory* BrowserPlugin::CreateDamageBuffer(
1377    const size_t size,
1378    base::SharedMemoryHandle* damage_buffer_handle) {
1379  scoped_ptr<base::SharedMemory> shared_buf(
1380      content::RenderThread::Get()->HostAllocateSharedMemoryBuffer(
1381          size).release());
1382
1383  if (shared_buf) {
1384    if (shared_buf->Map(size)) {
1385      // Insert the magic word.
1386      *static_cast<unsigned int*>(shared_buf->memory()) = 0xdeadbeef;
1387      shared_buf->ShareToProcess(base::GetCurrentProcessHandle(),
1388                                 damage_buffer_handle);
1389      return shared_buf.release();
1390    }
1391  }
1392  NOTREACHED();
1393  return NULL;
1394}
1395#elif defined(OS_WIN)
1396base::SharedMemory* BrowserPlugin::CreateDamageBuffer(
1397    const size_t size,
1398    base::SharedMemoryHandle* damage_buffer_handle) {
1399  scoped_ptr<base::SharedMemory> shared_buf(new base::SharedMemory());
1400
1401  if (!shared_buf->CreateAndMapAnonymous(size)) {
1402    NOTREACHED() << "Buffer allocation failed";
1403    return NULL;
1404  }
1405
1406  // Insert the magic word.
1407  *static_cast<unsigned int*>(shared_buf->memory()) = 0xdeadbeef;
1408  if (shared_buf->ShareToProcess(base::GetCurrentProcessHandle(),
1409                                 damage_buffer_handle))
1410    return shared_buf.release();
1411  NOTREACHED();
1412  return NULL;
1413}
1414#endif
1415
1416void BrowserPlugin::updateFocus(bool focused) {
1417  if (plugin_focused_ == focused)
1418    return;
1419
1420  bool old_guest_focus_state = ShouldGuestBeFocused();
1421  plugin_focused_ = focused;
1422
1423  if (ShouldGuestBeFocused() != old_guest_focus_state)
1424    UpdateGuestFocusState();
1425}
1426
1427void BrowserPlugin::updateVisibility(bool visible) {
1428  if (visible_ == visible)
1429    return;
1430
1431  visible_ = visible;
1432  if (!HasGuest())
1433    return;
1434
1435  if (compositing_helper_)
1436    compositing_helper_->UpdateVisibility(visible);
1437
1438  browser_plugin_manager()->Send(new BrowserPluginHostMsg_SetVisibility(
1439      render_view_routing_id_,
1440      instance_id_,
1441      visible));
1442}
1443
1444bool BrowserPlugin::acceptsInputEvents() {
1445  return true;
1446}
1447
1448bool BrowserPlugin::handleInputEvent(const WebKit::WebInputEvent& event,
1449                                     WebKit::WebCursorInfo& cursor_info) {
1450  if (guest_crashed_ || !HasGuest() ||
1451      event.type == WebKit::WebInputEvent::ContextMenu)
1452    return false;
1453
1454  const WebKit::WebInputEvent* modified_event = &event;
1455  scoped_ptr<WebKit::WebTouchEvent> touch_event;
1456  // WebKit gives BrowserPlugin a list of touches that are down, but the browser
1457  // process expects a list of all touches. We modify the TouchEnd event here to
1458  // match these expectations.
1459  if (event.type == WebKit::WebInputEvent::TouchEnd) {
1460    const WebKit::WebTouchEvent* orig_touch_event =
1461        static_cast<const WebKit::WebTouchEvent*>(&event);
1462    touch_event.reset(new WebKit::WebTouchEvent());
1463    memcpy(touch_event.get(), orig_touch_event, sizeof(WebKit::WebTouchEvent));
1464    if (touch_event->changedTouchesLength > 0) {
1465      memcpy(&touch_event->touches[touch_event->touchesLength],
1466             &touch_event->changedTouches,
1467            touch_event->changedTouchesLength * sizeof(WebKit::WebTouchPoint));
1468    }
1469    touch_event->touchesLength += touch_event->changedTouchesLength;
1470    modified_event = touch_event.get();
1471  }
1472  browser_plugin_manager()->Send(
1473      new BrowserPluginHostMsg_HandleInputEvent(render_view_routing_id_,
1474                                                instance_id_,
1475                                                plugin_rect_,
1476                                                modified_event));
1477  cursor_.GetCursorInfo(&cursor_info);
1478  return true;
1479}
1480
1481bool BrowserPlugin::handleDragStatusUpdate(WebKit::WebDragStatus drag_status,
1482                                           const WebKit::WebDragData& drag_data,
1483                                           WebKit::WebDragOperationsMask mask,
1484                                           const WebKit::WebPoint& position,
1485                                           const WebKit::WebPoint& screen) {
1486  if (guest_crashed_ || !HasGuest())
1487    return false;
1488  browser_plugin_manager()->Send(
1489      new BrowserPluginHostMsg_DragStatusUpdate(
1490        render_view_routing_id_,
1491        instance_id_,
1492        drag_status,
1493        WebDropData(drag_data),
1494        mask,
1495        position));
1496  return true;
1497}
1498
1499void BrowserPlugin::didReceiveResponse(
1500    const WebKit::WebURLResponse& response) {
1501}
1502
1503void BrowserPlugin::didReceiveData(const char* data, int data_length) {
1504}
1505
1506void BrowserPlugin::didFinishLoading() {
1507}
1508
1509void BrowserPlugin::didFailLoading(const WebKit::WebURLError& error) {
1510}
1511
1512void BrowserPlugin::didFinishLoadingFrameRequest(const WebKit::WebURL& url,
1513                                                 void* notify_data) {
1514}
1515
1516void BrowserPlugin::didFailLoadingFrameRequest(
1517    const WebKit::WebURL& url,
1518    void* notify_data,
1519    const WebKit::WebURLError& error) {
1520}
1521
1522bool BrowserPlugin::executeEditCommand(const WebKit::WebString& name) {
1523  browser_plugin_manager()->Send(new BrowserPluginHostMsg_ExecuteEditCommand(
1524      render_view_routing_id_,
1525      instance_id_,
1526      name.utf8()));
1527
1528  // BrowserPlugin swallows edit commands.
1529  return true;
1530}
1531
1532void BrowserPlugin::OnLockMouseACK(bool succeeded) {
1533  browser_plugin_manager()->Send(new BrowserPluginHostMsg_LockMouse_ACK(
1534      render_view_routing_id_,
1535      instance_id_,
1536      succeeded));
1537}
1538
1539void BrowserPlugin::OnMouseLockLost() {
1540  browser_plugin_manager()->Send(new BrowserPluginHostMsg_UnlockMouse_ACK(
1541      render_view_routing_id_,
1542      instance_id_));
1543}
1544
1545bool BrowserPlugin::HandleMouseLockedInputEvent(
1546    const WebKit::WebMouseEvent& event) {
1547  browser_plugin_manager()->Send(
1548      new BrowserPluginHostMsg_HandleInputEvent(render_view_routing_id_,
1549                                                instance_id_,
1550                                                plugin_rect_,
1551                                                &event));
1552  return true;
1553}
1554
1555}  // namespace content
1556