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

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

Issue 8391004: Add webstorePrivate API for installing bundles of extensions. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: . Created 9 years, 2 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_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) {}
« no previous file with comments | « chrome/browser/extensions/webstore_bundle.h ('k') | chrome/browser/extensions/webstore_bundle_browsertest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698