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) {} |