Home | History | Annotate | Download | only in webui
      1 // Copyright (c) 2012 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 "base/auto_reset.h"
      6 #include "base/files/scoped_temp_dir.h"
      7 #include "base/json/json_reader.h"
      8 #include "base/prefs/pref_service.h"
      9 #include "base/values.h"
     10 #include "chrome/browser/history/download_row.h"
     11 #include "chrome/browser/profiles/profile.h"
     12 #include "chrome/browser/ui/browser.h"
     13 #include "chrome/browser/ui/webui/downloads_dom_handler.h"
     14 #include "chrome/common/pref_names.h"
     15 #include "chrome/test/base/in_process_browser_test.h"
     16 #include "chrome/test/base/ui_test_utils.h"
     17 #include "content/public/browser/web_contents.h"
     18 
     19 namespace {
     20 
     21 // Reads |right_json| into a ListValue |left_list|; returns true if all
     22 // key-value pairs in in all dictionaries in |right_list| are also in the
     23 // corresponding dictionary in |left_list|. Ignores keys in dictionaries in
     24 // |left_list| that are not in the corresponding dictionary in |right_list|.
     25 bool ListMatches(base::ListValue* left_list, const std::string& right_json) {
     26   scoped_ptr<base::Value> right_value(base::JSONReader::Read(right_json));
     27   base::ListValue* right_list = NULL;
     28   CHECK(right_value->GetAsList(&right_list));
     29   for (size_t i = 0; i < left_list->GetSize(); ++i) {
     30     base::DictionaryValue* left_dict = NULL;
     31     base::DictionaryValue* right_dict = NULL;
     32     CHECK(left_list->GetDictionary(i, &left_dict));
     33     CHECK(right_list->GetDictionary(i, &right_dict));
     34     for (base::DictionaryValue::Iterator iter(*right_dict);
     35           !iter.IsAtEnd(); iter.Advance()) {
     36       base::Value* left_value = NULL;
     37       if (left_dict->HasKey(iter.key()) &&
     38           left_dict->Get(iter.key(), &left_value) &&
     39           !iter.value().Equals(left_value)) {
     40         LOG(WARNING) << iter.key();
     41         return false;
     42       }
     43     }
     44   }
     45   return true;
     46 }
     47 
     48 // A |DownloadsDOMHandler| that doesn't use a real WebUI object, but is real in
     49 // all other respects.
     50 class MockDownloadsDOMHandler : public DownloadsDOMHandler {
     51  public:
     52   explicit MockDownloadsDOMHandler(content::DownloadManager* dlm)
     53     : DownloadsDOMHandler(dlm),
     54       waiting_list_(false),
     55       waiting_updated_(false) {
     56   }
     57   virtual ~MockDownloadsDOMHandler() {}
     58 
     59   base::ListValue* downloads_list() { return downloads_list_.get(); }
     60   base::ListValue* download_updated() { return download_updated_.get(); }
     61 
     62   void WaitForDownloadsList() {
     63     if (downloads_list_.get())
     64       return;
     65     base::AutoReset<bool> reset_waiting(&waiting_list_, true);
     66     content::RunMessageLoop();
     67   }
     68 
     69   void WaitForDownloadUpdated() {
     70     if (download_updated_.get())
     71       return;
     72     base::AutoReset<bool> reset_waiting(&waiting_updated_, true);
     73     content::RunMessageLoop();
     74   }
     75 
     76   void ForceSendCurrentDownloads() {
     77     ScheduleSendCurrentDownloads();
     78   }
     79 
     80   void reset_downloads_list() { downloads_list_.reset(); }
     81   void reset_download_updated() { download_updated_.reset(); }
     82 
     83  protected:
     84   virtual content::WebContents* GetWebUIWebContents() OVERRIDE {
     85     return NULL;
     86   }
     87 
     88   virtual void CallDownloadsList(const base::ListValue& downloads) OVERRIDE {
     89     downloads_list_.reset(downloads.DeepCopy());
     90     if (waiting_list_) {
     91       content::BrowserThread::PostTask(content::BrowserThread::UI,
     92                                        FROM_HERE,
     93                                        base::MessageLoop::QuitClosure());
     94     }
     95   }
     96 
     97   virtual void CallDownloadUpdated(const base::ListValue& download) OVERRIDE {
     98     download_updated_.reset(download.DeepCopy());
     99     if (waiting_updated_) {
    100       content::BrowserThread::PostTask(content::BrowserThread::UI,
    101                                        FROM_HERE,
    102                                        base::MessageLoop::QuitClosure());
    103     }
    104   }
    105 
    106  private:
    107   scoped_ptr<base::ListValue> downloads_list_;
    108   scoped_ptr<base::ListValue> download_updated_;
    109   bool waiting_list_;
    110   bool waiting_updated_;
    111 
    112   DISALLOW_COPY_AND_ASSIGN(MockDownloadsDOMHandler);
    113 };
    114 
    115 }  // namespace
    116 
    117 class DownloadsDOMHandlerTest : public InProcessBrowserTest {
    118  public:
    119   DownloadsDOMHandlerTest() {}
    120 
    121   virtual ~DownloadsDOMHandlerTest() {}
    122 
    123   virtual void SetUpOnMainThread() OVERRIDE {
    124     mock_handler_.reset(new MockDownloadsDOMHandler(download_manager()));
    125     CHECK(downloads_directory_.CreateUniqueTempDir());
    126     browser()->profile()->GetPrefs()->SetFilePath(
    127         prefs::kDownloadDefaultDirectory,
    128         downloads_directory_.path());
    129     CHECK(test_server()->Start());
    130   }
    131 
    132   content::DownloadManager* download_manager() {
    133     return content::BrowserContext::GetDownloadManager(browser()->profile());
    134   }
    135 
    136   void DownloadAnItem() {
    137     GURL url = test_server()->GetURL("files/downloads/image.jpg");
    138     std::vector<GURL> url_chain;
    139     url_chain.push_back(url);
    140     base::Time current(base::Time::Now());
    141     download_manager()->CreateDownloadItem(
    142         1, // id
    143         base::FilePath(FILE_PATH_LITERAL("/path/to/file")),
    144         base::FilePath(FILE_PATH_LITERAL("/path/to/file")),
    145         url_chain,
    146         GURL(std::string()),
    147         current,
    148         current,
    149         std::string(),
    150         std::string(),
    151         128,
    152         128,
    153         content::DownloadItem::COMPLETE,
    154         content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
    155         content::DOWNLOAD_INTERRUPT_REASON_NONE,
    156         false);
    157 
    158     mock_handler_->WaitForDownloadsList();
    159     ASSERT_EQ(1, static_cast<int>(mock_handler_->downloads_list()->GetSize()));
    160     EXPECT_TRUE(ListMatches(
    161         mock_handler_->downloads_list(),
    162         "[{\"file_externally_removed\": false,"
    163         "  \"file_name\": \"file\","
    164         "  \"id\": 1,"
    165         "  \"otr\": false,"
    166         "  \"since_string\": \"Today\","
    167         "  \"state\": \"COMPLETE\","
    168         "  \"total\": 128}]"));
    169   }
    170 
    171  protected:
    172   scoped_ptr<MockDownloadsDOMHandler> mock_handler_;
    173 
    174  private:
    175   base::ScopedTempDir downloads_directory_;
    176 
    177   DISALLOW_COPY_AND_ASSIGN(DownloadsDOMHandlerTest);
    178 };
    179 
    180 // Tests removing all items, both when prohibited and when allowed.
    181 IN_PROC_BROWSER_TEST_F(DownloadsDOMHandlerTest, RemoveAll) {
    182   DownloadAnItem();
    183 
    184   mock_handler_->reset_downloads_list();
    185   browser()->profile()->GetPrefs()->SetBoolean(
    186       prefs::kAllowDeletingBrowserHistory, false);
    187   mock_handler_->HandleClearAll(NULL);
    188   mock_handler_->WaitForDownloadsList();
    189   ASSERT_EQ(1, static_cast<int>(mock_handler_->downloads_list()->GetSize()));
    190 
    191   mock_handler_->reset_downloads_list();
    192   browser()->profile()->GetPrefs()->SetBoolean(
    193       prefs::kAllowDeletingBrowserHistory, true);
    194   mock_handler_->HandleClearAll(NULL);
    195   mock_handler_->WaitForDownloadsList();
    196   EXPECT_EQ(0, static_cast<int>(mock_handler_->downloads_list()->GetSize()));
    197 }
    198 
    199 // Tests removing one item, both when prohibited and when allowed.
    200 IN_PROC_BROWSER_TEST_F(DownloadsDOMHandlerTest, RemoveOneItem) {
    201   DownloadAnItem();
    202   base::ListValue item;
    203   item.AppendInteger(1);
    204 
    205   mock_handler_->reset_downloads_list();
    206   browser()->profile()->GetPrefs()->SetBoolean(
    207       prefs::kAllowDeletingBrowserHistory, false);
    208   mock_handler_->HandleRemove(&item);
    209   // Removing an item only sends the new download list if anything was actually
    210   // removed, so force it.
    211   mock_handler_->ForceSendCurrentDownloads();
    212   mock_handler_->WaitForDownloadsList();
    213   ASSERT_EQ(1, static_cast<int>(mock_handler_->downloads_list()->GetSize()));
    214 
    215   mock_handler_->reset_downloads_list();
    216   browser()->profile()->GetPrefs()->SetBoolean(
    217       prefs::kAllowDeletingBrowserHistory, true);
    218   mock_handler_->HandleRemove(&item);
    219   mock_handler_->WaitForDownloadsList();
    220   EXPECT_EQ(0, static_cast<int>(mock_handler_->downloads_list()->GetSize()));
    221 }
    222 
    223 // Tests that DownloadsDOMHandler detects new downloads and relays them to the
    224 // renderer.
    225 // crbug.com/159390: This test fails when daylight savings time ends.
    226 IN_PROC_BROWSER_TEST_F(DownloadsDOMHandlerTest, DownloadsRelayed) {
    227   DownloadAnItem();
    228 
    229   mock_handler_->WaitForDownloadUpdated();
    230   ASSERT_EQ(1, static_cast<int>(mock_handler_->download_updated()->GetSize()));
    231   EXPECT_TRUE(ListMatches(
    232       mock_handler_->download_updated(),
    233       "[{\"file_externally_removed\": true,"
    234       "  \"id\": 1}]"));
    235 
    236   mock_handler_->reset_downloads_list();
    237   browser()->profile()->GetPrefs()->SetBoolean(
    238       prefs::kAllowDeletingBrowserHistory, true);
    239   mock_handler_->HandleClearAll(NULL);
    240   mock_handler_->WaitForDownloadsList();
    241   EXPECT_EQ(0, static_cast<int>(mock_handler_->downloads_list()->GetSize()));
    242 }
    243 
    244 
    245 // TODO(benjhayden): Test the extension downloads filter for both
    246 // mock_handler_.downloads_list() and mock_handler_.download_updated().
    247 
    248 // TODO(benjhayden): Test incognito, both downloads_list() and that on-record
    249 // calls can't access off-record items.
    250 
    251 // TODO(benjhayden): Test that bad download ids incoming from the javascript are
    252 // dropped on the floor.
    253 
    254 // TODO(benjhayden): Test that IsTemporary() downloads are not shown.
    255 
    256 // TODO(benjhayden): Test that RemoveObserver is called on all download items,
    257 // including items that crossed IsTemporary() and back.
    258