MacViews: Get c/b/ui/views/tabs to build on Mac
[chromium-blink-merge.git] / chrome / browser / extensions / bundle_installer.cc
blobd4922291d7269777a3fa7ecbecf4d23714d967a9
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/extensions/permissions_updater.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/browser/ui/browser.h"
19 #include "chrome/browser/ui/browser_finder.h"
20 #include "chrome/browser/ui/browser_list.h"
21 #include "chrome/browser/ui/tabs/tab_strip_model.h"
22 #include "chrome/common/chrome_switches.h"
23 #include "chrome/grit/generated_resources.h"
24 #include "content/public/browser/web_contents.h"
25 #include "extensions/common/extension.h"
26 #include "extensions/common/permissions/permission_set.h"
27 #include "extensions/common/permissions/permissions_data.h"
28 #include "ui/base/l10n/l10n_util.h"
30 namespace extensions {
32 namespace {
34 enum AutoApproveForTest {
35 DO_NOT_SKIP = 0,
36 PROCEED,
37 ABORT
40 AutoApproveForTest g_auto_approve_for_test = DO_NOT_SKIP;
42 // Creates a dummy extension and sets the manifest's name to the item's
43 // localized name.
44 scoped_refptr<Extension> CreateDummyExtension(
45 const BundleInstaller::Item& item,
46 base::DictionaryValue* manifest,
47 content::BrowserContext* browser_context) {
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 scoped_refptr<Extension> extension = Extension::Create(base::FilePath(),
54 Manifest::INTERNAL,
55 *manifest,
56 Extension::NO_FLAGS,
57 item.id,
58 &error);
59 // Initialize permissions so that withheld permissions are displayed properly
60 // in the install prompt.
61 PermissionsUpdater(browser_context, PermissionsUpdater::INIT_FLAG_TRANSIENT)
62 .InitializePermissions(extension.get());
63 return extension;
66 bool IsAppPredicate(scoped_refptr<const Extension> extension) {
67 return extension->is_app();
70 struct MatchIdFunctor {
71 explicit MatchIdFunctor(const std::string& id) : id(id) {}
72 bool operator()(scoped_refptr<const Extension> extension) {
73 return extension->id() == id;
75 std::string id;
78 // Holds the message IDs for BundleInstaller::GetHeadingTextFor.
79 const int kHeadingIds[3][4] = {
82 IDS_EXTENSION_BUNDLE_INSTALL_PROMPT_HEADING_EXTENSIONS,
83 IDS_EXTENSION_BUNDLE_INSTALL_PROMPT_HEADING_APPS,
84 IDS_EXTENSION_BUNDLE_INSTALL_PROMPT_HEADING_EXTENSION_APPS
88 IDS_EXTENSION_BUNDLE_INSTALLED_HEADING_EXTENSIONS,
89 IDS_EXTENSION_BUNDLE_INSTALLED_HEADING_APPS,
90 IDS_EXTENSION_BUNDLE_INSTALLED_HEADING_EXTENSION_APPS
94 } // namespace
96 // static
97 void BundleInstaller::SetAutoApproveForTesting(bool auto_approve) {
98 CHECK(CommandLine::ForCurrentProcess()->HasSwitch(switches::kTestType));
99 g_auto_approve_for_test = auto_approve ? PROCEED : ABORT;
102 BundleInstaller::Item::Item() : state(STATE_PENDING) {}
104 base::string16 BundleInstaller::Item::GetNameForDisplay() {
105 base::string16 name = base::UTF8ToUTF16(localized_name);
106 base::i18n::AdjustStringForLocaleDirection(&name);
107 return l10n_util::GetStringFUTF16(IDS_EXTENSION_PERMISSION_LINE, name);
110 BundleInstaller::BundleInstaller(Browser* browser,
111 const BundleInstaller::ItemList& items)
112 : approved_(false),
113 browser_(browser),
114 host_desktop_type_(browser->host_desktop_type()),
115 profile_(browser->profile()),
116 delegate_(NULL) {
117 BrowserList::AddObserver(this);
118 for (size_t i = 0; i < items.size(); ++i) {
119 items_[items[i].id] = items[i];
120 items_[items[i].id].state = Item::STATE_PENDING;
124 BundleInstaller::ItemList BundleInstaller::GetItemsWithState(
125 Item::State state) const {
126 ItemList list;
128 for (ItemMap::const_iterator i = items_.begin(); i != items_.end(); ++i) {
129 if (i->second.state == state)
130 list.push_back(i->second);
133 return list;
136 void BundleInstaller::PromptForApproval(Delegate* delegate) {
137 delegate_ = delegate;
139 AddRef(); // Balanced in ReportApproved() and ReportCanceled().
141 ParseManifests();
144 void BundleInstaller::CompleteInstall(content::WebContents* web_contents,
145 Delegate* delegate) {
146 CHECK(approved_);
148 delegate_ = delegate;
150 AddRef(); // Balanced in ReportComplete();
152 if (GetItemsWithState(Item::STATE_PENDING).empty()) {
153 ReportComplete();
154 return;
157 // Start each WebstoreInstaller.
158 for (ItemMap::iterator i = items_.begin(); i != items_.end(); ++i) {
159 if (i->second.state != Item::STATE_PENDING)
160 continue;
162 // Since we've already confirmed the permissions, create an approval that
163 // lets CrxInstaller bypass the prompt.
164 scoped_ptr<WebstoreInstaller::Approval> approval(
165 WebstoreInstaller::Approval::CreateWithNoInstallPrompt(
166 profile_,
167 i->first,
168 scoped_ptr<base::DictionaryValue>(
169 parsed_manifests_[i->first]->DeepCopy()), true));
170 approval->use_app_installed_bubble = false;
171 approval->skip_post_install_ui = true;
173 scoped_refptr<WebstoreInstaller> installer = new WebstoreInstaller(
174 profile_,
175 this,
176 web_contents,
177 i->first,
178 approval.Pass(),
179 WebstoreInstaller::INSTALL_SOURCE_OTHER);
180 installer->Start();
184 base::string16 BundleInstaller::GetHeadingTextFor(Item::State state) const {
185 // For STATE_FAILED, we can't tell if the items were apps or extensions
186 // so we always show the same message.
187 if (state == Item::STATE_FAILED) {
188 if (GetItemsWithState(state).size())
189 return l10n_util::GetStringUTF16(IDS_EXTENSION_BUNDLE_ERROR_HEADING);
190 return base::string16();
193 size_t total = GetItemsWithState(state).size();
194 size_t apps = std::count_if(
195 dummy_extensions_.begin(), dummy_extensions_.end(), &IsAppPredicate);
197 bool has_apps = apps > 0;
198 bool has_extensions = apps < total;
199 size_t index = (has_extensions << 0) + (has_apps << 1);
201 CHECK_LT(static_cast<size_t>(state), arraysize(kHeadingIds));
202 CHECK_LT(index, arraysize(kHeadingIds[state]));
204 int msg_id = kHeadingIds[state][index];
205 if (!msg_id)
206 return base::string16();
208 return l10n_util::GetStringUTF16(msg_id);
211 BundleInstaller::~BundleInstaller() {
212 BrowserList::RemoveObserver(this);
215 void BundleInstaller::ParseManifests() {
216 if (items_.empty()) {
217 ReportCanceled(false);
218 return;
221 for (ItemMap::iterator i = items_.begin(); i != items_.end(); ++i) {
222 scoped_refptr<WebstoreInstallHelper> helper = new WebstoreInstallHelper(
223 this, i->first, i->second.manifest, std::string(), GURL(), NULL);
224 helper->Start();
228 void BundleInstaller::ReportApproved() {
229 if (delegate_)
230 delegate_->OnBundleInstallApproved();
232 Release(); // Balanced in PromptForApproval().
235 void BundleInstaller::ReportCanceled(bool user_initiated) {
236 if (delegate_)
237 delegate_->OnBundleInstallCanceled(user_initiated);
239 Release(); // Balanced in PromptForApproval().
242 void BundleInstaller::ReportComplete() {
243 if (delegate_)
244 delegate_->OnBundleInstallCompleted();
246 Release(); // Balanced in CompleteInstall().
249 void BundleInstaller::ShowPromptIfDoneParsing() {
250 // We don't prompt until all the manifests have been parsed.
251 ItemList pending_items = GetItemsWithState(Item::STATE_PENDING);
252 if (pending_items.size() != dummy_extensions_.size())
253 return;
255 ShowPrompt();
258 void BundleInstaller::ShowPrompt() {
259 // Abort if we couldn't create any Extensions out of the manifests.
260 if (dummy_extensions_.empty()) {
261 ReportCanceled(false);
262 return;
265 scoped_refptr<PermissionSet> permissions;
266 for (size_t i = 0; i < dummy_extensions_.size(); ++i) {
267 permissions = PermissionSet::CreateUnion(
268 permissions.get(),
269 dummy_extensions_[i]->permissions_data()->active_permissions().get());
272 if (g_auto_approve_for_test == PROCEED) {
273 InstallUIProceed();
274 } else if (g_auto_approve_for_test == ABORT) {
275 InstallUIAbort(true);
276 } else {
277 Browser* browser = browser_;
278 if (!browser) {
279 // The browser that we got initially could have gone away during our
280 // thread hopping.
281 browser = chrome::FindLastActiveWithProfile(profile_, host_desktop_type_);
283 content::WebContents* web_contents = NULL;
284 if (browser)
285 web_contents = browser->tab_strip_model()->GetActiveWebContents();
286 install_ui_.reset(new ExtensionInstallPrompt(web_contents));
287 install_ui_->ConfirmBundleInstall(this, permissions.get());
291 void BundleInstaller::ShowInstalledBubbleIfDone() {
292 // We're ready to show the installed bubble when no items are pending.
293 if (!GetItemsWithState(Item::STATE_PENDING).empty())
294 return;
296 if (browser_)
297 ShowInstalledBubble(this, browser_);
299 ReportComplete();
302 void BundleInstaller::OnWebstoreParseSuccess(
303 const std::string& id,
304 const SkBitmap& icon,
305 base::DictionaryValue* manifest) {
306 dummy_extensions_.push_back(
307 CreateDummyExtension(items_[id], manifest, profile_));
308 parsed_manifests_[id] = linked_ptr<base::DictionaryValue>(manifest);
310 ShowPromptIfDoneParsing();
313 void BundleInstaller::OnWebstoreParseFailure(
314 const std::string& id,
315 WebstoreInstallHelper::Delegate::InstallHelperResultCode result_code,
316 const std::string& error_message) {
317 items_[id].state = Item::STATE_FAILED;
319 ShowPromptIfDoneParsing();
322 void BundleInstaller::InstallUIProceed() {
323 approved_ = true;
324 ReportApproved();
327 void BundleInstaller::InstallUIAbort(bool user_initiated) {
328 for (ItemMap::iterator i = items_.begin(); i != items_.end(); ++i)
329 i->second.state = Item::STATE_FAILED;
331 ReportCanceled(user_initiated);
334 void BundleInstaller::OnExtensionInstallSuccess(const std::string& id) {
335 items_[id].state = Item::STATE_INSTALLED;
337 ShowInstalledBubbleIfDone();
340 void BundleInstaller::OnExtensionInstallFailure(
341 const std::string& id,
342 const std::string& error,
343 WebstoreInstaller::FailureReason reason) {
344 items_[id].state = Item::STATE_FAILED;
346 ExtensionList::iterator i = std::find_if(
347 dummy_extensions_.begin(), dummy_extensions_.end(), MatchIdFunctor(id));
348 CHECK(dummy_extensions_.end() != i);
349 dummy_extensions_.erase(i);
351 ShowInstalledBubbleIfDone();
354 void BundleInstaller::OnBrowserAdded(Browser* browser) {}
356 void BundleInstaller::OnBrowserRemoved(Browser* browser) {
357 if (browser_ == browser)
358 browser_ = NULL;
361 void BundleInstaller::OnBrowserSetLastActive(Browser* browser) {}
363 } // namespace extensions