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

Side by Side 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, 1 month 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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) {}
OLDNEW
« 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