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