// Copyright (c) 2011 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 "ppapi/proxy/ppb_url_util_proxy.h"

#include "base/basictypes.h"
#include "ppapi/c/dev/ppb_url_util_dev.h"
#include "ppapi/c/dev/ppb_var_deprecated.h"
#include "ppapi/c/ppb_core.h"
#include "ppapi/proxy/plugin_dispatcher.h"
#include "ppapi/proxy/ppapi_messages.h"
#include "ppapi/shared_impl/url_util_impl.h"

namespace pp {
namespace proxy {

using ppapi::URLUtilImpl;

namespace {

URLUtilImpl::VarFromUtf8 GetVarFromUtf8() {
  const PPB_Var_Deprecated* var_deprecated =
      static_cast<const PPB_Var_Deprecated*>(
          PluginDispatcher::GetInterfaceFromDispatcher(
              PPB_VAR_DEPRECATED_INTERFACE));
  return var_deprecated->VarFromUtf8;
}

const std::string* GetStringFromVar(PP_Var var) {
  return PluginVarTracker::GetInstance()->GetExistingString(var);
}

PP_Var Canonicalize(PP_Var url,
                    PP_URLComponents_Dev* components) {
  return URLUtilImpl::Canonicalize(&GetStringFromVar, GetVarFromUtf8(),
                                   0, url, components);
}

// Helper function for the functions below that optionally take a components
// structure. It's annoying to serialze the large PP_URLComponents structure
// and this data often isn't needed.
//
// To avoid this, we instead just parse the result again in the plugin, which
// this function does if the given URL is valid and the components are
// non-NULL. The URL var will be returned.
PP_Var ConvertComponentsAndReturnURL(PP_Var url,
                                     PP_URLComponents_Dev* components) {
  if (!components)
    return url;  // Common case - plugin doesn't care about parsing.

  const std::string* url_string = GetStringFromVar(url);
  if (!url_string)
    return url;

  PP_Var result = Canonicalize(url, components);
  PluginVarTracker::GetInstance()->Release(url);
  return result;
}

PP_Var ResolveRelativeToURL(PP_Var base_url,
                            PP_Var relative,
                            PP_URLComponents_Dev* components) {
  return URLUtilImpl::ResolveRelativeToURL(&GetStringFromVar, GetVarFromUtf8(),
                                           0, base_url, relative, components);
}

PP_Var ResolveRelativeToDocument(PP_Instance instance,
                                 PP_Var relative_string,
                                 PP_URLComponents_Dev* components) {
  PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance);
  if (!dispatcher)
    return PP_MakeNull();

  ReceiveSerializedVarReturnValue result;
  dispatcher->Send(new PpapiHostMsg_PPBURLUtil_ResolveRelativeToDocument(
      INTERFACE_ID_PPB_URL_UTIL, instance,
      SerializedVarSendInput(dispatcher, relative_string),
      &result));
  return ConvertComponentsAndReturnURL(result.Return(dispatcher), components);
}

PP_Bool IsSameSecurityOrigin(PP_Var url_a, PP_Var url_b) {
  return URLUtilImpl::IsSameSecurityOrigin(&GetStringFromVar, url_a, url_b);
}

PP_Bool DocumentCanRequest(PP_Instance instance, PP_Var url) {
  PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance);
  if (!dispatcher)
    return PP_FALSE;

  PP_Bool result = PP_FALSE;
  dispatcher->Send(new PpapiHostMsg_PPBURLUtil_DocumentCanRequest(
      INTERFACE_ID_PPB_URL_UTIL, instance,
      SerializedVarSendInput(dispatcher, url),
      &result));
  return result;
}

PP_Bool DocumentCanAccessDocument(PP_Instance active, PP_Instance target) {
  PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(active);
  if (!dispatcher)
    return PP_FALSE;

  PP_Bool result = PP_FALSE;
  dispatcher->Send(new PpapiHostMsg_PPBURLUtil_DocumentCanAccessDocument(
      INTERFACE_ID_PPB_URL_UTIL, active, target, &result));
  return result;
}

PP_Var GetDocumentURL(PP_Instance instance,
                      struct PP_URLComponents_Dev* components) {
  PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance);
  if (!dispatcher)
    return PP_MakeNull();

  ReceiveSerializedVarReturnValue result;
  dispatcher->Send(new PpapiHostMsg_PPBURLUtil_GetDocumentURL(
      INTERFACE_ID_PPB_URL_UTIL, instance, &result));
  return ConvertComponentsAndReturnURL(result.Return(dispatcher), components);
}

PP_Var GetPluginInstanceURL(PP_Instance instance,
                            struct PP_URLComponents_Dev* components) {
  PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance);
  if (!dispatcher)
    return PP_MakeNull();

  ReceiveSerializedVarReturnValue result;
  dispatcher->Send(new PpapiHostMsg_PPBURLUtil_GetPluginInstanceURL(
      INTERFACE_ID_PPB_URL_UTIL, instance, &result));
  return ConvertComponentsAndReturnURL(result.Return(dispatcher), components);
}

const PPB_URLUtil_Dev url_util_interface = {
  &Canonicalize,
  &ResolveRelativeToURL,
  &ResolveRelativeToDocument,
  &IsSameSecurityOrigin,
  &DocumentCanRequest,
  &DocumentCanAccessDocument,
  &GetDocumentURL,
  &GetPluginInstanceURL
};

InterfaceProxy* CreateURLUtilProxy(Dispatcher* dispatcher,
                                   const void* target_interface) {
  return new PPB_URLUtil_Proxy(dispatcher, target_interface);
}

}  // namespace

PPB_URLUtil_Proxy::PPB_URLUtil_Proxy(Dispatcher* dispatcher,
                                     const void* target_interface)
    : InterfaceProxy(dispatcher, target_interface) {
}

PPB_URLUtil_Proxy::~PPB_URLUtil_Proxy() {
}

// static
const InterfaceProxy::Info* PPB_URLUtil_Proxy::GetInfo() {
  static const Info info = {
    &url_util_interface,
    PPB_URLUTIL_DEV_INTERFACE,
    INTERFACE_ID_PPB_URL_UTIL,
    false,
    &CreateURLUtilProxy,
  };
  return &info;
}

bool PPB_URLUtil_Proxy::OnMessageReceived(const IPC::Message& msg) {
  bool handled = true;
  IPC_BEGIN_MESSAGE_MAP(PPB_URLUtil_Proxy, msg)
    IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBURLUtil_ResolveRelativeToDocument,
                        OnMsgResolveRelativeToDocument)
    IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBURLUtil_DocumentCanRequest,
                        OnMsgDocumentCanRequest)
    IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBURLUtil_DocumentCanAccessDocument,
                        OnMsgDocumentCanAccessDocument)
    IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBURLUtil_GetDocumentURL,
                        OnMsgGetDocumentURL)
    IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBURLUtil_GetPluginInstanceURL,
                        OnMsgGetPluginInstanceURL)
    IPC_MESSAGE_UNHANDLED(handled = false)
  IPC_END_MESSAGE_MAP()
  return handled;
}

void PPB_URLUtil_Proxy::OnMsgResolveRelativeToDocument(
    PP_Instance instance,
    SerializedVarReceiveInput relative,
    SerializedVarReturnValue result) {
  result.Return(dispatcher(),
                ppb_url_util_target()->ResolveRelativeToDocument(
                    instance, relative.Get(dispatcher()), NULL));
}

void PPB_URLUtil_Proxy::OnMsgDocumentCanRequest(PP_Instance instance,
                                                SerializedVarReceiveInput url,
                                                PP_Bool* result) {
  *result = ppb_url_util_target()->DocumentCanRequest(instance,
                                                      url.Get(dispatcher()));
}

void PPB_URLUtil_Proxy::OnMsgDocumentCanAccessDocument(PP_Instance active,
                                                       PP_Instance target,
                                                       PP_Bool* result) {
  *result = ppb_url_util_target()->DocumentCanAccessDocument(
      active, target);
}

void PPB_URLUtil_Proxy::OnMsgGetDocumentURL(PP_Instance instance,
                                            SerializedVarReturnValue result) {
  result.Return(dispatcher(),
                ppb_url_util_target()->GetDocumentURL(instance, NULL));
}

void PPB_URLUtil_Proxy::OnMsgGetPluginInstanceURL(
    PP_Instance instance, SerializedVarReturnValue result) {
  result.Return(dispatcher(),
                ppb_url_util_target()->GetPluginInstanceURL(instance, NULL));
}

}  // namespace proxy
}  // namespace pp