1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is Mozilla Communicator client code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
23 * David Hyatt <hyatt@netscape.com> (Original Author)
25 * Alternatively, the contents of this file may be used under the terms of
26 * either of the GNU General Public License Version 2 or later (the "GPL"),
27 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
39 #include "nsXBLProtoImpl.h"
40 #include "nsIContent.h"
41 #include "nsIDocument.h"
42 #include "nsContentUtils.h"
43 #include "nsIScriptGlobalObject.h"
44 #include "nsIScriptGlobalObjectOwner.h"
45 #include "nsIScriptContext.h"
46 #include "nsIXPConnect.h"
47 #include "nsIServiceManager.h"
48 #include "nsIXBLDocumentInfo.h"
49 #include "nsIDOMNode.h"
50 #include "nsXBLPrototypeBinding.h"
53 nsXBLProtoImpl::InstallImplementation(nsXBLPrototypeBinding
* aBinding
, nsIContent
* aBoundElement
)
55 // This function is called to install a concrete implementation on a bound element using
56 // this prototype implementation as a guide. The prototype implementation is compiled lazily,
57 // so for the first bound element that needs a concrete implementation, we also build the
58 // prototype implementation.
59 if (!mMembers
&& !mFields
) // Constructor and destructor also live in mMembers
60 return NS_OK
; // Nothing to do, so let's not waste time.
62 // If the way this gets the script context changes, fix
63 // nsXBLProtoImplAnonymousMethod::Execute
64 nsIDocument
* document
= aBoundElement
->GetOwnerDoc();
65 if (!document
) return NS_OK
;
67 nsIScriptGlobalObject
*global
= document
->GetScopeObject();
68 if (!global
) return NS_OK
;
70 nsCOMPtr
<nsIScriptContext
> context
= global
->GetContext();
71 if (!context
) return NS_OK
;
73 // InitTarget objects gives us back the JS object that represents the bound element and the
74 // class object in the bound document that represents the concrete version of this implementation.
75 // This function also has the side effect of building up the prototype implementation if it has
76 // not been built already.
77 nsCOMPtr
<nsIXPConnectJSObjectHolder
> holder
;
78 void * targetClassObject
= nsnull
;
79 nsresult rv
= InitTargetObjects(aBinding
, context
, aBoundElement
,
80 getter_AddRefs(holder
), &targetClassObject
);
81 NS_ENSURE_SUCCESS(rv
, rv
); // kick out if we were unable to properly intialize our target objects
83 JSObject
* targetScriptObject
;
84 holder
->GetJSObject(&targetScriptObject
);
86 JSContext
*cx
= (JSContext
*)context
->GetNativeContext();
87 // Set version up front so we don't thrash it
88 JSVersion oldVersion
= ::JS_SetVersion(cx
, JSVERSION_LATEST
);
90 // Walk our member list and install each one in turn.
91 for (nsXBLProtoImplMember
* curr
= mMembers
;
93 curr
= curr
->GetNext())
94 curr
->InstallMember(context
, aBoundElement
, targetScriptObject
,
95 targetClassObject
, mClassName
);
97 ::JS_SetVersion(cx
, oldVersion
);
102 nsXBLProtoImpl::InitTargetObjects(nsXBLPrototypeBinding
* aBinding
,
103 nsIScriptContext
* aContext
,
104 nsIContent
* aBoundElement
,
105 nsIXPConnectJSObjectHolder
** aScriptObjectHolder
,
106 void** aTargetClassObject
)
109 *aScriptObjectHolder
= nsnull
;
112 rv
= CompilePrototypeMembers(aBinding
); // This is the first time we've ever installed this binding on an element.
113 // We need to go ahead and compile all methods and properties on a class
114 // in our prototype binding.
119 return NS_OK
; // This can be ok, if all we've got are fields (and no methods/properties).
122 nsIDocument
*ownerDoc
= aBoundElement
->GetOwnerDoc();
123 nsIScriptGlobalObject
*sgo
;
125 if (!ownerDoc
|| !(sgo
= ownerDoc
->GetScopeObject())) {
126 return NS_ERROR_UNEXPECTED
;
129 // Because our prototype implementation has a class, we need to build up a corresponding
130 // class for the concrete implementation in the bound document.
131 JSContext
* jscontext
= (JSContext
*)aContext
->GetNativeContext();
132 JSObject
* global
= sgo
->GetGlobalJSObject();
133 nsCOMPtr
<nsIXPConnectJSObjectHolder
> wrapper
;
134 rv
= nsContentUtils::XPConnect()->WrapNative(jscontext
, global
,
136 NS_GET_IID(nsISupports
),
137 getter_AddRefs(wrapper
));
138 NS_ENSURE_SUCCESS(rv
, rv
);
139 JSObject
* object
= nsnull
;
140 rv
= wrapper
->GetJSObject(&object
);
141 NS_ENSURE_SUCCESS(rv
, rv
);
143 // All of the above code was just obtaining the bound element's script object and its immediate
144 // concrete base class. We need to alter the object so that our concrete class is interposed
145 // between the object and its base class. We become the new base class of the object, and the
146 // object's old base class becomes the new class' base class.
147 rv
= aBinding
->InitClass(mClassName
, jscontext
, global
, object
,
152 aBoundElement
->PreserveWrapper();
154 wrapper
.swap(*aScriptObjectHolder
);
160 nsXBLProtoImpl::CompilePrototypeMembers(nsXBLPrototypeBinding
* aBinding
)
162 // We want to pre-compile our implementation's members against a "prototype context". Then when we actually
163 // bind the prototype to a real xbl instance, we'll clone the pre-compiled JS into the real instance's
165 nsCOMPtr
<nsIScriptGlobalObjectOwner
> globalOwner(
166 do_QueryInterface(aBinding
->XBLDocumentInfo()));
167 nsIScriptGlobalObject
* globalObject
= globalOwner
->GetScriptGlobalObject();
168 NS_ENSURE_TRUE(globalObject
, NS_ERROR_UNEXPECTED
);
170 nsIScriptContext
*context
= globalObject
->GetContext();
171 NS_ENSURE_TRUE(context
, NS_ERROR_OUT_OF_MEMORY
);
173 JSContext
*cx
= (JSContext
*)context
->GetNativeContext();
174 JSObject
*global
= globalObject
->GetGlobalJSObject();
178 nsresult rv
= aBinding
->InitClass(mClassName
, cx
, global
, global
,
183 mClassObject
= (JSObject
*) classObject
;
185 return NS_ERROR_FAILURE
;
187 // Set version up front so we don't thrash it
188 JSVersion oldVersion
= ::JS_SetVersion(cx
, JSVERSION_LATEST
);
190 // Now that we have a class object installed, we walk our member list and compile each of our
191 // properties and methods in turn.
192 for (nsXBLProtoImplMember
* curr
= mMembers
;
194 curr
= curr
->GetNext()) {
195 nsresult rv
= curr
->CompileMember(context
, mClassName
, mClassObject
);
198 ::JS_SetVersion(cx
, oldVersion
);
203 ::JS_SetVersion(cx
, oldVersion
);
208 nsXBLProtoImpl::Trace(TraceCallback aCallback
, void *aClosure
) const
210 // If we don't have a class object then we either didn't compile members
211 // or we only have fields, in both cases there are no cycles through our
217 nsXBLProtoImplMember
*member
;
218 for (member
= mMembers
; member
; member
= member
->GetNext()) {
219 member
->Trace(aCallback
, aClosure
);
224 nsXBLProtoImpl::UnlinkJSObjects()
232 nsXBLProtoImpl::FindField(const nsString
& aFieldName
) const
234 for (nsXBLProtoImplField
* f
= mFields
; f
; f
= f
->GetNext()) {
235 if (aFieldName
.Equals(f
->GetName())) {
244 nsXBLProtoImpl::ResolveAllFields(JSContext
*cx
, JSObject
*obj
) const
246 // Set version up front so we don't thrash it
247 JSVersion oldVersion
= ::JS_SetVersion(cx
, JSVERSION_LATEST
);
248 for (nsXBLProtoImplField
* f
= mFields
; f
; f
= f
->GetNext()) {
249 // Using OBJ_LOOKUP_PROPERTY is a pain, since what we have is a
250 // PRUnichar* for the property name. Let's just use the public API and
252 nsDependentString
name(f
->GetName());
254 if (!::JS_LookupUCProperty(cx
, obj
,
255 reinterpret_cast<const jschar
*>(name
.get()),
256 name
.Length(), &dummy
)) {
257 ::JS_SetVersion(cx
, oldVersion
);
262 ::JS_SetVersion(cx
, oldVersion
);
267 nsXBLProtoImpl::UndefineFields(JSContext
*cx
, JSObject
*obj
) const
269 JSAutoRequest
ar(cx
);
270 for (nsXBLProtoImplField
* f
= mFields
; f
; f
= f
->GetNext()) {
271 nsDependentString
name(f
->GetName());
273 const jschar
* s
= reinterpret_cast<const jschar
*>(name
.get());
275 if (::JS_AlreadyHasOwnUCProperty(cx
, obj
, s
, name
.Length(), &hasProp
) &&
278 ::JS_DeleteUCProperty2(cx
, obj
, s
, name
.Length(), &dummy
);
284 nsXBLProtoImpl::DestroyMembers()
286 NS_ASSERTION(mClassObject
, "This should never be called when there is no class object");
290 mConstructor
= nsnull
;
291 mDestructor
= nsnull
;
295 NS_NewXBLProtoImpl(nsXBLPrototypeBinding
* aBinding
,
296 const PRUnichar
* aClassName
,
297 nsXBLProtoImpl
** aResult
)
299 nsXBLProtoImpl
* impl
= new nsXBLProtoImpl();
301 return NS_ERROR_OUT_OF_MEMORY
;
303 impl
->mClassName
.AssignWithConversion(aClassName
);
305 aBinding
->BindingURI()->GetSpec(impl
->mClassName
);
306 aBinding
->SetImplementation(impl
);