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); 74 virtual std::string GetMimeType(const std::string&) const { 75 return "text/html"; 76 } 77 78 private: 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); 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 142 ActiveDownloadsUIHTMLSource::ActiveDownloadsUIHTMLSource() 143 : DataSource(chrome::kChromeUIActiveDownloadsHost, MessageLoop::current()) { 144 } 145 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 196 ActiveDownloadsHandler::ActiveDownloadsHandler() 197 : profile_(NULL), 198 tab_contents_(NULL), 199 download_manager_(NULL) { 200 } 201 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 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 218 void ActiveDownloadsHandler::Init() { 219 download_manager_ = profile_->GetDownloadManager(); 220 download_manager_->AddObserver(this); 221 } 222 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 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 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 258 void ActiveDownloadsHandler::HandlePauseToggleDownload(const ListValue* args) { 259 DownloadItem* item = GetDownloadById(args); 260 if (item && item->IsPartialDownload()) 261 item->TogglePause(); 262 } 263 264 void ActiveDownloadsHandler::HandleAllowDownload(const ListValue* args) { 265 DownloadItem* item = GetDownloadById(args); 266 if (item) 267 item->DangerousDownloadValidated(); 268 } 269 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 279 void ActiveDownloadsHandler::OpenNewFullWindow(const ListValue* args) { 280 OpenNewWindow(args, false); 281 } 282 283 void ActiveDownloadsHandler::OpenNewPopupWindow(const ListValue* args) { 284 OpenNewWindow(args, true); 285 } 286 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 301 void ActiveDownloadsHandler::ModelChanged() { 302 UpdateDownloadList(); 303 } 304 305 void ActiveDownloadsHandler::HandleGetDownloads(const ListValue* args) { 306 UpdateDownloadList(); 307 } 308 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 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 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 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 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 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 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