// Copyright 2013 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/common/extensions/manifest_handlers/ui_overrides_handler.h"

#include "base/memory/scoped_ptr.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "extensions/common/error_utils.h"
#include "extensions/common/extension_messages.h"
#include "extensions/common/feature_switch.h"
#include "extensions/common/manifest_constants.h"
#include "extensions/common/permissions/manifest_permission.h"
#include "extensions/common/permissions/permissions_data.h"
#include "extensions/common/permissions/permissions_info.h"
#include "grit/generated_resources.h"
#include "ipc/ipc_message.h"
#include "ui/base/l10n/l10n_util.h"

using extensions::api::manifest_types::ChromeUIOverrides;

namespace extensions {

// The manifest permission implementation supports a permission for overriding
// the bookmark UI.
class UIOverridesHandler::ManifestPermissionImpl : public ManifestPermission {
 public:
  explicit ManifestPermissionImpl(bool override_bookmarks_ui_permission)
      : override_bookmarks_ui_permission_(override_bookmarks_ui_permission) {}

  // extensions::ManifestPermission overrides.
  virtual std::string name() const OVERRIDE {
    return manifest_keys::kUIOverride;
  }

  virtual std::string id() const OVERRIDE {
    return name();
  }

  virtual bool HasMessages() const OVERRIDE {
    return override_bookmarks_ui_permission_;
  }

  virtual PermissionMessages GetMessages() const OVERRIDE {
    PermissionMessages result;
    if (override_bookmarks_ui_permission_) {
      result.push_back(PermissionMessage(
          PermissionMessage::kOverrideBookmarksUI,
          l10n_util::GetStringUTF16(
              IDS_EXTENSION_PROMPT_WARNING_OVERRIDE_BOOKMARKS_UI)));
    }
    return result;
  }

  virtual bool FromValue(const base::Value* value) OVERRIDE {
    return value && value->GetAsBoolean(&override_bookmarks_ui_permission_);
  }

  virtual scoped_ptr<base::Value> ToValue() const OVERRIDE {
    return scoped_ptr<base::Value>(
        new base::FundamentalValue(override_bookmarks_ui_permission_)).Pass();
  }

  virtual ManifestPermission* Clone() const OVERRIDE {
    return scoped_ptr<ManifestPermissionImpl>(
        new ManifestPermissionImpl(
            override_bookmarks_ui_permission_)).release();
  }

  virtual ManifestPermission* Diff(const ManifestPermission* rhs) const
      OVERRIDE {
    const ManifestPermissionImpl* other =
        static_cast<const ManifestPermissionImpl*>(rhs);

    return scoped_ptr<ManifestPermissionImpl>(new ManifestPermissionImpl(
        override_bookmarks_ui_permission_ &&
        !other->override_bookmarks_ui_permission_)).release();
  }

  virtual ManifestPermission* Union(const ManifestPermission* rhs) const
      OVERRIDE {
    const ManifestPermissionImpl* other =
        static_cast<const ManifestPermissionImpl*>(rhs);

    return scoped_ptr<ManifestPermissionImpl>(new ManifestPermissionImpl(
        override_bookmarks_ui_permission_ ||
        other->override_bookmarks_ui_permission_)).release();
  }

  virtual ManifestPermission* Intersect(const ManifestPermission* rhs) const
      OVERRIDE {
    const ManifestPermissionImpl* other =
        static_cast<const ManifestPermissionImpl*>(rhs);

    return scoped_ptr<ManifestPermissionImpl>(new ManifestPermissionImpl(
        override_bookmarks_ui_permission_ &&
        other->override_bookmarks_ui_permission_)).release();
  }

  virtual bool Contains(const ManifestPermission* rhs) const OVERRIDE {
    const ManifestPermissionImpl* other =
        static_cast<const ManifestPermissionImpl*>(rhs);

    return !other->override_bookmarks_ui_permission_ ||
        override_bookmarks_ui_permission_;
  }

  virtual bool Equal(const ManifestPermission* rhs) const OVERRIDE {
    const ManifestPermissionImpl* other =
        static_cast<const ManifestPermissionImpl*>(rhs);

    return override_bookmarks_ui_permission_ ==
        other->override_bookmarks_ui_permission_;
  }

  virtual void Write(IPC::Message* m) const OVERRIDE {
    IPC::WriteParam(m, override_bookmarks_ui_permission_);
  }

  virtual bool Read(const IPC::Message* m, PickleIterator* iter) OVERRIDE {
    return IPC::ReadParam(m, iter, &override_bookmarks_ui_permission_);
  }

  virtual void Log(std::string* log) const OVERRIDE {
    IPC::LogParam(override_bookmarks_ui_permission_, log);
  }

 private:
  bool override_bookmarks_ui_permission_;
};

UIOverrides::UIOverrides() {}

UIOverrides::~UIOverrides() {}

const UIOverrides* UIOverrides::Get(const Extension* extension) {
  return static_cast<UIOverrides*>(
      extension->GetManifestData(manifest_keys::kUIOverride));
}

bool UIOverrides::RemovesBookmarkButton(const UIOverrides& ui_overrides) {
  return ui_overrides.bookmarks_ui &&
      ui_overrides.bookmarks_ui->remove_button &&
      *ui_overrides.bookmarks_ui->remove_button;
}

bool UIOverrides::RemovesBookmarkShortcut(const UIOverrides& ui_overrides) {
  return ui_overrides.bookmarks_ui &&
      ui_overrides.bookmarks_ui->remove_bookmark_shortcut &&
      *ui_overrides.bookmarks_ui->remove_bookmark_shortcut;
}

bool UIOverrides::RemovesBookmarkOpenPagesShortcut(
    const UIOverrides& ui_overrides) {
  return ui_overrides.bookmarks_ui &&
      ui_overrides.bookmarks_ui->remove_bookmark_open_pages_shortcut &&
      *ui_overrides.bookmarks_ui->remove_bookmark_open_pages_shortcut;
}

UIOverridesHandler::UIOverridesHandler() {}

UIOverridesHandler::~UIOverridesHandler() {}

bool UIOverridesHandler::Parse(Extension* extension, base::string16* error) {
  const base::Value* dict = NULL;
  CHECK(extension->manifest()->Get(manifest_keys::kUIOverride, &dict));
  scoped_ptr<ChromeUIOverrides> overrides(
      ChromeUIOverrides::FromValue(*dict, error));
  if (!overrides)
    return false;

  scoped_ptr<UIOverrides> info(new UIOverrides);
  info->bookmarks_ui.swap(overrides->bookmarks_ui);
  if (!info->bookmarks_ui) {
    *error = ErrorUtils::FormatErrorMessageUTF16(
        manifest_errors::kInvalidEmptyDictionary,
        manifest_keys::kUIOverride);
    return false;
  }
  info->manifest_permission.reset(new ManifestPermissionImpl(
      UIOverrides::RemovesBookmarkButton(*info)));
  extension->SetManifestData(manifest_keys::kUIOverride, info.release());
  return true;
}

bool UIOverridesHandler::Validate(const Extension* extension,
                                  std::string* error,
                                  std::vector<InstallWarning>* warnings) const {
  const UIOverrides* ui_overrides = UIOverrides::Get(extension);

  if (ui_overrides && ui_overrides->bookmarks_ui) {
    if (!FeatureSwitch::enable_override_bookmarks_ui()->IsEnabled()) {
      warnings->push_back(InstallWarning(
          ErrorUtils::FormatErrorMessage(
              manifest_errors::kUnrecognizedManifestProperty,
              manifest_keys::kBookmarkUI,
              manifest_keys::kUIOverride)));
    }
  }

  return true;
}

ManifestPermission* UIOverridesHandler::CreatePermission() {
  return new ManifestPermissionImpl(false);
}

ManifestPermission* UIOverridesHandler::CreateInitialRequiredPermission(
    const Extension* extension) {
  const UIOverrides* data = UIOverrides::Get(extension);
  if (data)
    return data->manifest_permission->Clone();
  return NULL;
}
const std::vector<std::string> UIOverridesHandler::Keys() const {
  return SingleKey(manifest_keys::kUIOverride);
}

}  // namespace extensions