// 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/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/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_icon_set.h"
#include "extensions/common/extension_resource.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/page_transition_types.h"
#include "ui/base/window_open_disposition.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/image/image_skia.h"

namespace extensions {

namespace {

const char kExtensionRemovedError[] =
    "Extension was removed before dialog closed.";

const char kReferrerId[] = "chrome-remove-extension-dialog";

// Returns bitmap for the default icon with size equal to the default icon's
// pixel size under maximal supported scale factor.
SkBitmap GetDefaultIconBitmapForMaxScaleFactor(bool is_app) {
  const gfx::ImageSkia& image =
      is_app ? util::GetDefaultAppIcon() : util::GetDefaultExtensionIcon();
  return image.GetRepresentation(
      gfx::ImageSkia::GetMaxSupportedScale()).sk_bitmap();
}

}  // namespace

ExtensionUninstallDialog::ExtensionUninstallDialog(
    Profile* profile,
    ExtensionUninstallDialog::Delegate* delegate)
    : profile_(profile),
      delegate_(delegate),
      uninstall_reason_(UNINSTALL_REASON_FOR_TESTING) {
}

ExtensionUninstallDialog::~ExtensionUninstallDialog() {
}

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;
  // Bookmark apps may not have 128x128 icons so accept 64x64 icons.
  const int icon_size = extension_->from_bookmark()
                            ? extension_misc::EXTENSION_ICON_SMALL * 2
                            : extension_misc::EXTENSION_ICON_LARGE;
  ExtensionResource image = IconsInfo::GetIconResource(
      extension_.get(), icon_size, ExtensionIconSet::MATCH_BIGGER);

  // Load the image asynchronously. The response will be sent to OnImageLoaded.
  ImageLoader* loader = ImageLoader::Get(profile_);

  SetIcon(gfx::Image());
  std::vector<ImageLoader::ImageRepresentation> images_list;
  images_list.push_back(ImageLoader::ImageRepresentation(
      image,
      ImageLoader::ImageRepresentation::NEVER_RESIZE,
      gfx::Size(),
      ui::SCALE_FACTOR_100P));
  loader->LoadImagesAsync(extension_.get(), images_list,
                          base::Bind(&ExtensionUninstallDialog::OnImageLoaded,
                                     AsWeakPtr(), extension_->id()));
}

void ExtensionUninstallDialog::SetIcon(const gfx::Image& image) {
  if (image.IsEmpty()) {
    // Let's set default icon bitmap whose size is equal to the default icon's
    // pixel size under maximal supported scale factor. If the bitmap is larger
    // than the one we need, it will be scaled down by the ui code.
    // TODO(tbarzic): We should use IconImage here and load the required bitmap
    //     lazily.
    icon_ = gfx::ImageSkia::CreateFrom1xBitmap(
        GetDefaultIconBitmapForMaxScaleFactor(extension_->is_app()));
  } else {
    icon_ = *image.ToImageSkia();
  }
}

void ExtensionUninstallDialog::OnImageLoaded(const std::string& extension_id,
                                             const gfx::Image& image) {
  const Extension* target_extension =
      ExtensionRegistry::Get(profile_)
          ->GetExtensionById(extension_id, ExtensionRegistry::EVERYTHING);
  if (!target_extension) {
    delegate_->OnExtensionUninstallDialogClosed(
        false, base::ASCIIToUTF16(kExtensionRemovedError));
    return;
  }

  SetIcon(image);

  switch (ScopedTestDialogAutoConfirm::GetAutoConfirmValue()) {
    case ScopedTestDialogAutoConfirm::NONE:
      Show();
      break;
    case ScopedTestDialogAutoConfirm::ACCEPT:
      OnDialogClosed(CLOSE_ACTION_UNINSTALL);
      break;
    case ScopedTestDialogAutoConfirm::CANCEL:
      OnDialogClosed(CLOSE_ACTION_CANCELED);
      break;
  }
}

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:
      HandleReportAbuse();
    // Fall through.
    case CLOSE_ACTION_UNINSTALL: {
      const Extension* current_extension =
          ExtensionRegistry::Get(profile_)->GetExtensionById(
              extension_->id(), ExtensionRegistry::EVERYTHING);
      if (current_extension) {
        success =
            ExtensionSystem::Get(profile_)
                ->extension_service()
                ->UninstallExtension(extension_->id(), uninstall_reason_,
                                     base::Bind(&base::DoNothing), &error);
      } else {
        error = base::ASCIIToUTF16(kExtensionRemovedError);
      }
      break;
    }
    case CLOSE_ACTION_CANCELED:
      error = base::ASCIIToUTF16("User canceled uninstall dialog");
      break;
    case CLOSE_ACTION_LAST:
      NOTREACHED();
  }

  delegate_->OnExtensionUninstallDialogClosed(success, error);
}

void ExtensionUninstallDialog::HandleReportAbuse() {
  chrome::NavigateParams params(
      profile_,
      extension_urls::GetWebstoreReportAbuseUrl(extension_->id(), kReferrerId),
      ui::PAGE_TRANSITION_LINK);
  params.disposition = NEW_FOREGROUND_TAB;
  chrome::Navigate(&params);
}

}  // namespace extensions