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