Home | History | Annotate | Download | only in browser_plugin
      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/browser/browser_plugin/browser_plugin_embedder.h"
      6 
      7 #include "base/values.h"
      8 #include "content/browser/browser_plugin/browser_plugin_guest.h"
      9 #include "content/browser/browser_plugin/browser_plugin_guest_manager.h"
     10 #include "content/browser/browser_plugin/browser_plugin_host_factory.h"
     11 #include "content/browser/renderer_host/render_view_host_impl.h"
     12 #include "content/browser/web_contents/web_contents_impl.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/drag_messages.h"
     16 #include "content/common/gpu/gpu_messages.h"
     17 #include "content/public/browser/browser_context.h"
     18 #include "content/public/browser/content_browser_client.h"
     19 #include "content/public/browser/native_web_keyboard_event.h"
     20 #include "content/public/browser/render_view_host.h"
     21 #include "content/public/browser/user_metrics.h"
     22 #include "content/public/common/content_switches.h"
     23 #include "content/public/common/result_codes.h"
     24 #include "content/public/common/url_constants.h"
     25 #include "net/base/escape.h"
     26 #include "ui/events/keycodes/keyboard_codes.h"
     27 
     28 namespace content {
     29 
     30 // static
     31 BrowserPluginHostFactory* BrowserPluginEmbedder::factory_ = NULL;
     32 
     33 BrowserPluginEmbedder::BrowserPluginEmbedder(WebContentsImpl* web_contents)
     34     : WebContentsObserver(web_contents),
     35       next_get_render_view_request_id_(0) {
     36 }
     37 
     38 BrowserPluginEmbedder::~BrowserPluginEmbedder() {
     39   CleanUp();
     40 }
     41 
     42 // static
     43 BrowserPluginEmbedder* BrowserPluginEmbedder::Create(
     44     WebContentsImpl* web_contents) {
     45   if (factory_)
     46     return factory_->CreateBrowserPluginEmbedder(web_contents);
     47   return new BrowserPluginEmbedder(web_contents);
     48 }
     49 
     50 void BrowserPluginEmbedder::DragEnteredGuest(BrowserPluginGuest* guest) {
     51   guest_dragging_over_ = guest->AsWeakPtr();
     52 }
     53 
     54 void BrowserPluginEmbedder::DragLeftGuest(BrowserPluginGuest* guest) {
     55   // Avoid race conditions in switching between guests being hovered over by
     56   // only un-setting if the caller is marked as the guest being dragged over.
     57   if (guest_dragging_over_.get() == guest) {
     58     guest_dragging_over_.reset();
     59   }
     60 }
     61 
     62 void BrowserPluginEmbedder::StartDrag(BrowserPluginGuest* guest) {
     63   guest_started_drag_ = guest->AsWeakPtr();
     64 }
     65 
     66 void BrowserPluginEmbedder::StopDrag(BrowserPluginGuest* guest) {
     67   if (guest_started_drag_.get() == guest) {
     68     guest_started_drag_.reset();
     69   }
     70 }
     71 
     72 void BrowserPluginEmbedder::GetRenderViewHostAtPosition(
     73     int x, int y, const WebContents::GetRenderViewHostCallback& callback) {
     74   // Store the callback so we can call it later when we have the response.
     75   pending_get_render_view_callbacks_.insert(
     76       std::make_pair(next_get_render_view_request_id_, callback));
     77   Send(new BrowserPluginMsg_PluginAtPositionRequest(
     78       routing_id(),
     79       next_get_render_view_request_id_,
     80       gfx::Point(x, y)));
     81   ++next_get_render_view_request_id_;
     82 }
     83 
     84 bool BrowserPluginEmbedder::DidSendScreenRectsCallback(
     85    BrowserPluginGuest* guest) {
     86   static_cast<RenderViewHostImpl*>(
     87       guest->GetWebContents()->GetRenderViewHost())->SendScreenRects();
     88   // Not handled => Iterate over all guests.
     89   return false;
     90 }
     91 
     92 void BrowserPluginEmbedder::DidSendScreenRects() {
     93   WebContentsImpl* embedder =
     94       static_cast<WebContentsImpl*>(web_contents());
     95   GetBrowserPluginGuestManager()->ForEachGuest(embedder, base::Bind(
     96       &BrowserPluginEmbedder::DidSendScreenRectsCallback,
     97       base::Unretained(this)));
     98 }
     99 
    100 bool BrowserPluginEmbedder::UnlockMouseIfNecessaryCallback(
    101     const NativeWebKeyboardEvent& event,
    102     BrowserPluginGuest* guest) {
    103   return guest->UnlockMouseIfNecessary(event);
    104 }
    105 
    106 bool BrowserPluginEmbedder::HandleKeyboardEvent(
    107     const NativeWebKeyboardEvent& event) {
    108   if ((event.type != blink::WebInputEvent::RawKeyDown) ||
    109       (event.windowsKeyCode != ui::VKEY_ESCAPE) ||
    110       (event.modifiers & blink::WebInputEvent::InputModifiers)) {
    111     return false;
    112   }
    113 
    114   WebContentsImpl* embedder =
    115       static_cast<WebContentsImpl*>(web_contents());
    116   return GetBrowserPluginGuestManager()->ForEachGuest(embedder, base::Bind(
    117       &BrowserPluginEmbedder::UnlockMouseIfNecessaryCallback,
    118       base::Unretained(this),
    119       event));
    120 }
    121 
    122 bool BrowserPluginEmbedder::SetZoomLevelCallback(
    123     double level, BrowserPluginGuest* guest) {
    124   guest->GetWebContents()->SetZoomLevel(level);
    125   // Not handled => Iterate over all guests.
    126   return false;
    127 }
    128 
    129 void BrowserPluginEmbedder::SetZoomLevel(double level) {
    130   WebContentsImpl* embedder =
    131       static_cast<WebContentsImpl*>(web_contents());
    132   GetBrowserPluginGuestManager()->ForEachGuest(embedder, base::Bind(
    133       &BrowserPluginEmbedder::SetZoomLevelCallback,
    134       base::Unretained(this),
    135       level));
    136 }
    137 
    138 void BrowserPluginEmbedder::RenderProcessGone(base::TerminationStatus status) {
    139   CleanUp();
    140 }
    141 
    142 bool BrowserPluginEmbedder::OnMessageReceived(const IPC::Message& message) {
    143   bool handled = true;
    144   IPC_BEGIN_MESSAGE_MAP(BrowserPluginEmbedder, message)
    145     IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_AllocateInstanceID,
    146                         OnAllocateInstanceID)
    147     IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_Attach, OnAttach)
    148     IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_PluginAtPositionResponse,
    149                         OnPluginAtPositionResponse)
    150     IPC_MESSAGE_HANDLER_GENERIC(DragHostMsg_UpdateDragCursor,
    151                                 OnUpdateDragCursor(&handled));
    152     IPC_MESSAGE_UNHANDLED(handled = false)
    153   IPC_END_MESSAGE_MAP()
    154   return handled;
    155 }
    156 
    157 void BrowserPluginEmbedder::DragSourceEndedAt(int client_x, int client_y,
    158     int screen_x, int screen_y, blink::WebDragOperation operation) {
    159   if (guest_started_drag_.get()) {
    160     gfx::Point guest_offset =
    161         guest_started_drag_->GetScreenCoordinates(gfx::Point());
    162     guest_started_drag_->DragSourceEndedAt(client_x - guest_offset.x(),
    163         client_y - guest_offset.y(), screen_x, screen_y, operation);
    164   }
    165 }
    166 
    167 void BrowserPluginEmbedder::DragSourceMovedTo(int client_x, int client_y,
    168                                               int screen_x, int screen_y) {
    169   if (guest_started_drag_.get()) {
    170     gfx::Point guest_offset =
    171         guest_started_drag_->GetScreenCoordinates(gfx::Point());
    172     guest_started_drag_->DragSourceMovedTo(client_x - guest_offset.x(),
    173         client_y - guest_offset.y(), screen_x, screen_y);
    174   }
    175 }
    176 
    177 void BrowserPluginEmbedder::SystemDragEnded() {
    178   if (guest_started_drag_.get() &&
    179       (guest_started_drag_.get() != guest_dragging_over_.get()))
    180     guest_started_drag_->EndSystemDrag();
    181   guest_started_drag_.reset();
    182   guest_dragging_over_.reset();
    183 }
    184 
    185 void BrowserPluginEmbedder::OnUpdateDragCursor(bool* handled) {
    186   *handled = (guest_dragging_over_.get() != NULL);
    187 }
    188 
    189 void BrowserPluginEmbedder::CleanUp() {
    190   // CleanUp gets called when BrowserPluginEmbedder's WebContents goes away
    191   // or the associated RenderViewHost is destroyed or swapped out. Therefore we
    192   // don't need to care about the pending callbacks anymore.
    193   pending_get_render_view_callbacks_.clear();
    194 }
    195 
    196 BrowserPluginGuestManager*
    197     BrowserPluginEmbedder::GetBrowserPluginGuestManager() {
    198   BrowserPluginGuestManager* guest_manager = static_cast<WebContentsImpl*>(
    199       web_contents())->GetBrowserPluginGuestManager();
    200   if (!guest_manager) {
    201     guest_manager = BrowserPluginGuestManager::Create();
    202     web_contents()->GetBrowserContext()->SetUserData(
    203         browser_plugin::kBrowserPluginGuestManagerKeyName, guest_manager);
    204   }
    205   return guest_manager;
    206 }
    207 
    208 void BrowserPluginEmbedder::OnAllocateInstanceID(int request_id) {
    209   int instance_id = GetBrowserPluginGuestManager()->get_next_instance_id();
    210   Send(new BrowserPluginMsg_AllocateInstanceID_ACK(
    211       routing_id(), request_id, instance_id));
    212 }
    213 
    214 void BrowserPluginEmbedder::OnAttach(
    215     int instance_id,
    216     const BrowserPluginHostMsg_Attach_Params& params,
    217     const base::DictionaryValue& extra_params) {
    218   if (!GetBrowserPluginGuestManager()->CanEmbedderAccessInstanceIDMaybeKill(
    219           web_contents()->GetRenderProcessHost()->GetID(), instance_id))
    220     return;
    221 
    222   BrowserPluginGuest* guest =
    223       GetBrowserPluginGuestManager()->GetGuestByInstanceID(
    224           instance_id, web_contents()->GetRenderProcessHost()->GetID());
    225 
    226   if (guest) {
    227     // There is an implicit order expectation here:
    228     // 1. The content embedder is made aware of the attachment.
    229     // 2. BrowserPluginGuest::Attach is called.
    230     // 3. The content embedder issues queued events if any that happened
    231     //    prior to attachment.
    232     GetContentClient()->browser()->GuestWebContentsAttached(
    233         guest->GetWebContents(),
    234         web_contents(),
    235         extra_params);
    236     guest->Attach(
    237         static_cast<WebContentsImpl*>(web_contents()), params, extra_params);
    238     return;
    239   }
    240 
    241   scoped_ptr<base::DictionaryValue> copy_extra_params(extra_params.DeepCopy());
    242   guest = GetBrowserPluginGuestManager()->CreateGuest(
    243       web_contents()->GetSiteInstance(),
    244       instance_id, params,
    245       copy_extra_params.Pass());
    246   if (guest) {
    247     GetContentClient()->browser()->GuestWebContentsAttached(
    248         guest->GetWebContents(),
    249         web_contents(),
    250         extra_params);
    251     guest->Initialize(params, static_cast<WebContentsImpl*>(web_contents()));
    252   }
    253 }
    254 
    255 void BrowserPluginEmbedder::OnPluginAtPositionResponse(
    256     int instance_id, int request_id, const gfx::Point& position) {
    257   const std::map<int, WebContents::GetRenderViewHostCallback>::iterator
    258       callback_iter = pending_get_render_view_callbacks_.find(request_id);
    259   if (callback_iter == pending_get_render_view_callbacks_.end())
    260     return;
    261 
    262   RenderViewHost* render_view_host;
    263   BrowserPluginGuest* guest = NULL;
    264   if (instance_id != browser_plugin::kInstanceIDNone) {
    265     guest = GetBrowserPluginGuestManager()->GetGuestByInstanceID(
    266                 instance_id, web_contents()->GetRenderProcessHost()->GetID());
    267   }
    268 
    269   if (guest)
    270     render_view_host = guest->GetWebContents()->GetRenderViewHost();
    271   else  // No plugin, use embedder's RenderViewHost.
    272     render_view_host = web_contents()->GetRenderViewHost();
    273 
    274   callback_iter->second.Run(render_view_host, position.x(), position.y());
    275   pending_get_render_view_callbacks_.erase(callback_iter);
    276 }
    277 
    278 }  // namespace content
    279