blob: 730b475c419ea6c8e2f33d9f08380ee3f0f4186e [file] [log] [blame]
[email protected]2894a512014-06-26 19:03:561// Copyright 2014 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/external_install_error.h"
6
7#include "base/bind.h"
8#include "base/strings/utf_string_conversions.h"
9#include "chrome/app/chrome_command_ids.h"
pkotwicz2f181782014-10-29 17:33:4510#include "chrome/browser/extensions/extension_install_prompt_show_params.h"
[email protected]2894a512014-06-26 19:03:5611#include "chrome/browser/extensions/extension_service.h"
12#include "chrome/browser/extensions/external_install_manager.h"
13#include "chrome/browser/extensions/webstore_data_fetcher.h"
14#include "chrome/browser/profiles/profile.h"
15#include "chrome/browser/ui/browser.h"
16#include "chrome/browser/ui/browser_finder.h"
17#include "chrome/browser/ui/global_error/global_error.h"
18#include "chrome/browser/ui/global_error/global_error_service.h"
19#include "chrome/browser/ui/global_error/global_error_service_factory.h"
20#include "chrome/browser/ui/tabs/tab_strip_model.h"
[email protected]af39f002014-08-22 10:18:1821#include "chrome/grit/generated_resources.h"
[email protected]2894a512014-06-26 19:03:5622#include "extensions/browser/extension_registry.h"
23#include "extensions/browser/extension_system.h"
[email protected]e43c61f2014-07-20 21:46:3424#include "extensions/browser/uninstall_reason.h"
[email protected]2894a512014-06-26 19:03:5625#include "extensions/common/constants.h"
26#include "extensions/common/extension.h"
[email protected]2894a512014-06-26 19:03:5627#include "ui/base/l10n/l10n_util.h"
28#include "ui/gfx/image/image.h"
29#include "ui/gfx/image/image_skia_operations.h"
30
31namespace extensions {
32
33namespace {
34
35// Return the menu label for a global error.
36base::string16 GetMenuItemLabel(const Extension* extension) {
37 if (!extension)
38 return base::string16();
39
40 int id = -1;
41 if (extension->is_app())
42 id = IDS_EXTENSION_EXTERNAL_INSTALL_ALERT_APP;
43 else if (extension->is_theme())
44 id = IDS_EXTENSION_EXTERNAL_INSTALL_ALERT_THEME;
45 else
46 id = IDS_EXTENSION_EXTERNAL_INSTALL_ALERT_EXTENSION;
47
48 return l10n_util::GetStringFUTF16(id, base::UTF8ToUTF16(extension->name()));
49}
50
51// A global error that spawns a dialog when the menu item is clicked.
52class ExternalInstallMenuAlert : public GlobalError {
53 public:
54 explicit ExternalInstallMenuAlert(ExternalInstallError* error);
dchengae36a4a2014-10-21 12:36:3655 ~ExternalInstallMenuAlert() override;
[email protected]2894a512014-06-26 19:03:5656
57 private:
58 // GlobalError implementation.
dchengae36a4a2014-10-21 12:36:3659 Severity GetSeverity() override;
60 bool HasMenuItem() override;
61 int MenuItemCommandID() override;
62 base::string16 MenuItemLabel() override;
63 void ExecuteMenuItem(Browser* browser) override;
64 bool HasBubbleView() override;
65 bool HasShownBubbleView() override;
66 void ShowBubbleView(Browser* browser) override;
67 GlobalErrorBubbleViewBase* GetBubbleView() override;
[email protected]2894a512014-06-26 19:03:5668
69 // The owning ExternalInstallError.
70 ExternalInstallError* error_;
71
72 DISALLOW_COPY_AND_ASSIGN(ExternalInstallMenuAlert);
73};
74
75// A global error that spawns a bubble when the menu item is clicked.
76class ExternalInstallBubbleAlert : public GlobalErrorWithStandardBubble {
77 public:
78 explicit ExternalInstallBubbleAlert(ExternalInstallError* error,
79 ExtensionInstallPrompt::Prompt* prompt);
dchengae36a4a2014-10-21 12:36:3680 ~ExternalInstallBubbleAlert() override;
[email protected]2894a512014-06-26 19:03:5681
82 private:
83 // GlobalError implementation.
dchengae36a4a2014-10-21 12:36:3684 Severity GetSeverity() override;
85 bool HasMenuItem() override;
86 int MenuItemCommandID() override;
87 base::string16 MenuItemLabel() override;
88 void ExecuteMenuItem(Browser* browser) override;
[email protected]2894a512014-06-26 19:03:5689
90 // GlobalErrorWithStandardBubble implementation.
dchengae36a4a2014-10-21 12:36:3691 gfx::Image GetBubbleViewIcon() override;
92 base::string16 GetBubbleViewTitle() override;
93 std::vector<base::string16> GetBubbleViewMessages() override;
94 base::string16 GetBubbleViewAcceptButtonLabel() override;
95 base::string16 GetBubbleViewCancelButtonLabel() override;
96 void OnBubbleViewDidClose(Browser* browser) override;
97 void BubbleViewAcceptButtonPressed(Browser* browser) override;
98 void BubbleViewCancelButtonPressed(Browser* browser) override;
[email protected]2894a512014-06-26 19:03:5699
100 // The owning ExternalInstallError.
101 ExternalInstallError* error_;
102
103 // The Prompt with all information, which we then use to populate the bubble.
104 ExtensionInstallPrompt::Prompt* prompt_;
105
106 DISALLOW_COPY_AND_ASSIGN(ExternalInstallBubbleAlert);
107};
108
109////////////////////////////////////////////////////////////////////////////////
110// ExternalInstallMenuAlert
111
112ExternalInstallMenuAlert::ExternalInstallMenuAlert(ExternalInstallError* error)
113 : error_(error) {
114}
115
116ExternalInstallMenuAlert::~ExternalInstallMenuAlert() {
117}
118
119GlobalError::Severity ExternalInstallMenuAlert::GetSeverity() {
120 return SEVERITY_LOW;
121}
122
123bool ExternalInstallMenuAlert::HasMenuItem() {
124 return true;
125}
126
127int ExternalInstallMenuAlert::MenuItemCommandID() {
128 return IDC_EXTERNAL_EXTENSION_ALERT;
129}
130
131base::string16 ExternalInstallMenuAlert::MenuItemLabel() {
132 return GetMenuItemLabel(error_->GetExtension());
133}
134
135void ExternalInstallMenuAlert::ExecuteMenuItem(Browser* browser) {
136 error_->ShowDialog(browser);
137}
138
139bool ExternalInstallMenuAlert::HasBubbleView() {
140 return false;
141}
142
143bool ExternalInstallMenuAlert::HasShownBubbleView() {
144 NOTREACHED();
145 return true;
146}
147
148void ExternalInstallMenuAlert::ShowBubbleView(Browser* browser) {
149 NOTREACHED();
150}
151
152GlobalErrorBubbleViewBase* ExternalInstallMenuAlert::GetBubbleView() {
153 return NULL;
154}
155
156////////////////////////////////////////////////////////////////////////////////
157// ExternalInstallBubbleAlert
158
159ExternalInstallBubbleAlert::ExternalInstallBubbleAlert(
160 ExternalInstallError* error,
161 ExtensionInstallPrompt::Prompt* prompt)
162 : error_(error), prompt_(prompt) {
163 DCHECK(error_);
164 DCHECK(prompt_);
165}
166
167ExternalInstallBubbleAlert::~ExternalInstallBubbleAlert() {
168}
169
170GlobalError::Severity ExternalInstallBubbleAlert::GetSeverity() {
171 return SEVERITY_LOW;
172}
173
174bool ExternalInstallBubbleAlert::HasMenuItem() {
175 return true;
176}
177
178int ExternalInstallBubbleAlert::MenuItemCommandID() {
179 return IDC_EXTERNAL_EXTENSION_ALERT;
180}
181
182base::string16 ExternalInstallBubbleAlert::MenuItemLabel() {
183 return GetMenuItemLabel(error_->GetExtension());
184}
185
186void ExternalInstallBubbleAlert::ExecuteMenuItem(Browser* browser) {
187 ShowBubbleView(browser);
188}
189
190gfx::Image ExternalInstallBubbleAlert::GetBubbleViewIcon() {
191 if (prompt_->icon().IsEmpty())
192 return GlobalErrorWithStandardBubble::GetBubbleViewIcon();
193 // Scale icon to a reasonable size.
194 return gfx::Image(gfx::ImageSkiaOperations::CreateResizedImage(
195 *prompt_->icon().ToImageSkia(),
196 skia::ImageOperations::RESIZE_BEST,
197 gfx::Size(extension_misc::EXTENSION_ICON_SMALL,
198 extension_misc::EXTENSION_ICON_SMALL)));
199}
200
201base::string16 ExternalInstallBubbleAlert::GetBubbleViewTitle() {
202 return prompt_->GetDialogTitle();
203}
204
205std::vector<base::string16>
206ExternalInstallBubbleAlert::GetBubbleViewMessages() {
gpdavis.chromium0fbac4d2014-09-19 20:57:54207 ExtensionInstallPrompt::PermissionsType regular_permissions =
208 ExtensionInstallPrompt::PermissionsType::REGULAR_PERMISSIONS;
209 ExtensionInstallPrompt::PermissionsType withheld_permissions =
210 ExtensionInstallPrompt::PermissionsType::WITHHELD_PERMISSIONS;
211
[email protected]2894a512014-06-26 19:03:56212 std::vector<base::string16> messages;
213 messages.push_back(prompt_->GetHeading());
gpdavis.chromium0fbac4d2014-09-19 20:57:54214 if (prompt_->GetPermissionCount(regular_permissions)) {
215 messages.push_back(prompt_->GetPermissionsHeading(regular_permissions));
216 for (size_t i = 0; i < prompt_->GetPermissionCount(regular_permissions);
217 ++i) {
[email protected]2894a512014-06-26 19:03:56218 messages.push_back(l10n_util::GetStringFUTF16(
gpdavis.chromium0fbac4d2014-09-19 20:57:54219 IDS_EXTENSION_PERMISSION_LINE,
220 prompt_->GetPermission(i, regular_permissions)));
221 }
222 }
223 if (prompt_->GetPermissionCount(withheld_permissions)) {
224 messages.push_back(prompt_->GetPermissionsHeading(withheld_permissions));
225 for (size_t i = 0; i < prompt_->GetPermissionCount(withheld_permissions);
226 ++i) {
227 messages.push_back(l10n_util::GetStringFUTF16(
228 IDS_EXTENSION_PERMISSION_LINE,
229 prompt_->GetPermission(i, withheld_permissions)));
[email protected]2894a512014-06-26 19:03:56230 }
231 }
232 // TODO(yoz): OAuth issue advice?
233 return messages;
234}
235
236base::string16 ExternalInstallBubbleAlert::GetBubbleViewAcceptButtonLabel() {
237 return prompt_->GetAcceptButtonLabel();
238}
239
240base::string16 ExternalInstallBubbleAlert::GetBubbleViewCancelButtonLabel() {
241 return prompt_->GetAbortButtonLabel();
242}
243
244void ExternalInstallBubbleAlert::OnBubbleViewDidClose(Browser* browser) {
245}
246
247void ExternalInstallBubbleAlert::BubbleViewAcceptButtonPressed(
248 Browser* browser) {
249 error_->InstallUIProceed();
250}
251
252void ExternalInstallBubbleAlert::BubbleViewCancelButtonPressed(
253 Browser* browser) {
254 error_->InstallUIAbort(true);
255}
256
257} // namespace
258
259////////////////////////////////////////////////////////////////////////////////
260// ExternalInstallError
261
262ExternalInstallError::ExternalInstallError(
263 content::BrowserContext* browser_context,
264 const std::string& extension_id,
265 AlertType alert_type,
266 ExternalInstallManager* manager)
267 : browser_context_(browser_context),
268 extension_id_(extension_id),
269 alert_type_(alert_type),
270 manager_(manager),
271 error_service_(GlobalErrorServiceFactory::GetForProfile(
272 Profile::FromBrowserContext(browser_context_))),
273 weak_factory_(this) {
274 prompt_ = new ExtensionInstallPrompt::Prompt(
275 ExtensionInstallPrompt::EXTERNAL_INSTALL_PROMPT);
276
277 webstore_data_fetcher_.reset(new WebstoreDataFetcher(
278 this, browser_context_->GetRequestContext(), GURL(), extension_id_));
279 webstore_data_fetcher_->Start();
280}
281
282ExternalInstallError::~ExternalInstallError() {
283 if (global_error_.get())
284 error_service_->RemoveGlobalError(global_error_.get());
285}
286
287void ExternalInstallError::InstallUIProceed() {
288 const Extension* extension = GetExtension();
289 if (extension) {
290 ExtensionSystem::Get(browser_context_)
291 ->extension_service()
292 ->GrantPermissionsAndEnableExtension(extension);
293 // Since the manager listens for the extension to be loaded, this will
294 // remove the error...
295 } else {
296 // ... Otherwise we have to do it explicitly.
297 manager_->RemoveExternalInstallError();
298 }
299}
300
301void ExternalInstallError::InstallUIAbort(bool user_initiated) {
302 if (user_initiated && GetExtension()) {
303 ExtensionSystem::Get(browser_context_)
304 ->extension_service()
[email protected]e43c61f2014-07-20 21:46:34305 ->UninstallExtension(extension_id_,
306 extensions::UNINSTALL_REASON_INSTALL_CANCELED,
[email protected]42d58f62014-07-31 01:32:45307 base::Bind(&base::DoNothing),
[email protected]e43c61f2014-07-20 21:46:34308 NULL); // Ignore error.
[email protected]2894a512014-06-26 19:03:56309 // Since the manager listens for the extension to be removed, this will
310 // remove the error...
311 } else {
312 // ... Otherwise we have to do it explicitly.
313 manager_->RemoveExternalInstallError();
314 }
315}
316
[email protected]2894a512014-06-26 19:03:56317void ExternalInstallError::ShowDialog(Browser* browser) {
318 DCHECK(install_ui_.get());
319 DCHECK(prompt_.get());
320 DCHECK(browser);
321 content::WebContents* web_contents = NULL;
[email protected]2894a512014-06-26 19:03:56322 web_contents = browser->tab_strip_model()->GetActiveWebContents();
pkotwicz2f181782014-10-29 17:33:45323 install_ui_show_params_.reset(
324 new ExtensionInstallPromptShowParams(web_contents));
[email protected]2894a512014-06-26 19:03:56325 ExtensionInstallPrompt::GetDefaultShowDialogCallback().Run(
pkotwicz2f181782014-10-29 17:33:45326 install_ui_show_params_.get(), this, prompt_);
[email protected]2894a512014-06-26 19:03:56327}
328
329const Extension* ExternalInstallError::GetExtension() const {
330 return ExtensionRegistry::Get(browser_context_)
331 ->GetExtensionById(extension_id_, ExtensionRegistry::EVERYTHING);
332}
333
334void ExternalInstallError::OnWebstoreRequestFailure() {
335 OnFetchComplete();
336}
337
338void ExternalInstallError::OnWebstoreResponseParseSuccess(
339 scoped_ptr<base::DictionaryValue> webstore_data) {
340 std::string localized_user_count;
[email protected]96aebe22014-07-16 04:07:51341 double average_rating = 0;
342 int rating_count = 0;
[email protected]2894a512014-06-26 19:03:56343 if (!webstore_data->GetString(kUsersKey, &localized_user_count) ||
344 !webstore_data->GetDouble(kAverageRatingKey, &average_rating) ||
345 !webstore_data->GetInteger(kRatingCountKey, &rating_count)) {
346 // If we don't get a valid webstore response, short circuit, and continue
347 // to show a prompt without webstore data.
348 OnFetchComplete();
349 return;
350 }
351
352 bool show_user_count = true;
353 webstore_data->GetBoolean(kShowUserCountKey, &show_user_count);
354
355 prompt_->SetWebstoreData(
356 localized_user_count, show_user_count, average_rating, rating_count);
357 OnFetchComplete();
358}
359
360void ExternalInstallError::OnWebstoreResponseParseFailure(
361 const std::string& error) {
362 OnFetchComplete();
363}
364
365void ExternalInstallError::OnFetchComplete() {
366 // Create a new ExtensionInstallPrompt. We pass in NULL for the UI
367 // components because we display at a later point, and don't want
368 // to pass ones which may be invalidated.
369 install_ui_.reset(
370 new ExtensionInstallPrompt(Profile::FromBrowserContext(browser_context_),
pkotwicz2175c622014-10-22 19:56:28371 NULL)); // NULL native window.
[email protected]2894a512014-06-26 19:03:56372
373 install_ui_->ConfirmExternalInstall(
374 this,
375 GetExtension(),
376 base::Bind(&ExternalInstallError::OnDialogReady,
377 weak_factory_.GetWeakPtr()),
378 prompt_);
379}
380
381void ExternalInstallError::OnDialogReady(
pkotwicz2f181782014-10-29 17:33:45382 ExtensionInstallPromptShowParams* show_params,
[email protected]2894a512014-06-26 19:03:56383 ExtensionInstallPrompt::Delegate* prompt_delegate,
384 scoped_refptr<ExtensionInstallPrompt::Prompt> prompt) {
385 DCHECK_EQ(this, prompt_delegate);
386 prompt_ = prompt;
387
388 if (alert_type_ == BUBBLE_ALERT) {
dchengc7047942014-08-26 05:05:31389 global_error_.reset(new ExternalInstallBubbleAlert(this, prompt_.get()));
[email protected]2894a512014-06-26 19:03:56390 error_service_->AddGlobalError(global_error_.get());
391
[email protected]13e062e2014-08-09 10:21:55392 Browser* browser =
[email protected]2894a512014-06-26 19:03:56393 chrome::FindTabbedBrowser(Profile::FromBrowserContext(browser_context_),
394 true,
395 chrome::GetActiveDesktop());
[email protected]2894a512014-06-26 19:03:56396 if (browser)
397 global_error_->ShowBubbleView(browser);
398 } else {
399 DCHECK(alert_type_ == MENU_ALERT);
400 global_error_.reset(new ExternalInstallMenuAlert(this));
401 error_service_->AddGlobalError(global_error_.get());
402 }
403}
404
405} // namespace extensions