blob: 56473490a86cf9f13e984696e784e6657a7ff38d [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/extensions/extension_uninstall_dialog.h"
#include "base/bind.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/extensions/chrome_app_icon_service.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser_navigator.h"
#include "chrome/browser/ui/browser_navigator_params.h"
#include "chrome/browser/ui/native_window_tracker.h"
#include "chrome/grit/generated_resources.h"
#include "extensions/browser/extension_dialog_auto_confirm.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_system.h"
#include "extensions/browser/image_loader.h"
#include "extensions/common/constants.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_urls.h"
#include "extensions/common/manifest_handlers/icons_handler.h"
#include "extensions/common/manifest_url_handlers.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/layout.h"
#include "ui/base/page_transition_types.h"
#include "ui/base/window_open_disposition.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/image/image_skia.h"
namespace extensions {
namespace {
constexpr int kIconSize = 64;
constexpr char kExtensionRemovedError[] =
"Extension was removed before dialog closed.";
constexpr char kReferrerId[] = "chrome-remove-extension-dialog";
float GetScaleFactor(gfx::NativeWindow window) {
const display::Screen* screen = display::Screen::GetScreen();
if (!screen)
return 1.0; // Happens in unit_tests.
if (window)
return screen->GetDisplayNearestWindow(window).device_scale_factor();
return screen->GetPrimaryDisplay().device_scale_factor();
}
} // namespace
ExtensionUninstallDialog::ExtensionUninstallDialog(
Profile* profile,
gfx::NativeWindow parent,
ExtensionUninstallDialog::Delegate* delegate)
: profile_(profile), parent_(parent), delegate_(delegate), observer_(this) {
if (parent)
parent_window_tracker_ = NativeWindowTracker::Create(parent);
}
ExtensionUninstallDialog::~ExtensionUninstallDialog() = default;
void ExtensionUninstallDialog::ConfirmUninstallByExtension(
const scoped_refptr<const Extension>& extension,
const scoped_refptr<const Extension>& triggering_extension,
UninstallReason reason,
UninstallSource source) {
triggering_extension_ = triggering_extension;
ConfirmUninstall(extension, reason, source);
}
void ExtensionUninstallDialog::ConfirmUninstall(
const scoped_refptr<const Extension>& extension,
UninstallReason reason,
UninstallSource source) {
DCHECK(thread_checker_.CalledOnValidThread());
UMA_HISTOGRAM_ENUMERATION("Extensions.UninstallSource", source,
NUM_UNINSTALL_SOURCES);
extension_ = extension;
uninstall_reason_ = reason;
if (parent() && parent_window_tracker_->WasNativeWindowClosed()) {
OnDialogClosed(CLOSE_ACTION_CANCELED);
return;
}
// Track that extension uninstalled externally.
DCHECK(!observer_.IsObserving(ExtensionRegistry::Get(profile_)));
observer_.Add(ExtensionRegistry::Get(profile_));
// Dialog will be shown once icon is loaded.
DCHECK(!dialog_shown_);
icon_ = ChromeAppIconService::Get(profile_)->CreateIcon(this, extension->id(),
kIconSize);
icon_->image_skia().GetRepresentation(GetScaleFactor(parent_));
}
void ExtensionUninstallDialog::OnIconUpdated(ChromeAppIcon* icon) {
// Ignore initial update.
if (!icon_ || dialog_shown_)
return;
DCHECK_EQ(icon, icon_.get());
dialog_shown_ = true;
if (parent() && parent_window_tracker_->WasNativeWindowClosed()) {
OnDialogClosed(CLOSE_ACTION_CANCELED);
return;
}
switch (ScopedTestDialogAutoConfirm::GetAutoConfirmValue()) {
case ScopedTestDialogAutoConfirm::NONE:
Show();
break;
case ScopedTestDialogAutoConfirm::ACCEPT_AND_OPTION:
OnDialogClosed(CLOSE_ACTION_UNINSTALL_AND_REPORT_ABUSE);
break;
case ScopedTestDialogAutoConfirm::ACCEPT:
OnDialogClosed(CLOSE_ACTION_UNINSTALL);
break;
case ScopedTestDialogAutoConfirm::CANCEL:
OnDialogClosed(CLOSE_ACTION_CANCELED);
break;
}
}
void ExtensionUninstallDialog::OnExtensionUninstalled(
content::BrowserContext* browser_context,
const Extension* extension,
UninstallReason reason) {
// Handle the case when extension was uninstalled externally and we have to
// close current dialog.
if (extension != extension_)
return;
delegate_->OnExtensionUninstallDialogClosed(
false, base::ASCIIToUTF16(kExtensionRemovedError));
}
std::string ExtensionUninstallDialog::GetHeadingText() {
if (triggering_extension_) {
return l10n_util::GetStringFUTF8(
IDS_EXTENSION_PROGRAMMATIC_UNINSTALL_PROMPT_HEADING,
base::UTF8ToUTF16(triggering_extension_->name()),
base::UTF8ToUTF16(extension_->name()));
}
return l10n_util::GetStringFUTF8(IDS_EXTENSION_UNINSTALL_PROMPT_HEADING,
base::UTF8ToUTF16(extension_->name()));
}
bool ExtensionUninstallDialog::ShouldShowReportAbuseCheckbox() const {
return ManifestURL::UpdatesFromGallery(extension_.get());
}
void ExtensionUninstallDialog::OnDialogClosed(CloseAction action) {
// We don't want to artificially weight any of the options, so only record if
// reporting abuse was available.
if (ShouldShowReportAbuseCheckbox()) {
UMA_HISTOGRAM_ENUMERATION("Extensions.UninstallDialogAction",
action,
CLOSE_ACTION_LAST);
}
bool success = false;
base::string16 error;
switch (action) {
case CLOSE_ACTION_UNINSTALL_AND_REPORT_ABUSE:
// If the extension specifies a custom uninstall page via
// chrome.runtime.setUninstallURL, then at uninstallation its uninstall
// page opens. To ensure that the CWS Report Abuse page is the active tab
// at uninstallation, HandleReportAbuse() is called after Uninstall().
success = Uninstall(&error);
HandleReportAbuse();
break;
case CLOSE_ACTION_UNINSTALL: {
success = Uninstall(&error);
break;
}
case CLOSE_ACTION_CANCELED:
error = base::ASCIIToUTF16("User canceled uninstall dialog");
break;
case CLOSE_ACTION_LAST:
NOTREACHED();
}
delegate_->OnExtensionUninstallDialogClosed(success, error);
}
bool ExtensionUninstallDialog::Uninstall(base::string16* error) {
const Extension* current_extension =
ExtensionRegistry::Get(profile_)->GetExtensionById(
extension_->id(), ExtensionRegistry::EVERYTHING);
if (current_extension) {
// Prevent notifications triggered by our request.
observer_.RemoveAll();
return ExtensionSystem::Get(profile_)
->extension_service()
->UninstallExtension(extension_->id(), uninstall_reason_, error);
}
*error = base::ASCIIToUTF16(kExtensionRemovedError);
return false;
}
void ExtensionUninstallDialog::HandleReportAbuse() {
NavigateParams params(
profile_,
extension_urls::GetWebstoreReportAbuseUrl(extension_->id(), kReferrerId),
ui::PAGE_TRANSITION_LINK);
params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
Navigate(&params);
}
} // namespace extensions