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