From f6ea63a2729dcda7c51ede473907a1900303e0c8 Mon Sep 17 00:00:00 2001 From: "rdevlin.cronin" Date: Wed, 4 Mar 2015 09:51:04 -0800 Subject: [PATCH] [Extensions] Make chrome://extensions use developerPrivate for unpacked loading Make the chrome://extensions page use chrome.developerPrivate API to select a file path and load an unpacked extension. Also add tests for the api function, and convert it to a UIThreadExtensionFunction. BUG=461039 Review URL: https://codereview.chromium.org/979453002 Cr-Commit-Position: refs/heads/master@{#319081} --- .../api/developer_private/developer_private_api.cc | 34 ++++++- .../api/developer_private/developer_private_api.h | 10 ++ .../developer_private_api_unittest.cc | 108 +++++++++++++++++++++ .../api/developer_private/entry_picker.cc | 20 ++-- chrome/browser/extensions/unpacked_installer.cc | 10 ++ chrome/browser/extensions/unpacked_installer.h | 11 ++- .../resources/extensions/extension_loader.js | 16 ++- .../webui/extensions/extension_loader_handler.cc | 107 +------------------- .../ui/webui/extensions/extension_loader_handler.h | 10 +- chrome/common/extensions/api/developer_private.idl | 12 ++- .../closure_compiler/externs/developer_private.js | 12 ++- 11 files changed, 216 insertions(+), 134 deletions(-) diff --git a/chrome/browser/extensions/api/developer_private/developer_private_api.cc b/chrome/browser/extensions/api/developer_private/developer_private_api.cc index f2e9ac6ffc95..9470e7821be8 100644 --- a/chrome/browser/extensions/api/developer_private/developer_private_api.cc +++ b/chrome/browser/extensions/api/developer_private/developer_private_api.cc @@ -756,7 +756,9 @@ ExtensionFunction::ResponseAction DeveloperPrivateReloadFunction::Run() { if (!extension) return RespondNow(Error(kNoSuchExtensionError)); - bool fail_quietly = params->options && params->options->fail_quietly; + bool fail_quietly = params->options && + params->options->fail_quietly && + *params->options->fail_quietly; ExtensionService* service = GetExtensionService(browser_context()); if (fail_quietly) @@ -871,7 +873,15 @@ bool DeveloperPrivateInspectFunction::RunSync() { DeveloperPrivateInspectFunction::~DeveloperPrivateInspectFunction() {} +DeveloperPrivateLoadUnpackedFunction::DeveloperPrivateLoadUnpackedFunction() + : fail_quietly_(false) { +} + ExtensionFunction::ResponseAction DeveloperPrivateLoadUnpackedFunction::Run() { + scoped_ptr params( + developer_private::LoadUnpacked::Params::Create(*args_)); + EXTENSION_FUNCTION_VALIDATE(params); + if (!ShowPicker( ui::SelectFileDialog::SELECT_FOLDER, l10n_util::GetStringUTF16(IDS_EXTENSION_LOAD_FROM_DIRECTORY), @@ -880,16 +890,25 @@ ExtensionFunction::ResponseAction DeveloperPrivateLoadUnpackedFunction::Run() { return RespondNow(Error(kCouldNotShowSelectFileDialogError)); } + fail_quietly_ = params->options && + params->options->fail_quietly && + *params->options->fail_quietly; + AddRef(); // Balanced in FileSelected / FileSelectionCanceled. return RespondLater(); } void DeveloperPrivateLoadUnpackedFunction::FileSelected( const base::FilePath& path) { - UnpackedInstaller::Create(GetExtensionService(browser_context()))->Load(path); + scoped_refptr installer( + UnpackedInstaller::Create(GetExtensionService(browser_context()))); + installer->set_be_noisy_on_failure(!fail_quietly_); + installer->set_completion_callback( + base::Bind(&DeveloperPrivateLoadUnpackedFunction::OnLoadComplete, this)); + installer->Load(path); + DeveloperPrivateAPI::Get(browser_context())->SetLastUnpackedDirectory(path); - // TODO(devlin): Shouldn't we wait until the extension is loaded? - Respond(NoArguments()); + Release(); // Balanced in Run(). } @@ -900,6 +919,13 @@ void DeveloperPrivateLoadUnpackedFunction::FileSelectionCanceled() { Release(); // Balanced in Run(). } +void DeveloperPrivateLoadUnpackedFunction::OnLoadComplete( + const Extension* extension, + const base::FilePath& file_path, + const std::string& error) { + Respond(extension ? NoArguments() : Error(error)); +} + bool DeveloperPrivateChooseEntryFunction::ShowPicker( ui::SelectFileDialog::Type picker_type, const base::string16& select_title, diff --git a/chrome/browser/extensions/api/developer_private/developer_private_api.h b/chrome/browser/extensions/api/developer_private/developer_private_api.h index be0c25e52730..cf95fc173ef0 100644 --- a/chrome/browser/extensions/api/developer_private/developer_private_api.h +++ b/chrome/browser/extensions/api/developer_private/developer_private_api.h @@ -312,6 +312,7 @@ class DeveloperPrivateLoadUnpackedFunction public: DECLARE_EXTENSION_FUNCTION("developerPrivate.loadUnpacked", DEVELOPERPRIVATE_LOADUNPACKED); + DeveloperPrivateLoadUnpackedFunction(); protected: ~DeveloperPrivateLoadUnpackedFunction() override; @@ -320,6 +321,15 @@ class DeveloperPrivateLoadUnpackedFunction // EntryPickerClient: void FileSelected(const base::FilePath& path) override; void FileSelectionCanceled() override; + + // Callback for the UnpackedLoader. + void OnLoadComplete(const Extension* extension, + const base::FilePath& file_path, + const std::string& error); + + private: + // Whether or not we should fail quietly in the event of a load error. + bool fail_quietly_; }; class DeveloperPrivateChoosePathFunction diff --git a/chrome/browser/extensions/api/developer_private/developer_private_api_unittest.cc b/chrome/browser/extensions/api/developer_private/developer_private_api_unittest.cc index 43b9f205850b..b7d90d2bbe8c 100644 --- a/chrome/browser/extensions/api/developer_private/developer_private_api_unittest.cc +++ b/chrome/browser/extensions/api/developer_private/developer_private_api_unittest.cc @@ -9,21 +9,32 @@ #include "chrome/browser/extensions/extension_service_test_base.h" #include "chrome/browser/extensions/extension_util.h" #include "chrome/browser/extensions/test_extension_dir.h" +#include "chrome/browser/extensions/test_extension_system.h" #include "chrome/browser/extensions/unpacked_installer.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/host_desktop.h" #include "chrome/common/extensions/api/developer_private.h" #include "chrome/test/base/test_browser_window.h" +#include "content/public/test/test_web_contents_factory.h" #include "extensions/browser/extension_prefs.h" #include "extensions/browser/extension_registry.h" #include "extensions/browser/extension_system.h" #include "extensions/browser/test_extension_registry_observer.h" #include "extensions/common/extension.h" #include "extensions/common/extension_set.h" +#include "extensions/common/manifest_constants.h" #include "extensions/common/test_util.h" namespace extensions { +namespace { + +KeyedService* BuildAPI(content::BrowserContext* context) { + return new DeveloperPrivateAPI(context); +} + +} // namespace + class DeveloperPrivateApiUnitTest : public ExtensionServiceTestBase { protected: DeveloperPrivateApiUnitTest() {} @@ -180,6 +191,13 @@ void DeveloperPrivateApiUnitTest::SetUp() { params.type = Browser::TYPE_TABBED; params.window = browser_window_.get(); browser_.reset(new Browser(params)); + + // Allow the API to be created. + static_cast(ExtensionSystem::Get(profile()))-> + SetEventRouter(make_scoped_ptr( + new EventRouter(profile(), ExtensionPrefs::Get(profile())))); + DeveloperPrivateAPI::GetFactoryInstance()->SetTestingFactory( + profile(), &BuildAPI); } void DeveloperPrivateApiUnitTest::TearDown() { @@ -264,4 +282,94 @@ TEST_F(DeveloperPrivateApiUnitTest, DeveloperPrivatePackFunction) { base::DeleteFile(pem_path, false); } +// Test developerPrivate.choosePath. +TEST_F(DeveloperPrivateApiUnitTest, DeveloperPrivateChoosePath) { + ResetThreadBundle(content::TestBrowserThreadBundle::DEFAULT); + content::TestWebContentsFactory web_contents_factory; + content::WebContents* web_contents = + web_contents_factory.CreateWebContents(profile()); + + base::FilePath expected_dir_path = data_dir().AppendASCII("good_unpacked"); + api::EntryPicker::SkipPickerAndAlwaysSelectPathForTest(&expected_dir_path); + + // Try selecting a directory. + base::ListValue choose_args; + choose_args.AppendString("FOLDER"); + choose_args.AppendString("LOAD"); + scoped_refptr function( + new api::DeveloperPrivateChoosePathFunction()); + function->SetRenderViewHost(web_contents->GetRenderViewHost()); + EXPECT_TRUE(RunFunction(function, choose_args)) << function->GetError(); + std::string path; + EXPECT_TRUE(function->GetResultList() && + function->GetResultList()->GetString(0, &path)); + EXPECT_EQ(path, expected_dir_path.AsUTF8Unsafe()); + + // Try selecting a pem file. + base::FilePath expected_file_path = + data_dir().AppendASCII("good_unpacked.pem"); + api::EntryPicker::SkipPickerAndAlwaysSelectPathForTest(&expected_file_path); + choose_args.Clear(); + choose_args.AppendString("FILE"); + choose_args.AppendString("PEM"); + function = new api::DeveloperPrivateChoosePathFunction(); + function->SetRenderViewHost(web_contents->GetRenderViewHost()); + EXPECT_TRUE(RunFunction(function, choose_args)) << function->GetError(); + EXPECT_TRUE(function->GetResultList() && + function->GetResultList()->GetString(0, &path)); + EXPECT_EQ(path, expected_file_path.AsUTF8Unsafe()); + + // Try canceling the file dialog. + api::EntryPicker::SkipPickerAndAlwaysCancelForTest(); + function = new api::DeveloperPrivateChoosePathFunction(); + function->SetRenderViewHost(web_contents->GetRenderViewHost()); + EXPECT_FALSE(RunFunction(function, choose_args)); + EXPECT_EQ(std::string("File selection was canceled."), function->GetError()); +} + +// Test developerPrivate.loadUnpacked. +TEST_F(DeveloperPrivateApiUnitTest, DeveloperPrivateLoadUnpacked) { + ResetThreadBundle(content::TestBrowserThreadBundle::DEFAULT); + content::TestWebContentsFactory web_contents_factory; + content::WebContents* web_contents = + web_contents_factory.CreateWebContents(profile()); + + base::FilePath path = data_dir().AppendASCII("good_unpacked"); + api::EntryPicker::SkipPickerAndAlwaysSelectPathForTest(&path); + + // Try loading a good extension (it should succeed, and the extension should + // be added). + scoped_refptr function( + new api::DeveloperPrivateLoadUnpackedFunction()); + function->SetRenderViewHost(web_contents->GetRenderViewHost()); + ExtensionIdSet current_ids = registry()->enabled_extensions().GetIDs(); + EXPECT_TRUE(RunFunction(function, base::ListValue())) << function->GetError(); + // We should have added one new extension. + ExtensionIdSet id_difference = base::STLSetDifference( + registry()->enabled_extensions().GetIDs(), current_ids); + ASSERT_EQ(1u, id_difference.size()); + // The new extension should have the same path. + EXPECT_EQ( + path, + registry()->enabled_extensions().GetByID(*id_difference.begin())->path()); + + path = data_dir().AppendASCII("empty_manifest"); + api::EntryPicker::SkipPickerAndAlwaysSelectPathForTest(&path); + + // Try loading a bad extension (it should fail, and we should get an error). + function = new api::DeveloperPrivateLoadUnpackedFunction(); + function->SetRenderViewHost(web_contents->GetRenderViewHost()); + base::ListValue unpacked_args; + scoped_ptr options(new base::DictionaryValue()); + options->SetBoolean("failQuietly", true); + unpacked_args.Append(options.release()); + current_ids = registry()->enabled_extensions().GetIDs(); + EXPECT_FALSE(RunFunction(function, unpacked_args)); + EXPECT_EQ(manifest_errors::kManifestUnreadable, function->GetError()); + // We should have no new extensions installed. + EXPECT_EQ(0u, base::STLSetDifference( + registry()->enabled_extensions().GetIDs(), + current_ids).size()); +} + } // namespace extensions diff --git a/chrome/browser/extensions/api/developer_private/entry_picker.cc b/chrome/browser/extensions/api/developer_private/entry_picker.cc index 9baaa1ec14f7..65d5638f5418 100644 --- a/chrome/browser/extensions/api/developer_private/entry_picker.cc +++ b/chrome/browser/extensions/api/developer_private/entry_picker.cc @@ -32,29 +32,29 @@ EntryPicker::EntryPicker(EntryPickerClient* client, const ui::SelectFileDialog::FileTypeInfo& info, int file_type_index) : client_(client) { - select_file_dialog_ = ui::SelectFileDialog::Create( - this, new ChromeSelectFilePolicy(web_contents)); - - gfx::NativeWindow owning_window = web_contents ? - platform_util::GetTopLevel(web_contents->GetNativeView()) : - NULL; - if (g_skip_picker_for_test) { if (g_path_to_be_picked_for_test) { content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, base::Bind( &EntryPicker::FileSelected, base::Unretained(this), *g_path_to_be_picked_for_test, 1, - static_cast(NULL))); + static_cast(nullptr))); } else { content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, base::Bind( &EntryPicker::FileSelectionCanceled, - base::Unretained(this), static_cast(NULL))); + base::Unretained(this), static_cast(nullptr))); } return; } + select_file_dialog_ = ui::SelectFileDialog::Create( + this, new ChromeSelectFilePolicy(web_contents)); + + gfx::NativeWindow owning_window = web_contents ? + platform_util::GetTopLevel(web_contents->GetNativeView()) : + nullptr; + select_file_dialog_->SelectFile(picker_type, select_title, last_directory, @@ -62,7 +62,7 @@ EntryPicker::EntryPicker(EntryPickerClient* client, file_type_index, base::FilePath::StringType(), owning_window, - NULL); + nullptr); } EntryPicker::~EntryPicker() {} diff --git a/chrome/browser/extensions/unpacked_installer.cc b/chrome/browser/extensions/unpacked_installer.cc index b4f3976ae77c..36368aca93b1 100644 --- a/chrome/browser/extensions/unpacked_installer.cc +++ b/chrome/browser/extensions/unpacked_installer.cc @@ -350,6 +350,11 @@ void UnpackedInstaller::ReportExtensionLoadError(const std::string &error) { service_weak_->profile(), be_noisy_on_failure_); } + + if (!callback_.is_null()) { + callback_.Run(nullptr, extension_path_, error); + callback_.Reset(); + } } void UnpackedInstaller::InstallExtension() { @@ -361,6 +366,11 @@ void UnpackedInstaller::InstallExtension() { service_weak_->OnExtensionInstalled( extension(), syncer::StringOrdinal(), kInstallFlagInstallImmediately); + + if (!callback_.is_null()) { + callback_.Run(extension(), extension_path_, std::string()); + callback_.Reset(); + } } } // namespace extensions diff --git a/chrome/browser/extensions/unpacked_installer.h b/chrome/browser/extensions/unpacked_installer.h index 853cc3979df9..db95a07d89e8 100644 --- a/chrome/browser/extensions/unpacked_installer.h +++ b/chrome/browser/extensions/unpacked_installer.h @@ -29,8 +29,9 @@ class Extension; class UnpackedInstaller : public base::RefCountedThreadSafe { public: - typedef base::Callback - OnFailureCallback; + using CompletionCallback = base::Callback; static scoped_refptr Create( ExtensionService* extension_service); @@ -68,6 +69,10 @@ class UnpackedInstaller be_noisy_on_failure_ = be_noisy_on_failure; } + void set_completion_callback(const CompletionCallback& callback) { + callback_ = callback; + } + private: friend class base::RefCountedThreadSafe; @@ -133,6 +138,8 @@ class UnpackedInstaller // installed. ExtensionInstallChecker install_checker_; + CompletionCallback callback_; + DISALLOW_COPY_AND_ASSIGN(UnpackedInstaller); }; diff --git a/chrome/browser/resources/extensions/extension_loader.js b/chrome/browser/resources/extensions/extension_loader.js index e089c6f29f9c..95def83d8bcc 100644 --- a/chrome/browser/resources/extensions/extension_loader.js +++ b/chrome/browser/resources/extensions/extension_loader.js @@ -186,11 +186,25 @@ cr.define('extensions', function() { ExtensionLoader.prototype = { /** + * Whether or not we are currently loading an unpacked extension. + * @private {boolean} + */ + isLoading_: false, + + /** * Begin the sequence of loading an unpacked extension. If an error is * encountered, this object will get notified via notifyFailed(). */ loadUnpacked: function() { - chrome.send('extensionLoaderLoadUnpacked'); + if (this.isLoading_) // Only one running load at a time. + return; + this.isLoading_ = true; + chrome.developerPrivate.loadUnpacked({failQuietly: true}, function() { + // Check lastError to avoid the log, but don't do anything with it - + // error-handling is done on the C++ side. + var lastError = chrome.runtime.lastError; + this.isLoading_ = false; + }.bind(this)); }, /** diff --git a/chrome/browser/ui/webui/extensions/extension_loader_handler.cc b/chrome/browser/ui/webui/extensions/extension_loader_handler.cc index ccf7045fe4b6..05584e43e30b 100644 --- a/chrome/browser/ui/webui/extensions/extension_loader_handler.cc +++ b/chrome/browser/ui/webui/extensions/extension_loader_handler.cc @@ -14,12 +14,9 @@ #include "base/strings/utf_string_conversions.h" #include "chrome/browser/extensions/path_util.h" #include "chrome/browser/extensions/unpacked_installer.h" -#include "chrome/browser/extensions/zipfile_installer.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/browser/ui/chrome_select_file_policy.h" #include "chrome/grit/generated_resources.h" #include "content/public/browser/browser_thread.h" -#include "content/public/browser/user_metrics.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_ui.h" #include "content/public/browser/web_ui_data_source.h" @@ -30,7 +27,6 @@ #include "extensions/common/manifest_constants.h" #include "third_party/re2/re2/re2.h" #include "ui/base/l10n/l10n_util.h" -#include "ui/shell_dialogs/select_file_dialog.h" namespace extensions { @@ -47,96 +43,8 @@ std::string ReadFileToString(const base::FilePath& path) { } // namespace -class ExtensionLoaderHandler::FileHelper - : public ui::SelectFileDialog::Listener { - public: - explicit FileHelper(ExtensionLoaderHandler* loader_handler); - ~FileHelper() override; - - // Create a FileDialog for the user to select the unpacked extension - // directory. - void ChooseFile(); - - private: - // ui::SelectFileDialog::Listener implementation. - void FileSelected(const base::FilePath& path, - int index, - void* params) override; - void MultiFilesSelected(const std::vector& files, - void* params) override; - - // The associated ExtensionLoaderHandler. Weak, but guaranteed to be alive, - // as it owns this object. - ExtensionLoaderHandler* loader_handler_; - - // The dialog used to pick a directory when loading an unpacked extension. - scoped_refptr load_extension_dialog_; - - // The last selected directory, so we can start in the same spot. - base::FilePath last_unpacked_directory_; - - // The title of the dialog. - base::string16 title_; - - DISALLOW_COPY_AND_ASSIGN(FileHelper); -}; - -ExtensionLoaderHandler::FileHelper::FileHelper( - ExtensionLoaderHandler* loader_handler) - : loader_handler_(loader_handler), - title_(l10n_util::GetStringUTF16(IDS_EXTENSION_LOAD_FROM_DIRECTORY)) { -} - -ExtensionLoaderHandler::FileHelper::~FileHelper() { - // There may be a pending file dialog; inform it the listener is destroyed so - // it doesn't try and call back. - if (load_extension_dialog_.get()) - load_extension_dialog_->ListenerDestroyed(); -} - -void ExtensionLoaderHandler::FileHelper::ChooseFile() { - static const int kFileTypeIndex = 0; // No file type information to index. - static const ui::SelectFileDialog::Type kSelectType = - ui::SelectFileDialog::SELECT_FOLDER; - - gfx::NativeWindow parent_window = - loader_handler_->web_ui()->GetWebContents()->GetTopLevelNativeWindow(); - if (!load_extension_dialog_.get()) { - load_extension_dialog_ = ui::SelectFileDialog::Create( - this, - new ChromeSelectFilePolicy( - loader_handler_->web_ui()->GetWebContents())); - } else if (load_extension_dialog_->IsRunning(parent_window)) { - // File chooser dialog is already running; ignore the click. - return; - } - - load_extension_dialog_->SelectFile( - kSelectType, - title_, - last_unpacked_directory_, - NULL, - kFileTypeIndex, - base::FilePath::StringType(), - parent_window, - NULL); - - content::RecordComputedAction("Options_LoadUnpackedExtension"); -} - -void ExtensionLoaderHandler::FileHelper::FileSelected( - const base::FilePath& path, int index, void* params) { - loader_handler_->LoadUnpackedExtensionImpl(path); -} - -void ExtensionLoaderHandler::FileHelper::MultiFilesSelected( - const std::vector& files, void* params) { - NOTREACHED(); -} - ExtensionLoaderHandler::ExtensionLoaderHandler(Profile* profile) : profile_(profile), - file_helper_(new FileHelper(this)), extension_error_reporter_observer_(this), ui_ready_(false), weak_ptr_factory_(this) { @@ -177,10 +85,6 @@ void ExtensionLoaderHandler::RegisterMessages() { content::WebContentsObserver::Observe(web_ui()->GetWebContents()); web_ui()->RegisterMessageCallback( - "extensionLoaderLoadUnpacked", - base::Bind(&ExtensionLoaderHandler::HandleLoadUnpacked, - weak_ptr_factory_.GetWeakPtr())); - web_ui()->RegisterMessageCallback( "extensionLoaderRetry", base::Bind(&ExtensionLoaderHandler::HandleRetry, weak_ptr_factory_.GetWeakPtr())); @@ -194,16 +98,11 @@ void ExtensionLoaderHandler::RegisterMessages() { weak_ptr_factory_.GetWeakPtr())); } -void ExtensionLoaderHandler::HandleLoadUnpacked(const base::ListValue* args) { - DCHECK(args->empty()); - file_helper_->ChooseFile(); -} - void ExtensionLoaderHandler::HandleRetry(const base::ListValue* args) { DCHECK(args->empty()); const base::FilePath file_path = failed_paths_.back(); failed_paths_.pop_back(); - LoadUnpackedExtensionImpl(file_path); + LoadUnpackedExtension(file_path); } void ExtensionLoaderHandler::HandleIgnoreFailure(const base::ListValue* args) { @@ -222,8 +121,8 @@ void ExtensionLoaderHandler::HandleDisplayFailures( NotifyFrontendOfFailure(); } -void ExtensionLoaderHandler::LoadUnpackedExtensionImpl( - const base::FilePath& file_path) { +void ExtensionLoaderHandler::LoadUnpackedExtension( + const base::FilePath& file_path) { scoped_refptr installer = UnpackedInstaller::Create( ExtensionSystem::Get(profile_)->extension_service()); diff --git a/chrome/browser/ui/webui/extensions/extension_loader_handler.h b/chrome/browser/ui/webui/extensions/extension_loader_handler.h index 15989a60d496..02415d06c7ae 100644 --- a/chrome/browser/ui/webui/extensions/extension_loader_handler.h +++ b/chrome/browser/ui/webui/extensions/extension_loader_handler.h @@ -45,11 +45,6 @@ class ExtensionLoaderHandler : public content::WebUIMessageHandler, void RegisterMessages() override; private: - class FileHelper; - - // Handle the 'extensionLoaderLoadUnpacked' message. - void HandleLoadUnpacked(const base::ListValue* args); - // Handle the 'extensionLoaderRetry' message. void HandleRetry(const base::ListValue* args); @@ -60,7 +55,7 @@ class ExtensionLoaderHandler : public content::WebUIMessageHandler, void HandleDisplayFailures(const base::ListValue* args); // Try to load an unpacked extension from the given |file_path|. - void LoadUnpackedExtensionImpl(const base::FilePath& file_path); + void LoadUnpackedExtension(const base::FilePath& file_path); // ExtensionErrorReporter::Observer: void OnLoadFailure(content::BrowserContext* browser_context, @@ -86,9 +81,6 @@ class ExtensionLoaderHandler : public content::WebUIMessageHandler, // The profile with which this Handler is associated. Profile* profile_; - // A helper to manage file picking. - scoped_ptr file_helper_; - // Holds information about all unpacked extension install failures that // were reported while the extensions page was loading. base::ListValue failures_; diff --git a/chrome/common/extensions/api/developer_private.idl b/chrome/common/extensions/api/developer_private.idl index af5123496ec6..0d61886ebd28 100644 --- a/chrome/common/extensions/api/developer_private.idl +++ b/chrome/common/extensions/api/developer_private.idl @@ -78,7 +78,13 @@ namespace developerPrivate { dictionary ReloadOptions { // If false, an alert dialog will show in the event of a reload error. // Defaults to false. - boolean failQuietly; + boolean? failQuietly; + }; + + dictionary LoadUnpackedOptions { + // If false, an alert dialog will show in the event of a reload error. + // Defaults to false. + boolean? failQuietly; }; enum PackStatus { @@ -247,7 +253,9 @@ namespace developerPrivate { optional VoidCallback callback); // Loads a user-selected unpacked item. - static void loadUnpacked(optional VoidCallback callback); + // |options| : Additional configuration parameters. + static void loadUnpacked(optional LoadUnpackedOptions options, + optional VoidCallback callback); // Loads an extension / app. // |directory| : The directory to load the extension from. diff --git a/third_party/closure_compiler/externs/developer_private.js b/third_party/closure_compiler/externs/developer_private.js index 1c1a51bc8c19..a94b19988679 100644 --- a/third_party/closure_compiler/externs/developer_private.js +++ b/third_party/closure_compiler/externs/developer_private.js @@ -76,12 +76,19 @@ var InspectOptions; /** * @typedef {{ - * failQuietly: boolean + * failQuietly: (boolean|undefined) * }} */ var ReloadOptions; /** + * @typedef {{ + * failQuietly: (boolean|undefined) + * }} + */ +var LoadUnpackedOptions; + +/** * @enum {string} */ chrome.developerPrivate.PackStatus = { @@ -251,9 +258,10 @@ chrome.developerPrivate.allowIncognito = function(extensionId, allow, callback) /** * Loads a user-selected unpacked item. + * @param {LoadUnpackedOptions=} options Additional configuration parameters. * @param {Function=} callback */ -chrome.developerPrivate.loadUnpacked = function(callback) {}; +chrome.developerPrivate.loadUnpacked = function(options, callback) {}; /** * Loads an extension / app. -- 2.11.4.GIT