Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / content / renderer / browser_plugin / browser_plugin.cc
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/json/json_string_value_serializer.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/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_manager.h"
21 #include "content/renderer/child_frame_compositing_helper.h"
22 #include "content/renderer/cursor_utils.h"
23 #include "content/renderer/drop_data_builder.h"
24 #include "content/renderer/render_thread_impl.h"
25 #include "content/renderer/sad_plugin.h"
26 #include "content/renderer/v8_value_converter_impl.h"
27 #include "third_party/WebKit/public/platform/WebRect.h"
28 #include "third_party/WebKit/public/web/WebBindings.h"
29 #include "third_party/WebKit/public/web/WebDOMCustomEvent.h"
30 #include "third_party/WebKit/public/web/WebDocument.h"
31 #include "third_party/WebKit/public/web/WebElement.h"
32 #include "third_party/WebKit/public/web/WebInputEvent.h"
33 #include "third_party/WebKit/public/web/WebLocalFrame.h"
34 #include "third_party/WebKit/public/web/WebPluginContainer.h"
35 #include "third_party/WebKit/public/web/WebPluginParams.h"
36 #include "third_party/WebKit/public/web/WebScriptSource.h"
37 #include "third_party/WebKit/public/web/WebView.h"
38 #include "third_party/skia/include/core/SkCanvas.h"
39 #include "ui/events/keycodes/keyboard_codes.h"
40
41 #if defined (OS_WIN)
42 #include "base/sys_info.h"
43 #endif
44
45 using blink::WebCanvas;
46 using blink::WebPluginContainer;
47 using blink::WebPluginParams;
48 using blink::WebPoint;
49 using blink::WebRect;
50 using blink::WebURL;
51 using blink::WebVector;
52
53 namespace content {
54
55 namespace {
56
57 const char* kCustomPersistPartition = "persist:custom_plugin";
58
59 static std::string GetInternalEventName(const char* event_name) {
60   return base::StringPrintf("-internal-%s", event_name);
61 }
62
63 }  // namespace
64
65 BrowserPlugin::BrowserPlugin(RenderViewImpl* render_view,
66                              blink::WebFrame* frame,
67                              bool auto_navigate)
68     : guest_instance_id_(browser_plugin::kInstanceIDNone),
69       attached_(false),
70       render_view_(render_view->AsWeakPtr()),
71       render_view_routing_id_(render_view->GetRoutingID()),
72       container_(NULL),
73       paint_ack_received_(true),
74       last_device_scale_factor_(1.0f),
75       sad_guest_(NULL),
76       guest_crashed_(false),
77       is_auto_size_state_dirty_(false),
78       persist_storage_(false),
79       valid_partition_id_(true),
80       content_window_routing_id_(MSG_ROUTING_NONE),
81       plugin_focused_(false),
82       visible_(true),
83       auto_navigate_(auto_navigate),
84       before_first_navigation_(true),
85       mouse_locked_(false),
86       browser_plugin_manager_(render_view->GetBrowserPluginManager()),
87       embedder_frame_url_(frame->document().url()),
88       weak_ptr_factory_(this) {
89 }
90
91 BrowserPlugin::~BrowserPlugin() {
92   // If the BrowserPlugin has never navigated then the browser process and
93   // BrowserPluginManager don't know about it and so there is nothing to do
94   // here.
95   if (!HasGuestInstanceID())
96     return;
97   browser_plugin_manager()->RemoveBrowserPlugin(guest_instance_id_);
98   browser_plugin_manager()->Send(
99       new BrowserPluginHostMsg_PluginDestroyed(render_view_routing_id_,
100                                                guest_instance_id_));
101 }
102
103 bool BrowserPlugin::OnMessageReceived(const IPC::Message& message) {
104   bool handled = true;
105   IPC_BEGIN_MESSAGE_MAP(BrowserPlugin, message)
106     IPC_MESSAGE_HANDLER(BrowserPluginMsg_AdvanceFocus, OnAdvanceFocus)
107     IPC_MESSAGE_HANDLER(BrowserPluginMsg_Attach_ACK, OnAttachACK)
108     IPC_MESSAGE_HANDLER(BrowserPluginMsg_BuffersSwapped, OnBuffersSwapped)
109     IPC_MESSAGE_HANDLER_GENERIC(BrowserPluginMsg_CompositorFrameSwapped,
110                                 OnCompositorFrameSwapped(message))
111     IPC_MESSAGE_HANDLER(BrowserPluginMsg_CopyFromCompositingSurface,
112                         OnCopyFromCompositingSurface)
113     IPC_MESSAGE_HANDLER(BrowserPluginMsg_GuestContentWindowReady,
114                         OnGuestContentWindowReady)
115     IPC_MESSAGE_HANDLER(BrowserPluginMsg_GuestGone, OnGuestGone)
116     IPC_MESSAGE_HANDLER(BrowserPluginMsg_SetCursor, OnSetCursor)
117     IPC_MESSAGE_HANDLER(BrowserPluginMsg_SetMouseLock, OnSetMouseLock)
118     IPC_MESSAGE_HANDLER(BrowserPluginMsg_ShouldAcceptTouchEvents,
119                         OnShouldAcceptTouchEvents)
120     IPC_MESSAGE_HANDLER(BrowserPluginMsg_UpdatedName, OnUpdatedName)
121     IPC_MESSAGE_HANDLER(BrowserPluginMsg_UpdateRect, OnUpdateRect)
122     IPC_MESSAGE_UNHANDLED(handled = false)
123   IPC_END_MESSAGE_MAP()
124   return handled;
125 }
126
127 void BrowserPlugin::UpdateDOMAttribute(const std::string& attribute_name,
128                                        const std::string& attribute_value) {
129   if (!container())
130     return;
131
132   blink::WebElement element = container()->element();
133   blink::WebString web_attribute_name =
134       blink::WebString::fromUTF8(attribute_name);
135   if (!HasDOMAttribute(attribute_name) ||
136       (std::string(element.getAttribute(web_attribute_name).utf8()) !=
137           attribute_value)) {
138     element.setAttribute(web_attribute_name,
139         blink::WebString::fromUTF8(attribute_value));
140   }
141 }
142
143 void BrowserPlugin::RemoveDOMAttribute(const std::string& attribute_name) {
144   if (!container())
145     return;
146
147   container()->element().removeAttribute(
148       blink::WebString::fromUTF8(attribute_name));
149 }
150
151 std::string BrowserPlugin::GetDOMAttributeValue(
152     const std::string& attribute_name) const {
153   if (!container())
154     return std::string();
155
156   return container()->element().getAttribute(
157       blink::WebString::fromUTF8(attribute_name)).utf8();
158 }
159
160 bool BrowserPlugin::HasDOMAttribute(const std::string& attribute_name) const {
161   if (!container())
162     return false;
163
164   return container()->element().hasAttribute(
165       blink::WebString::fromUTF8(attribute_name));
166 }
167
168 std::string BrowserPlugin::GetNameAttribute() const {
169   return GetDOMAttributeValue(browser_plugin::kAttributeName);
170 }
171
172 bool BrowserPlugin::GetAllowTransparencyAttribute() const {
173   return HasDOMAttribute(browser_plugin::kAttributeAllowTransparency);
174 }
175
176 std::string BrowserPlugin::GetSrcAttribute() const {
177   return GetDOMAttributeValue(browser_plugin::kAttributeSrc);
178 }
179
180 bool BrowserPlugin::GetAutoSizeAttribute() const {
181   return HasDOMAttribute(browser_plugin::kAttributeAutoSize);
182 }
183
184 int BrowserPlugin::GetMaxHeightAttribute() const {
185   int max_height;
186   base::StringToInt(GetDOMAttributeValue(browser_plugin::kAttributeMaxHeight),
187                     &max_height);
188   return max_height;
189 }
190
191 int BrowserPlugin::GetMaxWidthAttribute() const {
192   int max_width;
193   base::StringToInt(GetDOMAttributeValue(browser_plugin::kAttributeMaxWidth),
194                     &max_width);
195   return max_width;
196 }
197
198 int BrowserPlugin::GetMinHeightAttribute() const {
199   int min_height;
200   base::StringToInt(GetDOMAttributeValue(browser_plugin::kAttributeMinHeight),
201                     &min_height);
202   return min_height;
203 }
204
205 int BrowserPlugin::GetMinWidthAttribute() const {
206   int min_width;
207   base::StringToInt(GetDOMAttributeValue(browser_plugin::kAttributeMinWidth),
208                     &min_width);
209   return min_width;
210 }
211
212 int BrowserPlugin::GetAdjustedMaxHeight() const {
213   int max_height = GetMaxHeightAttribute();
214   return max_height ? max_height : height();
215 }
216
217 int BrowserPlugin::GetAdjustedMaxWidth() const {
218   int max_width = GetMaxWidthAttribute();
219   return max_width ? max_width : width();
220 }
221
222 int BrowserPlugin::GetAdjustedMinHeight() const {
223   int min_height = GetMinHeightAttribute();
224   // FrameView.cpp does not allow this value to be <= 0, so when the value is
225   // unset (or set to 0), we set it to the container size.
226   min_height = min_height ? min_height : height();
227   // For autosize, minHeight should not be bigger than maxHeight.
228   return std::min(min_height, GetAdjustedMaxHeight());
229 }
230
231 int BrowserPlugin::GetAdjustedMinWidth() const {
232   int min_width = GetMinWidthAttribute();
233   // FrameView.cpp does not allow this value to be <= 0, so when the value is
234   // unset (or set to 0), we set it to the container size.
235   min_width = min_width ? min_width : width();
236   // For autosize, minWidth should not be bigger than maxWidth.
237   return std::min(min_width, GetAdjustedMaxWidth());
238 }
239
240 std::string BrowserPlugin::GetPartitionAttribute() const {
241   return GetDOMAttributeValue(browser_plugin::kAttributePartition);
242 }
243
244 void BrowserPlugin::ParseNameAttribute() {
245   if (!HasGuestInstanceID())
246     return;
247   browser_plugin_manager()->Send(
248       new BrowserPluginHostMsg_SetName(render_view_routing_id_,
249                                        guest_instance_id_,
250                                        GetNameAttribute()));
251 }
252
253 void BrowserPlugin::ParseAllowTransparencyAttribute() {
254   if (!HasGuestInstanceID())
255     return;
256
257   bool opaque = !GetAllowTransparencyAttribute();
258
259   if (compositing_helper_)
260     compositing_helper_->SetContentsOpaque(opaque);
261
262   browser_plugin_manager()->Send(new BrowserPluginHostMsg_SetContentsOpaque(
263         render_view_routing_id_,
264         guest_instance_id_,
265         opaque));
266 }
267
268 bool BrowserPlugin::ParseSrcAttribute(std::string* error_message) {
269   if (!valid_partition_id_) {
270     *error_message = browser_plugin::kErrorInvalidPartition;
271     return false;
272   }
273   std::string src = GetSrcAttribute();
274   if (src.empty())
275     return true;
276
277   // If we haven't created the guest yet, do so now. We will navigate it right
278   // after creation. If |src| is empty, we can delay the creation until we
279   // actually need it.
280   if (!HasGuestInstanceID()) {
281     // On initial navigation, we request an instance ID from the browser
282     // process. We essentially ignore all subsequent calls to SetSrcAttribute
283     // until we receive an instance ID. |before_first_navigation_|
284     // prevents BrowserPlugin from allocating more than one instance ID.
285     // Upon receiving an instance ID from the browser process, we continue
286     // the process of navigation by populating the
287     // BrowserPluginHostMsg_Attach_Params with the current state of
288     // BrowserPlugin and sending a BrowserPluginHostMsg_CreateGuest to the
289     // browser process in order to create a new guest.
290     if (before_first_navigation_) {
291       browser_plugin_manager()->AllocateInstanceID(
292           weak_ptr_factory_.GetWeakPtr());
293       before_first_navigation_ = false;
294     }
295     return true;
296   }
297
298   browser_plugin_manager()->Send(
299       new BrowserPluginHostMsg_NavigateGuest(render_view_routing_id_,
300                                              guest_instance_id_,
301                                              src));
302   return true;
303 }
304
305 void BrowserPlugin::ParseAutoSizeAttribute() {
306   last_view_size_ = plugin_rect_.size();
307   is_auto_size_state_dirty_ = true;
308   UpdateGuestAutoSizeState(GetAutoSizeAttribute());
309 }
310
311 void BrowserPlugin::PopulateAutoSizeParameters(
312     BrowserPluginHostMsg_AutoSize_Params* params, bool auto_size_enabled) {
313   params->enable = auto_size_enabled;
314   // No need to populate the params if autosize is off.
315   if (auto_size_enabled) {
316     params->max_size = gfx::Size(GetAdjustedMaxWidth(), GetAdjustedMaxHeight());
317     params->min_size = gfx::Size(GetAdjustedMinWidth(), GetAdjustedMinHeight());
318
319     if (max_auto_size_ != params->max_size)
320       is_auto_size_state_dirty_ = true;
321
322     max_auto_size_ = params->max_size;
323   } else {
324     max_auto_size_ = gfx::Size();
325   }
326 }
327
328 void BrowserPlugin::UpdateGuestAutoSizeState(bool auto_size_enabled) {
329   // If we haven't yet heard back from the guest about the last resize request,
330   // then we don't issue another request until we do in
331   // BrowserPlugin::OnUpdateRect.
332   if (!HasGuestInstanceID() || !paint_ack_received_)
333     return;
334
335   BrowserPluginHostMsg_AutoSize_Params auto_size_params;
336   BrowserPluginHostMsg_ResizeGuest_Params resize_guest_params;
337   if (auto_size_enabled) {
338     GetSizeParams(&auto_size_params, &resize_guest_params, true);
339   } else {
340     GetSizeParams(NULL, &resize_guest_params, true);
341   }
342   paint_ack_received_ = false;
343   browser_plugin_manager()->Send(
344       new BrowserPluginHostMsg_SetAutoSize(render_view_routing_id_,
345                                            guest_instance_id_,
346                                            auto_size_params,
347                                            resize_guest_params));
348 }
349
350 void BrowserPlugin::OnInstanceIDAllocated(int guest_instance_id) {
351   CHECK(guest_instance_id != browser_plugin::kInstanceIDNone);
352
353   if (auto_navigate_) {
354     scoped_ptr<base::DictionaryValue> params(new base::DictionaryValue());
355     Attach(guest_instance_id, params.Pass());
356     return;
357   }
358
359   std::map<std::string, base::Value*> props;
360   props[browser_plugin::kWindowID] =
361       new base::FundamentalValue(guest_instance_id);
362   TriggerEvent(browser_plugin::kEventInternalInstanceIDAllocated, &props);
363 }
364
365 void BrowserPlugin::Attach(int guest_instance_id,
366                            scoped_ptr<base::DictionaryValue> extra_params) {
367   CHECK(guest_instance_id != browser_plugin::kInstanceIDNone);
368
369   // If this BrowserPlugin is already attached to a guest, then do nothing.
370   if (HasGuestInstanceID())
371     return;
372
373   // This API may be called directly without setting the src attribute.
374   // In that case, we need to make sure we don't allocate another instance ID.
375   before_first_navigation_ = false;
376   guest_instance_id_ = guest_instance_id;
377   browser_plugin_manager()->AddBrowserPlugin(guest_instance_id, this);
378
379   BrowserPluginHostMsg_Attach_Params attach_params;
380   attach_params.focused = ShouldGuestBeFocused();
381   attach_params.visible = visible_;
382   attach_params.opaque = !GetAllowTransparencyAttribute();
383   attach_params.name = GetNameAttribute();
384   attach_params.storage_partition_id = storage_partition_id_;
385   attach_params.persist_storage = persist_storage_;
386   attach_params.src = GetSrcAttribute();
387   attach_params.embedder_frame_url = embedder_frame_url_;
388   GetSizeParams(&attach_params.auto_size_params,
389                 &attach_params.resize_guest_params,
390                 false);
391
392   browser_plugin_manager()->Send(
393       new BrowserPluginHostMsg_Attach(render_view_routing_id_,
394                                       guest_instance_id_, attach_params,
395                                       *extra_params));
396 }
397
398 void BrowserPlugin::DidCommitCompositorFrame() {
399   if (compositing_helper_.get())
400     compositing_helper_->DidCommitCompositorFrame();
401 }
402
403 void BrowserPlugin::OnAdvanceFocus(int guest_instance_id, bool reverse) {
404   DCHECK(render_view_.get());
405   render_view_->GetWebView()->advanceFocus(reverse);
406 }
407
408 void BrowserPlugin::OnAttachACK(
409     int guest_instance_id,
410     const BrowserPluginMsg_Attach_ACK_Params& params) {
411   // Update BrowserPlugin attributes to match the state of the guest.
412   if (!params.name.empty())
413     OnUpdatedName(guest_instance_id, params.name);
414   if (!params.storage_partition_id.empty()) {
415     std::string partition_name =
416         (params.persist_storage ? browser_plugin::kPersistPrefix : "") +
417             params.storage_partition_id;
418     UpdateDOMAttribute(browser_plugin::kAttributePartition, partition_name);
419   }
420   attached_ = true;
421 }
422
423 void BrowserPlugin::OnBuffersSwapped(
424     int instance_id,
425     const FrameMsg_BuffersSwapped_Params& params) {
426   EnableCompositing(true);
427
428   compositing_helper_->OnBuffersSwapped(params.size,
429                                         params.mailbox,
430                                         params.gpu_route_id,
431                                         params.gpu_host_id,
432                                         GetDeviceScaleFactor());
433 }
434
435 void BrowserPlugin::OnCompositorFrameSwapped(const IPC::Message& message) {
436   BrowserPluginMsg_CompositorFrameSwapped::Param param;
437   if (!BrowserPluginMsg_CompositorFrameSwapped::Read(&message, &param))
438     return;
439   scoped_ptr<cc::CompositorFrame> frame(new cc::CompositorFrame);
440   param.b.frame.AssignTo(frame.get());
441
442   EnableCompositing(true);
443   compositing_helper_->OnCompositorFrameSwapped(frame.Pass(),
444                                                 param.b.producing_route_id,
445                                                 param.b.output_surface_id,
446                                                 param.b.producing_host_id,
447                                                 param.b.shared_memory_handle);
448 }
449
450 void BrowserPlugin::OnCopyFromCompositingSurface(int guest_instance_id,
451                                                  int request_id,
452                                                  gfx::Rect source_rect,
453                                                  gfx::Size dest_size) {
454   if (!compositing_helper_) {
455     browser_plugin_manager()->Send(
456         new BrowserPluginHostMsg_CopyFromCompositingSurfaceAck(
457             render_view_routing_id_,
458             guest_instance_id_,
459             request_id,
460             SkBitmap()));
461     return;
462   }
463   compositing_helper_->CopyFromCompositingSurface(request_id, source_rect,
464                                                   dest_size);
465 }
466
467 void BrowserPlugin::OnGuestContentWindowReady(int guest_instance_id,
468                                               int content_window_routing_id) {
469   DCHECK(content_window_routing_id != MSG_ROUTING_NONE);
470   content_window_routing_id_ = content_window_routing_id;
471 }
472
473 void BrowserPlugin::OnGuestGone(int guest_instance_id) {
474   guest_crashed_ = true;
475
476   // Turn off compositing so we can display the sad graphic. Changes to
477   // compositing state will show up at a later time after a layout and commit.
478   EnableCompositing(false);
479
480   // Queue up showing the sad graphic to give content embedders an opportunity
481   // to fire their listeners and potentially overlay the webview with custom
482   // behavior. If the BrowserPlugin is destroyed in the meantime, then the
483   // task will not be executed.
484   base::MessageLoop::current()->PostTask(
485       FROM_HERE,
486       base::Bind(&BrowserPlugin::ShowSadGraphic,
487                  weak_ptr_factory_.GetWeakPtr()));
488 }
489
490 void BrowserPlugin::OnSetCursor(int guest_instance_id,
491                                 const WebCursor& cursor) {
492   cursor_ = cursor;
493 }
494
495 void BrowserPlugin::OnSetMouseLock(int guest_instance_id,
496                                    bool enable) {
497   if (enable) {
498     if (mouse_locked_)
499       return;
500     render_view_->mouse_lock_dispatcher()->LockMouse(this);
501   } else {
502     if (!mouse_locked_) {
503       OnLockMouseACK(false);
504       return;
505     }
506     render_view_->mouse_lock_dispatcher()->UnlockMouse(this);
507   }
508 }
509
510 void BrowserPlugin::OnShouldAcceptTouchEvents(int guest_instance_id,
511                                               bool accept) {
512   if (container()) {
513     container()->requestTouchEventType(accept ?
514         blink::WebPluginContainer::TouchEventRequestTypeRaw :
515         blink::WebPluginContainer::TouchEventRequestTypeNone);
516   }
517 }
518
519 void BrowserPlugin::OnUpdatedName(int guest_instance_id,
520                                   const std::string& name) {
521   UpdateDOMAttribute(browser_plugin::kAttributeName, name);
522 }
523
524 void BrowserPlugin::OnUpdateRect(
525     int guest_instance_id,
526     const BrowserPluginMsg_UpdateRect_Params& params) {
527   // Note that there is no need to send ACK for this message.
528   // If the guest has updated pixels then it is no longer crashed.
529   guest_crashed_ = false;
530
531   bool auto_size = GetAutoSizeAttribute();
532   // We receive a resize ACK in regular mode, but not in autosize.
533   // In Compositing mode, we need to do it here so we can continue sending
534   // resize messages when needed.
535   if (params.is_resize_ack || (auto_size || is_auto_size_state_dirty_))
536     paint_ack_received_ = true;
537
538   bool was_auto_size_state_dirty = auto_size && is_auto_size_state_dirty_;
539   is_auto_size_state_dirty_ = false;
540
541   if ((!auto_size && (width() != params.view_size.width() ||
542                       height() != params.view_size.height())) ||
543       (auto_size && was_auto_size_state_dirty) ||
544       GetDeviceScaleFactor() != params.scale_factor) {
545     UpdateGuestAutoSizeState(auto_size);
546     return;
547   }
548
549   if (auto_size && (params.view_size != last_view_size_))
550     last_view_size_ = params.view_size;
551
552   BrowserPluginHostMsg_AutoSize_Params auto_size_params;
553   BrowserPluginHostMsg_ResizeGuest_Params resize_guest_params;
554
555   // BrowserPluginHostMsg_UpdateRect_ACK is used by both the compositing and
556   // software paths to piggyback updated autosize parameters.
557   if (auto_size)
558     PopulateAutoSizeParameters(&auto_size_params, auto_size);
559
560   browser_plugin_manager()->Send(
561       new BrowserPluginHostMsg_SetAutoSize(render_view_routing_id_,
562                                            guest_instance_id_,
563                                            auto_size_params,
564                                            resize_guest_params));
565 }
566
567 void BrowserPlugin::ParseSizeContraintsChanged() {
568   bool auto_size = GetAutoSizeAttribute();
569   if (auto_size) {
570     is_auto_size_state_dirty_ = true;
571     UpdateGuestAutoSizeState(true);
572   }
573 }
574
575 bool BrowserPlugin::InAutoSizeBounds(const gfx::Size& size) const {
576   return size.width() <= GetAdjustedMaxWidth() &&
577       size.height() <= GetAdjustedMaxHeight();
578 }
579
580 NPObject* BrowserPlugin::GetContentWindow() const {
581   if (content_window_routing_id_ == MSG_ROUTING_NONE)
582     return NULL;
583   RenderViewImpl* guest_render_view = RenderViewImpl::FromRoutingID(
584       content_window_routing_id_);
585   if (!guest_render_view)
586     return NULL;
587   blink::WebFrame* guest_frame = guest_render_view->GetWebView()->mainFrame();
588   return guest_frame->windowObject();
589 }
590
591 bool BrowserPlugin::HasNavigated() const {
592   return !before_first_navigation_;
593 }
594
595 bool BrowserPlugin::HasGuestInstanceID() const {
596   return guest_instance_id_ != browser_plugin::kInstanceIDNone;
597 }
598
599 bool BrowserPlugin::ParsePartitionAttribute(std::string* error_message) {
600   if (HasNavigated()) {
601     *error_message = browser_plugin::kErrorAlreadyNavigated;
602     return false;
603   }
604
605   std::string input;
606   if (auto_navigate_)
607     input = kCustomPersistPartition;
608   else
609     input = GetPartitionAttribute();
610
611   // Since the "persist:" prefix is in ASCII, StartsWith will work fine on
612   // UTF-8 encoded |partition_id|. If the prefix is a match, we can safely
613   // remove the prefix without splicing in the middle of a multi-byte codepoint.
614   // We can use the rest of the string as UTF-8 encoded one.
615   if (StartsWithASCII(input, browser_plugin::kPersistPrefix, true)) {
616     size_t index = input.find(":");
617     CHECK(index != std::string::npos);
618     // It is safe to do index + 1, since we tested for the full prefix above.
619     input = input.substr(index + 1);
620     if (input.empty()) {
621       valid_partition_id_ = false;
622       *error_message = browser_plugin::kErrorInvalidPartition;
623       return false;
624     }
625     persist_storage_ = true;
626   } else {
627     persist_storage_ = false;
628   }
629
630   valid_partition_id_ = true;
631   storage_partition_id_ = input;
632   return true;
633 }
634
635 bool BrowserPlugin::CanRemovePartitionAttribute(std::string* error_message) {
636   if (HasGuestInstanceID())
637     *error_message = browser_plugin::kErrorCannotRemovePartition;
638   return !HasGuestInstanceID();
639 }
640
641 void BrowserPlugin::ShowSadGraphic() {
642   // If the BrowserPlugin is scheduled to be deleted, then container_ will be
643   // NULL so we shouldn't attempt to access it.
644   if (container_)
645     container_->invalidate();
646 }
647
648 void BrowserPlugin::ParseAttributes() {
649   // TODO(mthiesse): Handle errors here?
650   std::string error;
651   ParsePartitionAttribute(&error);
652
653   // Parse the 'src' attribute last, as it will set the has_navigated_ flag to
654   // true, which prevents changing the 'partition' attribute.
655   ParseSrcAttribute(&error);
656 }
657
658 float BrowserPlugin::GetDeviceScaleFactor() const {
659   if (!render_view_.get())
660     return 1.0f;
661   return render_view_->GetWebView()->deviceScaleFactor();
662 }
663
664 void BrowserPlugin::UpdateDeviceScaleFactor(float device_scale_factor) {
665   if (last_device_scale_factor_ == device_scale_factor || !paint_ack_received_)
666     return;
667
668   BrowserPluginHostMsg_ResizeGuest_Params params;
669   PopulateResizeGuestParameters(&params, plugin_rect(), false);
670   browser_plugin_manager()->Send(new BrowserPluginHostMsg_ResizeGuest(
671       render_view_routing_id_,
672       guest_instance_id_,
673       params));
674 }
675
676 void BrowserPlugin::TriggerEvent(const std::string& event_name,
677                                  std::map<std::string, base::Value*>* props) {
678   if (!container())
679     return;
680
681   blink::WebLocalFrame* frame = container()->element().document().frame();
682   if (!frame)
683     return;
684
685   v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
686   v8::Local<v8::Context> context = frame->mainWorldScriptContext();
687   v8::Context::Scope context_scope(context);
688
689   std::string json_string;
690   if (props) {
691     base::DictionaryValue dict;
692     for (std::map<std::string, base::Value*>::iterator iter = props->begin(),
693              end = props->end(); iter != end; ++iter) {
694       dict.Set(iter->first, iter->second);
695     }
696
697     JSONStringValueSerializer serializer(&json_string);
698     if (!serializer.Serialize(dict))
699       return;
700   }
701
702   blink::WebDOMEvent dom_event = frame->document().createEvent("CustomEvent");
703   blink::WebDOMCustomEvent event = dom_event.to<blink::WebDOMCustomEvent>();
704
705   // The events triggered directly from the plugin <object> are internal events
706   // whose implementation details can (and likely will) change over time. The
707   // wrapper/shim (e.g. <webview> tag) should receive these events, and expose a
708   // more appropriate (and stable) event to the consumers as part of the API.
709   event.initCustomEvent(
710       blink::WebString::fromUTF8(GetInternalEventName(event_name.c_str())),
711       false,
712       false,
713       blink::WebSerializedScriptValue::serialize(
714           v8::String::NewFromUtf8(context->GetIsolate(),
715                                   json_string.c_str(),
716                                   v8::String::kNormalString,
717                                   json_string.size())));
718   container()->element().dispatchEvent(event);
719 }
720
721 void BrowserPlugin::UpdateGuestFocusState() {
722   if (!HasGuestInstanceID())
723     return;
724   bool should_be_focused = ShouldGuestBeFocused();
725   browser_plugin_manager()->Send(new BrowserPluginHostMsg_SetFocus(
726       render_view_routing_id_,
727       guest_instance_id_,
728       should_be_focused));
729 }
730
731 bool BrowserPlugin::ShouldGuestBeFocused() const {
732   bool embedder_focused = false;
733   if (render_view_.get())
734     embedder_focused = render_view_->has_focus();
735   return plugin_focused_ && embedder_focused;
736 }
737
738 blink::WebPluginContainer* BrowserPlugin::container() const {
739   return container_;
740 }
741
742 bool BrowserPlugin::initialize(WebPluginContainer* container) {
743   if (!container)
744     return false;
745
746   if (!GetContentClient()->renderer()->AllowBrowserPlugin(container) &&
747       !auto_navigate_)
748     return false;
749
750   // Tell |container| to allow this plugin to use script objects.
751   npp_.reset(new NPP_t);
752   container->allowScriptObjects();
753
754   bindings_.reset(new BrowserPluginBindings(this));
755   container_ = container;
756   container_->setWantsWheelEvents(true);
757   ParseAttributes();
758   return true;
759 }
760
761 void BrowserPlugin::EnableCompositing(bool enable) {
762   bool enabled = !!compositing_helper_;
763   if (enabled == enable)
764     return;
765
766   if (enable) {
767     DCHECK(!compositing_helper_.get());
768     if (!compositing_helper_.get()) {
769       compositing_helper_ =
770           ChildFrameCompositingHelper::CreateCompositingHelperForBrowserPlugin(
771               weak_ptr_factory_.GetWeakPtr());
772     }
773   }
774   compositing_helper_->EnableCompositing(enable);
775   compositing_helper_->SetContentsOpaque(!GetAllowTransparencyAttribute());
776
777   if (!enable) {
778     DCHECK(compositing_helper_.get());
779     compositing_helper_->OnContainerDestroy();
780     compositing_helper_ = NULL;
781   }
782 }
783
784 void BrowserPlugin::destroy() {
785   // If the plugin was initialized then it has a valid |npp_| identifier, and
786   // the |container_| must clear references to the plugin's script objects.
787   DCHECK(!npp_ || container_);
788   if (container_)
789     container_->clearScriptObjects();
790
791   if (compositing_helper_.get())
792     compositing_helper_->OnContainerDestroy();
793   container_ = NULL;
794   // Will be a no-op if the mouse is not currently locked.
795   if (render_view_.get())
796     render_view_->mouse_lock_dispatcher()->OnLockTargetDestroyed(this);
797   base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
798 }
799
800 NPObject* BrowserPlugin::scriptableObject() {
801   if (!bindings_)
802     return NULL;
803
804   NPObject* browser_plugin_np_object(bindings_->np_object());
805   // The object is expected to be retained before it is returned.
806   blink::WebBindings::retainObject(browser_plugin_np_object);
807   return browser_plugin_np_object;
808 }
809
810 NPP BrowserPlugin::pluginNPP() {
811   return npp_.get();
812 }
813
814 bool BrowserPlugin::supportsKeyboardFocus() const {
815   return true;
816 }
817
818 bool BrowserPlugin::supportsEditCommands() const {
819   return true;
820 }
821
822 bool BrowserPlugin::supportsInputMethod() const {
823   return true;
824 }
825
826 bool BrowserPlugin::canProcessDrag() const {
827   return true;
828 }
829
830 void BrowserPlugin::paint(WebCanvas* canvas, const WebRect& rect) {
831   if (guest_crashed_) {
832     if (!sad_guest_)  // Lazily initialize bitmap.
833       sad_guest_ = content::GetContentClient()->renderer()->
834           GetSadWebViewBitmap();
835     // content_shell does not have the sad plugin bitmap, so we'll paint black
836     // instead to make it clear that something went wrong.
837     if (sad_guest_) {
838       PaintSadPlugin(canvas, plugin_rect_, *sad_guest_);
839       return;
840     }
841   }
842   SkAutoCanvasRestore auto_restore(canvas, true);
843   canvas->translate(plugin_rect_.x(), plugin_rect_.y());
844   SkRect image_data_rect = SkRect::MakeXYWH(
845       SkIntToScalar(0),
846       SkIntToScalar(0),
847       SkIntToScalar(plugin_rect_.width()),
848       SkIntToScalar(plugin_rect_.height()));
849   canvas->clipRect(image_data_rect);
850   // Paint black or white in case we have nothing in our backing store or we
851   // need to show a gutter.
852   SkPaint paint;
853   paint.setStyle(SkPaint::kFill_Style);
854   paint.setColor(guest_crashed_ ? SK_ColorBLACK : SK_ColorWHITE);
855   canvas->drawRect(image_data_rect, paint);
856 }
857
858 // static
859 bool BrowserPlugin::ShouldForwardToBrowserPlugin(
860     const IPC::Message& message) {
861   switch (message.type()) {
862     case BrowserPluginMsg_AdvanceFocus::ID:
863     case BrowserPluginMsg_Attach_ACK::ID:
864     case BrowserPluginMsg_BuffersSwapped::ID:
865     case BrowserPluginMsg_CompositorFrameSwapped::ID:
866     case BrowserPluginMsg_CopyFromCompositingSurface::ID:
867     case BrowserPluginMsg_GuestContentWindowReady::ID:
868     case BrowserPluginMsg_GuestGone::ID:
869     case BrowserPluginMsg_SetCursor::ID:
870     case BrowserPluginMsg_SetMouseLock::ID:
871     case BrowserPluginMsg_ShouldAcceptTouchEvents::ID:
872     case BrowserPluginMsg_UpdatedName::ID:
873     case BrowserPluginMsg_UpdateRect::ID:
874       return true;
875     default:
876       break;
877   }
878   return false;
879 }
880
881 void BrowserPlugin::updateGeometry(
882     const WebRect& window_rect,
883     const WebRect& clip_rect,
884     const WebVector<WebRect>& cut_outs_rects,
885     bool is_visible) {
886   int old_width = width();
887   int old_height = height();
888   plugin_rect_ = window_rect;
889   if (!attached())
890     return;
891
892   // In AutoSize mode, guests don't care when the BrowserPlugin container is
893   // resized. If |!paint_ack_received_|, then we are still waiting on a
894   // previous resize to be ACK'ed and so we don't issue additional resizes
895   // until the previous one is ACK'ed.
896   // TODO(mthiesse): Assess the performance of calling GetAutoSizeAttribute() on
897   // resize.
898   if (!paint_ack_received_ ||
899       (old_width == window_rect.width && old_height == window_rect.height) ||
900       GetAutoSizeAttribute()) {
901     // Let the browser know about the updated view rect.
902     browser_plugin_manager()->Send(new BrowserPluginHostMsg_UpdateGeometry(
903         render_view_routing_id_, guest_instance_id_, plugin_rect_));
904     return;
905   }
906
907   BrowserPluginHostMsg_ResizeGuest_Params params;
908   PopulateResizeGuestParameters(&params, plugin_rect(), false);
909   paint_ack_received_ = false;
910   browser_plugin_manager()->Send(new BrowserPluginHostMsg_ResizeGuest(
911       render_view_routing_id_,
912       guest_instance_id_,
913       params));
914 }
915
916 void BrowserPlugin::PopulateResizeGuestParameters(
917     BrowserPluginHostMsg_ResizeGuest_Params* params,
918     const gfx::Rect& view_rect,
919     bool needs_repaint) {
920   params->size_changed = true;
921   params->view_rect = view_rect;
922   params->repaint = needs_repaint;
923   params->scale_factor = GetDeviceScaleFactor();
924   if (last_device_scale_factor_ != params->scale_factor){
925     params->repaint = true;
926     last_device_scale_factor_ = params->scale_factor;
927   }
928 }
929
930 void BrowserPlugin::GetSizeParams(
931     BrowserPluginHostMsg_AutoSize_Params* auto_size_params,
932     BrowserPluginHostMsg_ResizeGuest_Params* resize_guest_params,
933     bool needs_repaint) {
934   if (auto_size_params) {
935     PopulateAutoSizeParameters(auto_size_params, GetAutoSizeAttribute());
936   } else {
937     max_auto_size_ = gfx::Size();
938   }
939   gfx::Size view_size = (auto_size_params && auto_size_params->enable) ?
940       auto_size_params->max_size : gfx::Size(width(), height());
941   if (view_size.IsEmpty())
942     return;
943   paint_ack_received_ = false;
944   gfx::Rect view_rect = gfx::Rect(plugin_rect_.origin(), view_size);
945   PopulateResizeGuestParameters(resize_guest_params, view_rect, needs_repaint);
946 }
947
948 void BrowserPlugin::updateFocus(bool focused) {
949   plugin_focused_ = focused;
950   UpdateGuestFocusState();
951 }
952
953 void BrowserPlugin::updateVisibility(bool visible) {
954   if (visible_ == visible)
955     return;
956
957   visible_ = visible;
958   if (!HasGuestInstanceID())
959     return;
960
961   if (compositing_helper_.get())
962     compositing_helper_->UpdateVisibility(visible);
963
964   browser_plugin_manager()->Send(new BrowserPluginHostMsg_SetVisibility(
965       render_view_routing_id_,
966       guest_instance_id_,
967       visible));
968 }
969
970 bool BrowserPlugin::acceptsInputEvents() {
971   return true;
972 }
973
974 bool BrowserPlugin::handleInputEvent(const blink::WebInputEvent& event,
975                                      blink::WebCursorInfo& cursor_info) {
976   if (guest_crashed_ || !HasGuestInstanceID())
977     return false;
978
979   if (event.type == blink::WebInputEvent::ContextMenu)
980     return true;
981
982   const blink::WebInputEvent* modified_event = &event;
983   scoped_ptr<blink::WebTouchEvent> touch_event;
984   if (blink::WebInputEvent::isTouchEventType(event.type)) {
985     const blink::WebTouchEvent* orig_touch_event =
986         static_cast<const blink::WebTouchEvent*>(&event);
987
988     touch_event.reset(new blink::WebTouchEvent());
989     memcpy(touch_event.get(), orig_touch_event, sizeof(blink::WebTouchEvent));
990
991     // TODO(bokan): Blink passes back a WebGestureEvent with a touches,
992     // changedTouches, and targetTouches lists; however, it doesn't set
993     // the state field on the touches which is what the RenderWidget uses
994     // to create a WebCore::TouchEvent. crbug.com/358132 tracks removing
995     // these multiple lists from WebTouchEvent since they lead to misuse
996     // like this and are functionally unused. In the mean time we'll setup
997     // the state field here manually to fix multi-touch BrowserPlugins.
998     for (size_t i = 0; i < touch_event->touchesLength; ++i) {
999       blink::WebTouchPoint& touch = touch_event->touches[i];
1000       touch.state = blink::WebTouchPoint::StateStationary;
1001       for (size_t j = 0; j < touch_event->changedTouchesLength; ++j) {
1002         blink::WebTouchPoint& changed_touch = touch_event->changedTouches[j];
1003         if (touch.id == changed_touch.id) {
1004           touch.state = changed_touch.state;
1005           break;
1006         }
1007       }
1008     }
1009
1010     // For End and Cancel, Blink gives BrowserPlugin a list of touches that
1011     // are down, but the browser process expects a list of all touches. We
1012     // modify these events here to match these expectations.
1013     if (event.type == blink::WebInputEvent::TouchEnd ||
1014         event.type == blink::WebInputEvent::TouchCancel) {
1015       if (touch_event->changedTouchesLength > 0) {
1016         memcpy(&touch_event->touches[touch_event->touchesLength],
1017                &touch_event->changedTouches,
1018               touch_event->changedTouchesLength * sizeof(blink::WebTouchPoint));
1019         touch_event->touchesLength += touch_event->changedTouchesLength;
1020       }
1021     }
1022     modified_event = touch_event.get();
1023   }
1024
1025   if (blink::WebInputEvent::isKeyboardEventType(event.type) &&
1026       !edit_commands_.empty()) {
1027     browser_plugin_manager()->Send(
1028         new BrowserPluginHostMsg_SetEditCommandsForNextKeyEvent(
1029             render_view_routing_id_,
1030             guest_instance_id_,
1031             edit_commands_));
1032     edit_commands_.clear();
1033   }
1034
1035   browser_plugin_manager()->Send(
1036       new BrowserPluginHostMsg_HandleInputEvent(render_view_routing_id_,
1037                                                 guest_instance_id_,
1038                                                 plugin_rect_,
1039                                                 modified_event));
1040   GetWebKitCursorInfo(cursor_, &cursor_info);
1041   return true;
1042 }
1043
1044 bool BrowserPlugin::handleDragStatusUpdate(blink::WebDragStatus drag_status,
1045                                            const blink::WebDragData& drag_data,
1046                                            blink::WebDragOperationsMask mask,
1047                                            const blink::WebPoint& position,
1048                                            const blink::WebPoint& screen) {
1049   if (guest_crashed_ || !HasGuestInstanceID())
1050     return false;
1051   browser_plugin_manager()->Send(
1052       new BrowserPluginHostMsg_DragStatusUpdate(
1053         render_view_routing_id_,
1054         guest_instance_id_,
1055         drag_status,
1056         DropDataBuilder::Build(drag_data),
1057         mask,
1058         position));
1059   return true;
1060 }
1061
1062 void BrowserPlugin::didReceiveResponse(
1063     const blink::WebURLResponse& response) {
1064 }
1065
1066 void BrowserPlugin::didReceiveData(const char* data, int data_length) {
1067   if (auto_navigate_) {
1068     std::string value(data, data_length);
1069     html_string_ += value;
1070   }
1071 }
1072
1073 void BrowserPlugin::didFinishLoading() {
1074   if (auto_navigate_) {
1075     UpdateDOMAttribute(content::browser_plugin::kAttributeSrc, html_string_);
1076     ParseAttributes();
1077   }
1078 }
1079
1080 void BrowserPlugin::didFailLoading(const blink::WebURLError& error) {
1081 }
1082
1083 void BrowserPlugin::didFinishLoadingFrameRequest(const blink::WebURL& url,
1084                                                  void* notify_data) {
1085 }
1086
1087 void BrowserPlugin::didFailLoadingFrameRequest(
1088     const blink::WebURL& url,
1089     void* notify_data,
1090     const blink::WebURLError& error) {
1091 }
1092
1093 bool BrowserPlugin::executeEditCommand(const blink::WebString& name) {
1094   browser_plugin_manager()->Send(new BrowserPluginHostMsg_ExecuteEditCommand(
1095       render_view_routing_id_,
1096       guest_instance_id_,
1097       name.utf8()));
1098
1099   // BrowserPlugin swallows edit commands.
1100   return true;
1101 }
1102
1103 bool BrowserPlugin::executeEditCommand(const blink::WebString& name,
1104                                        const blink::WebString& value) {
1105   edit_commands_.push_back(EditCommand(name.utf8(), value.utf8()));
1106   // BrowserPlugin swallows edit commands.
1107   return true;
1108 }
1109
1110 bool BrowserPlugin::setComposition(
1111     const blink::WebString& text,
1112     const blink::WebVector<blink::WebCompositionUnderline>& underlines,
1113     int selectionStart,
1114     int selectionEnd) {
1115   if (!HasGuestInstanceID())
1116     return false;
1117   std::vector<blink::WebCompositionUnderline> std_underlines;
1118   for (size_t i = 0; i < underlines.size(); ++i) {
1119     std_underlines.push_back(underlines[i]);
1120   }
1121   browser_plugin_manager()->Send(new BrowserPluginHostMsg_ImeSetComposition(
1122       render_view_routing_id_,
1123       guest_instance_id_,
1124       text.utf8(),
1125       std_underlines,
1126       selectionStart,
1127       selectionEnd));
1128   // TODO(kochi): This assumes the IPC handling always succeeds.
1129   return true;
1130 }
1131
1132 bool BrowserPlugin::confirmComposition(
1133     const blink::WebString& text,
1134     blink::WebWidget::ConfirmCompositionBehavior selectionBehavior) {
1135   if (!HasGuestInstanceID())
1136     return false;
1137   bool keep_selection = (selectionBehavior == blink::WebWidget::KeepSelection);
1138   browser_plugin_manager()->Send(new BrowserPluginHostMsg_ImeConfirmComposition(
1139       render_view_routing_id_,
1140       guest_instance_id_,
1141       text.utf8(),
1142       keep_selection));
1143   // TODO(kochi): This assumes the IPC handling always succeeds.
1144   return true;
1145 }
1146
1147 void BrowserPlugin::extendSelectionAndDelete(int before, int after) {
1148   if (!HasGuestInstanceID())
1149     return;
1150   browser_plugin_manager()->Send(
1151       new BrowserPluginHostMsg_ExtendSelectionAndDelete(
1152           render_view_routing_id_,
1153           guest_instance_id_,
1154           before,
1155           after));
1156 }
1157
1158 void BrowserPlugin::OnLockMouseACK(bool succeeded) {
1159   mouse_locked_ = succeeded;
1160   browser_plugin_manager()->Send(new BrowserPluginHostMsg_LockMouse_ACK(
1161       render_view_routing_id_,
1162       guest_instance_id_,
1163       succeeded));
1164 }
1165
1166 void BrowserPlugin::OnMouseLockLost() {
1167   mouse_locked_ = false;
1168   browser_plugin_manager()->Send(new BrowserPluginHostMsg_UnlockMouse_ACK(
1169       render_view_routing_id_,
1170       guest_instance_id_));
1171 }
1172
1173 bool BrowserPlugin::HandleMouseLockedInputEvent(
1174     const blink::WebMouseEvent& event) {
1175   browser_plugin_manager()->Send(
1176       new BrowserPluginHostMsg_HandleInputEvent(render_view_routing_id_,
1177                                                 guest_instance_id_,
1178                                                 plugin_rect_,
1179                                                 &event));
1180   return true;
1181 }
1182
1183 }  // namespace content