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