diff options
17 files changed, 414 insertions, 61 deletions
diff --git a/chrome/browser/automation/automation_provider_observers.cc b/chrome/browser/automation/automation_provider_observers.cc index b7173cd..d9a998e 100644 --- a/chrome/browser/automation/automation_provider_observers.cc +++ b/chrome/browser/automation/automation_provider_observers.cc @@ -571,6 +571,8 @@ ExtensionReadyNotificationObserver::ExtensionReadyNotificationObserver( NotificationService::AllSources()); registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED, NotificationService::AllSources()); + registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOAD_ERROR, + NotificationService::AllSources()); registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR, NotificationService::AllSources()); registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UPDATE_DISABLED, @@ -607,6 +609,7 @@ void ExtensionReadyNotificationObserver::Observe( return; break; case chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR: + case chrome::NOTIFICATION_EXTENSION_LOAD_ERROR: case chrome::NOTIFICATION_EXTENSION_UPDATE_DISABLED: break; default: diff --git a/chrome/browser/download/download_crx_util.cc b/chrome/browser/download/download_crx_util.cc index 1d5b4a4..f27391b 100644 --- a/chrome/browser/download/download_crx_util.cc +++ b/chrome/browser/download/download_crx_util.cc @@ -72,8 +72,10 @@ scoped_refptr<CrxInstaller> OpenChromeExtension( download_item.GetURL(), download_item.referrer_url()); installer->set_original_mime_type(download_item.original_mime_type()); installer->set_apps_require_extension_mime_type(true); - installer->set_original_url(download_item.GetURL()); + installer->set_download_url(download_item.GetURL()); installer->set_is_gallery_install(is_gallery_download); + if (is_gallery_download) + installer->set_original_download_url(download_item.original_url()); installer->set_allow_silent_install(is_gallery_download); installer->set_install_cause(extension_misc::INSTALL_CAUSE_USER_DOWNLOAD); installer->InstallCrx(download_item.full_path()); diff --git a/chrome/browser/extensions/crx_installer.cc b/chrome/browser/extensions/crx_installer.cc index 9eced99..e73f4f1 100644 --- a/chrome/browser/extensions/crx_installer.cc +++ b/chrome/browser/extensions/crx_installer.cc @@ -125,6 +125,7 @@ CrxInstaller::CrxInstaller(base::WeakPtr<ExtensionService> frontend_weak, create_app_shortcut_(false), page_index_(-1), frontend_weak_(frontend_weak), + profile_(frontend_weak->profile()), client_(client), apps_require_extension_mime_type_(false), allow_silent_install_(false), @@ -173,11 +174,11 @@ void CrxInstaller::InstallCrx(const FilePath& source_file) { } void CrxInstaller::InstallUserScript(const FilePath& source_file, - const GURL& original_url) { - DCHECK(!original_url.is_empty()); + const GURL& download_url) { + DCHECK(!download_url.is_empty()); source_file_ = source_file; - original_url_ = original_url; + download_url_ = download_url; if (!BrowserThread::PostTask( BrowserThread::FILE, FROM_HERE, @@ -189,7 +190,7 @@ void CrxInstaller::InstallUserScript(const FilePath& source_file, void CrxInstaller::ConvertUserScriptOnFileThread() { std::string error; scoped_refptr<Extension> extension = - ConvertUserScriptToExtension(source_file_, original_url_, &error); + ConvertUserScriptToExtension(source_file_, download_url_, &error); if (!extension) { ReportFailureFromFileThread(error); return; @@ -261,7 +262,7 @@ bool CrxInstaller::AllowInstall(const Extension* extension, // will be set. In this case, check that it was served with the // right mime type. Make an exception for file URLs, which come // from the users computer and have no headers. - if (!original_url_.SchemeIsFile() && + if (!download_url_.SchemeIsFile() && apps_require_extension_mime_type_ && original_mime_type_ != Extension::kMimeType) { *error = base::StringPrintf( @@ -288,7 +289,7 @@ bool CrxInstaller::AllowInstall(const Extension* extension, // host (or a subdomain of the host) the download happened from. There's // no way for us to verify that the app controls any other hosts. URLPattern pattern(UserScript::kValidUserScriptSchemes); - pattern.SetHost(original_url_.host()); + pattern.SetHost(download_url_.host()); pattern.SetMatchSubdomains(true); URLPatternSet patterns = extension_->web_extent(); diff --git a/chrome/browser/extensions/crx_installer.h b/chrome/browser/extensions/crx_installer.h index 23ea41a..ce6bcfb 100644 --- a/chrome/browser/extensions/crx_installer.h +++ b/chrome/browser/extensions/crx_installer.h @@ -102,7 +102,7 @@ class CrxInstaller // Convert the specified user script into an extension and install it. void InstallUserScript(const FilePath& source_file, - const GURL& original_url); + const GURL& download_url); // Convert the specified web app into an extension and install it. void InstallWebApp(const WebApplicationInfo& web_app); @@ -111,8 +111,8 @@ class CrxInstaller virtual void InstallUIProceed() OVERRIDE; virtual void InstallUIAbort(bool user_initiated) OVERRIDE; - const GURL& original_url() const { return original_url_; } - void set_original_url(const GURL& val) { original_url_ = val; } + const GURL& download_url() const { return download_url_; } + void set_download_url(const GURL& val) { download_url_ = val; } Extension::Location install_source() const { return install_source_; } void set_install_source(Extension::Location source) { @@ -135,6 +135,14 @@ class CrxInstaller bool is_gallery_install() const { return is_gallery_install_; } void set_is_gallery_install(bool val) { is_gallery_install_ = val; } + // The original download URL should be set when the WebstoreInstaller is + // tracking the installation. The WebstoreInstaller uses this URL to match + // failure notifications to the extension. + const GURL& original_download_url() const { return original_download_url_; } + void set_original_download_url(const GURL& url) { + original_download_url_ = url; + } + // If |apps_require_extension_mime_type_| is set to true, be sure to set // |original_mime_type_| as well. void set_apps_require_extension_mime_type( @@ -158,6 +166,8 @@ class CrxInstaller page_index_ = page_index; } + Profile* profile() { return profile_; } + private: friend class ExtensionUpdaterTest; @@ -203,7 +213,7 @@ class CrxInstaller FilePath source_file_; // The URL the file was downloaded from. - GURL original_url_; + GURL download_url_; // The directory extensions are installed to. FilePath install_directory_; @@ -235,6 +245,9 @@ class CrxInstaller // Whether the install originated from the gallery. bool is_gallery_install_; + // The download URL, before redirects, if this is a gallery install. + GURL original_download_url_; + // Whether to create an app shortcut after successful installation. This is // set based on the user's selection in the UI and can only ever be true for // apps. @@ -265,6 +278,9 @@ class CrxInstaller // The frontend we will report results back to. base::WeakPtr<ExtensionService> frontend_weak_; + // The Profile where the extension is being installed in. + Profile* profile_; + // The client we will work with to do the installation. This can be NULL, in // which case the install is silent. // NOTE: we may be deleted on the file thread. To ensure the UI is deleted on diff --git a/chrome/browser/extensions/extension_browsertest.cc b/chrome/browser/extensions/extension_browsertest.cc index 8fa5035..0121824 100644 --- a/chrome/browser/extensions/extension_browsertest.cc +++ b/chrome/browser/extensions/extension_browsertest.cc @@ -247,6 +247,8 @@ bool ExtensionBrowserTest::InstallOrUpdateExtension(const std::string& id, NotificationService::AllSources()); registrar.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR, NotificationService::AllSources()); + registrar.Add(this, chrome::NOTIFICATION_EXTENSION_LOAD_ERROR, + NotificationService::AllSources()); ExtensionInstallUI* install_ui = NULL; if (ui_type == INSTALL_UI_TYPE_CANCEL) @@ -434,6 +436,11 @@ void ExtensionBrowserTest::Observe(int type, MessageLoopForUI::current()->Quit(); break; + case chrome::NOTIFICATION_EXTENSION_LOAD_ERROR: + VLOG(1) << "Got EXTENSION_LOAD_ERROR notification."; + MessageLoopForUI::current()->Quit(); + break; + case chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR: VLOG(1) << "Got EXTENSION_INSTALL_ERROR notification."; MessageLoopForUI::current()->Quit(); diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc index 6fe755b..ceaa22e 100644 --- a/chrome/browser/extensions/extension_service.cc +++ b/chrome/browser/extensions/extension_service.cc @@ -378,8 +378,7 @@ void ExtensionServiceBackend::ReportExtensionLoadError( CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); if (frontend_.get()) frontend_->ReportExtensionLoadError( - extension_path, error, chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR, - true /* alert_on_error */); + extension_path, error, true /* alert_on_error */); } void ExtensionServiceBackend::OnLoadSingleExtension( @@ -606,6 +605,7 @@ ExtensionService::ExtensionService(Profile* profile, app_notification_manager_(new AppNotificationManager(profile)), permissions_manager_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), apps_promo_(profile->GetPrefs()), + webstore_installer_(profile), event_routers_initialized_(false), extension_warnings_(profile) { CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); @@ -819,7 +819,7 @@ bool ExtensionService::UpdateExtension( if (extension && extension->from_webstore()) installer->set_is_gallery_install(true); installer->set_delete_source(true); - installer->set_original_url(download_url); + installer->set_download_url(download_url); installer->set_install_cause(extension_misc::INSTALL_CAUSE_UPDATE); installer->InstallCrx(extension_path); @@ -1168,11 +1168,7 @@ void ExtensionService::LoadExtensionFromCommandLine( &error)); if (!extension) { - ReportExtensionLoadError( - extension_path, - error, - chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR, - true); + ReportExtensionLoadError(extension_path, error, true); return; } @@ -1470,10 +1466,7 @@ void ExtensionService::LoadInstalledExtension(const ExtensionInfo& info, } if (!extension) { - ReportExtensionLoadError(info.extension_path, - error, - chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR, - false); + ReportExtensionLoadError(info.extension_path, error, false); return; } @@ -2796,10 +2789,9 @@ void ExtensionService::OnExternalExtensionFileFound( void ExtensionService::ReportExtensionLoadError( const FilePath& extension_path, const std::string &error, - int type, bool be_noisy) { NotificationService* service = NotificationService::current(); - service->Notify(type, + service->Notify(chrome::NOTIFICATION_EXTENSION_LOAD_ERROR, Source<Profile>(profile_), Details<const std::string>(&error)); diff --git a/chrome/browser/extensions/extension_service.h b/chrome/browser/extensions/extension_service.h index 9f89a35..d59644a 100644 --- a/chrome/browser/extensions/extension_service.h +++ b/chrome/browser/extensions/extension_service.h @@ -35,6 +35,7 @@ #include "chrome/browser/extensions/external_extension_provider_interface.h" #include "chrome/browser/extensions/pending_extension_manager.h" #include "chrome/browser/extensions/sandboxed_extension_unpacker.h" +#include "chrome/browser/extensions/webstore_installer.h" #include "chrome/browser/prefs/pref_change_registrar.h" #include "chrome/browser/sync/api/sync_change.h" #include "chrome/browser/sync/api/syncable_service.h" @@ -206,6 +207,7 @@ class ExtensionService const FilePath& install_directory() const { return install_directory_; } AppsPromo* apps_promo() { return &apps_promo_; } + WebstoreInstaller* webstore_installer() { return &webstore_installer_; } // Whether this extension can run in an incognito window. virtual bool IsIncognitoEnabled(const std::string& extension_id) const; @@ -484,7 +486,6 @@ class ExtensionService // This method is public because ExtensionServiceBackend can post to here. void ReportExtensionLoadError(const FilePath& extension_path, const std::string& error, - int type, bool be_noisy); // ExtensionHost of background page calls this method right after its render @@ -787,6 +788,9 @@ class ExtensionService // Manages the promotion of the web store. AppsPromo apps_promo_; + // Coordinates the extension download and install process from the gallery. + WebstoreInstaller webstore_installer_; + // Flag to make sure event routers are only initialized once. bool event_routers_initialized_; diff --git a/chrome/browser/extensions/extension_webstore_private_api.cc b/chrome/browser/extensions/extension_webstore_private_api.cc index fc86aaa..151b044 100644 --- a/chrome/browser/extensions/extension_webstore_private_api.cc +++ b/chrome/browser/extensions/extension_webstore_private_api.cc @@ -95,6 +95,8 @@ DictionaryValue* CreateLoginResult(Profile* profile) { return dictionary; } +WebstoreInstaller::Delegate* test_webstore_installer_delegate = NULL; + } // namespace // static @@ -104,6 +106,12 @@ void WebstorePrivateApi::SetTestingProfileSyncService( } // static +void WebstorePrivateApi::SetWebstoreInstallerDelegateForTesting( + WebstoreInstaller::Delegate* delegate) { + test_webstore_installer_delegate = delegate; +} + +// static void BeginInstallFunction::SetIgnoreUserGestureForTests(bool ignore) { ignore_user_gesture_for_tests = ignore; } @@ -361,18 +369,13 @@ bool CompleteInstallFunction::RunImpl() { return false; } - GURL install_url(extension_urls::GetWebstoreInstallUrl( - id, g_browser_process->GetApplicationLocale())); - - // The download url for the given |id| is now contained in |url|. We - // navigate the current (calling) tab to this url which will result in a - // download starting. Once completed it will go through the normal extension - // install flow. The above call to SetWhitelistedInstallId will bypass the - // normal permissions install dialog. - NavigationController& controller = - dispatcher()->delegate()->GetAssociatedTabContents()->controller(); - controller.LoadURL(install_url, source_url(), content::PAGE_TRANSITION_LINK, - std::string()); + // The extension will install through the normal extension install flow, but + // the above call to SetWhitelistedInstallId will bypass the normal + // permissions install dialog. + WebstoreInstaller* installer = + profile()->GetExtensionService()->webstore_installer(); + installer->InstallExtension( + id, test_webstore_installer_delegate, WebstoreInstaller::FLAG_NONE); return true; } diff --git a/chrome/browser/extensions/extension_webstore_private_api.h b/chrome/browser/extensions/extension_webstore_private_api.h index 8edc6a2..23d8914b 100644 --- a/chrome/browser/extensions/extension_webstore_private_api.h +++ b/chrome/browser/extensions/extension_webstore_private_api.h @@ -11,6 +11,7 @@ #include "chrome/browser/extensions/extension_function.h" #include "chrome/browser/extensions/extension_install_ui.h" #include "chrome/browser/extensions/webstore_install_helper.h" +#include "chrome/browser/extensions/webstore_installer.h" #include "chrome/common/net/gaia/google_service_auth_error.h" #include "content/common/notification_observer.h" #include "content/common/notification_registrar.h" @@ -22,6 +23,10 @@ class WebstorePrivateApi { // Allows you to set the ProfileSyncService the function will use for // testing purposes. static void SetTestingProfileSyncService(ProfileSyncService* service); + + // Allows you to override the WebstoreInstaller delegate for testing. + static void SetWebstoreInstallerDelegateForTesting( + WebstoreInstaller::Delegate* delegate); }; // TODO(asargent): this is being deprecated in favor of diff --git a/chrome/browser/extensions/extension_webstore_private_apitest.cc b/chrome/browser/extensions/extension_webstore_private_apitest.cc index 90b76b2..8e0fac2 100644 --- a/chrome/browser/extensions/extension_webstore_private_apitest.cc +++ b/chrome/browser/extensions/extension_webstore_private_apitest.cc @@ -8,6 +8,7 @@ #include "chrome/browser/extensions/extension_install_ui.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_webstore_private_api.h" +#include "chrome/browser/extensions/webstore_installer.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/common/chrome_notification_types.h" @@ -18,6 +19,53 @@ #include "content/common/notification_service.h" #include "net/base/mock_host_resolver.h" +namespace { + +class WebstoreInstallListener : public WebstoreInstaller::Delegate { + public: + WebstoreInstallListener() + : received_failure_(false), received_success_(false) {} + + void OnExtensionInstallSuccess(const std::string& id) OVERRIDE; + void OnExtensionInstallFailure(const std::string& id, + const std::string& error) OVERRIDE; + void Wait(); + + bool received_failure() const { return received_failure_; } + bool received_success() const { return received_success_; } + const std::string& id() const { return id_; } + const std::string& error() const { return error_; } + + private: + bool received_failure_; + bool received_success_; + std::string id_; + std::string error_; +}; + +void WebstoreInstallListener::OnExtensionInstallSuccess(const std::string& id) { + received_success_ = true; + id_ = id; + MessageLoopForUI::current()->Quit(); +} + +void WebstoreInstallListener::OnExtensionInstallFailure( + const std::string& id, const std::string& error) { + received_failure_ = true; + id_ = id; + error_ = error; + MessageLoopForUI::current()->Quit(); +} + +void WebstoreInstallListener::Wait() { + if (received_success_ || received_failure_) + return; + + ui_test_utils::RunMessageLoop(); +} + +} // namespace + // A base class for tests below. class ExtensionWebstorePrivateApiTest : public ExtensionApiTest { public: @@ -92,27 +140,34 @@ IN_PROC_BROWSER_TEST_F(ExtensionWebstorePrivateApiTest, InstallNoGesture) { IN_PROC_BROWSER_TEST_F(ExtensionWebstorePrivateApiTest, IncorrectManifest1) { - ui_test_utils::WindowedNotificationObserver observer( - chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR, - NotificationService::AllSources()); + WebstoreInstallListener listener; + WebstorePrivateApi::SetWebstoreInstallerDelegateForTesting(&listener); ASSERT_TRUE(RunInstallTest("incorrect_manifest1.html", "extension.crx")); - observer.Wait(); + listener.Wait(); + ASSERT_TRUE(listener.received_failure()); + ASSERT_EQ("Manifest file is invalid.", listener.error()); } IN_PROC_BROWSER_TEST_F(ExtensionWebstorePrivateApiTest, IncorrectManifest2) { - ui_test_utils::WindowedNotificationObserver observer( - chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR, - NotificationService::AllSources()); + WebstoreInstallListener listener; + WebstorePrivateApi::SetWebstoreInstallerDelegateForTesting(&listener); ASSERT_TRUE(RunInstallTest("incorrect_manifest2.html", "extension.crx")); - observer.Wait(); + listener.Wait(); + EXPECT_TRUE(listener.received_failure()); + ASSERT_EQ("Manifest file is invalid.", listener.error()); } // Tests that we can request an app installed bubble (instead of the default // UI when an app is installed). IN_PROC_BROWSER_TEST_F(ExtensionWebstorePrivateApiTest, AppInstallBubble) { + WebstoreInstallListener listener; + WebstorePrivateApi::SetWebstoreInstallerDelegateForTesting(&listener); ASSERT_TRUE(RunInstallTest("app_install_bubble.html", "app.crx")); + listener.Wait(); + ASSERT_TRUE(listener.received_success()); + ASSERT_EQ("iladmdjkfniedhfhcfoefgojhgaiaccc", listener.id()); } // Tests using the iconUrl parameter to the install function. diff --git a/chrome/browser/extensions/webstore_inline_install_browsertest.cc b/chrome/browser/extensions/webstore_inline_install_browsertest.cc index f6af718..91b9ded 100644 --- a/chrome/browser/extensions/webstore_inline_install_browsertest.cc +++ b/chrome/browser/extensions/webstore_inline_install_browsertest.cc @@ -57,8 +57,8 @@ class WebstoreInlineInstallTest : public InProcessBrowserTest { protected: GURL GenerateTestServerUrl(const std::string& domain, const std::string& page_filename) { - GURL page_url = test_server()->GetURL( - "files/extensions/api_test/webstore_inline_install/" + page_filename); + GURL page_url = test_server()->GetURL( + "files/extensions/api_test/webstore_inline_install/" + page_filename); GURL::Replacements replace_host; replace_host.SetHostStr(domain); diff --git a/chrome/browser/extensions/webstore_inline_installer.cc b/chrome/browser/extensions/webstore_inline_installer.cc index f5f9835..97a5aad 100644 --- a/chrome/browser/extensions/webstore_inline_installer.cc +++ b/chrome/browser/extensions/webstore_inline_installer.cc @@ -11,6 +11,8 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/extensions/crx_installer.h" #include "chrome/browser/extensions/extension_install_dialog.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/webstore_installer.h" #include "chrome/browser/profiles/profile.h" #include "chrome/common/chrome_utility_messages.h" #include "chrome/common/extensions/extension.h" @@ -375,22 +377,17 @@ void WebstoreInlineInstaller::InstallUIProceed() { entry->use_app_installed_bubble = true; CrxInstaller::SetWhitelistEntry(id_, entry); - GURL install_url(extension_urls::GetWebstoreInstallUrl( - id_, g_browser_process->GetApplicationLocale())); + Profile* profile = Profile::FromBrowserContext( + tab_contents()->browser_context()); - NavigationController& controller = tab_contents()->controller(); - // TODO(mihaip): we pretend like the referrer is the gallery in order to pass - // the checks in ExtensionService::IsDownloadFromGallery. We should instead - // pass the real referrer, track that this is an inline install in the - // whitelist entry and look that up when checking that this is a valid - // download. - GURL referrer(extension_urls::GetWebstoreItemDetailURLPrefix() + id_); - controller.LoadURL(install_url, referrer, content::PAGE_TRANSITION_LINK, - std::string()); + WebstoreInstaller* installer = + profile->GetExtensionService()->webstore_installer(); + installer->InstallExtension(id_, NULL, + WebstoreInstaller::FLAG_OVERRIDE_REFERRER); // TODO(mihaip): the success message should happen later, when the extension - // is actually downloaded and installed (when NOTIFICATION_EXTENSION_INSTALLED - // or NOTIFICATION_EXTENSION_INSTALL_ERROR fire). + // is actually downloaded and installed (by using the callbacks on + // ExtensionInstaller::Delegate). CompleteInstall(""); } diff --git a/chrome/browser/extensions/webstore_installer.cc b/chrome/browser/extensions/webstore_installer.cc new file mode 100644 index 0000000..13f8e73 --- /dev/null +++ b/chrome/browser/extensions/webstore_installer.cc @@ -0,0 +1,176 @@ +// Copyright (c) 2011 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/extensions/webstore_installer.h" + +#include "chrome/browser/browser_process.h" +#include "chrome/browser/extensions/crx_installer.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/tabs/tab_strip_model.h" +#include "chrome/browser/ui/browser_list.h" +#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" +#include "chrome/common/chrome_notification_types.h" +#include "chrome/common/extensions/extension.h" +#include "content/browser/tab_contents/navigation_controller.h" +#include "content/common/notification_details.h" +#include "content/common/notification_source.h" +#include "googleurl/src/gurl.h" + +namespace { + +const char kInvalidIdError[] = "Invalid id"; +const char kNoBrowserError[] = "No browser found"; + +} // namespace + + +WebstoreInstaller::WebstoreInstaller(Profile* profile) + : profile_(profile) { + registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALLED, + Source<Profile>(profile)); + registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR, + Source<CrxInstaller>(NULL)); +} + +WebstoreInstaller::~WebstoreInstaller() {} + +struct WebstoreInstaller::PendingInstall { + PendingInstall() : delegate(NULL) {} + PendingInstall(const std::string& id, const GURL& url, Delegate* delegate) + : id(id), download_url(url), delegate(delegate) {} + ~PendingInstall() {} + + // The id of the extension. + std::string id; + + // The gallery download URL for the extension. + GURL download_url; + + // The delegate for this install. + Delegate* delegate; +}; + +void WebstoreInstaller::InstallExtension( + const std::string& id, Delegate* delegate, int flags) { + if (!Extension::IdIsValid(id)) { + ReportFailure(id, kInvalidIdError); + return; + } + + Browser* browser = BrowserList::GetLastActiveWithProfile(profile_); + if (!browser) { + ReportFailure(id, kNoBrowserError); + return; + } + + PendingInstall pending_install = CreatePendingInstall(id, delegate); + + // The download url for the given |id| is now contained in + // |pending_install.download_url|. We navigate the current tab to this url + // which will result in a download starting. Once completed it will go through + // the normal extension install flow. + NavigationController& controller = + browser->tabstrip_model()->GetActiveTabContents()->controller(); + + // TODO(mihaip, jstritar): For inline installs, we pretend like the referrer + // is the gallery, even though this could be an inline install, in order to + // pass the checks in ExtensionService::IsDownloadFromGallery. We should + // instead pass the real referrer, track if this is an inline install in the + // whitelist entry and look that up when checking that this is a valid + // download. + GURL referrer = controller.GetActiveEntry()->url(); + if (flags & FLAG_OVERRIDE_REFERRER) + referrer = GURL(extension_urls::GetWebstoreItemDetailURLPrefix() + id); + + controller.LoadURL(pending_install.download_url, + referrer, + content::PAGE_TRANSITION_LINK, + std::string()); +} + +void WebstoreInstaller::Observe(int type, + const NotificationSource& source, + const NotificationDetails& details) { + switch (type) { + case chrome::NOTIFICATION_EXTENSION_INSTALLED: { + CHECK(profile_->IsSameProfile(Source<Profile>(source).ptr())); + const Extension* extension = Details<const Extension>(details).ptr(); + ReportSuccess(extension->id()); + break; + } + + case chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR: { + CrxInstaller* crx_installer = Source<CrxInstaller>(source).ptr(); + CHECK(crx_installer); + if (!profile_->IsSameProfile(crx_installer->profile())) + return; + + std::string id = GetPendingInstallId( + crx_installer->original_download_url()); + const std::string* error = Details<const std::string>(details).ptr(); + if (!id.empty()) + ReportFailure(id, *error); + break; + } + + default: + NOTREACHED(); + } +} + +bool WebstoreInstaller::ClearPendingInstall( + const std::string& id, PendingInstall* install) { + for (size_t i = 0; i < pending_installs_.size(); ++i) { + if (pending_installs_[i].id == id) { + *install = pending_installs_[i]; + pending_installs_.erase(pending_installs_.begin() + i); + return true; + } + } + return false; +} + +const WebstoreInstaller::PendingInstall& + WebstoreInstaller::CreatePendingInstall( + const std::string& id, Delegate* delegate) { + GURL install_url = extension_urls::GetWebstoreInstallUrl( + id, g_browser_process->GetApplicationLocale()); + + PendingInstall pending_install(id, install_url, delegate); + pending_installs_.push_back(pending_install); + + return *(pending_installs_.end() - 1); +} + + +std::string WebstoreInstaller::GetPendingInstallId(const GURL& url) { + if (url.is_empty()) + return std::string(); + + for (size_t i = 0; i < pending_installs_.size(); ++i) { + if (pending_installs_[i].download_url == url) + return pending_installs_[i].id; + } + + return std::string(); +} + +void WebstoreInstaller::ReportFailure( + const std::string& id, const std::string& error) { + PendingInstall install; + if (!ClearPendingInstall(id, &install)) + return; + + if (install.delegate) + install.delegate->OnExtensionInstallFailure(id, error); +} + +void WebstoreInstaller::ReportSuccess(const std::string& id) { + PendingInstall install; + if (!ClearPendingInstall(id, &install)) + return; + + if (install.delegate) + install.delegate->OnExtensionInstallSuccess(id); +} diff --git a/chrome/browser/extensions/webstore_installer.h b/chrome/browser/extensions/webstore_installer.h new file mode 100644 index 0000000..c17d4c6 --- /dev/null +++ b/chrome/browser/extensions/webstore_installer.h @@ -0,0 +1,78 @@ +// Copyright (c) 2011 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_EXTENSIONS_WEBSTORE_INSTALLER_H_ +#define CHROME_BROWSER_EXTENSIONS_WEBSTORE_INSTALLER_H_ +#pragma once + +#include <string> +#include <vector> + +#include "base/compiler_specific.h" +#include "content/common/notification_observer.h" +#include "content/common/notification_registrar.h" + +class GURL; +class Profile; + +// Downloads and installs extensions from the web store. +class WebstoreInstaller : public NotificationObserver { + public: + enum Flag { + FLAG_NONE = 0, + + // Sets the download's referral to the extension's page in the gallery. + FLAG_OVERRIDE_REFERRER = 1 << 0 + }; + + class Delegate { + public: + virtual void OnExtensionInstallSuccess(const std::string& id) = 0; + virtual void OnExtensionInstallFailure(const std::string& id, + const std::string& error) = 0; + }; + + explicit WebstoreInstaller(Profile* profile); + virtual ~WebstoreInstaller(); + + // Download and install the extension with the given |id| from the Chrome + // Web Store. If |delegate| is not NULL, it will be notified when the + // install succeeds or fails. + // Note: the delegate should stay alive until being called back. + void InstallExtension(const std::string& id, Delegate* delegate, int flags); + + // NotificationObserver + virtual void Observe(int type, + const NotificationSource& source, + const NotificationDetails& details) OVERRIDE; + + private: + struct PendingInstall; + + // Removes the PendingIntall data for the given extension, returning true and + // setting |install| if there is such data. + bool ClearPendingInstall(const std::string& id, PendingInstall* install); + + // Creates the PendingInstall for the specified extension. + const PendingInstall& CreatePendingInstall( + const std::string& id, Delegate* delegate); + + // Gets the extension id for the given gallery install |url|. + std::string GetPendingInstallId(const GURL& url); + + // Reports an install |error| to the delegate for the given extension if this + // managed its installation. This also removes the associated PendingInstall. + void ReportFailure(const std::string& id, const std::string& error); + + // Reports a successful install to the delegate for the given extension if + // this managed its installation. This also removes the associated + // PendingInstall. + void ReportSuccess(const std::string& id); + + NotificationRegistrar registrar_; + Profile* profile_; + std::vector<PendingInstall> pending_installs_; +}; + +#endif // CHROME_BROWSER_EXTENSIONS_WEBSTORE_INSTALLER_H_ diff --git a/chrome/browser/ui/webui/ntp/app_launcher_handler.cc b/chrome/browser/ui/webui/ntp/app_launcher_handler.cc index f3c7a10..0524367 100644 --- a/chrome/browser/ui/webui/ntp/app_launcher_handler.cc +++ b/chrome/browser/ui/webui/ntp/app_launcher_handler.cc @@ -371,6 +371,12 @@ void AppLauncherHandler::Observe(int type, break; } case chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR: { + CrxInstaller* crx_installer = Source<CrxInstaller>(source).ptr(); + if (!Profile::FromWebUI(web_ui_)->IsSameProfile(crx_installer->profile())) + return; + // Fall Through. + } + case chrome::NOTIFICATION_EXTENSION_LOAD_ERROR: { attempted_bookmark_app_install_ = false; break; } @@ -536,6 +542,8 @@ void AppLauncherHandler::HandleGetApps(const ListValue* args) { registrar_.Add(this, chrome::NOTIFICATION_WEB_STORE_PROMO_LOADED, Source<Profile>(profile)); registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR, + Source<CrxInstaller>(NULL)); + registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOAD_ERROR, Source<Profile>(profile)); } diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 9ce4001..6190361 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -1233,6 +1233,8 @@ 'browser/extensions/webstore_inline_installer.h', 'browser/extensions/webstore_install_helper.cc', 'browser/extensions/webstore_install_helper.h', + 'browser/extensions/webstore_installer.cc', + 'browser/extensions/webstore_installer.h', 'browser/external_protocol/external_protocol_handler.cc', 'browser/external_protocol/external_protocol_handler.h', 'browser/external_protocol/external_protocol_observer.cc', diff --git a/chrome/common/chrome_notification_types.h b/chrome/common/chrome_notification_types.h index c6357b0..515c6ed 100644 --- a/chrome/common/chrome_notification_types.h +++ b/chrome/common/chrome_notification_types.h @@ -367,6 +367,10 @@ enum NotificationType { // the source is a Profile. NOTIFICATION_EXTENSION_LOADED, + // An error occured while attempting to load an extension. The details are a + // string with details about why the load failed. + NOTIFICATION_EXTENSION_LOAD_ERROR, + // Sent when attempting to load a new extension, but they are disabled. The // details are an Extension*, and the source is a Profile*. NOTIFICATION_EXTENSION_UPDATE_DISABLED, |