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