| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/browser/download/download_request_core.h" | 5 #include "content/browser/download/download_request_core.h" |
| 6 | 6 |
| 7 #include <string> | 7 #include <string> |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/callback_helpers.h" |
| 10 #include "base/location.h" | 11 #include "base/location.h" |
| 11 #include "base/logging.h" | 12 #include "base/logging.h" |
| 12 #include "base/metrics/histogram_macros.h" | 13 #include "base/metrics/histogram_macros.h" |
| 13 #include "base/metrics/sparse_histogram.h" | 14 #include "base/metrics/sparse_histogram.h" |
| 14 #include "base/single_thread_task_runner.h" | 15 #include "base/single_thread_task_runner.h" |
| 15 #include "base/strings/stringprintf.h" | 16 #include "base/strings/stringprintf.h" |
| 16 #include "base/thread_task_runner_handle.h" | 17 #include "base/thread_task_runner_handle.h" |
| 17 #include "content/browser/byte_stream.h" | 18 #include "content/browser/byte_stream.h" |
| 18 #include "content/browser/download/download_create_info.h" | 19 #include "content/browser/download/download_create_info.h" |
| 19 #include "content/browser/download/download_interrupt_reasons_impl.h" | 20 #include "content/browser/download/download_interrupt_reasons_impl.h" |
| 20 #include "content/browser/download/download_manager_impl.h" | 21 #include "content/browser/download/download_manager_impl.h" |
| 22 #include "content/browser/download/download_request_handle.h" |
| 21 #include "content/browser/download/download_stats.h" | 23 #include "content/browser/download/download_stats.h" |
| 22 #include "content/public/browser/browser_thread.h" | 24 #include "content/public/browser/browser_thread.h" |
| 23 #include "content/public/browser/download_interrupt_reasons.h" | 25 #include "content/public/browser/download_interrupt_reasons.h" |
| 24 #include "content/public/browser/download_item.h" | 26 #include "content/public/browser/download_item.h" |
| 25 #include "content/public/browser/download_manager_delegate.h" | 27 #include "content/public/browser/download_manager_delegate.h" |
| 26 #include "content/public/browser/navigation_entry.h" | 28 #include "content/public/browser/navigation_entry.h" |
| 27 #include "content/public/browser/power_save_blocker.h" | 29 #include "content/public/browser/power_save_blocker.h" |
| 28 #include "content/public/browser/web_contents.h" | 30 #include "content/public/browser/web_contents.h" |
| 29 #include "net/base/io_buffer.h" | 31 #include "net/base/io_buffer.h" |
| 30 #include "net/base/net_errors.h" | 32 #include "net/base/net_errors.h" |
| 31 #include "net/http/http_response_headers.h" | 33 #include "net/http/http_response_headers.h" |
| 32 #include "net/http/http_status_code.h" | 34 #include "net/http/http_status_code.h" |
| 33 #include "net/url_request/url_request_context.h" | 35 #include "net/url_request/url_request_context.h" |
| 34 | 36 |
| 35 namespace content { | 37 namespace content { |
| 36 | 38 |
| 37 namespace { | |
| 38 | |
| 39 void CallStartedCBOnUIThread( | |
| 40 const DownloadUrlParameters::OnStartedCallback& started_cb, | |
| 41 DownloadItem* item, | |
| 42 DownloadInterruptReason interrupt_reason) { | |
| 43 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
| 44 | |
| 45 if (started_cb.is_null()) | |
| 46 return; | |
| 47 started_cb.Run(item, interrupt_reason); | |
| 48 } | |
| 49 | |
| 50 // Static function in order to prevent any accidental accesses to | |
| 51 // DownloadRequestCore members from the UI thread. | |
| 52 static void StartOnUIThread( | |
| 53 scoped_ptr<DownloadCreateInfo> info, | |
| 54 scoped_ptr<ByteStreamReader> stream, | |
| 55 base::WeakPtr<DownloadManager> download_manager, | |
| 56 const DownloadUrlParameters::OnStartedCallback& started_cb) { | |
| 57 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
| 58 | |
| 59 if (!download_manager) { | |
| 60 // nullptr in unittests or if the page closed right after starting the | |
| 61 // download. | |
| 62 if (!started_cb.is_null()) | |
| 63 started_cb.Run(nullptr, DOWNLOAD_INTERRUPT_REASON_USER_CANCELED); | |
| 64 | |
| 65 // |stream| gets deleted on non-FILE thread, but it's ok since | |
| 66 // we're not using stream_writer_ yet. | |
| 67 | |
| 68 return; | |
| 69 } | |
| 70 | |
| 71 download_manager->StartDownload(info.Pass(), stream.Pass(), started_cb); | |
| 72 } | |
| 73 | |
| 74 } // namespace | |
| 75 | |
| 76 const int DownloadRequestCore::kDownloadByteStreamSize = 100 * 1024; | 39 const int DownloadRequestCore::kDownloadByteStreamSize = 100 * 1024; |
| 77 | 40 |
| 78 DownloadRequestCore::DownloadRequestCore( | 41 DownloadRequestCore::DownloadRequestCore( |
| 79 uint32 id, | |
| 80 net::URLRequest* request, | 42 net::URLRequest* request, |
| 81 const DownloadUrlParameters::OnStartedCallback& started_cb, | |
| 82 scoped_ptr<DownloadSaveInfo> save_info, | 43 scoped_ptr<DownloadSaveInfo> save_info, |
| 83 base::WeakPtr<DownloadManagerImpl> download_manager) | 44 const base::Closure& on_ready_to_read_callback) |
| 84 : request_(request), | 45 : on_ready_to_read_callback_(on_ready_to_read_callback), |
| 85 download_id_(id), | 46 request_(request), |
| 86 started_cb_(started_cb), | 47 save_info_(std::move(save_info)), |
| 87 save_info_(save_info.Pass()), | |
| 88 last_buffer_size_(0), | 48 last_buffer_size_(0), |
| 89 bytes_read_(0), | 49 bytes_read_(0), |
| 90 pause_count_(0), | 50 pause_count_(0), |
| 91 was_deferred_(false), | 51 was_deferred_(false) { |
| 92 on_response_started_called_(false), | 52 DCHECK(request_); |
| 93 download_manager_(download_manager) { | 53 DCHECK(save_info_); |
| 54 DCHECK(!on_ready_to_read_callback_.is_null()); |
| 94 RecordDownloadCount(UNTHROTTLED_COUNT); | 55 RecordDownloadCount(UNTHROTTLED_COUNT); |
| 95 | |
| 96 power_save_blocker_ = PowerSaveBlocker::Create( | 56 power_save_blocker_ = PowerSaveBlocker::Create( |
| 97 PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension, | 57 PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension, |
| 98 PowerSaveBlocker::kReasonOther, "Download in progress"); | 58 PowerSaveBlocker::kReasonOther, "Download in progress"); |
| 99 } | 59 } |
| 100 | 60 |
| 101 DownloadRequestCore::~DownloadRequestCore() { | 61 DownloadRequestCore::~DownloadRequestCore() { |
| 102 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 62 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 103 | |
| 104 // This won't do anything if the callback was called before. | |
| 105 // If it goes through, it will likely be because OnWillStart() returned | |
| 106 // false somewhere in the chain of resource handlers. | |
| 107 CallStartedCB(nullptr, DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED); | |
| 108 | |
| 109 // Remove output stream callback if a stream exists. | 63 // Remove output stream callback if a stream exists. |
| 110 if (stream_writer_) | 64 if (stream_writer_) |
| 111 stream_writer_->RegisterCallback(base::Closure()); | 65 stream_writer_->RegisterCallback(base::Closure()); |
| 112 | 66 |
| 113 UMA_HISTOGRAM_TIMES("SB2.DownloadDuration", | 67 UMA_HISTOGRAM_TIMES("SB2.DownloadDuration", |
| 114 base::TimeTicks::Now() - download_start_time_); | 68 base::TimeTicks::Now() - download_start_time_); |
| 115 } | 69 } |
| 116 | 70 |
| 117 // Send the download creation information to the download thread. | 71 // Send the download creation information to the download thread. |
| 118 bool DownloadRequestCore::OnResponseStarted() { | 72 void DownloadRequestCore::OnResponseStarted( |
| 73 scoped_ptr<DownloadCreateInfo>* create_info, |
| 74 scoped_ptr<ByteStreamReader>* stream_reader) { |
| 119 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 75 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 120 // There can be only one (call) | 76 DCHECK(save_info_); |
| 121 DCHECK(!on_response_started_called_); | |
| 122 on_response_started_called_ = true; | |
| 123 | |
| 124 DVLOG(20) << __FUNCTION__ << "()" << DebugString(); | 77 DVLOG(20) << __FUNCTION__ << "()" << DebugString(); |
| 125 download_start_time_ = base::TimeTicks::Now(); | 78 download_start_time_ = base::TimeTicks::Now(); |
| 126 | 79 |
| 127 // If it's a download, we don't want to poison the cache with it. | 80 // If it's a download, we don't want to poison the cache with it. |
| 128 request()->StopCaching(); | 81 request()->StopCaching(); |
| 129 | 82 |
| 130 // Lower priority as well, so downloads don't contend for resources | 83 // Lower priority as well, so downloads don't contend for resources |
| 131 // with main frames. | 84 // with main frames. |
| 132 request()->SetPriority(net::IDLE); | 85 request()->SetPriority(net::IDLE); |
| 133 | 86 |
| 134 // If the content-length header is not present (or contains something other | 87 // If the content-length header is not present (or contains something other |
| 135 // than numbers), the incoming content_length is -1 (unknown size). | 88 // than numbers), the incoming content_length is -1 (unknown size). |
| 136 // Set the content length to 0 to indicate unknown size to DownloadManager. | 89 // Set the content length to 0 to indicate unknown size to DownloadManager. |
| 137 int64 content_length = request()->GetExpectedContentSize() > 0 | 90 int64 content_length = request()->GetExpectedContentSize() > 0 |
| 138 ? request()->GetExpectedContentSize() | 91 ? request()->GetExpectedContentSize() |
| 139 : 0; | 92 : 0; |
| 140 | 93 |
| 141 // Deleted in DownloadManager. | 94 // Deleted in DownloadManager. |
| 142 scoped_ptr<DownloadCreateInfo> info(new DownloadCreateInfo( | 95 scoped_ptr<DownloadCreateInfo> info( |
| 143 base::Time::Now(), content_length, request()->net_log(), false, | 96 new DownloadCreateInfo(base::Time::Now(), content_length, |
| 144 ui::PAGE_TRANSITION_LINK, save_info_.Pass())); | 97 request()->net_log(), std::move(save_info_))); |
| 145 | 98 |
| 146 // Create the ByteStream for sending data to the download sink. | 99 // Create the ByteStream for sending data to the download sink. |
| 147 scoped_ptr<ByteStreamReader> stream_reader; | |
| 148 CreateByteStream( | 100 CreateByteStream( |
| 149 base::ThreadTaskRunnerHandle::Get(), | 101 base::ThreadTaskRunnerHandle::Get(), |
| 150 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE), | 102 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE), |
| 151 kDownloadByteStreamSize, &stream_writer_, &stream_reader); | 103 kDownloadByteStreamSize, &stream_writer_, stream_reader); |
| 152 stream_writer_->RegisterCallback( | 104 stream_writer_->RegisterCallback( |
| 153 base::Bind(&DownloadRequestCore::ResumeRequest, AsWeakPtr())); | 105 base::Bind(&DownloadRequestCore::ResumeRequest, AsWeakPtr())); |
| 154 | 106 |
| 155 info->download_id = download_id_; | |
| 156 info->url_chain = request()->url_chain(); | 107 info->url_chain = request()->url_chain(); |
| 157 info->referrer_url = GURL(request()->referrer()); | 108 info->referrer_url = GURL(request()->referrer()); |
| 158 string mime_type; | 109 string mime_type; |
| 159 request()->GetMimeType(&mime_type); | 110 request()->GetMimeType(&mime_type); |
| 160 info->mime_type = mime_type; | 111 info->mime_type = mime_type; |
| 161 info->remote_address = request()->GetSocketAddress().host(); | 112 info->remote_address = request()->GetSocketAddress().host(); |
| 162 if (request()->response_headers()) { | 113 if (request()->response_headers()) { |
| 163 // Grab the first content-disposition header. There may be more than one, | 114 // Grab the first content-disposition header. There may be more than one, |
| 164 // though as of this writing, the network stack ensures if there are, they | 115 // though as of this writing, the network stack ensures if there are, they |
| 165 // are all duplicates. | 116 // are all duplicates. |
| 166 request()->response_headers()->EnumerateHeader( | 117 request()->response_headers()->EnumerateHeader( |
| 167 nullptr, "content-disposition", &info->content_disposition); | 118 nullptr, "Content-Disposition", &info->content_disposition); |
| 168 } | 119 } |
| 169 RecordDownloadMimeType(info->mime_type); | 120 RecordDownloadMimeType(info->mime_type); |
| 170 RecordDownloadContentDisposition(info->content_disposition); | 121 RecordDownloadContentDisposition(info->content_disposition); |
| 171 | 122 |
| 172 // Get the last modified time and etag. | 123 // Get the last modified time and etag. |
| 173 const net::HttpResponseHeaders* headers = request()->response_headers(); | 124 const net::HttpResponseHeaders* headers = request()->response_headers(); |
| 174 if (headers) { | 125 if (headers) { |
| 175 if (headers->HasStrongValidators()) { | 126 if (headers->HasStrongValidators()) { |
| 176 // If we don't have strong validators as per RFC 2616 section 13.3.3, then | 127 // If we don't have strong validators as per RFC 2616 section 13.3.3, then |
| 177 // we neither store nor use them for range requests. | 128 // we neither store nor use them for range requests. |
| (...skipping 17 matching lines...) Expand all Loading... |
| 195 } | 146 } |
| 196 | 147 |
| 197 // Blink verifies that the requester of this download is allowed to set a | 148 // Blink verifies that the requester of this download is allowed to set a |
| 198 // suggested name for the security origin of the downlaod URL. However, this | 149 // suggested name for the security origin of the downlaod URL. However, this |
| 199 // assumption doesn't hold if there were cross origin redirects. Therefore, | 150 // assumption doesn't hold if there were cross origin redirects. Therefore, |
| 200 // clear the suggested_name for such requests. | 151 // clear the suggested_name for such requests. |
| 201 if (info->url_chain.size() > 1 && | 152 if (info->url_chain.size() > 1 && |
| 202 info->url_chain.front().GetOrigin() != info->url_chain.back().GetOrigin()) | 153 info->url_chain.front().GetOrigin() != info->url_chain.back().GetOrigin()) |
| 203 info->save_info->suggested_name.clear(); | 154 info->save_info->suggested_name.clear(); |
| 204 | 155 |
| 205 BrowserThread::PostTask( | 156 info.swap(*create_info); |
| 206 BrowserThread::UI, FROM_HERE, | |
| 207 base::Bind(&StartOnUIThread, base::Passed(&info), | |
| 208 base::Passed(&stream_reader), download_manager_, | |
| 209 // Pass to StartOnUIThread so that variable | |
| 210 // access is always on IO thread but function | |
| 211 // is called on UI thread. | |
| 212 started_cb_)); | |
| 213 // Guaranteed to be called in StartOnUIThread | |
| 214 started_cb_.Reset(); | |
| 215 | |
| 216 return true; | |
| 217 } | |
| 218 | |
| 219 void DownloadRequestCore::CallStartedCB( | |
| 220 DownloadItem* item, | |
| 221 DownloadInterruptReason interrupt_reason) { | |
| 222 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 223 if (started_cb_.is_null()) | |
| 224 return; | |
| 225 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
| 226 base::Bind(&CallStartedCBOnUIThread, started_cb_, | |
| 227 item, interrupt_reason)); | |
| 228 started_cb_.Reset(); | |
| 229 } | 157 } |
| 230 | 158 |
| 231 // Create a new buffer, which will be handed to the download thread for file | 159 // Create a new buffer, which will be handed to the download thread for file |
| 232 // writing and deletion. | 160 // writing and deletion. |
| 233 bool DownloadRequestCore::OnWillRead(scoped_refptr<net::IOBuffer>* buf, | 161 bool DownloadRequestCore::OnWillRead(scoped_refptr<net::IOBuffer>* buf, |
| 234 int* buf_size, | 162 int* buf_size, |
| 235 int min_size) { | 163 int min_size) { |
| 236 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 164 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 237 DCHECK(buf && buf_size); | 165 DCHECK(buf && buf_size); |
| 238 DCHECK(!read_buffer_.get()); | 166 DCHECK(!read_buffer_.get()); |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 277 } | 205 } |
| 278 | 206 |
| 279 read_buffer_ = NULL; // Drop our reference. | 207 read_buffer_ = NULL; // Drop our reference. |
| 280 | 208 |
| 281 if (pause_count_ > 0) | 209 if (pause_count_ > 0) |
| 282 *defer = was_deferred_ = true; | 210 *defer = was_deferred_ = true; |
| 283 | 211 |
| 284 return true; | 212 return true; |
| 285 } | 213 } |
| 286 | 214 |
| 287 void DownloadRequestCore::OnResponseCompleted( | 215 DownloadInterruptReason DownloadRequestCore::OnResponseCompleted( |
| 288 const net::URLRequestStatus& status) { | 216 const net::URLRequestStatus& status) { |
| 289 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 217 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 290 int response_code = status.is_success() ? request()->GetResponseCode() : 0; | 218 int response_code = status.is_success() ? request()->GetResponseCode() : 0; |
| 291 DVLOG(20) << __FUNCTION__ << "()" << DebugString() | 219 DVLOG(20) << __FUNCTION__ << "()" << DebugString() |
| 292 << " status.status() = " << status.status() | 220 << " status.status() = " << status.status() |
| 293 << " status.error() = " << status.error() | 221 << " status.error() = " << status.error() |
| 294 << " response_code = " << response_code; | 222 << " response_code = " << response_code; |
| 295 | 223 |
| 296 net::Error error_code = net::OK; | 224 net::Error error_code = net::OK; |
| 297 if (status.status() == net::URLRequestStatus::FAILED || | 225 if (status.status() == net::URLRequestStatus::FAILED || |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 375 if (request()->response_headers()) { | 303 if (request()->response_headers()) { |
| 376 request()->response_headers()->EnumerateHeader(nullptr, "Accept-Ranges", | 304 request()->response_headers()->EnumerateHeader(nullptr, "Accept-Ranges", |
| 377 &accept_ranges); | 305 &accept_ranges); |
| 378 has_strong_validators = | 306 has_strong_validators = |
| 379 request()->response_headers()->HasStrongValidators(); | 307 request()->response_headers()->HasStrongValidators(); |
| 380 } | 308 } |
| 381 RecordAcceptsRanges(accept_ranges, bytes_read_, has_strong_validators); | 309 RecordAcceptsRanges(accept_ranges, bytes_read_, has_strong_validators); |
| 382 RecordNetworkBlockage(base::TimeTicks::Now() - download_start_time_, | 310 RecordNetworkBlockage(base::TimeTicks::Now() - download_start_time_, |
| 383 total_pause_time_); | 311 total_pause_time_); |
| 384 | 312 |
| 385 CallStartedCB(nullptr, reason); | |
| 386 | |
| 387 // Send the info down the stream. Conditional is in case we get | 313 // Send the info down the stream. Conditional is in case we get |
| 388 // OnResponseCompleted without OnResponseStarted. | 314 // OnResponseCompleted without OnResponseStarted. |
| 389 if (stream_writer_) | 315 if (stream_writer_) |
| 390 stream_writer_->Close(reason); | 316 stream_writer_->Close(reason); |
| 391 | 317 |
| 392 // If the error mapped to something unknown, record it so that | 318 // If the error mapped to something unknown, record it so that |
| 393 // we can drill down. | 319 // we can drill down. |
| 394 if (reason == DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED) { | 320 if (reason == DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED) { |
| 395 UMA_HISTOGRAM_SPARSE_SLOWLY("Download.MapErrorNetworkFailed", | 321 UMA_HISTOGRAM_SPARSE_SLOWLY("Download.MapErrorNetworkFailed", |
| 396 std::abs(status.error())); | 322 std::abs(status.error())); |
| 397 } | 323 } |
| 398 | 324 |
| 399 stream_writer_.reset(); // We no longer need the stream. | 325 stream_writer_.reset(); // We no longer need the stream. |
| 400 read_buffer_ = nullptr; | 326 read_buffer_ = nullptr; |
| 327 |
| 328 return reason; |
| 401 } | 329 } |
| 402 | 330 |
| 403 void DownloadRequestCore::PauseRequest() { | 331 void DownloadRequestCore::PauseRequest() { |
| 404 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 332 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 405 | 333 |
| 406 ++pause_count_; | 334 ++pause_count_; |
| 407 } | 335 } |
| 408 | 336 |
| 409 void DownloadRequestCore::ResumeRequest() { | 337 void DownloadRequestCore::ResumeRequest() { |
| 410 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 338 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 411 DCHECK_LT(0, pause_count_); | 339 DCHECK_LT(0, pause_count_); |
| 412 | 340 |
| 413 --pause_count_; | 341 --pause_count_; |
| 414 | 342 |
| 415 if (!was_deferred_) | 343 if (!was_deferred_) |
| 416 return; | 344 return; |
| 417 if (pause_count_ > 0) | 345 if (pause_count_ > 0) |
| 418 return; | 346 return; |
| 419 | 347 |
| 420 was_deferred_ = false; | 348 was_deferred_ = false; |
| 421 if (!last_stream_pause_time_.is_null()) { | 349 if (!last_stream_pause_time_.is_null()) { |
| 422 total_pause_time_ += (base::TimeTicks::Now() - last_stream_pause_time_); | 350 total_pause_time_ += (base::TimeTicks::Now() - last_stream_pause_time_); |
| 423 last_stream_pause_time_ = base::TimeTicks(); | 351 last_stream_pause_time_ = base::TimeTicks(); |
| 424 } | 352 } |
| 425 | 353 |
| 426 downloader_->ResumeReading(); | 354 on_ready_to_read_callback_.Run(); |
| 427 } | 355 } |
| 428 | 356 |
| 429 std::string DownloadRequestCore::DebugString() const { | 357 std::string DownloadRequestCore::DebugString() const { |
| 430 return base::StringPrintf( | 358 return base::StringPrintf( |
| 431 "{" | 359 "{" |
| 432 " url_ = " | 360 " url_ = " |
| 433 "\"%s\"" | 361 "\"%s\"" |
| 434 " }", | 362 " }", |
| 435 request() ? request()->url().spec().c_str() : "<NULL request>"); | 363 request() ? request()->url().spec().c_str() : "<NULL request>"); |
| 436 } | 364 } |
| 437 | 365 |
| 438 } // namespace content | 366 } // namespace content |
| OLD | NEW |