Commit 83c39e68 authored by rdevlin.cronin's avatar rdevlin.cronin Committed by Commit bot

[Extensions] Make chrome://extensions use developerPrivate for packing crxs

Make the chrome://extensions page use chrome.developerPrivate API to
select a file path for a crx and to perform the pack job itself.
Also add tests for the api function, and convert it to a
UIThreadExtensionFunction.

BUG=461039

Review URL: https://codereview.chromium.org/958803004

Cr-Commit-Position: refs/heads/master@{#318754}
parent 787f3433
...@@ -96,6 +96,10 @@ const char kCannotModifyPolicyExtensionError[] = ...@@ -96,6 +96,10 @@ const char kCannotModifyPolicyExtensionError[] =
"Cannot modify the extension by policy."; "Cannot modify the extension by policy.";
const char kRequiresUserGestureError[] = const char kRequiresUserGestureError[] =
"This action requires a user gesture."; "This action requires a user gesture.";
const char kCouldNotShowSelectFileDialogError[] =
"Could not show a file chooser.";
const char kFileSelectionCanceled[] =
"File selection was canceled.";
const char kUnpackedAppsFolder[] = "apps_target"; const char kUnpackedAppsFolder[] = "apps_target";
...@@ -867,67 +871,59 @@ bool DeveloperPrivateInspectFunction::RunSync() { ...@@ -867,67 +871,59 @@ bool DeveloperPrivateInspectFunction::RunSync() {
DeveloperPrivateInspectFunction::~DeveloperPrivateInspectFunction() {} DeveloperPrivateInspectFunction::~DeveloperPrivateInspectFunction() {}
bool DeveloperPrivateLoadUnpackedFunction::RunAsync() { ExtensionFunction::ResponseAction DeveloperPrivateLoadUnpackedFunction::Run() {
base::string16 select_title = if (!ShowPicker(
l10n_util::GetStringUTF16(IDS_EXTENSION_LOAD_FROM_DIRECTORY); ui::SelectFileDialog::SELECT_FOLDER,
l10n_util::GetStringUTF16(IDS_EXTENSION_LOAD_FROM_DIRECTORY),
ui::SelectFileDialog::FileTypeInfo(),
0 /* file_type_index */)) {
return RespondNow(Error(kCouldNotShowSelectFileDialogError));
}
// Balanced in FileSelected / FileSelectionCanceled. AddRef(); // Balanced in FileSelected / FileSelectionCanceled.
AddRef(); return RespondLater();
bool result = ShowPicker(
ui::SelectFileDialog::SELECT_FOLDER,
DeveloperPrivateAPI::Get(GetProfile())->GetLastUnpackedDirectory(),
select_title,
ui::SelectFileDialog::FileTypeInfo(),
0);
return result;
} }
void DeveloperPrivateLoadUnpackedFunction::FileSelected( void DeveloperPrivateLoadUnpackedFunction::FileSelected(
const base::FilePath& path) { const base::FilePath& path) {
ExtensionService* service = GetExtensionService(GetProfile()); UnpackedInstaller::Create(GetExtensionService(browser_context()))->Load(path);
UnpackedInstaller::Create(service)->Load(path); DeveloperPrivateAPI::Get(browser_context())->SetLastUnpackedDirectory(path);
DeveloperPrivateAPI::Get(GetProfile())->SetLastUnpackedDirectory(path); // TODO(devlin): Shouldn't we wait until the extension is loaded?
SendResponse(true); Respond(NoArguments());
Release(); Release(); // Balanced in Run().
} }
void DeveloperPrivateLoadUnpackedFunction::FileSelectionCanceled() { void DeveloperPrivateLoadUnpackedFunction::FileSelectionCanceled() {
SendResponse(false); // This isn't really an error, but we should keep it like this for
Release(); // backward compatability.
Respond(Error(kFileSelectionCanceled));
Release(); // Balanced in Run().
} }
bool DeveloperPrivateChooseEntryFunction::ShowPicker( bool DeveloperPrivateChooseEntryFunction::ShowPicker(
ui::SelectFileDialog::Type picker_type, ui::SelectFileDialog::Type picker_type,
const base::FilePath& last_directory,
const base::string16& select_title, const base::string16& select_title,
const ui::SelectFileDialog::FileTypeInfo& info, const ui::SelectFileDialog::FileTypeInfo& info,
int file_type_index) { int file_type_index) {
AppWindowRegistry* registry = AppWindowRegistry::Get(GetProfile()); content::WebContents* web_contents = GetSenderWebContents();
DCHECK(registry); if (!web_contents)
AppWindow* app_window =
registry->GetAppWindowForRenderViewHost(render_view_host());
if (!app_window) {
return false; return false;
}
// The entry picker will hold a reference to this function instance, // The entry picker will hold a reference to this function instance,
// and subsequent sending of the function response) until the user has // and subsequent sending of the function response) until the user has
// selected a file or cancelled the picker. At that point, the picker will // selected a file or cancelled the picker. At that point, the picker will
// delete itself. // delete itself.
new EntryPicker(this, new EntryPicker(this,
app_window->web_contents(), web_contents,
picker_type, picker_type,
last_directory, DeveloperPrivateAPI::Get(browser_context())->
GetLastUnpackedDirectory(),
select_title, select_title,
info, info,
file_type_index); file_type_index);
return true; return true;
} }
bool DeveloperPrivateChooseEntryFunction::RunAsync() {
return false;
}
DeveloperPrivateChooseEntryFunction::~DeveloperPrivateChooseEntryFunction() {} DeveloperPrivateChooseEntryFunction::~DeveloperPrivateChooseEntryFunction() {}
void DeveloperPrivatePackDirectoryFunction::OnPackSuccess( void DeveloperPrivatePackDirectoryFunction::OnPackSuccess(
...@@ -937,9 +933,8 @@ void DeveloperPrivatePackDirectoryFunction::OnPackSuccess( ...@@ -937,9 +933,8 @@ void DeveloperPrivatePackDirectoryFunction::OnPackSuccess(
response.message = base::UTF16ToUTF8( response.message = base::UTF16ToUTF8(
PackExtensionJob::StandardSuccessMessage(crx_file, pem_file)); PackExtensionJob::StandardSuccessMessage(crx_file, pem_file));
response.status = developer::PACK_STATUS_SUCCESS; response.status = developer::PACK_STATUS_SUCCESS;
results_ = developer::PackDirectory::Results::Create(response); Respond(OneArgument(response.ToValue().release()));
SendResponse(true); Release(); // Balanced in Run().
Release();
} }
void DeveloperPrivatePackDirectoryFunction::OnPackFailure( void DeveloperPrivatePackDirectoryFunction::OnPackFailure(
...@@ -955,15 +950,14 @@ void DeveloperPrivatePackDirectoryFunction::OnPackFailure( ...@@ -955,15 +950,14 @@ void DeveloperPrivatePackDirectoryFunction::OnPackFailure(
} else { } else {
response.status = developer::PACK_STATUS_ERROR; response.status = developer::PACK_STATUS_ERROR;
} }
results_ = developer::PackDirectory::Results::Create(response); Respond(OneArgument(response.ToValue().release()));
SendResponse(true); Release(); // Balanced in Run().
Release();
} }
bool DeveloperPrivatePackDirectoryFunction::RunAsync() { ExtensionFunction::ResponseAction DeveloperPrivatePackDirectoryFunction::Run() {
scoped_ptr<PackDirectory::Params> params( scoped_ptr<PackDirectory::Params> params(
PackDirectory::Params::Create(*args_)); PackDirectory::Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params.get()); EXTENSION_FUNCTION_VALIDATE(params);
int flags = params->flags ? *params->flags : 0; int flags = params->flags ? *params->flags : 0;
item_path_str_ = params->path; item_path_str_ = params->path;
...@@ -972,7 +966,6 @@ bool DeveloperPrivatePackDirectoryFunction::RunAsync() { ...@@ -972,7 +966,6 @@ bool DeveloperPrivatePackDirectoryFunction::RunAsync() {
base::FilePath root_directory = base::FilePath root_directory =
base::FilePath::FromUTF8Unsafe(item_path_str_); base::FilePath::FromUTF8Unsafe(item_path_str_);
base::FilePath key_file = base::FilePath::FromUTF8Unsafe(key_path_str_); base::FilePath key_file = base::FilePath::FromUTF8Unsafe(key_path_str_);
developer::PackDirectoryResponse response; developer::PackDirectoryResponse response;
...@@ -985,33 +978,29 @@ bool DeveloperPrivatePackDirectoryFunction::RunAsync() { ...@@ -985,33 +978,29 @@ bool DeveloperPrivatePackDirectoryFunction::RunAsync() {
IDS_EXTENSION_PACK_DIALOG_ERROR_ROOT_INVALID); IDS_EXTENSION_PACK_DIALOG_ERROR_ROOT_INVALID);
response.status = developer::PACK_STATUS_ERROR; response.status = developer::PACK_STATUS_ERROR;
results_ = developer::PackDirectory::Results::Create(response); return RespondNow(OneArgument(response.ToValue().release()));
SendResponse(true);
return true;
} }
if (!key_path_str_.empty() && key_file.empty()) { if (!key_path_str_.empty() && key_file.empty()) {
response.message = l10n_util::GetStringUTF8( response.message = l10n_util::GetStringUTF8(
IDS_EXTENSION_PACK_DIALOG_ERROR_KEY_INVALID); IDS_EXTENSION_PACK_DIALOG_ERROR_KEY_INVALID);
response.status = developer::PACK_STATUS_ERROR; response.status = developer::PACK_STATUS_ERROR;
results_ = developer::PackDirectory::Results::Create(response); return RespondNow(OneArgument(response.ToValue().release()));
SendResponse(true);
return true;
} }
// Balanced in OnPackSuccess / OnPackFailure. AddRef(); // Balanced in OnPackSuccess / OnPackFailure.
AddRef();
// TODO(devlin): Why is PackExtensionJob ref-counted?
pack_job_ = new PackExtensionJob(this, root_directory, key_file, flags); pack_job_ = new PackExtensionJob(this, root_directory, key_file, flags);
pack_job_->Start(); pack_job_->Start();
return true; return RespondLater();
} }
DeveloperPrivatePackDirectoryFunction::DeveloperPrivatePackDirectoryFunction() DeveloperPrivatePackDirectoryFunction::DeveloperPrivatePackDirectoryFunction() {
{} }
DeveloperPrivatePackDirectoryFunction::~DeveloperPrivatePackDirectoryFunction() DeveloperPrivatePackDirectoryFunction::
{} ~DeveloperPrivatePackDirectoryFunction() {}
DeveloperPrivateLoadUnpackedFunction::~DeveloperPrivateLoadUnpackedFunction() {} DeveloperPrivateLoadUnpackedFunction::~DeveloperPrivateLoadUnpackedFunction() {}
...@@ -1246,16 +1235,16 @@ DeveloperPrivateLoadDirectoryFunction::DeveloperPrivateLoadDirectoryFunction() ...@@ -1246,16 +1235,16 @@ DeveloperPrivateLoadDirectoryFunction::DeveloperPrivateLoadDirectoryFunction()
DeveloperPrivateLoadDirectoryFunction::~DeveloperPrivateLoadDirectoryFunction() DeveloperPrivateLoadDirectoryFunction::~DeveloperPrivateLoadDirectoryFunction()
{} {}
bool DeveloperPrivateChoosePathFunction::RunAsync() { ExtensionFunction::ResponseAction DeveloperPrivateChoosePathFunction::Run() {
scoped_ptr<developer::ChoosePath::Params> params( scoped_ptr<developer::ChoosePath::Params> params(
developer::ChoosePath::Params::Create(*args_)); developer::ChoosePath::Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params.get() != NULL); EXTENSION_FUNCTION_VALIDATE(params);
ui::SelectFileDialog::Type type = ui::SelectFileDialog::SELECT_FOLDER; ui::SelectFileDialog::Type type = ui::SelectFileDialog::SELECT_FOLDER;
ui::SelectFileDialog::FileTypeInfo info; ui::SelectFileDialog::FileTypeInfo info;
if (params->select_type == developer::SELECT_TYPE_FILE) {
if (params->select_type == developer::SELECT_TYPE_FILE)
type = ui::SelectFileDialog::SELECT_OPEN_FILE; type = ui::SelectFileDialog::SELECT_OPEN_FILE;
}
base::string16 select_title; base::string16 select_title;
int file_type_index = 0; int file_type_index = 0;
...@@ -1264,8 +1253,8 @@ bool DeveloperPrivateChoosePathFunction::RunAsync() { ...@@ -1264,8 +1253,8 @@ bool DeveloperPrivateChoosePathFunction::RunAsync() {
} else if (params->file_type == developer::FILE_TYPE_PEM) { } else if (params->file_type == developer::FILE_TYPE_PEM) {
select_title = l10n_util::GetStringUTF16( select_title = l10n_util::GetStringUTF16(
IDS_EXTENSION_PACK_DIALOG_SELECT_KEY); IDS_EXTENSION_PACK_DIALOG_SELECT_KEY);
info.extensions.push_back(std::vector<base::FilePath::StringType>()); info.extensions.push_back(std::vector<base::FilePath::StringType>(
info.extensions.front().push_back(FILE_PATH_LITERAL("pem")); 1, FILE_PATH_LITERAL("pem")));
info.extension_description_overrides.push_back( info.extension_description_overrides.push_back(
l10n_util::GetStringUTF16( l10n_util::GetStringUTF16(
IDS_EXTENSION_PACK_DIALOG_KEY_FILE_TYPE_DESCRIPTION)); IDS_EXTENSION_PACK_DIALOG_KEY_FILE_TYPE_DESCRIPTION));
...@@ -1275,26 +1264,28 @@ bool DeveloperPrivateChoosePathFunction::RunAsync() { ...@@ -1275,26 +1264,28 @@ bool DeveloperPrivateChoosePathFunction::RunAsync() {
NOTREACHED(); NOTREACHED();
} }
// Balanced by FileSelected / FileSelectionCanceled. if (!ShowPicker(
AddRef(); type,
bool result = ShowPicker( select_title,
type, info,
DeveloperPrivateAPI::Get(GetProfile())->GetLastUnpackedDirectory(), file_type_index)) {
select_title, return RespondNow(Error(kCouldNotShowSelectFileDialogError));
info, }
file_type_index);
return result; AddRef(); // Balanced by FileSelected / FileSelectionCanceled.
return RespondLater();
} }
void DeveloperPrivateChoosePathFunction::FileSelected( void DeveloperPrivateChoosePathFunction::FileSelected(
const base::FilePath& path) { const base::FilePath& path) {
SetResult(new base::StringValue(base::UTF16ToUTF8(path.LossyDisplayName()))); Respond(OneArgument(new base::StringValue(path.LossyDisplayName())));
SendResponse(true);
Release(); Release();
} }
void DeveloperPrivateChoosePathFunction::FileSelectionCanceled() { void DeveloperPrivateChoosePathFunction::FileSelectionCanceled() {
SendResponse(false); // This isn't really an error, but we should keep it like this for
// backward compatability.
Respond(Error(kFileSelectionCanceled));
Release(); Release();
} }
......
...@@ -296,20 +296,14 @@ class DeveloperPrivateShowPermissionsDialogFunction ...@@ -296,20 +296,14 @@ class DeveloperPrivateShowPermissionsDialogFunction
std::string extension_id_; std::string extension_id_;
}; };
class DeveloperPrivateChooseEntryFunction : public ChromeAsyncExtensionFunction, class DeveloperPrivateChooseEntryFunction : public UIThreadExtensionFunction,
public EntryPickerClient { public EntryPickerClient {
protected: protected:
~DeveloperPrivateChooseEntryFunction() override; ~DeveloperPrivateChooseEntryFunction() override;
bool RunAsync() override;
bool ShowPicker(ui::SelectFileDialog::Type picker_type, bool ShowPicker(ui::SelectFileDialog::Type picker_type,
const base::FilePath& last_directory,
const base::string16& select_title, const base::string16& select_title,
const ui::SelectFileDialog::FileTypeInfo& info, const ui::SelectFileDialog::FileTypeInfo& info,
int file_type_index); int file_type_index);
// EntryPickerClient functions.
void FileSelected(const base::FilePath& path) override = 0;
void FileSelectionCanceled() override = 0;
}; };
...@@ -321,9 +315,9 @@ class DeveloperPrivateLoadUnpackedFunction ...@@ -321,9 +315,9 @@ class DeveloperPrivateLoadUnpackedFunction
protected: protected:
~DeveloperPrivateLoadUnpackedFunction() override; ~DeveloperPrivateLoadUnpackedFunction() override;
bool RunAsync() override; ResponseAction Run() override;
// EntryPickerCLient implementation. // EntryPickerClient:
void FileSelected(const base::FilePath& path) override; void FileSelected(const base::FilePath& path) override;
void FileSelectionCanceled() override; void FileSelectionCanceled() override;
}; };
...@@ -336,15 +330,15 @@ class DeveloperPrivateChoosePathFunction ...@@ -336,15 +330,15 @@ class DeveloperPrivateChoosePathFunction
protected: protected:
~DeveloperPrivateChoosePathFunction() override; ~DeveloperPrivateChoosePathFunction() override;
bool RunAsync() override; ResponseAction Run() override;
// EntryPickerClient functions. // EntryPickerClient:
void FileSelected(const base::FilePath& path) override; void FileSelected(const base::FilePath& path) override;
void FileSelectionCanceled() override; void FileSelectionCanceled() override;
}; };
class DeveloperPrivatePackDirectoryFunction class DeveloperPrivatePackDirectoryFunction
: public ChromeAsyncExtensionFunction, : public DeveloperPrivateAPIFunction,
public PackExtensionJob::Client { public PackExtensionJob::Client {
public: public:
...@@ -361,7 +355,7 @@ class DeveloperPrivatePackDirectoryFunction ...@@ -361,7 +355,7 @@ class DeveloperPrivatePackDirectoryFunction
protected: protected:
~DeveloperPrivatePackDirectoryFunction() override; ~DeveloperPrivatePackDirectoryFunction() override;
bool RunAsync() override; ResponseAction Run() override;
private: private:
scoped_refptr<PackExtensionJob> pack_job_; scoped_refptr<PackExtensionJob> pack_job_;
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "chrome/browser/extensions/unpacked_installer.h" #include "chrome/browser/extensions/unpacked_installer.h"
#include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/host_desktop.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 "chrome/test/base/test_browser_window.h"
#include "extensions/browser/extension_prefs.h" #include "extensions/browser/extension_prefs.h"
#include "extensions/browser/extension_registry.h" #include "extensions/browser/extension_registry.h"
...@@ -43,6 +44,11 @@ class DeveloperPrivateApiUnitTest : public ExtensionServiceTestBase { ...@@ -43,6 +44,11 @@ class DeveloperPrivateApiUnitTest : public ExtensionServiceTestBase {
void TestExtensionPrefSetting( void TestExtensionPrefSetting(
bool (*has_pref)(const std::string&, content::BrowserContext*)); bool (*has_pref)(const std::string&, content::BrowserContext*));
testing::AssertionResult TestPackExtensionFunction(
const base::ListValue& args,
api::developer_private::PackStatus expected_status,
int expected_flags);
Browser* browser() { return browser_.get(); } Browser* browser() { return browser_.get(); }
private: private:
...@@ -134,6 +140,37 @@ void DeveloperPrivateApiUnitTest::TestExtensionPrefSetting( ...@@ -134,6 +140,37 @@ void DeveloperPrivateApiUnitTest::TestExtensionPrefSetting(
EXPECT_FALSE(has_pref(extension_id, profile())); EXPECT_FALSE(has_pref(extension_id, profile()));
} }
testing::AssertionResult DeveloperPrivateApiUnitTest::TestPackExtensionFunction(
const base::ListValue& args,
api::developer_private::PackStatus expected_status,
int expected_flags) {
scoped_refptr<UIThreadExtensionFunction> function(
new api::DeveloperPrivatePackDirectoryFunction());
if (!RunFunction(function, args))
return testing::AssertionFailure() << "Could not run function.";
// Extract the result. We don't have to test this here, since it's verified as
// part of the general extension api system.
const base::Value* response_value = nullptr;
CHECK(function->GetResultList()->Get(0u, &response_value));
scoped_ptr<api::developer_private::PackDirectoryResponse> response =
api::developer_private::PackDirectoryResponse::FromValue(*response_value);
CHECK(response);
if (response->status != expected_status) {
return testing::AssertionFailure() << "Expected status: " <<
expected_status << ", found status: " << response->status <<
", message: " << response->message;
}
if (response->override_flags != expected_flags) {
return testing::AssertionFailure() << "Expected flags: " <<
expected_flags << ", found flags: " << response->override_flags;
}
return testing::AssertionSuccess();
}
void DeveloperPrivateApiUnitTest::SetUp() { void DeveloperPrivateApiUnitTest::SetUp() {
ExtensionServiceTestBase::SetUp(); ExtensionServiceTestBase::SetUp();
InitializeEmptyExtensionService(); InitializeEmptyExtensionService();
...@@ -183,4 +220,48 @@ TEST_F(DeveloperPrivateApiUnitTest, DeveloperPrivateAllowFileAccess) { ...@@ -183,4 +220,48 @@ TEST_F(DeveloperPrivateApiUnitTest, DeveloperPrivateAllowFileAccess) {
&util::AllowFileAccess); &util::AllowFileAccess);
} }
// Test developerPrivate.packDirectory.
TEST_F(DeveloperPrivateApiUnitTest, DeveloperPrivatePackFunction) {
ResetThreadBundle(content::TestBrowserThreadBundle::DEFAULT);
base::FilePath root_path = data_dir().AppendASCII("good_unpacked");
base::FilePath crx_path = data_dir().AppendASCII("good_unpacked.crx");
base::FilePath pem_path = data_dir().AppendASCII("good_unpacked.pem");
// First, test a directory that should pack properly.
base::ListValue pack_args;
pack_args.AppendString(root_path.AsUTF8Unsafe());
EXPECT_TRUE(TestPackExtensionFunction(
pack_args, api::developer_private::PACK_STATUS_SUCCESS, 0));
// Should have created crx file and pem file.
EXPECT_TRUE(base::PathExists(crx_path));
EXPECT_TRUE(base::PathExists(pem_path));
// Deliberately don't cleanup the files, and append the pem path.
pack_args.AppendString(pem_path.AsUTF8Unsafe());
// Try to pack again - we should get a warning abot overwriting the crx.
EXPECT_TRUE(TestPackExtensionFunction(
pack_args,
api::developer_private::PACK_STATUS_WARNING,
ExtensionCreator::kOverwriteCRX));
// Try to pack again, with the overwrite flag; this should succeed.
pack_args.AppendInteger(ExtensionCreator::kOverwriteCRX);
EXPECT_TRUE(TestPackExtensionFunction(
pack_args, api::developer_private::PACK_STATUS_SUCCESS, 0));
// Try to pack a final time when omitting (an existing) pem file. We should
// get an error.
base::DeleteFile(crx_path, false);
EXPECT_TRUE(pack_args.Remove(1u, nullptr)); // Remove the pem key argument.
EXPECT_TRUE(pack_args.Remove(1u, nullptr)); // Remove the flags argument.
EXPECT_TRUE(TestPackExtensionFunction(
pack_args, api::developer_private::PACK_STATUS_ERROR, 0));
base::DeleteFile(crx_path, false);
base::DeleteFile(pem_path, false);
}
} // namespace extensions } // namespace extensions
...@@ -79,6 +79,13 @@ void EntryPicker::FileSelectionCanceled(void* params) { ...@@ -79,6 +79,13 @@ void EntryPicker::FileSelectionCanceled(void* params) {
delete this; delete this;
} }
void EntryPicker::MultiFilesSelected(const std::vector<base::FilePath>& files,
void* params) {
NOTREACHED();
client_->FileSelectionCanceled();
delete this;
}
// static // static
void EntryPicker::SkipPickerAndAlwaysSelectPathForTest( void EntryPicker::SkipPickerAndAlwaysSelectPathForTest(
base::FilePath* path) { base::FilePath* path) {
......
...@@ -42,12 +42,13 @@ class EntryPicker : public ui::SelectFileDialog::Listener { ...@@ -42,12 +42,13 @@ class EntryPicker : public ui::SelectFileDialog::Listener {
~EntryPicker() override; ~EntryPicker() override;
private: private:
// ui::SelectFileDialog::Listener implementation. // ui::SelectFileDialog::Listener:
void FileSelected(const base::FilePath& path, void FileSelected(const base::FilePath& path,
int index, int index,
void* params) override; void* params) override;
void FileSelectionCanceled(void* params) override; void FileSelectionCanceled(void* params) override;
void MultiFilesSelected(const std::vector<base::FilePath>& files,
void* params) override;
scoped_refptr<ui::SelectFileDialog> select_file_dialog_; scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
EntryPickerClient* client_; EntryPickerClient* client_;
......
...@@ -343,28 +343,6 @@ cr.define('extensions', function() { ...@@ -343,28 +343,6 @@ cr.define('extensions', function() {
ExtensionList.decorate($('extension-settings-list')); ExtensionList.decorate($('extension-settings-list'));
}; };
// Indicate that warning |message| has occured for pack of |crx_path| and
// |pem_path| files. Ask if user wants override the warning. Send
// |overrideFlags| to repeated 'pack' call to accomplish the override.
ExtensionSettings.askToOverrideWarning =
function(message, crx_path, pem_path, overrideFlags) {
var closeAlert = function() {
ExtensionSettings.showOverlay(null);
};
alertOverlay.setValues(
loadTimeData.getString('packExtensionWarningTitle'),
message,
loadTimeData.getString('packExtensionProceedAnyway'),
loadTimeData.getString('cancel'),
function() {
chrome.send('pack', [crx_path, pem_path, overrideFlags]);
closeAlert();
},
closeAlert);
ExtensionSettings.showOverlay($('alertOverlay'));
};
/** /**
* Returns the current overlay or null if one does not exist. * Returns the current overlay or null if one does not exist.
* @return {Element} The overlay element. * @return {Element} The overlay element.
......
...@@ -48,24 +48,24 @@ cr.define('extensions', function() { ...@@ -48,24 +48,24 @@ cr.define('extensions', function() {
handleCommit_: function(e) { handleCommit_: function(e) {
var extensionPath = $('extension-root-dir').value; var extensionPath = $('extension-root-dir').value;
var privateKeyPath = $('extension-private-key').value; var privateKeyPath = $('extension-private-key').value;
chrome.send('pack', [extensionPath, privateKeyPath, 0]); chrome.developerPrivate.packDirectory(
extensionPath, privateKeyPath, 0, this.onPackResponse_.bind(this));
}, },
/** /**
* Utility function which asks the C++ to show a platform-specific file * Utility function which asks the C++ to show a platform-specific file
* select dialog, and fire |callback| with the |filePath| that resulted. * select dialog, and set the value property of |node| to the selected path.
* |selectType| can be either 'file' or 'folder'. |operation| can be 'load' * @param {SelectType} selectType The type of selection to use.
* or 'pem' which are signals to the C++ to do some operation-specific * @param {FileType} fileType The type of file to select.
* configuration. * @param {HTMLInputElement} node The node to set the value of.
* @private * @private
*/ */
showFileDialog_: function(selectType, operation, callback) { showFileDialog_: function(selectType, fileType, node) {
window.handleFilePathSelected = function(filePath) { chrome.developerPrivate.choosePath(selectType, fileType, function(path) {
callback(filePath); // Last error is set if the user canceled the dialog.
window.handleFilePathSelected = function() {}; if (!chrome.runtime.lastError && path)
}; node.value = path;
});
chrome.send('packExtensionSelectFilePath', [selectType, operation]);
}, },
/** /**
...@@ -74,9 +74,10 @@ cr.define('extensions', function() { ...@@ -74,9 +74,10 @@ cr.define('extensions', function() {
* @private * @private
*/ */
handleBrowseExtensionDir_: function(e) { handleBrowseExtensionDir_: function(e) {
this.showFileDialog_('folder', 'load', function(filePath) { this.showFileDialog_(
$('extension-root-dir').value = filePath; 'FOLDER',
}); 'LOAD',
/** @type {HTMLInputElement} */ ($('extension-root-dir')));
}, },
/** /**
...@@ -85,44 +86,78 @@ cr.define('extensions', function() { ...@@ -85,44 +86,78 @@ cr.define('extensions', function() {
* @private * @private
*/ */
handleBrowsePrivateKey_: function(e) { handleBrowsePrivateKey_: function(e) {
this.showFileDialog_('file', 'pem', function(filePath) { this.showFileDialog_(
$('extension-private-key').value = filePath; 'FILE',
}); 'PEM',
/** @type {HTMLInputElement} */ ($('extension-private-key')));
}, },
};
/** /**
* Wrap up the pack process by showing the success |message| and closing * Handles a response from a packDirectory call.
* the overlay. * @param {PackDirectoryResponse} response The response of the pack call.
* @param {string} message The message to show to the user. * @private
*/ */
PackExtensionOverlay.showSuccessMessage = function(message) { onPackResponse_: function(response) {
alertOverlay.setValues( /** @type {string} */
loadTimeData.getString('packExtensionOverlay'), var alertTitle;
message, /** @type {string} */
loadTimeData.getString('ok'), var alertOk;
'', /** @type {string} */
function() { var alertCancel;
extensions.ExtensionSettings.showOverlay(null); /** @type {function()} */
}); var alertOkCallback;
extensions.ExtensionSettings.showOverlay($('alertOverlay')); /** @type {function()} */
}; var alertCancelCallback;
/** var closeAlert = function() {
* Post an alert overlay showing |message|, and upon acknowledgement, close extensions.ExtensionSettings.showOverlay(null);
* the alert overlay and return to showing the PackExtensionOverlay. };
* @param {string} message The error message.
*/ // TODO(devlin): Once we expose enums on extension APIs, we should use
PackExtensionOverlay.showError = function(message) { // those objects, instead of a string.
alertOverlay.setValues( switch (response.status) {
loadTimeData.getString('packExtensionErrorTitle'), case 'SUCCESS':
message, alertTitle = loadTimeData.getString('packExtensionOverlay');
loadTimeData.getString('ok'), alertOk = loadTimeData.getString('ok');
'', alertOkCallback = closeAlert;
function() { // No 'Cancel' option.
extensions.ExtensionSettings.showOverlay($('pack-extension-overlay')); break;
}); case 'WARNING':
extensions.ExtensionSettings.showOverlay($('alertOverlay')); alertTitle = loadTimeData.getString('packExtensionWarningTitle');
alertOk = loadTimeData.getString('packExtensionProceedAnyway');
alertCancel = loadTimeData.getString('cancel');
alertOkCallback = function() {
chrome.developerPrivate.packDirectory(
response.item_path,
response.pem_path,
response.override_flags,
this.onPackResponse_.bind(this));
closeAlert();
}.bind(this);
alertCancelCallback = closeAlert;
break;
case 'ERROR':
alertTitle = loadTimeData.getString('packExtensionErrorTitle');
alertOk = loadTimeData.getString('ok');
alertOkCallback = function() {
extensions.ExtensionSettings.showOverlay(
$('pack-extension-overlay'));
};
// No 'Cancel' option.
break;
default:
assertNotReached();
return;
}
alertOverlay.setValues(alertTitle,
response.message,
alertOk,
alertCancel,
alertOkCallback,
alertCancelCallback);
extensions.ExtensionSettings.showOverlay($('alertOverlay'));
},
}; };
// Export // Export
......
...@@ -636,6 +636,30 @@ void ExtensionSettingsHandler::GetLocalizedValues( ...@@ -636,6 +636,30 @@ void ExtensionSettingsHandler::GetLocalizedValues(
// uber extensions. // uber extensions.
source->AddString("extensionUninstall", source->AddString("extensionUninstall",
l10n_util::GetStringUTF16(IDS_EXTENSIONS_UNINSTALL)); l10n_util::GetStringUTF16(IDS_EXTENSIONS_UNINSTALL));
// Pack Extension Overlay:
source->AddString("packExtensionOverlay",
l10n_util::GetStringUTF16(IDS_EXTENSION_PACK_DIALOG_TITLE));
source->AddString("packExtensionHeading",
l10n_util::GetStringUTF16(IDS_EXTENSION_PACK_DIALOG_HEADING));
source->AddString("packExtensionCommit",
l10n_util::GetStringUTF16(IDS_EXTENSION_PACK_BUTTON));
source->AddString("ok", l10n_util::GetStringUTF16(IDS_OK));
source->AddString("cancel", l10n_util::GetStringUTF16(IDS_CANCEL));
source->AddString("packExtensionRootDir",
l10n_util::GetStringUTF16(
IDS_EXTENSION_PACK_DIALOG_ROOT_DIRECTORY_LABEL));
source->AddString("packExtensionPrivateKey",
l10n_util::GetStringUTF16(IDS_EXTENSION_PACK_DIALOG_PRIVATE_KEY_LABEL));
source->AddString("packExtensionBrowseButton",
l10n_util::GetStringUTF16(IDS_EXTENSION_PACK_DIALOG_BROWSE));
source->AddString("packExtensionProceedAnyway",
l10n_util::GetStringUTF16(IDS_EXTENSION_PROCEED_ANYWAY));
source->AddString("packExtensionWarningTitle",
l10n_util::GetStringUTF16(IDS_EXTENSION_PACK_WARNING_TITLE));
source->AddString("packExtensionErrorTitle",
l10n_util::GetStringUTF16(IDS_EXTENSION_PACK_ERROR_TITLE));
} }
void ExtensionSettingsHandler::RenderViewDeleted( void ExtensionSettingsHandler::RenderViewDeleted(
......
...@@ -10,7 +10,6 @@ ...@@ -10,7 +10,6 @@
#include "chrome/browser/ui/webui/extensions/extension_loader_handler.h" #include "chrome/browser/ui/webui/extensions/extension_loader_handler.h"
#include "chrome/browser/ui/webui/extensions/extension_settings_handler.h" #include "chrome/browser/ui/webui/extensions/extension_settings_handler.h"
#include "chrome/browser/ui/webui/extensions/install_extension_handler.h" #include "chrome/browser/ui/webui/extensions/install_extension_handler.h"
#include "chrome/browser/ui/webui/extensions/pack_extension_handler.h"
#include "chrome/browser/ui/webui/metrics_handler.h" #include "chrome/browser/ui/webui/metrics_handler.h"
#include "chrome/common/url_constants.h" #include "chrome/common/url_constants.h"
#include "content/public/browser/web_ui.h" #include "content/public/browser/web_ui.h"
...@@ -51,10 +50,6 @@ ExtensionsUI::ExtensionsUI(content::WebUI* web_ui) : WebUIController(web_ui) { ...@@ -51,10 +50,6 @@ ExtensionsUI::ExtensionsUI(content::WebUI* web_ui) : WebUIController(web_ui) {
handler->GetLocalizedValues(source); handler->GetLocalizedValues(source);
web_ui->AddMessageHandler(handler); web_ui->AddMessageHandler(handler);
PackExtensionHandler* pack_handler = new PackExtensionHandler();
pack_handler->GetLocalizedValues(source);
web_ui->AddMessageHandler(pack_handler);
CommandHandler* commands_handler = new CommandHandler(profile); CommandHandler* commands_handler = new CommandHandler(profile);
commands_handler->GetLocalizedValues(source); commands_handler->GetLocalizedValues(source);
web_ui->AddMessageHandler(commands_handler); web_ui->AddMessageHandler(commands_handler);
......
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/webui/extensions/pack_extension_handler.h"
#include "base/bind.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/extensions/extension_creator.h"
#include "chrome/browser/ui/chrome_select_file_policy.h"
#include "chrome/grit/generated_resources.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h"
#include "content/public/browser/web_ui_data_source.h"
#include "ui/base/l10n/l10n_util.h"
namespace extensions {
PackExtensionHandler::PackExtensionHandler() {
}
PackExtensionHandler::~PackExtensionHandler() {
// There may be pending file dialogs, we need to tell them that we've gone
// away so they don't try and call back to us.
if (load_extension_dialog_.get())
load_extension_dialog_->ListenerDestroyed();
if (pack_job_.get())
pack_job_->ClearClient();
}
void PackExtensionHandler::GetLocalizedValues(
content::WebUIDataSource* source) {
source->AddString("packExtensionOverlay",
l10n_util::GetStringUTF16(IDS_EXTENSION_PACK_DIALOG_TITLE));
source->AddString("packExtensionHeading",
l10n_util::GetStringUTF16(IDS_EXTENSION_PACK_DIALOG_HEADING));
source->AddString("packExtensionCommit",
l10n_util::GetStringUTF16(IDS_EXTENSION_PACK_BUTTON));
source->AddString("ok", l10n_util::GetStringUTF16(IDS_OK));
source->AddString("cancel", l10n_util::GetStringUTF16(IDS_CANCEL));
source->AddString("packExtensionRootDir",
l10n_util::GetStringUTF16(
IDS_EXTENSION_PACK_DIALOG_ROOT_DIRECTORY_LABEL));
source->AddString("packExtensionPrivateKey",
l10n_util::GetStringUTF16(IDS_EXTENSION_PACK_DIALOG_PRIVATE_KEY_LABEL));
source->AddString("packExtensionBrowseButton",
l10n_util::GetStringUTF16(IDS_EXTENSION_PACK_DIALOG_BROWSE));
source->AddString("packExtensionProceedAnyway",
l10n_util::GetStringUTF16(IDS_EXTENSION_PROCEED_ANYWAY));
source->AddString("packExtensionWarningTitle",
l10n_util::GetStringUTF16(IDS_EXTENSION_PACK_WARNING_TITLE));
source->AddString("packExtensionErrorTitle",
l10n_util::GetStringUTF16(IDS_EXTENSION_PACK_ERROR_TITLE));
}
void PackExtensionHandler::RegisterMessages() {
web_ui()->RegisterMessageCallback(
"pack",
base::Bind(&PackExtensionHandler::HandlePackMessage,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"packExtensionSelectFilePath",
base::Bind(&PackExtensionHandler::HandleSelectFilePathMessage,
base::Unretained(this)));
}
void PackExtensionHandler::OnPackSuccess(const base::FilePath& crx_file,
const base::FilePath& pem_file) {
base::ListValue arguments;
arguments.Append(new base::StringValue(base::UTF16ToUTF8(
PackExtensionJob::StandardSuccessMessage(crx_file, pem_file))));
web_ui()->CallJavascriptFunction(
"extensions.PackExtensionOverlay.showSuccessMessage", arguments);
}
void PackExtensionHandler::OnPackFailure(const std::string& error,
ExtensionCreator::ErrorType type) {
if (type == ExtensionCreator::kCRXExists) {
base::StringValue error_str(error);
base::StringValue extension_path_str(extension_path_.value());
base::StringValue key_path_str(private_key_path_.value());
base::FundamentalValue overwrite_flag(ExtensionCreator::kOverwriteCRX);
web_ui()->CallJavascriptFunction(
"extensions.ExtensionSettings.askToOverrideWarning",
error_str, extension_path_str, key_path_str, overwrite_flag);
} else {
ShowAlert(error);
}
}
void PackExtensionHandler::FileSelected(const base::FilePath& path, int index,
void* params) {
base::ListValue results;
results.Append(new base::StringValue(path.value()));
web_ui()->CallJavascriptFunction("window.handleFilePathSelected", results);
}
void PackExtensionHandler::MultiFilesSelected(
const std::vector<base::FilePath>& files, void* params) {
NOTREACHED();
}
void PackExtensionHandler::HandlePackMessage(const base::ListValue* args) {
DCHECK_EQ(3U, args->GetSize());
double flags_double = 0.0;
base::FilePath::StringType extension_path_str;
base::FilePath::StringType private_key_path_str;
if (!args->GetString(0, &extension_path_str) ||
!args->GetString(1, &private_key_path_str) ||
!args->GetDouble(2, &flags_double)) {
NOTREACHED();
return;
}
extension_path_ = base::FilePath(extension_path_str);
private_key_path_ = base::FilePath(private_key_path_str);
int run_flags = static_cast<int>(flags_double);
base::FilePath root_directory = extension_path_;
base::FilePath key_file = private_key_path_;
last_used_path_ = extension_path_;
if (root_directory.empty()) {
if (extension_path_.empty()) {
ShowAlert(l10n_util::GetStringUTF8(
IDS_EXTENSION_PACK_DIALOG_ERROR_ROOT_REQUIRED));
} else {
ShowAlert(l10n_util::GetStringUTF8(
IDS_EXTENSION_PACK_DIALOG_ERROR_ROOT_INVALID));
}
return;
}
if (!private_key_path_.empty() && key_file.empty()) {
ShowAlert(l10n_util::GetStringUTF8(
IDS_EXTENSION_PACK_DIALOG_ERROR_KEY_INVALID));
return;
}
pack_job_ = new PackExtensionJob(this, root_directory, key_file, run_flags);
pack_job_->Start();
}
void PackExtensionHandler::HandleSelectFilePathMessage(
const base::ListValue* args) {
DCHECK_EQ(2U, args->GetSize());
std::string select_type;
if (!args->GetString(0, &select_type))
NOTREACHED();
std::string operation;
if (!args->GetString(1, &operation))
NOTREACHED();
ui::SelectFileDialog::Type type = ui::SelectFileDialog::SELECT_FOLDER;
ui::SelectFileDialog::FileTypeInfo info;
int file_type_index = 0;
base::FilePath path_to_use = last_used_path_;
if (select_type == "file") {
type = ui::SelectFileDialog::SELECT_OPEN_FILE;
path_to_use = base::FilePath();
}
base::string16 select_title;
if (operation == "load") {
select_title = l10n_util::GetStringUTF16(IDS_EXTENSION_LOAD_FROM_DIRECTORY);
} else if (operation == "pem") {
select_title = l10n_util::GetStringUTF16(
IDS_EXTENSION_PACK_DIALOG_SELECT_KEY);
info.extensions.push_back(std::vector<base::FilePath::StringType>());
info.extensions.front().push_back(FILE_PATH_LITERAL("pem"));
info.extension_description_overrides.push_back(
l10n_util::GetStringUTF16(
IDS_EXTENSION_PACK_DIALOG_KEY_FILE_TYPE_DESCRIPTION));
info.include_all_files = true;
file_type_index = 1;
} else {
NOTREACHED();
}
load_extension_dialog_ = ui::SelectFileDialog::Create(
this, new ChromeSelectFilePolicy(web_ui()->GetWebContents()));
load_extension_dialog_->SelectFile(
type,
select_title,
path_to_use,
&info,
file_type_index,
base::FilePath::StringType(),
web_ui()->GetWebContents()->GetTopLevelNativeWindow(),
NULL);
}
void PackExtensionHandler::ShowAlert(const std::string& message) {
base::ListValue arguments;
arguments.Append(new base::StringValue(message));
web_ui()->CallJavascriptFunction(
"extensions.PackExtensionOverlay.showError", arguments);
}
} // namespace extensions
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_WEBUI_EXTENSIONS_PACK_EXTENSION_HANDLER_H_
#define CHROME_BROWSER_UI_WEBUI_EXTENSIONS_PACK_EXTENSION_HANDLER_H_
#include <string>
#include "base/files/file_path.h"
#include "chrome/browser/browsing_data/browsing_data_remover.h"
#include "chrome/browser/extensions/pack_extension_job.h"
#include "chrome/browser/plugins/plugin_data_remover_helper.h"
#include "content/public/browser/web_ui_message_handler.h"
#include "ui/shell_dialogs/select_file_dialog.h"
namespace content {
class WebUIDataSource;
}
namespace extensions {
// Clear browser data handler page UI handler.
class PackExtensionHandler : public content::WebUIMessageHandler,
public ui::SelectFileDialog::Listener,
public PackExtensionJob::Client {
public:
PackExtensionHandler();
~PackExtensionHandler() override;
void GetLocalizedValues(content::WebUIDataSource* source);
// WebUIMessageHandler implementation.
void RegisterMessages() override;
// ExtensionPackJob::Client implementation.
void OnPackSuccess(const base::FilePath& crx_file,
const base::FilePath& key_file) override;
void OnPackFailure(const std::string& error,
ExtensionCreator::ErrorType) override;
private:
// SelectFileDialog::Listener implementation.
void FileSelected(const base::FilePath& path,
int index,
void* params) override;
void MultiFilesSelected(const std::vector<base::FilePath>& files,
void* params) override;
void FileSelectionCanceled(void* params) override {}
// JavaScript callback to start packing an extension.
void HandlePackMessage(const base::ListValue* args);
// JavaScript callback to show a file browse dialog.
// |args[0]| must be a string that specifies the file dialog type: file or
// folder.
// |args[1]| must be a string that specifies the operation to perform: load
// or pem.
void HandleSelectFilePathMessage(const base::ListValue* args);
// A function to ask the page to show an alert.
void ShowAlert(const std::string& message);
// Used to package the extension.
scoped_refptr<PackExtensionJob> pack_job_;
// Returned by the SelectFileDialog machinery. Used to initiate the selection
// dialog.
scoped_refptr<ui::SelectFileDialog> load_extension_dialog_;
// Path to root directory of extension.
base::FilePath extension_path_;
// Path to private key file, or null if none specified.
base::FilePath private_key_path_;
// Path to the last used folder to load an extension.
base::FilePath last_used_path_;
DISALLOW_COPY_AND_ASSIGN(PackExtensionHandler);
};
} // namespace extensions
#endif // CHROME_BROWSER_UI_WEBUI_EXTENSIONS_PACK_EXTENSION_HANDLER_H_
...@@ -2504,8 +2504,6 @@ ...@@ -2504,8 +2504,6 @@
'browser/ui/webui/extensions/extension_error_ui_util.h', 'browser/ui/webui/extensions/extension_error_ui_util.h',
'browser/ui/webui/extensions/extension_icon_source.cc', 'browser/ui/webui/extensions/extension_icon_source.cc',
'browser/ui/webui/extensions/extension_icon_source.h', 'browser/ui/webui/extensions/extension_icon_source.h',
'browser/ui/webui/extensions/pack_extension_handler.cc',
'browser/ui/webui/extensions/pack_extension_handler.h',
'browser/ui/webui/voice_search_ui.cc', 'browser/ui/webui/voice_search_ui.cc',
'browser/ui/webui/voice_search_ui.h', 'browser/ui/webui/voice_search_ui.h',
], ],
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment