Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(560)

Unified Diff: chrome/browser/extensions/webstore_inline_installer.cc

Issue 7741037: Add WebstoreInlineInstaller (downloads store data, shows the install UI, and starts the install). (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 9 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/extensions/webstore_inline_installer.cc
diff --git a/chrome/browser/extensions/webstore_inline_installer.cc b/chrome/browser/extensions/webstore_inline_installer.cc
new file mode 100644
index 0000000000000000000000000000000000000000..bbe0a70cc3976eccf4fc570e7a86760c95f21584
--- /dev/null
+++ b/chrome/browser/extensions/webstore_inline_installer.cc
@@ -0,0 +1,322 @@
+// 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 kWebstoreRequestError[] = "Could not fetch data from webstore";
+const char kInvalidWebstoreResponseError[] = "Invalid webstore reponse";
+const char kInvalidManifestError[] = "Invalid manifest";
+const char kUserCancelledError[] = "User cancelled install";
+
+// A flag used for WebstoreInlineInstaller::SetAutoConfirmForTests.
+enum AutoConfirmForTest {
+ DO_NOT_SKIP = 0,
+ PROCEED,
+ ABORT
+};
+AutoConfirmForTest auto_confirm_for_tests = DO_NOT_SKIP;
+
+class SafeWebstoreResponseParser : public UtilityProcessHost::Client {
+ public:
+ SafeWebstoreResponseParser(WebstoreInlineInstaller *client,
+ const std::string& webstore_data)
+ : client_(client)
+ , webstore_data_(webstore_data)
asargent_no_longer_on_chrome 2011/08/26 17:50:40 commas in weird place here and line below
Mihai Parparita -not on Chrome 2011/08/29 19:00:56 Fixed.
+ , 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)
asargent_no_longer_on_chrome 2011/08/26 17:50:40 commas in weird place here too
Mihai Parparita -not on Chrome 2011/08/29 19:00:56 Fixed.
+ , delegate_(delegate) {}
+
+WebstoreInlineInstaller::~WebstoreInlineInstaller() {
+}
+
+void WebstoreInlineInstaller::BeginInstall() {
+ GURL webstore_data_url(extension_urls::GetWebstoreItemJsonDataURL(id_));
asargent_no_longer_on_chrome 2011/08/26 17:50:40 It might be good to add a check of Extension::IdIs
Mihai Parparita -not on Chrome 2011/08/29 19:00:56 Done.
+
+ AddRef(); // Balanced in CompleteInstall.
+
+ 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::SetAutoConfirmForTests(
asargent_no_longer_on_chrome 2011/08/26 17:50:40 add comment with "// static" here
Mihai Parparita -not on Chrome 2011/08/29 19:00:56 Now that I've refactored the auto-confirm code to
+ bool should_proceed) {
+ auto_confirm_for_tests = should_proceed ? PROCEED : ABORT;
+}
+
+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;
+
+ scoped_ptr<DictionaryValue> localized_manifest;
+ if (!localized_name_.empty()) {
+ localized_manifest.reset(manifest->DeepCopy());
+ localized_manifest->SetString(extension_manifest_keys::kName,
+ localized_name_);
+ }
+
+ std::string init_errors;
+ scoped_refptr<Extension> dummy_extension = Extension::CreateWithId(
+ FilePath(),
+ Extension::INTERNAL,
+ localized_manifest.get() ? *localized_manifest.get() : *manifest,
+ Extension::NO_FLAGS,
+ id_,
+ &init_errors);
+ if (!dummy_extension.get()) {
+ CompleteInstall(kInvalidManifestError);
+ return;
+ }
+
+ if (icon_.empty())
+ icon_ = Extension::GetDefaultIcon(dummy_extension->is_app());
+
+ // In tests, we may have setup to proceed or abort without putting up the real
+ // confirmation dialog.
+ if (auto_confirm_for_tests != DO_NOT_SKIP) {
+ if (auto_confirm_for_tests == PROCEED)
+ this->InstallUIProceed();
+ else
+ this->InstallUIAbort(true);
+ return;
+ }
+
+ Profile* profile = Profile::FromBrowserContext(
+ tab_contents_->browser_context());
+ ShowExtensionInstallDialog(profile,
+ this,
+ dummy_extension.get(),
+ &icon_,
+ dummy_extension->GetPermissionMessageStrings(),
+ ExtensionInstallUI::INSTALL_PROMPT);
asargent_no_longer_on_chrome 2011/08/26 17:50:40 Do you think it's worth it to extract this code to
Mihai Parparita -not on Chrome 2011/08/29 19:00:56 Added ShowExtensionInstallDialogForManifest (the s
+}
+
+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.
asargent_no_longer_on_chrome 2011/08/26 17:50:40 Yeah, for the case where the download succeeds you
+ 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.
+}

Powered by Google App Engine
This is Rietveld 408576698