| Index: chrome/browser/extensions/webstore_bundle.cc
|
| diff --git a/chrome/browser/extensions/webstore_bundle.cc b/chrome/browser/extensions/webstore_bundle.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..c6617a298c994331a7918afe31c7c36e130afc6b
|
| --- /dev/null
|
| +++ b/chrome/browser/extensions/webstore_bundle.cc
|
| @@ -0,0 +1,363 @@
|
| +// 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_bundle.h"
|
| +
|
| +#include <string>
|
| +#include <vector>
|
| +
|
| +#include "base/string16.h"
|
| +#include "base/values.h"
|
| +#include "chrome/browser/extensions/crx_installer.h"
|
| +#include "chrome/browser/extensions/extension_install_dialog.h"
|
| +#include "chrome/browser/profiles/profile.h"
|
| +#include "chrome/browser/ui/browser.h"
|
| +#include "chrome/browser/ui/browser_list.h"
|
| +#include "content/browser/tab_contents/navigation_controller.h"
|
| +#include "grit/generated_resources.h"
|
| +#include "ui/base/l10n/l10n_util.h"
|
| +
|
| +namespace {
|
| +
|
| +enum AutoApproveForTest {
|
| + DO_NOT_SKIP = 0,
|
| + PROCEED,
|
| + ABORT
|
| +};
|
| +
|
| +AutoApproveForTest auto_approve_for_test = DO_NOT_SKIP;
|
| +
|
| +} // namespace
|
| +
|
| +// static
|
| +void WebstoreBundle::SetAutoApproveForTesting(bool auto_approve) {
|
| + auto_approve_for_test = auto_approve ? PROCEED : ABORT;
|
| +}
|
| +
|
| +WebstoreBundle::Item::Item(const std::string& id,
|
| + const std::string& manifest,
|
| + const std::string& localized_name)
|
| + : id_(id),
|
| + manifest_(manifest),
|
| + localized_name_(localized_name),
|
| + state_(STATE_INITIAL) {
|
| + if (!Extension::IdIsValid(id))
|
| + state_ = STATE_NOT_INSTALLED;
|
| +}
|
| +
|
| +WebstoreBundle::Item::~Item() {}
|
| +
|
| +void WebstoreBundle::Item::SetDummyExtension(
|
| + DictionaryValue* parsed_manifest) {
|
| + // We require localized names so we can have nice error messages when we can't
|
| + // parse an extension manifest.
|
| + CHECK(!localized_name_.empty());
|
| + parsed_manifest_.reset(parsed_manifest);
|
| +
|
| + scoped_ptr<DictionaryValue> localized_manifest(parsed_manifest_->DeepCopy());
|
| + localized_manifest->SetString(
|
| + extension_manifest_keys::kName, localized_name_);
|
| +
|
| + std::string init_errors;
|
| + dummy_extension_ = Extension::CreateWithId(
|
| + FilePath(),
|
| + Extension::INTERNAL,
|
| + *localized_manifest,
|
| + Extension::NO_FLAGS,
|
| + id_,
|
| + &init_errors);
|
| +
|
| + state_ = dummy_extension_.get() ? STATE_PARSED : STATE_NOT_INSTALLED;
|
| +}
|
| +
|
| +WebstoreBundle::WebstoreBundle(Profile* profile, ItemList* items)
|
| + : profile_(profile),
|
| + controller_(NULL),
|
| + delegate_(NULL) {
|
| + items_.swap(*items);
|
| +}
|
| +
|
| +WebstoreBundle::~WebstoreBundle() {}
|
| +
|
| +void WebstoreBundle::PromptForApproval(Delegate* delegate) {
|
| + delegate_ = delegate;
|
| +
|
| + AddRef(); // Balanced in ReportApproved() and ReportCanceled().
|
| +
|
| + ParseManifests();
|
| +}
|
| +
|
| +void WebstoreBundle::CompleteInstall(NavigationController* controller,
|
| + Delegate* delegate) {
|
| + controller_ = controller;
|
| + delegate_ = delegate;
|
| +
|
| + AddRef(); // Balanced in ReportComplete();
|
| +
|
| + InstallNextItem();
|
| +}
|
| +
|
| +void WebstoreBundle::InstallNextItem() {
|
| + for (size_t i = 0; i < items_.size(); ++i) {
|
| + if (items_[i]->state() == Item::STATE_APPROVED) {
|
| + scoped_refptr<WebstoreInstaller> installer = new WebstoreInstaller(
|
| + profile_, this, controller_, items_[i]->id(),
|
| + WebstoreInstaller::FLAG_NONE);
|
| + installer->Start();
|
| +
|
| + // We only install one at a time, since the WebstoreInstaller navigates
|
| + // |controller_| to the extension's download URL.
|
| + return;
|
| + }
|
| + }
|
| +
|
| + ShowInstalledBubbleIfDone();
|
| +}
|
| +
|
| +void WebstoreBundle::ReportApproved() {
|
| + if (delegate_)
|
| + delegate_->OnBundleInstallApproved();
|
| +
|
| + Release(); // Balanced in ParseManifests().
|
| +}
|
| +
|
| +void WebstoreBundle::ReportCanceled(bool user_initiated) {
|
| + if (delegate_)
|
| + delegate_->OnBundleInstallCanceled(user_initiated);
|
| +
|
| + Release(); // Balanced in ParseManifests().
|
| +}
|
| +
|
| +void WebstoreBundle::ReportComplete() {
|
| + if (delegate_)
|
| + delegate_->OnBundleInstallCompleted();
|
| +
|
| + Release(); // Balanced in CompleteInstall().
|
| +}
|
| +
|
| +bool WebstoreBundle::IsSameBundle(const std::vector<std::string>& ids) {
|
| + if (ids.size() != items_.size())
|
| + return false;
|
| +
|
| + // This should be updated if we ever start installing large bundles.
|
| + for (size_t i = 0; i < items_.size(); ++i) {
|
| + if (std::find(ids.begin(), ids.end(), items_[i]->id()) == ids.end())
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +std::vector<const WebstoreBundle::Item*> WebstoreBundle::GetItemsInState(
|
| + Item::State state, int matcher) const {
|
| + std::vector<const Item*> result;
|
| + for (size_t i = 0; i < items_.size(); ++i) {
|
| + if (items_[i]->state() == state) {
|
| + if (matcher == MATCH_ALL) {
|
| + result.push_back(items_[i]);
|
| + continue;
|
| + }
|
| +
|
| + if (!items_[i]->dummy_extension().get())
|
| + continue;
|
| +
|
| + bool is_app = items_[i]->dummy_extension()->is_app();
|
| + if (!is_app && (matcher & MATCH_EXTENSIONS)) {
|
| + result.push_back(items_[i]);
|
| + continue;
|
| + }
|
| + if (is_app && (matcher & MATCH_APPS)) {
|
| + result.push_back(items_[i]);
|
| + continue;
|
| + }
|
| + }
|
| + }
|
| +
|
| + return result;
|
| +}
|
| +
|
| +string16 WebstoreBundle::GetHeadingTextForPrompt() const {
|
| + bool has_app = HasAppsInState(Item::STATE_PARSED);
|
| + bool has_extension = HasExtensionsInState(Item::STATE_PARSED);
|
| +
|
| + int msg_id = 0;
|
| + if (has_app && has_extension)
|
| + msg_id = IDS_EXTENSION_BUNDLE_INSTALL_PROMPT_HEADING_EXTENSION_APPS;
|
| + else if (has_app)
|
| + msg_id = IDS_EXTENSION_BUNDLE_INSTALL_PROMPT_HEADING_APPS;
|
| + else if (has_extension)
|
| + msg_id = IDS_EXTENSION_BUNDLE_INSTALL_PROMPT_HEADING_EXTENSIONS;
|
| + else
|
| + NOTREACHED();
|
| +
|
| + return l10n_util::GetStringUTF16(msg_id);
|
| +}
|
| +
|
| +string16 WebstoreBundle::GetInstalledHeadingTextForBubble() const {
|
| + bool installed_apps = HasAppsInState(Item::STATE_INSTALLED);
|
| + bool installed_extensions = HasExtensionsInState(Item::STATE_INSTALLED);
|
| +
|
| + int msg_id = 0;
|
| + if (installed_apps && installed_extensions)
|
| + msg_id = IDS_EXTENSION_BUNDLE_INSTALLED_HEADING_EXTENSION_APPS;
|
| + else if (installed_apps)
|
| + msg_id = IDS_EXTENSION_BUNDLE_INSTALLED_HEADING_APPS;
|
| + else if (installed_extensions)
|
| + msg_id = IDS_EXTENSION_BUNDLE_INSTALLED_HEADING_EXTENSIONS;
|
| + else
|
| + return string16();
|
| +
|
| + return l10n_util::GetStringUTF16(msg_id);
|
| +}
|
| +
|
| +string16 WebstoreBundle::GetFailedHeadingTextForBubble() const {
|
| + if (GetItemsInState(WebstoreBundle::Item::STATE_NOT_INSTALLED,
|
| + WebstoreBundle::MATCH_ALL).size()) {
|
| + return l10n_util::GetStringUTF16(IDS_EXTENSION_BUNDLE_ERROR_HEADING);
|
| + } else {
|
| + return string16();
|
| + }
|
| +}
|
| +
|
| +bool WebstoreBundle::HasAppsInState(Item::State state) const {
|
| + return GetItemsInState(state, MATCH_APPS).size() > 0;
|
| +}
|
| +
|
| +bool WebstoreBundle::HasExtensionsInState(Item::State state) const {
|
| + return GetItemsInState(state, MATCH_EXTENSIONS).size() > 0;
|
| +}
|
| +
|
| +void WebstoreBundle::OnWebstoreParseSuccess(
|
| + const std::string& id,
|
| + const SkBitmap& icon,
|
| + base::DictionaryValue* parsed_manifest) {
|
| + GetItemById(id)->SetDummyExtension(parsed_manifest);
|
| + ShowPromptIfDoneParsing();
|
| +}
|
| +
|
| +void WebstoreBundle::OnWebstoreParseFailure(
|
| + const std::string& id,
|
| + WebstoreInstallHelper::Delegate::InstallHelperResultCode result_code,
|
| + const std::string& error_message) {
|
| + GetItemById(id)->MarkNotInstalled();
|
| + ShowPromptIfDoneParsing();
|
| +}
|
| +
|
| +void WebstoreBundle::InstallUIProceed() {
|
| + for (size_t i = 0; i < items_.size(); ++i) {
|
| + Item* item = items_[i];
|
| + if (item->state() == Item::STATE_PARSED) {
|
| + item->MarkApproved();
|
| +
|
| + // Create a whitelist entry for each of the approved extensions.
|
| + CrxInstaller::WhitelistEntry* entry = new CrxInstaller::WhitelistEntry;
|
| + entry->parsed_manifest.reset(item->parsed_manifest()->DeepCopy());
|
| + entry->localized_name = item->localized_name();
|
| + entry->use_app_installed_bubble = false;
|
| + entry->skip_post_install_ui = true;
|
| + CrxInstaller::SetWhitelistEntry(item->id(), entry);
|
| + }
|
| + }
|
| +
|
| + ReportApproved();
|
| +}
|
| +
|
| +void WebstoreBundle::InstallUIAbort(bool user_initiated) {
|
| + for (size_t i = 0; i < items_.size(); ++i)
|
| + items_[i]->MarkNotInstalled();
|
| +
|
| + ReportCanceled(user_initiated);
|
| +}
|
| +
|
| +void WebstoreBundle::OnExtensionInstallSuccess(const std::string& id) {
|
| + GetItemById(id)->MarkInstalled();
|
| + InstallNextItem();
|
| +}
|
| +
|
| +void WebstoreBundle::OnExtensionInstallFailure(const std::string& id,
|
| + const std::string& error) {
|
| + GetItemById(id)->MarkNotInstalled();
|
| + InstallNextItem();
|
| +}
|
| +
|
| +WebstoreBundle::Item* WebstoreBundle::GetItemById(const std::string& id) {
|
| + for (size_t i = 0; i < items_.size(); ++i) {
|
| + if (items_[i]->id() == id)
|
| + return items_[i];
|
| + }
|
| + NOTREACHED();
|
| + return NULL;
|
| +}
|
| +
|
| +void WebstoreBundle::ParseManifests() {
|
| + bool at_least_one_item = false;
|
| + for (size_t i = 0; i < items_.size(); ++i) {
|
| + Item* item = items_[i];
|
| + if (item->state() != Item::STATE_INITIAL)
|
| + continue;
|
| +
|
| + at_least_one_item = true;
|
| +
|
| + scoped_refptr<WebstoreInstallHelper> helper = new WebstoreInstallHelper(
|
| + this, item->id(), item->manifest(), "", GURL(), NULL);
|
| + helper->Start();
|
| + }
|
| +
|
| + if (!at_least_one_item)
|
| + ReportCanceled(false);
|
| +}
|
| +
|
| +void WebstoreBundle::ShowPromptIfDoneParsing() {
|
| + for (size_t i = 0; i < items_.size(); ++i) {
|
| + if (items_[i]->state() == Item::STATE_INITIAL)
|
| + return;
|
| + }
|
| +
|
| + ShowPrompt();
|
| +}
|
| +
|
| +void WebstoreBundle::ShowPrompt() {
|
| + scoped_refptr<ExtensionPermissionSet> permissions;
|
| + bool at_least_one_item = false;
|
| +
|
| + for (size_t i = 0; i < items_.size(); ++i) {
|
| + Item* item = items_[i];
|
| + CHECK_NE(Item::STATE_INITIAL, item->state());
|
| + if (item->state() == Item::STATE_PARSED) {
|
| + at_least_one_item = true;
|
| + permissions = ExtensionPermissionSet::CreateUnion(
|
| + permissions, item->dummy_extension()->required_permission_set());
|
| + }
|
| + }
|
| +
|
| + if (at_least_one_item) {
|
| + // TODO(jstritar): Show the confirmation prompt, which was left out to
|
| + // limit the scope of this CL.
|
| + if (auto_approve_for_test == PROCEED)
|
| + InstallUIProceed();
|
| + else if (auto_approve_for_test == ABORT)
|
| + InstallUIAbort(true);
|
| + } else {
|
| + ReportCanceled(false);
|
| + }
|
| +}
|
| +
|
| +void WebstoreBundle::ShowInstalledBubbleIfDone() {
|
| + CHECK_LT(Item::STATE_INSTALLED, Item::STATE_NOT_INSTALLED);
|
| + for (size_t i = 0; i < items_.size(); ++i) {
|
| + if (items_[i]->state() < Item::STATE_INSTALLED)
|
| + return;
|
| + }
|
| +
|
| + Browser* browser = BrowserList::GetLastActiveWithProfile(profile_);
|
| + if (browser)
|
| + ShowInstalledBubble(this, browser);
|
| +
|
| + ReportComplete();
|
| +}
|
| +
|
| +// TODO(jstritar): remove this after landing the bubble implementations.
|
| +// static
|
| +void WebstoreBundle::ShowInstalledBubble(
|
| + const WebstoreBundle* bundle, Browser* browser) {}
|
|
|