dom/bindings/Utils.h
author Boris Zbarsky <bzbarsky@mit.edu>
Thu, 26 Jan 2012 17:23:31 +0100
changeset 85149 4119ff5bd1104150455174319f6f1edba3d41c8f
parent 85112 86362ccc381770312d0e683b599a56cf010ae310
child 85151 45bb8983777618c73775dcb52c81255764b5ae38
permissions -rw-r--r--
Implement prototype setup infrastructure.

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef mozilla_dom_bindings_Utils_h__
#define mozilla_dom_bindings_Utils_h__

#include "jsapi.h"
#include "DOMJSClass.h"
#include "XPCQuickStubs.h"
#include "XPCWrapper.h"
#include "PrototypeList.h"

/* All DOM globals must have a slot at DOM_PROTOTYPE_SLOT */
#define DOM_PROTOTYPE_SLOT (JSCLASS_GLOBAL_SLOT_COUNT + 1)

namespace mozilla {
namespace dom {
namespace bindings {

inline bool
IsDOMClass(JSClass *clasp)
{
  return clasp->flags & JSCLASS_IS_DOMJSCLASS;
}

template<class T>
inline T*
UnwrapDOMObject(JSObject *obj)
{
  MOZ_ASSERT(IsDOMClass(js::GetObjectJSClass(obj)));
  return static_cast<T*>(js::GetReservedSlot(obj,
                                             DOM_OBJECT_SLOT).toPrivate());
}

/*
 * - protoID is the ID of the prototype that corresponds to type T
 * - protoIDIndex is the index at which we expect to find protoID in the DOM
 *                class's mInterfaceChain.
 */
template<class T>
inline nsresult
UnwrapObject(JSContext *cx,
             JSObject *obj,
             prototypes::ID protoID,
             size_t protoIDIndex,
             T **value)
{
  /* First check to see whether we have a DOM object */
  JSClass *clasp = js::GetObjectJSClass(obj);
  if (!IsDOMClass(clasp)) {
    /* Maybe we have a security wrapper or outer window? */
    if (!js::IsWrapper(obj)) {
      /* Not a DOM object, not a wrapper, just bail */
      return NS_ERROR_XPC_BAD_CONVERT_JS;
    }
    
    obj = XPCWrapper::Unwrap(cx, obj);
    if (!obj) {
      return NS_ERROR_XPC_SECURITY_MANAGER_VETO;
    }
    clasp = js::GetObjectJSClass(obj);
    if (!IsDOMClass(clasp)) {
      /* We might still have an outer window */
      if (!js::IsWrapper(obj)) {
        /* Not a DOM object, not a wrapper, just bail */
        return NS_ERROR_XPC_BAD_CONVERT_JS;
      }

      obj = XPCWrapper::Unwrap(cx, obj);
      if (!obj) {
        return NS_ERROR_XPC_SECURITY_MANAGER_VETO;
      }

      MOZ_ASSERT(!js::IsWrapper(obj));
      clasp = js::GetObjectJSClass(obj);
      if (!IsDOMClass(clasp)) {
        /* We don't have a DOM object */
        return NS_ERROR_XPC_BAD_CONVERT_JS;
      }
    }
  }

  MOZ_ASSERT(IsDOMClass(clasp));

  /* This object is a DOM object.  Double-check that it is safely
     castable to T by checking whether it claims to inherit from the
     class identified by protoID. */
  DOMJSClass *domClass = static_cast<DOMJSClass*>(clasp);
  if (domClass->mInterfaceChain[protoIDIndex] == protoID) {
    *value = UnwrapDOMObject<T>(obj);
    return NS_OK;
  }

  /* It's the wrong sort of DOM object */
  return NS_ERROR_XPC_BAD_CONVERT_JS;
}

template<class T>
inline bool
UnwrapThis(JSContext *cx,
           JSObject *obj,
           prototypes::ID protoID,
           size_t protoIDIndex,
           T **value)
{
  nsresult rv = UnwrapObject(cx, obj, protoID, protoIDIndex, value);
  if (NS_FAILED(rv)) {
    return xpc_qsThrow(cx, rv);
  }
  return true;
}

template<class T>
inline nsresult
UnwrapInterfaceArg(JSContext *cx,
                   jsval v,
                   prototypes::ID protoID,
                   size_t protoIDIndex,
                   T **value,
                   nsISupports **argRef,
                   jsval *vp)
{
  if (v.isObject()) {
    // Fast-path the case when we have a DOM object.
    nsresult rv = UnwrapObject(cx, &v.toObject(), protoID, protoIDIndex, value);
    if (NS_SUCCEEDED(rv)) {
      // It was a DOM object.  We're done.
      *vp = v;
      return rv;
    }
  
    if (rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO) {
      // This is a fatal failure; just return
      return rv;
    }
  }

  // Fall back on unwrapping old-style arguments (possibly including
  // nodelist bindings).
  return xpc_qsUnwrapArg(cx, v, value, argRef, vp);
}

void
AllocateProtoCache(JSObject *obj)
{
  // Important: The () at the end ensure zero-initialization
  JSObject** protoArray = new JSObject*[prototypes::id::Count]();
  js::SetReservedSlot(obj, DOM_PROTOTYPE_SLOT, js::PrivateValue(protoArray));
}

void
DestroyProtoCache(JSObject *obj)
{
  JSObject **protoArray = static_cast<JSObject**>(
    js::GetReservedSlot(obj, DOM_PROTOTYPE_SLOT).toPrivate());
  delete [] protoArray;
}

JSObject*
GetCanonicalObjectProto(JSContext *cx, JSObject *global)
{
  JSObject* proto;
  if (!js_GetClassPrototype(cx, global, JSProto_Object, &proto)) {
    return NULL;
  }
  return proto;
}

} // namespace bindings
} // namespace dom
} // namespace mozilla

#endif /* mozilla_dom_bindings_Utils_h__ */