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