| OLD | NEW |
| (Empty) |
| 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/browser/renderer_host/async_resource_handler.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 #include <vector> | |
| 9 | |
| 10 #include "base/command_line.h" | |
| 11 #include "base/debug/alias.h" | |
| 12 #include "base/hash_tables.h" | |
| 13 #include "base/logging.h" | |
| 14 #include "base/metrics/histogram.h" | |
| 15 #include "base/shared_memory.h" | |
| 16 #include "base/string_number_conversions.h" | |
| 17 #include "content/browser/debugger/devtools_netlog_observer.h" | |
| 18 #include "content/browser/host_zoom_map_impl.h" | |
| 19 #include "content/browser/renderer_host/resource_buffer.h" | |
| 20 #include "content/browser/renderer_host/resource_dispatcher_host_impl.h" | |
| 21 #include "content/browser/renderer_host/resource_message_filter.h" | |
| 22 #include "content/browser/renderer_host/resource_request_info_impl.h" | |
| 23 #include "content/browser/resource_context_impl.h" | |
| 24 #include "content/common/resource_messages.h" | |
| 25 #include "content/common/view_messages.h" | |
| 26 #include "content/public/browser/global_request_id.h" | |
| 27 #include "content/public/browser/resource_dispatcher_host_delegate.h" | |
| 28 #include "content/public/common/resource_response.h" | |
| 29 #include "net/base/io_buffer.h" | |
| 30 #include "net/base/load_flags.h" | |
| 31 #include "net/base/net_log.h" | |
| 32 #include "net/base/net_util.h" | |
| 33 #include "webkit/glue/resource_loader_bridge.h" | |
| 34 | |
| 35 using base::TimeTicks; | |
| 36 | |
| 37 namespace content { | |
| 38 namespace { | |
| 39 | |
| 40 static int kBufferSize = 1024 * 512; | |
| 41 static int kMinAllocationSize = 1024 * 4; | |
| 42 static int kMaxAllocationSize = 1024 * 32; | |
| 43 | |
| 44 void GetNumericArg(const std::string& name, int* result) { | |
| 45 const std::string& value = | |
| 46 CommandLine::ForCurrentProcess()->GetSwitchValueASCII(name); | |
| 47 if (!value.empty()) | |
| 48 base::StringToInt(value, result); | |
| 49 } | |
| 50 | |
| 51 void InitializeResourceBufferConstants() { | |
| 52 static bool did_init = false; | |
| 53 if (did_init) | |
| 54 return; | |
| 55 did_init = true; | |
| 56 | |
| 57 GetNumericArg("resource-buffer-size", &kBufferSize); | |
| 58 GetNumericArg("resource-buffer-min-allocation-size", &kMinAllocationSize); | |
| 59 GetNumericArg("resource-buffer-max-allocation-size", &kMaxAllocationSize); | |
| 60 } | |
| 61 | |
| 62 int CalcUsedPercentage(int bytes_read, int buffer_size) { | |
| 63 double ratio = static_cast<double>(bytes_read) / buffer_size; | |
| 64 return static_cast<int>(ratio * 100.0 + 0.5); // Round to nearest integer. | |
| 65 } | |
| 66 | |
| 67 } // namespace | |
| 68 | |
| 69 class DependentIOBuffer : public net::WrappedIOBuffer { | |
| 70 public: | |
| 71 DependentIOBuffer(ResourceBuffer* backing, char* memory) | |
| 72 : net::WrappedIOBuffer(memory), | |
| 73 backing_(backing) { | |
| 74 } | |
| 75 private: | |
| 76 ~DependentIOBuffer() {} | |
| 77 scoped_refptr<ResourceBuffer> backing_; | |
| 78 }; | |
| 79 | |
| 80 AsyncResourceHandler::AsyncResourceHandler( | |
| 81 ResourceMessageFilter* filter, | |
| 82 int routing_id, | |
| 83 net::URLRequest* request, | |
| 84 ResourceDispatcherHostImpl* rdh) | |
| 85 : filter_(filter), | |
| 86 routing_id_(routing_id), | |
| 87 request_(request), | |
| 88 rdh_(rdh), | |
| 89 pending_data_count_(0), | |
| 90 allocation_size_(0), | |
| 91 did_defer_(false), | |
| 92 sent_received_response_msg_(false), | |
| 93 sent_first_data_msg_(false) { | |
| 94 // Set a back-pointer from ResourceRequestInfoImpl to |this|, so that the | |
| 95 // ResourceDispatcherHostImpl can send us IPC messages. | |
| 96 // TODO(darin): Implement an IPC message filter instead? | |
| 97 ResourceRequestInfoImpl::ForRequest(request_)->set_async_handler(this); | |
| 98 | |
| 99 InitializeResourceBufferConstants(); | |
| 100 } | |
| 101 | |
| 102 AsyncResourceHandler::~AsyncResourceHandler() { | |
| 103 // Cleanup back-pointer stored on the request info. | |
| 104 ResourceRequestInfoImpl::ForRequest(request_)->set_async_handler(NULL); | |
| 105 } | |
| 106 | |
| 107 void AsyncResourceHandler::OnFollowRedirect( | |
| 108 bool has_new_first_party_for_cookies, | |
| 109 const GURL& new_first_party_for_cookies) { | |
| 110 if (!request_->status().is_success()) { | |
| 111 DVLOG(1) << "OnFollowRedirect for invalid request"; | |
| 112 return; | |
| 113 } | |
| 114 | |
| 115 if (has_new_first_party_for_cookies) | |
| 116 request_->set_first_party_for_cookies(new_first_party_for_cookies); | |
| 117 | |
| 118 ResumeIfDeferred(); | |
| 119 } | |
| 120 | |
| 121 void AsyncResourceHandler::OnDataReceivedACK() { | |
| 122 --pending_data_count_; | |
| 123 | |
| 124 buffer_->RecycleLeastRecentlyAllocated(); | |
| 125 if (buffer_->CanAllocate()) | |
| 126 ResumeIfDeferred(); | |
| 127 } | |
| 128 | |
| 129 bool AsyncResourceHandler::OnUploadProgress(int request_id, | |
| 130 uint64 position, | |
| 131 uint64 size) { | |
| 132 return filter_->Send(new ResourceMsg_UploadProgress(routing_id_, request_id, | |
| 133 position, size)); | |
| 134 } | |
| 135 | |
| 136 bool AsyncResourceHandler::OnRequestRedirected(int request_id, | |
| 137 const GURL& new_url, | |
| 138 ResourceResponse* response, | |
| 139 bool* defer) { | |
| 140 *defer = did_defer_ = true; | |
| 141 | |
| 142 if (rdh_->delegate()) { | |
| 143 rdh_->delegate()->OnRequestRedirected(new_url, request_, | |
| 144 filter_->resource_context(), | |
| 145 response); | |
| 146 } | |
| 147 | |
| 148 DevToolsNetLogObserver::PopulateResponseInfo(request_, response); | |
| 149 response->head.request_start = request_->creation_time(); | |
| 150 response->head.response_start = TimeTicks::Now(); | |
| 151 return filter_->Send(new ResourceMsg_ReceivedRedirect( | |
| 152 routing_id_, request_id, new_url, response->head)); | |
| 153 } | |
| 154 | |
| 155 bool AsyncResourceHandler::OnResponseStarted(int request_id, | |
| 156 ResourceResponse* response, | |
| 157 bool* defer) { | |
| 158 // For changes to the main frame, inform the renderer of the new URL's | |
| 159 // per-host settings before the request actually commits. This way the | |
| 160 // renderer will be able to set these precisely at the time the | |
| 161 // request commits, avoiding the possibility of e.g. zooming the old content | |
| 162 // or of having to layout the new content twice. | |
| 163 | |
| 164 ResourceContext* resource_context = filter_->resource_context(); | |
| 165 if (rdh_->delegate()) { | |
| 166 rdh_->delegate()->OnResponseStarted(request_, resource_context, response, | |
| 167 filter_); | |
| 168 } | |
| 169 | |
| 170 DevToolsNetLogObserver::PopulateResponseInfo(request_, response); | |
| 171 | |
| 172 HostZoomMap* host_zoom_map = | |
| 173 GetHostZoomMapForResourceContext(resource_context); | |
| 174 | |
| 175 const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(request_); | |
| 176 if (info->GetResourceType() == ResourceType::MAIN_FRAME && host_zoom_map) { | |
| 177 const GURL& request_url = request_->url(); | |
| 178 filter_->Send(new ViewMsg_SetZoomLevelForLoadingURL( | |
| 179 info->GetRouteID(), | |
| 180 request_url, host_zoom_map->GetZoomLevel(net::GetHostOrSpecFromURL( | |
| 181 request_url)))); | |
| 182 } | |
| 183 | |
| 184 response->head.request_start = request_->creation_time(); | |
| 185 response->head.response_start = TimeTicks::Now(); | |
| 186 filter_->Send(new ResourceMsg_ReceivedResponse( | |
| 187 routing_id_, request_id, response->head)); | |
| 188 sent_received_response_msg_ = true; | |
| 189 | |
| 190 if (request_->response_info().metadata) { | |
| 191 std::vector<char> copy(request_->response_info().metadata->data(), | |
| 192 request_->response_info().metadata->data() + | |
| 193 request_->response_info().metadata->size()); | |
| 194 filter_->Send(new ResourceMsg_ReceivedCachedMetadata( | |
| 195 routing_id_, request_id, copy)); | |
| 196 } | |
| 197 | |
| 198 return true; | |
| 199 } | |
| 200 | |
| 201 bool AsyncResourceHandler::OnWillStart(int request_id, | |
| 202 const GURL& url, | |
| 203 bool* defer) { | |
| 204 return true; | |
| 205 } | |
| 206 | |
| 207 bool AsyncResourceHandler::OnWillRead(int request_id, net::IOBuffer** buf, | |
| 208 int* buf_size, int min_size) { | |
| 209 DCHECK_EQ(-1, min_size); | |
| 210 | |
| 211 if (!EnsureResourceBufferIsInitialized()) | |
| 212 return false; | |
| 213 | |
| 214 DCHECK(buffer_->CanAllocate()); | |
| 215 char* memory = buffer_->Allocate(&allocation_size_); | |
| 216 CHECK(memory); | |
| 217 | |
| 218 *buf = new DependentIOBuffer(buffer_, memory); | |
| 219 *buf_size = allocation_size_; | |
| 220 | |
| 221 UMA_HISTOGRAM_CUSTOM_COUNTS( | |
| 222 "Net.AsyncResourceHandler_SharedIOBuffer_Alloc", | |
| 223 *buf_size, 0, kMaxAllocationSize, 100); | |
| 224 return true; | |
| 225 } | |
| 226 | |
| 227 bool AsyncResourceHandler::OnReadCompleted(int request_id, int bytes_read, | |
| 228 bool* defer) { | |
| 229 if (!bytes_read) | |
| 230 return true; | |
| 231 | |
| 232 buffer_->ShrinkLastAllocation(bytes_read); | |
| 233 | |
| 234 UMA_HISTOGRAM_CUSTOM_COUNTS( | |
| 235 "Net.AsyncResourceHandler_SharedIOBuffer_Used", | |
| 236 bytes_read, 0, kMaxAllocationSize, 100); | |
| 237 UMA_HISTOGRAM_PERCENTAGE( | |
| 238 "Net.AsyncResourceHandler_SharedIOBuffer_UsedPercentage", | |
| 239 CalcUsedPercentage(bytes_read, allocation_size_)); | |
| 240 | |
| 241 if (!sent_first_data_msg_) { | |
| 242 base::SharedMemoryHandle handle; | |
| 243 int size; | |
| 244 if (!buffer_->ShareToProcess(filter_->peer_handle(), &handle, &size)) | |
| 245 return false; | |
| 246 filter_->Send( | |
| 247 new ResourceMsg_SetDataBuffer(routing_id_, request_id, handle, size)); | |
| 248 sent_first_data_msg_ = true; | |
| 249 } | |
| 250 | |
| 251 int data_offset = buffer_->GetLastAllocationOffset(); | |
| 252 int encoded_data_length = | |
| 253 DevToolsNetLogObserver::GetAndResetEncodedDataLength(request_); | |
| 254 | |
| 255 filter_->Send( | |
| 256 new ResourceMsg_DataReceived(routing_id_, request_id, data_offset, | |
| 257 bytes_read, encoded_data_length)); | |
| 258 ++pending_data_count_; | |
| 259 UMA_HISTOGRAM_CUSTOM_COUNTS( | |
| 260 "Net.AsyncResourceHandler_PendingDataCount", | |
| 261 pending_data_count_, 0, 100, 100); | |
| 262 | |
| 263 if (!buffer_->CanAllocate()) { | |
| 264 UMA_HISTOGRAM_CUSTOM_COUNTS( | |
| 265 "Net.AsyncResourceHandler_PendingDataCount_WhenFull", | |
| 266 pending_data_count_, 0, 100, 100); | |
| 267 *defer = did_defer_ = true; | |
| 268 } | |
| 269 | |
| 270 return true; | |
| 271 } | |
| 272 | |
| 273 void AsyncResourceHandler::OnDataDownloaded( | |
| 274 int request_id, int bytes_downloaded) { | |
| 275 filter_->Send(new ResourceMsg_DataDownloaded( | |
| 276 routing_id_, request_id, bytes_downloaded)); | |
| 277 } | |
| 278 | |
| 279 bool AsyncResourceHandler::OnResponseCompleted( | |
| 280 int request_id, | |
| 281 const net::URLRequestStatus& status, | |
| 282 const std::string& security_info) { | |
| 283 // If we crash here, figure out what URL the renderer was requesting. | |
| 284 // http://crbug.com/107692 | |
| 285 char url_buf[128]; | |
| 286 base::strlcpy(url_buf, request_->url().spec().c_str(), arraysize(url_buf)); | |
| 287 base::debug::Alias(url_buf); | |
| 288 | |
| 289 // TODO(gavinp): Remove this CHECK when we figure out the cause of | |
| 290 // http://crbug.com/124680 . This check mirrors closely check in | |
| 291 // WebURLLoaderImpl::OnCompletedRequest that routes this message to a WebCore | |
| 292 // ResourceHandleInternal which asserts on its state and crashes. By crashing | |
| 293 // when the message is sent, we should get better crash reports. | |
| 294 CHECK(status.status() != net::URLRequestStatus::SUCCESS || | |
| 295 sent_received_response_msg_); | |
| 296 | |
| 297 TimeTicks completion_time = TimeTicks::Now(); | |
| 298 | |
| 299 int error_code = status.error(); | |
| 300 bool was_ignored_by_handler = | |
| 301 ResourceRequestInfoImpl::ForRequest(request_)->WasIgnoredByHandler(); | |
| 302 | |
| 303 DCHECK(status.status() != net::URLRequestStatus::IO_PENDING); | |
| 304 // If this check fails, then we're in an inconsistent state because all | |
| 305 // requests ignored by the handler should be canceled (which should result in | |
| 306 // the ERR_ABORTED error code). | |
| 307 DCHECK(!was_ignored_by_handler || error_code == net::ERR_ABORTED); | |
| 308 | |
| 309 // TODO(mkosiba): Fix up cases where we create a URLRequestStatus | |
| 310 // with a status() != SUCCESS and an error_code() == net::OK. | |
| 311 if (status.status() == net::URLRequestStatus::CANCELED && | |
| 312 error_code == net::OK) { | |
| 313 error_code = net::ERR_ABORTED; | |
| 314 } else if (status.status() == net::URLRequestStatus::FAILED && | |
| 315 error_code == net::OK) { | |
| 316 error_code = net::ERR_FAILED; | |
| 317 } | |
| 318 | |
| 319 filter_->Send(new ResourceMsg_RequestComplete(routing_id_, | |
| 320 request_id, | |
| 321 error_code, | |
| 322 was_ignored_by_handler, | |
| 323 security_info, | |
| 324 completion_time)); | |
| 325 return true; | |
| 326 } | |
| 327 | |
| 328 bool AsyncResourceHandler::EnsureResourceBufferIsInitialized() { | |
| 329 if (buffer_ && buffer_->IsInitialized()) | |
| 330 return true; | |
| 331 | |
| 332 buffer_ = new ResourceBuffer(); | |
| 333 return buffer_->Initialize(kBufferSize, | |
| 334 kMinAllocationSize, | |
| 335 kMaxAllocationSize); | |
| 336 } | |
| 337 | |
| 338 void AsyncResourceHandler::ResumeIfDeferred() { | |
| 339 if (did_defer_) { | |
| 340 did_defer_ = false; | |
| 341 controller()->Resume(); | |
| 342 } | |
| 343 } | |
| 344 | |
| 345 } // namespace content | |
| OLD | NEW |