blob: 7f9effc15bc91d3c00b8092c0ccb743a44b79c94 [file] [log] [blame]
[email protected]45e4fab22012-01-09 19:38:021// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]21d7a4e302011-08-15 16:17:122// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]cdcf60242011-11-04 15:53:435#include "chrome/browser/download/download_extension_api.h"
[email protected]21d7a4e302011-08-15 16:17:126
7#include <algorithm>
[email protected]8e3ae68c2011-09-16 22:15:478#include <cctype>
[email protected]370e9502011-09-14 19:31:449#include <iterator>
10#include <set>
[email protected]21d7a4e302011-08-15 16:17:1211#include <string>
12
[email protected]8e3ae68c2011-09-16 22:15:4713#include "base/bind.h"
[email protected]c579aa272011-12-10 00:17:1614#include "base/bind_helpers.h"
[email protected]8e3ae68c2011-09-16 22:15:4715#include "base/callback.h"
[email protected]370e9502011-09-14 19:31:4416#include "base/json/json_writer.h"
[email protected]21d7a4e302011-08-15 16:17:1217#include "base/logging.h"
[email protected]8a9bc222011-08-24 18:21:0018#include "base/metrics/histogram.h"
[email protected]21d7a4e302011-08-15 16:17:1219#include "base/stl_util.h"
[email protected]8e3ae68c2011-09-16 22:15:4720#include "base/string16.h"
21#include "base/string_util.h"
22#include "base/stringprintf.h"
[email protected]21d7a4e302011-08-15 16:17:1223#include "base/values.h"
24#include "chrome/browser/browser_process.h"
[email protected]45e4fab22012-01-09 19:38:0225#include "chrome/browser/download/download_file_icon_extractor.h"
[email protected]9bb54ee2011-10-12 17:43:3526#include "chrome/browser/download/download_service.h"
27#include "chrome/browser/download/download_service_factory.h"
[email protected]21d7a4e302011-08-15 16:17:1228#include "chrome/browser/download/download_util.h"
[email protected]370e9502011-09-14 19:31:4429#include "chrome/browser/extensions/extension_event_names.h"
30#include "chrome/browser/extensions/extension_event_router.h"
[email protected]21d7a4e302011-08-15 16:17:1231#include "chrome/browser/icon_loader.h"
32#include "chrome/browser/icon_manager.h"
33#include "chrome/browser/renderer_host/chrome_render_message_filter.h"
34#include "chrome/browser/ui/browser_list.h"
[email protected]45e4fab22012-01-09 19:38:0235#include "chrome/browser/ui/webui/web_ui_util.h"
[email protected]b39e7a88b2012-01-10 21:43:1736#include "content/browser/download/download_file_manager.h"
[email protected]5d3e83642011-12-16 01:14:3637#include "content/browser/download/download_id.h"
[email protected]3ab4c0a2011-11-17 22:47:4038#include "content/browser/download/download_state_info.h"
[email protected]8e3ae68c2011-09-16 22:15:4739#include "content/browser/download/download_types.h"
[email protected]be76b7e2011-10-13 12:57:5740#include "content/browser/download/interrupt_reasons.h"
[email protected]21d7a4e302011-08-15 16:17:1241#include "content/browser/renderer_host/render_view_host.h"
42#include "content/browser/renderer_host/resource_dispatcher_host.h"
[email protected]e582fdd2011-12-20 16:48:1743#include "content/public/browser/download_item.h"
[email protected]f3b1a082011-11-18 00:34:3044#include "content/public/browser/render_process_host.h"
[email protected]8e3ae68c2011-09-16 22:15:4745#include "net/http/http_util.h"
46#include "net/url_request/url_request.h"
[email protected]21d7a4e302011-08-15 16:17:1247
[email protected]631bb742011-11-02 11:29:3948using content::BrowserThread;
[email protected]e582fdd2011-12-20 16:48:1749using content::DownloadItem;
50using content::DownloadManager;
[email protected]631bb742011-11-02 11:29:3951
[email protected]5d3e83642011-12-16 01:14:3652namespace download_extension_errors {
[email protected]cdcf60242011-11-04 15:53:4353
54// Error messages
[email protected]cdcf60242011-11-04 15:53:4355const char kGenericError[] = "I'm afraid I can't do that.";
[email protected]45e4fab22012-01-09 19:38:0256const char kIconNotFoundError[] = "Icon not found.";
[email protected]5d3e83642011-12-16 01:14:3657const char kInvalidOperationError[] = "Invalid operation.";
[email protected]45e4fab22012-01-09 19:38:0258const char kInvalidURLError[] = "Invalid URL.";
59const char kNotImplementedError[] = "NotImplemented.";
[email protected]5d3e83642011-12-16 01:14:3660
61} // namespace download_extension_errors
62
63namespace {
[email protected]cdcf60242011-11-04 15:53:4364
[email protected]45e4fab22012-01-09 19:38:0265// Default icon size for getFileIcon() in pixels.
66const int kDefaultIconSize = 32;
67
[email protected]cdcf60242011-11-04 15:53:4368// Parameter keys
69const char kBodyKey[] = "body";
70const char kBytesReceivedKey[] = "bytesReceived";
71const char kDangerAcceptedKey[] = "dangerAccepted";
[email protected]3ab4c0a2011-11-17 22:47:4072const char kDangerContent[] = "content";
[email protected]cdcf60242011-11-04 15:53:4373const char kDangerFile[] = "file";
74const char kDangerKey[] = "danger";
75const char kDangerSafe[] = "safe";
76const char kDangerUrl[] = "url";
77const char kEndTimeKey[] = "endTime";
78const char kErrorKey[] = "error";
79const char kFileSizeKey[] = "fileSize";
80const char kFilenameKey[] = "filename";
81const char kHeaderNameKey[] = "name";
82const char kHeaderValueKey[] = "value";
83const char kHeadersKey[] = "headers";
84const char kIdKey[] = "id";
85const char kMethodKey[] = "method";
86const char kMimeKey[] = "mime";
87const char kPausedKey[] = "paused";
88const char kSaveAsKey[] = "saveAs";
[email protected]45e4fab22012-01-09 19:38:0289const char kSizeKey[] = "size";
[email protected]cdcf60242011-11-04 15:53:4390const char kStartTimeKey[] = "startTime";
91const char kStateComplete[] = "complete";
92const char kStateInProgress[] = "in_progress";
93const char kStateInterrupted[] = "interrupted";
94const char kStateKey[] = "state";
95const char kTotalBytesKey[] = "totalBytes";
96const char kUrlKey[] = "url";
97
[email protected]3ab4c0a2011-11-17 22:47:4098const char* DangerString(DownloadStateInfo::DangerType danger) {
[email protected]cdcf60242011-11-04 15:53:4399 switch (danger) {
[email protected]3ab4c0a2011-11-17 22:47:40100 case DownloadStateInfo::MAYBE_DANGEROUS_CONTENT:
101 case DownloadStateInfo::NOT_DANGEROUS: return kDangerSafe;
102 case DownloadStateInfo::DANGEROUS_FILE: return kDangerFile;
103 case DownloadStateInfo::DANGEROUS_URL: return kDangerUrl;
104 case DownloadStateInfo::DANGEROUS_CONTENT: return kDangerContent;
[email protected]cdcf60242011-11-04 15:53:43105 default:
106 NOTREACHED();
107 return "";
108 }
109}
110
111const char* StateString(DownloadItem::DownloadState state) {
112 switch (state) {
113 case DownloadItem::IN_PROGRESS: return kStateInProgress;
114 case DownloadItem::COMPLETE: return kStateComplete;
115 case DownloadItem::INTERRUPTED: // fall through
116 case DownloadItem::CANCELLED: return kStateInterrupted;
117 case DownloadItem::REMOVING: // fall through
118 default:
119 NOTREACHED();
120 return "";
121 }
122}
123
124} // namespace
[email protected]21d7a4e302011-08-15 16:17:12125
[email protected]8a9bc222011-08-24 18:21:00126bool DownloadsFunctionInterface::RunImplImpl(
127 DownloadsFunctionInterface* pimpl) {
128 CHECK(pimpl);
129 if (!pimpl->ParseArgs()) return false;
130 UMA_HISTOGRAM_ENUMERATION(
131 "Download.ApiFunctions", pimpl->function(), DOWNLOADS_FUNCTION_LAST);
[email protected]5d3e83642011-12-16 01:14:36132 return pimpl->RunInternal();
[email protected]8a9bc222011-08-24 18:21:00133}
134
135SyncDownloadsFunction::SyncDownloadsFunction(
136 DownloadsFunctionInterface::DownloadsFunctionName function)
137 : function_(function) {
138}
139
140SyncDownloadsFunction::~SyncDownloadsFunction() {}
141
142bool SyncDownloadsFunction::RunImpl() {
143 return DownloadsFunctionInterface::RunImplImpl(this);
144}
145
146DownloadsFunctionInterface::DownloadsFunctionName
147SyncDownloadsFunction::function() const {
148 return function_;
149}
150
151AsyncDownloadsFunction::AsyncDownloadsFunction(
152 DownloadsFunctionInterface::DownloadsFunctionName function)
153 : function_(function) {
154}
155
156AsyncDownloadsFunction::~AsyncDownloadsFunction() {}
157
158bool AsyncDownloadsFunction::RunImpl() {
159 return DownloadsFunctionInterface::RunImplImpl(this);
160}
161
162DownloadsFunctionInterface::DownloadsFunctionName
163AsyncDownloadsFunction::function() const {
164 return function_;
165}
166
[email protected]21d7a4e302011-08-15 16:17:12167DownloadsDownloadFunction::DownloadsDownloadFunction()
[email protected]8e3ae68c2011-09-16 22:15:47168 : AsyncDownloadsFunction(DOWNLOADS_FUNCTION_DOWNLOAD) {
[email protected]21d7a4e302011-08-15 16:17:12169}
[email protected]8a9bc222011-08-24 18:21:00170
[email protected]21d7a4e302011-08-15 16:17:12171DownloadsDownloadFunction::~DownloadsDownloadFunction() {}
172
[email protected]8e3ae68c2011-09-16 22:15:47173DownloadsDownloadFunction::IOData::IOData()
174 : save_as(false),
175 extra_headers(NULL),
176 method("GET"),
177 rdh(NULL),
178 resource_context(NULL),
179 render_process_host_id(0),
180 render_view_host_routing_id(0) {
181}
182
183DownloadsDownloadFunction::IOData::~IOData() {}
184
[email protected]8a9bc222011-08-24 18:21:00185bool DownloadsDownloadFunction::ParseArgs() {
[email protected]21d7a4e302011-08-15 16:17:12186 base::DictionaryValue* options = NULL;
[email protected]8e3ae68c2011-09-16 22:15:47187 std::string url;
188 iodata_.reset(new IOData());
[email protected]21d7a4e302011-08-15 16:17:12189 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &options));
[email protected]cdcf60242011-11-04 15:53:43190 EXTENSION_FUNCTION_VALIDATE(options->GetString(kUrlKey, &url));
[email protected]8e3ae68c2011-09-16 22:15:47191 iodata_->url = GURL(url);
192 if (!iodata_->url.is_valid()) {
[email protected]5d3e83642011-12-16 01:14:36193 error_ = download_extension_errors::kInvalidURLError;
[email protected]8e3ae68c2011-09-16 22:15:47194 return false;
195 }
[email protected]cdcf60242011-11-04 15:53:43196 if (options->HasKey(kFilenameKey))
[email protected]21d7a4e302011-08-15 16:17:12197 EXTENSION_FUNCTION_VALIDATE(options->GetString(
[email protected]cdcf60242011-11-04 15:53:43198 kFilenameKey, &iodata_->filename));
[email protected]8e3ae68c2011-09-16 22:15:47199 // TODO(benjhayden): More robust validation of filename.
200 if (((iodata_->filename[0] == L'.') && (iodata_->filename[1] == L'.')) ||
201 (iodata_->filename[0] == L'/')) {
[email protected]5d3e83642011-12-16 01:14:36202 error_ = download_extension_errors::kGenericError;
[email protected]8e3ae68c2011-09-16 22:15:47203 return false;
204 }
[email protected]cdcf60242011-11-04 15:53:43205 if (options->HasKey(kSaveAsKey))
[email protected]21d7a4e302011-08-15 16:17:12206 EXTENSION_FUNCTION_VALIDATE(options->GetBoolean(
[email protected]cdcf60242011-11-04 15:53:43207 kSaveAsKey, &iodata_->save_as));
208 if (options->HasKey(kMethodKey))
[email protected]21d7a4e302011-08-15 16:17:12209 EXTENSION_FUNCTION_VALIDATE(options->GetString(
[email protected]cdcf60242011-11-04 15:53:43210 kMethodKey, &iodata_->method));
[email protected]8e3ae68c2011-09-16 22:15:47211 // It's ok to use a pointer to extra_headers without DeepCopy()ing because
212 // |args_| (which owns *extra_headers) is guaranteed to live as long as
213 // |this|.
[email protected]cdcf60242011-11-04 15:53:43214 if (options->HasKey(kHeadersKey))
[email protected]8e3ae68c2011-09-16 22:15:47215 EXTENSION_FUNCTION_VALIDATE(options->GetList(
[email protected]cdcf60242011-11-04 15:53:43216 kHeadersKey, &iodata_->extra_headers));
217 if (options->HasKey(kBodyKey))
[email protected]21d7a4e302011-08-15 16:17:12218 EXTENSION_FUNCTION_VALIDATE(options->GetString(
[email protected]cdcf60242011-11-04 15:53:43219 kBodyKey, &iodata_->post_body));
[email protected]8e3ae68c2011-09-16 22:15:47220 if (iodata_->extra_headers != NULL) {
221 for (size_t index = 0; index < iodata_->extra_headers->GetSize(); ++index) {
222 base::DictionaryValue* header = NULL;
223 std::string name, value;
224 EXTENSION_FUNCTION_VALIDATE(iodata_->extra_headers->GetDictionary(
225 index, &header));
226 EXTENSION_FUNCTION_VALIDATE(header->GetString(
[email protected]cdcf60242011-11-04 15:53:43227 kHeaderNameKey, &name));
[email protected]8e3ae68c2011-09-16 22:15:47228 EXTENSION_FUNCTION_VALIDATE(header->GetString(
[email protected]cdcf60242011-11-04 15:53:43229 kHeaderValueKey, &value));
[email protected]8e3ae68c2011-09-16 22:15:47230 if (!net::HttpUtil::IsSafeHeader(name)) {
[email protected]5d3e83642011-12-16 01:14:36231 error_ = download_extension_errors::kGenericError;
[email protected]8e3ae68c2011-09-16 22:15:47232 return false;
233 }
234 }
235 }
[email protected]b39e7a88b2012-01-10 21:43:17236 iodata_->rdh = g_browser_process->resource_dispatcher_host();
[email protected]8e3ae68c2011-09-16 22:15:47237 iodata_->resource_context = &profile()->GetResourceContext();
[email protected]f3b1a082011-11-18 00:34:30238 iodata_->render_process_host_id = render_view_host()->process()->GetID();
[email protected]8e3ae68c2011-09-16 22:15:47239 iodata_->render_view_host_routing_id = render_view_host()->routing_id();
240 return true;
[email protected]21d7a4e302011-08-15 16:17:12241}
242
[email protected]5d3e83642011-12-16 01:14:36243bool DownloadsDownloadFunction::RunInternal() {
[email protected]8e3ae68c2011-09-16 22:15:47244 VLOG(1) << __FUNCTION__ << " " << iodata_->url.spec();
[email protected]53612e82011-10-18 18:00:36245 if (!BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(
246 &DownloadsDownloadFunction::BeginDownloadOnIOThread, this))) {
[email protected]5d3e83642011-12-16 01:14:36247 error_ = download_extension_errors::kGenericError;
248 return false;
[email protected]8e3ae68c2011-09-16 22:15:47249 }
[email protected]5d3e83642011-12-16 01:14:36250 return true;
[email protected]8e3ae68c2011-09-16 22:15:47251}
252
253void DownloadsDownloadFunction::BeginDownloadOnIOThread() {
254 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
255 DVLOG(1) << __FUNCTION__ << " " << iodata_->url.spec();
256 DownloadSaveInfo save_info;
257 // TODO(benjhayden) Ensure that this filename is interpreted as a path
258 // relative to the default downloads directory without allowing '..'.
259 save_info.suggested_name = iodata_->filename;
260 net::URLRequest* request = new net::URLRequest(iodata_->url, iodata_->rdh);
261 request->set_method(iodata_->method);
262 if (iodata_->extra_headers != NULL) {
263 for (size_t index = 0; index < iodata_->extra_headers->GetSize(); ++index) {
264 base::DictionaryValue* header = NULL;
265 std::string name, value;
266 CHECK(iodata_->extra_headers->GetDictionary(index, &header));
267 CHECK(header->GetString("name", &name));
268 CHECK(header->GetString("value", &value));
269 request->SetExtraRequestHeaderByName(name, value, false/*overwrite*/);
270 }
271 }
272 if (!iodata_->post_body.empty()) {
273 request->AppendBytesToUpload(iodata_->post_body.data(),
274 iodata_->post_body.size());
275 }
276 iodata_->rdh->BeginDownload(
277 request,
278 save_info,
279 iodata_->save_as,
280 base::Bind(&DownloadsDownloadFunction::OnStarted, this),
281 iodata_->render_process_host_id,
282 iodata_->render_view_host_routing_id,
283 *(iodata_->resource_context));
284 iodata_.reset();
285}
286
[email protected]eda58402011-09-21 19:32:02287void DownloadsDownloadFunction::OnStarted(DownloadId dl_id, net::Error error) {
[email protected]8e3ae68c2011-09-16 22:15:47288 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
289 VLOG(1) << __FUNCTION__ << " " << dl_id << " " << error;
[email protected]53612e82011-10-18 18:00:36290 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
291 &DownloadsDownloadFunction::RespondOnUIThread, this,
[email protected]eda58402011-09-21 19:32:02292 dl_id.local(), error));
[email protected]8e3ae68c2011-09-16 22:15:47293}
294
295void DownloadsDownloadFunction::RespondOnUIThread(int dl_id, net::Error error) {
296 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
297 VLOG(1) << __FUNCTION__;
298 if (dl_id >= 0) {
299 result_.reset(base::Value::CreateIntegerValue(dl_id));
300 } else {
301 error_ = net::ErrorToString(error);
302 }
303 SendResponse(error_.empty());
[email protected]8a9bc222011-08-24 18:21:00304}
305
306DownloadsSearchFunction::DownloadsSearchFunction()
307 : SyncDownloadsFunction(DOWNLOADS_FUNCTION_SEARCH) {
308}
309
[email protected]21d7a4e302011-08-15 16:17:12310DownloadsSearchFunction::~DownloadsSearchFunction() {}
311
[email protected]8a9bc222011-08-24 18:21:00312bool DownloadsSearchFunction::ParseArgs() {
[email protected]8e3ae68c2011-09-16 22:15:47313 base::DictionaryValue* query_json = NULL;
[email protected]21d7a4e302011-08-15 16:17:12314 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &query_json));
[email protected]5d3e83642011-12-16 01:14:36315 error_ = download_extension_errors::kNotImplementedError;
[email protected]21d7a4e302011-08-15 16:17:12316 return false;
317}
318
[email protected]5d3e83642011-12-16 01:14:36319bool DownloadsSearchFunction::RunInternal() {
[email protected]8a9bc222011-08-24 18:21:00320 NOTIMPLEMENTED();
[email protected]5d3e83642011-12-16 01:14:36321 return false;
[email protected]8a9bc222011-08-24 18:21:00322}
323
324DownloadsPauseFunction::DownloadsPauseFunction()
[email protected]5d3e83642011-12-16 01:14:36325 : SyncDownloadsFunction(DOWNLOADS_FUNCTION_PAUSE),
326 download_id_(DownloadId::Invalid().local()) {
[email protected]8a9bc222011-08-24 18:21:00327}
328
[email protected]21d7a4e302011-08-15 16:17:12329DownloadsPauseFunction::~DownloadsPauseFunction() {}
330
[email protected]8a9bc222011-08-24 18:21:00331bool DownloadsPauseFunction::ParseArgs() {
[email protected]5d3e83642011-12-16 01:14:36332 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &download_id_));
333 return true;
[email protected]21d7a4e302011-08-15 16:17:12334}
335
[email protected]5d3e83642011-12-16 01:14:36336bool DownloadsPauseFunction::RunInternal() {
337 DownloadManager* download_manager =
338 DownloadServiceFactory::GetForProfile(profile())->GetDownloadManager();
339 DownloadItem* download_item =
340 download_manager->GetActiveDownloadItem(download_id_);
341 DCHECK(!download_item || download_item->IsInProgress());
342
343 if (!download_item) {
344 // This could be due to an invalid download ID, or it could be due to the
345 // download not being currently active.
346 error_ = download_extension_errors::kInvalidOperationError;
347 } else if (!download_item->IsPaused()) {
348 // If download_item->IsPaused() already then we treat it as a success.
349 download_item->TogglePause();
350 }
351 return error_.empty();
[email protected]8a9bc222011-08-24 18:21:00352}
353
354DownloadsResumeFunction::DownloadsResumeFunction()
[email protected]5d3e83642011-12-16 01:14:36355 : SyncDownloadsFunction(DOWNLOADS_FUNCTION_RESUME),
356 download_id_(DownloadId::Invalid().local()) {
[email protected]8a9bc222011-08-24 18:21:00357}
358
[email protected]21d7a4e302011-08-15 16:17:12359DownloadsResumeFunction::~DownloadsResumeFunction() {}
360
[email protected]8a9bc222011-08-24 18:21:00361bool DownloadsResumeFunction::ParseArgs() {
[email protected]5d3e83642011-12-16 01:14:36362 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &download_id_));
363 return true;
[email protected]21d7a4e302011-08-15 16:17:12364}
365
[email protected]5d3e83642011-12-16 01:14:36366bool DownloadsResumeFunction::RunInternal() {
367 DownloadManager* download_manager =
368 DownloadServiceFactory::GetForProfile(profile())->GetDownloadManager();
369 DownloadItem* download_item =
370 download_manager->GetActiveDownloadItem(download_id_);
371 DCHECK(!download_item || download_item->IsInProgress());
372
373 if (!download_item) {
374 // This could be due to an invalid download ID, or it could be due to the
375 // download not being currently active.
376 error_ = download_extension_errors::kInvalidOperationError;
377 } else if (download_item->IsPaused()) {
378 // If !download_item->IsPaused() already, then we treat it as a success.
379 download_item->TogglePause();
380 }
381 return error_.empty();
[email protected]8a9bc222011-08-24 18:21:00382}
383
384DownloadsCancelFunction::DownloadsCancelFunction()
[email protected]5d3e83642011-12-16 01:14:36385 : SyncDownloadsFunction(DOWNLOADS_FUNCTION_CANCEL),
386 download_id_(DownloadId::Invalid().local()) {
[email protected]8a9bc222011-08-24 18:21:00387}
388
[email protected]21d7a4e302011-08-15 16:17:12389DownloadsCancelFunction::~DownloadsCancelFunction() {}
390
[email protected]8a9bc222011-08-24 18:21:00391bool DownloadsCancelFunction::ParseArgs() {
[email protected]5d3e83642011-12-16 01:14:36392 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &download_id_));
393 return true;
[email protected]21d7a4e302011-08-15 16:17:12394}
395
[email protected]5d3e83642011-12-16 01:14:36396bool DownloadsCancelFunction::RunInternal() {
397 DownloadManager* download_manager =
398 DownloadServiceFactory::GetForProfile(profile())->GetDownloadManager();
399 DownloadItem* download_item =
400 download_manager->GetActiveDownloadItem(download_id_);
401
402 if (download_item)
403 download_item->Cancel(true);
404 // |download_item| can be NULL if the download ID was invalid or if the
405 // download is not currently active. Either way, we don't consider it a
406 // failure.
407 return error_.empty();
[email protected]8a9bc222011-08-24 18:21:00408}
409
410DownloadsEraseFunction::DownloadsEraseFunction()
411 : AsyncDownloadsFunction(DOWNLOADS_FUNCTION_ERASE) {
412}
413
[email protected]21d7a4e302011-08-15 16:17:12414DownloadsEraseFunction::~DownloadsEraseFunction() {}
415
[email protected]8a9bc222011-08-24 18:21:00416bool DownloadsEraseFunction::ParseArgs() {
[email protected]8e3ae68c2011-09-16 22:15:47417 base::DictionaryValue* query_json = NULL;
[email protected]21d7a4e302011-08-15 16:17:12418 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &query_json));
[email protected]5d3e83642011-12-16 01:14:36419 error_ = download_extension_errors::kNotImplementedError;
[email protected]21d7a4e302011-08-15 16:17:12420 return false;
421}
422
[email protected]5d3e83642011-12-16 01:14:36423bool DownloadsEraseFunction::RunInternal() {
[email protected]8a9bc222011-08-24 18:21:00424 NOTIMPLEMENTED();
[email protected]5d3e83642011-12-16 01:14:36425 return false;
[email protected]8a9bc222011-08-24 18:21:00426}
427
428DownloadsSetDestinationFunction::DownloadsSetDestinationFunction()
429 : AsyncDownloadsFunction(DOWNLOADS_FUNCTION_SET_DESTINATION) {
430}
431
[email protected]21d7a4e302011-08-15 16:17:12432DownloadsSetDestinationFunction::~DownloadsSetDestinationFunction() {}
433
[email protected]8a9bc222011-08-24 18:21:00434bool DownloadsSetDestinationFunction::ParseArgs() {
[email protected]21d7a4e302011-08-15 16:17:12435 int dl_id = 0;
436 std::string path;
437 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &dl_id));
438 EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &path));
439 VLOG(1) << __FUNCTION__ << " " << dl_id << " " << &path;
[email protected]5d3e83642011-12-16 01:14:36440 error_ = download_extension_errors::kNotImplementedError;
[email protected]21d7a4e302011-08-15 16:17:12441 return false;
442}
443
[email protected]5d3e83642011-12-16 01:14:36444bool DownloadsSetDestinationFunction::RunInternal() {
[email protected]8a9bc222011-08-24 18:21:00445 NOTIMPLEMENTED();
[email protected]5d3e83642011-12-16 01:14:36446 return false;
[email protected]8a9bc222011-08-24 18:21:00447}
448
449DownloadsAcceptDangerFunction::DownloadsAcceptDangerFunction()
450 : AsyncDownloadsFunction(DOWNLOADS_FUNCTION_ACCEPT_DANGER) {
451}
452
[email protected]21d7a4e302011-08-15 16:17:12453DownloadsAcceptDangerFunction::~DownloadsAcceptDangerFunction() {}
454
[email protected]8a9bc222011-08-24 18:21:00455bool DownloadsAcceptDangerFunction::ParseArgs() {
[email protected]21d7a4e302011-08-15 16:17:12456 int dl_id = 0;
457 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &dl_id));
458 VLOG(1) << __FUNCTION__ << " " << dl_id;
[email protected]5d3e83642011-12-16 01:14:36459 error_ = download_extension_errors::kNotImplementedError;
[email protected]21d7a4e302011-08-15 16:17:12460 return false;
461}
462
[email protected]5d3e83642011-12-16 01:14:36463bool DownloadsAcceptDangerFunction::RunInternal() {
[email protected]8a9bc222011-08-24 18:21:00464 NOTIMPLEMENTED();
[email protected]5d3e83642011-12-16 01:14:36465 return false;
[email protected]8a9bc222011-08-24 18:21:00466}
467
468DownloadsShowFunction::DownloadsShowFunction()
469 : AsyncDownloadsFunction(DOWNLOADS_FUNCTION_SHOW) {
470}
471
[email protected]21d7a4e302011-08-15 16:17:12472DownloadsShowFunction::~DownloadsShowFunction() {}
473
[email protected]8a9bc222011-08-24 18:21:00474bool DownloadsShowFunction::ParseArgs() {
[email protected]21d7a4e302011-08-15 16:17:12475 int dl_id = 0;
476 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &dl_id));
477 VLOG(1) << __FUNCTION__ << " " << dl_id;
[email protected]5d3e83642011-12-16 01:14:36478 error_ = download_extension_errors::kNotImplementedError;
[email protected]21d7a4e302011-08-15 16:17:12479 return false;
480}
481
[email protected]5d3e83642011-12-16 01:14:36482bool DownloadsShowFunction::RunInternal() {
[email protected]8a9bc222011-08-24 18:21:00483 NOTIMPLEMENTED();
[email protected]5d3e83642011-12-16 01:14:36484 return false;
[email protected]8a9bc222011-08-24 18:21:00485}
486
487DownloadsDragFunction::DownloadsDragFunction()
488 : AsyncDownloadsFunction(DOWNLOADS_FUNCTION_DRAG) {
489}
490
[email protected]21d7a4e302011-08-15 16:17:12491DownloadsDragFunction::~DownloadsDragFunction() {}
492
[email protected]8a9bc222011-08-24 18:21:00493bool DownloadsDragFunction::ParseArgs() {
[email protected]21d7a4e302011-08-15 16:17:12494 int dl_id = 0;
495 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &dl_id));
496 VLOG(1) << __FUNCTION__ << " " << dl_id;
[email protected]5d3e83642011-12-16 01:14:36497 error_ = download_extension_errors::kNotImplementedError;
[email protected]21d7a4e302011-08-15 16:17:12498 return false;
499}
[email protected]8a9bc222011-08-24 18:21:00500
[email protected]5d3e83642011-12-16 01:14:36501bool DownloadsDragFunction::RunInternal() {
[email protected]8a9bc222011-08-24 18:21:00502 NOTIMPLEMENTED();
[email protected]5d3e83642011-12-16 01:14:36503 return false;
[email protected]8a9bc222011-08-24 18:21:00504}
[email protected]370e9502011-09-14 19:31:44505
506namespace {
[email protected]45e4fab22012-01-09 19:38:02507
508class DownloadFileIconExtractorImpl : public DownloadFileIconExtractor {
509 public:
510 DownloadFileIconExtractorImpl() {}
511
512 ~DownloadFileIconExtractorImpl() {}
513
514 virtual bool ExtractIconURLForPath(const FilePath& path,
515 IconLoader::IconSize icon_size,
516 IconURLCallback callback) OVERRIDE;
517 private:
518 void OnIconLoadComplete(IconManager::Handle handle, gfx::Image* icon);
519
520 CancelableRequestConsumer cancelable_consumer_;
521 IconURLCallback callback_;
522};
523
524bool DownloadFileIconExtractorImpl::ExtractIconURLForPath(
525 const FilePath& path,
526 IconLoader::IconSize icon_size,
527 IconURLCallback callback) {
528 callback_ = callback;
529 IconManager* im = g_browser_process->icon_manager();
530 // The contents of the file at |path| may have changed since a previous
531 // request, in which case the associated icon may also have changed.
532 // Therefore, for the moment we always call LoadIcon instead of attempting
533 // a LookupIcon.
534 im->LoadIcon(
535 path, icon_size, &cancelable_consumer_,
536 base::Bind(&DownloadFileIconExtractorImpl::OnIconLoadComplete,
537 base::Unretained(this)));
538 return true;
539}
540
541void DownloadFileIconExtractorImpl::OnIconLoadComplete(
542 IconManager::Handle handle,
543 gfx::Image* icon) {
544 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
545 std::string url;
546 if (icon)
547 url = web_ui_util::GetImageDataUrl(*icon);
548 callback_.Run(url);
549}
550
551IconLoader::IconSize IconLoaderSizeFromPixelSize(int pixel_size) {
552 switch (pixel_size) {
553 case 16: return IconLoader::SMALL;
554 case 32: return IconLoader::NORMAL;
555 default:
556 NOTREACHED();
557 return IconLoader::NORMAL;
558 }
559}
560
561} // namespace
562
563DownloadsGetFileIconFunction::DownloadsGetFileIconFunction()
564 : AsyncDownloadsFunction(DOWNLOADS_FUNCTION_GET_FILE_ICON),
565 icon_size_(kDefaultIconSize),
566 icon_extractor_(new DownloadFileIconExtractorImpl()) {
567}
568
569DownloadsGetFileIconFunction::~DownloadsGetFileIconFunction() {}
570
571void DownloadsGetFileIconFunction::SetIconExtractorForTesting(
572 DownloadFileIconExtractor* extractor) {
573 DCHECK(extractor);
574 icon_extractor_.reset(extractor);
575}
576
577bool DownloadsGetFileIconFunction::ParseArgs() {
578 int dl_id = 0;
579 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &dl_id));
580
581 base::DictionaryValue* options = NULL;
582 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &options));
583 if (options->HasKey(kSizeKey)) {
584 EXTENSION_FUNCTION_VALIDATE(options->GetInteger(kSizeKey, &icon_size_));
585 // We only support 16px and 32px icons. This is enforced in
586 // experimental.downloads.json.
587 DCHECK(icon_size_ == 16 || icon_size_ == 32);
588 }
589
590 DownloadManager* download_manager =
591 DownloadServiceFactory::GetForProfile(profile())->GetDownloadManager();
592 DownloadItem* download_item = download_manager->GetDownloadItem(dl_id);
593 if (download_item == NULL) {
594 // The DownloadItem is is added to history when the path is determined. If
595 // the download is not in history, then we don't have a path / final
596 // filename and no icon.
597 error_ = download_extension_errors::kInvalidOperationError;
598 return false;
599 }
600 // In-progress downloads return the intermediate filename for GetFullPath()
601 // which doesn't have the final extension. Therefore we won't be able to
602 // derive a good file icon for it. So we use GetTargetFilePath() instead.
603 path_ = download_item->GetTargetFilePath();
604 DCHECK(!path_.empty());
605 return true;
606}
607
608bool DownloadsGetFileIconFunction::RunInternal() {
609 DCHECK(!path_.empty());
610 DCHECK(icon_extractor_.get());
611 EXTENSION_FUNCTION_VALIDATE(icon_extractor_->ExtractIconURLForPath(
612 path_, IconLoaderSizeFromPixelSize(icon_size_),
613 base::Bind(&DownloadsGetFileIconFunction::OnIconURLExtracted, this)));
614 return true;
615}
616
617void DownloadsGetFileIconFunction::OnIconURLExtracted(const std::string& url) {
618 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
619 if (url.empty())
620 error_ = download_extension_errors::kIconNotFoundError;
621 else
622 result_.reset(base::Value::CreateStringValue(url));
623 SendResponse(error_.empty());
624}
625
626namespace {
[email protected]370e9502011-09-14 19:31:44627base::DictionaryValue* DownloadItemToJSON(DownloadItem* item) {
628 base::DictionaryValue* json = new base::DictionaryValue();
[email protected]c09a8fd2011-11-21 19:54:50629 json->SetInteger(kIdKey, item->GetId());
630 json->SetString(kUrlKey, item->GetOriginalUrl().spec());
631 json->SetString(kFilenameKey, item->GetFullPath().LossyDisplayName());
[email protected]cdcf60242011-11-04 15:53:43632 json->SetString(kDangerKey, DangerString(item->GetDangerType()));
633 json->SetBoolean(kDangerAcceptedKey,
[email protected]c09a8fd2011-11-21 19:54:50634 item->GetSafetyState() == DownloadItem::DANGEROUS_BUT_VALIDATED);
635 json->SetString(kStateKey, StateString(item->GetState()));
636 json->SetBoolean(kPausedKey, item->IsPaused());
637 json->SetString(kMimeKey, item->GetMimeType());
[email protected]cdcf60242011-11-04 15:53:43638 json->SetInteger(kStartTimeKey,
[email protected]c09a8fd2011-11-21 19:54:50639 (item->GetStartTime() - base::Time::UnixEpoch()).InMilliseconds());
640 json->SetInteger(kBytesReceivedKey, item->GetReceivedBytes());
641 json->SetInteger(kTotalBytesKey, item->GetTotalBytes());
642 if (item->GetState() == DownloadItem::INTERRUPTED)
643 json->SetInteger(kErrorKey, static_cast<int>(item->GetLastReason()));
[email protected]370e9502011-09-14 19:31:44644 // TODO(benjhayden): Implement endTime and fileSize.
[email protected]cdcf60242011-11-04 15:53:43645 // json->SetInteger(kEndTimeKey, -1);
[email protected]c09a8fd2011-11-21 19:54:50646 json->SetInteger(kFileSizeKey, item->GetTotalBytes());
[email protected]370e9502011-09-14 19:31:44647 return json;
648}
649} // anonymous namespace
650
[email protected]c579aa272011-12-10 00:17:16651ExtensionDownloadsEventRouter::ExtensionDownloadsEventRouter(Profile* profile)
[email protected]370e9502011-09-14 19:31:44652 : profile_(profile),
[email protected]c579aa272011-12-10 00:17:16653 manager_(NULL) {
[email protected]370e9502011-09-14 19:31:44654 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
655 DCHECK(profile_);
[email protected]c579aa272011-12-10 00:17:16656 // Register a callback with the DownloadService for this profile to be called
657 // when it creates the DownloadManager, or now if the manager already exists.
658 DownloadServiceFactory::GetForProfile(profile)->OnManagerCreated(base::Bind(
659 &ExtensionDownloadsEventRouter::Init, base::Unretained(this)));
660}
661
662// The only public methods on this class are ModelChanged() and
663// ManagerGoingDown(), and they are only called by DownloadManager, so
664// there's no way for any methods on this class to be called before
665// DownloadService calls Init() via the OnManagerCreated Callback above.
666void ExtensionDownloadsEventRouter::Init(DownloadManager* manager) {
667 DCHECK(manager_ == NULL);
668 manager_ = manager;
[email protected]370e9502011-09-14 19:31:44669 manager_->AddObserver(this);
670}
671
672ExtensionDownloadsEventRouter::~ExtensionDownloadsEventRouter() {
[email protected]fb4f8d902011-09-16 06:07:08673 if (manager_ != NULL)
674 manager_->RemoveObserver(this);
[email protected]370e9502011-09-14 19:31:44675}
676
677void ExtensionDownloadsEventRouter::ModelChanged() {
678 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]fb4f8d902011-09-16 06:07:08679 if (manager_ == NULL)
680 return;
[email protected]370e9502011-09-14 19:31:44681 DownloadManager::DownloadVector current_vec;
682 manager_->SearchDownloads(string16(), &current_vec);
683 DownloadIdSet current_set;
684 ItemMap current_map;
685 for (DownloadManager::DownloadVector::const_iterator iter =
686 current_vec.begin();
687 iter != current_vec.end(); ++iter) {
688 DownloadItem* item = *iter;
[email protected]c09a8fd2011-11-21 19:54:50689 int item_id = item->GetId();
[email protected]370e9502011-09-14 19:31:44690 // TODO(benjhayden): Remove the following line when every item's id >= 0,
691 // which will allow firing onErased events for items from the history.
692 if (item_id < 0) continue;
693 DCHECK(current_map.find(item_id) == current_map.end());
694 current_set.insert(item_id);
695 current_map[item_id] = item;
696 }
697 DownloadIdSet new_set; // current_set - downloads_;
698 DownloadIdSet erased_set; // downloads_ - current_set;
699 std::insert_iterator<DownloadIdSet> new_insertor(new_set, new_set.begin());
700 std::insert_iterator<DownloadIdSet> erased_insertor(
701 erased_set, erased_set.begin());
702 std::set_difference(current_set.begin(), current_set.end(),
703 downloads_.begin(), downloads_.end(),
704 new_insertor);
705 std::set_difference(downloads_.begin(), downloads_.end(),
706 current_set.begin(), current_set.end(),
707 erased_insertor);
708 for (DownloadIdSet::const_iterator iter = new_set.begin();
709 iter != new_set.end(); ++iter) {
710 DispatchEvent(extension_event_names::kOnDownloadCreated,
711 DownloadItemToJSON(current_map[*iter]));
712 }
713 for (DownloadIdSet::const_iterator iter = erased_set.begin();
714 iter != erased_set.end(); ++iter) {
715 DispatchEvent(extension_event_names::kOnDownloadErased,
716 base::Value::CreateIntegerValue(*iter));
717 }
718 downloads_.swap(current_set);
719}
720
721void ExtensionDownloadsEventRouter::ManagerGoingDown() {
[email protected]fb4f8d902011-09-16 06:07:08722 manager_->RemoveObserver(this);
[email protected]370e9502011-09-14 19:31:44723 manager_ = NULL;
724}
725
726void ExtensionDownloadsEventRouter::DispatchEvent(
727 const char* event_name, base::Value* arg) {
728 ListValue args;
729 args.Append(arg);
730 std::string json_args;
731 base::JSONWriter::Write(&args, false, &json_args);
732 profile_->GetExtensionEventRouter()->DispatchEventToRenderers(
733 event_name,
734 json_args,
735 profile_,
736 GURL());
737}