summaryrefslogtreecommitdiffstats
path: root/chrome/browser/extensions/webstore_inline_installer.cc
diff options
context:
space:
mode:
authormihaip@chromium.org <mihaip@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-08-29 22:14:37 +0000
committermihaip@chromium.org <mihaip@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-08-29 22:14:37 +0000
commit8915f3407209106b732b65dae883b75b35e6b086 (patch)
tree2c860479ee69dcbfa99158d43ca0ca40ee3ba420 /chrome/browser/extensions/webstore_inline_installer.cc
parent82c7bfb8ca07b27406154548434eca6dc111b097 (diff)
downloadchromium_src-8915f3407209106b732b65dae883b75b35e6b086.zip
chromium_src-8915f3407209106b732b65dae883b75b35e6b086.tar.gz
chromium_src-8915f3407209106b732b65dae883b75b35e6b086.tar.bz2
Add WebstoreInlineInstaller (downloads store data, shows the install UI, and starts the install).
The flow is: 1. Fetch store metadata as JSON (using URLFetcher) 2. Parse response in utility process (via SafeWebstoreResponseParser) 3. Parse manifest and get icon data using WebstoreInstallHelper 4. Show install UI 5. Whitelist extension ID for download and start download Still missing are a way of informing the page that the inline install succeeded or failed. Also removes ExtensionTabHelper::GetCustomFrameNativeWindow, since it wasn't actually overriding anything (the method was removed from ExtensionFunctionDispatcher::Delegate by r74835). R=asargent@chromium.org Review URL: http://codereview.chromium.org/7741037 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@98712 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/extensions/webstore_inline_installer.cc')
-rw-r--r--chrome/browser/extensions/webstore_inline_installer.cc293
1 files changed, 293 insertions, 0 deletions
diff --git a/chrome/browser/extensions/webstore_inline_installer.cc b/chrome/browser/extensions/webstore_inline_installer.cc
new file mode 100644
index 0000000..f13c94f
--- /dev/null
+++ b/chrome/browser/extensions/webstore_inline_installer.cc
@@ -0,0 +1,293 @@
+// 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_inline_installer.h"
+
+#include <vector>
+
+#include "base/string_util.h"
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/extensions/crx_installer.h"
+#include "chrome/browser/extensions/extension_install_dialog.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/chrome_utility_messages.h"
+#include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/extension_constants.h"
+#include "content/browser/tab_contents/tab_contents.h"
+#include "content/browser/utility_process_host.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/escape.h"
+#include "net/url_request/url_request_status.h"
+
+const char kManifestKey[] = "manifest";
+const char kIconUrlKey[] = "icon_url";
+const char kLocalizedNameKey[] = "localized_name";
+
+const char kInvalidWebstoreItemId[] = "Invalid webstore item ID";
+const char kWebstoreRequestError[] = "Could not fetch data from webstore";
+const char kInvalidWebstoreResponseError[] = "Invalid webstore reponse";
+const char kInvalidManifestError[] = "Invalid manifest";
+const char kUserCancelledError[] = "User cancelled install";
+
+class SafeWebstoreResponseParser : public UtilityProcessHost::Client {
+ public:
+ SafeWebstoreResponseParser(WebstoreInlineInstaller *client,
+ const std::string& webstore_data)
+ : client_(client),
+ webstore_data_(webstore_data),
+ utility_host_(NULL) {}
+
+ void Start() {
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ NewRunnableMethod(this,
+ &SafeWebstoreResponseParser::StartWorkOnIOThread));
+ }
+
+ void StartWorkOnIOThread() {
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ utility_host_ = new UtilityProcessHost(this, BrowserThread::IO);
+ utility_host_->Send(new ChromeUtilityMsg_ParseJSON(webstore_data_));
+ }
+
+ // Implementing pieces of the UtilityProcessHost::Client interface.
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(SafeWebstoreResponseParser, message)
+ IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ParseJSON_Succeeded,
+ OnJSONParseSucceeded)
+ IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ParseJSON_Failed,
+ OnJSONParseFailed)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+ }
+
+ void OnJSONParseSucceeded(const ListValue& wrapper) {
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ Value* value = NULL;
+ CHECK(wrapper.Get(0, &value));
+ if (value->IsType(Value::TYPE_DICTIONARY)) {
+ parsed_webstore_data_.reset(
+ static_cast<DictionaryValue*>(value)->DeepCopy());
+ } else {
+ error_ = kInvalidWebstoreResponseError;
+ }
+
+ ReportResults();
+ }
+
+ virtual void OnJSONParseFailed(const std::string& error_message) {
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ error_ = error_message;
+ ReportResults();
+ }
+
+ void ReportResults() {
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ // The utility_host_ will take care of deleting itself after this call.
+ utility_host_ = NULL;
+
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ NewRunnableMethod(this,
+ &SafeWebstoreResponseParser::ReportResultOnUIThread));
+ }
+
+ void ReportResultOnUIThread() {
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ if (error_.empty() && parsed_webstore_data_.get()) {
+ client_->OnWebstoreResponseParseSuccess(parsed_webstore_data_.release());
+ } else {
+ client_->OnWebstoreResponseParseFailure(error_);
+ }
+ }
+
+ private:
+ virtual ~SafeWebstoreResponseParser() {}
+
+ WebstoreInlineInstaller* client_;
+
+ std::string webstore_data_;
+
+ UtilityProcessHost* utility_host_;
+
+ std::string error_;
+ scoped_ptr<DictionaryValue> parsed_webstore_data_;
+};
+
+WebstoreInlineInstaller::WebstoreInlineInstaller(TabContents* tab_contents,
+ std::string webstore_item_id,
+ Delegate* delegate)
+ : tab_contents_(tab_contents),
+ id_(webstore_item_id),
+ delegate_(delegate) {}
+
+WebstoreInlineInstaller::~WebstoreInlineInstaller() {
+}
+
+void WebstoreInlineInstaller::BeginInstall() {
+ AddRef(); // Balanced in CompleteInstall.
+
+ if (!Extension::IdIsValid(id_)) {
+ CompleteInstall(kInvalidWebstoreItemId);
+ return;
+ }
+
+ GURL webstore_data_url(extension_urls::GetWebstoreItemJsonDataURL(id_));
+
+ webstore_data_url_fetcher_.reset(
+ new URLFetcher(webstore_data_url, URLFetcher::GET, this));
+ Profile* profile = Profile::FromBrowserContext(
+ tab_contents_->browser_context());
+ webstore_data_url_fetcher_->set_request_context(
+ profile->GetRequestContext());
+ webstore_data_url_fetcher_->Start();
+}
+
+void WebstoreInlineInstaller::OnURLFetchComplete(const URLFetcher* source) {
+ CHECK_EQ(webstore_data_url_fetcher_.get(), source);
+
+ if (!webstore_data_url_fetcher_->status().is_success() ||
+ webstore_data_url_fetcher_->response_code() != 200) {
+ CompleteInstall(kWebstoreRequestError);
+ return;
+ }
+
+ std::string webstore_json_data;
+ webstore_data_url_fetcher_->GetResponseAsString(&webstore_json_data);
+ webstore_data_url_fetcher_.reset();
+
+ scoped_refptr<SafeWebstoreResponseParser> parser =
+ new SafeWebstoreResponseParser(this, webstore_json_data);
+ // The parser will call us back via OnWebstoreResponseParseSucces or
+ // OnWebstoreResponseParseFailure.
+ parser->Start();
+}
+
+void WebstoreInlineInstaller::OnWebstoreResponseParseSuccess(
+ DictionaryValue* webstore_data) {
+ webstore_data_.reset(webstore_data);
+
+ std::string manifest;
+ if (!webstore_data->GetString(kManifestKey, &manifest)) {
+ CompleteInstall(kInvalidWebstoreResponseError);
+ return;
+ }
+
+ // Localized name is optional.
+ if (webstore_data->HasKey(kLocalizedNameKey) &&
+ !webstore_data->GetString(kLocalizedNameKey, &localized_name_)) {
+ CompleteInstall(kInvalidWebstoreResponseError);
+ return;
+ }
+
+ // Icon URL is optional.
+ GURL icon_url;
+ if (webstore_data->HasKey(kIconUrlKey)) {
+ std::string icon_url_string;
+ if (!webstore_data->GetString(kIconUrlKey, &icon_url_string)) {
+ CompleteInstall(kInvalidWebstoreResponseError);
+ return;
+ }
+ icon_url = GURL(extension_urls::GetWebstoreLaunchURL()).Resolve(
+ icon_url_string);
+ if (!icon_url.is_valid()) {
+ CompleteInstall(kInvalidWebstoreResponseError);
+ return;
+ }
+ }
+
+ scoped_refptr<WebstoreInstallHelper> helper = new WebstoreInstallHelper(
+ this,
+ manifest,
+ "", // We don't have any icon data.
+ icon_url,
+ Profile::FromBrowserContext(tab_contents_->browser_context())->
+ GetRequestContext());
+ // The helper will call us back via OnWebstoreParseSucces or
+ // OnWebstoreParseFailure.
+ helper->Start();
+}
+
+void WebstoreInlineInstaller::OnWebstoreResponseParseFailure(
+ const std::string& error) {
+ CompleteInstall(error);
+}
+
+void WebstoreInlineInstaller::OnWebstoreParseSuccess(
+ const SkBitmap& icon,
+ base::DictionaryValue* manifest) {
+ manifest_.reset(manifest);
+ icon_ = icon;
+
+ Profile* profile = Profile::FromBrowserContext(
+ tab_contents_->browser_context());
+ scoped_refptr<Extension> dummy_extension;
+ ShowExtensionInstallDialogForManifest(profile,
+ this,
+ manifest,
+ id_,
+ localized_name_,
+ &icon_,
+ ExtensionInstallUI::INSTALL_PROMPT,
+ &dummy_extension);
+
+ if (!dummy_extension.get()) {
+ CompleteInstall(kInvalidManifestError);
+ return;
+ }
+
+ // Control flow finishes up in InstallUIProceed or InstallUIAbort.
+}
+
+void WebstoreInlineInstaller::OnWebstoreParseFailure(
+ InstallHelperResultCode result_code,
+ const std::string& error_message) {
+ CompleteInstall(error_message);
+}
+
+void WebstoreInlineInstaller::InstallUIProceed() {
+ CrxInstaller::WhitelistEntry* entry = new CrxInstaller::WhitelistEntry;
+
+ entry->parsed_manifest.reset(manifest_.get()->DeepCopy());
+ entry->localized_name = localized_name_;
+ entry->use_app_installed_bubble = true;
+ CrxInstaller::SetWhitelistEntry(id_, entry);
+
+ GURL install_url(extension_urls::GetWebstoreInstallUrl(
+ id_, g_browser_process->GetApplicationLocale()));
+
+ 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, PageTransition::LINK);
+
+ // 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).
+ CompleteInstall("");
+}
+
+void WebstoreInlineInstaller::InstallUIAbort(bool user_initiated) {
+ CompleteInstall(kUserCancelledError);
+}
+
+void WebstoreInlineInstaller::CompleteInstall(const std::string& error) {
+ if (error.empty()) {
+ delegate_->OnInlineInstallSuccess();
+ } else {
+ delegate_->OnInlineInstallFailure(error);
+ }
+ Release(); // Matches the AddRef in BeginInstall.
+}