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