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.
5 #include "content/renderer/npapi/webplugin_impl.h"
8 #include "base/command_line.h"
9 #include "base/debug/crash_logging.h"
10 #include "base/logging.h"
11 #include "base/memory/linked_ptr.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/metrics/user_metrics_action.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "cc/layers/io_surface_layer.h"
18 #include "content/child/appcache/web_application_cache_host_impl.h"
19 #include "content/child/npapi/plugin_host.h"
20 #include "content/child/npapi/plugin_instance.h"
21 #include "content/child/npapi/webplugin_delegate_impl.h"
22 #include "content/child/npapi/webplugin_resource_client.h"
23 #include "content/common/view_messages.h"
24 #include "content/public/common/content_constants.h"
25 #include "content/public/common/content_switches.h"
26 #include "content/public/renderer/content_renderer_client.h"
27 #include "content/renderer/npapi/webplugin_delegate_proxy.h"
28 #include "content/renderer/render_frame_impl.h"
29 #include "content/renderer/render_process.h"
30 #include "content/renderer/render_thread_impl.h"
31 #include "content/renderer/render_view_impl.h"
32 #include "net/base/escape.h"
33 #include "net/base/net_errors.h"
34 #include "net/http/http_response_headers.h"
35 #include "skia/ext/platform_canvas.h"
36 #include "third_party/WebKit/public/platform/WebCString.h"
37 #include "third_party/WebKit/public/platform/WebCookieJar.h"
38 #include "third_party/WebKit/public/platform/WebCursorInfo.h"
39 #include "third_party/WebKit/public/platform/WebData.h"
40 #include "third_party/WebKit/public/platform/WebHTTPBody.h"
41 #include "third_party/WebKit/public/platform/WebHTTPHeaderVisitor.h"
42 #include "third_party/WebKit/public/platform/WebURL.h"
43 #include "third_party/WebKit/public/platform/WebURLError.h"
44 #include "third_party/WebKit/public/platform/WebURLLoader.h"
45 #include "third_party/WebKit/public/platform/WebURLLoaderClient.h"
46 #include "third_party/WebKit/public/platform/WebURLResponse.h"
47 #include "third_party/WebKit/public/web/WebConsoleMessage.h"
48 #include "third_party/WebKit/public/web/WebDocument.h"
49 #include "third_party/WebKit/public/web/WebFrame.h"
50 #include "third_party/WebKit/public/web/WebInputEvent.h"
51 #include "third_party/WebKit/public/web/WebKit.h"
52 #include "third_party/WebKit/public/web/WebPluginContainer.h"
53 #include "third_party/WebKit/public/web/WebPluginParams.h"
54 #include "third_party/WebKit/public/web/WebURLLoaderOptions.h"
55 #include "third_party/WebKit/public/web/WebView.h"
56 #include "ui/gfx/rect.h"
58 #include "url/url_util.h"
59 #include "webkit/child/multipart_response_delegate.h"
60 #include "webkit/renderer/compositor_bindings/web_layer_impl.h"
62 using blink::WebCanvas;
63 using blink::WebConsoleMessage;
64 using blink::WebCookieJar;
65 using blink::WebCString;
66 using blink::WebCursorInfo;
68 using blink::WebDataSource;
69 using blink::WebFrame;
70 using blink::WebHTTPBody;
71 using blink::WebHTTPHeaderVisitor;
72 using blink::WebInputEvent;
73 using blink::WebKeyboardEvent;
74 using blink::WebMouseEvent;
75 using blink::WebPluginContainer;
76 using blink::WebPluginParams;
78 using blink::WebString;
80 using blink::WebURLError;
81 using blink::WebURLLoader;
82 using blink::WebURLLoaderClient;
83 using blink::WebURLLoaderOptions;
84 using blink::WebURLRequest;
85 using blink::WebURLResponse;
86 using blink::WebVector;
88 using webkit_glue::MultipartResponseDelegate;
94 // This class handles individual multipart responses. It is instantiated when
95 // we receive HTTP status code 206 in the HTTP response. This indicates
96 // that the response could have multiple parts each separated by a boundary
97 // specified in the response header.
98 class MultiPartResponseClient : public WebURLLoaderClient {
100 explicit MultiPartResponseClient(WebPluginResourceClient* resource_client)
101 : byte_range_lower_bound_(0), resource_client_(resource_client) {}
103 virtual void willSendRequest(
104 WebURLLoader*, WebURLRequest&, const WebURLResponse&) {}
105 virtual void didSendData(
106 WebURLLoader*, unsigned long long, unsigned long long) {}
108 // Called when the multipart parser encounters an embedded multipart
110 virtual void didReceiveResponse(
111 WebURLLoader*, const WebURLResponse& response) {
112 int64 byte_range_upper_bound, instance_size;
113 if (!MultipartResponseDelegate::ReadContentRanges(
115 &byte_range_lower_bound_,
116 &byte_range_upper_bound,
122 // Receives individual part data from a multipart response.
123 virtual void didReceiveData(WebURLLoader*,
126 int encoded_data_length) {
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;
135 virtual void didFinishLoading(WebURLLoader*,
137 int64_t total_encoded_data_length) {}
138 virtual void didFail(WebURLLoader*, const WebURLError&) {}
141 // The lower bound of the byte range.
142 int64 byte_range_lower_bound_;
143 // The handler for the data.
144 WebPluginResourceClient* resource_client_;
147 class HeaderFlattener : public WebHTTPHeaderVisitor {
149 explicit HeaderFlattener(std::string* buf) : buf_(buf) {
152 virtual void visitHeader(const WebString& name, const WebString& value) {
153 // TODO(darin): Should we really exclude headers with an empty value?
154 if (!name.isEmpty() && !value.isEmpty()) {
155 buf_->append(name.utf8());
157 buf_->append(value.utf8());
166 std::string GetAllHeaders(const WebURLResponse& response) {
167 // TODO(darin): It is possible for httpStatusText to be empty and still have
168 // an interesting response, so this check seems wrong.
170 const WebString& status = response.httpStatusText();
171 if (status.isEmpty())
174 // TODO(darin): Shouldn't we also report HTTP version numbers?
175 result = base::StringPrintf("HTTP %d ", response.httpStatusCode());
176 result.append(status.utf8());
179 HeaderFlattener flattener(&result);
180 response.visitHTTPHeaderFields(&flattener);
185 struct ResponseInfo {
187 std::string mime_type;
188 uint32 last_modified;
189 uint32 expected_length;
192 void GetResponseInfo(const WebURLResponse& response,
193 ResponseInfo* response_info) {
194 response_info->url = response.url();
195 response_info->mime_type = response.mimeType().utf8();
197 // Measured in seconds since 12:00 midnight GMT, January 1, 1970.
198 response_info->last_modified =
199 static_cast<uint32>(response.lastModifiedDate());
201 // If the length comes in as -1, then it indicates that it was not
202 // read off the HTTP headers. We replicate Safari webkit behavior here,
203 // which is to set it to 0.
204 response_info->expected_length =
205 static_cast<uint32>(std::max(response.expectedContentLength(), 0LL));
207 WebString content_encoding =
208 response.httpHeaderField(WebString::fromUTF8("Content-Encoding"));
209 if (!content_encoding.isNull() &&
210 !EqualsASCII(content_encoding, "identity")) {
211 // Don't send the compressed content length to the plugin, which only
212 // cares about the decoded length.
213 response_info->expected_length = 0;
219 // blink::WebPlugin ----------------------------------------------------------
221 struct WebPluginImpl::ClientInfo {
223 WebPluginResourceClient* client;
224 blink::WebURLRequest request;
225 bool pending_failure_notification;
226 linked_ptr<blink::WebURLLoader> loader;
227 bool notify_redirects;
228 bool is_plugin_src_load;
232 bool WebPluginImpl::initialize(WebPluginContainer* container) {
233 if (!render_view_.get()) {
234 LOG(ERROR) << "No RenderView";
238 WebPluginDelegateProxy* plugin_delegate = new WebPluginDelegateProxy(
239 this, mime_type_, render_view_, render_frame_);
241 // Store the plugin's unique identifier, used by the container to track its
243 npp_ = plugin_delegate->GetPluginNPP();
245 // Set the container before Initialize because the plugin may
246 // synchronously call NPN_GetValue to get its container, or make calls
247 // passing script objects that need to be tracked, during initialization.
248 SetContainer(container);
250 bool ok = plugin_delegate->Initialize(
251 plugin_url_, arg_names_, arg_values_, load_manually_);
253 plugin_delegate->PluginDestroyed();
255 blink::WebPlugin* replacement_plugin =
256 GetContentClient()->renderer()->CreatePluginReplacement(
257 render_frame_, file_path_);
258 if (!replacement_plugin)
261 // Disable scripting by this plugin before replacing it with the new
262 // one. This plugin also needs destroying, so use destroy(), which will
263 // implicitly disable scripting while un-setting the container.
266 // Inform the container of the replacement plugin, then initialize it.
267 container->setPlugin(replacement_plugin);
268 return replacement_plugin->initialize(container);
271 delegate_ = plugin_delegate;
276 void WebPluginImpl::destroy() {
278 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
281 NPObject* WebPluginImpl::scriptableObject() {
285 return delegate_->GetPluginScriptableObject();
288 NPP WebPluginImpl::pluginNPP() {
292 bool WebPluginImpl::getFormValue(blink::WebString& value) {
295 base::string16 form_value;
296 if (!delegate_->GetFormValue(&form_value))
302 void WebPluginImpl::paint(WebCanvas* canvas, const WebRect& paint_rect) {
303 if (!delegate_ || !container_)
307 // Force a geometry update if needed to allow plugins like media player
308 // which defer the initial geometry update to work.
309 container_->reportGeometry();
312 // Note that |canvas| is only used when in windowless mode.
313 delegate_->Paint(canvas, paint_rect);
316 void WebPluginImpl::updateGeometry(
317 const WebRect& window_rect, const WebRect& clip_rect,
318 const WebVector<WebRect>& cutout_rects, bool is_visible) {
319 WebPluginGeometry new_geometry;
320 new_geometry.window = window_;
321 new_geometry.window_rect = window_rect;
322 new_geometry.clip_rect = clip_rect;
323 new_geometry.visible = is_visible;
324 new_geometry.rects_valid = true;
325 for (size_t i = 0; i < cutout_rects.size(); ++i)
326 new_geometry.cutout_rects.push_back(cutout_rects[i]);
328 // Only send DidMovePlugin if the geometry changed in some way.
329 if (window_ && (first_geometry_update_ || !new_geometry.Equals(geometry_))) {
330 render_frame_->GetRenderWidget()->SchedulePluginMove(new_geometry);
331 // We invalidate windowed plugins during the first geometry update to
332 // ensure that they get reparented to the wrapper window in the browser.
333 // This ensures that they become visible and are painted by the OS. This is
334 // required as some pages don't invalidate when the plugin is added.
335 if (first_geometry_update_ && window_) {
336 InvalidateRect(window_rect);
340 // Only UpdateGeometry if either the window or clip rects have changed.
341 if (delegate_ && (first_geometry_update_ ||
342 new_geometry.window_rect != geometry_.window_rect ||
343 new_geometry.clip_rect != geometry_.clip_rect)) {
344 // Notify the plugin that its parameters have changed.
345 delegate_->UpdateGeometry(new_geometry.window_rect, new_geometry.clip_rect);
348 // Initiate a download on the plugin url. This should be done for the
349 // first update geometry sequence. We need to ensure that the plugin
350 // receives the geometry update before it starts receiving data.
351 if (first_geometry_update_) {
352 // An empty url corresponds to an EMBED tag with no src attribute.
353 if (!load_manually_ && plugin_url_.is_valid()) {
354 // The Flash plugin hangs for a while if it receives data before
355 // receiving valid plugin geometry. By valid geometry we mean the
356 // geometry received by a call to setFrameRect in the Webkit
357 // layout code path. To workaround this issue we download the
358 // plugin source url on a timer.
359 base::MessageLoop::current()->PostTask(
361 base::Bind(&WebPluginImpl::OnDownloadPluginSrcUrl,
362 weak_factory_.GetWeakPtr()));
367 // Don't cache the geometry during the first geometry update. The first
368 // geometry update sequence is received when Widget::setParent is called.
369 // For plugins like media player which have a bug where they only honor
370 // the first geometry update, we have a quirk which ignores the first
371 // geometry update. To ensure that these plugins work correctly in cases
372 // where we receive only one geometry update from webkit, we also force
373 // a geometry update during paint which should go out correctly as the
374 // initial geometry update was not cached.
375 if (!first_geometry_update_)
376 geometry_ = new_geometry;
378 geometry_ = new_geometry;
380 first_geometry_update_ = false;
383 void WebPluginImpl::updateFocus(bool focused) {
384 if (accepts_input_events_)
385 delegate_->SetFocus(focused);
388 void WebPluginImpl::updateVisibility(bool visible) {
392 WebPluginGeometry move;
393 move.window = window_;
394 move.window_rect = gfx::Rect();
395 move.clip_rect = gfx::Rect();
396 move.rects_valid = false;
397 move.visible = visible;
399 render_frame_->GetRenderWidget()->SchedulePluginMove(move);
402 bool WebPluginImpl::acceptsInputEvents() {
403 return accepts_input_events_;
406 bool WebPluginImpl::handleInputEvent(
407 const WebInputEvent& event, WebCursorInfo& cursor_info) {
408 // Swallow context menu events in order to suppress the default context menu.
409 if (event.type == WebInputEvent::ContextMenu)
412 WebCursor::CursorInfo web_cursor_info;
413 bool ret = delegate_->HandleInputEvent(event, &web_cursor_info);
414 cursor_info.type = web_cursor_info.type;
415 cursor_info.hotSpot = web_cursor_info.hotspot;
416 cursor_info.customImage = web_cursor_info.custom_image;
417 cursor_info.imageScaleFactor = web_cursor_info.image_scale_factor;
419 cursor_info.externalHandle = web_cursor_info.external_handle;
424 void WebPluginImpl::didReceiveResponse(const WebURLResponse& response) {
425 ignore_response_error_ = false;
427 ResponseInfo response_info;
428 GetResponseInfo(response, &response_info);
430 delegate_->DidReceiveManualResponse(
432 response_info.mime_type,
433 GetAllHeaders(response),
434 response_info.expected_length,
435 response_info.last_modified);
438 void WebPluginImpl::didReceiveData(const char* data, int data_length) {
439 delegate_->DidReceiveManualData(data, data_length);
442 void WebPluginImpl::didFinishLoading() {
443 delegate_->DidFinishManualLoading();
446 void WebPluginImpl::didFailLoading(const WebURLError& error) {
447 if (!ignore_response_error_)
448 delegate_->DidManualLoadFail();
451 void WebPluginImpl::didFinishLoadingFrameRequest(
452 const WebURL& url, void* notify_data) {
454 // We're converting a void* into an arbitrary int id. Though
455 // these types are the same size on all the platforms we support,
456 // the compiler may complain as though they are different, so to
457 // make the casting gods happy go through an intptr_t (the union
458 // of void* and int) rather than converting straight across.
459 delegate_->DidFinishLoadWithReason(
460 url, NPRES_DONE, reinterpret_cast<intptr_t>(notify_data));
464 void WebPluginImpl::didFailLoadingFrameRequest(
465 const WebURL& url, void* notify_data, const WebURLError& error) {
470 error.reason == net::ERR_ABORTED ? NPRES_USER_BREAK : NPRES_NETWORK_ERR;
471 // See comment in didFinishLoadingFrameRequest about the cast here.
472 delegate_->DidFinishLoadWithReason(
473 url, reason, reinterpret_cast<intptr_t>(notify_data));
476 bool WebPluginImpl::isPlaceholder() {
480 WebPluginImpl::LoaderClient::LoaderClient(WebPluginImpl* parent)
483 void WebPluginImpl::LoaderClient::willSendRequest(
484 blink::WebURLLoader* loader, blink::WebURLRequest& newRequest,
485 const blink::WebURLResponse& redirectResponse) {
486 parent_->willSendRequest(loader, newRequest, redirectResponse);
489 void WebPluginImpl::LoaderClient::didSendData(
490 blink::WebURLLoader* loader, unsigned long long bytesSent,
491 unsigned long long totalBytesToBeSent) {
492 parent_->didSendData(loader, bytesSent, totalBytesToBeSent);
495 void WebPluginImpl::LoaderClient::didReceiveResponse(
496 blink::WebURLLoader* loader, const blink::WebURLResponse& response) {
497 parent_->didReceiveResponse(loader, response);
500 void WebPluginImpl::LoaderClient::didDownloadData(
501 blink::WebURLLoader* loader, int dataLength, int encodedDataLength) {
504 void WebPluginImpl::LoaderClient::didReceiveData(
505 blink::WebURLLoader* loader, const char* data,
506 int dataLength, int encodedDataLength) {
507 parent_->didReceiveData(loader, data, dataLength, encodedDataLength);
510 void WebPluginImpl::LoaderClient::didReceiveCachedMetadata(
511 blink::WebURLLoader* loader, const char* data, int dataLength) {
514 void WebPluginImpl::LoaderClient::didFinishLoading(
515 blink::WebURLLoader* loader, double finishTime,
516 int64_t total_encoded_data_length) {
517 parent_->didFinishLoading(loader, finishTime);
520 void WebPluginImpl::LoaderClient::didFail(
521 blink::WebURLLoader* loader, const blink::WebURLError& error) {
522 parent_->didFail(loader, error);
525 // -----------------------------------------------------------------------------
527 WebPluginImpl::WebPluginImpl(
529 const WebPluginParams& params,
530 const base::FilePath& file_path,
531 const base::WeakPtr<RenderViewImpl>& render_view,
532 RenderFrameImpl* render_frame)
533 : windowless_(false),
534 window_(gfx::kNullPluginWindow),
535 accepts_input_events_(false),
536 render_frame_(render_frame),
537 render_view_(render_view),
542 plugin_url_(params.url),
543 load_manually_(params.loadManually),
544 first_geometry_update_(true),
545 ignore_response_error_(false),
546 file_path_(file_path),
547 mime_type_(UTF16ToASCII(params.mimeType)),
549 loader_client_(this) {
550 DCHECK_EQ(params.attributeNames.size(), params.attributeValues.size());
551 StringToLowerASCII(&mime_type_);
553 for (size_t i = 0; i < params.attributeNames.size(); ++i) {
554 arg_names_.push_back(params.attributeNames[i].utf8());
555 arg_values_.push_back(params.attributeValues[i].utf8());
558 // Set subresource URL for crash reporting.
559 base::debug::SetCrashKeyValue("subresource_url", plugin_url_.spec());
562 WebPluginImpl::~WebPluginImpl() {
565 void WebPluginImpl::SetWindow(gfx::PluginWindowHandle window) {
567 DCHECK(!windowless_);
569 #if defined(OS_MACOSX)
570 // TODO(kbr): remove. http://crbug.com/105344
572 // Lie to ourselves about being windowless even if we got a fake
573 // plugin window handle, so we continue to get input events.
575 accepts_input_events_ = true;
576 // We do not really need to notify the page delegate that a plugin
577 // window was created -- so don't.
579 accepts_input_events_ = false;
582 // Tell the view delegate that the plugin window was created, so that it
583 // can create necessary container widgets.
584 render_frame_->Send(new ViewHostMsg_CreatePluginContainer(
585 render_frame_->GetRenderWidget()->routing_id(), window));
590 DCHECK(!window_); // Make sure not called twice.
592 accepts_input_events_ = true;
596 void WebPluginImpl::SetAcceptsInputEvents(bool accepts) {
597 accepts_input_events_ = accepts;
600 void WebPluginImpl::WillDestroyWindow(gfx::PluginWindowHandle window) {
601 DCHECK_EQ(window, window_);
602 window_ = gfx::kNullPluginWindow;
603 if (render_view_.get()) {
605 render_frame_->Send(new ViewHostMsg_DestroyPluginContainer(
606 render_frame_->GetRenderWidget()->routing_id(), window));
608 render_frame_->GetRenderWidget()->CleanupWindowInPluginMoves(window);
612 GURL WebPluginImpl::CompleteURL(const char* url) {
617 // TODO(darin): Is conversion from UTF8 correct here?
618 return webframe_->document().completeURL(WebString::fromUTF8(url));
621 void WebPluginImpl::CancelResource(unsigned long id) {
622 for (size_t i = 0; i < clients_.size(); ++i) {
623 if (clients_[i].id == id) {
624 if (clients_[i].loader.get()) {
625 clients_[i].loader->setDefersLoading(false);
626 clients_[i].loader->cancel();
634 bool WebPluginImpl::SetPostData(WebURLRequest* request,
637 std::vector<std::string> names;
638 std::vector<std::string> values;
639 std::vector<char> body;
640 bool rv = PluginHost::SetPostData(buf, length, &names, &values, &body);
642 for (size_t i = 0; i < names.size(); ++i) {
643 request->addHTTPHeaderField(WebString::fromUTF8(names[i]),
644 WebString::fromUTF8(values[i]));
647 WebString content_type_header = WebString::fromUTF8("Content-Type");
648 const WebString& content_type =
649 request->httpHeaderField(content_type_header);
650 if (content_type.isEmpty()) {
651 request->setHTTPHeaderField(
653 WebString::fromUTF8("application/x-www-form-urlencoded"));
656 WebHTTPBody http_body;
658 http_body.initialize();
659 http_body.appendData(WebData(&body[0], body.size()));
661 request->setHTTPBody(http_body);
666 bool WebPluginImpl::IsValidUrl(const GURL& url, Referrer referrer_flag) {
667 if (referrer_flag == PLUGIN_SRC &&
668 mime_type_ == kFlashPluginSwfMimeType &&
669 url.GetOrigin() != plugin_url_.GetOrigin()) {
670 // Do url check to make sure that there are no @, ;, \ chars in between url
671 // scheme and url path.
672 const char* url_to_check(url.spec().data());
673 url_parse::Parsed parsed;
674 url_parse::ParseStandardURL(url_to_check, strlen(url_to_check), &parsed);
675 if (parsed.path.begin <= parsed.scheme.end())
677 std::string string_to_search;
678 string_to_search.assign(url_to_check + parsed.scheme.end(),
679 parsed.path.begin - parsed.scheme.end());
680 if (string_to_search.find("@") != std::string::npos ||
681 string_to_search.find(";") != std::string::npos ||
682 string_to_search.find("\\") != std::string::npos)
689 WebPluginImpl::RoutingStatus WebPluginImpl::RouteToFrame(
691 bool is_javascript_url,
698 Referrer referrer_flag) {
699 // If there is no target, there is nothing to do
703 // This could happen if the WebPluginContainer was already deleted.
707 WebString target_str = WebString::fromUTF8(target);
709 // Take special action for JavaScript URLs
710 if (is_javascript_url) {
711 WebFrame* target_frame =
712 webframe_->view()->findFrameByName(target_str, webframe_);
713 // For security reasons, do not allow JavaScript on frames
714 // other than this frame.
715 if (target_frame != webframe_) {
716 // TODO(darin): Localize this message.
717 const char kMessage[] =
718 "Ignoring cross-frame javascript URL load requested by plugin.";
719 webframe_->addMessageToConsole(
720 WebConsoleMessage(WebConsoleMessage::LevelError,
721 WebString::fromUTF8(kMessage)));
725 // Route javascript calls back to the plugin.
729 // If we got this far, we're routing content to a target frame.
732 GURL complete_url = CompleteURL(url);
733 // Remove when flash bug is fixed. http://crbug.com/40016.
734 if (!WebPluginImpl::IsValidUrl(complete_url, referrer_flag))
737 if (strcmp(method, "GET") != 0) {
738 // We're only going to route HTTP/HTTPS requests
739 if (!complete_url.SchemeIsHTTPOrHTTPS())
743 WebURLRequest request(complete_url);
744 SetReferrer(&request, referrer_flag);
746 request.setHTTPMethod(WebString::fromUTF8(method));
747 request.setFirstPartyForCookies(
748 webframe_->document().firstPartyForCookies());
749 request.setHasUserGesture(popups_allowed);
751 if (!SetPostData(&request, buf, len)) {
752 // Uhoh - we're in trouble. There isn't a good way
753 // to recover at this point. Break out.
759 container_->loadFrameRequest(
760 request, target_str, notify_id != 0, reinterpret_cast<void*>(notify_id));
764 NPObject* WebPluginImpl::GetWindowScriptNPObject() {
769 return webframe_->windowObject();
772 NPObject* WebPluginImpl::GetPluginElement() {
773 return container_->scriptableObjectForElement();
776 bool WebPluginImpl::FindProxyForUrl(const GURL& url, std::string* proxy_list) {
777 // Proxy resolving doesn't work in single-process mode.
781 void WebPluginImpl::SetCookie(const GURL& url,
782 const GURL& first_party_for_cookies,
783 const std::string& cookie) {
784 if (!render_view_.get())
787 WebCookieJar* cookie_jar = render_frame_->cookie_jar();
789 DLOG(WARNING) << "No cookie jar!";
793 cookie_jar->setCookie(
794 url, first_party_for_cookies, WebString::fromUTF8(cookie));
797 std::string WebPluginImpl::GetCookies(const GURL& url,
798 const GURL& first_party_for_cookies) {
799 if (!render_view_.get())
800 return std::string();
802 WebCookieJar* cookie_jar = render_frame_->cookie_jar();
804 DLOG(WARNING) << "No cookie jar!";
805 return std::string();
808 return base::UTF16ToUTF8(cookie_jar->cookies(url, first_party_for_cookies));
811 void WebPluginImpl::URLRedirectResponse(bool allow, int resource_id) {
812 for (size_t i = 0; i < clients_.size(); ++i) {
813 if (clients_[i].id == static_cast<unsigned long>(resource_id)) {
814 if (clients_[i].loader.get()) {
816 clients_[i].loader->setDefersLoading(false);
818 clients_[i].loader->cancel();
819 if (clients_[i].client)
820 clients_[i].client->DidFail(clients_[i].id);
828 bool WebPluginImpl::CheckIfRunInsecureContent(const GURL& url) {
832 return webframe_->checkIfRunInsecureContent(url);
835 #if defined(OS_MACOSX)
836 WebPluginAcceleratedSurface* WebPluginImpl::GetAcceleratedSurface(
837 gfx::GpuPreference gpu_preference) {
841 void WebPluginImpl::AcceleratedPluginEnabledRendering() {
844 void WebPluginImpl::AcceleratedPluginAllocatedIOSurface(int32 width,
847 next_io_surface_allocated_ = true;
848 next_io_surface_width_ = width;
849 next_io_surface_height_ = height;
850 next_io_surface_id_ = surface_id;
853 void WebPluginImpl::AcceleratedPluginSwappedIOSurface() {
856 // Deferring the call to setBackingIOSurfaceId is an attempt to
857 // work around garbage occasionally showing up in the plugin's
858 // area during live resizing of Core Animation plugins. The
859 // assumption was that by the time this was called, the plugin
860 // process would have populated the newly allocated IOSurface. It
861 // is not 100% clear at this point why any garbage is getting
862 // through. More investigation is needed. http://crbug.com/105346
863 if (next_io_surface_allocated_) {
864 if (next_io_surface_id_) {
865 if (!io_surface_layer_.get()) {
866 io_surface_layer_ = cc::IOSurfaceLayer::Create();
867 web_layer_.reset(new webkit::WebLayerImpl(io_surface_layer_));
868 container_->setWebLayer(web_layer_.get());
870 io_surface_layer_->SetIOSurfaceProperties(
872 gfx::Size(next_io_surface_width_, next_io_surface_height_));
874 container_->setWebLayer(NULL);
876 io_surface_layer_ = NULL;
878 next_io_surface_allocated_ = false;
880 if (io_surface_layer_.get())
881 io_surface_layer_->SetNeedsDisplay();
886 void WebPluginImpl::Invalidate() {
888 container_->invalidate();
891 void WebPluginImpl::InvalidateRect(const gfx::Rect& rect) {
893 container_->invalidateRect(rect);
896 void WebPluginImpl::OnDownloadPluginSrcUrl() {
897 HandleURLRequestInternal(
898 plugin_url_.spec().c_str(), "GET", NULL, NULL, 0, 0, false, DOCUMENT_URL,
902 WebPluginResourceClient* WebPluginImpl::GetClientFromLoader(
903 WebURLLoader* loader) {
904 ClientInfo* client_info = GetClientInfoFromLoader(loader);
906 return client_info->client;
910 WebPluginImpl::ClientInfo* WebPluginImpl::GetClientInfoFromLoader(
911 WebURLLoader* loader) {
912 for (size_t i = 0; i < clients_.size(); ++i) {
913 if (clients_[i].loader.get() == loader)
921 void WebPluginImpl::willSendRequest(WebURLLoader* loader,
922 WebURLRequest& request,
923 const WebURLResponse& response) {
924 // TODO(jam): THIS LOGIC IS COPIED IN PluginURLFetcher::OnReceivedRedirect
925 // until kDirectNPAPIRequests is the default and we can remove this old path.
926 WebPluginImpl::ClientInfo* client_info = GetClientInfoFromLoader(loader);
928 // Currently this check is just to catch an https -> http redirect when
929 // loading the main plugin src URL. Longer term, we could investigate
930 // firing mixed diplay or scripting issues for subresource loads
931 // initiated by plug-ins.
932 if (client_info->is_plugin_src_load &&
934 !webframe_->checkIfRunInsecureContent(request.url())) {
936 client_info->client->DidFail(client_info->id);
939 if (net::HttpResponseHeaders::IsRedirectResponseCode(
940 response.httpStatusCode())) {
941 // If the plugin does not participate in url redirect notifications then
942 // just block cross origin 307 POST redirects.
943 if (!client_info->notify_redirects) {
944 if (response.httpStatusCode() == 307 &&
945 LowerCaseEqualsASCII(request.httpMethod().utf8(), "post")) {
946 GURL original_request_url(response.url());
947 GURL response_url(request.url());
948 if (original_request_url.GetOrigin() != response_url.GetOrigin()) {
949 loader->setDefersLoading(true);
951 client_info->client->DidFail(client_info->id);
956 loader->setDefersLoading(true);
959 client_info->client->WillSendRequest(request.url(),
960 response.httpStatusCode());
964 void WebPluginImpl::didSendData(WebURLLoader* loader,
965 unsigned long long bytes_sent,
966 unsigned long long total_bytes_to_be_sent) {
969 void WebPluginImpl::didReceiveResponse(WebURLLoader* loader,
970 const WebURLResponse& response) {
971 // TODO(jam): THIS LOGIC IS COPIED IN PluginURLFetcher::OnReceivedResponse
972 // until kDirectNPAPIRequests is the default and we can remove this old path.
973 static const int kHttpPartialResponseStatusCode = 206;
974 static const int kHttpResponseSuccessStatusCode = 200;
976 WebPluginResourceClient* client = GetClientFromLoader(loader);
980 ResponseInfo response_info;
981 GetResponseInfo(response, &response_info);
982 ClientInfo* client_info = GetClientInfoFromLoader(loader);
986 bool request_is_seekable = true;
987 if (client->IsMultiByteResponseExpected()) {
988 if (response.httpStatusCode() == kHttpPartialResponseStatusCode) {
989 ClientInfo* client_info = GetClientInfoFromLoader(loader);
992 if (HandleHttpMultipartResponse(response, client)) {
993 // Multiple ranges requested, data will be delivered by
994 // MultipartResponseDelegate.
995 client_info->data_offset = 0;
998 int64 upper_bound = 0, instance_size = 0;
999 // Single range requested - go through original processing for
1000 // non-multipart requests, but update data offset.
1001 MultipartResponseDelegate::ReadContentRanges(response,
1002 &client_info->data_offset,
1005 } else if (response.httpStatusCode() == kHttpResponseSuccessStatusCode) {
1006 RenderThreadImpl::current()->RecordAction(
1007 base::UserMetricsAction("Plugin_200ForByteRange"));
1008 // If the client issued a byte range request and the server responds with
1009 // HTTP 200 OK, it indicates that the server does not support byte range
1011 // We need to emulate Firefox behavior by doing the following:-
1012 // 1. Destroy the plugin instance in the plugin process. Ensure that
1013 // existing resource requests initiated for the plugin instance
1014 // continue to remain valid.
1015 // 2. Create a new plugin instance and notify it about the response
1017 if (!ReinitializePluginForResponse(loader)) {
1022 // The server does not support byte range requests. No point in creating
1023 // seekable streams.
1024 request_is_seekable = false;
1029 // Create a new resource client for this request.
1030 for (size_t i = 0; i < clients_.size(); ++i) {
1031 if (clients_[i].loader.get() == loader) {
1032 WebPluginResourceClient* resource_client =
1033 delegate_->CreateResourceClient(clients_[i].id, plugin_url_, 0);
1034 clients_[i].client = resource_client;
1035 client = resource_client;
1040 DCHECK(client != NULL);
1044 // Calling into a plugin could result in reentrancy if the plugin yields
1045 // control to the OS like entering a modal loop etc. Prevent this by
1046 // stopping further loading until the plugin notifies us that it is ready to
1048 loader->setDefersLoading(true);
1050 client->DidReceiveResponse(
1051 response_info.mime_type,
1052 GetAllHeaders(response),
1053 response_info.expected_length,
1054 response_info.last_modified,
1055 request_is_seekable);
1057 // Bug http://b/issue?id=925559. The flash plugin would not handle the HTTP
1058 // error codes in the stream header and as a result, was unaware of the
1059 // fate of the HTTP requests issued via NPN_GetURLNotify. Webkit and FF
1060 // destroy the stream and invoke the NPP_DestroyStream function on the
1061 // plugin if the HTTP request fails.
1062 const GURL& url = response.url();
1063 if (url.SchemeIs("http") || url.SchemeIs("https")) {
1064 if (response.httpStatusCode() < 100 || response.httpStatusCode() >= 400) {
1065 // The plugin instance could be in the process of deletion here.
1066 // Verify if the WebPluginResourceClient instance still exists before
1068 ClientInfo* client_info = GetClientInfoFromLoader(loader);
1070 client_info->pending_failure_notification = true;
1076 void WebPluginImpl::didReceiveData(WebURLLoader* loader,
1079 int encoded_data_length) {
1080 WebPluginResourceClient* client = GetClientFromLoader(loader);
1084 MultiPartResponseHandlerMap::iterator index =
1085 multi_part_response_map_.find(client);
1086 if (index != multi_part_response_map_.end()) {
1087 MultipartResponseDelegate* multi_part_handler = (*index).second;
1088 DCHECK(multi_part_handler != NULL);
1089 multi_part_handler->OnReceivedData(buffer,
1091 encoded_data_length);
1093 loader->setDefersLoading(true);
1094 ClientInfo* client_info = GetClientInfoFromLoader(loader);
1095 client->DidReceiveData(buffer, data_length, client_info->data_offset);
1096 client_info->data_offset += data_length;
1100 void WebPluginImpl::didFinishLoading(WebURLLoader* loader, double finishTime) {
1101 ClientInfo* client_info = GetClientInfoFromLoader(loader);
1102 if (client_info && client_info->client) {
1103 MultiPartResponseHandlerMap::iterator index =
1104 multi_part_response_map_.find(client_info->client);
1105 if (index != multi_part_response_map_.end()) {
1106 delete (*index).second;
1107 multi_part_response_map_.erase(index);
1110 loader->setDefersLoading(true);
1111 WebPluginResourceClient* resource_client = client_info->client;
1112 // The ClientInfo can get deleted in the call to DidFinishLoading below.
1113 // It is not safe to access this structure after that.
1114 client_info->client = NULL;
1115 resource_client->DidFinishLoading(client_info->id);
1119 void WebPluginImpl::didFail(WebURLLoader* loader,
1120 const WebURLError& error) {
1121 ClientInfo* client_info = GetClientInfoFromLoader(loader);
1122 if (client_info && client_info->client) {
1123 loader->setDefersLoading(true);
1124 WebPluginResourceClient* resource_client = client_info->client;
1125 // The ClientInfo can get deleted in the call to DidFail below.
1126 // It is not safe to access this structure after that.
1127 client_info->client = NULL;
1128 resource_client->DidFail(client_info->id);
1132 void WebPluginImpl::RemoveClient(size_t i) {
1133 clients_.erase(clients_.begin() + i);
1136 void WebPluginImpl::RemoveClient(WebURLLoader* loader) {
1137 for (size_t i = 0; i < clients_.size(); ++i) {
1138 if (clients_[i].loader.get() == loader) {
1145 void WebPluginImpl::SetContainer(WebPluginContainer* container) {
1147 TearDownPluginInstance(NULL);
1148 container_ = container;
1150 container_->allowScriptObjects();
1153 void WebPluginImpl::HandleURLRequest(const char* url,
1159 bool popups_allowed,
1160 bool notify_redirects) {
1161 // GetURL/PostURL requests initiated explicitly by plugins should specify the
1162 // plugin SRC url as the referrer if it is available.
1163 HandleURLRequestInternal(
1164 url, method, target, buf, len, notify_id, popups_allowed, PLUGIN_SRC,
1165 notify_redirects, false);
1168 void WebPluginImpl::HandleURLRequestInternal(const char* url,
1174 bool popups_allowed,
1175 Referrer referrer_flag,
1176 bool notify_redirects,
1177 bool is_plugin_src_load) {
1178 // For this request, we either route the output to a frame
1179 // because a target has been specified, or we handle the request
1180 // here, i.e. by executing the script if it is a javascript url
1181 // or by initiating a download on the URL, etc. There is one special
1182 // case in that the request is a javascript url and the target is "_self",
1183 // in which case we route the output to the plugin rather than routing it
1184 // to the plugin's frame.
1185 bool is_javascript_url = url_util::FindAndCompareScheme(
1186 url, strlen(url), "javascript", NULL);
1187 RoutingStatus routing_status = RouteToFrame(
1188 url, is_javascript_url, popups_allowed, method, target, buf, len,
1189 notify_id, referrer_flag);
1190 if (routing_status == ROUTED)
1193 if (is_javascript_url) {
1195 WebString result = container_->executeScriptURL(gurl, popups_allowed);
1197 // delegate_ could be NULL because executeScript caused the container to
1200 delegate_->SendJavaScriptStream(
1201 gurl, result.utf8(), !result.isNull(), notify_id);
1207 unsigned long resource_id = GetNextResourceId();
1211 GURL complete_url = CompleteURL(url);
1212 // Remove when flash bug is fixed. http://crbug.com/40016.
1213 if (!WebPluginImpl::IsValidUrl(complete_url, referrer_flag))
1216 // If the RouteToFrame call returned a failure then inform the result
1217 // back to the plugin asynchronously.
1218 if ((routing_status == INVALID_URL) ||
1219 (routing_status == GENERAL_FAILURE)) {
1220 WebPluginResourceClient* resource_client = delegate_->CreateResourceClient(
1221 resource_id, complete_url, notify_id);
1222 if (resource_client)
1223 resource_client->DidFail(resource_id);
1227 // CreateResourceClient() sends a synchronous IPC message so it's possible
1228 // that TearDownPluginInstance() may have been called in the nested
1229 // message loop. If so, don't start the request.
1233 if (!CommandLine::ForCurrentProcess()->HasSwitch(
1234 switches::kDisableDirectNPAPIRequests)) {
1235 // We got here either because the plugin called GetURL/PostURL, or because
1236 // we're fetching the data for an embed tag. If we're in multi-process mode,
1237 // we want to fetch the data in the plugin process as the renderer won't be
1238 // able to request any origin when site isolation is in place. So bounce
1239 // this request back to the plugin process which will use ResourceDispatcher
1240 // to fetch the url.
1242 // TODO(jam): any better way of getting this? Can't find a way to get
1243 // frame()->loader()->outgoingReferrer() which
1244 // WebFrameImpl::setReferrerForRequest does.
1245 WebURLRequest request(complete_url);
1246 SetReferrer(&request, referrer_flag);
1248 request.httpHeaderField(WebString::fromUTF8("Referer")).utf8());
1250 GURL first_party_for_cookies = webframe_->document().firstPartyForCookies();
1251 delegate_->FetchURL(resource_id, notify_id, complete_url,
1252 first_party_for_cookies, method, buf, len, referrer,
1253 notify_redirects, is_plugin_src_load, 0,
1254 render_frame_->GetRoutingID(),
1255 render_view_->GetRoutingID());
1257 WebPluginResourceClient* resource_client = delegate_->CreateResourceClient(
1258 resource_id, complete_url, notify_id);
1259 if (!resource_client)
1261 InitiateHTTPRequest(resource_id, resource_client, complete_url, method, buf,
1262 len, NULL, referrer_flag, notify_redirects,
1263 is_plugin_src_load);
1267 unsigned long WebPluginImpl::GetNextResourceId() {
1270 WebView* view = webframe_->view();
1273 return view->createUniqueIdentifierForRequest();
1276 bool WebPluginImpl::InitiateHTTPRequest(unsigned long resource_id,
1277 WebPluginResourceClient* client,
1282 const char* range_info,
1283 Referrer referrer_flag,
1284 bool notify_redirects,
1285 bool is_plugin_src_load) {
1292 info.id = resource_id;
1293 info.client = client;
1294 info.request.initialize();
1295 info.request.setURL(url);
1296 info.request.setFirstPartyForCookies(
1297 webframe_->document().firstPartyForCookies());
1298 info.request.setRequestorProcessID(delegate_->GetProcessId());
1299 info.request.setTargetType(WebURLRequest::TargetIsObject);
1300 info.request.setHTTPMethod(WebString::fromUTF8(method));
1301 info.pending_failure_notification = false;
1302 info.notify_redirects = notify_redirects;
1303 info.is_plugin_src_load = is_plugin_src_load;
1304 info.data_offset = 0;
1307 info.request.addHTTPHeaderField(WebString::fromUTF8("Range"),
1308 WebString::fromUTF8(range_info));
1311 if (strcmp(method, "POST") == 0) {
1312 // Adds headers or form data to a request. This must be called before
1313 // we initiate the actual request.
1314 SetPostData(&info.request, buf, buf_len);
1317 SetReferrer(&info.request, referrer_flag);
1319 WebURLLoaderOptions options;
1320 options.allowCredentials = true;
1321 options.crossOriginRequestPolicy =
1322 WebURLLoaderOptions::CrossOriginRequestPolicyAllow;
1323 info.loader.reset(webframe_->createAssociatedURLLoader(options));
1324 if (!info.loader.get())
1326 info.loader->loadAsynchronously(info.request, &loader_client_);
1328 clients_.push_back(info);
1332 void WebPluginImpl::CancelDocumentLoad() {
1334 ignore_response_error_ = true;
1335 webframe_->stopLoading();
1339 void WebPluginImpl::InitiateHTTPRangeRequest(
1340 const char* url, const char* range_info, int range_request_id) {
1341 unsigned long resource_id = GetNextResourceId();
1345 GURL complete_url = CompleteURL(url);
1346 // Remove when flash bug is fixed. http://crbug.com/40016.
1347 if (!WebPluginImpl::IsValidUrl(complete_url,
1348 load_manually_ ? NO_REFERRER : PLUGIN_SRC))
1351 WebPluginResourceClient* resource_client =
1352 delegate_->CreateSeekableResourceClient(resource_id, range_request_id);
1353 InitiateHTTPRequest(
1354 resource_id, resource_client, complete_url, "GET", NULL, 0, range_info,
1355 load_manually_ ? NO_REFERRER : PLUGIN_SRC, false, false);
1358 void WebPluginImpl::DidStartLoading() {
1359 if (render_view_.get()) {
1360 // TODO(darin): Make is_loading_ be a counter!
1361 render_view_->didStartLoading();
1365 void WebPluginImpl::DidStopLoading() {
1366 if (render_view_.get()) {
1367 // TODO(darin): Make is_loading_ be a counter!
1368 render_view_->didStopLoading();
1372 void WebPluginImpl::SetDeferResourceLoading(unsigned long resource_id,
1374 std::vector<ClientInfo>::iterator client_index = clients_.begin();
1375 while (client_index != clients_.end()) {
1376 ClientInfo& client_info = *client_index;
1378 if (client_info.id == resource_id) {
1379 client_info.loader->setDefersLoading(defer);
1381 // If we determined that the request had failed via the HTTP headers
1382 // in the response then we send out a failure notification to the
1383 // plugin process, as certain plugins don't handle HTTP failure codes
1385 if (!defer && client_info.client &&
1386 client_info.pending_failure_notification) {
1387 // The ClientInfo and the iterator can become invalid due to the call
1388 // to DidFail below.
1389 WebPluginResourceClient* resource_client = client_info.client;
1390 client_info.loader->cancel();
1391 clients_.erase(client_index++);
1392 resource_client->DidFail(resource_id);
1400 bool WebPluginImpl::IsOffTheRecord() {
1404 bool WebPluginImpl::HandleHttpMultipartResponse(
1405 const WebURLResponse& response, WebPluginResourceClient* client) {
1406 std::string multipart_boundary;
1407 if (!MultipartResponseDelegate::ReadMultipartBoundary(
1408 response, &multipart_boundary)) {
1414 MultiPartResponseClient* multi_part_response_client =
1415 new MultiPartResponseClient(client);
1417 MultipartResponseDelegate* multi_part_response_handler =
1418 new MultipartResponseDelegate(multi_part_response_client, NULL,
1420 multipart_boundary);
1421 multi_part_response_map_[client] = multi_part_response_handler;
1425 bool WebPluginImpl::ReinitializePluginForResponse(
1426 WebURLLoader* loader) {
1427 WebFrame* webframe = webframe_;
1431 WebView* webview = webframe->view();
1435 WebPluginContainer* container_widget = container_;
1437 // Destroy the current plugin instance.
1438 TearDownPluginInstance(loader);
1440 container_ = container_widget;
1441 webframe_ = webframe;
1443 WebPluginDelegateProxy* plugin_delegate = new WebPluginDelegateProxy(
1444 this, mime_type_, render_view_, render_frame_);
1446 // Store the plugin's unique identifier, used by the container to track its
1447 // script objects, and enable script objects (since Initialize may use them
1448 // even if it fails).
1449 npp_ = plugin_delegate->GetPluginNPP();
1450 container_->allowScriptObjects();
1452 bool ok = plugin_delegate && plugin_delegate->Initialize(
1453 plugin_url_, arg_names_, arg_values_, load_manually_);
1456 container_->clearScriptObjects();
1458 // TODO(iyengar) Should we delete the current plugin instance here?
1462 delegate_ = plugin_delegate;
1464 // Force a geometry update to occur to ensure that the plugin becomes
1466 container_->reportGeometry();
1468 // The plugin move sequences accumulated via DidMove are sent to the browser
1469 // whenever the renderer paints. Force a paint here to ensure that changes
1470 // to the plugin window are propagated to the browser.
1471 container_->invalidate();
1475 void WebPluginImpl::TearDownPluginInstance(
1476 WebURLLoader* loader_to_ignore) {
1477 // JavaScript garbage collection may cause plugin script object references to
1478 // be retained long after the plugin is destroyed. Some plugins won't cope
1479 // with their objects being released after they've been destroyed, and once
1480 // we've actually unloaded the plugin the object's releaseobject() code may
1481 // no longer be in memory. The container tracks the plugin's objects and lets
1482 // us invalidate them, releasing the references to them held by the JavaScript
1485 container_->clearScriptObjects();
1486 container_->setWebLayer(NULL);
1489 // Call PluginDestroyed() first to prevent the plugin from calling us back
1490 // in the middle of tearing down the render tree.
1492 // The plugin may call into the browser and pass script objects even during
1493 // teardown, so temporarily re-enable plugin script objects.
1495 container_->allowScriptObjects();
1497 delegate_->PluginDestroyed();
1500 // Invalidate any script objects created during teardown here, before the
1501 // plugin might actually be unloaded.
1502 container_->clearScriptObjects();
1505 // Cancel any pending requests because otherwise this deleted object will
1506 // be called by the ResourceDispatcher.
1507 std::vector<ClientInfo>::iterator client_index = clients_.begin();
1508 while (client_index != clients_.end()) {
1509 ClientInfo& client_info = *client_index;
1511 if (loader_to_ignore == client_info.loader) {
1516 if (client_info.loader.get())
1517 client_info.loader->cancel();
1519 client_index = clients_.erase(client_index);
1522 // This needs to be called now and not in the destructor since the
1523 // webframe_ might not be valid anymore.
1525 weak_factory_.InvalidateWeakPtrs();
1528 void WebPluginImpl::SetReferrer(blink::WebURLRequest* request,
1529 Referrer referrer_flag) {
1530 switch (referrer_flag) {
1532 webframe_->setReferrerForRequest(*request, GURL());
1536 webframe_->setReferrerForRequest(*request, plugin_url_);
1544 } // namespace content