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