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 |