Chromium Code Reviews| Index: chrome/browser/extensions/bundle_installer.cc |
| diff --git a/chrome/browser/extensions/bundle_installer.cc b/chrome/browser/extensions/bundle_installer.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..b17ab4f5129ae7a1888439fd673845181fbff14b |
| --- /dev/null |
| +++ b/chrome/browser/extensions/bundle_installer.cc |
| @@ -0,0 +1,274 @@ |
| +// Copyright (c) 2012 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/bundle_installer.h" |
| + |
| +#include <string> |
| +#include <vector> |
| + |
| +#include "base/command_line.h" |
| +#include "base/values.h" |
| +#include "chrome/browser/extensions/crx_installer.h" |
| +#include "chrome/browser/profiles/profile.h" |
| +#include "chrome/browser/ui/browser.h" |
| +#include "chrome/common/chrome_switches.h" |
| +#include "content/public/browser/browser_thread.h" |
| +#include "content/public/browser/navigation_controller.h" |
| +#include "content/public/browser/web_contents.h" |
| + |
| +using content::NavigationController; |
| + |
| +namespace extensions { |
| + |
| +namespace { |
| + |
| +enum AutoApproveForTest { |
| + DO_NOT_SKIP = 0, |
| + PROCEED, |
| + ABORT |
| +}; |
| + |
| +AutoApproveForTest g_auto_approve_for_test = DO_NOT_SKIP; |
| + |
| +} // namespace |
| + |
| +// static |
| +void BundleInstaller::SetAutoApproveForTesting(bool auto_approve) { |
| + CHECK(CommandLine::ForCurrentProcess()->HasSwitch(switches::kTestType)); |
| + g_auto_approve_for_test = auto_approve ? PROCEED : ABORT; |
| +} |
| + |
| +BundleInstaller::Item::Item() : state(STATE_PENDING) {} |
| + |
| +scoped_refptr<Extension> BundleInstaller::Item::CreateDummyExtension( |
| + DictionaryValue* manifest) { |
|
Yoyo Zhou
2012/02/22 00:20:26
It's slightly confusing that this argument has the
jstritar
2012/02/22 15:45:48
Done.
|
| + // We require localized names so we can have nice error messages when we can't |
| + // parse an extension manifest. |
| + CHECK(!localized_name.empty()); |
| + |
| + manifest->SetString(extension_manifest_keys::kName, localized_name); |
| + |
| + std::string error; |
| + return Extension::Create(FilePath(), |
| + Extension::INTERNAL, |
| + *manifest, |
| + Extension::NO_FLAGS, |
| + id, |
| + &error); |
| +} |
| + |
| +BundleInstaller::BundleInstaller(Profile* profile, |
| + const BundleInstaller::ItemList& items) |
| + : approved_(false), |
| + browser_(NULL), |
| + profile_(profile), |
| + delegate_(NULL) { |
| + BrowserList::AddObserver(this); |
| + for (size_t i = 0; i < items.size(); ++i) { |
| + items_[items[i].id] = items[i]; |
| + items_[items[i].id].state = Item::STATE_PENDING; |
| + } |
| +} |
| + |
| +BundleInstaller::~BundleInstaller() { |
| + BrowserList::RemoveObserver(this); |
| +} |
| + |
| +BundleInstaller::ItemList BundleInstaller::GetItemsByState( |
| + Item::State state) const { |
| + ItemList list; |
| + |
| + for (ItemMap::const_iterator i = items_.begin(); i != items_.end(); ++i) { |
| + if (i->second.state == state) |
| + list.push_back(i->second); |
| + } |
| + |
| + return list; |
| +} |
| + |
| +void BundleInstaller::PromptForApproval(Delegate* delegate) { |
| + delegate_ = delegate; |
| + |
| + AddRef(); // Balanced in ReportApproved() and ReportCanceled(). |
| + |
| + ParseManifests(); |
| +} |
| + |
| +void BundleInstaller::CompleteInstall(NavigationController* controller, |
| + Browser* browser, |
| + Delegate* delegate) { |
| + browser_ = browser; |
| + delegate_ = delegate; |
|
Yoyo Zhou
2012/02/22 00:20:26
Is this delegate argument necessary? It looks like
jstritar
2012/02/22 15:45:48
Yeah, PromptForApproval and CompleteInstall will b
|
| + |
| + AddRef(); // Balanced in ReportComplete(); |
| + |
| + if (!approved_ || GetItemsByState(Item::STATE_PENDING).empty()) { |
|
Yoyo Zhou
2012/02/22 00:20:26
How do we get here if !approved_; could that be a
jstritar
2012/02/22 15:45:48
Good point -- even if these methods are called by
|
| + ReportComplete(); |
| + return; |
| + } |
| + |
| + // Start each WebstoreInstaller. |
| + for (ItemMap::iterator i = items_.begin(); i != items_.end(); ++i) { |
| + if (i->second.state != Item::STATE_PENDING) |
| + continue; |
| + |
| + scoped_refptr<WebstoreInstaller> installer = new WebstoreInstaller( |
| + profile_, |
| + this, |
| + controller, |
| + i->first, |
| + WebstoreInstaller::FLAG_NONE); |
| + installer->Start(); |
| + } |
| +} |
| + |
| +// static |
| +void BundleInstaller::ShowInstalledBubble( |
| + const BundleInstaller* bundle, Browser* browser) { |
| + // TODO(jstritar): provide platform specific implementations. |
| +} |
| + |
| +void BundleInstaller::ParseManifests() { |
| + if (items_.empty()) { |
| + ReportCanceled(false); |
| + return; |
| + } |
| + |
| + for (ItemMap::iterator i = items_.begin(); i != items_.end(); ++i) { |
| + scoped_refptr<WebstoreInstallHelper> helper = new WebstoreInstallHelper( |
| + this, i->first, i->second.manifest, "", GURL(), NULL); |
| + helper->Start(); |
| + } |
| +} |
| + |
| +void BundleInstaller::ReportApproved() { |
| + if (delegate_) |
| + delegate_->OnBundleInstallApproved(); |
| + |
| + Release(); // Balanced in ParseManifests(). |
|
Yoyo Zhou
2012/02/22 00:20:26
in PromptForApproval
jstritar
2012/02/22 15:45:48
Done.
|
| +} |
| + |
| +void BundleInstaller::ReportCanceled(bool user_initiated) { |
| + if (delegate_) |
| + delegate_->OnBundleInstallCanceled(user_initiated); |
| + |
| + Release(); // Balanced in ParseManifests(). |
|
Yoyo Zhou
2012/02/22 00:20:26
ditto
jstritar
2012/02/22 15:45:48
Done.
|
| +} |
| + |
| +void BundleInstaller::ReportComplete() { |
| + if (delegate_) |
| + delegate_->OnBundleInstallCompleted(); |
| + |
| + Release(); // Balanced in CompleteInstall(). |
| +} |
| + |
| +void BundleInstaller::ShowPromptIfDoneParsing() { |
| + // We don't prompt until all the manifests have been parsed. |
| + if (GetItemsByState(Item::STATE_PENDING).size() != dummy_extensions_.size()) |
| + return; |
| + |
| + ShowPrompt(); |
| +} |
| + |
| +void BundleInstaller::ShowPrompt() { |
| + // Abort if we couldn't create any Extensions out of the manifests. |
| + if (dummy_extensions_.empty()) { |
| + ReportCanceled(false); |
| + return; |
| + } |
| + |
| + scoped_refptr<ExtensionPermissionSet> permissions; |
| + for (size_t i = 0; i < dummy_extensions_.size(); ++i) { |
| + permissions = ExtensionPermissionSet::CreateUnion( |
| + permissions, dummy_extensions_[i]->required_permission_set()); |
| + } |
| + |
| + // TODO(jstritar): show the actual prompt. |
| + if (g_auto_approve_for_test == PROCEED) |
| + InstallUIProceed(); |
| + else if (g_auto_approve_for_test == ABORT) |
| + InstallUIAbort(true); |
| + else |
| + InstallUIAbort(false); |
| +} |
| + |
| +void BundleInstaller::ShowInstalledBubbleIfDone() { |
| + // We're ready to show the installed bubble when no items are pending. |
| + if (!GetItemsByState(Item::STATE_PENDING).empty()) |
| + return; |
| + |
| + if (browser_) |
| + ShowInstalledBubble(this, browser_); |
| + |
| + ReportComplete(); |
| +} |
| + |
| +void BundleInstaller::OnWebstoreParseSuccess( |
| + const std::string& id, |
| + const SkBitmap& icon, |
| + DictionaryValue* manifest) { |
| + dummy_extensions_.push_back(items_[id].CreateDummyExtension(manifest)); |
| + parsed_manifests_[id] = linked_ptr<DictionaryValue>(manifest); |
| + |
| + ShowPromptIfDoneParsing(); |
| +} |
| + |
| +void BundleInstaller::OnWebstoreParseFailure( |
| + const std::string& id, |
| + WebstoreInstallHelper::Delegate::InstallHelperResultCode result_code, |
| + const std::string& error_message) { |
| + items_[id].state = Item::STATE_FAILED; |
| + |
| + ShowPromptIfDoneParsing(); |
| +} |
| + |
| +void BundleInstaller::InstallUIProceed() { |
| + approved_ = true; |
| + for (ItemMap::iterator i = items_.begin(); i != items_.end(); ++i) { |
| + if (i->second.state != Item::STATE_PENDING) |
| + continue; |
| + |
| + // Create a whitelist entry for each of the approved extensions. |
| + CrxInstaller::WhitelistEntry* entry = new CrxInstaller::WhitelistEntry; |
| + entry->parsed_manifest.reset(parsed_manifests_[i->first]->DeepCopy()); |
| + entry->localized_name = i->second.localized_name; |
| + entry->use_app_installed_bubble = false; |
| + entry->skip_post_install_ui = true; |
| + CrxInstaller::SetWhitelistEntry(i->first, entry); |
| + } |
| + ReportApproved(); |
| +} |
| + |
| +void BundleInstaller::InstallUIAbort(bool user_initiated) { |
| + for (ItemMap::iterator i = items_.begin(); i != items_.end(); ++i) |
| + i->second.state = Item::STATE_FAILED; |
| + |
| + ReportCanceled(user_initiated); |
| +} |
| + |
| +void BundleInstaller::OnExtensionInstallSuccess(const std::string& id) { |
| + items_[id].state = Item::STATE_INSTALLED; |
| + |
| + ShowInstalledBubbleIfDone(); |
| +} |
| + |
| +void BundleInstaller::OnExtensionInstallFailure(const std::string& id, |
| + const std::string& error) { |
| + items_[id].state = Item::STATE_FAILED; |
| + |
| + ShowInstalledBubbleIfDone(); |
| +} |
| + |
| +void BundleInstaller::OnBrowserAdded(const Browser* browser) { |
| +} |
| + |
| +void BundleInstaller::OnBrowserRemoved(const Browser* browser) { |
| + if (browser_ == browser) |
| + browser_ = NULL; |
| +} |
| + |
| +void BundleInstaller::OnBrowserSetLastActive(const Browser* browser) { |
| +} |
| + |
| +} // namespace extensions |