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