diff options
-rw-r--r-- | chrome/browser/chrome_browser_main.cc | 8 | ||||
-rw-r--r-- | chrome/browser/extensions/crx_installer.cc | 14 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_install_dialog.cc | 4 | ||||
-rw-r--r-- | chrome/browser/extensions/startup_helper.cc | 75 | ||||
-rw-r--r-- | chrome/browser/extensions/startup_helper.h | 5 | ||||
-rw-r--r-- | chrome/browser/extensions/tab_helper.cc | 29 | ||||
-rw-r--r-- | chrome/browser/extensions/tab_helper.h | 12 | ||||
-rw-r--r-- | chrome/browser/extensions/webstore_inline_install_browsertest.cc | 60 | ||||
-rw-r--r-- | chrome/browser/extensions/webstore_inline_installer.cc | 51 | ||||
-rw-r--r-- | chrome/browser/extensions/webstore_inline_installer.h | 33 | ||||
-rw-r--r-- | chrome/common/chrome_result_codes.h | 4 | ||||
-rw-r--r-- | chrome/common/chrome_switches.cc | 7 | ||||
-rw-r--r-- | chrome/common/chrome_switches.h | 1 |
13 files changed, 232 insertions, 71 deletions
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc index f619d2a0..a5ab527 100644 --- a/chrome/browser/chrome_browser_main.cc +++ b/chrome/browser/chrome_browser_main.cc @@ -1292,6 +1292,14 @@ int ChromeBrowserMainParts::PreMainMessageLoopRunImpl() { return chrome::RESULT_CODE_UNINSTALL_EXTENSION_ERROR; } + if (parsed_command_line().HasSwitch(switches::kInstallFromWebstore)) { + extensions::StartupHelper helper; + if (helper.InstallFromWebstore(parsed_command_line(), profile_)) + return content::RESULT_CODE_NORMAL_EXIT; + return chrome::RESULT_CODE_INSTALL_FROM_WEBSTORE_ERROR; + } + + // Start watching for hangs during startup. We disarm this hang detector when // ThreadWatcher takes over or when browser is shutdown or when // startup_watcher_ is deleted. diff --git a/chrome/browser/extensions/crx_installer.cc b/chrome/browser/extensions/crx_installer.cc index 3886528..d1f2a77 100644 --- a/chrome/browser/extensions/crx_installer.cc +++ b/chrome/browser/extensions/crx_installer.cc @@ -110,9 +110,11 @@ CrxInstaller::CrxInstaller( return; CHECK(profile_->IsSameProfile(approval->profile)); - client_->install_ui()->SetUseAppInstalledBubble( - approval->use_app_installed_bubble); - client_->install_ui()->SetSkipPostInstallUI(approval->skip_post_install_ui); + if (client_) { + client_->install_ui()->SetUseAppInstalledBubble( + approval->use_app_installed_bubble); + client_->install_ui()->SetSkipPostInstallUI(approval->skip_post_install_ui); + } if (approval->skip_install_dialog) { // Mark the extension as approved, but save the expected manifest and ID @@ -139,8 +141,10 @@ CrxInstaller::~CrxInstaller() { base::Bind(&extension_file_util::DeleteFile, source_file_, false)); } // Make sure the UI is deleted on the ui thread. - BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, client_); - client_ = NULL; + if (client_) { + BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, client_); + client_ = NULL; + } } void CrxInstaller::InstallCrx(const FilePath& source_file) { diff --git a/chrome/browser/extensions/extension_install_dialog.cc b/chrome/browser/extensions/extension_install_dialog.cc index 71fa908bd..a3cd87b 100644 --- a/chrome/browser/extensions/extension_install_dialog.cc +++ b/chrome/browser/extensions/extension_install_dialog.cc @@ -68,9 +68,5 @@ void ShowExtensionInstallDialog(gfx::NativeWindow parent, DoAutoConfirm(auto_confirm, delegate); return; } - if (!parent) { - delegate->InstallUIAbort(false); - return; - } ShowExtensionInstallDialogImpl(parent, navigator, delegate, prompt); } diff --git a/chrome/browser/extensions/startup_helper.cc b/chrome/browser/extensions/startup_helper.cc index b0c90bc..aabbf71 100644 --- a/chrome/browser/extensions/startup_helper.cc +++ b/chrome/browser/extensions/startup_helper.cc @@ -4,13 +4,19 @@ #include "chrome/browser/extensions/startup_helper.h" +#include "base/bind.h" #include "base/command_line.h" +#include "base/message_loop.h" #include "base/string_util.h" #include "base/stringprintf.h" #include "base/utf_string_conversions.h" #include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/webstore_inline_installer.h" #include "chrome/browser/profiles/profile.h" #include "chrome/common/chrome_switches.h" +#include "chrome/common/extensions/extension.h" +#include "content/public/browser/web_contents.h" +#include "ipc/ipc_message.h" namespace { @@ -76,6 +82,75 @@ bool StartupHelper::UninstallExtension(const CommandLine& cmd_line, extension_id); } +namespace { + +class AppInstallHelper { + public: + AppInstallHelper(); + virtual ~AppInstallHelper(); + bool success() { return success_; } + const std::string& error() { return error_; } + + WebstoreInlineInstaller::Callback Callback(); + void OnAppInstallComplete(bool success, const std::string& error); + + private: + // These hold on to the result of the app install when it is complete. + bool success_; + std::string error_; +}; + +AppInstallHelper::AppInstallHelper() : success_(false) {} + +AppInstallHelper::~AppInstallHelper() {} + +WebstoreInlineInstaller::Callback AppInstallHelper::Callback() { + return base::Bind(&AppInstallHelper::OnAppInstallComplete, + base::Unretained(this)); +} +void AppInstallHelper::OnAppInstallComplete(bool success, + const std::string& error) { + success_ = success; + error_= error; + MessageLoop::current()->Quit(); +} + +} // namespace + +bool StartupHelper::InstallFromWebstore(const CommandLine& cmd_line, + Profile* profile) { + std::string id = cmd_line.GetSwitchValueASCII(switches::kInstallFromWebstore); + if (!Extension::IdIsValid(id)) { + LOG(ERROR) << "Invalid id for " << switches::kInstallFromWebstore + << " : '" << id << "'"; + return false; + } + + // TODO(asargent) - it would be nice not to need a WebContents just to + // use the inline installer. (crbug.com/149039) + scoped_ptr<content::WebContents> web_contents( + content::WebContents::Create(profile, NULL, MSG_ROUTING_NONE, NULL)); + + AppInstallHelper helper; + WebstoreInlineInstaller::Callback callback = + base::Bind(&AppInstallHelper::OnAppInstallComplete, + base::Unretained(&helper)); + scoped_refptr<WebstoreInlineInstaller> installer( + new WebstoreInlineInstaller( + web_contents.get(), + id, + WebstoreInlineInstaller::DO_NOT_REQUIRE_VERIFIED_SITE, + GURL(), + callback)); + installer->set_skip_post_install_ui(true); + installer->BeginInstall(); + + MessageLoop::current()->Run(); + if (!helper.success()) + LOG(ERROR) << "InstallFromWebstore failed with error: " << helper.error(); + return helper.success(); +} + StartupHelper::~StartupHelper() { if (pack_job_.get()) pack_job_->ClearClient(); diff --git a/chrome/browser/extensions/startup_helper.h b/chrome/browser/extensions/startup_helper.h index f4aedc4..1f88d99 100644 --- a/chrome/browser/extensions/startup_helper.h +++ b/chrome/browser/extensions/startup_helper.h @@ -33,6 +33,11 @@ class StartupHelper : public PackExtensionJob::Client { // could not be started. bool UninstallExtension(const CommandLine& cmd_line, Profile* profile); + // Handle --install-from-webstore flag from |cmd_line| by downloading + // metadata from the webstore for the given id, prompting the user to + // confirm, and then downloading the crx and installing it. + bool InstallFromWebstore(const CommandLine& cmd_line, Profile* profile); + private: scoped_refptr<PackExtensionJob> pack_job_; bool pack_job_succeeded_; diff --git a/chrome/browser/extensions/tab_helper.cc b/chrome/browser/extensions/tab_helper.cc index 0f5c91f..0d3c477 100644 --- a/chrome/browser/extensions/tab_helper.cc +++ b/chrome/browser/extensions/tab_helper.cc @@ -259,13 +259,15 @@ void TabHelper::OnInlineWebstoreInstall( int return_route_id, const std::string& webstore_item_id, const GURL& requestor_url) { + WebstoreInlineInstaller::Callback callback = + base::Bind(&TabHelper::OnInlineInstallComplete, base::Unretained(this), + install_id, return_route_id); scoped_refptr<WebstoreInlineInstaller> installer(new WebstoreInlineInstaller( web_contents(), - install_id, - return_route_id, webstore_item_id, + WebstoreInlineInstaller::REQUIRE_VERIFIED_SITE, requestor_url, - this)); + callback)); installer->BeginInstall(); } @@ -415,16 +417,17 @@ WindowController* TabHelper::GetExtensionWindowController() const { return ExtensionTabUtil::GetWindowControllerOfTab(web_contents()); } -void TabHelper::OnInlineInstallSuccess(int install_id, int return_route_id) { - Send(new ExtensionMsg_InlineWebstoreInstallResponse( - return_route_id, install_id, true, "")); -} - -void TabHelper::OnInlineInstallFailure(int install_id, - int return_route_id, - const std::string& error) { - Send(new ExtensionMsg_InlineWebstoreInstallResponse( - return_route_id, install_id, false, error)); +void TabHelper::OnInlineInstallComplete(int install_id, + int return_route_id, + bool success, + const std::string& error) { + if (success) { + Send(new ExtensionMsg_InlineWebstoreInstallResponse( + return_route_id, install_id, true, "")); + } else { + Send(new ExtensionMsg_InlineWebstoreInstallResponse( + return_route_id, install_id, false, error)); + } } WebContents* TabHelper::GetAssociatedWebContents() const { diff --git a/chrome/browser/extensions/tab_helper.h b/chrome/browser/extensions/tab_helper.h index 05a2665..f9fac32 100644 --- a/chrome/browser/extensions/tab_helper.h +++ b/chrome/browser/extensions/tab_helper.h @@ -37,7 +37,6 @@ class ScriptExecutor; class TabHelper : public content::WebContentsObserver, public ExtensionFunctionDispatcher::Delegate, public ImageLoadingTracker::Observer, - public WebstoreInlineInstaller::Delegate, public AppNotifyChannelSetup::Delegate, public base::SupportsWeakPtr<TabHelper>, public content::NotificationObserver, @@ -163,12 +162,11 @@ class TabHelper : public content::WebContentsObserver, const std::string& extension_id, int index) OVERRIDE; - // WebstoreInlineInstaller::Delegate. - virtual void OnInlineInstallSuccess(int install_id, - int return_route_id) OVERRIDE; - virtual void OnInlineInstallFailure(int install_id, - int return_route_id, - const std::string& error) OVERRIDE; + // WebstoreInlineInstaller::Callback. + virtual void OnInlineInstallComplete(int install_id, + int return_route_id, + bool success, + const std::string& error); // AppNotifyChannelSetup::Delegate. virtual void AppNotifyChannelSetupComplete( diff --git a/chrome/browser/extensions/webstore_inline_install_browsertest.cc b/chrome/browser/extensions/webstore_inline_install_browsertest.cc index becb99d..737ce5b 100644 --- a/chrome/browser/extensions/webstore_inline_install_browsertest.cc +++ b/chrome/browser/extensions/webstore_inline_install_browsertest.cc @@ -9,6 +9,7 @@ #include "chrome/browser/extensions/extension_install_dialog.h" #include "chrome/browser/extensions/extension_install_ui.h" #include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/startup_helper.h" #include "chrome/browser/extensions/webstore_inline_installer.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_tabstrip.h" @@ -17,6 +18,8 @@ #include "chrome/common/chrome_switches.h" #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/ui_test_utils.h" +#include "content/public/browser/notification_registrar.h" +#include "content/public/browser/notification_service.h" #include "content/public/browser/notification_types.h" #include "content/public/browser/web_contents.h" #include "content/public/test/browser_test_utils.h" @@ -25,10 +28,12 @@ #include "net/base/mock_host_resolver.h" using content::WebContents; +using extensions::Extension; const char kWebstoreDomain[] = "cws.com"; const char kAppDomain[] = "app.com"; const char kNonAppDomain[] = "nonapp.com"; +const char kTestExtensionId[] = "ecglahbcnmdpdciemllbhojghbkagdje"; class WebstoreInlineInstallTest : public InProcessBrowserTest { public: @@ -93,8 +98,7 @@ IN_PROC_BROWSER_TEST_F(WebstoreInlineInstallTest, Install) { RunInlineInstallTest("runTest"); const extensions::Extension* extension = browser()->profile()-> - GetExtensionService()->GetExtensionById( - "ecglahbcnmdpdciemllbhojghbkagdje", false); + GetExtensionService()->GetExtensionById(kTestExtensionId, false); EXPECT_TRUE(extension); } @@ -174,3 +178,55 @@ IN_PROC_BROWSER_TEST_F(WebstoreInlineInstallUnpackFailureTest, RunInlineInstallTest("runTest"); } + +class CommandLineWebstoreInstall : public WebstoreInlineInstallTest, + public content::NotificationObserver { + public: + CommandLineWebstoreInstall() : saw_install_(false) {} + virtual ~CommandLineWebstoreInstall() {} + + virtual void SetUpOnMainThread() OVERRIDE { + WebstoreInlineInstallTest::SetUpOnMainThread(); + registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALLED, + content::NotificationService::AllSources()); + CommandLine::ForCurrentProcess()->AppendSwitchASCII( + switches::kInstallFromWebstore, kTestExtensionId); + } + + bool saw_install() { return saw_install_; } + + protected: + // NotificationObserver interface. + virtual void Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) OVERRIDE { + ASSERT_EQ(chrome::NOTIFICATION_EXTENSION_INSTALLED, type); + const Extension* extension = content::Details<Extension>(details).ptr(); + ASSERT_TRUE(extension != NULL); + EXPECT_EQ(extension->id(), kTestExtensionId); + saw_install_ = true; + } + + content::NotificationRegistrar registrar_; + + // Have we seen an installation notification for kTestExtensionId ? + bool saw_install_; +}; + +IN_PROC_BROWSER_TEST_F(CommandLineWebstoreInstall, Accept) { + CommandLine* command_line = CommandLine::ForCurrentProcess(); + command_line->AppendSwitchASCII( + switches::kAppsGalleryInstallAutoConfirmForTests, "accept"); + extensions::StartupHelper helper; + EXPECT_TRUE(helper.InstallFromWebstore(*command_line, browser()->profile())); + EXPECT_TRUE(saw_install()); +} + +IN_PROC_BROWSER_TEST_F(CommandLineWebstoreInstall, Cancel) { + CommandLine* command_line = CommandLine::ForCurrentProcess(); + command_line->AppendSwitchASCII( + switches::kAppsGalleryInstallAutoConfirmForTests, "cancel"); + extensions::StartupHelper helper; + EXPECT_FALSE(helper.InstallFromWebstore(*command_line, browser()->profile())); + EXPECT_FALSE(saw_install()); +} diff --git a/chrome/browser/extensions/webstore_inline_installer.cc b/chrome/browser/extensions/webstore_inline_installer.cc index c833c7a..6fe8c37 100644 --- a/chrome/browser/extensions/webstore_inline_installer.cc +++ b/chrome/browser/extensions/webstore_inline_installer.cc @@ -147,20 +147,21 @@ class SafeWebstoreResponseParser : public UtilityProcessHostClient { scoped_ptr<DictionaryValue> parsed_webstore_data_; }; -WebstoreInlineInstaller::WebstoreInlineInstaller(WebContents* web_contents, - int install_id, - int return_route_id, - std::string webstore_item_id, - GURL requestor_url, - Delegate* delegate) +WebstoreInlineInstaller::WebstoreInlineInstaller( + WebContents* web_contents, + std::string webstore_item_id, + VerifiedSiteRequired require_verified_site, + GURL requestor_url, + Callback callback) : content::WebContentsObserver(web_contents), - install_id_(install_id), - return_route_id_(return_route_id), id_(webstore_item_id), + require_verified_site_(require_verified_site == REQUIRE_VERIFIED_SITE), requestor_url_(requestor_url), - delegate_(delegate), + callback_(callback), + skip_post_install_ui_(false), average_rating_(0.0), rating_count_(0) { + CHECK(!callback.is_null()); } void WebstoreInlineInstaller::BeginInstall() { @@ -294,21 +295,21 @@ void WebstoreInlineInstaller::OnWebstoreResponseParseSuccess( } } - // Verified site is required - if (webstore_data->HasKey(kVerifiedSiteKey)) { + // Check for a verified site if required. + if (require_verified_site_) { + if (!webstore_data->HasKey(kVerifiedSiteKey)) { + CompleteInstall(kNoVerifiedSiteError); + return; + } std::string verified_site; if (!webstore_data->GetString(kVerifiedSiteKey, &verified_site)) { CompleteInstall(kInvalidWebstoreResponseError); return; } - if (!IsRequestorURLInVerifiedSite(requestor_url_, verified_site)) { CompleteInstall(kNotFromVerifiedSiteError); return; } - } else { - CompleteInstall(kNoVerifiedSiteError); - return; } scoped_refptr<WebstoreInstallHelper> helper = new WebstoreInstallHelper( @@ -365,8 +366,7 @@ void WebstoreInlineInstaller::OnWebstoreParseSuccess( return; } - install_ui_.reset( - ExtensionInstallUI::CreateInstallPromptWithWebContents(web_contents())); + install_ui_.reset(new ExtensionInstallPrompt(NULL, web_contents(), profile)); install_ui_->ConfirmInlineInstall(this, dummy_extension_, &icon_, prompt); // Control flow finishes up in InstallUIProceed or InstallUIAbort. } @@ -393,7 +393,10 @@ void WebstoreInlineInstaller::InstallUIProceed() { profile, id_, scoped_ptr<base::DictionaryValue>(manifest_.get()->DeepCopy()))); - approval->use_app_installed_bubble = true; + if (skip_post_install_ui_) + approval->skip_post_install_ui = true; + else + approval->use_app_installed_bubble = true; scoped_refptr<WebstoreInstaller> installer = new WebstoreInstaller( profile, this, &(web_contents()->GetController()), id_, approval.Pass(), @@ -406,6 +409,7 @@ void WebstoreInlineInstaller::InstallUIAbort(bool user_initiated) { } void WebstoreInlineInstaller::WebContentsDestroyed(WebContents* web_contents) { + callback_.Reset(); // Abort any in-progress fetches. if (webstore_data_url_fetcher_.get()) { webstore_data_url_fetcher_.reset(); @@ -425,15 +429,8 @@ void WebstoreInlineInstaller::OnExtensionInstallFailure( } void WebstoreInlineInstaller::CompleteInstall(const std::string& error) { - // Only bother responding if there's still a tab contents to send back the - // response to. - if (web_contents()) { - if (error.empty()) { - delegate_->OnInlineInstallSuccess(install_id_, return_route_id_); - } else { - delegate_->OnInlineInstallFailure(install_id_, return_route_id_, error); - } - } + if (!callback_.is_null()) + callback_.Run(error.empty(), error); Release(); // Matches the AddRef in BeginInstall. } diff --git a/chrome/browser/extensions/webstore_inline_installer.h b/chrome/browser/extensions/webstore_inline_installer.h index c01b481..5525b69 100644 --- a/chrome/browser/extensions/webstore_inline_installer.h +++ b/chrome/browser/extensions/webstore_inline_installer.h @@ -7,6 +7,7 @@ #include <string> +#include "base/callback.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/values.h" @@ -27,6 +28,9 @@ namespace extensions { class Extension; class SafeWebstoreResponseParser; +// TODO(asargent) - rename this class to something like +// WebstoreStandaloneInstaller. +// // Manages inline installs requested by a page (downloads and parses metadata // from the webstore, shows the install UI, starts the download once the user // confirms). Clients must implement the WebstoreInlineInstaller::Delegate @@ -41,21 +45,24 @@ class WebstoreInlineInstaller public WebstoreInstaller::Delegate, public WebstoreInstallHelper::Delegate { public: - class Delegate { - public: - virtual void OnInlineInstallSuccess(int install_id, - int return_route_id) = 0; - virtual void OnInlineInstallFailure(int install_id, - int return_route_id, - const std::string& error) = 0; + enum VerifiedSiteRequired { + REQUIRE_VERIFIED_SITE, + DO_NOT_REQUIRE_VERIFIED_SITE }; + // A callback for when the install process completes successfully or not. If + // there was a failure, |success| will be false and |error| may contain a + // developer-readable error message about why it failed. + typedef base::Callback<void(bool success, const std::string& error)> Callback; + WebstoreInlineInstaller(content::WebContents* web_contents, - int install_id, - int return_route_id, std::string webstore_item_id, + VerifiedSiteRequired require_verified_site, GURL requestor_url, - Delegate* d); + Callback callback); + + void set_skip_post_install_ui(bool skip) { skip_post_install_ui_ = skip; } + void BeginInstall(); private: @@ -115,12 +122,12 @@ class WebstoreInlineInstaller static bool IsRequestorURLInVerifiedSite(const GURL& requestor_url, const std::string& verified_site); - int install_id_; - int return_route_id_; std::string id_; + bool require_verified_site_; GURL requestor_url_; - Delegate* delegate_; + Callback callback_; scoped_ptr<ExtensionInstallPrompt> install_ui_; + bool skip_post_install_ui_; // For fetching webstore JSON data. scoped_ptr<net::URLFetcher> webstore_data_url_fetcher_; diff --git a/chrome/common/chrome_result_codes.h b/chrome/common/chrome_result_codes.h index 8d9077a..045edd9 100644 --- a/chrome/common/chrome_result_codes.h +++ b/chrome/common/chrome_result_codes.h @@ -72,6 +72,10 @@ enum ResultCode { // running browser. RESULT_CODE_NORMAL_EXIT_PROCESS_NOTIFIED, + // Failed to install an item from the webstore when the kInstallFromWebstore + // command line flag was present. + RESULT_CODE_INSTALL_FROM_WEBSTORE_ERROR, + // Last return code (keep this last). RESULT_CODE_CHROME_LAST_CODE, }; diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc index b638862..9962f3d 100644 --- a/chrome/common/chrome_switches.cc +++ b/chrome/common/chrome_switches.cc @@ -91,6 +91,9 @@ const char kAppsGalleryURL[] = "apps-gallery-url"; // The update url used by gallery/webstore extensions. const char kAppsGalleryUpdateURL[] = "apps-gallery-update-url"; +// TODO(asargent) - remove this flag and change uses of it to instead use +// kInstallFromWebstore. +// // Specifies the URL of an application manifest to retrieve. The user will be // prompted for consent and the application retrieved/installed if consented. const char kAppsInstallFromManifestURL[] = "apps-install-from-manifest-url"; @@ -793,6 +796,10 @@ const char kImportFromFile[] = "import-from-file"; // Causes the browser to launch directly in incognito mode. const char kIncognito[] = "incognito"; +// Causes Chrome to attempt to get metadata from the webstore for the +// app/extension ID given, and then prompt the user to download and install it. +const char kInstallFromWebstore[] = "install-from-webstore"; + // URL to use for instant. If specified this overrides the url from the // TemplateURL. const char kInstantURL[] = "instant-url"; diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h index a126885..67dc75b 100644 --- a/chrome/common/chrome_switches.h +++ b/chrome/common/chrome_switches.h @@ -214,6 +214,7 @@ extern const char kIgnoreGpuBlacklist[]; extern const char kImport[]; extern const char kImportFromFile[]; extern const char kIncognito[]; +extern const char kInstallFromWebstore[]; extern const char kInstantURL[]; extern const char kKeepAliveForTest[]; extern const char kKioskMode[]; |