Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
|
mmenke
2016/06/09 18:09:29
Weird that git isn't picking up that this file was
clamy
2016/06/21 16:14:15
Done.
| |
| 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/browser/loader/replacing_resource_handler.h" | |
| 6 | |
| 7 #include <utility> | |
| 8 #include <vector> | |
| 9 | |
| 10 #include "base/bind.h" | |
| 11 #include "base/location.h" | |
| 12 #include "base/logging.h" | |
| 13 #include "base/metrics/histogram_macros.h" | |
| 14 #include "base/single_thread_task_runner.h" | |
| 15 #include "base/strings/string_util.h" | |
| 16 #include "base/threading/thread_task_runner_handle.h" | |
| 17 #include "components/mime_util/mime_util.h" | |
| 18 #include "content/browser/download/download_resource_handler.h" | |
| 19 #include "content/browser/download/download_stats.h" | |
| 20 #include "content/browser/loader/resource_dispatcher_host_impl.h" | |
| 21 #include "content/browser/loader/resource_request_info_impl.h" | |
| 22 #include "content/browser/loader/stream_resource_handler.h" | |
| 23 #include "content/public/browser/content_browser_client.h" | |
| 24 #include "content/public/browser/download_item.h" | |
| 25 #include "content/public/browser/download_save_info.h" | |
| 26 #include "content/public/browser/download_url_parameters.h" | |
| 27 #include "content/public/browser/plugin_service.h" | |
| 28 #include "content/public/browser/resource_context.h" | |
| 29 #include "content/public/browser/resource_dispatcher_host_delegate.h" | |
| 30 #include "content/public/common/resource_response.h" | |
| 31 #include "content/public/common/webplugininfo.h" | |
| 32 #include "net/base/io_buffer.h" | |
| 33 #include "net/base/mime_sniffer.h" | |
|
mattm
2016/06/09 22:55:32
not used here?
clamy
2016/06/21 16:14:15
Done.
| |
| 34 #include "net/base/mime_util.h" | |
| 35 #include "net/http/http_content_disposition.h" | |
| 36 #include "net/http/http_response_headers.h" | |
| 37 | |
| 38 namespace content { | |
| 39 | |
| 40 ReplacingResourceHandler::ReplacingResourceHandler( | |
| 41 std::unique_ptr<ResourceHandler> next_handler, | |
| 42 ResourceDispatcherHostImpl* host, | |
| 43 PluginService* plugin_service, | |
| 44 net::URLRequest* request) | |
| 45 : LayeredResourceHandler(request, std::move(next_handler)), | |
| 46 state_(State::STARTING), | |
| 47 host_(host), | |
| 48 #if defined(ENABLE_PLUGINS) | |
| 49 plugin_service_(plugin_service), | |
| 50 #endif | |
| 51 switched_handler_(false), | |
| 52 must_download_(false), | |
| 53 must_download_is_set_(false), | |
| 54 bytes_read_(0), | |
| 55 weak_ptr_factory_(this) { | |
| 56 } | |
| 57 | |
| 58 ReplacingResourceHandler::~ReplacingResourceHandler() {} | |
| 59 | |
| 60 bool ReplacingResourceHandler::OnResponseStarted(ResourceResponse* response, | |
| 61 bool* defer) { | |
| 62 if (response->head.headers.get() && | |
| 63 response->head.headers->response_code() == 304) { | |
| 64 return true; | |
|
mmenke
2016/06/09 18:09:29
BUG: This should be passed through to the next ha
clamy
2016/06/21 16:14:15
Done.
| |
| 65 } | |
| 66 | |
| 67 response_ = response; | |
| 68 | |
| 69 state_ = State::WAITING_FOR_NEW_HANDLER; | |
| 70 | |
| 71 return MakeHandlerChoice(defer); | |
| 72 } | |
| 73 | |
| 74 bool ReplacingResourceHandler::OnWillRead(scoped_refptr<net::IOBuffer>* buf, | |
| 75 int* buf_size, | |
| 76 int min_size) { | |
| 77 if (state_ == State::CHOICE_MADE) | |
| 78 return next_handler_->OnWillRead(buf, buf_size, min_size); | |
| 79 | |
| 80 DCHECK_EQ(State::STARTING, state_); | |
| 81 DCHECK_EQ(-1, min_size); | |
| 82 | |
| 83 if (!next_handler_->OnWillRead(buf, buf_size, min_size)) | |
| 84 return false; | |
| 85 | |
| 86 read_buffer_ = *buf; | |
| 87 return true; | |
| 88 } | |
| 89 | |
| 90 bool ReplacingResourceHandler::OnReadCompleted(int bytes_read, bool* defer) { | |
| 91 if (state_ == State::CHOICE_MADE) | |
| 92 return next_handler_->OnReadCompleted(bytes_read, defer); | |
| 93 | |
| 94 DCHECK(state_ == State::WAITING_FOR_BUFFER_COPY); | |
|
mmenke
2016/06/09 18:09:29
DCHECK_EQ
clamy
2016/06/21 16:14:15
Done.
| |
| 95 bytes_read_ += bytes_read; | |
| 96 if (!CopyReadBufferToNextHandler()) | |
| 97 return false; | |
| 98 | |
| 99 return next_handler_->OnReadCompleted(bytes_read, defer); | |
| 100 } | |
| 101 | |
| 102 bool ReplacingResourceHandler::MakeHandlerChoice(bool* defer) { | |
| 103 if (!SelectNextHandler(defer)) | |
| 104 return false; | |
| 105 | |
| 106 if (*defer) | |
| 107 return true; | |
| 108 | |
| 109 if (!next_handler_->OnResponseStarted(response_.get(), defer)) | |
| 110 return false; | |
| 111 | |
| 112 state_ = read_buffer_ && switched_handler_ ? State::WAITING_FOR_BUFFER_COPY | |
| 113 : State::CHOICE_MADE; | |
| 114 return true; | |
| 115 } | |
| 116 | |
| 117 bool ReplacingResourceHandler::SelectPluginHandler(bool* defer, | |
| 118 bool* handled_by_plugin) { | |
| 119 *handled_by_plugin = false; | |
| 120 #if defined(ENABLE_PLUGINS) | |
| 121 ResourceRequestInfoImpl* info = GetRequestInfo(); | |
| 122 bool allow_wildcard = false; | |
| 123 bool stale; | |
| 124 WebPluginInfo plugin; | |
| 125 bool has_plugin = plugin_service_->GetPluginInfo( | |
| 126 info->GetChildID(), info->GetRenderFrameID(), info->GetContext(), | |
| 127 request()->url(), GURL(), response_->head.mime_type, allow_wildcard, | |
| 128 &stale, &plugin, NULL); | |
| 129 | |
| 130 if (stale) { | |
| 131 // Refresh the plugins asynchronously. | |
| 132 plugin_service_->GetPlugins( | |
| 133 base::Bind(&ReplacingResourceHandler::OnPluginsLoaded, | |
| 134 weak_ptr_factory_.GetWeakPtr())); | |
| 135 request()->LogBlockedBy("ReplacingResourceHandler"); | |
| 136 *defer = true; | |
| 137 return true; | |
| 138 } | |
| 139 | |
| 140 if (has_plugin && plugin.type != WebPluginInfo::PLUGIN_TYPE_BROWSER_PLUGIN) { | |
| 141 *handled_by_plugin = true; | |
| 142 return true; | |
| 143 } | |
| 144 | |
| 145 // Attempt to intercept the request as a stream. | |
| 146 base::FilePath plugin_path; | |
| 147 if (has_plugin) | |
| 148 plugin_path = plugin.path; | |
| 149 std::string payload; | |
| 150 std::unique_ptr<ResourceHandler> handler(host_->MaybeInterceptAsStream( | |
| 151 plugin_path, request(), response_.get(), &payload)); | |
| 152 if (handler) { | |
| 153 *handled_by_plugin = true; | |
| 154 return UseAlternateNextHandler(std::move(handler), payload); | |
| 155 } | |
| 156 #endif | |
| 157 return true; | |
| 158 } | |
| 159 | |
| 160 bool ReplacingResourceHandler::SelectNextHandler(bool* defer) { | |
| 161 DCHECK(!response_->head.mime_type.empty()); | |
| 162 | |
| 163 ResourceRequestInfoImpl* info = GetRequestInfo(); | |
| 164 const std::string& mime_type = response_->head.mime_type; | |
| 165 | |
| 166 // https://crbug.com/568184 - Temporary hack to track servers that aren't | |
| 167 // setting Content-Disposition when sending x-x509-user-cert and expecting | |
| 168 // the browser to automatically install certificates; this is being | |
| 169 // deprecated and will be removed upon full <keygen> removal. | |
| 170 if (mime_type == "application/x-x509-user-cert") { | |
| 171 UMA_HISTOGRAM_BOOLEAN( | |
| 172 "UserCert.ContentDisposition", | |
| 173 response_->head.headers->HasHeader("Content-Disposition")); | |
| 174 } | |
| 175 | |
| 176 // Allow requests for object/embed tags to be intercepted as streams. | |
| 177 if (info->GetResourceType() == content::RESOURCE_TYPE_OBJECT) { | |
| 178 DCHECK(!info->allow_download()); | |
| 179 | |
| 180 bool handled_by_plugin; | |
| 181 if (!SelectPluginHandler(defer, &handled_by_plugin)) | |
| 182 return false; | |
| 183 if (handled_by_plugin || *defer) | |
| 184 return true; | |
| 185 } | |
| 186 | |
| 187 if (!info->allow_download()) | |
| 188 return true; | |
| 189 | |
| 190 // info->allow_download() == true implies | |
| 191 // info->GetResourceType() == RESOURCE_TYPE_MAIN_FRAME or | |
| 192 // info->GetResourceType() == RESOURCE_TYPE_SUB_FRAME. | |
| 193 DCHECK(info->GetResourceType() == RESOURCE_TYPE_MAIN_FRAME || | |
| 194 info->GetResourceType() == RESOURCE_TYPE_SUB_FRAME); | |
| 195 | |
| 196 bool must_download = MustDownload(); | |
| 197 if (!must_download) { | |
| 198 if (mime_util::IsSupportedMimeType(mime_type)) | |
| 199 return true; | |
| 200 | |
| 201 bool handled_by_plugin; | |
| 202 if (!SelectPluginHandler(defer, &handled_by_plugin)) | |
| 203 return false; | |
| 204 if (handled_by_plugin || *defer) | |
| 205 return true; | |
| 206 } | |
| 207 | |
| 208 // Install download handler | |
| 209 info->set_is_download(true); | |
| 210 std::unique_ptr<ResourceHandler> handler( | |
| 211 host_->CreateResourceHandlerForDownload(request(), | |
| 212 true, // is_content_initiated | |
| 213 must_download)); | |
| 214 return UseAlternateNextHandler(std::move(handler), std::string()); | |
| 215 } | |
| 216 | |
| 217 bool ReplacingResourceHandler::UseAlternateNextHandler( | |
| 218 std::unique_ptr<ResourceHandler> new_handler, | |
| 219 const std::string& payload_for_old_handler) { | |
| 220 if (response_->head.headers.get() && // Can be NULL if FTP. | |
| 221 response_->head.headers->response_code() / 100 != 2) { | |
| 222 // The response code indicates that this is an error page, but we don't | |
| 223 // know how to display the content. We follow Firefox here and show our | |
| 224 // own error page instead of triggering a download. | |
| 225 // TODO(abarth): We should abstract the response_code test, but this kind | |
| 226 // of check is scattered throughout our codebase. | |
| 227 request()->CancelWithError(net::ERR_INVALID_RESPONSE); | |
| 228 return false; | |
| 229 } | |
| 230 | |
| 231 // Inform the original ResourceHandler that this will be handled entirely by | |
| 232 // the new ResourceHandler. | |
| 233 // TODO(darin): We should probably check the return values of these. | |
| 234 bool defer_ignored = false; | |
| 235 next_handler_->OnResponseStarted(response_.get(), &defer_ignored); | |
| 236 // Although deferring OnResponseStarted is legal, the only downstream handler | |
| 237 // which does so is CrossSiteResourceHandler. Cross-site transitions should | |
| 238 // not trigger when switching handlers. | |
| 239 DCHECK(!defer_ignored); | |
| 240 if (payload_for_old_handler.empty()) { | |
| 241 net::URLRequestStatus status(net::URLRequestStatus::CANCELED, | |
| 242 net::ERR_ABORTED); | |
| 243 next_handler_->OnResponseCompleted(status, std::string(), &defer_ignored); | |
| 244 DCHECK(!defer_ignored); | |
| 245 } else { | |
| 246 scoped_refptr<net::IOBuffer> buf; | |
| 247 int size = 0; | |
| 248 next_handler_->OnWillRead(&buf, &size, -1); | |
| 249 CHECK_GE(size, static_cast<int>(payload_for_old_handler.length())); | |
| 250 memcpy(buf->data(), payload_for_old_handler.c_str(), | |
| 251 payload_for_old_handler.length()); | |
| 252 next_handler_->OnReadCompleted(payload_for_old_handler.length(), | |
| 253 &defer_ignored); | |
| 254 DCHECK(!defer_ignored); | |
| 255 | |
| 256 net::URLRequestStatus status(net::URLRequestStatus::SUCCESS, 0); | |
| 257 next_handler_->OnResponseCompleted(status, std::string(), &defer_ignored); | |
| 258 DCHECK(!defer_ignored); | |
| 259 } | |
| 260 | |
| 261 // This is handled entirely within the new ResourceHandler, so just reset the | |
| 262 // original ResourceHandler. | |
| 263 next_handler_ = std::move(new_handler); | |
| 264 next_handler_->SetController(controller()); | |
| 265 | |
| 266 switched_handler_ = true; | |
| 267 | |
| 268 return true; | |
| 269 } | |
| 270 | |
| 271 bool ReplacingResourceHandler::MustDownload() { | |
| 272 if (must_download_is_set_) | |
| 273 return must_download_; | |
| 274 | |
| 275 must_download_is_set_ = true; | |
| 276 | |
| 277 std::string disposition; | |
| 278 request()->GetResponseHeaderByName("content-disposition", &disposition); | |
| 279 if (!disposition.empty() && | |
| 280 net::HttpContentDisposition(disposition, std::string()).is_attachment()) { | |
| 281 must_download_ = true; | |
| 282 } else if (host_->delegate() && | |
| 283 host_->delegate()->ShouldForceDownloadResource( | |
| 284 request()->url(), response_->head.mime_type)) { | |
| 285 must_download_ = true; | |
| 286 } else { | |
| 287 must_download_ = false; | |
| 288 } | |
| 289 | |
| 290 return must_download_; | |
| 291 } | |
| 292 | |
| 293 void ReplacingResourceHandler::OnPluginsLoaded( | |
| 294 const std::vector<WebPluginInfo>& plugins) { | |
| 295 request()->LogUnblocked(); | |
| 296 bool defer = false; | |
| 297 if (!MakeHandlerChoice(&defer)) { | |
| 298 controller()->Cancel(); | |
| 299 } else if (!defer) { | |
| 300 controller()->Resume(); | |
| 301 } | |
| 302 } | |
| 303 | |
| 304 bool ReplacingResourceHandler::CopyReadBufferToNextHandler() { | |
| 305 DCHECK(read_buffer_.get()); | |
| 306 scoped_refptr<net::IOBuffer> buf; | |
| 307 int buf_len = 0; | |
| 308 if (!next_handler_->OnWillRead(&buf, &buf_len, bytes_read_)) | |
| 309 return false; | |
| 310 | |
| 311 CHECK((buf_len >= bytes_read_) && (bytes_read_ >= 0)); | |
| 312 memcpy(buf->data(), read_buffer_->data(), bytes_read_); | |
| 313 state_ = State::CHOICE_MADE; | |
| 314 return true; | |
| 315 } | |
| 316 | |
| 317 } // namespace content | |
| OLD | NEW |