Home | History | Annotate | Download | only in npapi
      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/child/npapi/plugin_stream_url.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "base/strings/string_util.h"
     10 #include "content/child/npapi/plugin_host.h"
     11 #include "content/child/npapi/plugin_instance.h"
     12 #include "content/child/npapi/plugin_lib.h"
     13 #include "content/child/npapi/plugin_url_fetcher.h"
     14 #include "content/child/npapi/webplugin.h"
     15 #include "net/http/http_response_headers.h"
     16 
     17 namespace content {
     18 
     19 PluginStreamUrl::PluginStreamUrl(
     20     unsigned long resource_id,
     21     const GURL &url,
     22     PluginInstance *instance,
     23     bool notify_needed,
     24     void *notify_data)
     25     : PluginStream(instance, url.spec().c_str(), notify_needed, notify_data),
     26       url_(url),
     27       id_(resource_id) {
     28 }
     29 
     30 void PluginStreamUrl::SetPluginURLFetcher(PluginURLFetcher* fetcher) {
     31   plugin_url_fetcher_.reset(fetcher);
     32 }
     33 
     34 void PluginStreamUrl::URLRedirectResponse(bool allow) {
     35   if (plugin_url_fetcher_.get()) {
     36     plugin_url_fetcher_->URLRedirectResponse(allow);
     37   } else {
     38     instance()->webplugin()->URLRedirectResponse(allow, id_);
     39   }
     40 
     41   if (allow)
     42     UpdateUrl(pending_redirect_url_.c_str());
     43 }
     44 
     45 bool PluginStreamUrl::Close(NPReason reason) {
     46   // Protect the stream against it being destroyed or the whole plugin instance
     47   // being destroyed within the destroy stream handler.
     48   scoped_refptr<PluginStream> protect(this);
     49   CancelRequest();
     50   bool result = PluginStream::Close(reason);
     51   instance()->RemoveStream(this);
     52   return result;
     53 }
     54 
     55 WebPluginResourceClient* PluginStreamUrl::AsResourceClient() {
     56   return static_cast<WebPluginResourceClient*>(this);
     57 }
     58 
     59 void PluginStreamUrl::CancelRequest() {
     60   if (id_ > 0) {
     61     if (plugin_url_fetcher_.get()) {
     62       plugin_url_fetcher_->Cancel();
     63     } else {
     64       if (instance()->webplugin()) {
     65         instance()->webplugin()->CancelResource(id_);
     66       }
     67     }
     68     id_ = 0;
     69   }
     70   if (instance()->webplugin()) {
     71     for (size_t i = 0; i < range_requests_.size(); ++i)
     72       instance()->webplugin()->CancelResource(range_requests_[i]);
     73   }
     74   range_requests_.clear();
     75 }
     76 
     77 void PluginStreamUrl::WillSendRequest(const GURL& url, int http_status_code) {
     78   if (notify_needed()) {
     79     // If the plugin participates in HTTP url redirect handling then notify it.
     80     if (net::HttpResponseHeaders::IsRedirectResponseCode(http_status_code) &&
     81         instance()->handles_url_redirects()) {
     82       pending_redirect_url_ = url.spec();
     83       instance()->NPP_URLRedirectNotify(url.spec().c_str(), http_status_code,
     84           notify_data());
     85       return;
     86     }
     87   }
     88   url_ = url;
     89   UpdateUrl(url.spec().c_str());
     90 }
     91 
     92 void PluginStreamUrl::DidReceiveResponse(const std::string& mime_type,
     93                                          const std::string& headers,
     94                                          uint32 expected_length,
     95                                          uint32 last_modified,
     96                                          bool request_is_seekable) {
     97   // Protect the stream against it being destroyed or the whole plugin instance
     98   // being destroyed within the new stream handler.
     99   scoped_refptr<PluginStream> protect(this);
    100 
    101   bool opened = Open(mime_type,
    102                      headers,
    103                      expected_length,
    104                      last_modified,
    105                      request_is_seekable);
    106   if (!opened) {
    107     CancelRequest();
    108     instance()->RemoveStream(this);
    109   } else {
    110     SetDeferLoading(false);
    111   }
    112 }
    113 
    114 void PluginStreamUrl::DidReceiveData(const char* buffer, int length,
    115                                      int data_offset) {
    116   if (!open())
    117     return;
    118 
    119   // Protect the stream against it being destroyed or the whole plugin instance
    120   // being destroyed within the write handlers
    121   scoped_refptr<PluginStream> protect(this);
    122 
    123   if (length > 0) {
    124     // The PluginStreamUrl instance could get deleted if the plugin fails to
    125     // accept data in NPP_Write.
    126     if (Write(const_cast<char*>(buffer), length, data_offset) > 0) {
    127       SetDeferLoading(false);
    128     }
    129   }
    130 }
    131 
    132 void PluginStreamUrl::DidFinishLoading(unsigned long resource_id) {
    133   if (!seekable()) {
    134     Close(NPRES_DONE);
    135   } else {
    136     std::vector<unsigned long>::iterator it_resource = std::find(
    137         range_requests_.begin(),
    138         range_requests_.end(),
    139         resource_id);
    140     // Resource id must be known to us - either main resource id, or one
    141     // of the resources, created for range requests.
    142     DCHECK(resource_id == id_ || it_resource != range_requests_.end());
    143     // We should notify the plugin about failed/finished requests to ensure
    144     // that the number of active resource clients does not continue to grow.
    145     if (instance()->webplugin())
    146       instance()->webplugin()->CancelResource(resource_id);
    147     if (it_resource != range_requests_.end())
    148       range_requests_.erase(it_resource);
    149   }
    150 }
    151 
    152 void PluginStreamUrl::DidFail(unsigned long resource_id) {
    153   Close(NPRES_NETWORK_ERR);
    154 }
    155 
    156 bool PluginStreamUrl::IsMultiByteResponseExpected() {
    157   return seekable();
    158 }
    159 
    160 int PluginStreamUrl::ResourceId() {
    161   return id_;
    162 }
    163 
    164 PluginStreamUrl::~PluginStreamUrl() {
    165   if (!plugin_url_fetcher_.get() && instance() && instance()->webplugin()) {
    166     instance()->webplugin()->ResourceClientDeleted(AsResourceClient());
    167   }
    168 }
    169 
    170 void PluginStreamUrl::AddRangeRequestResourceId(unsigned long resource_id) {
    171   DCHECK_NE(resource_id, 0u);
    172   range_requests_.push_back(resource_id);
    173 }
    174 
    175 void PluginStreamUrl::SetDeferLoading(bool value) {
    176   // If we determined that the request had failed via the HTTP headers in the
    177   // response then we send out a failure notification to the plugin process, as
    178   // certain plugins don't handle HTTP failure codes correctly.
    179   if (!value &&
    180       plugin_url_fetcher_.get() &&
    181       plugin_url_fetcher_->pending_failure_notification()) {
    182     // This object may be deleted now.
    183     DidFail(id_);
    184     return;
    185   }
    186   if (id_ > 0)
    187     instance()->webplugin()->SetDeferResourceLoading(id_, value);
    188   for (size_t i = 0; i < range_requests_.size(); ++i)
    189     instance()->webplugin()->SetDeferResourceLoading(range_requests_[i],
    190                                                      value);
    191 }
    192 
    193 void PluginStreamUrl::UpdateUrl(const char* url) {
    194   DCHECK(!open());
    195   free(const_cast<char*>(stream()->url));
    196   stream()->url = base::strdup(url);
    197   pending_redirect_url_.clear();
    198 }
    199 
    200 }  // namespace content
    201