15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/views/select_file_dialog_extension.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/file_util.h"
82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/files/scoped_temp_dir.h"
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/memory/scoped_ptr.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/path_service.h"
12868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"  // ASCIIToUTF16
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/threading/platform_thread.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "build/build_config.h"
152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/extensions/component_loader.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/extension_browsertest.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/extension_test_message_listener.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/profiles/profile.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/browser.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/browser_navigator.h"
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/browser_window.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/chrome_paths.h"
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/browser_context.h"
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/notification_service.h"
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/notification_types.h"
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/render_view_host.h"
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/storage_partition.h"
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/test/test_utils.h"
292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/shell_dialogs/select_file_dialog.h"
302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/shell_dialogs/selected_file_info.h"
3190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "webkit/browser/fileapi/external_mount_points.h"
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using content::BrowserContext;
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Mock listener used by test below.
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class MockSelectFileDialogListener : public ui::SelectFileDialog::Listener {
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MockSelectFileDialogListener()
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : file_selected_(false),
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      canceled_(false),
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      params_(NULL) {
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool file_selected() const { return file_selected_; }
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool canceled() const { return canceled_; }
462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::FilePath path() const { return path_; }
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void* params() const { return params_; }
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // ui::SelectFileDialog::Listener implementation.
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual void FileSelected(const base::FilePath& path,
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            int index,
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            void* params) OVERRIDE {
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    file_selected_ = true;
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    path_ = path;
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    params_ = params;
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual void FileSelectedWithExtraInfo(
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      const ui::SelectedFileInfo& selected_file_info,
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      int index,
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      void* params) OVERRIDE {
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    FileSelected(selected_file_info.local_path, index, params);
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual void MultiFilesSelected(
642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      const std::vector<base::FilePath>& files, void* params) OVERRIDE {}
652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual void FileSelectionCanceled(void* params) OVERRIDE {
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    canceled_ = true;
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    params_ = params;
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool file_selected_;
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool canceled_;
732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::FilePath path_;
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void* params_;
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(MockSelectFileDialogListener);
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class SelectFileDialogExtensionBrowserTest : public ExtensionBrowserTest {
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  enum DialogButtonType {
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DIALOG_BTN_OK,
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DIALOG_BTN_CANCEL
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual void SetUp() OVERRIDE {
872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    extensions::ComponentLoader::EnableBackgroundExtensionsForTesting();
882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Create the dialog wrapper object, but don't show it yet.
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    listener_.reset(new MockSelectFileDialogListener());
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    dialog_ = new SelectFileDialogExtension(listener_.get(), NULL);
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We have to provide at least one mount point.
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // File manager looks for "Downloads" mount point, so use this name.
952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    base::FilePath tmp_path;
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    PathService::Get(base::DIR_TEMP, &tmp_path);
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ASSERT_TRUE(tmp_dir_.CreateUniqueTempDirUnderPath(tmp_path));
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    downloads_dir_ = tmp_dir_.path().Append("Downloads");
99a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    base::CreateDirectory(downloads_dir_);
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Must run after our setup because it actually runs the test.
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ExtensionBrowserTest::SetUp();
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual void TearDown() OVERRIDE {
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ExtensionBrowserTest::TearDown();
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Delete the dialog first, as it holds a pointer to the listener.
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    dialog_ = NULL;
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    listener_.reset();
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    second_dialog_ = NULL;
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    second_listener_.reset();
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Creates a file system mount point for a directory.
1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  void AddMountPoint(const base::FilePath& path) {
1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    std::string mount_point_name = path.BaseName().AsUTF8Unsafe();
1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    fileapi::ExternalMountPoints* mount_points =
1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        BrowserContext::GetMountPoints(browser()->profile());
1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // The Downloads mount point already exists so it must be removed before
1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // adding the test mount point (which will also be mapped as Downloads).
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    mount_points->RevokeFileSystem(mount_point_name);
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    EXPECT_TRUE(mount_points->RegisterFileSystem(
125a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        mount_point_name, fileapi::kFileSystemTypeNativeLocal,
126a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        fileapi::FileSystemMountOption(), path));
1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  void CheckJavascriptErrors() {
1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    content::RenderViewHost* host = dialog_->GetRenderViewHost();
1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    scoped_ptr<base::Value> value =
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        content::ExecuteScriptAndGetValue(host, "window.JSErrorCount");
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int js_error_count = 0;
1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    ASSERT_TRUE(value->GetAsInteger(&js_error_count));
1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    ASSERT_EQ(0, js_error_count);
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void OpenDialog(ui::SelectFileDialog::Type dialog_type,
1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                  const base::FilePath& file_path,
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  const gfx::NativeWindow& owning_window,
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  const std::string& additional_message) {
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Spawn a dialog to open a file.  The dialog will signal that it is ready
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // via chrome.test.sendMessage() in the extension JavaScript.
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ExtensionTestMessageListener init_listener("worker-initialized",
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                               false /* will_reply */);
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    scoped_ptr<ExtensionTestMessageListener> additional_listener;
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!additional_message.empty()) {
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      additional_listener.reset(
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          new ExtensionTestMessageListener(additional_message, false));
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    dialog_->SelectFile(dialog_type,
154a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                        base::string16() /* title */,
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        file_path,
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        NULL /* file_types */,
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         0 /* file_type_index */,
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        FILE_PATH_LITERAL("") /* default_extension */,
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        owning_window,
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        this /* params */);
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(INFO) << "Waiting for JavaScript ready message.";
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ASSERT_TRUE(init_listener.WaitUntilSatisfied());
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (additional_listener.get()) {
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      LOG(INFO) << "Waiting for JavaScript " << additional_message
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                << " message.";
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ASSERT_TRUE(additional_listener->WaitUntilSatisfied());
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Dialog should be running now.
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ASSERT_TRUE(dialog_->IsRunning(owning_window));
1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    ASSERT_NO_FATAL_FAILURE(CheckJavascriptErrors());
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void TryOpeningSecondDialog(const gfx::NativeWindow& owning_window) {
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    second_listener_.reset(new MockSelectFileDialogListener());
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    second_dialog_ = new SelectFileDialogExtension(second_listener_.get(),
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                   NULL);
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // At the moment we don't really care about dialog type, but we have to put
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // some dialog type.
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    second_dialog_->SelectFile(ui::SelectFileDialog::SELECT_OPEN_FILE,
185a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                               base::string16() /* title */,
1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                               base::FilePath() /* default_path */,
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                               NULL /* file_types */,
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                               0 /* file_type_index */,
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                               FILE_PATH_LITERAL("") /* default_extension */,
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                               owning_window,
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                               this /* params */);
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void CloseDialog(DialogButtonType button_type,
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   const gfx::NativeWindow& owning_window) {
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Inject JavaScript to click the cancel button and wait for notification
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // that the window has closed.
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    content::WindowedNotificationObserver host_destroyed(
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        content::NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED,
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        content::NotificationService::AllSources());
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    content::RenderViewHost* host = dialog_->GetRenderViewHost();
202a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    base::string16 main_frame;
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string button_class =
204eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        (button_type == DIALOG_BTN_OK) ? ".button-panel .ok" :
205eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                         ".button-panel .cancel";
206a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    base::string16 script = ASCIIToUTF16(
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        "console.log(\'Test JavaScript injected.\');"
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        "document.querySelector(\'" + button_class + "\').click();");
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // The file selection handler closes the dialog and does not return control
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // to JavaScript, so do not wait for return values.
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    host->ExecuteJavascriptInWebFrame(main_frame, script);
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(INFO) << "Waiting for window close notification.";
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    host_destroyed.Wait();
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Dialog no longer believes it is running.
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ASSERT_FALSE(dialog_->IsRunning(owning_window));
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<MockSelectFileDialogListener> listener_;
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_refptr<SelectFileDialogExtension> dialog_;
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<MockSelectFileDialogListener> second_listener_;
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_refptr<SelectFileDialogExtension> second_dialog_;
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::ScopedTempDir tmp_dir_;
2262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::FilePath downloads_dir_;
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)IN_PROC_BROWSER_TEST_F(SelectFileDialogExtensionBrowserTest, CreateAndDestroy) {
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Browser window must be up for us to test dialog window parent.
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::NativeWindow native_window = browser()->window()->GetNativeWindow();
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_TRUE(native_window != NULL);
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Before we call SelectFile, dialog is not running/visible.
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_FALSE(dialog_->IsRunning(native_window));
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)IN_PROC_BROWSER_TEST_F(SelectFileDialogExtensionBrowserTest, DestroyListener) {
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Some users of SelectFileDialog destroy their listener before cleaning
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // up the dialog.  Make sure we don't crash.
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  dialog_->ListenerDestroyed();
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  listener_.reset();
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// TODO(jamescook): Add a test for selecting a file for an <input type='file'/>
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// page element, as that uses different memory management pathways.
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// crbug.com/98791
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)IN_PROC_BROWSER_TEST_F(SelectFileDialogExtensionBrowserTest,
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       SelectFileAndCancel) {
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AddMountPoint(downloads_dir_);
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::NativeWindow owning_window = browser()->window()->GetNativeWindow();
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // base::FilePath() for default path.
2562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ASSERT_NO_FATAL_FAILURE(OpenDialog(ui::SelectFileDialog::SELECT_OPEN_FILE,
2572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                     base::FilePath(), owning_window, ""));
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Press cancel button.
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CloseDialog(DIALOG_BTN_CANCEL, owning_window);
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Listener should have been informed of the cancellation.
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_FALSE(listener_->file_selected());
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_TRUE(listener_->canceled());
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_EQ(this, listener_->params());
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)IN_PROC_BROWSER_TEST_F(SelectFileDialogExtensionBrowserTest,
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       SelectFileAndOpen) {
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AddMountPoint(downloads_dir_);
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::FilePath test_file =
2732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      downloads_dir_.AppendASCII("file_manager_test.html");
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Create an empty file to give us something to select.
276a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  FILE* fp = base::OpenFile(test_file, "w");
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_TRUE(fp != NULL);
278a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  ASSERT_TRUE(base::CloseFile(fp));
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::NativeWindow owning_window = browser()->window()->GetNativeWindow();
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Spawn a dialog to open a file.  Provide the path to the file so the dialog
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // will automatically select it.  Ensure that the OK button is enabled by
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // waiting for chrome.test.sendMessage('selection-change-complete').
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The extension starts a Web Worker to read file metadata, so it may send
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // 'selection-change-complete' before 'worker-initialized'.  This is OK.
2872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ASSERT_NO_FATAL_FAILURE(OpenDialog(ui::SelectFileDialog::SELECT_OPEN_FILE,
2882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                     test_file, owning_window,
2892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                     "selection-change-complete"));
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Click open button.
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CloseDialog(DIALOG_BTN_OK, owning_window);
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Listener should have been informed that the file was opened.
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_TRUE(listener_->file_selected());
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_FALSE(listener_->canceled());
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_EQ(test_file, listener_->path());
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_EQ(this, listener_->params());
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)IN_PROC_BROWSER_TEST_F(SelectFileDialogExtensionBrowserTest,
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       SelectFileAndSave) {
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AddMountPoint(downloads_dir_);
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::FilePath test_file =
3062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      downloads_dir_.AppendASCII("file_manager_test.html");
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::NativeWindow owning_window = browser()->window()->GetNativeWindow();
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Spawn a dialog to save a file, providing a suggested path.
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Ensure "Save" button is enabled by waiting for notification from
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // chrome.test.sendMessage().
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The extension starts a Web Worker to read file metadata, so it may send
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // 'directory-change-complete' before 'worker-initialized'.  This is OK.
3152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ASSERT_NO_FATAL_FAILURE(OpenDialog(ui::SelectFileDialog::SELECT_SAVEAS_FILE,
3162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                     test_file, owning_window,
3172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                     "directory-change-complete"));
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Click save button.
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CloseDialog(DIALOG_BTN_OK, owning_window);
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Listener should have been informed that the file was selected.
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_TRUE(listener_->file_selected());
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_FALSE(listener_->canceled());
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_EQ(test_file, listener_->path());
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_EQ(this, listener_->params());
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)IN_PROC_BROWSER_TEST_F(SelectFileDialogExtensionBrowserTest,
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       OpenSingletonTabAndCancel) {
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AddMountPoint(downloads_dir_);
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::NativeWindow owning_window = browser()->window()->GetNativeWindow();
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ASSERT_NO_FATAL_FAILURE(OpenDialog(ui::SelectFileDialog::SELECT_OPEN_FILE,
3362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                     base::FilePath(), owning_window, ""));
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Open a singleton tab in background.
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  chrome::NavigateParams p(browser(), GURL("www.google.com"),
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           content::PAGE_TRANSITION_LINK);
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  p.window_action = chrome::NavigateParams::SHOW_WINDOW;
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  p.disposition = SINGLETON_TAB;
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  chrome::Navigate(&p);
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Press cancel button.
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CloseDialog(DIALOG_BTN_CANCEL, owning_window);
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Listener should have been informed of the cancellation.
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_FALSE(listener_->file_selected());
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_TRUE(listener_->canceled());
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_EQ(this, listener_->params());
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)IN_PROC_BROWSER_TEST_F(SelectFileDialogExtensionBrowserTest,
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       OpenTwoDialogs) {
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AddMountPoint(downloads_dir_);
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::NativeWindow owning_window = browser()->window()->GetNativeWindow();
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ASSERT_NO_FATAL_FAILURE(OpenDialog(ui::SelectFileDialog::SELECT_OPEN_FILE,
3612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                     base::FilePath(), owning_window, ""));
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  TryOpeningSecondDialog(owning_window);
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Second dialog should not be running.
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_FALSE(second_dialog_->IsRunning(owning_window));
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Click cancel button.
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CloseDialog(DIALOG_BTN_CANCEL, owning_window);
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Listener should have been informed of the cancellation.
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_FALSE(listener_->file_selected());
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_TRUE(listener_->canceled());
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_EQ(this, listener_->params());
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
376