| 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..f13c94f400b5f4ce7c60d432e982354815caf4cb
|
| --- /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.
|
| +}
|
|
|