2007-03-22 10:30:00 -07:00
|
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
2012-05-21 04:12:37 -07:00
|
|
|
/* 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/. */
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
#include "nsCOMPtr.h"
|
|
|
|
#include "nsIAtom.h"
|
2010-07-14 18:53:11 -07:00
|
|
|
#include "nsXBLDocumentInfo.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
#include "nsIInputStream.h"
|
|
|
|
#include "nsINameSpaceManager.h"
|
|
|
|
#include "nsHashtable.h"
|
|
|
|
#include "nsIURI.h"
|
|
|
|
#include "nsIURL.h"
|
2007-05-14 02:11:38 -07:00
|
|
|
#include "nsIDOMEventTarget.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
#include "nsIChannel.h"
|
|
|
|
#include "nsXPIDLString.h"
|
|
|
|
#include "nsReadableUtils.h"
|
|
|
|
#include "nsNetUtil.h"
|
|
|
|
#include "plstr.h"
|
|
|
|
#include "nsIContent.h"
|
|
|
|
#include "nsIDocument.h"
|
|
|
|
#include "nsContentUtils.h"
|
|
|
|
#ifdef MOZ_XUL
|
|
|
|
#include "nsIXULDocument.h"
|
|
|
|
#endif
|
|
|
|
#include "nsIXMLContentSink.h"
|
|
|
|
#include "nsContentCID.h"
|
|
|
|
#include "nsXMLDocument.h"
|
|
|
|
#include "jsapi.h"
|
|
|
|
#include "nsXBLService.h"
|
|
|
|
#include "nsXBLInsertionPoint.h"
|
|
|
|
#include "nsIXPConnect.h"
|
|
|
|
#include "nsIScriptContext.h"
|
|
|
|
#include "nsCRT.h"
|
|
|
|
|
|
|
|
// Event listeners
|
2011-06-23 19:18:01 -07:00
|
|
|
#include "nsEventListenerManager.h"
|
2011-08-08 11:26:26 -07:00
|
|
|
#include "nsIDOMEventListener.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
#include "nsAttrName.h"
|
|
|
|
|
|
|
|
#include "nsGkAtoms.h"
|
|
|
|
|
|
|
|
#include "nsIDOMAttr.h"
|
|
|
|
#include "nsIDOMNamedNodeMap.h"
|
|
|
|
|
|
|
|
#include "nsXBLPrototypeHandler.h"
|
|
|
|
|
|
|
|
#include "nsXBLPrototypeBinding.h"
|
|
|
|
#include "nsXBLBinding.h"
|
|
|
|
#include "nsIPrincipal.h"
|
|
|
|
#include "nsIScriptSecurityManager.h"
|
|
|
|
#include "nsGUIEvent.h"
|
|
|
|
|
|
|
|
#include "prprf.h"
|
|
|
|
#include "nsNodeUtils.h"
|
|
|
|
|
2007-09-28 06:45:01 -07:00
|
|
|
// Nasty hack. Maybe we could move some of the classinfo utility methods
|
2012-06-19 16:01:10 -07:00
|
|
|
// (e.g. WrapNative) over to nsContentUtils?
|
2007-09-28 06:45:01 -07:00
|
|
|
#include "nsDOMClassInfo.h"
|
|
|
|
#include "nsJSUtils.h"
|
|
|
|
|
2011-07-20 12:18:54 -07:00
|
|
|
#include "mozilla/dom/Element.h"
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// Helper classes
|
|
|
|
|
|
|
|
/***********************************************************************/
|
|
|
|
//
|
|
|
|
// The JS class for XBLBinding
|
|
|
|
//
|
2008-09-06 15:21:43 -07:00
|
|
|
static void
|
2012-03-19 07:34:55 -07:00
|
|
|
XBLFinalize(JSFreeOp *fop, JSObject *obj)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2010-07-14 18:53:11 -07:00
|
|
|
nsXBLDocumentInfo* docInfo =
|
2012-02-05 12:07:23 -08:00
|
|
|
static_cast<nsXBLDocumentInfo*>(::JS_GetPrivate(obj));
|
2012-12-06 10:44:31 -08:00
|
|
|
xpc::DeferredRelease(static_cast<nsIScriptGlobalObjectOwner*>(docInfo));
|
2007-09-28 06:45:01 -07:00
|
|
|
|
2012-02-03 16:54:57 -08:00
|
|
|
nsXBLJSClass* c = static_cast<nsXBLJSClass*>(::JS_GetClass(obj));
|
2007-03-22 10:30:00 -07:00
|
|
|
c->Drop();
|
|
|
|
}
|
|
|
|
|
2012-05-29 12:01:30 -07:00
|
|
|
// XBL fields are represented on elements inheriting that field a bit trickily.
|
|
|
|
// Initially the element itself won't have a property for the field. When an
|
|
|
|
// attempt is made to access the field, the element's resolve hook won't find
|
|
|
|
// it. But the XBL prototype object, in the prototype chain of the element,
|
|
|
|
// will resolve an accessor property for the field on the XBL prototype object.
|
|
|
|
// That accessor, when used, will then (via InstallXBLField below) reify a
|
|
|
|
// property for the field onto the actual XBL-backed element.
|
|
|
|
//
|
|
|
|
// The accessor property is a plain old property backed by a getter function and
|
|
|
|
// a setter function. These properties are backed by the FieldGetter and
|
|
|
|
// FieldSetter natives; they're created by XBLResolve. The precise field to be
|
|
|
|
// reified is identified using two extra slots on the getter/setter functions.
|
|
|
|
// XBLPROTO_SLOT stores the XBL prototype object that provides the field.
|
|
|
|
// FIELD_SLOT stores the name of the field, i.e. its JavaScript property name.
|
|
|
|
//
|
|
|
|
// This two-step field installation process -- reify an accessor on the
|
|
|
|
// prototype, then have that reify an own property on the actual element -- is
|
|
|
|
// admittedly convoluted. Better would be for XBL-backed elements to be proxies
|
|
|
|
// that could resolve fields onto themselves. But given that XBL bindings are
|
|
|
|
// associated with elements mutably -- you can add/remove/change -moz-binding
|
|
|
|
// whenever you want, alas -- doing so would require all elements to be proxies,
|
|
|
|
// which isn't performant now. So we do this two-step instead.
|
|
|
|
static const uint32_t XBLPROTO_SLOT = 0;
|
|
|
|
static const uint32_t FIELD_SLOT = 1;
|
|
|
|
|
2012-08-16 11:40:05 -07:00
|
|
|
bool
|
2012-07-03 17:44:22 -07:00
|
|
|
ValueHasISupportsPrivate(const JS::Value &v)
|
2012-05-29 12:01:30 -07:00
|
|
|
{
|
2012-07-03 17:44:22 -07:00
|
|
|
if (!v.isObject()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
JSClass* clasp = ::JS_GetClass(&v.toObject());
|
2012-05-29 12:01:30 -07:00
|
|
|
const uint32_t HAS_PRIVATE_NSISUPPORTS =
|
|
|
|
JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS;
|
|
|
|
return (clasp->flags & HAS_PRIVATE_NSISUPPORTS) == HAS_PRIVATE_NSISUPPORTS;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Define a shadowing property on |this| for the XBL field defined by the
|
|
|
|
// contents of the callee's reserved slots. If the property was defined,
|
|
|
|
// *installed will be true, and idp will be set to the property name that was
|
|
|
|
// defined.
|
2008-09-06 15:21:43 -07:00
|
|
|
static JSBool
|
2012-05-29 12:01:30 -07:00
|
|
|
InstallXBLField(JSContext* cx,
|
|
|
|
JS::Handle<JSObject*> callee, JS::Handle<JSObject*> thisObj,
|
|
|
|
jsid* idp, bool* installed)
|
2007-09-28 06:45:01 -07:00
|
|
|
{
|
2012-05-29 12:01:30 -07:00
|
|
|
*installed = false;
|
2007-09-28 06:45:01 -07:00
|
|
|
|
2012-05-29 12:01:30 -07:00
|
|
|
// First ensure |this| is a reasonable XBL bound node.
|
|
|
|
//
|
|
|
|
// FieldAccessorGuard already determined whether |thisObj| was acceptable as
|
|
|
|
// |this| in terms of not throwing a TypeError. Assert this for good measure.
|
2012-07-03 17:44:22 -07:00
|
|
|
MOZ_ASSERT(ValueHasISupportsPrivate(JS::ObjectValue(*thisObj)));
|
2007-09-28 06:45:01 -07:00
|
|
|
|
2012-05-29 12:01:30 -07:00
|
|
|
// But there are some cases where we must accept |thisObj| but not install a
|
|
|
|
// property on it, or otherwise touch it. Hence this split of |this|-vetting
|
|
|
|
// duties.
|
|
|
|
nsCOMPtr<nsIXPConnectWrappedNative> xpcWrapper =
|
|
|
|
do_QueryInterface(static_cast<nsISupports*>(::JS_GetPrivate(thisObj)));
|
|
|
|
if (!xpcWrapper) {
|
|
|
|
// Looks like whatever |thisObj| is it's not our nsIContent. It might well
|
|
|
|
// be the proto our binding installed, however, where the private is the
|
|
|
|
// nsXBLDocumentInfo, so just baul out quietly. Do NOT throw an exception
|
|
|
|
// here.
|
|
|
|
//
|
|
|
|
// We could make this stricter by checking the class maybe, but whatever.
|
|
|
|
return true;
|
2007-09-28 06:45:01 -07:00
|
|
|
}
|
|
|
|
|
2012-05-29 12:01:30 -07:00
|
|
|
nsCOMPtr<nsIContent> xblNode = do_QueryWrappedNative(xpcWrapper);
|
|
|
|
if (!xblNode) {
|
|
|
|
xpc::Throw(cx, NS_ERROR_UNEXPECTED);
|
|
|
|
return false;
|
|
|
|
}
|
2007-09-28 06:45:01 -07:00
|
|
|
|
2012-05-29 12:01:30 -07:00
|
|
|
// Now that |this| is okay, actually install the field. Some of this
|
|
|
|
// installation work could have been done in XBLResolve, but this splitting
|
|
|
|
// of work seems simplest to implement and friendliest regarding lifetimes
|
|
|
|
// and potential cycles.
|
|
|
|
|
|
|
|
// Because of the possibility (due to XBL binding inheritance, because each
|
|
|
|
// XBL binding lives in its own global object) that |this| might be in a
|
|
|
|
// different compartment from the callee (not to mention that this method can
|
|
|
|
// be called with an arbitrary |this| regardless of how insane XBL is), and
|
|
|
|
// because in this method we've entered |this|'s compartment (see in
|
|
|
|
// Field[GS]etter where we attempt a cross-compartment call), we must enter
|
|
|
|
// the callee's compartment to access its reserved slots.
|
|
|
|
nsXBLPrototypeBinding* protoBinding;
|
|
|
|
nsDependentJSString fieldName;
|
|
|
|
{
|
2012-08-21 18:42:53 -07:00
|
|
|
JSAutoCompartment ac(cx, callee);
|
2012-05-29 12:01:30 -07:00
|
|
|
|
2012-09-24 18:08:22 -07:00
|
|
|
js::Rooted<JSObject*> xblProto(cx);
|
2012-05-29 12:01:30 -07:00
|
|
|
xblProto = &js::GetFunctionNativeReserved(callee, XBLPROTO_SLOT).toObject();
|
|
|
|
|
|
|
|
JS::Value name = js::GetFunctionNativeReserved(callee, FIELD_SLOT);
|
|
|
|
JSFlatString* fieldStr = JS_ASSERT_STRING_IS_FLAT(name.toString());
|
|
|
|
fieldName.init(fieldStr);
|
|
|
|
|
|
|
|
MOZ_ALWAYS_TRUE(JS_ValueToId(cx, name, idp));
|
|
|
|
|
|
|
|
JS::Value slotVal = ::JS_GetReservedSlot(xblProto, 0);
|
|
|
|
protoBinding = static_cast<nsXBLPrototypeBinding*>(slotVal.toPrivate());
|
|
|
|
MOZ_ASSERT(protoBinding);
|
|
|
|
}
|
2007-09-28 06:45:01 -07:00
|
|
|
|
|
|
|
nsXBLProtoImplField* field = protoBinding->FindField(fieldName);
|
2012-05-29 12:01:30 -07:00
|
|
|
MOZ_ASSERT(field);
|
|
|
|
|
|
|
|
// This mirrors code in nsXBLProtoImpl::InstallImplementation
|
|
|
|
nsIScriptGlobalObject* global = xblNode->OwnerDoc()->GetScriptGlobalObject();
|
|
|
|
if (!global) {
|
|
|
|
return true;
|
2007-09-28 06:45:01 -07:00
|
|
|
}
|
|
|
|
|
2012-05-29 12:01:30 -07:00
|
|
|
nsCOMPtr<nsIScriptContext> context = global->GetContext();
|
|
|
|
if (!context) {
|
|
|
|
return true;
|
2007-09-28 06:45:01 -07:00
|
|
|
}
|
2012-05-29 12:01:30 -07:00
|
|
|
|
|
|
|
nsresult rv = field->InstallField(context, thisObj, xblNode->NodePrincipal(),
|
|
|
|
protoBinding->DocURI(), installed);
|
|
|
|
if (NS_SUCCEEDED(rv)) {
|
|
|
|
return true;
|
2007-09-28 06:45:01 -07:00
|
|
|
}
|
|
|
|
|
2012-05-29 12:01:30 -07:00
|
|
|
if (!::JS_IsExceptionPending(cx)) {
|
|
|
|
xpc::Throw(cx, rv);
|
2007-09-28 06:45:01 -07:00
|
|
|
}
|
2012-05-29 12:01:30 -07:00
|
|
|
return false;
|
|
|
|
}
|
2007-09-28 06:45:01 -07:00
|
|
|
|
2012-08-16 11:40:05 -07:00
|
|
|
bool
|
2012-07-03 17:44:22 -07:00
|
|
|
FieldGetterImpl(JSContext *cx, JS::CallArgs args)
|
2012-05-29 12:01:30 -07:00
|
|
|
{
|
2012-07-03 17:44:22 -07:00
|
|
|
const JS::Value &thisv = args.thisv();
|
|
|
|
MOZ_ASSERT(ValueHasISupportsPrivate(thisv));
|
2012-05-29 12:01:30 -07:00
|
|
|
|
2012-09-24 18:08:22 -07:00
|
|
|
js::Rooted<JSObject*> thisObj(cx, &thisv.toObject());
|
2007-09-28 06:45:01 -07:00
|
|
|
|
2012-05-29 12:01:30 -07:00
|
|
|
bool installed = false;
|
2012-09-24 18:08:22 -07:00
|
|
|
js::Rooted<JSObject*> callee(cx, &args.calleev().toObject());
|
|
|
|
js::Rooted<jsid> id(cx);
|
2012-05-29 12:01:30 -07:00
|
|
|
if (!InstallXBLField(cx, callee, thisObj, id.address(), &installed)) {
|
|
|
|
return false;
|
|
|
|
}
|
2007-09-28 06:45:01 -07:00
|
|
|
|
2012-05-29 12:01:30 -07:00
|
|
|
if (!installed) {
|
2012-07-30 04:19:09 -07:00
|
|
|
args.rval().setUndefined();
|
2012-05-29 12:01:30 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-09-24 18:08:22 -07:00
|
|
|
js::Rooted<JS::Value> v(cx);
|
2012-05-29 12:01:30 -07:00
|
|
|
if (!JS_GetPropertyById(cx, thisObj, id, v.address())) {
|
|
|
|
return false;
|
|
|
|
}
|
2012-07-30 04:19:09 -07:00
|
|
|
args.rval().set(v);
|
2012-05-29 12:01:30 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static JSBool
|
2012-07-03 17:44:22 -07:00
|
|
|
FieldGetter(JSContext *cx, unsigned argc, JS::Value *vp)
|
2012-05-29 12:01:30 -07:00
|
|
|
{
|
2012-07-03 17:44:22 -07:00
|
|
|
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
|
2012-08-16 11:40:05 -07:00
|
|
|
return JS::CallNonGenericMethod<ValueHasISupportsPrivate, FieldGetterImpl>
|
|
|
|
(cx, args);
|
2012-07-03 17:44:22 -07:00
|
|
|
}
|
|
|
|
|
2012-08-16 11:40:05 -07:00
|
|
|
bool
|
2012-07-03 17:44:22 -07:00
|
|
|
FieldSetterImpl(JSContext *cx, JS::CallArgs args)
|
|
|
|
{
|
|
|
|
const JS::Value &thisv = args.thisv();
|
|
|
|
MOZ_ASSERT(ValueHasISupportsPrivate(thisv));
|
|
|
|
|
2012-09-24 18:08:22 -07:00
|
|
|
js::Rooted<JSObject*> thisObj(cx, &thisv.toObject());
|
2007-09-28 06:45:01 -07:00
|
|
|
|
2012-05-29 12:01:30 -07:00
|
|
|
bool installed = false;
|
2012-09-24 18:08:22 -07:00
|
|
|
js::Rooted<JSObject*> callee(cx, &args.calleev().toObject());
|
|
|
|
js::Rooted<jsid> id(cx);
|
2012-05-29 12:01:30 -07:00
|
|
|
if (!InstallXBLField(cx, callee, thisObj, id.address(), &installed)) {
|
|
|
|
return false;
|
2007-10-02 07:38:35 -07:00
|
|
|
}
|
|
|
|
|
2012-10-17 13:42:58 -07:00
|
|
|
if (installed) {
|
|
|
|
js::Rooted<JS::Value> v(cx,
|
|
|
|
args.length() > 0 ? args[0] : JS::UndefinedValue());
|
|
|
|
if (!::JS_SetPropertyById(cx, thisObj, id, v.address())) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
args.rval().setUndefined();
|
|
|
|
return true;
|
2012-05-29 12:01:30 -07:00
|
|
|
}
|
|
|
|
|
2012-07-03 17:44:22 -07:00
|
|
|
static JSBool
|
|
|
|
FieldSetter(JSContext *cx, unsigned argc, JS::Value *vp)
|
|
|
|
{
|
|
|
|
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
|
2012-08-16 11:40:05 -07:00
|
|
|
return JS::CallNonGenericMethod<ValueHasISupportsPrivate, FieldSetterImpl>
|
|
|
|
(cx, args);
|
2012-07-03 17:44:22 -07:00
|
|
|
}
|
|
|
|
|
2012-05-29 12:01:30 -07:00
|
|
|
static JSBool
|
|
|
|
XBLResolve(JSContext *cx, JSHandleObject obj, JSHandleId id, unsigned flags,
|
2012-07-04 11:12:16 -07:00
|
|
|
JSMutableHandleObject objp)
|
2012-05-29 12:01:30 -07:00
|
|
|
{
|
2012-07-04 11:12:16 -07:00
|
|
|
objp.set(NULL);
|
2012-05-29 12:01:30 -07:00
|
|
|
|
|
|
|
if (!JSID_IS_STRING(id)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsXBLPrototypeBinding* protoBinding =
|
|
|
|
static_cast<nsXBLPrototypeBinding*>(::JS_GetReservedSlot(obj, 0).toPrivate());
|
|
|
|
MOZ_ASSERT(protoBinding);
|
|
|
|
|
|
|
|
// If the field's not present, don't resolve it. Also don't resolve it if the
|
|
|
|
// field is empty; see also nsXBLProtoImplField::InstallField which also must
|
|
|
|
// implement the not-empty requirement.
|
|
|
|
nsDependentJSString fieldName(id);
|
|
|
|
nsXBLProtoImplField* field = protoBinding->FindField(fieldName);
|
|
|
|
if (!field || field->IsEmpty()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We have a field: now install a getter/setter pair which will resolve the
|
|
|
|
// field onto the actual object, when invoked.
|
2012-09-24 18:08:22 -07:00
|
|
|
js::Rooted<JSObject*> global(cx, JS_GetGlobalForObject(cx, obj));
|
2012-05-29 12:01:30 -07:00
|
|
|
|
2012-09-24 18:08:22 -07:00
|
|
|
js::Rooted<JSObject*> get(cx);
|
2012-05-29 12:01:30 -07:00
|
|
|
get = ::JS_GetFunctionObject(js::NewFunctionByIdWithReserved(cx, FieldGetter,
|
|
|
|
0, 0, global,
|
|
|
|
id));
|
|
|
|
if (!get) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
js::SetFunctionNativeReserved(get, XBLPROTO_SLOT, JS::ObjectValue(*obj));
|
|
|
|
js::SetFunctionNativeReserved(get, FIELD_SLOT,
|
|
|
|
JS::StringValue(JSID_TO_STRING(id)));
|
|
|
|
|
2012-09-24 18:08:22 -07:00
|
|
|
js::Rooted<JSObject*> set(cx);
|
2012-05-29 12:01:30 -07:00
|
|
|
set = ::JS_GetFunctionObject(js::NewFunctionByIdWithReserved(cx, FieldSetter,
|
|
|
|
1, 0, global,
|
|
|
|
id));
|
|
|
|
if (!set) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
js::SetFunctionNativeReserved(set, XBLPROTO_SLOT, JS::ObjectValue(*obj));
|
|
|
|
js::SetFunctionNativeReserved(set, FIELD_SLOT,
|
|
|
|
JS::StringValue(JSID_TO_STRING(id)));
|
|
|
|
|
|
|
|
if (!::JS_DefinePropertyById(cx, obj, id, JS::UndefinedValue(),
|
2012-07-04 13:34:42 -07:00
|
|
|
JS_DATA_TO_FUNC_PTR(JSPropertyOp, get.get()),
|
|
|
|
JS_DATA_TO_FUNC_PTR(JSStrictPropertyOp, set.get()),
|
2012-05-29 12:01:30 -07:00
|
|
|
field->AccessorAttributes())) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-07-04 11:12:16 -07:00
|
|
|
objp.set(obj);
|
2012-05-29 12:01:30 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static JSBool
|
|
|
|
XBLEnumerate(JSContext *cx, JS::Handle<JSObject*> obj)
|
|
|
|
{
|
|
|
|
nsXBLPrototypeBinding* protoBinding =
|
|
|
|
static_cast<nsXBLPrototypeBinding*>(::JS_GetReservedSlot(obj, 0).toPrivate());
|
|
|
|
MOZ_ASSERT(protoBinding);
|
|
|
|
|
|
|
|
return protoBinding->ResolveAllFields(cx, obj);
|
2007-09-28 06:45:01 -07:00
|
|
|
}
|
|
|
|
|
2012-12-12 07:10:02 -08:00
|
|
|
uint64_t nsXBLJSClass::sIdCount = 0;
|
|
|
|
|
|
|
|
nsXBLJSClass::nsXBLJSClass(const nsAFlatCString& aClassName,
|
|
|
|
const nsCString& aKey)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
memset(this, 0, sizeof(nsXBLJSClass));
|
2007-07-08 00:08:04 -07:00
|
|
|
next = prev = static_cast<JSCList*>(this);
|
2007-03-22 10:30:00 -07:00
|
|
|
name = ToNewCString(aClassName);
|
2007-09-28 06:45:01 -07:00
|
|
|
flags =
|
|
|
|
JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS |
|
2012-05-29 12:01:30 -07:00
|
|
|
JSCLASS_NEW_RESOLVE |
|
2007-09-28 06:45:01 -07:00
|
|
|
// Our one reserved slot holds the relevant nsXBLPrototypeBinding
|
|
|
|
JSCLASS_HAS_RESERVED_SLOTS(1);
|
2011-02-09 11:31:40 -08:00
|
|
|
addProperty = delProperty = getProperty = ::JS_PropertyStub;
|
|
|
|
setProperty = ::JS_StrictPropertyStub;
|
2012-05-29 12:01:30 -07:00
|
|
|
enumerate = XBLEnumerate;
|
2007-09-28 06:45:01 -07:00
|
|
|
resolve = (JSResolveOp)XBLResolve;
|
2007-03-22 10:30:00 -07:00
|
|
|
convert = ::JS_ConvertStub;
|
|
|
|
finalize = XBLFinalize;
|
2012-12-12 07:10:02 -08:00
|
|
|
mKey = aKey;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
nsrefcnt
|
|
|
|
nsXBLJSClass::Destroy()
|
|
|
|
{
|
2007-07-08 00:08:04 -07:00
|
|
|
NS_ASSERTION(next == prev && prev == static_cast<JSCList*>(this),
|
2007-03-22 10:30:00 -07:00
|
|
|
"referenced nsXBLJSClass is on LRU list already!?");
|
|
|
|
|
|
|
|
if (nsXBLService::gClassTable) {
|
2012-12-12 07:10:02 -08:00
|
|
|
nsCStringKey key(mKey);
|
2007-03-22 10:30:00 -07:00
|
|
|
(nsXBLService::gClassTable)->Remove(&key);
|
2012-12-12 07:10:02 -08:00
|
|
|
mKey.Truncate();
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (nsXBLService::gClassLRUListLength >= nsXBLService::gClassLRUListQuota) {
|
|
|
|
// Over LRU list quota, just unhash and delete this class.
|
|
|
|
delete this;
|
|
|
|
} else {
|
|
|
|
// Put this most-recently-used class on end of the LRU-sorted freelist.
|
2007-07-08 00:08:04 -07:00
|
|
|
JSCList* mru = static_cast<JSCList*>(this);
|
2007-03-22 10:30:00 -07:00
|
|
|
JS_APPEND_LINK(mru, &nsXBLService::gClassLRUList);
|
|
|
|
nsXBLService::gClassLRUListLength++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Implementation /////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
// Constructors/Destructors
|
|
|
|
nsXBLBinding::nsXBLBinding(nsXBLPrototypeBinding* aBinding)
|
2012-04-17 12:23:11 -07:00
|
|
|
: mIsStyleBinding(true),
|
|
|
|
mMarkedForDeath(false),
|
|
|
|
mPrototypeBinding(aBinding),
|
2012-07-30 07:20:58 -07:00
|
|
|
mInsertionPointTable(nullptr)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
NS_ASSERTION(mPrototypeBinding, "Must have a prototype binding!");
|
|
|
|
// Grab a ref to the document info so the prototype binding won't die
|
|
|
|
NS_ADDREF(mPrototypeBinding->XBLDocumentInfo());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
nsXBLBinding::~nsXBLBinding(void)
|
|
|
|
{
|
2009-01-06 11:37:28 -08:00
|
|
|
if (mContent) {
|
2011-10-18 03:53:36 -07:00
|
|
|
nsXBLBinding::UninstallAnonymousContent(mContent->OwnerDoc(), mContent);
|
2009-01-06 11:37:28 -08:00
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
delete mInsertionPointTable;
|
2010-07-14 18:53:11 -07:00
|
|
|
nsXBLDocumentInfo* info = mPrototypeBinding->XBLDocumentInfo();
|
2007-03-22 10:30:00 -07:00
|
|
|
NS_RELEASE(info);
|
|
|
|
}
|
|
|
|
|
2008-10-10 08:04:34 -07:00
|
|
|
static PLDHashOperator
|
2007-05-24 07:10:02 -07:00
|
|
|
TraverseKey(nsISupports* aKey, nsInsertionPointList* aData, void* aClosure)
|
|
|
|
{
|
|
|
|
nsCycleCollectionTraversalCallback &cb =
|
2007-07-08 00:08:04 -07:00
|
|
|
*static_cast<nsCycleCollectionTraversalCallback*>(aClosure);
|
2007-05-24 07:10:02 -07:00
|
|
|
|
2008-03-17 16:11:08 -07:00
|
|
|
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mInsertionPointTable key");
|
2007-05-24 07:10:02 -07:00
|
|
|
cb.NoteXPCOMChild(aKey);
|
|
|
|
if (aData) {
|
2012-11-14 23:32:39 -08:00
|
|
|
ImplCycleCollectionTraverse(cb, *aData, "mInsertionPointTable value");
|
2007-05-24 07:10:02 -07:00
|
|
|
}
|
|
|
|
return PL_DHASH_NEXT;
|
|
|
|
}
|
|
|
|
|
2012-06-03 23:30:26 -07:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_NATIVE_CLASS(nsXBLBinding)
|
2012-11-22 09:15:38 -08:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXBLBinding)
|
2007-05-24 07:10:02 -07:00
|
|
|
// XXX Probably can't unlink mPrototypeBinding->XBLDocumentInfo(), because
|
|
|
|
// mPrototypeBinding is weak.
|
2009-01-06 11:37:28 -08:00
|
|
|
if (tmp->mContent) {
|
2011-10-18 03:53:36 -07:00
|
|
|
nsXBLBinding::UninstallAnonymousContent(tmp->mContent->OwnerDoc(),
|
2009-01-06 11:37:28 -08:00
|
|
|
tmp->mContent);
|
|
|
|
}
|
2012-11-14 23:32:40 -08:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mContent)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mNextBinding)
|
2011-06-22 05:56:44 -07:00
|
|
|
delete tmp->mInsertionPointTable;
|
2012-07-30 07:20:58 -07:00
|
|
|
tmp->mInsertionPointTable = nullptr;
|
2007-05-24 07:10:02 -07:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
2012-11-22 09:15:38 -08:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXBLBinding)
|
2012-01-23 15:25:53 -08:00
|
|
|
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
|
|
|
|
"mPrototypeBinding->XBLDocumentInfo()");
|
2010-11-11 14:52:30 -08:00
|
|
|
cb.NoteXPCOMChild(static_cast<nsIScriptGlobalObjectOwner*>(
|
|
|
|
tmp->mPrototypeBinding->XBLDocumentInfo()));
|
2012-11-14 23:32:40 -08:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContent)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNextBinding)
|
2007-05-24 07:10:02 -07:00
|
|
|
if (tmp->mInsertionPointTable)
|
|
|
|
tmp->mInsertionPointTable->EnumerateRead(TraverseKey, &cb);
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsXBLBinding, AddRef)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsXBLBinding, Release)
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
void
|
|
|
|
nsXBLBinding::SetBaseBinding(nsXBLBinding* aBinding)
|
|
|
|
{
|
|
|
|
if (mNextBinding) {
|
|
|
|
NS_ERROR("Base XBL binding is already defined!");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
mNextBinding = aBinding; // Comptr handles rel/add
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2012-10-10 12:04:42 -07:00
|
|
|
nsXBLBinding::InstallAnonymousContent(nsIContent* aAnonParent, nsIContent* aElement,
|
|
|
|
bool aChromeOnlyContent)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
// We need to ensure two things.
|
|
|
|
// (1) The anonymous content should be fooled into thinking it's in the bound
|
|
|
|
// element's document, assuming that the bound element is in a document
|
|
|
|
// Note that we don't change the current doc of aAnonParent here, since that
|
|
|
|
// quite simply does not matter. aAnonParent is just a way of keeping refs
|
|
|
|
// to all its kids, which are anonymous content from the point of view of
|
|
|
|
// aElement.
|
|
|
|
// (2) The children's parent back pointer should not be to this synthetic root
|
|
|
|
// but should instead point to the enclosing parent element.
|
|
|
|
nsIDocument* doc = aElement->GetCurrentDoc();
|
2011-09-28 23:19:26 -07:00
|
|
|
bool allowScripts = AllowScripts();
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-04-11 10:29:06 -07:00
|
|
|
nsAutoScriptBlocker scriptBlocker;
|
2011-09-27 00:54:58 -07:00
|
|
|
for (nsIContent* child = aAnonParent->GetFirstChild();
|
|
|
|
child;
|
|
|
|
child = child->GetNextSibling()) {
|
2007-03-22 10:30:00 -07:00
|
|
|
child->UnbindFromTree();
|
2012-10-10 12:04:42 -07:00
|
|
|
if (aChromeOnlyContent) {
|
|
|
|
child->SetFlags(NODE_CHROME_ONLY_ACCESS |
|
|
|
|
NODE_IS_ROOT_OF_CHROME_ONLY_ACCESS);
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
nsresult rv =
|
|
|
|
child->BindToTree(doc, aElement, mBoundElement, allowScripts);
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
// Oh, well... Just give up.
|
|
|
|
// XXXbz This really shouldn't be a void method!
|
|
|
|
child->UnbindFromTree();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-02-24 10:39:09 -08:00
|
|
|
child->SetFlags(NODE_IS_ANONYMOUS);
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
#ifdef MOZ_XUL
|
|
|
|
// To make XUL templates work (and other goodies that happen when
|
|
|
|
// an element is added to a XUL document), we need to notify the
|
|
|
|
// XUL document using its special API.
|
|
|
|
nsCOMPtr<nsIXULDocument> xuldoc(do_QueryInterface(doc));
|
|
|
|
if (xuldoc)
|
|
|
|
xuldoc->AddSubtreeToDocument(child);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-01-06 11:37:28 -08:00
|
|
|
void
|
|
|
|
nsXBLBinding::UninstallAnonymousContent(nsIDocument* aDocument,
|
|
|
|
nsIContent* aAnonParent)
|
|
|
|
{
|
|
|
|
nsAutoScriptBlocker scriptBlocker;
|
|
|
|
// Hold a strong ref while doing this, just in case.
|
|
|
|
nsCOMPtr<nsIContent> anonParent = aAnonParent;
|
|
|
|
#ifdef MOZ_XUL
|
|
|
|
nsCOMPtr<nsIXULDocument> xuldoc =
|
|
|
|
do_QueryInterface(aDocument);
|
|
|
|
#endif
|
2011-09-27 00:54:58 -07:00
|
|
|
for (nsIContent* child = aAnonParent->GetFirstChild();
|
|
|
|
child;
|
|
|
|
child = child->GetNextSibling()) {
|
2009-01-06 11:37:28 -08:00
|
|
|
child->UnbindFromTree();
|
|
|
|
#ifdef MOZ_XUL
|
|
|
|
if (xuldoc) {
|
|
|
|
xuldoc->RemoveSubtreeFromDocument(child);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
void
|
|
|
|
nsXBLBinding::SetBoundElement(nsIContent* aElement)
|
|
|
|
{
|
|
|
|
mBoundElement = aElement;
|
|
|
|
if (mNextBinding)
|
|
|
|
mNextBinding->SetBoundElement(aElement);
|
|
|
|
}
|
|
|
|
|
2011-09-28 23:19:26 -07:00
|
|
|
bool
|
2007-03-22 10:30:00 -07:00
|
|
|
nsXBLBinding::HasStyleSheets() const
|
|
|
|
{
|
|
|
|
// Find out if we need to re-resolve style. We'll need to do this
|
|
|
|
// if we have additional stylesheets in our binding document.
|
|
|
|
if (mPrototypeBinding->HasStyleSheets())
|
2011-10-17 07:59:28 -07:00
|
|
|
return true;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2011-10-17 07:59:28 -07:00
|
|
|
return mNextBinding ? mNextBinding->HasStyleSheets() : false;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
struct EnumData {
|
|
|
|
nsXBLBinding* mBinding;
|
|
|
|
|
|
|
|
EnumData(nsXBLBinding* aBinding)
|
|
|
|
:mBinding(aBinding)
|
2007-04-23 07:21:53 -07:00
|
|
|
{}
|
2007-03-22 10:30:00 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
struct ContentListData : public EnumData {
|
|
|
|
nsBindingManager* mBindingManager;
|
|
|
|
nsresult mRv;
|
|
|
|
|
|
|
|
ContentListData(nsXBLBinding* aBinding, nsBindingManager* aManager)
|
|
|
|
:EnumData(aBinding), mBindingManager(aManager), mRv(NS_OK)
|
2007-04-23 07:21:53 -07:00
|
|
|
{}
|
2007-03-22 10:30:00 -07:00
|
|
|
};
|
|
|
|
|
2008-10-10 08:04:34 -07:00
|
|
|
static PLDHashOperator
|
2007-03-22 10:30:00 -07:00
|
|
|
BuildContentLists(nsISupports* aKey,
|
|
|
|
nsAutoPtr<nsInsertionPointList>& aData,
|
|
|
|
void* aClosure)
|
|
|
|
{
|
|
|
|
ContentListData* data = (ContentListData*)aClosure;
|
|
|
|
nsBindingManager* bm = data->mBindingManager;
|
|
|
|
nsXBLBinding* binding = data->mBinding;
|
|
|
|
|
|
|
|
nsIContent *boundElement = binding->GetBoundElement();
|
|
|
|
|
2012-08-22 08:56:38 -07:00
|
|
|
int32_t count = aData->Length();
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
if (count == 0)
|
|
|
|
return PL_DHASH_NEXT;
|
|
|
|
|
|
|
|
// Figure out the relevant content node.
|
|
|
|
nsXBLInsertionPoint* currPoint = aData->ElementAt(0);
|
|
|
|
nsCOMPtr<nsIContent> parent = currPoint->GetInsertionParent();
|
2007-10-12 04:07:29 -07:00
|
|
|
if (!parent) {
|
|
|
|
data->mRv = NS_ERROR_FAILURE;
|
|
|
|
return PL_DHASH_STOP;
|
|
|
|
}
|
2012-08-22 08:56:38 -07:00
|
|
|
int32_t currIndex = currPoint->GetInsertionIndex();
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2011-03-28 14:18:08 -07:00
|
|
|
// XXX Could this array just be altered in place and passed directly to
|
|
|
|
// SetContentListFor? We'd save space if we could pull this off.
|
|
|
|
nsInsertionPointList* contentList = new nsInsertionPointList;
|
|
|
|
if (!contentList) {
|
|
|
|
data->mRv = NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
return PL_DHASH_STOP;
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
nsCOMPtr<nsIDOMNodeList> nodeList;
|
|
|
|
if (parent == boundElement) {
|
|
|
|
// We are altering anonymous nodes to accommodate insertion points.
|
|
|
|
nodeList = binding->GetAnonymousNodes();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// We are altering the explicit content list of a node to accommodate insertion points.
|
|
|
|
nsCOMPtr<nsIDOMNode> node(do_QueryInterface(parent));
|
|
|
|
node->GetChildNodes(getter_AddRefs(nodeList));
|
|
|
|
}
|
|
|
|
|
2012-07-30 07:20:58 -07:00
|
|
|
nsXBLInsertionPoint* pseudoPoint = nullptr;
|
2012-08-22 08:56:38 -07:00
|
|
|
uint32_t childCount;
|
2007-03-22 10:30:00 -07:00
|
|
|
nodeList->GetLength(&childCount);
|
2012-08-22 08:56:38 -07:00
|
|
|
int32_t j = 0;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2012-08-22 08:56:38 -07:00
|
|
|
for (uint32_t i = 0; i < childCount; i++) {
|
2007-03-22 10:30:00 -07:00
|
|
|
nsCOMPtr<nsIDOMNode> node;
|
|
|
|
nodeList->Item(i, getter_AddRefs(node));
|
|
|
|
nsCOMPtr<nsIContent> child(do_QueryInterface(node));
|
2012-08-22 08:56:38 -07:00
|
|
|
if (((int32_t)i) == currIndex) {
|
2007-03-22 10:30:00 -07:00
|
|
|
// Add the currPoint to the insertion point list.
|
|
|
|
contentList->AppendElement(currPoint);
|
|
|
|
|
|
|
|
// Get the next real insertion point and update our currIndex.
|
|
|
|
j++;
|
|
|
|
if (j < count) {
|
|
|
|
currPoint = aData->ElementAt(j);
|
|
|
|
currIndex = currPoint->GetInsertionIndex();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Null out our current pseudo-point.
|
2012-07-30 07:20:58 -07:00
|
|
|
pseudoPoint = nullptr;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!pseudoPoint) {
|
2012-08-22 08:56:38 -07:00
|
|
|
pseudoPoint = new nsXBLInsertionPoint(parent, (uint32_t) -1, nullptr);
|
2007-03-22 10:30:00 -07:00
|
|
|
if (pseudoPoint) {
|
|
|
|
contentList->AppendElement(pseudoPoint);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (pseudoPoint) {
|
|
|
|
pseudoPoint->AddChild(child);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add in all the remaining insertion points.
|
|
|
|
contentList->AppendElements(aData->Elements() + j, count - j);
|
|
|
|
|
|
|
|
// Now set the content list using the binding manager,
|
|
|
|
// If the bound element is the parent, then we alter the anonymous node list
|
|
|
|
// instead. This allows us to always maintain two distinct lists should
|
|
|
|
// insertion points be nested into an inner binding.
|
|
|
|
if (parent == boundElement)
|
|
|
|
bm->SetAnonymousNodesFor(parent, contentList);
|
|
|
|
else
|
|
|
|
bm->SetContentListFor(parent, contentList);
|
|
|
|
return PL_DHASH_NEXT;
|
|
|
|
}
|
|
|
|
|
2008-10-10 08:04:34 -07:00
|
|
|
static PLDHashOperator
|
2007-03-22 10:30:00 -07:00
|
|
|
RealizeDefaultContent(nsISupports* aKey,
|
|
|
|
nsAutoPtr<nsInsertionPointList>& aData,
|
|
|
|
void* aClosure)
|
|
|
|
{
|
|
|
|
ContentListData* data = (ContentListData*)aClosure;
|
|
|
|
nsBindingManager* bm = data->mBindingManager;
|
|
|
|
nsXBLBinding* binding = data->mBinding;
|
|
|
|
|
2012-08-22 08:56:38 -07:00
|
|
|
int32_t count = aData->Length();
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2012-08-22 08:56:38 -07:00
|
|
|
for (int32_t i = 0; i < count; i++) {
|
2007-03-22 10:30:00 -07:00
|
|
|
nsXBLInsertionPoint* currPoint = aData->ElementAt(i);
|
2012-08-22 08:56:38 -07:00
|
|
|
int32_t insCount = currPoint->ChildCount();
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
if (insCount == 0) {
|
|
|
|
nsCOMPtr<nsIContent> defContent = currPoint->GetDefaultContentTemplate();
|
|
|
|
if (defContent) {
|
|
|
|
// We need to take this template and use it to realize the
|
|
|
|
// actual default content (through cloning).
|
|
|
|
// Clone this insertion point element.
|
|
|
|
nsCOMPtr<nsIContent> insParent = currPoint->GetInsertionParent();
|
2007-10-12 04:07:29 -07:00
|
|
|
if (!insParent) {
|
|
|
|
data->mRv = NS_ERROR_FAILURE;
|
|
|
|
return PL_DHASH_STOP;
|
|
|
|
}
|
2011-10-18 03:53:36 -07:00
|
|
|
nsIDocument *document = insParent->OwnerDoc();
|
2012-10-09 05:31:24 -07:00
|
|
|
nsCOMPtr<nsINode> clonedNode;
|
2007-03-22 10:30:00 -07:00
|
|
|
nsCOMArray<nsINode> nodesWithProperties;
|
2011-10-17 07:59:28 -07:00
|
|
|
nsNodeUtils::Clone(defContent, true, document->NodeInfoManager(),
|
2007-03-22 10:30:00 -07:00
|
|
|
nodesWithProperties, getter_AddRefs(clonedNode));
|
|
|
|
|
|
|
|
// Now that we have the cloned content, install the default content as
|
|
|
|
// if it were additional anonymous content.
|
|
|
|
nsCOMPtr<nsIContent> clonedContent(do_QueryInterface(clonedNode));
|
2012-10-10 12:04:42 -07:00
|
|
|
binding->InstallAnonymousContent(clonedContent, insParent,
|
|
|
|
binding->PrototypeBinding()->
|
|
|
|
ChromeOnlyContent());
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// Cache the clone so that it can be properly destroyed if/when our
|
|
|
|
// other anonymous content is destroyed.
|
|
|
|
currPoint->SetDefaultContent(clonedContent);
|
|
|
|
|
|
|
|
// Now make sure the kids of the clone are added to the insertion point as
|
|
|
|
// children.
|
2011-09-27 00:54:58 -07:00
|
|
|
for (nsIContent* child = clonedContent->GetFirstChild();
|
|
|
|
child;
|
|
|
|
child = child->GetNextSibling()) {
|
|
|
|
bm->SetInsertionParent(child, insParent);
|
|
|
|
currPoint->AddChild(child);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return PL_DHASH_NEXT;
|
|
|
|
}
|
|
|
|
|
2008-10-10 08:04:34 -07:00
|
|
|
static PLDHashOperator
|
2007-03-22 10:30:00 -07:00
|
|
|
ChangeDocumentForDefaultContent(nsISupports* aKey,
|
|
|
|
nsAutoPtr<nsInsertionPointList>& aData,
|
|
|
|
void* aClosure)
|
|
|
|
{
|
2012-08-22 08:56:38 -07:00
|
|
|
int32_t count = aData->Length();
|
|
|
|
for (int32_t i = 0; i < count; i++) {
|
2008-01-28 15:34:28 -08:00
|
|
|
aData->ElementAt(i)->UnbindDefaultContent();
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return PL_DHASH_NEXT;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsXBLBinding::GenerateAnonymousContent()
|
|
|
|
{
|
2009-11-18 07:14:14 -08:00
|
|
|
NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
|
|
|
|
"Someone forgot a script blocker");
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// Fetch the content element for this binding.
|
|
|
|
nsIContent* content =
|
|
|
|
mPrototypeBinding->GetImmediateChild(nsGkAtoms::content);
|
|
|
|
|
|
|
|
if (!content) {
|
|
|
|
// We have no anonymous content.
|
|
|
|
if (mNextBinding)
|
|
|
|
mNextBinding->GenerateAnonymousContent();
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find out if we're really building kids or if we're just
|
|
|
|
// using the attribute-setting shorthand hack.
|
2012-08-22 08:56:38 -07:00
|
|
|
uint32_t contentCount = content->GetChildCount();
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// Plan to build the content by default.
|
2011-09-28 23:19:26 -07:00
|
|
|
bool hasContent = (contentCount > 0);
|
|
|
|
bool hasInsertionPoints = mPrototypeBinding->HasInsertionPoints();
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
// See if there's an includes attribute.
|
|
|
|
if (nsContentUtils::HasNonEmptyAttr(content, kNameSpaceID_None,
|
|
|
|
nsGkAtoms::includes)) {
|
2012-09-01 19:35:17 -07:00
|
|
|
nsAutoCString message("An XBL Binding with URI ");
|
|
|
|
nsAutoCString uri;
|
2007-03-22 10:30:00 -07:00
|
|
|
mPrototypeBinding->BindingURI()->GetSpec(uri);
|
|
|
|
message += uri;
|
|
|
|
message += " is still using the deprecated\n<content includes=\"\"> syntax! Use <children> instead!\n";
|
|
|
|
NS_WARNING(message.get());
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (hasContent || hasInsertionPoints) {
|
2011-10-18 03:53:36 -07:00
|
|
|
nsIDocument* doc = mBoundElement->OwnerDoc();
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
nsBindingManager *bindingManager = doc->BindingManager();
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMNodeList> children;
|
|
|
|
bindingManager->GetContentListFor(mBoundElement, getter_AddRefs(children));
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMNode> node;
|
|
|
|
nsCOMPtr<nsIContent> childContent;
|
2012-08-22 08:56:38 -07:00
|
|
|
uint32_t length;
|
2007-03-22 10:30:00 -07:00
|
|
|
children->GetLength(&length);
|
|
|
|
if (length > 0 && !hasInsertionPoints) {
|
|
|
|
// There are children being placed underneath us, but we have no specified
|
|
|
|
// insertion points, and therefore no place to put the kids. Don't generate
|
|
|
|
// anonymous content.
|
|
|
|
// Special case template and observes.
|
2012-08-22 08:56:38 -07:00
|
|
|
for (uint32_t i = 0; i < length; i++) {
|
2007-03-22 10:30:00 -07:00
|
|
|
children->Item(i, getter_AddRefs(node));
|
|
|
|
childContent = do_QueryInterface(node);
|
|
|
|
|
|
|
|
nsINodeInfo *ni = childContent->NodeInfo();
|
|
|
|
nsIAtom *localName = ni->NameAtom();
|
|
|
|
if (ni->NamespaceID() != kNameSpaceID_XUL ||
|
|
|
|
(localName != nsGkAtoms::observes &&
|
|
|
|
localName != nsGkAtoms::_template)) {
|
2011-10-17 07:59:28 -07:00
|
|
|
hasContent = false;
|
2007-03-22 10:30:00 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hasContent || hasInsertionPoints) {
|
2012-10-09 05:31:24 -07:00
|
|
|
nsCOMPtr<nsINode> clonedNode;
|
2007-03-22 10:30:00 -07:00
|
|
|
nsCOMArray<nsINode> nodesWithProperties;
|
2011-10-17 07:59:28 -07:00
|
|
|
nsNodeUtils::Clone(content, true, doc->NodeInfoManager(),
|
2007-03-22 10:30:00 -07:00
|
|
|
nodesWithProperties, getter_AddRefs(clonedNode));
|
|
|
|
|
|
|
|
mContent = do_QueryInterface(clonedNode);
|
2012-10-10 12:04:42 -07:00
|
|
|
InstallAnonymousContent(mContent, mBoundElement,
|
|
|
|
mPrototypeBinding->ChromeOnlyContent());
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
if (hasInsertionPoints) {
|
|
|
|
// Now check and see if we have a single insertion point
|
|
|
|
// or multiple insertion points.
|
|
|
|
|
|
|
|
// Enumerate the prototype binding's insertion table to build
|
|
|
|
// our table of instantiated insertion points.
|
|
|
|
mPrototypeBinding->InstantiateInsertionPoints(this);
|
|
|
|
|
|
|
|
// We now have our insertion point table constructed. We
|
|
|
|
// enumerate this table. For each array of insertion points
|
|
|
|
// bundled under the same content node, we generate a content
|
|
|
|
// list. In the case of the bound element, we generate a new
|
|
|
|
// anonymous node list that will be used in place of the binding's
|
|
|
|
// cached anonymous node list.
|
|
|
|
ContentListData data(this, bindingManager);
|
|
|
|
mInsertionPointTable->Enumerate(BuildContentLists, &data);
|
|
|
|
if (NS_FAILED(data.mRv)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We need to place the children
|
|
|
|
// at their respective insertion points.
|
2012-08-22 08:56:38 -07:00
|
|
|
uint32_t index = 0;
|
2011-09-28 23:19:26 -07:00
|
|
|
bool multiplePoints = false;
|
2007-03-22 10:30:00 -07:00
|
|
|
nsIContent *singlePoint = GetSingleInsertionPoint(&index,
|
|
|
|
&multiplePoints);
|
|
|
|
|
|
|
|
if (children) {
|
|
|
|
if (multiplePoints) {
|
|
|
|
// We must walk the entire content list in order to determine where
|
|
|
|
// each child belongs.
|
|
|
|
children->GetLength(&length);
|
2012-08-22 08:56:38 -07:00
|
|
|
for (uint32_t i = 0; i < length; i++) {
|
2007-03-22 10:30:00 -07:00
|
|
|
children->Item(i, getter_AddRefs(node));
|
|
|
|
childContent = do_QueryInterface(node);
|
|
|
|
|
|
|
|
// Now determine the insertion point in the prototype table.
|
2012-08-22 08:56:38 -07:00
|
|
|
uint32_t index;
|
2007-03-22 10:30:00 -07:00
|
|
|
nsIContent *point = GetInsertionPoint(childContent, &index);
|
|
|
|
bindingManager->SetInsertionParent(childContent, point);
|
|
|
|
|
|
|
|
// Find the correct nsIXBLInsertion point in our table.
|
2012-07-30 07:20:58 -07:00
|
|
|
nsInsertionPointList* arr = nullptr;
|
2007-03-22 10:30:00 -07:00
|
|
|
GetInsertionPointsFor(point, &arr);
|
2012-07-30 07:20:58 -07:00
|
|
|
nsXBLInsertionPoint* insertionPoint = nullptr;
|
2012-08-22 08:56:38 -07:00
|
|
|
int32_t arrCount = arr->Length();
|
|
|
|
for (int32_t j = 0; j < arrCount; j++) {
|
2007-03-22 10:30:00 -07:00
|
|
|
insertionPoint = arr->ElementAt(j);
|
|
|
|
if (insertionPoint->Matches(point, index))
|
|
|
|
break;
|
2012-07-30 07:20:58 -07:00
|
|
|
insertionPoint = nullptr;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (insertionPoint)
|
|
|
|
insertionPoint->AddChild(childContent);
|
|
|
|
else {
|
|
|
|
// We were unable to place this child. All anonymous content
|
|
|
|
// should be thrown out. Special-case template and observes.
|
|
|
|
|
|
|
|
nsINodeInfo *ni = childContent->NodeInfo();
|
|
|
|
nsIAtom *localName = ni->NameAtom();
|
|
|
|
if (ni->NamespaceID() != kNameSpaceID_XUL ||
|
|
|
|
(localName != nsGkAtoms::observes &&
|
|
|
|
localName != nsGkAtoms::_template)) {
|
2008-12-19 15:40:00 -08:00
|
|
|
// Undo InstallAnonymousContent
|
2009-01-06 11:37:28 -08:00
|
|
|
UninstallAnonymousContent(doc, mContent);
|
2008-12-19 15:40:00 -08:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// Kill all anonymous content.
|
2012-07-30 07:20:58 -07:00
|
|
|
mContent = nullptr;
|
|
|
|
bindingManager->SetContentListFor(mBoundElement, nullptr);
|
|
|
|
bindingManager->SetAnonymousNodesFor(mBoundElement, nullptr);
|
2007-03-22 10:30:00 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// All of our children are shunted to this single insertion point.
|
2012-07-30 07:20:58 -07:00
|
|
|
nsInsertionPointList* arr = nullptr;
|
2007-03-22 10:30:00 -07:00
|
|
|
GetInsertionPointsFor(singlePoint, &arr);
|
|
|
|
nsXBLInsertionPoint* insertionPoint = arr->ElementAt(0);
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMNode> node;
|
|
|
|
nsCOMPtr<nsIContent> content;
|
2012-08-22 08:56:38 -07:00
|
|
|
uint32_t length;
|
2007-03-22 10:30:00 -07:00
|
|
|
children->GetLength(&length);
|
|
|
|
|
2012-08-22 08:56:38 -07:00
|
|
|
for (uint32_t i = 0; i < length; i++) {
|
2007-03-22 10:30:00 -07:00
|
|
|
children->Item(i, getter_AddRefs(node));
|
|
|
|
content = do_QueryInterface(node);
|
|
|
|
bindingManager->SetInsertionParent(content, singlePoint);
|
|
|
|
insertionPoint->AddChild(content);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now that all of our children have been added, we need to walk all of our
|
|
|
|
// nsIXBLInsertion points to see if any of them have default content that
|
|
|
|
// needs to be built.
|
|
|
|
mInsertionPointTable->Enumerate(RealizeDefaultContent, &data);
|
|
|
|
if (NS_FAILED(data.mRv)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mPrototypeBinding->SetInitialAttributes(mBoundElement, mContent);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Always check the content element for potential attributes.
|
|
|
|
// This shorthand hack always happens, even when we didn't
|
|
|
|
// build anonymous content.
|
|
|
|
const nsAttrName* attrName;
|
2012-08-22 08:56:38 -07:00
|
|
|
for (uint32_t i = 0; (attrName = content->GetAttrNameAt(i)); ++i) {
|
|
|
|
int32_t namespaceID = attrName->NamespaceID();
|
2008-03-19 12:44:08 -07:00
|
|
|
// Hold a strong reference here so that the atom doesn't go away during
|
|
|
|
// UnsetAttr.
|
|
|
|
nsCOMPtr<nsIAtom> name = attrName->LocalName();
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
if (name != nsGkAtoms::includes) {
|
|
|
|
if (!nsContentUtils::HasNonEmptyAttr(mBoundElement, namespaceID, name)) {
|
|
|
|
nsAutoString value2;
|
|
|
|
content->GetAttr(namespaceID, name, value2);
|
|
|
|
mBoundElement->SetAttr(namespaceID, name, attrName->GetPrefix(),
|
2011-10-17 07:59:28 -07:00
|
|
|
value2, false);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Conserve space by wiping the attributes off the clone.
|
|
|
|
if (mContent)
|
2011-10-17 07:59:28 -07:00
|
|
|
mContent->UnsetAttr(namespaceID, name, false);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsXBLBinding::InstallEventHandlers()
|
|
|
|
{
|
|
|
|
// Don't install handlers if scripts aren't allowed.
|
|
|
|
if (AllowScripts()) {
|
|
|
|
// Fetch the handlers prototypes for this binding.
|
|
|
|
nsXBLPrototypeHandler* handlerChain = mPrototypeBinding->GetPrototypeHandlers();
|
|
|
|
|
|
|
|
if (handlerChain) {
|
2011-06-23 19:18:01 -07:00
|
|
|
nsEventListenerManager* manager =
|
2011-10-17 07:59:28 -07:00
|
|
|
mBoundElement->GetListenerManager(true);
|
2007-03-22 10:30:00 -07:00
|
|
|
if (!manager)
|
|
|
|
return;
|
|
|
|
|
2011-09-28 23:19:26 -07:00
|
|
|
bool isChromeDoc =
|
2011-10-18 03:53:36 -07:00
|
|
|
nsContentUtils::IsChromeDoc(mBoundElement->OwnerDoc());
|
2011-09-28 23:19:26 -07:00
|
|
|
bool isChromeBinding = mPrototypeBinding->IsChrome();
|
2007-03-22 10:30:00 -07:00
|
|
|
nsXBLPrototypeHandler* curr;
|
|
|
|
for (curr = handlerChain; curr; curr = curr->GetNextHandler()) {
|
|
|
|
// Fetch the event type.
|
|
|
|
nsCOMPtr<nsIAtom> eventAtom = curr->GetEventName();
|
|
|
|
if (!eventAtom ||
|
|
|
|
eventAtom == nsGkAtoms::keyup ||
|
|
|
|
eventAtom == nsGkAtoms::keydown ||
|
|
|
|
eventAtom == nsGkAtoms::keypress)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
nsXBLEventHandler* handler = curr->GetEventHandler();
|
|
|
|
if (handler) {
|
|
|
|
// Figure out if we're using capturing or not.
|
2012-08-22 08:56:38 -07:00
|
|
|
int32_t flags = (curr->GetPhase() == NS_PHASE_CAPTURING) ?
|
2007-03-22 10:30:00 -07:00
|
|
|
NS_EVENT_FLAG_CAPTURE : NS_EVENT_FLAG_BUBBLE;
|
|
|
|
|
2011-06-23 19:18:02 -07:00
|
|
|
// If this is a command, add it in the system event group
|
|
|
|
if ((curr->GetType() & (NS_HANDLER_TYPE_XBL_COMMAND |
|
|
|
|
NS_HANDLER_TYPE_SYSTEM)) &&
|
|
|
|
(isChromeBinding || mBoundElement->IsInNativeAnonymousSubtree())) {
|
|
|
|
flags |= NS_EVENT_FLAG_SYSTEM_EVENT;
|
|
|
|
}
|
|
|
|
|
2011-09-28 23:19:26 -07:00
|
|
|
bool hasAllowUntrustedAttr = curr->HasAllowUntrustedAttr();
|
2007-04-15 09:28:53 -07:00
|
|
|
if ((hasAllowUntrustedAttr && curr->AllowUntrustedEvents()) ||
|
|
|
|
(!hasAllowUntrustedAttr && !isChromeDoc)) {
|
2007-03-22 10:30:00 -07:00
|
|
|
flags |= NS_PRIV_EVENT_UNTRUSTED_PERMITTED;
|
|
|
|
}
|
|
|
|
|
2010-03-08 07:45:00 -08:00
|
|
|
manager->AddEventListenerByType(handler,
|
|
|
|
nsDependentAtomString(eventAtom),
|
2011-06-23 19:18:02 -07:00
|
|
|
flags);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const nsCOMArray<nsXBLKeyEventHandler>* keyHandlers =
|
|
|
|
mPrototypeBinding->GetKeyEventHandlers();
|
2012-08-22 08:56:38 -07:00
|
|
|
int32_t i;
|
2007-03-22 10:30:00 -07:00
|
|
|
for (i = 0; i < keyHandlers->Count(); ++i) {
|
|
|
|
nsXBLKeyEventHandler* handler = keyHandlers->ObjectAt(i);
|
2007-04-15 09:28:53 -07:00
|
|
|
handler->SetIsBoundToChrome(isChromeDoc);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
nsAutoString type;
|
|
|
|
handler->GetEventName(type);
|
|
|
|
|
|
|
|
// If this is a command, add it in the system event group, otherwise
|
|
|
|
// add it to the standard event group.
|
|
|
|
|
|
|
|
// Figure out if we're using capturing or not.
|
2012-08-22 08:56:38 -07:00
|
|
|
int32_t flags = (handler->GetPhase() == NS_PHASE_CAPTURING) ?
|
2007-03-22 10:30:00 -07:00
|
|
|
NS_EVENT_FLAG_CAPTURE : NS_EVENT_FLAG_BUBBLE;
|
|
|
|
|
2011-06-23 19:18:02 -07:00
|
|
|
if ((handler->GetType() & (NS_HANDLER_TYPE_XBL_COMMAND |
|
|
|
|
NS_HANDLER_TYPE_SYSTEM)) &&
|
|
|
|
(isChromeBinding || mBoundElement->IsInNativeAnonymousSubtree())) {
|
|
|
|
flags |= NS_EVENT_FLAG_SYSTEM_EVENT;
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// For key handlers we have to set NS_PRIV_EVENT_UNTRUSTED_PERMITTED flag.
|
|
|
|
// Whether the handling of the event is allowed or not is handled in
|
|
|
|
// nsXBLKeyEventHandler::HandleEvent
|
|
|
|
flags |= NS_PRIV_EVENT_UNTRUSTED_PERMITTED;
|
|
|
|
|
2011-06-23 19:18:02 -07:00
|
|
|
manager->AddEventListenerByType(handler, type, flags);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mNextBinding)
|
|
|
|
mNextBinding->InstallEventHandlers();
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsXBLBinding::InstallImplementation()
|
|
|
|
{
|
|
|
|
// Always install the base class properties first, so that
|
|
|
|
// derived classes can reference the base class properties.
|
|
|
|
|
|
|
|
if (mNextBinding) {
|
|
|
|
nsresult rv = mNextBinding->InstallImplementation();
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
}
|
|
|
|
|
|
|
|
// iterate through each property in the prototype's list and install the property.
|
|
|
|
if (AllowScripts())
|
|
|
|
return mPrototypeBinding->InstallImplementation(mBoundElement);
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIAtom*
|
2012-08-22 08:56:38 -07:00
|
|
|
nsXBLBinding::GetBaseTag(int32_t* aNameSpaceID)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
nsIAtom *tag = mPrototypeBinding->GetBaseTag(aNameSpaceID);
|
|
|
|
if (!tag && mNextBinding)
|
|
|
|
return mNextBinding->GetBaseTag(aNameSpaceID);
|
|
|
|
|
|
|
|
return tag;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2012-08-22 08:56:38 -07:00
|
|
|
nsXBLBinding::AttributeChanged(nsIAtom* aAttribute, int32_t aNameSpaceID,
|
2011-09-28 23:19:26 -07:00
|
|
|
bool aRemoveFlag, bool aNotify)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
// XXX Change if we ever allow multiple bindings in a chain to contribute anonymous content
|
|
|
|
if (!mContent) {
|
|
|
|
if (mNextBinding)
|
|
|
|
mNextBinding->AttributeChanged(aAttribute, aNameSpaceID,
|
|
|
|
aRemoveFlag, aNotify);
|
|
|
|
} else {
|
|
|
|
mPrototypeBinding->AttributeChanged(aAttribute, aNameSpaceID, aRemoveFlag,
|
|
|
|
mBoundElement, mContent, aNotify);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsXBLBinding::ExecuteAttachedHandler()
|
|
|
|
{
|
|
|
|
if (mNextBinding)
|
|
|
|
mNextBinding->ExecuteAttachedHandler();
|
|
|
|
|
2011-06-11 13:43:33 -07:00
|
|
|
if (AllowScripts())
|
2007-03-22 10:30:00 -07:00
|
|
|
mPrototypeBinding->BindingAttached(mBoundElement);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsXBLBinding::ExecuteDetachedHandler()
|
|
|
|
{
|
|
|
|
if (AllowScripts())
|
|
|
|
mPrototypeBinding->BindingDetached(mBoundElement);
|
|
|
|
|
|
|
|
if (mNextBinding)
|
|
|
|
mNextBinding->ExecuteDetachedHandler();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsXBLBinding::UnhookEventHandlers()
|
|
|
|
{
|
|
|
|
nsXBLPrototypeHandler* handlerChain = mPrototypeBinding->GetPrototypeHandlers();
|
|
|
|
|
|
|
|
if (handlerChain) {
|
2011-06-23 19:18:01 -07:00
|
|
|
nsEventListenerManager* manager =
|
2011-10-17 07:59:28 -07:00
|
|
|
mBoundElement->GetListenerManager(false);
|
2007-11-15 14:29:08 -08:00
|
|
|
if (!manager) {
|
|
|
|
return;
|
|
|
|
}
|
2009-06-17 06:22:37 -07:00
|
|
|
|
2011-09-28 23:19:26 -07:00
|
|
|
bool isChromeBinding = mPrototypeBinding->IsChrome();
|
2007-03-22 10:30:00 -07:00
|
|
|
nsXBLPrototypeHandler* curr;
|
|
|
|
for (curr = handlerChain; curr; curr = curr->GetNextHandler()) {
|
|
|
|
nsXBLEventHandler* handler = curr->GetCachedEventHandler();
|
2007-11-15 14:29:08 -08:00
|
|
|
if (!handler) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsIAtom> eventAtom = curr->GetEventName();
|
|
|
|
if (!eventAtom ||
|
|
|
|
eventAtom == nsGkAtoms::keyup ||
|
|
|
|
eventAtom == nsGkAtoms::keydown ||
|
|
|
|
eventAtom == nsGkAtoms::keypress)
|
|
|
|
continue;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-11-15 14:29:08 -08:00
|
|
|
// Figure out if we're using capturing or not.
|
2012-08-22 08:56:38 -07:00
|
|
|
int32_t flags = (curr->GetPhase() == NS_PHASE_CAPTURING) ?
|
2007-11-15 14:29:08 -08:00
|
|
|
NS_EVENT_FLAG_CAPTURE : NS_EVENT_FLAG_BUBBLE;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-11-15 14:29:08 -08:00
|
|
|
// If this is a command, remove it from the system event group,
|
|
|
|
// otherwise remove it from the standard event group.
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2011-06-23 19:18:02 -07:00
|
|
|
if ((curr->GetType() & (NS_HANDLER_TYPE_XBL_COMMAND |
|
|
|
|
NS_HANDLER_TYPE_SYSTEM)) &&
|
2009-06-23 03:07:39 -07:00
|
|
|
(isChromeBinding || mBoundElement->IsInNativeAnonymousSubtree())) {
|
2011-06-23 19:18:02 -07:00
|
|
|
flags |= NS_EVENT_FLAG_SYSTEM_EVENT;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
2007-11-15 14:29:08 -08:00
|
|
|
|
2010-03-08 07:45:00 -08:00
|
|
|
manager->RemoveEventListenerByType(handler,
|
|
|
|
nsDependentAtomString(eventAtom),
|
2011-06-23 19:18:02 -07:00
|
|
|
flags);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
const nsCOMArray<nsXBLKeyEventHandler>* keyHandlers =
|
|
|
|
mPrototypeBinding->GetKeyEventHandlers();
|
2012-08-22 08:56:38 -07:00
|
|
|
int32_t i;
|
2007-03-22 10:30:00 -07:00
|
|
|
for (i = 0; i < keyHandlers->Count(); ++i) {
|
|
|
|
nsXBLKeyEventHandler* handler = keyHandlers->ObjectAt(i);
|
|
|
|
|
|
|
|
nsAutoString type;
|
|
|
|
handler->GetEventName(type);
|
|
|
|
|
|
|
|
// Figure out if we're using capturing or not.
|
2012-08-22 08:56:38 -07:00
|
|
|
int32_t flags = (handler->GetPhase() == NS_PHASE_CAPTURING) ?
|
2007-11-15 14:29:08 -08:00
|
|
|
NS_EVENT_FLAG_CAPTURE : NS_EVENT_FLAG_BUBBLE;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// If this is a command, remove it from the system event group, otherwise
|
|
|
|
// remove it from the standard event group.
|
|
|
|
|
2009-06-23 03:07:39 -07:00
|
|
|
if ((handler->GetType() & (NS_HANDLER_TYPE_XBL_COMMAND | NS_HANDLER_TYPE_SYSTEM)) &&
|
|
|
|
(isChromeBinding || mBoundElement->IsInNativeAnonymousSubtree())) {
|
2011-06-23 19:18:02 -07:00
|
|
|
flags |= NS_EVENT_FLAG_SYSTEM_EVENT;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2011-06-23 19:18:02 -07:00
|
|
|
manager->RemoveEventListenerByType(handler, type, flags);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsXBLBinding::ChangeDocument(nsIDocument* aOldDocument, nsIDocument* aNewDocument)
|
|
|
|
{
|
|
|
|
if (aOldDocument != aNewDocument) {
|
2007-10-19 21:22:43 -07:00
|
|
|
// Only style bindings get their prototypes unhooked. First do ourselves.
|
2007-03-22 10:30:00 -07:00
|
|
|
if (mIsStyleBinding) {
|
|
|
|
// Now the binding dies. Unhook our prototypes.
|
2007-10-19 21:22:43 -07:00
|
|
|
if (mPrototypeBinding->HasImplementation()) {
|
|
|
|
nsIScriptGlobalObject *global = aOldDocument->GetScopeObject();
|
2007-03-22 10:30:00 -07:00
|
|
|
if (global) {
|
2010-05-04 21:28:18 -07:00
|
|
|
JSObject *scope = global->GetGlobalJSObject();
|
|
|
|
// scope might be null if we've cycle-collected the global
|
|
|
|
// object, since the Unlink phase of cycle collection happens
|
|
|
|
// after JS GC finalization. But in that case, we don't care
|
|
|
|
// about fixing the prototype chain, since everything's going
|
|
|
|
// away immediately.
|
|
|
|
|
2007-10-24 15:13:00 -07:00
|
|
|
nsCOMPtr<nsIScriptContext> context = global->GetContext();
|
2010-05-04 21:28:18 -07:00
|
|
|
if (context && scope) {
|
2011-09-18 02:22:17 -07:00
|
|
|
JSContext *cx = context->GetNativeContext();
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2009-03-14 20:42:50 -07:00
|
|
|
nsCxPusher pusher;
|
|
|
|
pusher.Push(cx);
|
|
|
|
|
2010-12-14 17:12:48 -08:00
|
|
|
nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
|
|
|
|
nsIXPConnect *xpc = nsContentUtils::XPConnect();
|
2009-08-14 12:00:24 -07:00
|
|
|
nsresult rv =
|
2010-12-14 17:12:48 -08:00
|
|
|
xpc->GetWrappedNativeOfNativeObject(cx, scope, mBoundElement,
|
|
|
|
NS_GET_IID(nsISupports),
|
|
|
|
getter_AddRefs(wrapper));
|
2007-03-22 10:30:00 -07:00
|
|
|
if (NS_FAILED(rv))
|
|
|
|
return;
|
|
|
|
|
2010-12-14 17:12:48 -08:00
|
|
|
JSObject* scriptObject;
|
|
|
|
if (wrapper)
|
|
|
|
wrapper->GetJSObject(&scriptObject);
|
|
|
|
else
|
2012-07-30 07:20:58 -07:00
|
|
|
scriptObject = nullptr;
|
2010-12-14 17:12:48 -08:00
|
|
|
|
|
|
|
if (scriptObject) {
|
|
|
|
// XXX Stay in sync! What if a layered binding has an
|
|
|
|
// <interface>?!
|
|
|
|
// XXXbz what does that comment mean, really? It seems to date
|
|
|
|
// back to when there was such a thing as an <interface>, whever
|
|
|
|
// that was...
|
|
|
|
|
|
|
|
// Find the right prototype.
|
|
|
|
JSObject* base = scriptObject;
|
|
|
|
JSObject* proto;
|
|
|
|
JSAutoRequest ar(cx);
|
2012-08-21 18:42:53 -07:00
|
|
|
JSAutoCompartment ac(cx, scriptObject);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2010-12-14 17:12:48 -08:00
|
|
|
for ( ; true; base = proto) { // Will break out on null proto
|
2012-09-03 16:42:17 -07:00
|
|
|
if (!JS_GetPrototype(cx, base, &proto)) {
|
|
|
|
return;
|
|
|
|
}
|
2010-12-14 17:12:48 -08:00
|
|
|
if (!proto) {
|
|
|
|
break;
|
|
|
|
}
|
2007-10-19 21:22:43 -07:00
|
|
|
|
2012-02-03 16:54:57 -08:00
|
|
|
JSClass* clazz = ::JS_GetClass(proto);
|
2010-12-14 17:12:48 -08:00
|
|
|
if (!clazz ||
|
|
|
|
(~clazz->flags &
|
|
|
|
(JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS)) ||
|
|
|
|
JSCLASS_RESERVED_SLOTS(clazz) != 1 ||
|
|
|
|
clazz->resolve != (JSResolveOp)XBLResolve ||
|
|
|
|
clazz->finalize != XBLFinalize) {
|
|
|
|
// Clearly not the right class
|
|
|
|
continue;
|
|
|
|
}
|
2010-09-22 17:34:20 -07:00
|
|
|
|
2010-12-14 17:12:48 -08:00
|
|
|
nsRefPtr<nsXBLDocumentInfo> docInfo =
|
2012-02-05 12:07:23 -08:00
|
|
|
static_cast<nsXBLDocumentInfo*>(::JS_GetPrivate(proto));
|
2010-12-14 17:12:48 -08:00
|
|
|
if (!docInfo) {
|
|
|
|
// Not the proto we seek
|
|
|
|
continue;
|
|
|
|
}
|
2007-10-19 21:22:43 -07:00
|
|
|
|
2012-02-05 12:07:23 -08:00
|
|
|
jsval protoBinding = ::JS_GetReservedSlot(proto, 0);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2010-12-14 17:12:48 -08:00
|
|
|
if (JSVAL_TO_PRIVATE(protoBinding) != mPrototypeBinding) {
|
|
|
|
// Not the right binding
|
|
|
|
continue;
|
|
|
|
}
|
2007-10-19 21:22:43 -07:00
|
|
|
|
2010-12-14 17:12:48 -08:00
|
|
|
// Alright! This is the right prototype. Pull it out of the
|
|
|
|
// proto chain.
|
2012-09-03 16:42:17 -07:00
|
|
|
JSObject* grandProto;
|
|
|
|
if (!JS_GetPrototype(cx, proto, &grandProto)) {
|
|
|
|
return;
|
|
|
|
}
|
2010-12-14 17:12:48 -08:00
|
|
|
::JS_SetPrototype(cx, base, grandProto);
|
|
|
|
break;
|
2007-10-19 21:22:43 -07:00
|
|
|
}
|
|
|
|
|
2010-12-14 17:12:48 -08:00
|
|
|
mPrototypeBinding->UndefineFields(cx, scriptObject);
|
2007-10-24 15:13:00 -07:00
|
|
|
|
2010-12-14 17:12:48 -08:00
|
|
|
// Don't remove the reference from the document to the
|
|
|
|
// wrapper here since it'll be removed by the element
|
|
|
|
// itself when that's taken out of the document.
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2007-11-15 14:29:08 -08:00
|
|
|
|
|
|
|
// Remove our event handlers
|
|
|
|
UnhookEventHandlers();
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2009-11-18 07:14:14 -08:00
|
|
|
{
|
|
|
|
nsAutoScriptBlocker scriptBlocker;
|
2007-10-19 21:22:43 -07:00
|
|
|
|
2009-11-18 07:14:14 -08:00
|
|
|
// Then do our ancestors. This reverses the construction order, so that at
|
|
|
|
// all times things are consistent as far as everyone is concerned.
|
|
|
|
if (mNextBinding) {
|
|
|
|
mNextBinding->ChangeDocument(aOldDocument, aNewDocument);
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2009-11-18 07:14:14 -08:00
|
|
|
// Update the anonymous content.
|
|
|
|
// XXXbz why not only for style bindings?
|
|
|
|
nsIContent *anonymous = mContent;
|
|
|
|
if (anonymous) {
|
|
|
|
// Also kill the default content within all our insertion points.
|
|
|
|
if (mInsertionPointTable)
|
|
|
|
mInsertionPointTable->Enumerate(ChangeDocumentForDefaultContent,
|
2012-07-30 07:20:58 -07:00
|
|
|
nullptr);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2009-11-18 07:14:14 -08:00
|
|
|
nsXBLBinding::UninstallAnonymousContent(aOldDocument, anonymous);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure that henceforth we don't claim that mBoundElement's children
|
|
|
|
// have insertion parents in the old document.
|
|
|
|
nsBindingManager* bindingManager = aOldDocument->BindingManager();
|
2011-09-27 00:54:58 -07:00
|
|
|
for (nsIContent* child = mBoundElement->GetLastChild();
|
|
|
|
child;
|
|
|
|
child = child->GetPreviousSibling()) {
|
2012-07-30 07:20:58 -07:00
|
|
|
bindingManager->SetInsertionParent(child, nullptr);
|
2009-11-18 07:14:14 -08:00
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-09-28 23:19:26 -07:00
|
|
|
bool
|
2007-03-22 10:30:00 -07:00
|
|
|
nsXBLBinding::InheritsStyle() const
|
|
|
|
{
|
|
|
|
// XXX Will have to change if we ever allow multiple bindings to contribute anonymous content.
|
|
|
|
// Most derived binding with anonymous content determines style inheritance for now.
|
|
|
|
|
|
|
|
// XXX What about bindings with <content> but no kids, e.g., my treecell-text binding?
|
|
|
|
if (mContent)
|
|
|
|
return mPrototypeBinding->InheritsStyle();
|
|
|
|
|
|
|
|
if (mNextBinding)
|
|
|
|
return mNextBinding->InheritsStyle();
|
|
|
|
|
2011-10-17 07:59:28 -07:00
|
|
|
return true;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsXBLBinding::WalkRules(nsIStyleRuleProcessor::EnumFunc aFunc, void* aData)
|
|
|
|
{
|
|
|
|
if (mNextBinding)
|
|
|
|
mNextBinding->WalkRules(aFunc, aData);
|
|
|
|
|
|
|
|
nsIStyleRuleProcessor *rules = mPrototypeBinding->GetRuleProcessor();
|
|
|
|
if (rules)
|
|
|
|
(*aFunc)(rules, aData);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Internal helper methods ////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
// static
|
|
|
|
nsresult
|
|
|
|
nsXBLBinding::DoInitJSClass(JSContext *cx, JSObject *global, JSObject *obj,
|
|
|
|
const nsAFlatCString& aClassName,
|
2007-09-28 06:45:01 -07:00
|
|
|
nsXBLPrototypeBinding* aProtoBinding,
|
2011-11-15 23:50:20 -08:00
|
|
|
JSObject** aClassObject)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
// First ensure our JS class is initialized.
|
2012-09-01 19:35:17 -07:00
|
|
|
nsAutoCString className(aClassName);
|
2012-12-12 07:10:02 -08:00
|
|
|
nsAutoCString xblKey(aClassName);
|
2012-07-30 07:20:58 -07:00
|
|
|
JSObject* parent_proto = nullptr; // If we have an "obj" we can set this
|
2007-03-22 10:30:00 -07:00
|
|
|
JSAutoRequest ar(cx);
|
2010-09-22 17:34:20 -07:00
|
|
|
|
2012-08-21 18:42:53 -07:00
|
|
|
JSAutoCompartment ac(cx, global);
|
2012-12-12 07:10:02 -08:00
|
|
|
nsXBLJSClass* c = nullptr;
|
2007-03-22 10:30:00 -07:00
|
|
|
if (obj) {
|
|
|
|
// Retrieve the current prototype of obj.
|
2012-09-03 16:42:17 -07:00
|
|
|
if (!JS_GetPrototype(cx, obj, &parent_proto)) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
if (parent_proto) {
|
|
|
|
// We need to create a unique classname based on aClassName and
|
2012-12-12 07:10:02 -08:00
|
|
|
// id. Append a space (an invalid URI character) to ensure that
|
2007-03-22 10:30:00 -07:00
|
|
|
// we don't have accidental collisions with the case when parent_proto is
|
|
|
|
// null and aClassName ends in some bizarre numbers (yeah, it's unlikely).
|
|
|
|
jsid parent_proto_id;
|
|
|
|
if (!::JS_GetObjectId(cx, parent_proto, &parent_proto_id)) {
|
|
|
|
// Probably OOM
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
}
|
|
|
|
|
|
|
|
// One space, maybe "0x", at most 16 chars (on a 64-bit system) of long,
|
|
|
|
// and a null-terminator (which PR_snprintf ensures is there even if the
|
|
|
|
// string representation of what we're printing does not fit in the buffer
|
|
|
|
// provided).
|
|
|
|
char buf[20];
|
2012-12-12 07:10:02 -08:00
|
|
|
if (sizeof(jsid) == 4) {
|
|
|
|
PR_snprintf(buf, sizeof(buf), " %lx", parent_proto_id);
|
|
|
|
} else {
|
|
|
|
MOZ_ASSERT(sizeof(jsid) == 8);
|
|
|
|
PR_snprintf(buf, sizeof(buf), " %llx", parent_proto_id);
|
|
|
|
}
|
|
|
|
xblKey.Append(buf);
|
|
|
|
nsCStringKey key(xblKey);
|
|
|
|
|
|
|
|
c = static_cast<nsXBLJSClass*>(nsXBLService::gClassTable->Get(&key));
|
|
|
|
if (c) {
|
|
|
|
className.Assign(c->name);
|
|
|
|
} else {
|
|
|
|
char buf[20];
|
|
|
|
PR_snprintf(buf, sizeof(buf), " %llx", nsXBLJSClass::NewId());
|
|
|
|
className.Append(buf);
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-11-15 23:50:20 -08:00
|
|
|
jsval val;
|
|
|
|
JSObject* proto = NULL;
|
2012-05-21 18:08:11 -07:00
|
|
|
if ((!::JS_LookupPropertyWithFlags(cx, global, className.get(), 0, &val)) ||
|
2007-03-22 10:30:00 -07:00
|
|
|
JSVAL_IS_PRIMITIVE(val)) {
|
|
|
|
// We need to initialize the class.
|
|
|
|
|
2012-12-12 07:10:02 -08:00
|
|
|
nsCStringKey key(xblKey);
|
|
|
|
if (!c) {
|
|
|
|
c = static_cast<nsXBLJSClass*>(nsXBLService::gClassTable->Get(&key));
|
|
|
|
}
|
|
|
|
if (c) {
|
2007-03-22 10:30:00 -07:00
|
|
|
// If c is on the LRU list (i.e., not linked to itself), remove it now!
|
2007-07-08 00:08:04 -07:00
|
|
|
JSCList* link = static_cast<JSCList*>(c);
|
2007-03-22 10:30:00 -07:00
|
|
|
if (c->next != link) {
|
|
|
|
JS_REMOVE_AND_INIT_LINK(link);
|
|
|
|
nsXBLService::gClassLRUListLength--;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (JS_CLIST_IS_EMPTY(&nsXBLService::gClassLRUList)) {
|
|
|
|
// We need to create a struct for this class.
|
2012-12-12 07:10:02 -08:00
|
|
|
c = new nsXBLJSClass(className, xblKey);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
if (!c)
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
} else {
|
|
|
|
// Pull the least recently used class struct off the list.
|
|
|
|
JSCList* lru = (nsXBLService::gClassLRUList).next;
|
|
|
|
JS_REMOVE_AND_INIT_LINK(lru);
|
|
|
|
nsXBLService::gClassLRUListLength--;
|
|
|
|
|
|
|
|
// Remove any mapping from the old name to the class struct.
|
2007-07-08 00:08:04 -07:00
|
|
|
c = static_cast<nsXBLJSClass*>(lru);
|
2012-12-12 07:10:02 -08:00
|
|
|
nsCStringKey oldKey(c->Key());
|
2007-03-22 10:30:00 -07:00
|
|
|
(nsXBLService::gClassTable)->Remove(&oldKey);
|
|
|
|
|
|
|
|
// Change the class name and we're done.
|
|
|
|
nsMemory::Free((void*) c->name);
|
|
|
|
c->name = ToNewCString(className);
|
2012-12-12 07:10:02 -08:00
|
|
|
c->SetKey(xblKey);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Add c to our table.
|
|
|
|
(nsXBLService::gClassTable)->Put(&key, (void*)c);
|
|
|
|
}
|
|
|
|
|
|
|
|
// The prototype holds a strong reference to its class struct.
|
|
|
|
c->Hold();
|
|
|
|
|
|
|
|
// Make a new object prototyped by parent_proto and parented by global.
|
|
|
|
proto = ::JS_InitClass(cx, // context
|
|
|
|
global, // global object
|
|
|
|
parent_proto, // parent proto
|
|
|
|
c, // JSClass
|
2012-07-30 07:20:58 -07:00
|
|
|
nullptr, // JSNative ctor
|
2007-03-22 10:30:00 -07:00
|
|
|
0, // ctor args
|
2012-07-30 07:20:58 -07:00
|
|
|
nullptr, // proto props
|
|
|
|
nullptr, // proto funcs
|
|
|
|
nullptr, // ctor props (static)
|
|
|
|
nullptr); // ctor funcs (static)
|
2007-03-22 10:30:00 -07:00
|
|
|
if (!proto) {
|
|
|
|
// This will happen if we're OOM or if the security manager
|
|
|
|
// denies defining the new class...
|
|
|
|
|
|
|
|
(nsXBLService::gClassTable)->Remove(&key);
|
|
|
|
|
|
|
|
c->Drop();
|
|
|
|
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
}
|
|
|
|
|
2007-09-28 06:45:01 -07:00
|
|
|
// Keep this proto binding alive while we're alive. Do this first so that
|
|
|
|
// we can guarantee that in XBLFinalize this will be non-null.
|
2010-09-15 12:40:11 -07:00
|
|
|
// Note that we can't just store aProtoBinding in the private and
|
|
|
|
// addref/release the nsXBLDocumentInfo through it, because cycle
|
|
|
|
// collection doesn't seem to work right if the private is not an
|
|
|
|
// nsISupports.
|
2010-07-14 18:53:11 -07:00
|
|
|
nsXBLDocumentInfo* docInfo = aProtoBinding->XBLDocumentInfo();
|
2012-02-05 12:07:23 -08:00
|
|
|
::JS_SetPrivate(proto, docInfo);
|
2007-09-28 06:45:01 -07:00
|
|
|
NS_ADDREF(docInfo);
|
|
|
|
|
2012-02-05 12:07:23 -08:00
|
|
|
::JS_SetReservedSlot(proto, 0, PRIVATE_TO_JSVAL(aProtoBinding));
|
2007-09-28 06:45:01 -07:00
|
|
|
|
2011-11-15 23:50:20 -08:00
|
|
|
*aClassObject = proto;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
proto = JSVAL_TO_OBJECT(val);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (obj) {
|
|
|
|
// Set the prototype of our object to be the new class.
|
|
|
|
if (!::JS_SetPrototype(cx, obj, proto)) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2011-09-28 23:19:26 -07:00
|
|
|
bool
|
2007-03-22 10:30:00 -07:00
|
|
|
nsXBLBinding::AllowScripts()
|
|
|
|
{
|
2010-07-14 18:55:54 -07:00
|
|
|
if (!mPrototypeBinding->GetAllowScripts())
|
2011-10-17 07:59:28 -07:00
|
|
|
return false;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// Nasty hack. Use the JSContext of the bound node, since the
|
|
|
|
// security manager API expects to get the docshell type from
|
|
|
|
// that. But use the nsIPrincipal of our document.
|
|
|
|
nsIScriptSecurityManager* mgr = nsContentUtils::GetSecurityManager();
|
|
|
|
if (!mgr) {
|
2011-10-17 07:59:28 -07:00
|
|
|
return false;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2012-07-30 07:20:58 -07:00
|
|
|
nsIDocument* doc = mBoundElement ? mBoundElement->OwnerDoc() : nullptr;
|
2007-03-22 10:30:00 -07:00
|
|
|
if (!doc) {
|
2011-10-17 07:59:28 -07:00
|
|
|
return false;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
nsIScriptGlobalObject* global = doc->GetScriptGlobalObject();
|
|
|
|
if (!global) {
|
2011-10-17 07:59:28 -07:00
|
|
|
return false;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsIScriptContext> context = global->GetContext();
|
|
|
|
if (!context) {
|
2011-10-17 07:59:28 -07:00
|
|
|
return false;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2011-09-18 02:22:17 -07:00
|
|
|
JSContext* cx = context->GetNativeContext();
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2010-07-14 18:55:54 -07:00
|
|
|
nsCOMPtr<nsIDocument> ourDocument =
|
|
|
|
mPrototypeBinding->XBLDocumentInfo()->GetDocument();
|
2011-09-28 23:19:26 -07:00
|
|
|
bool canExecute;
|
2007-03-22 10:30:00 -07:00
|
|
|
nsresult rv =
|
|
|
|
mgr->CanExecuteScripts(cx, ourDocument->NodePrincipal(), &canExecute);
|
2012-10-21 23:29:55 -07:00
|
|
|
return NS_SUCCEEDED(rv) && canExecute;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2007-10-12 04:07:29 -07:00
|
|
|
void
|
|
|
|
nsXBLBinding::RemoveInsertionParent(nsIContent* aParent)
|
|
|
|
{
|
|
|
|
if (mNextBinding) {
|
|
|
|
mNextBinding->RemoveInsertionParent(aParent);
|
|
|
|
}
|
|
|
|
if (mInsertionPointTable) {
|
2012-07-30 07:20:58 -07:00
|
|
|
nsInsertionPointList* list = nullptr;
|
2007-10-12 04:07:29 -07:00
|
|
|
mInsertionPointTable->Get(aParent, &list);
|
|
|
|
if (list) {
|
2012-08-22 08:56:38 -07:00
|
|
|
int32_t count = list->Length();
|
|
|
|
for (int32_t i = 0; i < count; ++i) {
|
2007-10-12 04:07:29 -07:00
|
|
|
nsRefPtr<nsXBLInsertionPoint> currPoint = list->ElementAt(i);
|
2008-01-28 15:34:28 -08:00
|
|
|
currPoint->UnbindDefaultContent();
|
2007-10-12 04:07:29 -07:00
|
|
|
#ifdef DEBUG
|
|
|
|
nsCOMPtr<nsIContent> parent = currPoint->GetInsertionParent();
|
|
|
|
NS_ASSERTION(!parent || parent == aParent, "Wrong insertion parent!");
|
|
|
|
#endif
|
|
|
|
currPoint->ClearInsertionParent();
|
|
|
|
}
|
|
|
|
mInsertionPointTable->Remove(aParent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-09-28 23:19:26 -07:00
|
|
|
bool
|
2007-10-12 04:07:29 -07:00
|
|
|
nsXBLBinding::HasInsertionParent(nsIContent* aParent)
|
|
|
|
{
|
|
|
|
if (mInsertionPointTable) {
|
2012-07-30 07:20:58 -07:00
|
|
|
nsInsertionPointList* list = nullptr;
|
2007-10-12 04:07:29 -07:00
|
|
|
mInsertionPointTable->Get(aParent, &list);
|
|
|
|
if (list) {
|
2011-10-17 07:59:28 -07:00
|
|
|
return true;
|
2007-10-12 04:07:29 -07:00
|
|
|
}
|
|
|
|
}
|
2011-10-17 07:59:28 -07:00
|
|
|
return mNextBinding ? mNextBinding->HasInsertionParent(aParent) : false;
|
2007-10-12 04:07:29 -07:00
|
|
|
}
|
|
|
|
|
2012-06-10 16:44:50 -07:00
|
|
|
void
|
2007-03-22 10:30:00 -07:00
|
|
|
nsXBLBinding::GetInsertionPointsFor(nsIContent* aParent,
|
|
|
|
nsInsertionPointList** aResult)
|
|
|
|
{
|
|
|
|
if (!mInsertionPointTable) {
|
|
|
|
mInsertionPointTable =
|
|
|
|
new nsClassHashtable<nsISupportsHashKey, nsInsertionPointList>;
|
2012-05-18 10:30:49 -07:00
|
|
|
mInsertionPointTable->Init(4);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
mInsertionPointTable->Get(aParent, aResult);
|
|
|
|
|
|
|
|
if (!*aResult) {
|
|
|
|
*aResult = new nsInsertionPointList;
|
2012-05-18 10:30:49 -07:00
|
|
|
mInsertionPointTable->Put(aParent, *aResult);
|
2007-10-12 04:07:29 -07:00
|
|
|
if (aParent) {
|
|
|
|
aParent->SetFlags(NODE_IS_INSERTION_PARENT);
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-05-21 15:26:48 -07:00
|
|
|
nsInsertionPointList*
|
|
|
|
nsXBLBinding::GetExistingInsertionPointsFor(nsIContent* aParent)
|
|
|
|
{
|
|
|
|
if (!mInsertionPointTable) {
|
2012-07-30 07:20:58 -07:00
|
|
|
return nullptr;
|
2007-05-21 15:26:48 -07:00
|
|
|
}
|
|
|
|
|
2012-07-30 07:20:58 -07:00
|
|
|
nsInsertionPointList* result = nullptr;
|
2007-05-21 15:26:48 -07:00
|
|
|
mInsertionPointTable->Get(aParent, &result);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
nsIContent*
|
2012-08-22 08:56:38 -07:00
|
|
|
nsXBLBinding::GetInsertionPoint(const nsIContent* aChild, uint32_t* aIndex)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
if (mContent) {
|
|
|
|
return mPrototypeBinding->GetInsertionPoint(mBoundElement, mContent,
|
|
|
|
aChild, aIndex);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mNextBinding)
|
|
|
|
return mNextBinding->GetInsertionPoint(aChild, aIndex);
|
|
|
|
|
2012-07-30 07:20:58 -07:00
|
|
|
return nullptr;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
nsIContent*
|
2012-08-22 08:56:38 -07:00
|
|
|
nsXBLBinding::GetSingleInsertionPoint(uint32_t* aIndex,
|
2011-09-28 23:19:26 -07:00
|
|
|
bool* aMultipleInsertionPoints)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2011-10-17 07:59:28 -07:00
|
|
|
*aMultipleInsertionPoints = false;
|
2007-03-22 10:30:00 -07:00
|
|
|
if (mContent) {
|
|
|
|
return mPrototypeBinding->GetSingleInsertionPoint(mBoundElement, mContent,
|
|
|
|
aIndex,
|
|
|
|
aMultipleInsertionPoints);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mNextBinding)
|
|
|
|
return mNextBinding->GetSingleInsertionPoint(aIndex,
|
|
|
|
aMultipleInsertionPoints);
|
|
|
|
|
2012-07-30 07:20:58 -07:00
|
|
|
return nullptr;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
nsXBLBinding*
|
|
|
|
nsXBLBinding::RootBinding()
|
|
|
|
{
|
|
|
|
if (mNextBinding)
|
|
|
|
return mNextBinding->RootBinding();
|
|
|
|
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsXBLBinding*
|
|
|
|
nsXBLBinding::GetFirstStyleBinding()
|
|
|
|
{
|
|
|
|
if (mIsStyleBinding)
|
|
|
|
return this;
|
|
|
|
|
2012-07-30 07:20:58 -07:00
|
|
|
return mNextBinding ? mNextBinding->GetFirstStyleBinding() : nullptr;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2011-09-28 23:19:26 -07:00
|
|
|
bool
|
2007-09-28 06:45:01 -07:00
|
|
|
nsXBLBinding::ResolveAllFields(JSContext *cx, JSObject *obj) const
|
|
|
|
{
|
|
|
|
if (!mPrototypeBinding->ResolveAllFields(cx, obj)) {
|
2011-10-17 07:59:28 -07:00
|
|
|
return false;
|
2007-09-28 06:45:01 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (mNextBinding) {
|
|
|
|
return mNextBinding->ResolveAllFields(cx, obj);
|
|
|
|
}
|
|
|
|
|
2011-10-17 07:59:28 -07:00
|
|
|
return true;
|
2007-09-28 06:45:01 -07:00
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
void
|
|
|
|
nsXBLBinding::MarkForDeath()
|
|
|
|
{
|
2011-10-17 07:59:28 -07:00
|
|
|
mMarkedForDeath = true;
|
2007-03-22 10:30:00 -07:00
|
|
|
ExecuteDetachedHandler();
|
|
|
|
}
|
|
|
|
|
2011-09-28 23:19:26 -07:00
|
|
|
bool
|
2007-03-22 10:30:00 -07:00
|
|
|
nsXBLBinding::ImplementsInterface(REFNSIID aIID) const
|
|
|
|
{
|
|
|
|
return mPrototypeBinding->ImplementsInterface(aIID) ||
|
|
|
|
(mNextBinding && mNextBinding->ImplementsInterface(aIID));
|
|
|
|
}
|
|
|
|
|
2009-01-29 11:46:18 -08:00
|
|
|
nsINodeList*
|
2007-03-22 10:30:00 -07:00
|
|
|
nsXBLBinding::GetAnonymousNodes()
|
|
|
|
{
|
|
|
|
if (mContent) {
|
2012-10-09 05:31:24 -07:00
|
|
|
return mContent->ChildNodes();
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (mNextBinding)
|
|
|
|
return mNextBinding->GetAnonymousNodes();
|
|
|
|
|
2012-07-30 07:20:58 -07:00
|
|
|
return nullptr;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|