blob: 11dbec40f9cacca42dae4130672120da14aef460 [file] [log] [blame]
[email protected]21d7a4e302011-08-15 16:17:121// Copyright (c) 2011 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 "chrome/browser/extensions/extension_downloads_api.h"
6
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"
14#include "base/callback.h"
[email protected]370e9502011-09-14 19:31:4415#include "base/json/json_writer.h"
[email protected]21d7a4e302011-08-15 16:17:1216#include "base/logging.h"
[email protected]8a9bc222011-08-24 18:21:0017#include "base/metrics/histogram.h"
[email protected]21d7a4e302011-08-15 16:17:1218#include "base/stl_util.h"
[email protected]8e3ae68c2011-09-16 22:15:4719#include "base/string16.h"
20#include "base/string_util.h"
21#include "base/stringprintf.h"
[email protected]21d7a4e302011-08-15 16:17:1222#include "base/values.h"
23#include "chrome/browser/browser_process.h"
[email protected]21d7a4e302011-08-15 16:17:1224#include "chrome/browser/download/download_util.h"
25#include "chrome/browser/extensions/extension_downloads_api_constants.h"
[email protected]370e9502011-09-14 19:31:4426#include "chrome/browser/extensions/extension_event_names.h"
27#include "chrome/browser/extensions/extension_event_router.h"
[email protected]21d7a4e302011-08-15 16:17:1228#include "chrome/browser/icon_loader.h"
29#include "chrome/browser/icon_manager.h"
30#include "chrome/browser/renderer_host/chrome_render_message_filter.h"
31#include "chrome/browser/ui/browser_list.h"
[email protected]71bf3f5e2011-08-15 21:05:2232#include "content/browser/download/download_file_manager.h"
33#include "content/browser/download/download_item.h"
[email protected]8e3ae68c2011-09-16 22:15:4734#include "content/browser/download/download_types.h"
35#include "content/browser/renderer_host/render_process_host.h"
[email protected]21d7a4e302011-08-15 16:17:1236#include "content/browser/renderer_host/render_view_host.h"
37#include "content/browser/renderer_host/resource_dispatcher_host.h"
[email protected]8e3ae68c2011-09-16 22:15:4738#include "net/http/http_util.h"
39#include "net/url_request/url_request.h"
[email protected]21d7a4e302011-08-15 16:17:1240
41namespace constants = extension_downloads_api_constants;
42
[email protected]8a9bc222011-08-24 18:21:0043bool DownloadsFunctionInterface::RunImplImpl(
44 DownloadsFunctionInterface* pimpl) {
45 CHECK(pimpl);
46 if (!pimpl->ParseArgs()) return false;
47 UMA_HISTOGRAM_ENUMERATION(
48 "Download.ApiFunctions", pimpl->function(), DOWNLOADS_FUNCTION_LAST);
49 pimpl->RunInternal();
50 return true;
51}
52
53SyncDownloadsFunction::SyncDownloadsFunction(
54 DownloadsFunctionInterface::DownloadsFunctionName function)
55 : function_(function) {
56}
57
58SyncDownloadsFunction::~SyncDownloadsFunction() {}
59
60bool SyncDownloadsFunction::RunImpl() {
61 return DownloadsFunctionInterface::RunImplImpl(this);
62}
63
64DownloadsFunctionInterface::DownloadsFunctionName
65SyncDownloadsFunction::function() const {
66 return function_;
67}
68
69AsyncDownloadsFunction::AsyncDownloadsFunction(
70 DownloadsFunctionInterface::DownloadsFunctionName function)
71 : function_(function) {
72}
73
74AsyncDownloadsFunction::~AsyncDownloadsFunction() {}
75
76bool AsyncDownloadsFunction::RunImpl() {
77 return DownloadsFunctionInterface::RunImplImpl(this);
78}
79
80DownloadsFunctionInterface::DownloadsFunctionName
81AsyncDownloadsFunction::function() const {
82 return function_;
83}
84
[email protected]21d7a4e302011-08-15 16:17:1285DownloadsDownloadFunction::DownloadsDownloadFunction()
[email protected]8e3ae68c2011-09-16 22:15:4786 : AsyncDownloadsFunction(DOWNLOADS_FUNCTION_DOWNLOAD) {
[email protected]21d7a4e302011-08-15 16:17:1287}
[email protected]8a9bc222011-08-24 18:21:0088
[email protected]21d7a4e302011-08-15 16:17:1289DownloadsDownloadFunction::~DownloadsDownloadFunction() {}
90
[email protected]8e3ae68c2011-09-16 22:15:4791DownloadsDownloadFunction::IOData::IOData()
92 : save_as(false),
93 extra_headers(NULL),
94 method("GET"),
95 rdh(NULL),
96 resource_context(NULL),
97 render_process_host_id(0),
98 render_view_host_routing_id(0) {
99}
100
101DownloadsDownloadFunction::IOData::~IOData() {}
102
[email protected]8a9bc222011-08-24 18:21:00103bool DownloadsDownloadFunction::ParseArgs() {
[email protected]21d7a4e302011-08-15 16:17:12104 base::DictionaryValue* options = NULL;
[email protected]8e3ae68c2011-09-16 22:15:47105 std::string url;
106 iodata_.reset(new IOData());
[email protected]21d7a4e302011-08-15 16:17:12107 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &options));
[email protected]8e3ae68c2011-09-16 22:15:47108 EXTENSION_FUNCTION_VALIDATE(options->GetString(constants::kUrlKey, &url));
109 iodata_->url = GURL(url);
110 if (!iodata_->url.is_valid()) {
111 error_ = constants::kInvalidURL;
112 return false;
113 }
[email protected]21d7a4e302011-08-15 16:17:12114 if (options->HasKey(constants::kFilenameKey))
115 EXTENSION_FUNCTION_VALIDATE(options->GetString(
[email protected]8e3ae68c2011-09-16 22:15:47116 constants::kFilenameKey, &iodata_->filename));
117 // TODO(benjhayden): More robust validation of filename.
118 if (((iodata_->filename[0] == L'.') && (iodata_->filename[1] == L'.')) ||
119 (iodata_->filename[0] == L'/')) {
120 error_ = constants::kGenericError;
121 return false;
122 }
[email protected]21d7a4e302011-08-15 16:17:12123 if (options->HasKey(constants::kSaveAsKey))
124 EXTENSION_FUNCTION_VALIDATE(options->GetBoolean(
[email protected]8e3ae68c2011-09-16 22:15:47125 constants::kSaveAsKey, &iodata_->save_as));
[email protected]21d7a4e302011-08-15 16:17:12126 if (options->HasKey(constants::kMethodKey))
127 EXTENSION_FUNCTION_VALIDATE(options->GetString(
[email protected]8e3ae68c2011-09-16 22:15:47128 constants::kMethodKey, &iodata_->method));
129 // It's ok to use a pointer to extra_headers without DeepCopy()ing because
130 // |args_| (which owns *extra_headers) is guaranteed to live as long as
131 // |this|.
[email protected]21d7a4e302011-08-15 16:17:12132 if (options->HasKey(constants::kHeadersKey))
[email protected]8e3ae68c2011-09-16 22:15:47133 EXTENSION_FUNCTION_VALIDATE(options->GetList(
134 constants::kHeadersKey, &iodata_->extra_headers));
[email protected]21d7a4e302011-08-15 16:17:12135 if (options->HasKey(constants::kBodyKey))
136 EXTENSION_FUNCTION_VALIDATE(options->GetString(
[email protected]8e3ae68c2011-09-16 22:15:47137 constants::kBodyKey, &iodata_->post_body));
138 if (iodata_->extra_headers != NULL) {
139 for (size_t index = 0; index < iodata_->extra_headers->GetSize(); ++index) {
140 base::DictionaryValue* header = NULL;
141 std::string name, value;
142 EXTENSION_FUNCTION_VALIDATE(iodata_->extra_headers->GetDictionary(
143 index, &header));
144 EXTENSION_FUNCTION_VALIDATE(header->GetString(
145 constants::kHeaderNameKey, &name));
146 EXTENSION_FUNCTION_VALIDATE(header->GetString(
147 constants::kHeaderValueKey, &value));
148 if (!net::HttpUtil::IsSafeHeader(name)) {
149 error_ = constants::kGenericError;
150 return false;
151 }
152 }
153 }
154 iodata_->rdh = g_browser_process->resource_dispatcher_host();
155 iodata_->resource_context = &profile()->GetResourceContext();
156 iodata_->render_process_host_id = render_view_host()->process()->id();
157 iodata_->render_view_host_routing_id = render_view_host()->routing_id();
158 return true;
[email protected]21d7a4e302011-08-15 16:17:12159}
160
[email protected]8a9bc222011-08-24 18:21:00161void DownloadsDownloadFunction::RunInternal() {
[email protected]8e3ae68c2011-09-16 22:15:47162 VLOG(1) << __FUNCTION__ << " " << iodata_->url.spec();
163 if (!BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, NewRunnableMethod(
164 this, &DownloadsDownloadFunction::BeginDownloadOnIOThread))) {
165 error_ = constants::kGenericError;
166 SendResponse(error_.empty());
167 }
168}
169
170void DownloadsDownloadFunction::BeginDownloadOnIOThread() {
171 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
172 DVLOG(1) << __FUNCTION__ << " " << iodata_->url.spec();
173 DownloadSaveInfo save_info;
174 // TODO(benjhayden) Ensure that this filename is interpreted as a path
175 // relative to the default downloads directory without allowing '..'.
176 save_info.suggested_name = iodata_->filename;
177 net::URLRequest* request = new net::URLRequest(iodata_->url, iodata_->rdh);
178 request->set_method(iodata_->method);
179 if (iodata_->extra_headers != NULL) {
180 for (size_t index = 0; index < iodata_->extra_headers->GetSize(); ++index) {
181 base::DictionaryValue* header = NULL;
182 std::string name, value;
183 CHECK(iodata_->extra_headers->GetDictionary(index, &header));
184 CHECK(header->GetString("name", &name));
185 CHECK(header->GetString("value", &value));
186 request->SetExtraRequestHeaderByName(name, value, false/*overwrite*/);
187 }
188 }
189 if (!iodata_->post_body.empty()) {
190 request->AppendBytesToUpload(iodata_->post_body.data(),
191 iodata_->post_body.size());
192 }
193 iodata_->rdh->BeginDownload(
194 request,
195 save_info,
196 iodata_->save_as,
197 base::Bind(&DownloadsDownloadFunction::OnStarted, this),
198 iodata_->render_process_host_id,
199 iodata_->render_view_host_routing_id,
200 *(iodata_->resource_context));
201 iodata_.reset();
202}
203
204void DownloadsDownloadFunction::OnStarted(int dl_id, net::Error error) {
205 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
206 VLOG(1) << __FUNCTION__ << " " << dl_id << " " << error;
207 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, NewRunnableMethod(
208 this, &DownloadsDownloadFunction::RespondOnUIThread, dl_id, error));
209}
210
211void DownloadsDownloadFunction::RespondOnUIThread(int dl_id, net::Error error) {
212 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
213 VLOG(1) << __FUNCTION__;
214 if (dl_id >= 0) {
215 result_.reset(base::Value::CreateIntegerValue(dl_id));
216 } else {
217 error_ = net::ErrorToString(error);
218 }
219 SendResponse(error_.empty());
[email protected]8a9bc222011-08-24 18:21:00220}
221
222DownloadsSearchFunction::DownloadsSearchFunction()
223 : SyncDownloadsFunction(DOWNLOADS_FUNCTION_SEARCH) {
224}
225
[email protected]21d7a4e302011-08-15 16:17:12226DownloadsSearchFunction::~DownloadsSearchFunction() {}
227
[email protected]8a9bc222011-08-24 18:21:00228bool DownloadsSearchFunction::ParseArgs() {
[email protected]8e3ae68c2011-09-16 22:15:47229 base::DictionaryValue* query_json = NULL;
[email protected]21d7a4e302011-08-15 16:17:12230 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &query_json));
231 error_ = constants::kNotImplemented;
232 return false;
233}
234
[email protected]8a9bc222011-08-24 18:21:00235void DownloadsSearchFunction::RunInternal() {
236 NOTIMPLEMENTED();
237}
238
239DownloadsPauseFunction::DownloadsPauseFunction()
240 : SyncDownloadsFunction(DOWNLOADS_FUNCTION_PAUSE) {
241}
242
[email protected]21d7a4e302011-08-15 16:17:12243DownloadsPauseFunction::~DownloadsPauseFunction() {}
244
[email protected]8a9bc222011-08-24 18:21:00245bool DownloadsPauseFunction::ParseArgs() {
[email protected]21d7a4e302011-08-15 16:17:12246 int dl_id = 0;
247 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &dl_id));
248 VLOG(1) << __FUNCTION__ << " " << dl_id;
249 error_ = constants::kNotImplemented;
250 return false;
251}
252
[email protected]8a9bc222011-08-24 18:21:00253void DownloadsPauseFunction::RunInternal() {
254 NOTIMPLEMENTED();
255}
256
257DownloadsResumeFunction::DownloadsResumeFunction()
258 : AsyncDownloadsFunction(DOWNLOADS_FUNCTION_RESUME) {
259}
260
[email protected]21d7a4e302011-08-15 16:17:12261DownloadsResumeFunction::~DownloadsResumeFunction() {}
262
[email protected]8a9bc222011-08-24 18:21:00263bool DownloadsResumeFunction::ParseArgs() {
[email protected]21d7a4e302011-08-15 16:17:12264 int dl_id = 0;
265 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &dl_id));
266 VLOG(1) << __FUNCTION__ << " " << dl_id;
267 error_ = constants::kNotImplemented;
268 return false;
269}
270
[email protected]8a9bc222011-08-24 18:21:00271void DownloadsResumeFunction::RunInternal() {
272 NOTIMPLEMENTED();
273}
274
275DownloadsCancelFunction::DownloadsCancelFunction()
276 : AsyncDownloadsFunction(DOWNLOADS_FUNCTION_CANCEL) {
277}
278
[email protected]21d7a4e302011-08-15 16:17:12279DownloadsCancelFunction::~DownloadsCancelFunction() {}
280
[email protected]8a9bc222011-08-24 18:21:00281bool DownloadsCancelFunction::ParseArgs() {
[email protected]21d7a4e302011-08-15 16:17:12282 int dl_id = 0;
283 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &dl_id));
284 VLOG(1) << __FUNCTION__ << " " << dl_id;
285 error_ = constants::kNotImplemented;
286 return false;
287}
288
[email protected]8a9bc222011-08-24 18:21:00289void DownloadsCancelFunction::RunInternal() {
290 NOTIMPLEMENTED();
291}
292
293DownloadsEraseFunction::DownloadsEraseFunction()
294 : AsyncDownloadsFunction(DOWNLOADS_FUNCTION_ERASE) {
295}
296
[email protected]21d7a4e302011-08-15 16:17:12297DownloadsEraseFunction::~DownloadsEraseFunction() {}
298
[email protected]8a9bc222011-08-24 18:21:00299bool DownloadsEraseFunction::ParseArgs() {
[email protected]8e3ae68c2011-09-16 22:15:47300 base::DictionaryValue* query_json = NULL;
[email protected]21d7a4e302011-08-15 16:17:12301 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &query_json));
302 error_ = constants::kNotImplemented;
303 return false;
304}
305
[email protected]8a9bc222011-08-24 18:21:00306void DownloadsEraseFunction::RunInternal() {
307 NOTIMPLEMENTED();
308}
309
310DownloadsSetDestinationFunction::DownloadsSetDestinationFunction()
311 : AsyncDownloadsFunction(DOWNLOADS_FUNCTION_SET_DESTINATION) {
312}
313
[email protected]21d7a4e302011-08-15 16:17:12314DownloadsSetDestinationFunction::~DownloadsSetDestinationFunction() {}
315
[email protected]8a9bc222011-08-24 18:21:00316bool DownloadsSetDestinationFunction::ParseArgs() {
[email protected]21d7a4e302011-08-15 16:17:12317 int dl_id = 0;
318 std::string path;
319 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &dl_id));
320 EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &path));
321 VLOG(1) << __FUNCTION__ << " " << dl_id << " " << &path;
322 error_ = constants::kNotImplemented;
323 return false;
324}
325
[email protected]8a9bc222011-08-24 18:21:00326void DownloadsSetDestinationFunction::RunInternal() {
327 NOTIMPLEMENTED();
328}
329
330DownloadsAcceptDangerFunction::DownloadsAcceptDangerFunction()
331 : AsyncDownloadsFunction(DOWNLOADS_FUNCTION_ACCEPT_DANGER) {
332}
333
[email protected]21d7a4e302011-08-15 16:17:12334DownloadsAcceptDangerFunction::~DownloadsAcceptDangerFunction() {}
335
[email protected]8a9bc222011-08-24 18:21:00336bool DownloadsAcceptDangerFunction::ParseArgs() {
[email protected]21d7a4e302011-08-15 16:17:12337 int dl_id = 0;
338 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &dl_id));
339 VLOG(1) << __FUNCTION__ << " " << dl_id;
340 error_ = constants::kNotImplemented;
341 return false;
342}
343
[email protected]8a9bc222011-08-24 18:21:00344void DownloadsAcceptDangerFunction::RunInternal() {
345 NOTIMPLEMENTED();
346}
347
348DownloadsShowFunction::DownloadsShowFunction()
349 : AsyncDownloadsFunction(DOWNLOADS_FUNCTION_SHOW) {
350}
351
[email protected]21d7a4e302011-08-15 16:17:12352DownloadsShowFunction::~DownloadsShowFunction() {}
353
[email protected]8a9bc222011-08-24 18:21:00354bool DownloadsShowFunction::ParseArgs() {
[email protected]21d7a4e302011-08-15 16:17:12355 int dl_id = 0;
356 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &dl_id));
357 VLOG(1) << __FUNCTION__ << " " << dl_id;
358 error_ = constants::kNotImplemented;
359 return false;
360}
361
[email protected]8a9bc222011-08-24 18:21:00362void DownloadsShowFunction::RunInternal() {
363 NOTIMPLEMENTED();
364}
365
366DownloadsDragFunction::DownloadsDragFunction()
367 : AsyncDownloadsFunction(DOWNLOADS_FUNCTION_DRAG) {
368}
369
[email protected]21d7a4e302011-08-15 16:17:12370DownloadsDragFunction::~DownloadsDragFunction() {}
371
[email protected]8a9bc222011-08-24 18:21:00372bool DownloadsDragFunction::ParseArgs() {
[email protected]21d7a4e302011-08-15 16:17:12373 int dl_id = 0;
374 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &dl_id));
375 VLOG(1) << __FUNCTION__ << " " << dl_id;
376 error_ = constants::kNotImplemented;
377 return false;
378}
[email protected]8a9bc222011-08-24 18:21:00379
380void DownloadsDragFunction::RunInternal() {
381 NOTIMPLEMENTED();
382}
[email protected]370e9502011-09-14 19:31:44383
384namespace {
385base::DictionaryValue* DownloadItemToJSON(DownloadItem* item) {
386 base::DictionaryValue* json = new base::DictionaryValue();
387 json->SetInteger(constants::kIdKey, item->id());
388 json->SetString(constants::kUrlKey, item->original_url().spec());
389 json->SetString(constants::kFilenameKey,
390 item->full_path().LossyDisplayName());
391 json->SetString(constants::kDangerKey,
392 constants::DangerString(item->GetDangerType()));
393 json->SetBoolean(constants::kDangerAcceptedKey,
394 item->safety_state() == DownloadItem::DANGEROUS_BUT_VALIDATED);
395 json->SetString(constants::kStateKey,
396 constants::StateString(item->state()));
397 json->SetBoolean(constants::kPausedKey, item->is_paused());
398 json->SetString(constants::kMimeKey, item->mime_type());
399 json->SetInteger(constants::kStartTimeKey,
400 (item->start_time() - base::Time::UnixEpoch()).InMilliseconds());
401 json->SetInteger(constants::kBytesReceivedKey, item->received_bytes());
402 json->SetInteger(constants::kTotalBytesKey, item->total_bytes());
403 if (item->state() == DownloadItem::INTERRUPTED)
404 json->SetInteger(constants::kErrorKey,
405 static_cast<int>(item->last_error()));
406 // TODO(benjhayden): Implement endTime and fileSize.
407 // json->SetInteger(constants::kEndTimeKey, -1);
408 json->SetInteger(constants::kFileSizeKey, item->total_bytes());
409 return json;
410}
411} // anonymous namespace
412
413ExtensionDownloadsEventRouter::ExtensionDownloadsEventRouter(
414 Profile* profile)
415 : profile_(profile),
416 manager_(profile ? profile->GetDownloadManager() : NULL) {
417 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
418 DCHECK(profile_);
419 DCHECK(manager_);
420 manager_->AddObserver(this);
421}
422
423ExtensionDownloadsEventRouter::~ExtensionDownloadsEventRouter() {
[email protected]fb4f8d902011-09-16 06:07:08424 if (manager_ != NULL)
425 manager_->RemoveObserver(this);
[email protected]370e9502011-09-14 19:31:44426}
427
428void ExtensionDownloadsEventRouter::ModelChanged() {
429 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]fb4f8d902011-09-16 06:07:08430 if (manager_ == NULL)
431 return;
[email protected]370e9502011-09-14 19:31:44432 DownloadManager::DownloadVector current_vec;
433 manager_->SearchDownloads(string16(), &current_vec);
434 DownloadIdSet current_set;
435 ItemMap current_map;
436 for (DownloadManager::DownloadVector::const_iterator iter =
437 current_vec.begin();
438 iter != current_vec.end(); ++iter) {
439 DownloadItem* item = *iter;
440 int item_id = item->id();
441 // TODO(benjhayden): Remove the following line when every item's id >= 0,
442 // which will allow firing onErased events for items from the history.
443 if (item_id < 0) continue;
444 DCHECK(current_map.find(item_id) == current_map.end());
445 current_set.insert(item_id);
446 current_map[item_id] = item;
447 }
448 DownloadIdSet new_set; // current_set - downloads_;
449 DownloadIdSet erased_set; // downloads_ - current_set;
450 std::insert_iterator<DownloadIdSet> new_insertor(new_set, new_set.begin());
451 std::insert_iterator<DownloadIdSet> erased_insertor(
452 erased_set, erased_set.begin());
453 std::set_difference(current_set.begin(), current_set.end(),
454 downloads_.begin(), downloads_.end(),
455 new_insertor);
456 std::set_difference(downloads_.begin(), downloads_.end(),
457 current_set.begin(), current_set.end(),
458 erased_insertor);
459 for (DownloadIdSet::const_iterator iter = new_set.begin();
460 iter != new_set.end(); ++iter) {
461 DispatchEvent(extension_event_names::kOnDownloadCreated,
462 DownloadItemToJSON(current_map[*iter]));
463 }
464 for (DownloadIdSet::const_iterator iter = erased_set.begin();
465 iter != erased_set.end(); ++iter) {
466 DispatchEvent(extension_event_names::kOnDownloadErased,
467 base::Value::CreateIntegerValue(*iter));
468 }
469 downloads_.swap(current_set);
470}
471
472void ExtensionDownloadsEventRouter::ManagerGoingDown() {
[email protected]fb4f8d902011-09-16 06:07:08473 manager_->RemoveObserver(this);
[email protected]370e9502011-09-14 19:31:44474 manager_ = NULL;
475}
476
477void ExtensionDownloadsEventRouter::DispatchEvent(
478 const char* event_name, base::Value* arg) {
479 ListValue args;
480 args.Append(arg);
481 std::string json_args;
482 base::JSONWriter::Write(&args, false, &json_args);
483 profile_->GetExtensionEventRouter()->DispatchEventToRenderers(
484 event_name,
485 json_args,
486 profile_,
487 GURL());
488}