1 // 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.
5 #include "chrome/browser/extensions/external_install_error.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "chrome/app/chrome_command_ids.h"
10 #include "chrome/browser/extensions/extension_install_prompt_show_params.h"
11 #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"
21 #include "chrome/grit/generated_resources.h"
22 #include "extensions/browser/extension_registry.h"
23 #include "extensions/browser/extension_system.h"
24 #include "extensions/browser/uninstall_reason.h"
25 #include "extensions/common/constants.h"
26 #include "extensions/common/extension.h"
27 #include "ui/base/l10n/l10n_util.h"
28 #include "ui/gfx/image/image.h"
29 #include "ui/gfx/image/image_skia_operations.h"
31 namespace extensions
{
35 // Return the menu label for a global error.
36 base::string16
GetMenuItemLabel(const Extension
* extension
) {
38 return base::string16();
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
;
46 id
= IDS_EXTENSION_EXTERNAL_INSTALL_ALERT_EXTENSION
;
48 return l10n_util::GetStringFUTF16(id
, base::UTF8ToUTF16(extension
->name()));
51 // A global error that spawns a dialog when the menu item is clicked.
52 class ExternalInstallMenuAlert
: public GlobalError
{
54 explicit ExternalInstallMenuAlert(ExternalInstallError
* error
);
55 ~ExternalInstallMenuAlert() override
;
58 // GlobalError implementation.
59 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
;
69 // The owning ExternalInstallError.
70 ExternalInstallError
* error_
;
72 DISALLOW_COPY_AND_ASSIGN(ExternalInstallMenuAlert
);
75 // A global error that spawns a bubble when the menu item is clicked.
76 class ExternalInstallBubbleAlert
: public GlobalErrorWithStandardBubble
{
78 explicit ExternalInstallBubbleAlert(ExternalInstallError
* error
,
79 ExtensionInstallPrompt::Prompt
* prompt
);
80 ~ExternalInstallBubbleAlert() override
;
83 // GlobalError implementation.
84 Severity
GetSeverity() override
;
85 bool HasMenuItem() override
;
86 int MenuItemCommandID() override
;
87 base::string16
MenuItemLabel() override
;
88 void ExecuteMenuItem(Browser
* browser
) override
;
90 // GlobalErrorWithStandardBubble implementation.
91 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
;
100 // The owning ExternalInstallError.
101 ExternalInstallError
* error_
;
103 // The Prompt with all information, which we then use to populate the bubble.
104 ExtensionInstallPrompt::Prompt
* prompt_
;
106 DISALLOW_COPY_AND_ASSIGN(ExternalInstallBubbleAlert
);
109 ////////////////////////////////////////////////////////////////////////////////
110 // ExternalInstallMenuAlert
112 ExternalInstallMenuAlert::ExternalInstallMenuAlert(ExternalInstallError
* error
)
116 ExternalInstallMenuAlert::~ExternalInstallMenuAlert() {
119 GlobalError::Severity
ExternalInstallMenuAlert::GetSeverity() {
123 bool ExternalInstallMenuAlert::HasMenuItem() {
127 int ExternalInstallMenuAlert::MenuItemCommandID() {
128 return IDC_EXTERNAL_EXTENSION_ALERT
;
131 base::string16
ExternalInstallMenuAlert::MenuItemLabel() {
132 return GetMenuItemLabel(error_
->GetExtension());
135 void ExternalInstallMenuAlert::ExecuteMenuItem(Browser
* browser
) {
136 error_
->ShowDialog(browser
);
139 bool ExternalInstallMenuAlert::HasBubbleView() {
143 bool ExternalInstallMenuAlert::HasShownBubbleView() {
148 void ExternalInstallMenuAlert::ShowBubbleView(Browser
* browser
) {
152 GlobalErrorBubbleViewBase
* ExternalInstallMenuAlert::GetBubbleView() {
156 ////////////////////////////////////////////////////////////////////////////////
157 // ExternalInstallBubbleAlert
159 ExternalInstallBubbleAlert::ExternalInstallBubbleAlert(
160 ExternalInstallError
* error
,
161 ExtensionInstallPrompt::Prompt
* prompt
)
162 : error_(error
), prompt_(prompt
) {
167 ExternalInstallBubbleAlert::~ExternalInstallBubbleAlert() {
170 GlobalError::Severity
ExternalInstallBubbleAlert::GetSeverity() {
174 bool ExternalInstallBubbleAlert::HasMenuItem() {
178 int ExternalInstallBubbleAlert::MenuItemCommandID() {
179 return IDC_EXTERNAL_EXTENSION_ALERT
;
182 base::string16
ExternalInstallBubbleAlert::MenuItemLabel() {
183 return GetMenuItemLabel(error_
->GetExtension());
186 void ExternalInstallBubbleAlert::ExecuteMenuItem(Browser
* browser
) {
187 ShowBubbleView(browser
);
190 gfx::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
)));
201 base::string16
ExternalInstallBubbleAlert::GetBubbleViewTitle() {
202 return l10n_util::GetStringFUTF16(
203 IDS_EXTENSION_EXTERNAL_INSTALL_ALERT_BUBBLE_TITLE
,
204 base::UTF8ToUTF16(prompt_
->extension()->name()));
207 std::vector
<base::string16
>
208 ExternalInstallBubbleAlert::GetBubbleViewMessages() {
209 ExtensionInstallPrompt::PermissionsType regular_permissions
=
210 ExtensionInstallPrompt::PermissionsType::REGULAR_PERMISSIONS
;
211 ExtensionInstallPrompt::PermissionsType withheld_permissions
=
212 ExtensionInstallPrompt::PermissionsType::WITHHELD_PERMISSIONS
;
214 std::vector
<base::string16
> messages
;
216 IDS_EXTENSION_EXTERNAL_INSTALL_ALERT_BUBBLE_HEADING_EXTENSION
;
217 if (prompt_
->extension()->is_app())
218 heading_id
= IDS_EXTENSION_EXTERNAL_INSTALL_ALERT_BUBBLE_HEADING_APP
;
219 else if (prompt_
->extension()->is_theme())
220 heading_id
= IDS_EXTENSION_EXTERNAL_INSTALL_ALERT_BUBBLE_HEADING_THEME
;
221 messages
.push_back(l10n_util::GetStringUTF16(heading_id
));
223 if (prompt_
->GetPermissionCount(regular_permissions
)) {
224 messages
.push_back(prompt_
->GetPermissionsHeading(regular_permissions
));
225 for (size_t i
= 0; i
< prompt_
->GetPermissionCount(regular_permissions
);
227 messages
.push_back(l10n_util::GetStringFUTF16(
228 IDS_EXTENSION_PERMISSION_LINE
,
229 prompt_
->GetPermission(i
, regular_permissions
)));
232 if (prompt_
->GetPermissionCount(withheld_permissions
)) {
233 messages
.push_back(prompt_
->GetPermissionsHeading(withheld_permissions
));
234 for (size_t i
= 0; i
< prompt_
->GetPermissionCount(withheld_permissions
);
236 messages
.push_back(l10n_util::GetStringFUTF16(
237 IDS_EXTENSION_PERMISSION_LINE
,
238 prompt_
->GetPermission(i
, withheld_permissions
)));
241 // TODO(yoz): OAuth issue advice?
245 base::string16
ExternalInstallBubbleAlert::GetBubbleViewAcceptButtonLabel() {
246 return prompt_
->GetAcceptButtonLabel();
249 base::string16
ExternalInstallBubbleAlert::GetBubbleViewCancelButtonLabel() {
250 return prompt_
->GetAbortButtonLabel();
253 void ExternalInstallBubbleAlert::OnBubbleViewDidClose(Browser
* browser
) {
256 void ExternalInstallBubbleAlert::BubbleViewAcceptButtonPressed(
258 error_
->InstallUIProceed();
261 void ExternalInstallBubbleAlert::BubbleViewCancelButtonPressed(
263 error_
->InstallUIAbort(true);
268 ////////////////////////////////////////////////////////////////////////////////
269 // ExternalInstallError
271 ExternalInstallError::ExternalInstallError(
272 content::BrowserContext
* browser_context
,
273 const std::string
& extension_id
,
274 AlertType alert_type
,
275 ExternalInstallManager
* manager
)
276 : browser_context_(browser_context
),
277 extension_id_(extension_id
),
278 alert_type_(alert_type
),
280 error_service_(GlobalErrorServiceFactory::GetForProfile(
281 Profile::FromBrowserContext(browser_context_
))),
282 weak_factory_(this) {
283 prompt_
= new ExtensionInstallPrompt::Prompt(
284 ExtensionInstallPrompt::EXTERNAL_INSTALL_PROMPT
);
286 webstore_data_fetcher_
.reset(new WebstoreDataFetcher(
287 this, browser_context_
->GetRequestContext(), GURL(), extension_id_
));
288 webstore_data_fetcher_
->Start();
291 ExternalInstallError::~ExternalInstallError() {
292 if (global_error_
.get())
293 error_service_
->RemoveGlobalError(global_error_
.get());
296 void ExternalInstallError::InstallUIProceed() {
297 const Extension
* extension
= GetExtension();
299 ExtensionSystem::Get(browser_context_
)
300 ->extension_service()
301 ->GrantPermissionsAndEnableExtension(extension
);
302 // Since the manager listens for the extension to be loaded, this will
303 // remove the error...
305 // ... Otherwise we have to do it explicitly.
306 manager_
->RemoveExternalInstallError();
310 void ExternalInstallError::InstallUIAbort(bool user_initiated
) {
311 if (user_initiated
&& GetExtension()) {
312 ExtensionSystem::Get(browser_context_
)
313 ->extension_service()
314 ->UninstallExtension(extension_id_
,
315 extensions::UNINSTALL_REASON_INSTALL_CANCELED
,
316 base::Bind(&base::DoNothing
),
317 NULL
); // Ignore error.
318 // Since the manager listens for the extension to be removed, this will
319 // remove the error...
321 // ... Otherwise we have to do it explicitly.
322 manager_
->RemoveExternalInstallError();
326 void ExternalInstallError::ShowDialog(Browser
* browser
) {
327 DCHECK(install_ui_
.get());
328 DCHECK(prompt_
.get());
330 content::WebContents
* web_contents
= NULL
;
331 web_contents
= browser
->tab_strip_model()->GetActiveWebContents();
332 install_ui_show_params_
.reset(
333 new ExtensionInstallPromptShowParams(web_contents
));
334 ExtensionInstallPrompt::GetDefaultShowDialogCallback().Run(
335 install_ui_show_params_
.get(), this, prompt_
);
338 const Extension
* ExternalInstallError::GetExtension() const {
339 return ExtensionRegistry::Get(browser_context_
)
340 ->GetExtensionById(extension_id_
, ExtensionRegistry::EVERYTHING
);
343 void ExternalInstallError::OnWebstoreRequestFailure() {
347 void ExternalInstallError::OnWebstoreResponseParseSuccess(
348 scoped_ptr
<base::DictionaryValue
> webstore_data
) {
349 std::string localized_user_count
;
350 double average_rating
= 0;
351 int rating_count
= 0;
352 if (!webstore_data
->GetString(kUsersKey
, &localized_user_count
) ||
353 !webstore_data
->GetDouble(kAverageRatingKey
, &average_rating
) ||
354 !webstore_data
->GetInteger(kRatingCountKey
, &rating_count
)) {
355 // If we don't get a valid webstore response, short circuit, and continue
356 // to show a prompt without webstore data.
361 bool show_user_count
= true;
362 webstore_data
->GetBoolean(kShowUserCountKey
, &show_user_count
);
364 prompt_
->SetWebstoreData(
365 localized_user_count
, show_user_count
, average_rating
, rating_count
);
369 void ExternalInstallError::OnWebstoreResponseParseFailure(
370 const std::string
& error
) {
374 void ExternalInstallError::OnFetchComplete() {
375 // Create a new ExtensionInstallPrompt. We pass in NULL for the UI
376 // components because we display at a later point, and don't want
377 // to pass ones which may be invalidated.
379 new ExtensionInstallPrompt(Profile::FromBrowserContext(browser_context_
),
380 NULL
)); // NULL native window.
382 install_ui_
->ConfirmExternalInstall(
385 base::Bind(&ExternalInstallError::OnDialogReady
,
386 weak_factory_
.GetWeakPtr()),
390 void ExternalInstallError::OnDialogReady(
391 ExtensionInstallPromptShowParams
* show_params
,
392 ExtensionInstallPrompt::Delegate
* prompt_delegate
,
393 scoped_refptr
<ExtensionInstallPrompt::Prompt
> prompt
) {
394 DCHECK_EQ(this, prompt_delegate
);
397 if (alert_type_
== BUBBLE_ALERT
) {
398 global_error_
.reset(new ExternalInstallBubbleAlert(this, prompt_
.get()));
399 error_service_
->AddGlobalError(global_error_
.get());
402 chrome::FindTabbedBrowser(Profile::FromBrowserContext(browser_context_
),
404 chrome::GetActiveDesktop());
406 global_error_
->ShowBubbleView(browser
);
408 DCHECK(alert_type_
== MENU_ALERT
);
409 global_error_
.reset(new ExternalInstallMenuAlert(this));
410 error_service_
->AddGlobalError(global_error_
.get());
414 } // namespace extensions