blob: ae71c44964004616a6fb57a80a04ed5d20c68f5c [file] [log] [blame]
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001// 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
Ben Murdochbb1529c2013-08-08 10:24:53 +01005#include "content/renderer/npapi/webplugin_impl.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +00006
7#include "base/bind.h"
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +01008#include "base/debug/crash_logging.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +00009#include "base/logging.h"
10#include "base/memory/linked_ptr.h"
Ben Murdoch9ab55632013-07-18 11:57:30 +010011#include "base/message_loop/message_loop.h"
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +010012#include "base/strings/string_util.h"
13#include "base/strings/stringprintf.h"
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010014#include "base/strings/utf_string_conversions.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000015#include "cc/layers/io_surface_layer.h"
Ben Murdochca12bfa2013-07-23 11:17:05 +010016#include "content/child/npapi/plugin_host.h"
17#include "content/child/npapi/plugin_instance.h"
18#include "content/child/npapi/webplugin_delegate_impl.h"
19#include "content/common/view_messages.h"
20#include "content/public/renderer/content_renderer_client.h"
Ben Murdochbb1529c2013-08-08 10:24:53 +010021#include "content/renderer/npapi/webplugin_delegate_proxy.h"
Ben Murdochca12bfa2013-07-23 11:17:05 +010022#include "content/renderer/render_process.h"
23#include "content/renderer/render_view_impl.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000024#include "net/base/escape.h"
25#include "net/base/net_errors.h"
26#include "net/http/http_response_headers.h"
27#include "skia/ext/platform_canvas.h"
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010028#include "third_party/WebKit/public/platform/WebCString.h"
29#include "third_party/WebKit/public/platform/WebCookieJar.h"
30#include "third_party/WebKit/public/platform/WebData.h"
31#include "third_party/WebKit/public/platform/WebHTTPBody.h"
32#include "third_party/WebKit/public/platform/WebHTTPHeaderVisitor.h"
33#include "third_party/WebKit/public/platform/WebURL.h"
34#include "third_party/WebKit/public/platform/WebURLError.h"
35#include "third_party/WebKit/public/platform/WebURLLoader.h"
36#include "third_party/WebKit/public/platform/WebURLLoaderClient.h"
37#include "third_party/WebKit/public/platform/WebURLResponse.h"
Ben Murdocheb525c52013-07-10 11:40:50 +010038#include "third_party/WebKit/public/web/WebConsoleMessage.h"
39#include "third_party/WebKit/public/web/WebCursorInfo.h"
40#include "third_party/WebKit/public/web/WebDocument.h"
41#include "third_party/WebKit/public/web/WebFrame.h"
42#include "third_party/WebKit/public/web/WebInputEvent.h"
43#include "third_party/WebKit/public/web/WebKit.h"
44#include "third_party/WebKit/public/web/WebPluginContainer.h"
45#include "third_party/WebKit/public/web/WebPluginParams.h"
46#include "third_party/WebKit/public/web/WebURLLoaderOptions.h"
47#include "third_party/WebKit/public/web/WebView.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000048#include "ui/gfx/rect.h"
Ben Murdocheb525c52013-07-10 11:40:50 +010049#include "url/gurl.h"
50#include "url/url_util.h"
Ben Murdochca12bfa2013-07-23 11:17:05 +010051#include "webkit/child/multipart_response_delegate.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000052#include "webkit/plugins/plugin_constants.h"
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010053#include "webkit/renderer/appcache/web_application_cache_host_impl.h"
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +010054#include "webkit/renderer/compositor_bindings/web_layer_impl.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000055
56using appcache::WebApplicationCacheHostImpl;
57using WebKit::WebCanvas;
58using WebKit::WebConsoleMessage;
59using WebKit::WebCookieJar;
60using WebKit::WebCString;
61using WebKit::WebCursorInfo;
62using WebKit::WebData;
63using WebKit::WebDataSource;
64using WebKit::WebFrame;
65using WebKit::WebHTTPBody;
66using WebKit::WebHTTPHeaderVisitor;
67using WebKit::WebInputEvent;
68using WebKit::WebKeyboardEvent;
69using WebKit::WebMouseEvent;
70using WebKit::WebPluginContainer;
71using WebKit::WebPluginParams;
72using WebKit::WebRect;
73using WebKit::WebString;
74using WebKit::WebURL;
75using WebKit::WebURLError;
76using WebKit::WebURLLoader;
77using WebKit::WebURLLoaderClient;
78using WebKit::WebURLLoaderOptions;
79using WebKit::WebURLRequest;
80using WebKit::WebURLResponse;
81using WebKit::WebVector;
82using WebKit::WebView;
83using webkit_glue::MultipartResponseDelegate;
84
Ben Murdochca12bfa2013-07-23 11:17:05 +010085namespace content {
Torne (Richard Coles)58218062012-11-14 11:43:16 +000086
87namespace {
88
Torne (Richard Coles)58218062012-11-14 11:43:16 +000089// This class handles individual multipart responses. It is instantiated when
90// we receive HTTP status code 206 in the HTTP response. This indicates
91// that the response could have multiple parts each separated by a boundary
92// specified in the response header.
93class MultiPartResponseClient : public WebURLLoaderClient {
94 public:
95 explicit MultiPartResponseClient(WebPluginResourceClient* resource_client)
96 : resource_client_(resource_client) {
97 Clear();
98 }
99
100 virtual void willSendRequest(
101 WebURLLoader*, WebURLRequest&, const WebURLResponse&) {}
102 virtual void didSendData(
103 WebURLLoader*, unsigned long long, unsigned long long) {}
104
105 // Called when the multipart parser encounters an embedded multipart
106 // response.
107 virtual void didReceiveResponse(
108 WebURLLoader*, const WebURLResponse& response) {
109 int64 instance_size;
110 if (!MultipartResponseDelegate::ReadContentRanges(
111 response,
112 &byte_range_lower_bound_,
113 &byte_range_upper_bound_,
114 &instance_size)) {
115 NOTREACHED();
116 return;
117 }
118
119 resource_response_ = response;
120 }
121
122 // Receives individual part data from a multipart response.
123 virtual void didReceiveData(WebURLLoader*,
124 const char* data,
125 int data_length,
126 int encoded_data_length) {
127 // TODO(ananta)
128 // We should defer further loads on multipart resources on the same lines
129 // as regular resources requested by plugins to prevent reentrancy.
130 resource_client_->DidReceiveData(
131 data, data_length, byte_range_lower_bound_);
132 byte_range_lower_bound_ += data_length;
133 }
134
135 virtual void didFinishLoading(WebURLLoader*, double finishTime) {}
136 virtual void didFail(WebURLLoader*, const WebURLError&) {}
137
138 void Clear() {
139 resource_response_.reset();
140 byte_range_lower_bound_ = 0;
141 byte_range_upper_bound_ = 0;
142 }
143
144 private:
145 WebURLResponse resource_response_;
146 // The lower bound of the byte range.
147 int64 byte_range_lower_bound_;
148 // The upper bound of the byte range.
149 int64 byte_range_upper_bound_;
150 // The handler for the data.
151 WebPluginResourceClient* resource_client_;
152};
153
154class HeaderFlattener : public WebHTTPHeaderVisitor {
155 public:
156 explicit HeaderFlattener(std::string* buf) : buf_(buf) {
157 }
158
159 virtual void visitHeader(const WebString& name, const WebString& value) {
160 // TODO(darin): Should we really exclude headers with an empty value?
161 if (!name.isEmpty() && !value.isEmpty()) {
162 buf_->append(name.utf8());
163 buf_->append(": ");
164 buf_->append(value.utf8());
165 buf_->append("\n");
166 }
167 }
168
169 private:
170 std::string* buf_;
171};
172
173std::string GetAllHeaders(const WebURLResponse& response) {
174 // TODO(darin): It is possible for httpStatusText to be empty and still have
175 // an interesting response, so this check seems wrong.
176 std::string result;
177 const WebString& status = response.httpStatusText();
178 if (status.isEmpty())
179 return result;
180
181 // TODO(darin): Shouldn't we also report HTTP version numbers?
182 result = base::StringPrintf("HTTP %d ", response.httpStatusCode());
183 result.append(status.utf8());
184 result.append("\n");
185
186 HeaderFlattener flattener(&result);
187 response.visitHTTPHeaderFields(&flattener);
188
189 return result;
190}
191
192struct ResponseInfo {
193 GURL url;
194 std::string mime_type;
195 uint32 last_modified;
196 uint32 expected_length;
197};
198
199void GetResponseInfo(const WebURLResponse& response,
200 ResponseInfo* response_info) {
201 response_info->url = response.url();
202 response_info->mime_type = response.mimeType().utf8();
203
204 // Measured in seconds since 12:00 midnight GMT, January 1, 1970.
205 response_info->last_modified =
206 static_cast<uint32>(response.lastModifiedDate());
207
208 // If the length comes in as -1, then it indicates that it was not
209 // read off the HTTP headers. We replicate Safari webkit behavior here,
210 // which is to set it to 0.
211 response_info->expected_length =
212 static_cast<uint32>(std::max(response.expectedContentLength(), 0LL));
213
214 WebString content_encoding =
215 response.httpHeaderField(WebString::fromUTF8("Content-Encoding"));
216 if (!content_encoding.isNull() &&
217 !EqualsASCII(content_encoding, "identity")) {
218 // Don't send the compressed content length to the plugin, which only
219 // cares about the decoded length.
220 response_info->expected_length = 0;
221 }
222}
223
224} // namespace
225
226// WebKit::WebPlugin ----------------------------------------------------------
227
228struct WebPluginImpl::ClientInfo {
229 unsigned long id;
230 WebPluginResourceClient* client;
231 WebKit::WebURLRequest request;
232 bool pending_failure_notification;
233 linked_ptr<WebKit::WebURLLoader> loader;
234 bool notify_redirects;
235 bool is_plugin_src_load;
Ben Murdocheb525c52013-07-10 11:40:50 +0100236 int64 data_offset;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000237};
238
239bool WebPluginImpl::initialize(WebPluginContainer* container) {
Ben Murdochca12bfa2013-07-23 11:17:05 +0100240 if (!render_view_.get()) {
241 LOG(ERROR) << "No RenderView";
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000242 return false;
243 }
244
Ben Murdochca12bfa2013-07-23 11:17:05 +0100245 WebPluginDelegate* plugin_delegate = CreatePluginDelegate();
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000246 if (!plugin_delegate)
247 return false;
248
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100249 // Store the plugin's unique identifier, used by the container to track its
250 // script objects.
251 npp_ = plugin_delegate->GetPluginNPP();
252
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000253 // Set the container before Initialize because the plugin may
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100254 // synchronously call NPN_GetValue to get its container, or make calls
255 // passing script objects that need to be tracked, during initialization.
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000256 SetContainer(container);
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100257
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000258 bool ok = plugin_delegate->Initialize(
259 plugin_url_, arg_names_, arg_values_, this, load_manually_);
260 if (!ok) {
261 LOG(ERROR) << "Couldn't initialize plug-in";
262 plugin_delegate->PluginDestroyed();
263
264 WebKit::WebPlugin* replacement_plugin =
Ben Murdochca12bfa2013-07-23 11:17:05 +0100265 GetContentClient()->renderer()->CreatePluginReplacement(
266 render_view_.get(), file_path_);
Ben Murdocheb525c52013-07-10 11:40:50 +0100267 if (!replacement_plugin)
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000268 return false;
269
Ben Murdocheb525c52013-07-10 11:40:50 +0100270 // Disable scripting by this plugin before replacing it with the new
271 // one. This plugin also needs destroying, so use destroy(), which will
272 // implicitly disable scripting while un-setting the container.
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000273 destroy();
Ben Murdocheb525c52013-07-10 11:40:50 +0100274
275 // Inform the container of the replacement plugin, then initialize it.
276 container->setPlugin(replacement_plugin);
277 return replacement_plugin->initialize(container);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000278 }
279
280 delegate_ = plugin_delegate;
281
282 return true;
283}
284
285void WebPluginImpl::destroy() {
286 SetContainer(NULL);
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100287 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000288}
289
290NPObject* WebPluginImpl::scriptableObject() {
291 if (!delegate_)
292 return NULL;
293
294 return delegate_->GetPluginScriptableObject();
295}
296
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100297NPP WebPluginImpl::pluginNPP() {
298 return npp_;
299}
300
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000301bool WebPluginImpl::getFormValue(WebKit::WebString& value) {
302 if (!delegate_)
303 return false;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100304 base::string16 form_value;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000305 if (!delegate_->GetFormValue(&form_value))
306 return false;
307 value = form_value;
308 return true;
309}
310
311void WebPluginImpl::paint(WebCanvas* canvas, const WebRect& paint_rect) {
312 if (!delegate_ || !container_)
313 return;
314
315#if defined(OS_WIN)
316 // Force a geometry update if needed to allow plugins like media player
317 // which defer the initial geometry update to work.
318 container_->reportGeometry();
319#endif // OS_WIN
320
321 // Note that |canvas| is only used when in windowless mode.
322 delegate_->Paint(canvas, paint_rect);
323}
324
325void WebPluginImpl::updateGeometry(
326 const WebRect& window_rect, const WebRect& clip_rect,
327 const WebVector<WebRect>& cutout_rects, bool is_visible) {
328 WebPluginGeometry new_geometry;
329 new_geometry.window = window_;
330 new_geometry.window_rect = window_rect;
331 new_geometry.clip_rect = clip_rect;
332 new_geometry.visible = is_visible;
333 new_geometry.rects_valid = true;
334 for (size_t i = 0; i < cutout_rects.size(); ++i)
335 new_geometry.cutout_rects.push_back(cutout_rects[i]);
336
337 // Only send DidMovePlugin if the geometry changed in some way.
Ben Murdochca12bfa2013-07-23 11:17:05 +0100338 if (window_ && render_view_.get() &&
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000339 (first_geometry_update_ || !new_geometry.Equals(geometry_))) {
Ben Murdochca12bfa2013-07-23 11:17:05 +0100340 render_view_->SchedulePluginMove(new_geometry);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000341 // We invalidate windowed plugins during the first geometry update to
342 // ensure that they get reparented to the wrapper window in the browser.
343 // This ensures that they become visible and are painted by the OS. This is
344 // required as some pages don't invalidate when the plugin is added.
345 if (first_geometry_update_ && window_) {
346 InvalidateRect(window_rect);
347 }
348 }
349
350 // Only UpdateGeometry if either the window or clip rects have changed.
351 if (delegate_ && (first_geometry_update_ ||
352 new_geometry.window_rect != geometry_.window_rect ||
353 new_geometry.clip_rect != geometry_.clip_rect)) {
354 // Notify the plugin that its parameters have changed.
355 delegate_->UpdateGeometry(new_geometry.window_rect, new_geometry.clip_rect);
356 }
357
358 // Initiate a download on the plugin url. This should be done for the
359 // first update geometry sequence. We need to ensure that the plugin
360 // receives the geometry update before it starts receiving data.
361 if (first_geometry_update_) {
362 // An empty url corresponds to an EMBED tag with no src attribute.
363 if (!load_manually_ && plugin_url_.is_valid()) {
364 // The Flash plugin hangs for a while if it receives data before
365 // receiving valid plugin geometry. By valid geometry we mean the
366 // geometry received by a call to setFrameRect in the Webkit
367 // layout code path. To workaround this issue we download the
368 // plugin source url on a timer.
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100369 base::MessageLoop::current()->PostTask(
370 FROM_HERE,
371 base::Bind(&WebPluginImpl::OnDownloadPluginSrcUrl,
372 weak_factory_.GetWeakPtr()));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000373 }
374 }
375
376#if defined(OS_WIN)
377 // Don't cache the geometry during the first geometry update. The first
378 // geometry update sequence is received when Widget::setParent is called.
379 // For plugins like media player which have a bug where they only honor
380 // the first geometry update, we have a quirk which ignores the first
381 // geometry update. To ensure that these plugins work correctly in cases
382 // where we receive only one geometry update from webkit, we also force
383 // a geometry update during paint which should go out correctly as the
384 // initial geometry update was not cached.
385 if (!first_geometry_update_)
386 geometry_ = new_geometry;
387#else // OS_WIN
388 geometry_ = new_geometry;
389#endif // OS_WIN
390 first_geometry_update_ = false;
391}
392
393void WebPluginImpl::updateFocus(bool focused) {
394 if (accepts_input_events_)
395 delegate_->SetFocus(focused);
396}
397
398void WebPluginImpl::updateVisibility(bool visible) {
Ben Murdochca12bfa2013-07-23 11:17:05 +0100399 if (!window_ || !render_view_.get())
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000400 return;
401
402 WebPluginGeometry move;
403 move.window = window_;
404 move.window_rect = gfx::Rect();
405 move.clip_rect = gfx::Rect();
406 move.rects_valid = false;
407 move.visible = visible;
408
Ben Murdochca12bfa2013-07-23 11:17:05 +0100409 render_view_->SchedulePluginMove(move);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000410}
411
412bool WebPluginImpl::acceptsInputEvents() {
413 return accepts_input_events_;
414}
415
416bool WebPluginImpl::handleInputEvent(
417 const WebInputEvent& event, WebCursorInfo& cursor_info) {
418 // Swallow context menu events in order to suppress the default context menu.
419 if (event.type == WebInputEvent::ContextMenu)
420 return true;
421
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100422 WebCursor::CursorInfo web_cursor_info;
423 bool ret = delegate_->HandleInputEvent(event, &web_cursor_info);
424 cursor_info.type = web_cursor_info.type;
425 cursor_info.hotSpot = web_cursor_info.hotspot;
426 cursor_info.customImage = web_cursor_info.custom_image;
427 cursor_info.imageScaleFactor = web_cursor_info.image_scale_factor;
428#if defined(OS_WIN)
429 cursor_info.externalHandle = web_cursor_info.external_handle;
430#endif
431 return ret;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000432}
433
434void WebPluginImpl::didReceiveResponse(const WebURLResponse& response) {
435 ignore_response_error_ = false;
436
437 ResponseInfo response_info;
438 GetResponseInfo(response, &response_info);
439
440 delegate_->DidReceiveManualResponse(
441 response_info.url,
442 response_info.mime_type,
443 GetAllHeaders(response),
444 response_info.expected_length,
445 response_info.last_modified);
446}
447
448void WebPluginImpl::didReceiveData(const char* data, int data_length) {
449 delegate_->DidReceiveManualData(data, data_length);
450}
451
452void WebPluginImpl::didFinishLoading() {
453 delegate_->DidFinishManualLoading();
454}
455
456void WebPluginImpl::didFailLoading(const WebURLError& error) {
457 if (!ignore_response_error_)
458 delegate_->DidManualLoadFail();
459}
460
461void WebPluginImpl::didFinishLoadingFrameRequest(
462 const WebURL& url, void* notify_data) {
463 if (delegate_) {
464 // We're converting a void* into an arbitrary int id. Though
465 // these types are the same size on all the platforms we support,
466 // the compiler may complain as though they are different, so to
467 // make the casting gods happy go through an intptr_t (the union
468 // of void* and int) rather than converting straight across.
469 delegate_->DidFinishLoadWithReason(
470 url, NPRES_DONE, reinterpret_cast<intptr_t>(notify_data));
471 }
472}
473
474void WebPluginImpl::didFailLoadingFrameRequest(
475 const WebURL& url, void* notify_data, const WebURLError& error) {
476 if (!delegate_)
477 return;
478
479 NPReason reason =
480 error.reason == net::ERR_ABORTED ? NPRES_USER_BREAK : NPRES_NETWORK_ERR;
481 // See comment in didFinishLoadingFrameRequest about the cast here.
482 delegate_->DidFinishLoadWithReason(
483 url, reason, reinterpret_cast<intptr_t>(notify_data));
484}
485
486bool WebPluginImpl::isPlaceholder() {
487 return false;
488}
489
490// -----------------------------------------------------------------------------
491
492WebPluginImpl::WebPluginImpl(
493 WebFrame* webframe,
494 const WebPluginParams& params,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000495 const base::FilePath& file_path,
Ben Murdochca12bfa2013-07-23 11:17:05 +0100496 const base::WeakPtr<RenderViewImpl>& render_view)
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000497 : windowless_(false),
498 window_(gfx::kNullPluginWindow),
499 accepts_input_events_(false),
Ben Murdochca12bfa2013-07-23 11:17:05 +0100500 render_view_(render_view),
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000501 webframe_(webframe),
502 delegate_(NULL),
503 container_(NULL),
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100504 npp_(NULL),
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000505 plugin_url_(params.url),
506 load_manually_(params.loadManually),
507 first_geometry_update_(true),
508 ignore_response_error_(false),
509 file_path_(file_path),
510 mime_type_(UTF16ToASCII(params.mimeType)),
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100511 weak_factory_(this) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000512 DCHECK_EQ(params.attributeNames.size(), params.attributeValues.size());
513 StringToLowerASCII(&mime_type_);
514
515 for (size_t i = 0; i < params.attributeNames.size(); ++i) {
516 arg_names_.push_back(params.attributeNames[i].utf8());
517 arg_values_.push_back(params.attributeValues[i].utf8());
518 }
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100519
520 // Set subresource URL for crash reporting.
521 base::debug::SetCrashKeyValue("subresource_url", plugin_url_.spec());
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000522}
523
524WebPluginImpl::~WebPluginImpl() {
525}
526
527void WebPluginImpl::SetWindow(gfx::PluginWindowHandle window) {
528 if (window) {
529 DCHECK(!windowless_);
530 window_ = window;
531#if defined(OS_MACOSX)
532 // TODO(kbr): remove. http://crbug.com/105344
533
534 // Lie to ourselves about being windowless even if we got a fake
535 // plugin window handle, so we continue to get input events.
536 windowless_ = true;
537 accepts_input_events_ = true;
538 // We do not really need to notify the page delegate that a plugin
539 // window was created -- so don't.
540#else
541 accepts_input_events_ = false;
Ben Murdochca12bfa2013-07-23 11:17:05 +0100542
543#if defined(USE_X11)
544 // Tell the view delegate that the plugin window was created, so that it
545 // can create necessary container widgets.
546 render_view_->Send(new ViewHostMsg_CreatePluginContainer(
547 render_view_->routing_id(), window));
548#endif // USE_X11
549
550#endif // OS_MACOSX
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000551 } else {
552 DCHECK(!window_); // Make sure not called twice.
553 windowless_ = true;
554 accepts_input_events_ = true;
555 }
556}
557
558void WebPluginImpl::SetAcceptsInputEvents(bool accepts) {
559 accepts_input_events_ = accepts;
560}
561
562void WebPluginImpl::WillDestroyWindow(gfx::PluginWindowHandle window) {
563 DCHECK_EQ(window, window_);
564 window_ = gfx::kNullPluginWindow;
Ben Murdochca12bfa2013-07-23 11:17:05 +0100565 if (render_view_.get()) {
566#if defined(USE_X11)
567 render_view_->Send(new ViewHostMsg_DestroyPluginContainer(
568 render_view_->routing_id(), window));
569#endif
570 render_view_->CleanupWindowInPluginMoves(window);
571 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000572}
573
574GURL WebPluginImpl::CompleteURL(const char* url) {
575 if (!webframe_) {
576 NOTREACHED();
577 return GURL();
578 }
579 // TODO(darin): Is conversion from UTF8 correct here?
580 return webframe_->document().completeURL(WebString::fromUTF8(url));
581}
582
583void WebPluginImpl::CancelResource(unsigned long id) {
584 for (size_t i = 0; i < clients_.size(); ++i) {
585 if (clients_[i].id == id) {
586 if (clients_[i].loader.get()) {
587 clients_[i].loader->setDefersLoading(false);
588 clients_[i].loader->cancel();
589 RemoveClient(i);
590 }
591 return;
592 }
593 }
594}
595
596bool WebPluginImpl::SetPostData(WebURLRequest* request,
597 const char *buf,
598 uint32 length) {
599 std::vector<std::string> names;
600 std::vector<std::string> values;
601 std::vector<char> body;
602 bool rv = PluginHost::SetPostData(buf, length, &names, &values, &body);
603
604 for (size_t i = 0; i < names.size(); ++i) {
605 request->addHTTPHeaderField(WebString::fromUTF8(names[i]),
606 WebString::fromUTF8(values[i]));
607 }
608
609 WebString content_type_header = WebString::fromUTF8("Content-Type");
610 const WebString& content_type =
611 request->httpHeaderField(content_type_header);
612 if (content_type.isEmpty()) {
613 request->setHTTPHeaderField(
614 content_type_header,
615 WebString::fromUTF8("application/x-www-form-urlencoded"));
616 }
617
618 WebHTTPBody http_body;
619 if (body.size()) {
620 http_body.initialize();
621 http_body.appendData(WebData(&body[0], body.size()));
622 }
623 request->setHTTPBody(http_body);
624
625 return rv;
626}
627
628WebPluginDelegate* WebPluginImpl::delegate() {
629 return delegate_;
630}
631
632bool WebPluginImpl::IsValidUrl(const GURL& url, Referrer referrer_flag) {
633 if (referrer_flag == PLUGIN_SRC &&
634 mime_type_ == kFlashPluginSwfMimeType &&
635 url.GetOrigin() != plugin_url_.GetOrigin()) {
636 // Do url check to make sure that there are no @, ;, \ chars in between url
637 // scheme and url path.
638 const char* url_to_check(url.spec().data());
639 url_parse::Parsed parsed;
640 url_parse::ParseStandardURL(url_to_check, strlen(url_to_check), &parsed);
641 if (parsed.path.begin <= parsed.scheme.end())
642 return true;
643 std::string string_to_search;
644 string_to_search.assign(url_to_check + parsed.scheme.end(),
645 parsed.path.begin - parsed.scheme.end());
646 if (string_to_search.find("@") != std::string::npos ||
647 string_to_search.find(";") != std::string::npos ||
648 string_to_search.find("\\") != std::string::npos)
649 return false;
650 }
651
652 return true;
653}
654
Ben Murdochca12bfa2013-07-23 11:17:05 +0100655WebPluginDelegate* WebPluginImpl::CreatePluginDelegate() {
656 bool in_process_plugin = RenderProcess::current()->UseInProcessPlugins();
657 if (in_process_plugin) {
658#if defined(OS_WIN) && !defined(USE_AURA)
659 return WebPluginDelegateImpl::Create(file_path_, mime_type_);
660#else
661 // In-proc plugins aren't supported on non-Windows.
662 NOTIMPLEMENTED();
663 return NULL;
664#endif
665 }
666
667 return new WebPluginDelegateProxy(mime_type_, render_view_);
668}
669
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000670WebPluginImpl::RoutingStatus WebPluginImpl::RouteToFrame(
671 const char* url,
672 bool is_javascript_url,
673 bool popups_allowed,
674 const char* method,
675 const char* target,
676 const char* buf,
677 unsigned int len,
678 int notify_id,
679 Referrer referrer_flag) {
680 // If there is no target, there is nothing to do
681 if (!target)
682 return NOT_ROUTED;
683
684 // This could happen if the WebPluginContainer was already deleted.
685 if (!webframe_)
686 return NOT_ROUTED;
687
688 WebString target_str = WebString::fromUTF8(target);
689
690 // Take special action for JavaScript URLs
691 if (is_javascript_url) {
692 WebFrame* target_frame =
693 webframe_->view()->findFrameByName(target_str, webframe_);
694 // For security reasons, do not allow JavaScript on frames
695 // other than this frame.
696 if (target_frame != webframe_) {
697 // TODO(darin): Localize this message.
698 const char kMessage[] =
699 "Ignoring cross-frame javascript URL load requested by plugin.";
700 webframe_->addMessageToConsole(
701 WebConsoleMessage(WebConsoleMessage::LevelError,
702 WebString::fromUTF8(kMessage)));
703 return ROUTED;
704 }
705
706 // Route javascript calls back to the plugin.
707 return NOT_ROUTED;
708 }
709
710 // If we got this far, we're routing content to a target frame.
711 // Go fetch the URL.
712
713 GURL complete_url = CompleteURL(url);
714 // Remove when flash bug is fixed. http://crbug.com/40016.
715 if (!WebPluginImpl::IsValidUrl(complete_url, referrer_flag))
716 return INVALID_URL;
717
718 if (strcmp(method, "GET") != 0) {
719 // We're only going to route HTTP/HTTPS requests
720 if (!(complete_url.SchemeIs("http") || complete_url.SchemeIs("https")))
721 return INVALID_URL;
722 }
723
724 WebURLRequest request(complete_url);
725 SetReferrer(&request, referrer_flag);
726
727 request.setHTTPMethod(WebString::fromUTF8(method));
728 request.setFirstPartyForCookies(
729 webframe_->document().firstPartyForCookies());
730 request.setHasUserGesture(popups_allowed);
731 if (len > 0) {
732 if (!SetPostData(&request, buf, len)) {
733 // Uhoh - we're in trouble. There isn't a good way
734 // to recover at this point. Break out.
735 NOTREACHED();
736 return ROUTED;
737 }
738 }
739
740 container_->loadFrameRequest(
741 request, target_str, notify_id != 0, reinterpret_cast<void*>(notify_id));
742 return ROUTED;
743}
744
745NPObject* WebPluginImpl::GetWindowScriptNPObject() {
746 if (!webframe_) {
747 NOTREACHED();
748 return NULL;
749 }
750 return webframe_->windowObject();
751}
752
753NPObject* WebPluginImpl::GetPluginElement() {
754 return container_->scriptableObjectForElement();
755}
756
757bool WebPluginImpl::FindProxyForUrl(const GURL& url, std::string* proxy_list) {
758 // Proxy resolving doesn't work in single-process mode.
759 return false;
760}
761
762void WebPluginImpl::SetCookie(const GURL& url,
763 const GURL& first_party_for_cookies,
764 const std::string& cookie) {
Ben Murdochca12bfa2013-07-23 11:17:05 +0100765 if (!render_view_.get())
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000766 return;
767
Ben Murdochca12bfa2013-07-23 11:17:05 +0100768 WebCookieJar* cookie_jar = render_view_->cookie_jar();
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000769 if (!cookie_jar) {
770 DLOG(WARNING) << "No cookie jar!";
771 return;
772 }
773
774 cookie_jar->setCookie(
775 url, first_party_for_cookies, WebString::fromUTF8(cookie));
776}
777
778std::string WebPluginImpl::GetCookies(const GURL& url,
779 const GURL& first_party_for_cookies) {
Ben Murdochca12bfa2013-07-23 11:17:05 +0100780 if (!render_view_.get())
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000781 return std::string();
782
Ben Murdochca12bfa2013-07-23 11:17:05 +0100783 WebCookieJar* cookie_jar = render_view_->cookie_jar();
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000784 if (!cookie_jar) {
785 DLOG(WARNING) << "No cookie jar!";
786 return std::string();
787 }
788
789 return UTF16ToUTF8(cookie_jar->cookies(url, first_party_for_cookies));
790}
791
792void WebPluginImpl::URLRedirectResponse(bool allow, int resource_id) {
793 for (size_t i = 0; i < clients_.size(); ++i) {
794 if (clients_[i].id == static_cast<unsigned long>(resource_id)) {
795 if (clients_[i].loader.get()) {
796 if (allow) {
797 clients_[i].loader->setDefersLoading(false);
798 } else {
799 clients_[i].loader->cancel();
800 if (clients_[i].client)
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100801 clients_[i].client->DidFail(clients_[i].id);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000802 }
803 }
804 break;
805 }
806 }
807}
808
809#if defined(OS_MACOSX)
810WebPluginAcceleratedSurface* WebPluginImpl::GetAcceleratedSurface(
811 gfx::GpuPreference gpu_preference) {
812 return NULL;
813}
814
815void WebPluginImpl::AcceleratedPluginEnabledRendering() {
816}
817
818void WebPluginImpl::AcceleratedPluginAllocatedIOSurface(int32 width,
819 int32 height,
820 uint32 surface_id) {
821 next_io_surface_allocated_ = true;
822 next_io_surface_width_ = width;
823 next_io_surface_height_ = height;
824 next_io_surface_id_ = surface_id;
825}
826
827void WebPluginImpl::AcceleratedPluginSwappedIOSurface() {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000828 if (!container_)
829 return;
830 // Deferring the call to setBackingIOSurfaceId is an attempt to
831 // work around garbage occasionally showing up in the plugin's
832 // area during live resizing of Core Animation plugins. The
833 // assumption was that by the time this was called, the plugin
834 // process would have populated the newly allocated IOSurface. It
835 // is not 100% clear at this point why any garbage is getting
836 // through. More investigation is needed. http://crbug.com/105346
837 if (next_io_surface_allocated_) {
838 if (next_io_surface_id_) {
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100839 if (!io_surface_layer_.get()) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000840 io_surface_layer_ = cc::IOSurfaceLayer::Create();
841 web_layer_.reset(new webkit::WebLayerImpl(io_surface_layer_));
842 container_->setWebLayer(web_layer_.get());
843 }
844 io_surface_layer_->SetIOSurfaceProperties(
845 next_io_surface_id_,
846 gfx::Size(next_io_surface_width_, next_io_surface_height_));
847 } else {
848 container_->setWebLayer(NULL);
849 web_layer_.reset();
850 io_surface_layer_ = NULL;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000851 }
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000852 next_io_surface_allocated_ = false;
853 } else {
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100854 if (io_surface_layer_.get())
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000855 io_surface_layer_->SetNeedsDisplay();
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000856 }
857}
858#endif
859
860void WebPluginImpl::Invalidate() {
861 if (container_)
862 container_->invalidate();
863}
864
865void WebPluginImpl::InvalidateRect(const gfx::Rect& rect) {
866 if (container_)
867 container_->invalidateRect(rect);
868}
869
870void WebPluginImpl::OnDownloadPluginSrcUrl() {
871 HandleURLRequestInternal(
872 plugin_url_.spec().c_str(), "GET", NULL, NULL, 0, 0, false, DOCUMENT_URL,
873 false, true);
874}
875
876WebPluginResourceClient* WebPluginImpl::GetClientFromLoader(
877 WebURLLoader* loader) {
878 ClientInfo* client_info = GetClientInfoFromLoader(loader);
879 if (client_info)
880 return client_info->client;
881 return NULL;
882}
883
884WebPluginImpl::ClientInfo* WebPluginImpl::GetClientInfoFromLoader(
885 WebURLLoader* loader) {
886 for (size_t i = 0; i < clients_.size(); ++i) {
887 if (clients_[i].loader.get() == loader)
888 return &clients_[i];
889 }
890
891 NOTREACHED();
892 return 0;
893}
894
895void WebPluginImpl::willSendRequest(WebURLLoader* loader,
896 WebURLRequest& request,
897 const WebURLResponse& response) {
898 WebPluginImpl::ClientInfo* client_info = GetClientInfoFromLoader(loader);
899 if (client_info) {
900 // Currently this check is just to catch an https -> http redirect when
901 // loading the main plugin src URL. Longer term, we could investigate
902 // firing mixed diplay or scripting issues for subresource loads
903 // initiated by plug-ins.
904 if (client_info->is_plugin_src_load &&
905 webframe_ &&
906 !webframe_->checkIfRunInsecureContent(request.url())) {
907 loader->cancel();
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100908 client_info->client->DidFail(client_info->id);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000909 return;
910 }
911 if (net::HttpResponseHeaders::IsRedirectResponseCode(
912 response.httpStatusCode())) {
913 // If the plugin does not participate in url redirect notifications then
914 // just block cross origin 307 POST redirects.
915 if (!client_info->notify_redirects) {
916 if (response.httpStatusCode() == 307 &&
917 LowerCaseEqualsASCII(request.httpMethod().utf8(), "post")) {
918 GURL original_request_url(response.url());
919 GURL response_url(request.url());
920 if (original_request_url.GetOrigin() != response_url.GetOrigin()) {
921 loader->setDefersLoading(true);
922 loader->cancel();
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100923 client_info->client->DidFail(client_info->id);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000924 return;
925 }
926 }
927 } else {
928 loader->setDefersLoading(true);
929 }
930 }
931 client_info->client->WillSendRequest(request.url(),
932 response.httpStatusCode());
933 }
934}
935
936void WebPluginImpl::didSendData(WebURLLoader* loader,
937 unsigned long long bytes_sent,
938 unsigned long long total_bytes_to_be_sent) {
939}
940
941void WebPluginImpl::didReceiveResponse(WebURLLoader* loader,
942 const WebURLResponse& response) {
943 static const int kHttpPartialResponseStatusCode = 206;
944 static const int kHttpResponseSuccessStatusCode = 200;
945
946 WebPluginResourceClient* client = GetClientFromLoader(loader);
947 if (!client)
948 return;
949
950 ResponseInfo response_info;
951 GetResponseInfo(response, &response_info);
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100952 ClientInfo* client_info = GetClientInfoFromLoader(loader);
953 if (!client_info)
954 return;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000955
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000956 bool request_is_seekable = true;
957 if (client->IsMultiByteResponseExpected()) {
958 if (response.httpStatusCode() == kHttpPartialResponseStatusCode) {
Ben Murdocheb525c52013-07-10 11:40:50 +0100959 ClientInfo* client_info = GetClientInfoFromLoader(loader);
960 if (!client_info)
961 return;
962 if (HandleHttpMultipartResponse(response, client)) {
963 // Multiple ranges requested, data will be delivered by
964 // MultipartResponseDelegate.
965 client_info->data_offset = 0;
966 return;
967 }
968 int64 upper_bound = 0, instance_size = 0;
969 // Single range requested - go through original processing for
970 // non-multipart requests, but update data offset.
971 MultipartResponseDelegate::ReadContentRanges(response,
972 &client_info->data_offset,
973 &upper_bound,
974 &instance_size);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000975 } else if (response.httpStatusCode() == kHttpResponseSuccessStatusCode) {
976 // If the client issued a byte range request and the server responds with
977 // HTTP 200 OK, it indicates that the server does not support byte range
978 // requests.
979 // We need to emulate Firefox behavior by doing the following:-
980 // 1. Destroy the plugin instance in the plugin process. Ensure that
981 // existing resource requests initiated for the plugin instance
982 // continue to remain valid.
983 // 2. Create a new plugin instance and notify it about the response
984 // received here.
985 if (!ReinitializePluginForResponse(loader)) {
986 NOTREACHED();
987 return;
988 }
989
990 // The server does not support byte range requests. No point in creating
991 // seekable streams.
992 request_is_seekable = false;
993
994 delete client;
995 client = NULL;
996
997 // Create a new resource client for this request.
998 for (size_t i = 0; i < clients_.size(); ++i) {
999 if (clients_[i].loader.get() == loader) {
1000 WebPluginResourceClient* resource_client =
1001 delegate_->CreateResourceClient(clients_[i].id, plugin_url_, 0);
1002 clients_[i].client = resource_client;
1003 client = resource_client;
1004 break;
1005 }
1006 }
1007
1008 DCHECK(client != NULL);
1009 }
1010 }
1011
1012 // Calling into a plugin could result in reentrancy if the plugin yields
1013 // control to the OS like entering a modal loop etc. Prevent this by
1014 // stopping further loading until the plugin notifies us that it is ready to
1015 // accept data
1016 loader->setDefersLoading(true);
1017
1018 client->DidReceiveResponse(
1019 response_info.mime_type,
1020 GetAllHeaders(response),
1021 response_info.expected_length,
1022 response_info.last_modified,
1023 request_is_seekable);
1024
1025 // Bug http://b/issue?id=925559. The flash plugin would not handle the HTTP
1026 // error codes in the stream header and as a result, was unaware of the
1027 // fate of the HTTP requests issued via NPN_GetURLNotify. Webkit and FF
1028 // destroy the stream and invoke the NPP_DestroyStream function on the
1029 // plugin if the HTTP request fails.
1030 const GURL& url = response.url();
1031 if (url.SchemeIs("http") || url.SchemeIs("https")) {
1032 if (response.httpStatusCode() < 100 || response.httpStatusCode() >= 400) {
1033 // The plugin instance could be in the process of deletion here.
1034 // Verify if the WebPluginResourceClient instance still exists before
1035 // use.
1036 ClientInfo* client_info = GetClientInfoFromLoader(loader);
1037 if (client_info) {
1038 client_info->pending_failure_notification = true;
1039 }
1040 }
1041 }
1042}
1043
1044void WebPluginImpl::didReceiveData(WebURLLoader* loader,
1045 const char *buffer,
1046 int data_length,
1047 int encoded_data_length) {
1048 WebPluginResourceClient* client = GetClientFromLoader(loader);
1049 if (!client)
1050 return;
1051
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001052 MultiPartResponseHandlerMap::iterator index =
1053 multi_part_response_map_.find(client);
1054 if (index != multi_part_response_map_.end()) {
1055 MultipartResponseDelegate* multi_part_handler = (*index).second;
1056 DCHECK(multi_part_handler != NULL);
1057 multi_part_handler->OnReceivedData(buffer,
1058 data_length,
1059 encoded_data_length);
1060 } else {
1061 loader->setDefersLoading(true);
Ben Murdocheb525c52013-07-10 11:40:50 +01001062 ClientInfo* client_info = GetClientInfoFromLoader(loader);
1063 client->DidReceiveData(buffer, data_length, client_info->data_offset);
1064 client_info->data_offset += data_length;
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001065 }
1066}
1067
1068void WebPluginImpl::didFinishLoading(WebURLLoader* loader, double finishTime) {
1069 ClientInfo* client_info = GetClientInfoFromLoader(loader);
1070 if (client_info && client_info->client) {
1071 MultiPartResponseHandlerMap::iterator index =
1072 multi_part_response_map_.find(client_info->client);
1073 if (index != multi_part_response_map_.end()) {
1074 delete (*index).second;
1075 multi_part_response_map_.erase(index);
Ben Murdochca12bfa2013-07-23 11:17:05 +01001076 if (render_view_.get()) {
1077 // TODO(darin): Make is_loading_ be a counter!
1078 render_view_->didStopLoading();
1079 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001080 }
1081 loader->setDefersLoading(true);
1082 WebPluginResourceClient* resource_client = client_info->client;
1083 // The ClientInfo can get deleted in the call to DidFinishLoading below.
1084 // It is not safe to access this structure after that.
1085 client_info->client = NULL;
Ben Murdoch7dbb3d52013-07-17 14:55:54 +01001086 resource_client->DidFinishLoading(client_info->id);
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001087 }
1088}
1089
1090void WebPluginImpl::didFail(WebURLLoader* loader,
1091 const WebURLError& error) {
1092 ClientInfo* client_info = GetClientInfoFromLoader(loader);
1093 if (client_info && client_info->client) {
1094 loader->setDefersLoading(true);
1095 WebPluginResourceClient* resource_client = client_info->client;
1096 // The ClientInfo can get deleted in the call to DidFail below.
1097 // It is not safe to access this structure after that.
1098 client_info->client = NULL;
Ben Murdoch7dbb3d52013-07-17 14:55:54 +01001099 resource_client->DidFail(client_info->id);
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001100 }
1101}
1102
1103void WebPluginImpl::RemoveClient(size_t i) {
1104 clients_.erase(clients_.begin() + i);
1105}
1106
1107void WebPluginImpl::RemoveClient(WebURLLoader* loader) {
1108 for (size_t i = 0; i < clients_.size(); ++i) {
1109 if (clients_[i].loader.get() == loader) {
1110 RemoveClient(i);
1111 return;
1112 }
1113 }
1114}
1115
1116void WebPluginImpl::SetContainer(WebPluginContainer* container) {
1117 if (!container)
1118 TearDownPluginInstance(NULL);
1119 container_ = container;
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +01001120 if (container_)
1121 container_->allowScriptObjects();
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001122}
1123
1124void WebPluginImpl::HandleURLRequest(const char* url,
1125 const char* method,
1126 const char* target,
1127 const char* buf,
1128 unsigned int len,
1129 int notify_id,
1130 bool popups_allowed,
1131 bool notify_redirects) {
1132 // GetURL/PostURL requests initiated explicitly by plugins should specify the
1133 // plugin SRC url as the referrer if it is available.
1134 HandleURLRequestInternal(
1135 url, method, target, buf, len, notify_id, popups_allowed, PLUGIN_SRC,
1136 notify_redirects, false);
1137}
1138
1139void WebPluginImpl::HandleURLRequestInternal(const char* url,
1140 const char* method,
1141 const char* target,
1142 const char* buf,
1143 unsigned int len,
1144 int notify_id,
1145 bool popups_allowed,
1146 Referrer referrer_flag,
1147 bool notify_redirects,
1148 bool is_plugin_src_load) {
1149 // For this request, we either route the output to a frame
1150 // because a target has been specified, or we handle the request
1151 // here, i.e. by executing the script if it is a javascript url
1152 // or by initiating a download on the URL, etc. There is one special
1153 // case in that the request is a javascript url and the target is "_self",
1154 // in which case we route the output to the plugin rather than routing it
1155 // to the plugin's frame.
1156 bool is_javascript_url = url_util::FindAndCompareScheme(
1157 url, strlen(url), "javascript", NULL);
1158 RoutingStatus routing_status = RouteToFrame(
1159 url, is_javascript_url, popups_allowed, method, target, buf, len,
1160 notify_id, referrer_flag);
1161 if (routing_status == ROUTED)
1162 return;
1163
1164 if (is_javascript_url) {
1165 GURL gurl(url);
1166 WebString result = container_->executeScriptURL(gurl, popups_allowed);
1167
1168 // delegate_ could be NULL because executeScript caused the container to
1169 // be deleted.
1170 if (delegate_) {
1171 delegate_->SendJavaScriptStream(
1172 gurl, result.utf8(), !result.isNull(), notify_id);
1173 }
1174
1175 return;
1176 }
1177
1178 unsigned long resource_id = GetNextResourceId();
1179 if (!resource_id)
1180 return;
1181
1182 GURL complete_url = CompleteURL(url);
1183 // Remove when flash bug is fixed. http://crbug.com/40016.
1184 if (!WebPluginImpl::IsValidUrl(complete_url, referrer_flag))
1185 return;
1186
1187 WebPluginResourceClient* resource_client = delegate_->CreateResourceClient(
1188 resource_id, complete_url, notify_id);
1189 if (!resource_client)
1190 return;
1191
1192 // If the RouteToFrame call returned a failure then inform the result
1193 // back to the plugin asynchronously.
1194 if ((routing_status == INVALID_URL) ||
1195 (routing_status == GENERAL_FAILURE)) {
Ben Murdoch7dbb3d52013-07-17 14:55:54 +01001196 resource_client->DidFail(resource_id);
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001197 return;
1198 }
1199
1200 // CreateResourceClient() sends a synchronous IPC message so it's possible
1201 // that TearDownPluginInstance() may have been called in the nested
1202 // message loop. If so, don't start the request.
1203 if (!delegate_)
1204 return;
1205
1206 InitiateHTTPRequest(resource_id, resource_client, complete_url, method, buf,
1207 len, NULL, referrer_flag, notify_redirects,
1208 is_plugin_src_load);
1209}
1210
1211unsigned long WebPluginImpl::GetNextResourceId() {
1212 if (!webframe_)
1213 return 0;
1214 WebView* view = webframe_->view();
1215 if (!view)
1216 return 0;
1217 return view->createUniqueIdentifierForRequest();
1218}
1219
1220bool WebPluginImpl::InitiateHTTPRequest(unsigned long resource_id,
1221 WebPluginResourceClient* client,
1222 const GURL& url,
1223 const char* method,
1224 const char* buf,
1225 int buf_len,
1226 const char* range_info,
1227 Referrer referrer_flag,
1228 bool notify_redirects,
1229 bool is_plugin_src_load) {
1230 if (!client) {
1231 NOTREACHED();
1232 return false;
1233 }
1234
1235 ClientInfo info;
1236 info.id = resource_id;
1237 info.client = client;
1238 info.request.initialize();
1239 info.request.setURL(url);
1240 info.request.setFirstPartyForCookies(
1241 webframe_->document().firstPartyForCookies());
1242 info.request.setRequestorProcessID(delegate_->GetProcessId());
1243 info.request.setTargetType(WebURLRequest::TargetIsObject);
1244 info.request.setHTTPMethod(WebString::fromUTF8(method));
1245 info.pending_failure_notification = false;
1246 info.notify_redirects = notify_redirects;
1247 info.is_plugin_src_load = is_plugin_src_load;
Ben Murdocheb525c52013-07-10 11:40:50 +01001248 info.data_offset = 0;
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001249
1250 if (range_info) {
1251 info.request.addHTTPHeaderField(WebString::fromUTF8("Range"),
1252 WebString::fromUTF8(range_info));
1253 }
1254
1255 if (strcmp(method, "POST") == 0) {
1256 // Adds headers or form data to a request. This must be called before
1257 // we initiate the actual request.
1258 SetPostData(&info.request, buf, buf_len);
1259 }
1260
1261 SetReferrer(&info.request, referrer_flag);
1262
1263 WebURLLoaderOptions options;
1264 options.allowCredentials = true;
1265 options.crossOriginRequestPolicy =
1266 WebURLLoaderOptions::CrossOriginRequestPolicyAllow;
1267 info.loader.reset(webframe_->createAssociatedURLLoader(options));
1268 if (!info.loader.get())
1269 return false;
1270 info.loader->loadAsynchronously(info.request, this);
1271
1272 clients_.push_back(info);
1273 return true;
1274}
1275
1276void WebPluginImpl::CancelDocumentLoad() {
1277 if (webframe_) {
1278 ignore_response_error_ = true;
1279 webframe_->stopLoading();
1280 }
1281}
1282
1283void WebPluginImpl::InitiateHTTPRangeRequest(
1284 const char* url, const char* range_info, int range_request_id) {
1285 unsigned long resource_id = GetNextResourceId();
1286 if (!resource_id)
1287 return;
1288
1289 GURL complete_url = CompleteURL(url);
1290 // Remove when flash bug is fixed. http://crbug.com/40016.
1291 if (!WebPluginImpl::IsValidUrl(complete_url,
1292 load_manually_ ? NO_REFERRER : PLUGIN_SRC))
1293 return;
1294
1295 WebPluginResourceClient* resource_client =
1296 delegate_->CreateSeekableResourceClient(resource_id, range_request_id);
1297 InitiateHTTPRequest(
1298 resource_id, resource_client, complete_url, "GET", NULL, 0, range_info,
1299 load_manually_ ? NO_REFERRER : PLUGIN_SRC, false, false);
1300}
1301
1302void WebPluginImpl::SetDeferResourceLoading(unsigned long resource_id,
1303 bool defer) {
1304 std::vector<ClientInfo>::iterator client_index = clients_.begin();
1305 while (client_index != clients_.end()) {
1306 ClientInfo& client_info = *client_index;
1307
1308 if (client_info.id == resource_id) {
1309 client_info.loader->setDefersLoading(defer);
1310
1311 // If we determined that the request had failed via the HTTP headers
1312 // in the response then we send out a failure notification to the
1313 // plugin process, as certain plugins don't handle HTTP failure codes
1314 // correctly.
1315 if (!defer && client_info.client &&
1316 client_info.pending_failure_notification) {
1317 // The ClientInfo and the iterator can become invalid due to the call
1318 // to DidFail below.
1319 WebPluginResourceClient* resource_client = client_info.client;
1320 client_info.loader->cancel();
1321 clients_.erase(client_index++);
Ben Murdoch7dbb3d52013-07-17 14:55:54 +01001322 resource_client->DidFail(resource_id);
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001323 }
1324 break;
1325 }
1326 client_index++;
1327 }
1328}
1329
1330bool WebPluginImpl::IsOffTheRecord() {
1331 return false;
1332}
1333
Ben Murdocheb525c52013-07-10 11:40:50 +01001334bool WebPluginImpl::HandleHttpMultipartResponse(
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001335 const WebURLResponse& response, WebPluginResourceClient* client) {
1336 std::string multipart_boundary;
1337 if (!MultipartResponseDelegate::ReadMultipartBoundary(
1338 response, &multipart_boundary)) {
Ben Murdocheb525c52013-07-10 11:40:50 +01001339 return false;
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001340 }
1341
Ben Murdochca12bfa2013-07-23 11:17:05 +01001342 if (render_view_.get()) {
1343 // TODO(darin): Make is_loading_ be a counter!
1344 render_view_->didStartLoading();
1345 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001346
1347 MultiPartResponseClient* multi_part_response_client =
1348 new MultiPartResponseClient(client);
1349
1350 MultipartResponseDelegate* multi_part_response_handler =
1351 new MultipartResponseDelegate(multi_part_response_client, NULL,
1352 response,
1353 multipart_boundary);
1354 multi_part_response_map_[client] = multi_part_response_handler;
Ben Murdocheb525c52013-07-10 11:40:50 +01001355 return true;
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001356}
1357
1358bool WebPluginImpl::ReinitializePluginForResponse(
1359 WebURLLoader* loader) {
1360 WebFrame* webframe = webframe_;
1361 if (!webframe)
1362 return false;
1363
1364 WebView* webview = webframe->view();
1365 if (!webview)
1366 return false;
1367
1368 WebPluginContainer* container_widget = container_;
1369
1370 // Destroy the current plugin instance.
1371 TearDownPluginInstance(loader);
1372
1373 container_ = container_widget;
1374 webframe_ = webframe;
1375
Ben Murdochca12bfa2013-07-23 11:17:05 +01001376 WebPluginDelegate* plugin_delegate = CreatePluginDelegate();
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001377
Ben Murdocheb525c52013-07-10 11:40:50 +01001378 // Store the plugin's unique identifier, used by the container to track its
1379 // script objects, and enable script objects (since Initialize may use them
1380 // even if it fails).
1381 npp_ = plugin_delegate->GetPluginNPP();
1382 container_->allowScriptObjects();
1383
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001384 bool ok = plugin_delegate && plugin_delegate->Initialize(
1385 plugin_url_, arg_names_, arg_values_, this, load_manually_);
1386
1387 if (!ok) {
Ben Murdocheb525c52013-07-10 11:40:50 +01001388 container_->clearScriptObjects();
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001389 container_ = NULL;
1390 // TODO(iyengar) Should we delete the current plugin instance here?
1391 return false;
1392 }
1393
1394 delegate_ = plugin_delegate;
1395
1396 // Force a geometry update to occur to ensure that the plugin becomes
1397 // visible.
1398 container_->reportGeometry();
1399
1400 // The plugin move sequences accumulated via DidMove are sent to the browser
1401 // whenever the renderer paints. Force a paint here to ensure that changes
1402 // to the plugin window are propagated to the browser.
1403 container_->invalidate();
1404 return true;
1405}
1406
1407void WebPluginImpl::TearDownPluginInstance(
1408 WebURLLoader* loader_to_ignore) {
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +01001409 // JavaScript garbage collection may cause plugin script object references to
1410 // be retained long after the plugin is destroyed. Some plugins won't cope
1411 // with their objects being released after they've been destroyed, and once
1412 // we've actually unloaded the plugin the object's releaseobject() code may
1413 // no longer be in memory. The container tracks the plugin's objects and lets
1414 // us invalidate them, releasing the references to them held by the JavaScript
1415 // runtime.
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001416 if (container_) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001417 container_->clearScriptObjects();
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001418 container_->setWebLayer(NULL);
1419 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001420
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +01001421 // Call PluginDestroyed() first to prevent the plugin from calling us back
1422 // in the middle of tearing down the render tree.
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001423 if (delegate_) {
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +01001424 // The plugin may call into the browser and pass script objects even during
1425 // teardown, so temporarily re-enable plugin script objects.
1426 DCHECK(container_);
1427 container_->allowScriptObjects();
1428
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001429 delegate_->PluginDestroyed();
1430 delegate_ = NULL;
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +01001431
1432 // Invalidate any script objects created during teardown here, before the
1433 // plugin might actually be unloaded.
1434 container_->clearScriptObjects();
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001435 }
1436
1437 // Cancel any pending requests because otherwise this deleted object will
1438 // be called by the ResourceDispatcher.
1439 std::vector<ClientInfo>::iterator client_index = clients_.begin();
1440 while (client_index != clients_.end()) {
1441 ClientInfo& client_info = *client_index;
1442
1443 if (loader_to_ignore == client_info.loader) {
1444 client_index++;
1445 continue;
1446 }
1447
1448 if (client_info.loader.get())
1449 client_info.loader->cancel();
1450
1451 client_index = clients_.erase(client_index);
1452 }
1453
1454 // This needs to be called now and not in the destructor since the
1455 // webframe_ might not be valid anymore.
1456 webframe_ = NULL;
1457 weak_factory_.InvalidateWeakPtrs();
1458}
1459
1460void WebPluginImpl::SetReferrer(WebKit::WebURLRequest* request,
1461 Referrer referrer_flag) {
1462 switch (referrer_flag) {
1463 case DOCUMENT_URL:
1464 webframe_->setReferrerForRequest(*request, GURL());
1465 break;
1466
1467 case PLUGIN_SRC:
1468 webframe_->setReferrerForRequest(*request, plugin_url_);
1469 break;
1470
1471 default:
1472 break;
1473 }
1474}
1475
Ben Murdochca12bfa2013-07-23 11:17:05 +01001476} // namespace content