Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / extensions / bundle_installer.cc
blob88cf2394447f091b871934238fa11e4ad632051a
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.
5 #include "chrome/browser/extensions/bundle_installer.h"
7 #include <algorithm>
8 #include <string>
9 #include <vector>
11 #include "base/command_line.h"
12 #include "base/i18n/rtl.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/values.h"
15 #include "chrome/browser/extensions/crx_installer.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/ui/browser.h"
18 #include "chrome/browser/ui/browser_finder.h"
19 #include "chrome/browser/ui/browser_list.h"
20 #include "chrome/browser/ui/tabs/tab_strip_model.h"
21 #include "chrome/common/chrome_switches.h"
22 #include "content/public/browser/navigation_controller.h"
23 #include "content/public/browser/web_contents.h"
24 #include "extensions/common/extension.h"
25 #include "extensions/common/permissions/permission_set.h"
26 #include "extensions/common/permissions/permissions_data.h"
27 #include "grit/generated_resources.h"
28 #include "ui/base/l10n/l10n_util.h"
30 using content::NavigationController;
32 namespace extensions {
34 namespace {
36 enum AutoApproveForTest {
37 DO_NOT_SKIP = 0,
38 PROCEED,
39 ABORT
42 AutoApproveForTest g_auto_approve_for_test = DO_NOT_SKIP;
44 // Creates a dummy extension and sets the manifest's name to the item's
45 // localized name.
46 scoped_refptr<Extension> CreateDummyExtension(const BundleInstaller::Item& item,
47 base::DictionaryValue* manifest) {
48 // We require localized names so we can have nice error messages when we can't
49 // parse an extension manifest.
50 CHECK(!item.localized_name.empty());
52 std::string error;
53 return Extension::Create(base::FilePath(),
54 Manifest::INTERNAL,
55 *manifest,
56 Extension::NO_FLAGS,
57 item.id,
58 &error);
61 bool IsAppPredicate(scoped_refptr<const Extension> extension) {
62 return extension->is_app();
65 struct MatchIdFunctor {
66 explicit MatchIdFunctor(const std::string& id) : id(id) {}
67 bool operator()(scoped_refptr<const Extension> extension) {
68 return extension->id() == id;
70 std::string id;
73 // Holds the message IDs for BundleInstaller::GetHeadingTextFor.
74 const int kHeadingIds[3][4] = {
77 IDS_EXTENSION_BUNDLE_INSTALL_PROMPT_HEADING_EXTENSIONS,
78 IDS_EXTENSION_BUNDLE_INSTALL_PROMPT_HEADING_APPS,
79 IDS_EXTENSION_BUNDLE_INSTALL_PROMPT_HEADING_EXTENSION_APPS
83 IDS_EXTENSION_BUNDLE_INSTALLED_HEADING_EXTENSIONS,
84 IDS_EXTENSION_BUNDLE_INSTALLED_HEADING_APPS,
85 IDS_EXTENSION_BUNDLE_INSTALLED_HEADING_EXTENSION_APPS
89 } // namespace
91 // static
92 void BundleInstaller::SetAutoApproveForTesting(bool auto_approve) {
93 CHECK(CommandLine::ForCurrentProcess()->HasSwitch(switches::kTestType));
94 g_auto_approve_for_test = auto_approve ? PROCEED : ABORT;
97 BundleInstaller::Item::Item() : state(STATE_PENDING) {}
99 base::string16 BundleInstaller::Item::GetNameForDisplay() {
100 base::string16 name = base::UTF8ToUTF16(localized_name);
101 base::i18n::AdjustStringForLocaleDirection(&name);
102 return l10n_util::GetStringFUTF16(IDS_EXTENSION_PERMISSION_LINE, name);
105 BundleInstaller::BundleInstaller(Browser* browser,
106 const BundleInstaller::ItemList& items)
107 : approved_(false),
108 browser_(browser),
109 host_desktop_type_(browser->host_desktop_type()),
110 profile_(browser->profile()),
111 delegate_(NULL) {
112 BrowserList::AddObserver(this);
113 for (size_t i = 0; i < items.size(); ++i) {
114 items_[items[i].id] = items[i];
115 items_[items[i].id].state = Item::STATE_PENDING;
119 BundleInstaller::ItemList BundleInstaller::GetItemsWithState(
120 Item::State state) const {
121 ItemList list;
123 for (ItemMap::const_iterator i = items_.begin(); i != items_.end(); ++i) {
124 if (i->second.state == state)
125 list.push_back(i->second);
128 return list;
131 void BundleInstaller::PromptForApproval(Delegate* delegate) {
132 delegate_ = delegate;
134 AddRef(); // Balanced in ReportApproved() and ReportCanceled().
136 ParseManifests();
139 void BundleInstaller::CompleteInstall(NavigationController* controller,
140 Delegate* delegate) {
141 CHECK(approved_);
143 delegate_ = delegate;
145 AddRef(); // Balanced in ReportComplete();
147 if (GetItemsWithState(Item::STATE_PENDING).empty()) {
148 ReportComplete();
149 return;
152 // Start each WebstoreInstaller.
153 for (ItemMap::iterator i = items_.begin(); i != items_.end(); ++i) {
154 if (i->second.state != Item::STATE_PENDING)
155 continue;
157 // Since we've already confirmed the permissions, create an approval that
158 // lets CrxInstaller bypass the prompt.
159 scoped_ptr<WebstoreInstaller::Approval> approval(
160 WebstoreInstaller::Approval::CreateWithNoInstallPrompt(
161 profile_,
162 i->first,
163 scoped_ptr<base::DictionaryValue>(
164 parsed_manifests_[i->first]->DeepCopy()), true));
165 approval->use_app_installed_bubble = false;
166 approval->skip_post_install_ui = true;
168 scoped_refptr<WebstoreInstaller> installer = new WebstoreInstaller(
169 profile_,
170 this,
171 controller,
172 i->first,
173 approval.Pass(),
174 WebstoreInstaller::INSTALL_SOURCE_OTHER);
175 installer->Start();
179 base::string16 BundleInstaller::GetHeadingTextFor(Item::State state) const {
180 // For STATE_FAILED, we can't tell if the items were apps or extensions
181 // so we always show the same message.
182 if (state == Item::STATE_FAILED) {
183 if (GetItemsWithState(state).size())
184 return l10n_util::GetStringUTF16(IDS_EXTENSION_BUNDLE_ERROR_HEADING);
185 return base::string16();
188 size_t total = GetItemsWithState(state).size();
189 size_t apps = std::count_if(
190 dummy_extensions_.begin(), dummy_extensions_.end(), &IsAppPredicate);
192 bool has_apps = apps > 0;
193 bool has_extensions = apps < total;
194 size_t index = (has_extensions << 0) + (has_apps << 1);
196 CHECK_LT(static_cast<size_t>(state), arraysize(kHeadingIds));
197 CHECK_LT(index, arraysize(kHeadingIds[state]));
199 int msg_id = kHeadingIds[state][index];
200 if (!msg_id)
201 return base::string16();
203 return l10n_util::GetStringUTF16(msg_id);
206 BundleInstaller::~BundleInstaller() {
207 BrowserList::RemoveObserver(this);
210 void BundleInstaller::ParseManifests() {
211 if (items_.empty()) {
212 ReportCanceled(false);
213 return;
216 for (ItemMap::iterator i = items_.begin(); i != items_.end(); ++i) {
217 scoped_refptr<WebstoreInstallHelper> helper = new WebstoreInstallHelper(
218 this, i->first, i->second.manifest, std::string(), GURL(), NULL);
219 helper->Start();
223 void BundleInstaller::ReportApproved() {
224 if (delegate_)
225 delegate_->OnBundleInstallApproved();
227 Release(); // Balanced in PromptForApproval().
230 void BundleInstaller::ReportCanceled(bool user_initiated) {
231 if (delegate_)
232 delegate_->OnBundleInstallCanceled(user_initiated);
234 Release(); // Balanced in PromptForApproval().
237 void BundleInstaller::ReportComplete() {
238 if (delegate_)
239 delegate_->OnBundleInstallCompleted();
241 Release(); // Balanced in CompleteInstall().
244 void BundleInstaller::ShowPromptIfDoneParsing() {
245 // We don't prompt until all the manifests have been parsed.
246 ItemList pending_items = GetItemsWithState(Item::STATE_PENDING);
247 if (pending_items.size() != dummy_extensions_.size())
248 return;
250 ShowPrompt();
253 void BundleInstaller::ShowPrompt() {
254 // Abort if we couldn't create any Extensions out of the manifests.
255 if (dummy_extensions_.empty()) {
256 ReportCanceled(false);
257 return;
260 scoped_refptr<PermissionSet> permissions;
261 for (size_t i = 0; i < dummy_extensions_.size(); ++i) {
262 permissions = PermissionSet::CreateUnion(
263 permissions.get(),
264 PermissionsData::GetRequiredPermissions(dummy_extensions_[i].get()));
267 if (g_auto_approve_for_test == PROCEED) {
268 InstallUIProceed();
269 } else if (g_auto_approve_for_test == ABORT) {
270 InstallUIAbort(true);
271 } else {
272 Browser* browser = browser_;
273 if (!browser) {
274 // The browser that we got initially could have gone away during our
275 // thread hopping.
276 browser = chrome::FindLastActiveWithProfile(profile_, host_desktop_type_);
278 content::WebContents* web_contents = NULL;
279 if (browser)
280 web_contents = browser->tab_strip_model()->GetActiveWebContents();
281 install_ui_.reset(new ExtensionInstallPrompt(web_contents));
282 install_ui_->ConfirmBundleInstall(this, permissions.get());
286 void BundleInstaller::ShowInstalledBubbleIfDone() {
287 // We're ready to show the installed bubble when no items are pending.
288 if (!GetItemsWithState(Item::STATE_PENDING).empty())
289 return;
291 if (browser_)
292 ShowInstalledBubble(this, browser_);
294 ReportComplete();
297 void BundleInstaller::OnWebstoreParseSuccess(
298 const std::string& id,
299 const SkBitmap& icon,
300 base::DictionaryValue* manifest) {
301 dummy_extensions_.push_back(CreateDummyExtension(items_[id], manifest));
302 parsed_manifests_[id] = linked_ptr<base::DictionaryValue>(manifest);
304 ShowPromptIfDoneParsing();
307 void BundleInstaller::OnWebstoreParseFailure(
308 const std::string& id,
309 WebstoreInstallHelper::Delegate::InstallHelperResultCode result_code,
310 const std::string& error_message) {
311 items_[id].state = Item::STATE_FAILED;
313 ShowPromptIfDoneParsing();
316 void BundleInstaller::InstallUIProceed() {
317 approved_ = true;
318 ReportApproved();
321 void BundleInstaller::InstallUIAbort(bool user_initiated) {
322 for (ItemMap::iterator i = items_.begin(); i != items_.end(); ++i)
323 i->second.state = Item::STATE_FAILED;
325 ReportCanceled(user_initiated);
328 void BundleInstaller::OnExtensionInstallSuccess(const std::string& id) {
329 items_[id].state = Item::STATE_INSTALLED;
331 ShowInstalledBubbleIfDone();
334 void BundleInstaller::OnExtensionInstallFailure(
335 const std::string& id,
336 const std::string& error,
337 WebstoreInstaller::FailureReason reason) {
338 items_[id].state = Item::STATE_FAILED;
340 ExtensionList::iterator i = std::find_if(
341 dummy_extensions_.begin(), dummy_extensions_.end(), MatchIdFunctor(id));
342 CHECK(dummy_extensions_.end() != i);
343 dummy_extensions_.erase(i);
345 ShowInstalledBubbleIfDone();
348 void BundleInstaller::OnBrowserAdded(Browser* browser) {}
350 void BundleInstaller::OnBrowserRemoved(Browser* browser) {
351 if (browser_ == browser)
352 browser_ = NULL;
355 void BundleInstaller::OnBrowserSetLastActive(Browser* browser) {}
357 } // namespace extensions