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