OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2011 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/webstore_bundle.h" |
| 6 |
| 7 #include <string> |
| 8 #include <vector> |
| 9 |
| 10 #include "base/string16.h" |
| 11 #include "base/values.h" |
| 12 #include "chrome/browser/extensions/crx_installer.h" |
| 13 #include "chrome/browser/extensions/extension_install_dialog.h" |
| 14 #include "chrome/browser/profiles/profile.h" |
| 15 #include "chrome/browser/ui/browser.h" |
| 16 #include "chrome/browser/ui/browser_list.h" |
| 17 #include "content/browser/tab_contents/navigation_controller.h" |
| 18 #include "grit/generated_resources.h" |
| 19 #include "ui/base/l10n/l10n_util.h" |
| 20 |
| 21 namespace { |
| 22 |
| 23 enum AutoApproveForTest { |
| 24 DO_NOT_SKIP = 0, |
| 25 PROCEED, |
| 26 ABORT |
| 27 }; |
| 28 |
| 29 AutoApproveForTest auto_approve_for_test = DO_NOT_SKIP; |
| 30 |
| 31 } // namespace |
| 32 |
| 33 // static |
| 34 void WebstoreBundle::SetAutoApproveForTesting(bool auto_approve) { |
| 35 auto_approve_for_test = auto_approve ? PROCEED : ABORT; |
| 36 } |
| 37 |
| 38 WebstoreBundle::Item::Item(const std::string& id, |
| 39 const std::string& manifest, |
| 40 const std::string& localized_name) |
| 41 : id_(id), |
| 42 manifest_(manifest), |
| 43 localized_name_(localized_name), |
| 44 state_(STATE_INITIAL) { |
| 45 if (!Extension::IdIsValid(id)) |
| 46 state_ = STATE_NOT_INSTALLED; |
| 47 } |
| 48 |
| 49 WebstoreBundle::Item::~Item() {} |
| 50 |
| 51 void WebstoreBundle::Item::SetDummyExtension( |
| 52 DictionaryValue* parsed_manifest) { |
| 53 // We require localized names so we can have nice error messages when we can't |
| 54 // parse an extension manifest. |
| 55 CHECK(!localized_name_.empty()); |
| 56 parsed_manifest_.reset(parsed_manifest); |
| 57 |
| 58 scoped_ptr<DictionaryValue> localized_manifest(parsed_manifest_->DeepCopy()); |
| 59 localized_manifest->SetString( |
| 60 extension_manifest_keys::kName, localized_name_); |
| 61 |
| 62 std::string init_errors; |
| 63 dummy_extension_ = Extension::CreateWithId( |
| 64 FilePath(), |
| 65 Extension::INTERNAL, |
| 66 *localized_manifest, |
| 67 Extension::NO_FLAGS, |
| 68 id_, |
| 69 &init_errors); |
| 70 |
| 71 state_ = dummy_extension_.get() ? STATE_PARSED : STATE_NOT_INSTALLED; |
| 72 } |
| 73 |
| 74 WebstoreBundle::WebstoreBundle(Profile* profile, ItemList* items) |
| 75 : profile_(profile), |
| 76 controller_(NULL), |
| 77 delegate_(NULL) { |
| 78 items_.swap(*items); |
| 79 } |
| 80 |
| 81 WebstoreBundle::~WebstoreBundle() {} |
| 82 |
| 83 void WebstoreBundle::PromptForApproval(Delegate* delegate) { |
| 84 delegate_ = delegate; |
| 85 |
| 86 AddRef(); // Balanced in ReportApproved() and ReportCanceled(). |
| 87 |
| 88 ParseManifests(); |
| 89 } |
| 90 |
| 91 void WebstoreBundle::CompleteInstall(NavigationController* controller, |
| 92 Delegate* delegate) { |
| 93 controller_ = controller; |
| 94 delegate_ = delegate; |
| 95 |
| 96 AddRef(); // Balanced in ReportComplete(); |
| 97 |
| 98 InstallNextItem(); |
| 99 } |
| 100 |
| 101 void WebstoreBundle::InstallNextItem() { |
| 102 for (size_t i = 0; i < items_.size(); ++i) { |
| 103 if (items_[i]->state() == Item::STATE_APPROVED) { |
| 104 scoped_refptr<WebstoreInstaller> installer = new WebstoreInstaller( |
| 105 profile_, this, controller_, items_[i]->id(), |
| 106 WebstoreInstaller::FLAG_NONE); |
| 107 installer->Start(); |
| 108 |
| 109 // We only install one at a time, since the WebstoreInstaller navigates |
| 110 // |controller_| to the extension's download URL. |
| 111 return; |
| 112 } |
| 113 } |
| 114 |
| 115 ShowInstalledBubbleIfDone(); |
| 116 } |
| 117 |
| 118 void WebstoreBundle::ReportApproved() { |
| 119 if (delegate_) |
| 120 delegate_->OnBundleInstallApproved(); |
| 121 |
| 122 Release(); // Balanced in ParseManifests(). |
| 123 } |
| 124 |
| 125 void WebstoreBundle::ReportCanceled(bool user_initiated) { |
| 126 if (delegate_) |
| 127 delegate_->OnBundleInstallCanceled(user_initiated); |
| 128 |
| 129 Release(); // Balanced in ParseManifests(). |
| 130 } |
| 131 |
| 132 void WebstoreBundle::ReportComplete() { |
| 133 if (delegate_) |
| 134 delegate_->OnBundleInstallCompleted(); |
| 135 |
| 136 Release(); // Balanced in CompleteInstall(). |
| 137 } |
| 138 |
| 139 bool WebstoreBundle::IsSameBundle(const std::vector<std::string>& ids) { |
| 140 if (ids.size() != items_.size()) |
| 141 return false; |
| 142 |
| 143 // This should be updated if we ever start installing large bundles. |
| 144 for (size_t i = 0; i < items_.size(); ++i) { |
| 145 if (std::find(ids.begin(), ids.end(), items_[i]->id()) == ids.end()) |
| 146 return false; |
| 147 } |
| 148 |
| 149 return true; |
| 150 } |
| 151 |
| 152 std::vector<const WebstoreBundle::Item*> WebstoreBundle::GetItemsInState( |
| 153 Item::State state, int matcher) const { |
| 154 std::vector<const Item*> result; |
| 155 for (size_t i = 0; i < items_.size(); ++i) { |
| 156 if (items_[i]->state() == state) { |
| 157 if (matcher == MATCH_ALL) { |
| 158 result.push_back(items_[i]); |
| 159 continue; |
| 160 } |
| 161 |
| 162 if (!items_[i]->dummy_extension().get()) |
| 163 continue; |
| 164 |
| 165 bool is_app = items_[i]->dummy_extension()->is_app(); |
| 166 if (!is_app && (matcher & MATCH_EXTENSIONS)) { |
| 167 result.push_back(items_[i]); |
| 168 continue; |
| 169 } |
| 170 if (is_app && (matcher & MATCH_APPS)) { |
| 171 result.push_back(items_[i]); |
| 172 continue; |
| 173 } |
| 174 } |
| 175 } |
| 176 |
| 177 return result; |
| 178 } |
| 179 |
| 180 string16 WebstoreBundle::GetHeadingTextForPrompt() const { |
| 181 bool has_app = HasAppsInState(Item::STATE_PARSED); |
| 182 bool has_extension = HasExtensionsInState(Item::STATE_PARSED); |
| 183 |
| 184 int msg_id = 0; |
| 185 if (has_app && has_extension) |
| 186 msg_id = IDS_EXTENSION_BUNDLE_INSTALL_PROMPT_HEADING_EXTENSION_APPS; |
| 187 else if (has_app) |
| 188 msg_id = IDS_EXTENSION_BUNDLE_INSTALL_PROMPT_HEADING_APPS; |
| 189 else if (has_extension) |
| 190 msg_id = IDS_EXTENSION_BUNDLE_INSTALL_PROMPT_HEADING_EXTENSIONS; |
| 191 else |
| 192 NOTREACHED(); |
| 193 |
| 194 return l10n_util::GetStringUTF16(msg_id); |
| 195 } |
| 196 |
| 197 string16 WebstoreBundle::GetInstalledHeadingTextForBubble() const { |
| 198 bool installed_apps = HasAppsInState(Item::STATE_INSTALLED); |
| 199 bool installed_extensions = HasExtensionsInState(Item::STATE_INSTALLED); |
| 200 |
| 201 int msg_id = 0; |
| 202 if (installed_apps && installed_extensions) |
| 203 msg_id = IDS_EXTENSION_BUNDLE_INSTALLED_HEADING_EXTENSION_APPS; |
| 204 else if (installed_apps) |
| 205 msg_id = IDS_EXTENSION_BUNDLE_INSTALLED_HEADING_APPS; |
| 206 else if (installed_extensions) |
| 207 msg_id = IDS_EXTENSION_BUNDLE_INSTALLED_HEADING_EXTENSIONS; |
| 208 else |
| 209 return string16(); |
| 210 |
| 211 return l10n_util::GetStringUTF16(msg_id); |
| 212 } |
| 213 |
| 214 string16 WebstoreBundle::GetFailedHeadingTextForBubble() const { |
| 215 if (GetItemsInState(WebstoreBundle::Item::STATE_NOT_INSTALLED, |
| 216 WebstoreBundle::MATCH_ALL).size()) { |
| 217 return l10n_util::GetStringUTF16(IDS_EXTENSION_BUNDLE_ERROR_HEADING); |
| 218 } else { |
| 219 return string16(); |
| 220 } |
| 221 } |
| 222 |
| 223 bool WebstoreBundle::HasAppsInState(Item::State state) const { |
| 224 return GetItemsInState(state, MATCH_APPS).size() > 0; |
| 225 } |
| 226 |
| 227 bool WebstoreBundle::HasExtensionsInState(Item::State state) const { |
| 228 return GetItemsInState(state, MATCH_EXTENSIONS).size() > 0; |
| 229 } |
| 230 |
| 231 void WebstoreBundle::OnWebstoreParseSuccess( |
| 232 const std::string& id, |
| 233 const SkBitmap& icon, |
| 234 base::DictionaryValue* parsed_manifest) { |
| 235 GetItemById(id)->SetDummyExtension(parsed_manifest); |
| 236 ShowPromptIfDoneParsing(); |
| 237 } |
| 238 |
| 239 void WebstoreBundle::OnWebstoreParseFailure( |
| 240 const std::string& id, |
| 241 WebstoreInstallHelper::Delegate::InstallHelperResultCode result_code, |
| 242 const std::string& error_message) { |
| 243 GetItemById(id)->MarkNotInstalled(); |
| 244 ShowPromptIfDoneParsing(); |
| 245 } |
| 246 |
| 247 void WebstoreBundle::InstallUIProceed() { |
| 248 for (size_t i = 0; i < items_.size(); ++i) { |
| 249 Item* item = items_[i]; |
| 250 if (item->state() == Item::STATE_PARSED) { |
| 251 item->MarkApproved(); |
| 252 |
| 253 // Create a whitelist entry for each of the approved extensions. |
| 254 CrxInstaller::WhitelistEntry* entry = new CrxInstaller::WhitelistEntry; |
| 255 entry->parsed_manifest.reset(item->parsed_manifest()->DeepCopy()); |
| 256 entry->localized_name = item->localized_name(); |
| 257 entry->use_app_installed_bubble = false; |
| 258 entry->skip_post_install_ui = true; |
| 259 CrxInstaller::SetWhitelistEntry(item->id(), entry); |
| 260 } |
| 261 } |
| 262 |
| 263 ReportApproved(); |
| 264 } |
| 265 |
| 266 void WebstoreBundle::InstallUIAbort(bool user_initiated) { |
| 267 for (size_t i = 0; i < items_.size(); ++i) |
| 268 items_[i]->MarkNotInstalled(); |
| 269 |
| 270 ReportCanceled(user_initiated); |
| 271 } |
| 272 |
| 273 void WebstoreBundle::OnExtensionInstallSuccess(const std::string& id) { |
| 274 GetItemById(id)->MarkInstalled(); |
| 275 InstallNextItem(); |
| 276 } |
| 277 |
| 278 void WebstoreBundle::OnExtensionInstallFailure(const std::string& id, |
| 279 const std::string& error) { |
| 280 GetItemById(id)->MarkNotInstalled(); |
| 281 InstallNextItem(); |
| 282 } |
| 283 |
| 284 WebstoreBundle::Item* WebstoreBundle::GetItemById(const std::string& id) { |
| 285 for (size_t i = 0; i < items_.size(); ++i) { |
| 286 if (items_[i]->id() == id) |
| 287 return items_[i]; |
| 288 } |
| 289 NOTREACHED(); |
| 290 return NULL; |
| 291 } |
| 292 |
| 293 void WebstoreBundle::ParseManifests() { |
| 294 bool at_least_one_item = false; |
| 295 for (size_t i = 0; i < items_.size(); ++i) { |
| 296 Item* item = items_[i]; |
| 297 if (item->state() != Item::STATE_INITIAL) |
| 298 continue; |
| 299 |
| 300 at_least_one_item = true; |
| 301 |
| 302 scoped_refptr<WebstoreInstallHelper> helper = new WebstoreInstallHelper( |
| 303 this, item->id(), item->manifest(), "", GURL(), NULL); |
| 304 helper->Start(); |
| 305 } |
| 306 |
| 307 if (!at_least_one_item) |
| 308 ReportCanceled(false); |
| 309 } |
| 310 |
| 311 void WebstoreBundle::ShowPromptIfDoneParsing() { |
| 312 for (size_t i = 0; i < items_.size(); ++i) { |
| 313 if (items_[i]->state() == Item::STATE_INITIAL) |
| 314 return; |
| 315 } |
| 316 |
| 317 ShowPrompt(); |
| 318 } |
| 319 |
| 320 void WebstoreBundle::ShowPrompt() { |
| 321 scoped_refptr<ExtensionPermissionSet> permissions; |
| 322 bool at_least_one_item = false; |
| 323 |
| 324 for (size_t i = 0; i < items_.size(); ++i) { |
| 325 Item* item = items_[i]; |
| 326 CHECK_NE(Item::STATE_INITIAL, item->state()); |
| 327 if (item->state() == Item::STATE_PARSED) { |
| 328 at_least_one_item = true; |
| 329 permissions = ExtensionPermissionSet::CreateUnion( |
| 330 permissions, item->dummy_extension()->required_permission_set()); |
| 331 } |
| 332 } |
| 333 |
| 334 if (at_least_one_item) { |
| 335 // TODO(jstritar): Show the confirmation prompt, which was left out to |
| 336 // limit the scope of this CL. |
| 337 if (auto_approve_for_test == PROCEED) |
| 338 InstallUIProceed(); |
| 339 else if (auto_approve_for_test == ABORT) |
| 340 InstallUIAbort(true); |
| 341 } else { |
| 342 ReportCanceled(false); |
| 343 } |
| 344 } |
| 345 |
| 346 void WebstoreBundle::ShowInstalledBubbleIfDone() { |
| 347 CHECK_LT(Item::STATE_INSTALLED, Item::STATE_NOT_INSTALLED); |
| 348 for (size_t i = 0; i < items_.size(); ++i) { |
| 349 if (items_[i]->state() < Item::STATE_INSTALLED) |
| 350 return; |
| 351 } |
| 352 |
| 353 Browser* browser = BrowserList::GetLastActiveWithProfile(profile_); |
| 354 if (browser) |
| 355 ShowInstalledBubble(this, browser); |
| 356 |
| 357 ReportComplete(); |
| 358 } |
| 359 |
| 360 // TODO(jstritar): remove this after landing the bubble implementations. |
| 361 // static |
| 362 void WebstoreBundle::ShowInstalledBubble( |
| 363 const WebstoreBundle* bundle, Browser* browser) {} |
OLD | NEW |