OLD | NEW |
(Empty) | |
| 1 // Copyright 2013 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/download/url_downloader.h" |
| 6 |
| 7 #include "base/bind.h" |
| 8 #include "base/callback.h" |
| 9 #include "content/browser/download/download_create_info.h" |
| 10 #include "content/browser/download/download_interrupt_reasons_impl.h" |
| 11 #include "content/browser/download/download_request_handle.h" |
| 12 #include "content/browser/download/download_request_model.h" |
| 13 #include "content/public/browser/browser_thread.h" |
| 14 #include "content/public/browser/download_url_parameters.h" |
| 15 #include "net/url_request/url_request.h" |
| 16 |
| 17 namespace content { |
| 18 |
| 19 class UrlDownloader::RequestHandle : public DownloadRequestHandle { |
| 20 public: |
| 21 RequestHandle(scoped_ptr<UrlDownloader> downloader); |
| 22 virtual ~RequestHandle(); |
| 23 |
| 24 private: |
| 25 // DownloadRequestHandle. |
| 26 virtual void Start(const RequestStartedCallback& callback) OVERRIDE; |
| 27 virtual void PauseRequest() const OVERRIDE; |
| 28 virtual void ResumeRequest() const OVERRIDE; |
| 29 virtual void CancelRequest() const OVERRIDE; |
| 30 virtual std::string DebugString() const OVERRIDE; |
| 31 |
| 32 scoped_ptr<UrlDownloader> downloader_; |
| 33 |
| 34 DISALLOW_COPY_AND_ASSIGN(RequestHandle); |
| 35 }; |
| 36 |
| 37 UrlDownloader::RequestHandle::RequestHandle( |
| 38 scoped_ptr<UrlDownloader> downloader) |
| 39 : downloader_(downloader.Pass()) {} |
| 40 |
| 41 UrlDownloader::RequestHandle::~RequestHandle() { |
| 42 BrowserThread::DeleteSoon( |
| 43 BrowserThread::IO, FROM_HERE, downloader_.release()); |
| 44 } |
| 45 |
| 46 void UrlDownloader::RequestHandle::Start( |
| 47 const RequestStartedCallback& callback) { |
| 48 BrowserThread::PostTask(BrowserThread::IO, |
| 49 FROM_HERE, |
| 50 base::Bind(&UrlDownloader::Start, |
| 51 base::Unretained(downloader_.get()), |
| 52 callback)); |
| 53 } |
| 54 |
| 55 void UrlDownloader::RequestHandle::PauseRequest() const { |
| 56 BrowserThread::PostTask(BrowserThread::IO, |
| 57 FROM_HERE, |
| 58 base::Bind(&UrlDownloader::PauseRequest, |
| 59 base::Unretained(downloader_.get()))); |
| 60 } |
| 61 |
| 62 void UrlDownloader::RequestHandle::ResumeRequest() const { |
| 63 BrowserThread::PostTask(BrowserThread::IO, |
| 64 FROM_HERE, |
| 65 base::Bind(&UrlDownloader::ResumeRequest, |
| 66 base::Unretained(downloader_.get()))); |
| 67 } |
| 68 |
| 69 void UrlDownloader::RequestHandle::CancelRequest() const { |
| 70 BrowserThread::PostTask(BrowserThread::IO, |
| 71 FROM_HERE, |
| 72 base::Bind(&UrlDownloader::CancelRequest, |
| 73 base::Unretained(downloader_.get()))); |
| 74 } |
| 75 |
| 76 std::string UrlDownloader::RequestHandle::DebugString() const { |
| 77 return std::string(); |
| 78 } |
| 79 |
| 80 // static |
| 81 scoped_ptr<DownloadRequestHandle> UrlDownloader::CreateDownloadRequest( |
| 82 scoped_ptr<DownloadUrlParameters> params) { |
| 83 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 84 scoped_ptr<UrlDownloader> downloader(new UrlDownloader(params.Pass())); |
| 85 scoped_ptr<DownloadRequestHandle> request_handle( |
| 86 new RequestHandle(downloader.Pass())); |
| 87 return request_handle.Pass(); |
| 88 } |
| 89 |
| 90 UrlDownloader::UrlDownloader(scoped_ptr<DownloadUrlParameters> params) |
| 91 : params_(params.Pass()), |
| 92 child_process_id_(params_->render_process_host_id()), |
| 93 is_request_active_(true) { |
| 94 // Can be created on the UI thread. |
| 95 } |
| 96 |
| 97 UrlDownloader::~UrlDownloader() {} |
| 98 |
| 99 void UrlDownloader::Start(const RequestStartedCallback& callback) { |
| 100 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 101 started_callback_ = callback; |
| 102 request_ = DownloadRequestModel::CreateRequest(*params_, this).Pass(); |
| 103 if (!request_) { |
| 104 scoped_ptr<DownloadCreateInfo> empty_create_info; |
| 105 BrowserThread::PostTask( |
| 106 BrowserThread::UI, |
| 107 FROM_HERE, |
| 108 base::Bind(callback, |
| 109 DOWNLOAD_INTERRUPT_REASON_NETWORK_INVALID_REQUEST, |
| 110 base::Passed(&empty_create_info))); |
| 111 return; |
| 112 } |
| 113 |
| 114 scoped_ptr<DownloadSaveInfo> save_info(new DownloadSaveInfo()); |
| 115 save_info->file_path = params_->file_path(); |
| 116 save_info->suggested_name = params_->suggested_name(); |
| 117 save_info->offset = params_->offset(); |
| 118 save_info->hash_state = params_->hash_state(); |
| 119 save_info->prompt_for_save_location = params_->prompt(); |
| 120 save_info->file_stream = params_->GetFileStream(); |
| 121 |
| 122 // TODO(asanka): This request will not have the LOAD_MAYBE_USER_GESTURE flag |
| 123 // set. This is correct since this is a programmatic download. Make sure that |
| 124 // this doesn't affect resumption since a download that was considered safe |
| 125 // before may be considered dangerous on resumption. |
| 126 |
| 127 request_model_.reset( |
| 128 new DownloadRequestModel(request_.get(), save_info.Pass())); |
| 129 request_->Start(); |
| 130 } |
| 131 |
| 132 void UrlDownloader::OnReceivedRedirect(net::URLRequest* request, |
| 133 const GURL& new_url, |
| 134 bool* defer_redirect) { |
| 135 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 136 if (!DownloadRequestModel::IsRequestAllowed(new_url, child_process_id_)) { |
| 137 request_model_->OnDownloadInterrupted( |
| 138 DOWNLOAD_INTERRUPT_REASON_NETWORK_INVALID_REQUEST); |
| 139 request_->Cancel(); |
| 140 return; |
| 141 } |
| 142 request_model_->OnRequestRedirected(new_url); |
| 143 } |
| 144 |
| 145 void UrlDownloader::OnResponseStarted(net::URLRequest* request) { |
| 146 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 147 if (!request_->status().is_success()) { |
| 148 DownloadInterruptReason reason = ConvertNetErrorToInterruptReason( |
| 149 static_cast<net::Error>(request_->status().error()), |
| 150 DOWNLOAD_INTERRUPT_FROM_NETWORK); |
| 151 scoped_ptr<DownloadCreateInfo> empty_create_info; |
| 152 BrowserThread::PostTask( |
| 153 BrowserThread::UI, |
| 154 FROM_HERE, |
| 155 base::Bind( |
| 156 started_callback_, reason, base::Passed(&empty_create_info))); |
| 157 request_->Cancel(); |
| 158 return; |
| 159 } |
| 160 |
| 161 scoped_ptr<DownloadCreateInfo> create_info = |
| 162 request_model_->OnResponseStarted( |
| 163 std::string(), |
| 164 false, |
| 165 PAGE_TRANSITION_LINK, |
| 166 base::Bind(&UrlDownloader::ResumeRequest, base::Unretained(this))); |
| 167 BrowserThread::PostTask(BrowserThread::UI, |
| 168 FROM_HERE, |
| 169 base::Bind(started_callback_, |
| 170 DOWNLOAD_INTERRUPT_REASON_NONE, |
| 171 base::Passed(&create_info))); |
| 172 ReadNextChunk(); |
| 173 } |
| 174 |
| 175 void UrlDownloader::OnReadCompleted(net::URLRequest* request, int bytes_read) { |
| 176 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 177 HandleCompletedRead(bytes_read); |
| 178 ReadNextChunk(); |
| 179 } |
| 180 |
| 181 void UrlDownloader::PauseRequest() { |
| 182 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 183 request_model_->OnPauseRequest(); |
| 184 } |
| 185 |
| 186 void UrlDownloader::ResumeRequest() { |
| 187 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 188 if (request_model_->OnResumeRequest() == |
| 189 DownloadRequestModel::READY_TO_READ && |
| 190 !is_request_active_ && |
| 191 request_->status().is_success()) |
| 192 ReadNextChunk(); |
| 193 } |
| 194 |
| 195 void UrlDownloader::CancelRequest() { |
| 196 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 197 request_->Cancel(); |
| 198 } |
| 199 |
| 200 void UrlDownloader::ReadNextChunk() { |
| 201 net::IOBuffer* buffer = NULL; |
| 202 int buf_size = 0; |
| 203 int bytes_read = 0; |
| 204 bool read_succeeded = false; |
| 205 |
| 206 if (!is_request_active_) |
| 207 return; |
| 208 |
| 209 do { |
| 210 request_model_->OnWillRead(&buffer, &buf_size, -1); |
| 211 DCHECK_LT(0, buf_size); |
| 212 |
| 213 // TODO(asanka): Check for re-entrancy issues. Can Read() result in a call |
| 214 // into OnReadCompleted()? |
| 215 read_succeeded = request_->Read(buffer, buf_size, &bytes_read); |
| 216 |
| 217 // Read returns false if IO is pending or if the read failed. |
| 218 if (read_succeeded || !request_->status().is_success()) |
| 219 HandleCompletedRead(bytes_read); |
| 220 } while(read_succeeded && is_request_active_); |
| 221 } |
| 222 |
| 223 void UrlDownloader::HandleCompletedRead(int bytes_read) { |
| 224 DownloadRequestModel::ReadState read_state = |
| 225 request_model_->OnReadCompleted(bytes_read); |
| 226 is_request_active_ = (read_state == DownloadRequestModel::READY_TO_READ); |
| 227 if (bytes_read == 0 || !request_->status().is_success()) { |
| 228 request_model_->OnResponseCompleted(); |
| 229 is_request_active_ = false; |
| 230 } |
| 231 } |
| 232 |
| 233 } // namespace content |
OLD | NEW |