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