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/renderer_host/render_view_host_impl.h"
10#include "content/browser/web_contents/web_contents_impl.h"
11#include "content/common/browser_plugin/browser_plugin_constants.h"
12#include "content/common/browser_plugin/browser_plugin_messages.h"
13#include "content/common/drag_messages.h"
14#include "content/common/gpu/gpu_messages.h"
15#include "content/public/browser/browser_context.h"
16#include "content/public/browser/browser_plugin_guest_manager.h"
17#include "content/public/browser/content_browser_client.h"
18#include "content/public/browser/native_web_keyboard_event.h"
19#include "content/public/browser/render_view_host.h"
20#include "content/public/browser/user_metrics.h"
21#include "content/public/common/content_switches.h"
22#include "content/public/common/result_codes.h"
23#include "content/public/common/url_constants.h"
24#include "net/base/escape.h"
25#include "ui/events/keycodes/keyboard_codes.h"
26
27namespace content {
28
29BrowserPluginEmbedder::BrowserPluginEmbedder(WebContentsImpl* web_contents)
30    : WebContentsObserver(web_contents),
31      guest_drag_ending_(false),
32      weak_ptr_factory_(this) {
33}
34
35BrowserPluginEmbedder::~BrowserPluginEmbedder() {
36}
37
38// static
39BrowserPluginEmbedder* BrowserPluginEmbedder::Create(
40    WebContentsImpl* web_contents) {
41  return new BrowserPluginEmbedder(web_contents);
42}
43
44void BrowserPluginEmbedder::DragEnteredGuest(BrowserPluginGuest* guest) {
45  guest_dragging_over_ = guest->AsWeakPtr();
46}
47
48void BrowserPluginEmbedder::DragLeftGuest(BrowserPluginGuest* guest) {
49  // Avoid race conditions in switching between guests being hovered over by
50  // only un-setting if the caller is marked as the guest being dragged over.
51  if (guest_dragging_over_.get() == guest) {
52    guest_dragging_over_.reset();
53  }
54}
55
56void BrowserPluginEmbedder::StartDrag(BrowserPluginGuest* guest) {
57  guest_started_drag_ = guest->AsWeakPtr();
58  guest_drag_ending_ = false;
59}
60
61WebContentsImpl* BrowserPluginEmbedder::GetWebContents() const {
62  return static_cast<WebContentsImpl*>(web_contents());
63}
64
65BrowserPluginGuestManager*
66BrowserPluginEmbedder::GetBrowserPluginGuestManager() const {
67  return GetWebContents()->GetBrowserContext()->GetGuestManager();
68}
69
70void BrowserPluginEmbedder::ClearGuestDragStateIfApplicable() {
71  // The order at which we observe SystemDragEnded() and DragSourceEndedAt() is
72  // platform dependent.
73  // In OSX, we see SystemDragEnded() first, where in aura, we see
74  // DragSourceEndedAt() first. For this reason, we check if both methods were
75  // called before resetting |guest_started_drag_|.
76  if (guest_drag_ending_) {
77    if (guest_started_drag_)
78      guest_started_drag_.reset();
79  } else {
80    guest_drag_ending_ = true;
81  }
82}
83
84bool BrowserPluginEmbedder::DidSendScreenRectsCallback(
85   WebContents* guest_web_contents) {
86  static_cast<RenderViewHostImpl*>(
87      guest_web_contents->GetRenderViewHost())->SendScreenRects();
88  // Not handled => Iterate over all guests.
89  return false;
90}
91
92void BrowserPluginEmbedder::DidSendScreenRects() {
93  GetBrowserPluginGuestManager()->ForEachGuest(
94          GetWebContents(), base::Bind(
95              &BrowserPluginEmbedder::DidSendScreenRectsCallback,
96              base::Unretained(this)));
97}
98
99bool BrowserPluginEmbedder::OnMessageReceived(const IPC::Message& message) {
100  bool handled = true;
101  IPC_BEGIN_MESSAGE_MAP(BrowserPluginEmbedder, message)
102    IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_Attach, OnAttach)
103    IPC_MESSAGE_HANDLER_GENERIC(DragHostMsg_UpdateDragCursor,
104                                OnUpdateDragCursor(&handled));
105    IPC_MESSAGE_UNHANDLED(handled = false)
106  IPC_END_MESSAGE_MAP()
107  return handled;
108}
109
110void BrowserPluginEmbedder::DragSourceEndedAt(int client_x, int client_y,
111    int screen_x, int screen_y, blink::WebDragOperation operation) {
112  if (guest_started_drag_) {
113    gfx::Point guest_offset =
114        guest_started_drag_->GetScreenCoordinates(gfx::Point());
115    guest_started_drag_->DragSourceEndedAt(client_x - guest_offset.x(),
116        client_y - guest_offset.y(), screen_x, screen_y, operation);
117  }
118  ClearGuestDragStateIfApplicable();
119}
120
121void BrowserPluginEmbedder::SystemDragEnded() {
122  // When the embedder's drag/drop operation ends, we need to pass the message
123  // to the guest that initiated the drag/drop operation. This will ensure that
124  // the guest's RVH state is reset properly.
125  if (guest_started_drag_)
126    guest_started_drag_->EndSystemDrag();
127  guest_dragging_over_.reset();
128  ClearGuestDragStateIfApplicable();
129}
130
131void BrowserPluginEmbedder::OnUpdateDragCursor(bool* handled) {
132  *handled = (guest_dragging_over_.get() != NULL);
133}
134
135void BrowserPluginEmbedder::OnAttach(
136    int browser_plugin_instance_id,
137    const BrowserPluginHostMsg_Attach_Params& params) {
138  WebContents* guest_web_contents =
139      GetBrowserPluginGuestManager()->GetGuestByInstanceID(
140          GetWebContents(), browser_plugin_instance_id);
141  if (!guest_web_contents)
142    return;
143  BrowserPluginGuest* guest = static_cast<WebContentsImpl*>(guest_web_contents)
144                                  ->GetBrowserPluginGuest();
145  guest->Attach(browser_plugin_instance_id, GetWebContents(), params);
146}
147
148bool BrowserPluginEmbedder::HandleKeyboardEvent(
149    const NativeWebKeyboardEvent& event) {
150  if ((event.windowsKeyCode != ui::VKEY_ESCAPE) ||
151      (event.modifiers & blink::WebInputEvent::InputModifiers)) {
152    return false;
153  }
154
155  bool event_consumed = false;
156  GetBrowserPluginGuestManager()->ForEachGuest(
157      GetWebContents(),
158      base::Bind(&BrowserPluginEmbedder::UnlockMouseIfNecessaryCallback,
159                 base::Unretained(this),
160                 &event_consumed));
161
162  return event_consumed;
163}
164
165bool BrowserPluginEmbedder::UnlockMouseIfNecessaryCallback(bool* mouse_unlocked,
166                                                           WebContents* guest) {
167  *mouse_unlocked |= static_cast<WebContentsImpl*>(guest)
168                         ->GetBrowserPluginGuest()
169                         ->mouse_locked();
170  guest->GotResponseToLockMouseRequest(false);
171
172  // Returns false to iterate over all guests.
173  return false;
174}
175
176}  // namespace content
177