// Copyright (c) 2017 The Chromium Embedded Framework 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 "libcef/browser/extension_impl.h"

#include "libcef/browser/extensions/extension_system.h"
#include "libcef/browser/request_context_impl.h"
#include "libcef/browser/thread_util.h"
#include "libcef/common/values_impl.h"

#include "extensions/common/extension.h"

CefExtensionImpl::CefExtensionImpl(const extensions::Extension* extension,
                                   CefRequestContext* loader_context,
                                   CefRefPtr<CefExtensionHandler> handler)
    : id_(extension->id()),
      path_(extension->path().value()),
      manifest_(
          new CefDictionaryValueImpl(extension->manifest()->value()->DeepCopy(),
                                     true,
                                     true)),
      loader_context_(loader_context),
      handler_(handler) {}

CefString CefExtensionImpl::GetIdentifier() {
  return id_;
}

CefString CefExtensionImpl::GetPath() {
  return path_;
}

CefRefPtr<CefDictionaryValue> CefExtensionImpl::GetManifest() {
  return manifest_;
}

bool CefExtensionImpl::IsSame(CefRefPtr<CefExtension> that) {
  CefExtensionImpl* that_impl = static_cast<CefExtensionImpl*>(that.get());
  if (!that_impl)
    return false;

  // Maybe the same object.
  if (this == that_impl)
    return true;

  return id_ == that_impl->id_ && path_ == that_impl->path_ &&
         loader_context_ == that_impl->loader_context_;
}

CefRefPtr<CefExtensionHandler> CefExtensionImpl::GetHandler() {
  return handler_;
}

CefRefPtr<CefRequestContext> CefExtensionImpl::GetLoaderContext() {
  if (!CEF_CURRENTLY_ON_UIT()) {
    NOTREACHED() << "called on invalid thread";
    return nullptr;
  }

  return loader_context_;
}

bool CefExtensionImpl::IsLoaded() {
  if (!CEF_CURRENTLY_ON_UIT()) {
    NOTREACHED() << "called on invalid thread";
    return false;
  }

  return !unloaded_;
}

void CefExtensionImpl::Unload() {
  if (!CEF_CURRENTLY_ON_UIT()) {
    CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefExtensionImpl::Unload, this));
    return;
  }

  // Will be NULL for internal extensions. They can't be unloaded.
  if (!loader_context_)
    return;

  if (unloaded_)
    return;

  // CefExtensionHandler callbacks triggered by UnloadExtension may check this
  // flag, so set it here.
  unloaded_ = true;

  const bool result = static_cast<CefRequestContextImpl*>(loader_context_)
                          ->GetBrowserContext()
                          ->extension_system()
                          ->UnloadExtension(id_);
  ALLOW_UNUSED_LOCAL(result);
  DCHECK(result);
}

void CefExtensionImpl::OnExtensionLoaded() {
  CEF_REQUIRE_UIT();
  if (handler_)
    handler_->OnExtensionLoaded(this);
}

void CefExtensionImpl::OnExtensionUnloaded() {
  CEF_REQUIRE_UIT();
  // Should not be called for internal extensions.
  DCHECK(loader_context_);

  unloaded_ = true;
  loader_context_ = nullptr;

  if (handler_)
    handler_->OnExtensionUnloaded(this);
}