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