Add ENABLE_MEDIA_ROUTER define to builds other than Android and iOS.
[chromium-blink-merge.git] / chrome / browser / extensions / bundle_installer.cc
blob898bb23141c27dbdca839a2e6379e4dc11214a08
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"
29 #include "ui/gfx/image/image_skia.h"
31 namespace extensions {
33 namespace {
35 enum AutoApproveForTest {
36 DO_NOT_SKIP = 0,
37 PROCEED,
38 ABORT
41 AutoApproveForTest g_auto_approve_for_test = DO_NOT_SKIP;
43 scoped_refptr<Extension> CreateDummyExtension(
44 const BundleInstaller::Item& item,
45 const base::DictionaryValue& manifest,
46 content::BrowserContext* browser_context) {
47 // We require localized names so we can have nice error messages when we can't
48 // parse an extension manifest.
49 CHECK(!item.localized_name.empty());
51 std::string error;
52 scoped_refptr<Extension> extension = Extension::Create(base::FilePath(),
53 Manifest::INTERNAL,
54 manifest,
55 Extension::NO_FLAGS,
56 item.id,
57 &error);
58 // Initialize permissions so that withheld permissions are displayed properly
59 // in the install prompt.
60 PermissionsUpdater(browser_context, PermissionsUpdater::INIT_FLAG_TRANSIENT)
61 .InitializePermissions(extension.get());
62 return extension;
65 // Holds the message IDs for BundleInstaller::GetHeadingTextFor.
66 const int kHeadingIds[3][4] = {
69 IDS_EXTENSION_BUNDLE_INSTALL_PROMPT_HEADING_EXTENSIONS,
70 IDS_EXTENSION_BUNDLE_INSTALL_PROMPT_HEADING_APPS,
71 IDS_EXTENSION_BUNDLE_INSTALL_PROMPT_HEADING_EXTENSION_APPS
75 IDS_EXTENSION_BUNDLE_INSTALLED_HEADING_EXTENSIONS,
76 IDS_EXTENSION_BUNDLE_INSTALLED_HEADING_APPS,
77 IDS_EXTENSION_BUNDLE_INSTALLED_HEADING_EXTENSION_APPS
81 } // namespace
83 // static
84 void BundleInstaller::SetAutoApproveForTesting(bool auto_approve) {
85 CHECK(base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kTestType));
86 g_auto_approve_for_test = auto_approve ? PROCEED : ABORT;
89 BundleInstaller::Item::Item() : state(STATE_PENDING) {}
91 BundleInstaller::Item::~Item() {}
93 base::string16 BundleInstaller::Item::GetNameForDisplay() const {
94 base::string16 name = base::UTF8ToUTF16(localized_name);
95 base::i18n::AdjustStringForLocaleDirection(&name);
96 return l10n_util::GetStringFUTF16(IDS_EXTENSION_PERMISSION_LINE, name);
99 BundleInstaller::BundleInstaller(Browser* browser,
100 const std::string& name,
101 const SkBitmap& icon,
102 const std::string& authuser,
103 const BundleInstaller::ItemList& items)
104 : approved_(false),
105 browser_(browser),
106 name_(name),
107 icon_(icon),
108 authuser_(authuser),
109 host_desktop_type_(browser->host_desktop_type()),
110 profile_(browser->profile()) {
111 BrowserList::AddObserver(this);
112 for (size_t i = 0; i < items.size(); ++i) {
113 items_[items[i].id] = items[i];
114 items_[items[i].id].state = Item::STATE_PENDING;
118 BundleInstaller::~BundleInstaller() {
119 BrowserList::RemoveObserver(this);
122 BundleInstaller::ItemList BundleInstaller::GetItemsWithState(
123 Item::State state) const {
124 ItemList list;
126 for (const std::pair<std::string, Item>& entry : items_) {
127 if (entry.second.state == state)
128 list.push_back(entry.second);
131 return list;
134 bool BundleInstaller::HasItemWithState(Item::State state) const {
135 return CountItemsWithState(state) > 0;
138 size_t BundleInstaller::CountItemsWithState(Item::State state) const {
139 return std::count_if(items_.begin(), items_.end(),
140 [state] (const std::pair<std::string, Item>& entry) {
141 return entry.second.state == state;
145 void BundleInstaller::PromptForApproval(const ApprovalCallback& callback) {
146 approval_callback_ = callback;
148 ParseManifests();
151 void BundleInstaller::CompleteInstall(content::WebContents* web_contents,
152 const base::Closure& callback) {
153 DCHECK(web_contents);
154 CHECK(approved_);
156 install_callback_ = callback;
158 if (!HasItemWithState(Item::STATE_PENDING)) {
159 install_callback_.Run();
160 return;
163 // Start each WebstoreInstaller.
164 for (const std::pair<std::string, Item>& entry : items_) {
165 if (entry.second.state != Item::STATE_PENDING)
166 continue;
168 // Since we've already confirmed the permissions, create an approval that
169 // lets CrxInstaller bypass the prompt.
170 scoped_ptr<WebstoreInstaller::Approval> approval(
171 WebstoreInstaller::Approval::CreateWithNoInstallPrompt(
172 profile_,
173 entry.first,
174 make_scoped_ptr(parsed_manifests_[entry.first]->DeepCopy()),
175 true));
176 approval->use_app_installed_bubble = false;
177 approval->skip_post_install_ui = true;
178 approval->authuser = authuser_;
179 approval->installing_icon =
180 gfx::ImageSkia::CreateFrom1xBitmap(entry.second.icon);
182 scoped_refptr<WebstoreInstaller> installer = new WebstoreInstaller(
183 profile_,
184 this,
185 web_contents,
186 entry.first,
187 approval.Pass(),
188 WebstoreInstaller::INSTALL_SOURCE_OTHER);
189 installer->Start();
193 base::string16 BundleInstaller::GetHeadingTextFor(Item::State state) const {
194 // For STATE_FAILED, we can't tell if the items were apps or extensions
195 // so we always show the same message.
196 if (state == Item::STATE_FAILED) {
197 if (HasItemWithState(state))
198 return l10n_util::GetStringUTF16(IDS_EXTENSION_BUNDLE_ERROR_HEADING);
199 return base::string16();
202 size_t total = CountItemsWithState(state);
203 size_t apps = std::count_if(
204 dummy_extensions_.begin(), dummy_extensions_.end(),
205 [] (const scoped_refptr<const Extension>& ext) { return ext->is_app(); });
207 bool has_apps = apps > 0;
208 bool has_extensions = apps < total;
209 size_t index = (has_extensions << 0) + (has_apps << 1);
211 CHECK_LT(static_cast<size_t>(state), arraysize(kHeadingIds));
212 CHECK_LT(index, arraysize(kHeadingIds[state]));
214 int msg_id = kHeadingIds[state][index];
215 if (!msg_id)
216 return base::string16();
218 // Only the "pending" message (in the confirmation prompt) contains the
219 // bundle name.
220 if (state == Item::STATE_PENDING)
221 return l10n_util::GetStringFUTF16(msg_id, base::UTF8ToUTF16(name_));
223 return l10n_util::GetStringUTF16(msg_id);
226 void BundleInstaller::ParseManifests() {
227 if (items_.empty()) {
228 approval_callback_.Run(APPROVAL_ERROR);
229 return;
232 net::URLRequestContextGetter* context_getter =
233 browser_ ? browser_->profile()->GetRequestContext() : nullptr;
235 for (const std::pair<std::string, Item>& entry : items_) {
236 scoped_refptr<WebstoreInstallHelper> helper = new WebstoreInstallHelper(
237 this, entry.first, entry.second.manifest, entry.second.icon_url,
238 context_getter);
239 helper->Start();
243 void BundleInstaller::ShowPromptIfDoneParsing() {
244 // We don't prompt until all the manifests have been parsed.
245 if (CountItemsWithState(Item::STATE_PENDING) != dummy_extensions_.size())
246 return;
248 ShowPrompt();
251 void BundleInstaller::ShowPrompt() {
252 // Abort if we couldn't create any Extensions out of the manifests.
253 if (dummy_extensions_.empty()) {
254 approval_callback_.Run(APPROVAL_ERROR);
255 return;
258 scoped_refptr<PermissionSet> permissions;
259 for (size_t i = 0; i < dummy_extensions_.size(); ++i) {
260 permissions = PermissionSet::CreateUnion(
261 permissions.get(),
262 dummy_extensions_[i]->permissions_data()->active_permissions().get());
265 if (g_auto_approve_for_test == PROCEED) {
266 InstallUIProceed();
267 } else if (g_auto_approve_for_test == ABORT) {
268 InstallUIAbort(true);
269 } else {
270 Browser* browser = browser_;
271 if (!browser) {
272 // The browser that we got initially could have gone away during our
273 // thread hopping.
274 browser = chrome::FindLastActiveWithProfile(profile_, host_desktop_type_);
276 content::WebContents* web_contents = NULL;
277 if (browser)
278 web_contents = browser->tab_strip_model()->GetActiveWebContents();
279 install_ui_.reset(new ExtensionInstallPrompt(web_contents));
280 install_ui_->ConfirmBundleInstall(this, permissions.get());
284 void BundleInstaller::ShowInstalledBubbleIfDone() {
285 // We're ready to show the installed bubble when no items are pending.
286 if (HasItemWithState(Item::STATE_PENDING))
287 return;
289 if (browser_)
290 ShowInstalledBubble(this, browser_);
292 install_callback_.Run();
295 void BundleInstaller::OnWebstoreParseSuccess(
296 const std::string& id,
297 const SkBitmap& icon,
298 base::DictionaryValue* manifest) {
299 items_[id].icon = icon;
300 dummy_extensions_.push_back(
301 CreateDummyExtension(items_[id], *manifest, profile_));
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 approval_callback_.Run(APPROVED);
321 void BundleInstaller::InstallUIAbort(bool user_initiated) {
322 for (std::pair<const std::string, Item>& entry : items_)
323 entry.second.state = Item::STATE_FAILED;
325 approval_callback_.Run(user_initiated ? USER_CANCELED : APPROVAL_ERROR);
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(),
342 [&id] (const scoped_refptr<const Extension>& ext) {
343 return ext->id() == id;
345 CHECK(dummy_extensions_.end() != i);
346 dummy_extensions_.erase(i);
348 ShowInstalledBubbleIfDone();
351 void BundleInstaller::OnBrowserRemoved(Browser* browser) {
352 if (browser_ == browser)
353 browser_ = nullptr;
356 } // namespace extensions