Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/browser/extensions/bundle_installer.h" | |
| 6 | |
| 7 #include <string> | |
| 8 #include <vector> | |
| 9 | |
| 10 #include "base/command_line.h" | |
| 11 #include "base/values.h" | |
| 12 #include "chrome/browser/extensions/crx_installer.h" | |
| 13 #include "chrome/browser/profiles/profile.h" | |
| 14 #include "chrome/browser/ui/browser.h" | |
| 15 #include "chrome/common/chrome_switches.h" | |
| 16 #include "content/public/browser/browser_thread.h" | |
| 17 #include "content/public/browser/navigation_controller.h" | |
| 18 #include "content/public/browser/web_contents.h" | |
| 19 | |
| 20 using content::NavigationController; | |
| 21 | |
| 22 namespace extensions { | |
| 23 | |
| 24 namespace { | |
| 25 | |
| 26 enum AutoApproveForTest { | |
| 27 DO_NOT_SKIP = 0, | |
| 28 PROCEED, | |
| 29 ABORT | |
| 30 }; | |
| 31 | |
| 32 AutoApproveForTest g_auto_approve_for_test = DO_NOT_SKIP; | |
| 33 | |
| 34 } // namespace | |
| 35 | |
| 36 // static | |
| 37 void BundleInstaller::SetAutoApproveForTesting(bool auto_approve) { | |
| 38 CHECK(CommandLine::ForCurrentProcess()->HasSwitch(switches::kTestType)); | |
| 39 g_auto_approve_for_test = auto_approve ? PROCEED : ABORT; | |
| 40 } | |
| 41 | |
| 42 BundleInstaller::Item::Item() : state(STATE_PENDING) {} | |
| 43 | |
| 44 scoped_refptr<Extension> BundleInstaller::Item::CreateDummyExtension( | |
| 45 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.
| |
| 46 // We require localized names so we can have nice error messages when we can't | |
| 47 // parse an extension manifest. | |
| 48 CHECK(!localized_name.empty()); | |
| 49 | |
| 50 manifest->SetString(extension_manifest_keys::kName, localized_name); | |
| 51 | |
| 52 std::string error; | |
| 53 return Extension::Create(FilePath(), | |
| 54 Extension::INTERNAL, | |
| 55 *manifest, | |
| 56 Extension::NO_FLAGS, | |
| 57 id, | |
| 58 &error); | |
| 59 } | |
| 60 | |
| 61 BundleInstaller::BundleInstaller(Profile* profile, | |
| 62 const BundleInstaller::ItemList& items) | |
| 63 : approved_(false), | |
| 64 browser_(NULL), | |
| 65 profile_(profile), | |
| 66 delegate_(NULL) { | |
| 67 BrowserList::AddObserver(this); | |
| 68 for (size_t i = 0; i < items.size(); ++i) { | |
| 69 items_[items[i].id] = items[i]; | |
| 70 items_[items[i].id].state = Item::STATE_PENDING; | |
| 71 } | |
| 72 } | |
| 73 | |
| 74 BundleInstaller::~BundleInstaller() { | |
| 75 BrowserList::RemoveObserver(this); | |
| 76 } | |
| 77 | |
| 78 BundleInstaller::ItemList BundleInstaller::GetItemsByState( | |
| 79 Item::State state) const { | |
| 80 ItemList list; | |
| 81 | |
| 82 for (ItemMap::const_iterator i = items_.begin(); i != items_.end(); ++i) { | |
| 83 if (i->second.state == state) | |
| 84 list.push_back(i->second); | |
| 85 } | |
| 86 | |
| 87 return list; | |
| 88 } | |
| 89 | |
| 90 void BundleInstaller::PromptForApproval(Delegate* delegate) { | |
| 91 delegate_ = delegate; | |
| 92 | |
| 93 AddRef(); // Balanced in ReportApproved() and ReportCanceled(). | |
| 94 | |
| 95 ParseManifests(); | |
| 96 } | |
| 97 | |
| 98 void BundleInstaller::CompleteInstall(NavigationController* controller, | |
| 99 Browser* browser, | |
| 100 Delegate* delegate) { | |
| 101 browser_ = browser; | |
| 102 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
| |
| 103 | |
| 104 AddRef(); // Balanced in ReportComplete(); | |
| 105 | |
| 106 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
| |
| 107 ReportComplete(); | |
| 108 return; | |
| 109 } | |
| 110 | |
| 111 // Start each WebstoreInstaller. | |
| 112 for (ItemMap::iterator i = items_.begin(); i != items_.end(); ++i) { | |
| 113 if (i->second.state != Item::STATE_PENDING) | |
| 114 continue; | |
| 115 | |
| 116 scoped_refptr<WebstoreInstaller> installer = new WebstoreInstaller( | |
| 117 profile_, | |
| 118 this, | |
| 119 controller, | |
| 120 i->first, | |
| 121 WebstoreInstaller::FLAG_NONE); | |
| 122 installer->Start(); | |
| 123 } | |
| 124 } | |
| 125 | |
| 126 // static | |
| 127 void BundleInstaller::ShowInstalledBubble( | |
| 128 const BundleInstaller* bundle, Browser* browser) { | |
| 129 // TODO(jstritar): provide platform specific implementations. | |
| 130 } | |
| 131 | |
| 132 void BundleInstaller::ParseManifests() { | |
| 133 if (items_.empty()) { | |
| 134 ReportCanceled(false); | |
| 135 return; | |
| 136 } | |
| 137 | |
| 138 for (ItemMap::iterator i = items_.begin(); i != items_.end(); ++i) { | |
| 139 scoped_refptr<WebstoreInstallHelper> helper = new WebstoreInstallHelper( | |
| 140 this, i->first, i->second.manifest, "", GURL(), NULL); | |
| 141 helper->Start(); | |
| 142 } | |
| 143 } | |
| 144 | |
| 145 void BundleInstaller::ReportApproved() { | |
| 146 if (delegate_) | |
| 147 delegate_->OnBundleInstallApproved(); | |
| 148 | |
| 149 Release(); // Balanced in ParseManifests(). | |
|
Yoyo Zhou
2012/02/22 00:20:26
in PromptForApproval
jstritar
2012/02/22 15:45:48
Done.
| |
| 150 } | |
| 151 | |
| 152 void BundleInstaller::ReportCanceled(bool user_initiated) { | |
| 153 if (delegate_) | |
| 154 delegate_->OnBundleInstallCanceled(user_initiated); | |
| 155 | |
| 156 Release(); // Balanced in ParseManifests(). | |
|
Yoyo Zhou
2012/02/22 00:20:26
ditto
jstritar
2012/02/22 15:45:48
Done.
| |
| 157 } | |
| 158 | |
| 159 void BundleInstaller::ReportComplete() { | |
| 160 if (delegate_) | |
| 161 delegate_->OnBundleInstallCompleted(); | |
| 162 | |
| 163 Release(); // Balanced in CompleteInstall(). | |
| 164 } | |
| 165 | |
| 166 void BundleInstaller::ShowPromptIfDoneParsing() { | |
| 167 // We don't prompt until all the manifests have been parsed. | |
| 168 if (GetItemsByState(Item::STATE_PENDING).size() != dummy_extensions_.size()) | |
| 169 return; | |
| 170 | |
| 171 ShowPrompt(); | |
| 172 } | |
| 173 | |
| 174 void BundleInstaller::ShowPrompt() { | |
| 175 // Abort if we couldn't create any Extensions out of the manifests. | |
| 176 if (dummy_extensions_.empty()) { | |
| 177 ReportCanceled(false); | |
| 178 return; | |
| 179 } | |
| 180 | |
| 181 scoped_refptr<ExtensionPermissionSet> permissions; | |
| 182 for (size_t i = 0; i < dummy_extensions_.size(); ++i) { | |
| 183 permissions = ExtensionPermissionSet::CreateUnion( | |
| 184 permissions, dummy_extensions_[i]->required_permission_set()); | |
| 185 } | |
| 186 | |
| 187 // TODO(jstritar): show the actual prompt. | |
| 188 if (g_auto_approve_for_test == PROCEED) | |
| 189 InstallUIProceed(); | |
| 190 else if (g_auto_approve_for_test == ABORT) | |
| 191 InstallUIAbort(true); | |
| 192 else | |
| 193 InstallUIAbort(false); | |
| 194 } | |
| 195 | |
| 196 void BundleInstaller::ShowInstalledBubbleIfDone() { | |
| 197 // We're ready to show the installed bubble when no items are pending. | |
| 198 if (!GetItemsByState(Item::STATE_PENDING).empty()) | |
| 199 return; | |
| 200 | |
| 201 if (browser_) | |
| 202 ShowInstalledBubble(this, browser_); | |
| 203 | |
| 204 ReportComplete(); | |
| 205 } | |
| 206 | |
| 207 void BundleInstaller::OnWebstoreParseSuccess( | |
| 208 const std::string& id, | |
| 209 const SkBitmap& icon, | |
| 210 DictionaryValue* manifest) { | |
| 211 dummy_extensions_.push_back(items_[id].CreateDummyExtension(manifest)); | |
| 212 parsed_manifests_[id] = linked_ptr<DictionaryValue>(manifest); | |
| 213 | |
| 214 ShowPromptIfDoneParsing(); | |
| 215 } | |
| 216 | |
| 217 void BundleInstaller::OnWebstoreParseFailure( | |
| 218 const std::string& id, | |
| 219 WebstoreInstallHelper::Delegate::InstallHelperResultCode result_code, | |
| 220 const std::string& error_message) { | |
| 221 items_[id].state = Item::STATE_FAILED; | |
| 222 | |
| 223 ShowPromptIfDoneParsing(); | |
| 224 } | |
| 225 | |
| 226 void BundleInstaller::InstallUIProceed() { | |
| 227 approved_ = true; | |
| 228 for (ItemMap::iterator i = items_.begin(); i != items_.end(); ++i) { | |
| 229 if (i->second.state != Item::STATE_PENDING) | |
| 230 continue; | |
| 231 | |
| 232 // Create a whitelist entry for each of the approved extensions. | |
| 233 CrxInstaller::WhitelistEntry* entry = new CrxInstaller::WhitelistEntry; | |
| 234 entry->parsed_manifest.reset(parsed_manifests_[i->first]->DeepCopy()); | |
| 235 entry->localized_name = i->second.localized_name; | |
| 236 entry->use_app_installed_bubble = false; | |
| 237 entry->skip_post_install_ui = true; | |
| 238 CrxInstaller::SetWhitelistEntry(i->first, entry); | |
| 239 } | |
| 240 ReportApproved(); | |
| 241 } | |
| 242 | |
| 243 void BundleInstaller::InstallUIAbort(bool user_initiated) { | |
| 244 for (ItemMap::iterator i = items_.begin(); i != items_.end(); ++i) | |
| 245 i->second.state = Item::STATE_FAILED; | |
| 246 | |
| 247 ReportCanceled(user_initiated); | |
| 248 } | |
| 249 | |
| 250 void BundleInstaller::OnExtensionInstallSuccess(const std::string& id) { | |
| 251 items_[id].state = Item::STATE_INSTALLED; | |
| 252 | |
| 253 ShowInstalledBubbleIfDone(); | |
| 254 } | |
| 255 | |
| 256 void BundleInstaller::OnExtensionInstallFailure(const std::string& id, | |
| 257 const std::string& error) { | |
| 258 items_[id].state = Item::STATE_FAILED; | |
| 259 | |
| 260 ShowInstalledBubbleIfDone(); | |
| 261 } | |
| 262 | |
| 263 void BundleInstaller::OnBrowserAdded(const Browser* browser) { | |
| 264 } | |
| 265 | |
| 266 void BundleInstaller::OnBrowserRemoved(const Browser* browser) { | |
| 267 if (browser_ == browser) | |
| 268 browser_ = NULL; | |
| 269 } | |
| 270 | |
| 271 void BundleInstaller::OnBrowserSetLastActive(const Browser* browser) { | |
| 272 } | |
| 273 | |
| 274 } // namespace extensions | |
| OLD | NEW |