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
17namespace content {
18
19PluginStreamUrl::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
30void PluginStreamUrl::SetPluginURLFetcher(PluginURLFetcher* fetcher) {
31  plugin_url_fetcher_.reset(fetcher);
32}
33
34void 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
45bool 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
55WebPluginResourceClient* PluginStreamUrl::AsResourceClient() {
56  return static_cast<WebPluginResourceClient*>(this);
57}
58
59void 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
77void 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
92void 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
114void 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
132void 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
152void PluginStreamUrl::DidFail(unsigned long resource_id) {
153  Close(NPRES_NETWORK_ERR);
154}
155
156bool PluginStreamUrl::IsMultiByteResponseExpected() {
157  return seekable();
158}
159
160int PluginStreamUrl::ResourceId() {
161  return id_;
162}
163
164PluginStreamUrl::~PluginStreamUrl() {
165  if (!plugin_url_fetcher_.get() && instance() && instance()->webplugin()) {
166    instance()->webplugin()->ResourceClientDeleted(AsResourceClient());
167  }
168}
169
170void PluginStreamUrl::AddRangeRequestResourceId(unsigned long resource_id) {
171  DCHECK_NE(resource_id, 0u);
172  range_requests_.push_back(resource_id);
173}
174
175void 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
193void 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