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 |