| // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ui/webui/active_downloads_ui.h" |
| |
| #include <algorithm> |
| #include <string> |
| #include <vector> |
| |
| #include "base/callback.h" |
| #include "base/command_line.h" |
| #include "base/file_util.h" |
| #include "base/logging.h" |
| #include "base/memory/singleton.h" |
| #include "base/message_loop.h" |
| #include "base/path_service.h" |
| #include "base/string_piece.h" |
| #include "base/string_util.h" |
| #include "base/threading/thread.h" |
| #include "base/time.h" |
| #include "base/utf_string_conversions.h" |
| #include "base/values.h" |
| #include "chrome/browser/chromeos/cros/cros_library.h" |
| #include "chrome/browser/chromeos/login/user_manager.h" |
| #include "chrome/browser/download/download_item.h" |
| #include "chrome/browser/download/download_manager.h" |
| #include "chrome/browser/download/download_util.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/tabs/tab_strip_model.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/browser_list.h" |
| #include "chrome/browser/ui/browser_window.h" |
| #include "chrome/browser/ui/webui/favicon_source.h" |
| #include "chrome/browser/ui/webui/mediaplayer_ui.h" |
| #include "chrome/common/chrome_paths.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "chrome/common/jstemplate_builder.h" |
| #include "chrome/common/net/url_fetcher.h" |
| #include "chrome/common/url_constants.h" |
| #include "content/browser/browser_thread.h" |
| #include "content/browser/tab_contents/tab_contents.h" |
| #include "grit/browser_resources.h" |
| #include "grit/chromium_strings.h" |
| #include "grit/generated_resources.h" |
| #include "grit/locale_settings.h" |
| #include "net/base/escape.h" |
| #include "net/url_request/url_request_file_job.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/base/resource/resource_bundle.h" |
| |
| namespace { |
| |
| static const int kPopupLeft = 0; |
| static const int kPopupTop = 0; |
| static const int kPopupWidth = 250; |
| // Minimum height of window must be 100, so kPopupHeight has space for |
| // 2 download rows of 36 px and 'Show All Downloads' which is 29px. |
| static const int kPopupHeight = 36 * 2 + 29; |
| |
| static const char kPropertyPath[] = "path"; |
| static const char kPropertyTitle[] = "title"; |
| static const char kPropertyDirectory[] = "isDirectory"; |
| |
| class ActiveDownloadsUIHTMLSource : public ChromeURLDataManager::DataSource { |
| public: |
| ActiveDownloadsUIHTMLSource(); |
| |
| // Called when the network layer has requested a resource underneath |
| // the path we registered. |
| virtual void StartDataRequest(const std::string& path, |
| bool is_incognito, |
| int request_id); |
| virtual std::string GetMimeType(const std::string&) const { |
| return "text/html"; |
| } |
| |
| private: |
| ~ActiveDownloadsUIHTMLSource() {} |
| |
| DISALLOW_COPY_AND_ASSIGN(ActiveDownloadsUIHTMLSource); |
| }; |
| |
| // The handler for Javascript messages related to the "active_downloads" view. |
| class ActiveDownloadsHandler |
| : public WebUIMessageHandler, |
| public DownloadManager::Observer, |
| public DownloadItem::Observer { |
| public: |
| ActiveDownloadsHandler(); |
| virtual ~ActiveDownloadsHandler(); |
| |
| // Initialization after Attach. |
| void Init(); |
| |
| // WebUIMessageHandler implementation. |
| virtual WebUIMessageHandler* Attach(WebUI* web_ui); |
| virtual void RegisterMessages(); |
| |
| // DownloadItem::Observer interface. |
| virtual void OnDownloadUpdated(DownloadItem* item); |
| virtual void OnDownloadOpened(DownloadItem* item) { } |
| |
| // DownloadManager::Observer interface. |
| virtual void ModelChanged(); |
| |
| // WebUI Callbacks. |
| void HandleGetDownloads(const ListValue* args); |
| void HandlePauseToggleDownload(const ListValue* args); |
| void HandleCancelDownload(const ListValue* args); |
| void HandleAllowDownload(const ListValue* args); |
| void OpenNewPopupWindow(const ListValue* args); |
| void OpenNewFullWindow(const ListValue* args); |
| void PlayMediaFile(const ListValue* args); |
| |
| private: |
| // Downloads helpers. |
| DownloadItem* GetDownloadById(const ListValue* args); |
| void UpdateDownloadList(); |
| void SendDownloads(); |
| void AddDownload(DownloadItem* item); |
| |
| void OpenNewWindow(const ListValue* args, bool popup); |
| |
| Profile* profile_; |
| TabContents* tab_contents_; |
| DownloadManager* download_manager_; |
| |
| typedef std::vector<DownloadItem*> DownloadList; |
| DownloadList active_downloads_; |
| DownloadList downloads_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ActiveDownloadsHandler); |
| }; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // ActiveDownloadsUIHTMLSource |
| // |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| ActiveDownloadsUIHTMLSource::ActiveDownloadsUIHTMLSource() |
| : DataSource(chrome::kChromeUIActiveDownloadsHost, MessageLoop::current()) { |
| } |
| |
| void ActiveDownloadsUIHTMLSource::StartDataRequest(const std::string& path, |
| bool is_incognito, |
| int request_id) { |
| DictionaryValue localized_strings; |
| localized_strings.SetString("allowdownload", |
| l10n_util::GetStringUTF16(IDS_FILEBROWSER_CONFIRM_DOWNLOAD)); |
| localized_strings.SetString("cancel", |
| l10n_util::GetStringUTF16(IDS_DOWNLOAD_LINK_CANCEL)); |
| localized_strings.SetString("open", |
| l10n_util::GetStringUTF16(IDS_FILEBROWSER_OPEN)); |
| localized_strings.SetString("pause", |
| l10n_util::GetStringUTF16(IDS_DOWNLOAD_LINK_PAUSE)); |
| localized_strings.SetString("resume", |
| l10n_util::GetStringUTF16(IDS_DOWNLOAD_LINK_RESUME)); |
| localized_strings.SetString("showalldownloads", |
| l10n_util::GetStringUTF16(IDS_FILEBROWSER_SHOW_ALL_DOWNLOADS)); |
| FilePath default_download_path; |
| if (!PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS, |
| &default_download_path)) { |
| NOTREACHED(); |
| } |
| // TODO(viettrungluu): this is wrong -- FilePath's need not be Unicode. |
| localized_strings.SetString("downloadpath", default_download_path.value()); |
| localized_strings.SetString("error_unknown_file_type", |
| l10n_util::GetStringUTF16(IDS_FILEBROWSER_ERROR_UNKNOWN_FILE_TYPE)); |
| SetFontAndTextDirection(&localized_strings); |
| |
| static const base::StringPiece active_downloads_html( |
| ResourceBundle::GetSharedInstance().GetRawDataResource( |
| IDR_ACTIVE_DOWNLOADS_HTML)); |
| const std::string full_html = jstemplate_builder::GetI18nTemplateHtml( |
| active_downloads_html, &localized_strings); |
| |
| scoped_refptr<RefCountedBytes> html_bytes(new RefCountedBytes); |
| html_bytes->data.resize(full_html.size()); |
| std::copy(full_html.begin(), full_html.end(), html_bytes->data.begin()); |
| |
| SendResponse(request_id, html_bytes); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // ActiveDownloadsHandler |
| // |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| ActiveDownloadsHandler::ActiveDownloadsHandler() |
| : profile_(NULL), |
| tab_contents_(NULL), |
| download_manager_(NULL) { |
| } |
| |
| ActiveDownloadsHandler::~ActiveDownloadsHandler() { |
| for (size_t i = 0; i < downloads_.size(); ++i) { |
| downloads_[i]->RemoveObserver(this); |
| } |
| download_manager_->RemoveObserver(this); |
| } |
| |
| WebUIMessageHandler* ActiveDownloadsHandler::Attach(WebUI* web_ui) { |
| // Create our favicon data source. |
| profile_ = web_ui->GetProfile(); |
| profile_->GetChromeURLDataManager()->AddDataSource( |
| new FaviconSource(profile_, FaviconSource::FAVICON)); |
| tab_contents_ = web_ui->tab_contents(); |
| return WebUIMessageHandler::Attach(web_ui); |
| } |
| |
| void ActiveDownloadsHandler::Init() { |
| download_manager_ = profile_->GetDownloadManager(); |
| download_manager_->AddObserver(this); |
| } |
| |
| void ActiveDownloadsHandler::RegisterMessages() { |
| web_ui_->RegisterMessageCallback("getDownloads", |
| NewCallback(this, &ActiveDownloadsHandler::HandleGetDownloads)); |
| web_ui_->RegisterMessageCallback("pauseToggleDownload", |
| NewCallback(this, &ActiveDownloadsHandler::HandlePauseToggleDownload)); |
| web_ui_->RegisterMessageCallback("cancelDownload", |
| NewCallback(this, &ActiveDownloadsHandler::HandleCancelDownload)); |
| web_ui_->RegisterMessageCallback("allowDownload", |
| NewCallback(this, &ActiveDownloadsHandler::HandleAllowDownload)); |
| web_ui_->RegisterMessageCallback("openNewPopupWindow", |
| NewCallback(this, &ActiveDownloadsHandler::OpenNewPopupWindow)); |
| web_ui_->RegisterMessageCallback("openNewFullWindow", |
| NewCallback(this, &ActiveDownloadsHandler::OpenNewFullWindow)); |
| web_ui_->RegisterMessageCallback("playMediaFile", |
| NewCallback(this, &ActiveDownloadsHandler::PlayMediaFile)); |
| } |
| |
| void ActiveDownloadsHandler::PlayMediaFile(const ListValue* args) { |
| FilePath file_path(UTF16ToUTF8(ExtractStringValue(args))); |
| |
| Browser* browser = Browser::GetBrowserForController( |
| &tab_contents_->controller(), NULL); |
| MediaPlayer* mediaplayer = MediaPlayer::GetInstance(); |
| mediaplayer->ForcePlayMediaFile(profile_, file_path, browser); |
| } |
| |
| DownloadItem* ActiveDownloadsHandler::GetDownloadById( |
| const ListValue* args) { |
| int i; |
| if (!ExtractIntegerValue(args, &i)) |
| return NULL; |
| size_t id(i); |
| return id < downloads_.size() ? downloads_[id] : NULL; |
| } |
| |
| void ActiveDownloadsHandler::HandlePauseToggleDownload(const ListValue* args) { |
| DownloadItem* item = GetDownloadById(args); |
| if (item && item->IsPartialDownload()) |
| item->TogglePause(); |
| } |
| |
| void ActiveDownloadsHandler::HandleAllowDownload(const ListValue* args) { |
| DownloadItem* item = GetDownloadById(args); |
| if (item) |
| download_manager_->DangerousDownloadValidated(item); |
| } |
| |
| void ActiveDownloadsHandler::HandleCancelDownload(const ListValue* args) { |
| DownloadItem* item = GetDownloadById(args); |
| if (item && item->IsPartialDownload()) { |
| item->Cancel(true); |
| item->Delete(DownloadItem::DELETE_DUE_TO_USER_DISCARD); |
| } |
| } |
| |
| void ActiveDownloadsHandler::OpenNewFullWindow(const ListValue* args) { |
| OpenNewWindow(args, false); |
| } |
| |
| void ActiveDownloadsHandler::OpenNewPopupWindow(const ListValue* args) { |
| OpenNewWindow(args, true); |
| } |
| |
| void ActiveDownloadsHandler::OpenNewWindow(const ListValue* args, bool popup) { |
| std::string url = UTF16ToUTF8(ExtractStringValue(args)); |
| Browser* browser = popup ? |
| Browser::CreateForType(Browser::TYPE_APP_PANEL, profile_) : |
| BrowserList::GetLastActive(); |
| browser::NavigateParams params(browser, GURL(url), PageTransition::LINK); |
| params.disposition = NEW_FOREGROUND_TAB; |
| browser::Navigate(¶ms); |
| // TODO(beng): The following two calls should be automatic by Navigate(). |
| if (popup) |
| params.browser->window()->SetBounds(gfx::Rect(0, 0, 400, 300)); |
| params.browser->window()->Show(); |
| } |
| |
| void ActiveDownloadsHandler::ModelChanged() { |
| UpdateDownloadList(); |
| } |
| |
| void ActiveDownloadsHandler::HandleGetDownloads(const ListValue* args) { |
| UpdateDownloadList(); |
| } |
| |
| void ActiveDownloadsHandler::UpdateDownloadList() { |
| DownloadList downloads; |
| download_manager_->GetAllDownloads(FilePath(), &downloads); |
| active_downloads_.clear(); |
| for (size_t i = 0; i < downloads.size(); ++i) { |
| AddDownload(downloads[i]); |
| } |
| SendDownloads(); |
| } |
| |
| void ActiveDownloadsHandler::AddDownload(DownloadItem* item) { |
| // Observe in progress and dangerous downloads. |
| if (item->state() == DownloadItem::IN_PROGRESS || |
| item->safety_state() == DownloadItem::DANGEROUS) { |
| active_downloads_.push_back(item); |
| |
| DownloadList::const_iterator it = |
| std::find(downloads_.begin(), downloads_.end(), item); |
| if (it == downloads_.end()) { |
| downloads_.push_back(item); |
| item->AddObserver(this); |
| } |
| } |
| } |
| |
| void ActiveDownloadsHandler::SendDownloads() { |
| ListValue results; |
| for (size_t i = 0; i < downloads_.size(); ++i) { |
| results.Append(download_util::CreateDownloadItemValue(downloads_[i], i)); |
| } |
| |
| web_ui_->CallJavascriptFunction("downloadsList", results); |
| } |
| |
| void ActiveDownloadsHandler::OnDownloadUpdated(DownloadItem* item) { |
| DownloadList::iterator it = |
| find(downloads_.begin(), downloads_.end(), item); |
| |
| if (it == downloads_.end()) { |
| NOTREACHED() << "Updated item " << item->full_path().value() |
| << " not found"; |
| } |
| |
| if (item->state() == DownloadItem::REMOVING) { |
| item->RemoveObserver(this); |
| downloads_.erase(it); |
| DownloadList::iterator ita = |
| find(active_downloads_.begin(), active_downloads_.end(), item); |
| if (ita != active_downloads_.end()) |
| active_downloads_.erase(ita); |
| SendDownloads(); |
| } else { |
| const size_t id = it - downloads_.begin(); |
| scoped_ptr<DictionaryValue> result( |
| download_util::CreateDownloadItemValue(item, id)); |
| web_ui_->CallJavascriptFunction("downloadUpdated", *result); |
| } |
| } |
| |
| } // namespace |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // ActiveDownloadsUI |
| // |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| |
| ActiveDownloadsUI::ActiveDownloadsUI(TabContents* contents) |
| : HtmlDialogUI(contents) { |
| ActiveDownloadsHandler* handler = new ActiveDownloadsHandler(); |
| AddMessageHandler(handler->Attach(this)); |
| handler->Init(); |
| ActiveDownloadsUIHTMLSource* html_source = new ActiveDownloadsUIHTMLSource(); |
| |
| // Set up the chrome://active-downloads/ source. |
| contents->profile()->GetChromeURLDataManager()->AddDataSource(html_source); |
| } |
| |
| // static |
| Browser* ActiveDownloadsUI::OpenPopup(Profile* profile) { |
| Browser* browser = GetPopup(profile); |
| |
| // Create new browser if no matching pop up is found. |
| if (browser == NULL) { |
| browser = Browser::CreateForType(Browser::TYPE_APP_PANEL, profile); |
| |
| browser::NavigateParams params( |
| browser, |
| GURL(chrome::kChromeUIActiveDownloadsURL), |
| PageTransition::LINK); |
| params.disposition = NEW_FOREGROUND_TAB; |
| browser::Navigate(¶ms); |
| |
| // TODO(beng): The following two calls should be automatic by Navigate(). |
| params.browser->window()->SetBounds(gfx::Rect(kPopupLeft, |
| kPopupTop, |
| kPopupWidth, |
| kPopupHeight)); |
| params.browser->window()->Show(); |
| } else { |
| browser->window()->Show(); |
| } |
| |
| return browser; |
| } |
| |
| Browser* ActiveDownloadsUI::GetPopup(Profile* profile) { |
| for (BrowserList::const_iterator it = BrowserList::begin(); |
| it != BrowserList::end(); |
| ++it) { |
| if (((*it)->type() == Browser::TYPE_APP_PANEL)) { |
| TabContents* tab_contents = (*it)->GetSelectedTabContents(); |
| DCHECK(tab_contents); |
| if (!tab_contents) |
| continue; |
| const GURL& url = tab_contents->GetURL(); |
| |
| if (url.SchemeIs(chrome::kChromeUIScheme) && |
| url.host() == chrome::kChromeUIActiveDownloadsHost && |
| (*it)->profile() == profile) { |
| return (*it); |
| } |
| } |
| } |
| return NULL; |
| } |
| |