1 // 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/ui/webui/active_downloads_ui.h"
6
7 #include <algorithm>
8 #include <string>
9 #include <vector>
10
11 #include "base/callback.h"
12 #include "base/command_line.h"
13 #include "base/file_util.h"
14 #include "base/logging.h"
15 #include "base/memory/singleton.h"
16 #include "base/message_loop.h"
17 #include "base/path_service.h"
18 #include "base/string_piece.h"
19 #include "base/string_util.h"
20 #include "base/threading/thread.h"
21 #include "base/time.h"
22 #include "base/utf_string_conversions.h"
23 #include "base/values.h"
24 #include "chrome/browser/chromeos/cros/cros_library.h"
25 #include "chrome/browser/chromeos/login/user_manager.h"
26 #include "chrome/browser/download/download_item.h"
27 #include "chrome/browser/download/download_manager.h"
28 #include "chrome/browser/download/download_util.h"
29 #include "chrome/browser/profiles/profile.h"
30 #include "chrome/browser/tabs/tab_strip_model.h"
31 #include "chrome/browser/ui/browser.h"
32 #include "chrome/browser/ui/browser_list.h"
33 #include "chrome/browser/ui/browser_window.h"
34 #include "chrome/browser/ui/webui/favicon_source.h"
35 #include "chrome/browser/ui/webui/mediaplayer_ui.h"
36 #include "chrome/common/chrome_paths.h"
37 #include "chrome/common/chrome_switches.h"
38 #include "chrome/common/jstemplate_builder.h"
39 #include "chrome/common/net/url_fetcher.h"
40 #include "chrome/common/url_constants.h"
41 #include "content/browser/browser_thread.h"
42 #include "content/browser/tab_contents/tab_contents.h"
43 #include "grit/browser_resources.h"
44 #include "grit/chromium_strings.h"
45 #include "grit/generated_resources.h"
46 #include "grit/locale_settings.h"
47 #include "net/base/escape.h"
48 #include "net/url_request/url_request_file_job.h"
49 #include "ui/base/l10n/l10n_util.h"
50 #include "ui/base/resource/resource_bundle.h"
51
52 namespace {
53
54 static const int kPopupLeft = 0;
55 static const int kPopupTop = 0;
56 static const int kPopupWidth = 250;
57 // Minimum height of window must be 100, so kPopupHeight has space for
58 // 2 download rows of 36 px and 'Show All Downloads' which is 29px.
59 static const int kPopupHeight = 36 * 2 + 29;
60
61 static const char kPropertyPath[] = "path";
62 static const char kPropertyTitle[] = "title";
63 static const char kPropertyDirectory[] = "isDirectory";
64
65 class ActiveDownloadsUIHTMLSource : public ChromeURLDataManager::DataSource {
66 public:
67 ActiveDownloadsUIHTMLSource();
68
69 // Called when the network layer has requested a resource underneath
70 // the path we registered.
71 virtual void StartDataRequest(const std::string& path,
72 bool is_incognito,
73 int request_id);
GetMimeType(const std::string &) const74 virtual std::string GetMimeType(const std::string&) const {
75 return "text/html";
76 }
77
78 private:
~ActiveDownloadsUIHTMLSource()79 ~ActiveDownloadsUIHTMLSource() {}
80
81 DISALLOW_COPY_AND_ASSIGN(ActiveDownloadsUIHTMLSource);
82 };
83
84 // The handler for Javascript messages related to the "active_downloads" view.
85 class ActiveDownloadsHandler
86 : public WebUIMessageHandler,
87 public DownloadManager::Observer,
88 public DownloadItem::Observer {
89 public:
90 ActiveDownloadsHandler();
91 virtual ~ActiveDownloadsHandler();
92
93 // Initialization after Attach.
94 void Init();
95
96 // WebUIMessageHandler implementation.
97 virtual WebUIMessageHandler* Attach(WebUI* web_ui);
98 virtual void RegisterMessages();
99
100 // DownloadItem::Observer interface.
101 virtual void OnDownloadUpdated(DownloadItem* item);
OnDownloadOpened(DownloadItem * item)102 virtual void OnDownloadOpened(DownloadItem* item) { }
103
104 // DownloadManager::Observer interface.
105 virtual void ModelChanged();
106
107 // WebUI Callbacks.
108 void HandleGetDownloads(const ListValue* args);
109 void HandlePauseToggleDownload(const ListValue* args);
110 void HandleCancelDownload(const ListValue* args);
111 void HandleAllowDownload(const ListValue* args);
112 void OpenNewPopupWindow(const ListValue* args);
113 void OpenNewFullWindow(const ListValue* args);
114 void PlayMediaFile(const ListValue* args);
115
116 private:
117 // Downloads helpers.
118 DownloadItem* GetDownloadById(const ListValue* args);
119 void UpdateDownloadList();
120 void SendDownloads();
121 void AddDownload(DownloadItem* item);
122
123 void OpenNewWindow(const ListValue* args, bool popup);
124
125 Profile* profile_;
126 TabContents* tab_contents_;
127 DownloadManager* download_manager_;
128
129 typedef std::vector<DownloadItem*> DownloadList;
130 DownloadList active_downloads_;
131 DownloadList downloads_;
132
133 DISALLOW_COPY_AND_ASSIGN(ActiveDownloadsHandler);
134 };
135
136 ////////////////////////////////////////////////////////////////////////////////
137 //
138 // ActiveDownloadsUIHTMLSource
139 //
140 ////////////////////////////////////////////////////////////////////////////////
141
ActiveDownloadsUIHTMLSource()142 ActiveDownloadsUIHTMLSource::ActiveDownloadsUIHTMLSource()
143 : DataSource(chrome::kChromeUIActiveDownloadsHost, MessageLoop::current()) {
144 }
145
StartDataRequest(const std::string & path,bool is_incognito,int request_id)146 void ActiveDownloadsUIHTMLSource::StartDataRequest(const std::string& path,
147 bool is_incognito,
148 int request_id) {
149 DictionaryValue localized_strings;
150 localized_strings.SetString("allowdownload",
151 l10n_util::GetStringUTF16(IDS_FILEBROWSER_CONFIRM_DOWNLOAD));
152 localized_strings.SetString("cancel",
153 l10n_util::GetStringUTF16(IDS_DOWNLOAD_LINK_CANCEL));
154 localized_strings.SetString("confirmcancel",
155 l10n_util::GetStringUTF16(IDS_FILEBROWSER_CONFIRM_CANCEL));
156 localized_strings.SetString("confirmyes",
157 l10n_util::GetStringUTF16(IDS_FILEBROWSER_CONFIRM_YES));
158 localized_strings.SetString("open",
159 l10n_util::GetStringUTF16(IDS_FILEBROWSER_OPEN));
160 localized_strings.SetString("pause",
161 l10n_util::GetStringUTF16(IDS_DOWNLOAD_LINK_PAUSE));
162 localized_strings.SetString("resume",
163 l10n_util::GetStringUTF16(IDS_DOWNLOAD_LINK_RESUME));
164 localized_strings.SetString("showalldownloads",
165 l10n_util::GetStringUTF16(IDS_FILEBROWSER_SHOW_ALL_DOWNLOADS));
166 FilePath default_download_path;
167 if (!PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS,
168 &default_download_path)) {
169 NOTREACHED();
170 }
171 // TODO(viettrungluu): this is wrong -- FilePath's need not be Unicode.
172 localized_strings.SetString("downloadpath", default_download_path.value());
173 localized_strings.SetString("error_unknown_file_type",
174 l10n_util::GetStringUTF16(IDS_FILEBROWSER_ERROR_UNKNOWN_FILE_TYPE));
175 SetFontAndTextDirection(&localized_strings);
176
177 static const base::StringPiece active_downloads_html(
178 ResourceBundle::GetSharedInstance().GetRawDataResource(
179 IDR_ACTIVE_DOWNLOADS_HTML));
180 const std::string full_html = jstemplate_builder::GetI18nTemplateHtml(
181 active_downloads_html, &localized_strings);
182
183 scoped_refptr<RefCountedBytes> html_bytes(new RefCountedBytes);
184 html_bytes->data.resize(full_html.size());
185 std::copy(full_html.begin(), full_html.end(), html_bytes->data.begin());
186
187 SendResponse(request_id, html_bytes);
188 }
189
190 ////////////////////////////////////////////////////////////////////////////////
191 //
192 // ActiveDownloadsHandler
193 //
194 ////////////////////////////////////////////////////////////////////////////////
195
ActiveDownloadsHandler()196 ActiveDownloadsHandler::ActiveDownloadsHandler()
197 : profile_(NULL),
198 tab_contents_(NULL),
199 download_manager_(NULL) {
200 }
201
~ActiveDownloadsHandler()202 ActiveDownloadsHandler::~ActiveDownloadsHandler() {
203 for (size_t i = 0; i < downloads_.size(); ++i) {
204 downloads_[i]->RemoveObserver(this);
205 }
206 download_manager_->RemoveObserver(this);
207 }
208
Attach(WebUI * web_ui)209 WebUIMessageHandler* ActiveDownloadsHandler::Attach(WebUI* web_ui) {
210 // Create our favicon data source.
211 profile_ = web_ui->GetProfile();
212 profile_->GetChromeURLDataManager()->AddDataSource(
213 new FaviconSource(profile_));
214 tab_contents_ = web_ui->tab_contents();
215 return WebUIMessageHandler::Attach(web_ui);
216 }
217
Init()218 void ActiveDownloadsHandler::Init() {
219 download_manager_ = profile_->GetDownloadManager();
220 download_manager_->AddObserver(this);
221 }
222
RegisterMessages()223 void ActiveDownloadsHandler::RegisterMessages() {
224 web_ui_->RegisterMessageCallback("getDownloads",
225 NewCallback(this, &ActiveDownloadsHandler::HandleGetDownloads));
226 web_ui_->RegisterMessageCallback("pauseToggleDownload",
227 NewCallback(this, &ActiveDownloadsHandler::HandlePauseToggleDownload));
228 web_ui_->RegisterMessageCallback("cancelDownload",
229 NewCallback(this, &ActiveDownloadsHandler::HandleCancelDownload));
230 web_ui_->RegisterMessageCallback("allowDownload",
231 NewCallback(this, &ActiveDownloadsHandler::HandleAllowDownload));
232 web_ui_->RegisterMessageCallback("openNewPopupWindow",
233 NewCallback(this, &ActiveDownloadsHandler::OpenNewPopupWindow));
234 web_ui_->RegisterMessageCallback("openNewFullWindow",
235 NewCallback(this, &ActiveDownloadsHandler::OpenNewFullWindow));
236 web_ui_->RegisterMessageCallback("playMediaFile",
237 NewCallback(this, &ActiveDownloadsHandler::PlayMediaFile));
238 }
239
PlayMediaFile(const ListValue * args)240 void ActiveDownloadsHandler::PlayMediaFile(const ListValue* args) {
241 FilePath file_path(UTF16ToUTF8(ExtractStringValue(args)));
242
243 Browser* browser = Browser::GetBrowserForController(
244 &tab_contents_->controller(), NULL);
245 MediaPlayer* mediaplayer = MediaPlayer::GetInstance();
246 mediaplayer->ForcePlayMediaFile(profile_, file_path, browser);
247 }
248
GetDownloadById(const ListValue * args)249 DownloadItem* ActiveDownloadsHandler::GetDownloadById(
250 const ListValue* args) {
251 int i;
252 if (!ExtractIntegerValue(args, &i))
253 return NULL;
254 size_t id(i);
255 return id < downloads_.size() ? downloads_[id] : NULL;
256 }
257
HandlePauseToggleDownload(const ListValue * args)258 void ActiveDownloadsHandler::HandlePauseToggleDownload(const ListValue* args) {
259 DownloadItem* item = GetDownloadById(args);
260 if (item && item->IsPartialDownload())
261 item->TogglePause();
262 }
263
HandleAllowDownload(const ListValue * args)264 void ActiveDownloadsHandler::HandleAllowDownload(const ListValue* args) {
265 DownloadItem* item = GetDownloadById(args);
266 if (item)
267 item->DangerousDownloadValidated();
268 }
269
HandleCancelDownload(const ListValue * args)270 void ActiveDownloadsHandler::HandleCancelDownload(const ListValue* args) {
271 DownloadItem* item = GetDownloadById(args);
272 if (item) {
273 if (item->IsPartialDownload())
274 item->Cancel(true);
275 item->Delete(DownloadItem::DELETE_DUE_TO_USER_DISCARD);
276 }
277 }
278
OpenNewFullWindow(const ListValue * args)279 void ActiveDownloadsHandler::OpenNewFullWindow(const ListValue* args) {
280 OpenNewWindow(args, false);
281 }
282
OpenNewPopupWindow(const ListValue * args)283 void ActiveDownloadsHandler::OpenNewPopupWindow(const ListValue* args) {
284 OpenNewWindow(args, true);
285 }
286
OpenNewWindow(const ListValue * args,bool popup)287 void ActiveDownloadsHandler::OpenNewWindow(const ListValue* args, bool popup) {
288 std::string url = UTF16ToUTF8(ExtractStringValue(args));
289 Browser* browser = popup ?
290 Browser::CreateForType(Browser::TYPE_APP_PANEL, profile_) :
291 BrowserList::GetLastActive();
292 browser::NavigateParams params(browser, GURL(url), PageTransition::LINK);
293 params.disposition = NEW_FOREGROUND_TAB;
294 browser::Navigate(¶ms);
295 // TODO(beng): The following two calls should be automatic by Navigate().
296 if (popup)
297 params.browser->window()->SetBounds(gfx::Rect(0, 0, 400, 300));
298 params.browser->window()->Show();
299 }
300
ModelChanged()301 void ActiveDownloadsHandler::ModelChanged() {
302 UpdateDownloadList();
303 }
304
HandleGetDownloads(const ListValue * args)305 void ActiveDownloadsHandler::HandleGetDownloads(const ListValue* args) {
306 UpdateDownloadList();
307 }
308
UpdateDownloadList()309 void ActiveDownloadsHandler::UpdateDownloadList() {
310 DownloadList downloads;
311 download_manager_->GetAllDownloads(FilePath(), &downloads);
312 active_downloads_.clear();
313 for (size_t i = 0; i < downloads.size(); ++i) {
314 AddDownload(downloads[i]);
315 }
316 SendDownloads();
317 }
318
AddDownload(DownloadItem * item)319 void ActiveDownloadsHandler::AddDownload(DownloadItem* item) {
320 // Observe in progress and dangerous downloads.
321 if (item->state() == DownloadItem::IN_PROGRESS ||
322 item->safety_state() == DownloadItem::DANGEROUS) {
323 active_downloads_.push_back(item);
324
325 DownloadList::const_iterator it =
326 std::find(downloads_.begin(), downloads_.end(), item);
327 if (it == downloads_.end()) {
328 downloads_.push_back(item);
329 item->AddObserver(this);
330 }
331 }
332 }
333
SendDownloads()334 void ActiveDownloadsHandler::SendDownloads() {
335 ListValue results;
336 for (size_t i = 0; i < downloads_.size(); ++i) {
337 results.Append(download_util::CreateDownloadItemValue(downloads_[i], i));
338 }
339
340 web_ui_->CallJavascriptFunction("downloadsList", results);
341 }
342
OnDownloadUpdated(DownloadItem * item)343 void ActiveDownloadsHandler::OnDownloadUpdated(DownloadItem* item) {
344 DownloadList::iterator it =
345 find(downloads_.begin(), downloads_.end(), item);
346
347 if (it == downloads_.end()) {
348 NOTREACHED() << "Updated item " << item->full_path().value()
349 << " not found";
350 }
351
352 if (item->state() == DownloadItem::REMOVING) {
353 item->RemoveObserver(this);
354 downloads_.erase(it);
355 DownloadList::iterator ita =
356 find(active_downloads_.begin(), active_downloads_.end(), item);
357 if (ita != active_downloads_.end())
358 active_downloads_.erase(ita);
359 SendDownloads();
360 } else {
361 const size_t id = it - downloads_.begin();
362 scoped_ptr<DictionaryValue> result(
363 download_util::CreateDownloadItemValue(item, id));
364 web_ui_->CallJavascriptFunction("downloadUpdated", *result);
365 }
366 }
367
368 } // namespace
369
370 ////////////////////////////////////////////////////////////////////////////////
371 //
372 // ActiveDownloadsUI
373 //
374 ////////////////////////////////////////////////////////////////////////////////
375
376
ActiveDownloadsUI(TabContents * contents)377 ActiveDownloadsUI::ActiveDownloadsUI(TabContents* contents)
378 : HtmlDialogUI(contents) {
379 ActiveDownloadsHandler* handler = new ActiveDownloadsHandler();
380 AddMessageHandler(handler->Attach(this));
381 handler->Init();
382 ActiveDownloadsUIHTMLSource* html_source = new ActiveDownloadsUIHTMLSource();
383
384 // Set up the chrome://active-downloads/ source.
385 contents->profile()->GetChromeURLDataManager()->AddDataSource(html_source);
386 }
387
388 // static
OpenPopup(Profile * profile)389 Browser* ActiveDownloadsUI::OpenPopup(Profile* profile) {
390 Browser* browser = GetPopup(profile);
391
392 // Create new browser if no matching pop up is found.
393 if (browser == NULL) {
394 browser = Browser::CreateForType(Browser::TYPE_APP_PANEL, profile);
395
396 browser::NavigateParams params(
397 browser,
398 GURL(chrome::kChromeUIActiveDownloadsURL),
399 PageTransition::LINK);
400 params.disposition = NEW_FOREGROUND_TAB;
401 browser::Navigate(¶ms);
402
403 // TODO(beng): The following two calls should be automatic by Navigate().
404 params.browser->window()->SetBounds(gfx::Rect(kPopupLeft,
405 kPopupTop,
406 kPopupWidth,
407 kPopupHeight));
408 params.browser->window()->Show();
409 } else {
410 browser->window()->Show();
411 }
412
413 return browser;
414 }
415
GetPopup(Profile * profile)416 Browser* ActiveDownloadsUI::GetPopup(Profile* profile) {
417 for (BrowserList::const_iterator it = BrowserList::begin();
418 it != BrowserList::end();
419 ++it) {
420 if (((*it)->type() == Browser::TYPE_APP_PANEL)) {
421 TabContents* tab_contents = (*it)->GetSelectedTabContents();
422 DCHECK(tab_contents);
423 if (!tab_contents)
424 continue;
425 const GURL& url = tab_contents->GetURL();
426
427 if (url.SchemeIs(chrome::kChromeUIScheme) &&
428 url.host() == chrome::kChromeUIActiveDownloadsHost &&
429 (*it)->profile() == profile) {
430 return (*it);
431 }
432 }
433 }
434 return NULL;
435 }
436
437