Home | History | Annotate | Download | only in webui
      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(&params);
    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(&params);
    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