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