1// Copyright 2013 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 "components/plugins/renderer/webview_plugin.h"
6
7#include "base/message_loop/message_loop.h"
8#include "base/metrics/histogram.h"
9#include "base/numerics/safe_conversions.h"
10#include "content/public/common/web_preferences.h"
11#include "content/public/renderer/render_view.h"
12#include "skia/ext/platform_canvas.h"
13#include "third_party/WebKit/public/platform/WebSize.h"
14#include "third_party/WebKit/public/platform/WebURL.h"
15#include "third_party/WebKit/public/platform/WebURLRequest.h"
16#include "third_party/WebKit/public/platform/WebURLResponse.h"
17#include "third_party/WebKit/public/web/WebDocument.h"
18#include "third_party/WebKit/public/web/WebElement.h"
19#include "third_party/WebKit/public/web/WebInputEvent.h"
20#include "third_party/WebKit/public/web/WebLocalFrame.h"
21#include "third_party/WebKit/public/web/WebPluginContainer.h"
22#include "third_party/WebKit/public/web/WebView.h"
23
24using blink::WebCanvas;
25using blink::WebCursorInfo;
26using blink::WebDragData;
27using blink::WebDragOperationsMask;
28using blink::WebImage;
29using blink::WebInputEvent;
30using blink::WebLocalFrame;
31using blink::WebMouseEvent;
32using blink::WebPlugin;
33using blink::WebPluginContainer;
34using blink::WebPoint;
35using blink::WebRect;
36using blink::WebSize;
37using blink::WebString;
38using blink::WebURLError;
39using blink::WebURLRequest;
40using blink::WebURLResponse;
41using blink::WebVector;
42using blink::WebView;
43using content::WebPreferences;
44
45WebViewPlugin::WebViewPlugin(WebViewPlugin::Delegate* delegate,
46                             const WebPreferences& preferences)
47    : delegate_(delegate),
48      container_(NULL),
49      web_view_(WebView::create(this)),
50      finished_loading_(false),
51      focused_(false) {
52  // ApplyWebPreferences before making a WebLocalFrame so that the frame sees a
53  // consistent view of our preferences.
54  content::RenderView::ApplyWebPreferences(preferences, web_view_);
55  web_frame_ = WebLocalFrame::create(this);
56  web_view_->setMainFrame(web_frame_);
57}
58
59// static
60WebViewPlugin* WebViewPlugin::Create(WebViewPlugin::Delegate* delegate,
61                                     const WebPreferences& preferences,
62                                     const std::string& html_data,
63                                     const GURL& url) {
64  WebViewPlugin* plugin = new WebViewPlugin(delegate, preferences);
65  plugin->web_view()->mainFrame()->loadHTMLString(html_data, url);
66  return plugin;
67}
68
69WebViewPlugin::~WebViewPlugin() {
70  web_view_->close();
71  web_frame_->close();
72}
73
74void WebViewPlugin::ReplayReceivedData(WebPlugin* plugin) {
75  if (!response_.isNull()) {
76    plugin->didReceiveResponse(response_);
77    size_t total_bytes = 0;
78    for (std::list<std::string>::iterator it = data_.begin(); it != data_.end();
79         ++it) {
80      plugin->didReceiveData(
81          it->c_str(), base::checked_cast<int, size_t>(it->length()));
82      total_bytes += it->length();
83    }
84    UMA_HISTOGRAM_MEMORY_KB(
85        "PluginDocument.Memory",
86        (base::checked_cast<int, size_t>(total_bytes / 1024)));
87    UMA_HISTOGRAM_COUNTS(
88        "PluginDocument.NumChunks",
89        (base::checked_cast<int, size_t>(data_.size())));
90  }
91  // We need to transfer the |focused_| to new plugin after it loaded.
92  if (focused_) {
93    plugin->updateFocus(true);
94  }
95  if (finished_loading_) {
96    plugin->didFinishLoading();
97  }
98  if (error_) {
99    plugin->didFailLoading(*error_);
100  }
101}
102
103void WebViewPlugin::RestoreTitleText() {
104  if (container_)
105    container_->element().setAttribute("title", old_title_);
106}
107
108WebPluginContainer* WebViewPlugin::container() const { return container_; }
109
110bool WebViewPlugin::initialize(WebPluginContainer* container) {
111  container_ = container;
112  if (container_)
113    old_title_ = container_->element().getAttribute("title");
114  return true;
115}
116
117void WebViewPlugin::destroy() {
118  if (delegate_) {
119    delegate_->PluginDestroyed();
120    delegate_ = NULL;
121  }
122  container_ = NULL;
123  base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
124}
125
126NPObject* WebViewPlugin::scriptableObject() { return NULL; }
127
128struct _NPP* WebViewPlugin::pluginNPP() { return NULL; }
129
130bool WebViewPlugin::getFormValue(WebString& value) { return false; }
131
132void WebViewPlugin::paint(WebCanvas* canvas, const WebRect& rect) {
133  gfx::Rect paint_rect = gfx::IntersectRects(rect_, rect);
134  if (paint_rect.IsEmpty())
135    return;
136
137  paint_rect.Offset(-rect_.x(), -rect_.y());
138
139  canvas->translate(SkIntToScalar(rect_.x()), SkIntToScalar(rect_.y()));
140  canvas->save();
141
142  web_view_->layout();
143  web_view_->paint(canvas, paint_rect);
144
145  canvas->restore();
146}
147
148// Coordinates are relative to the containing window.
149void WebViewPlugin::updateGeometry(const WebRect& frame_rect,
150                                   const WebRect& clip_rect,
151                                   const WebVector<WebRect>& cut_out_rects,
152                                   bool is_visible) {
153  if (static_cast<gfx::Rect>(frame_rect) != rect_) {
154    rect_ = frame_rect;
155    WebSize newSize(frame_rect.width, frame_rect.height);
156    web_view_->resize(newSize);
157  }
158}
159
160void WebViewPlugin::updateFocus(bool focused) {
161  focused_ = focused;
162}
163
164bool WebViewPlugin::acceptsInputEvents() { return true; }
165
166bool WebViewPlugin::handleInputEvent(const WebInputEvent& event,
167                                     WebCursorInfo& cursor) {
168  // For tap events, don't handle them. They will be converted to
169  // mouse events later and passed to here.
170  if (event.type == WebInputEvent::GestureTap)
171    return false;
172
173  if (event.type == WebInputEvent::ContextMenu) {
174    if (delegate_) {
175      const WebMouseEvent& mouse_event =
176          reinterpret_cast<const WebMouseEvent&>(event);
177      delegate_->ShowContextMenu(mouse_event);
178    }
179    return true;
180  }
181  current_cursor_ = cursor;
182  bool handled = web_view_->handleInputEvent(event);
183  cursor = current_cursor_;
184
185  return handled;
186}
187
188void WebViewPlugin::didReceiveResponse(const WebURLResponse& response) {
189  DCHECK(response_.isNull());
190  response_ = response;
191}
192
193void WebViewPlugin::didReceiveData(const char* data, int data_length) {
194  data_.push_back(std::string(data, data_length));
195}
196
197void WebViewPlugin::didFinishLoading() {
198  DCHECK(!finished_loading_);
199  finished_loading_ = true;
200}
201
202void WebViewPlugin::didFailLoading(const WebURLError& error) {
203  DCHECK(!error_.get());
204  error_.reset(new WebURLError(error));
205}
206
207bool WebViewPlugin::acceptsLoadDrops() { return false; }
208
209void WebViewPlugin::setToolTipText(const WebString& text,
210                                   blink::WebTextDirection hint) {
211  if (container_)
212    container_->element().setAttribute("title", text);
213}
214
215void WebViewPlugin::startDragging(WebLocalFrame*,
216                                  const WebDragData&,
217                                  WebDragOperationsMask,
218                                  const WebImage&,
219                                  const WebPoint&) {
220  // Immediately stop dragging.
221  web_view_->dragSourceSystemDragEnded();
222}
223
224bool WebViewPlugin::allowsBrokenNullLayerTreeView() const {
225  return true;
226}
227
228void WebViewPlugin::didInvalidateRect(const WebRect& rect) {
229  if (container_)
230    container_->invalidateRect(rect);
231}
232
233void WebViewPlugin::didChangeCursor(const WebCursorInfo& cursor) {
234  current_cursor_ = cursor;
235}
236
237void WebViewPlugin::scheduleAnimation() {
238  if (container_)
239    container_->invalidate();
240}
241
242void WebViewPlugin::didClearWindowObject(WebLocalFrame* frame) {
243  if (delegate_)
244    delegate_->BindWebFrame(frame);
245}
246
247void WebViewPlugin::didReceiveResponse(WebLocalFrame* frame,
248                                       unsigned identifier,
249                                       const WebURLResponse& response) {
250  WebFrameClient::didReceiveResponse(frame, identifier, response);
251}
252