Bug 460926 A11y hierachy is broken on Ubuntu 8.10 (GNOME 2.24), r=Evan.Yan sr=roc
[wine-gecko.git] / content / xbl / src / nsXBLProtoImpl.cpp
blob0d1a8329ed464add07ffc63b63504eeb9282dba2
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
13 * License.
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.
22 * Contributor(s):
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"
52 nsresult
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;
92 curr;
93 curr = curr->GetNext())
94 curr->InstallMember(context, aBoundElement, targetScriptObject,
95 targetClassObject, mClassName);
97 ::JS_SetVersion(cx, oldVersion);
98 return NS_OK;
101 nsresult
102 nsXBLProtoImpl::InitTargetObjects(nsXBLPrototypeBinding* aBinding,
103 nsIScriptContext* aContext,
104 nsIContent* aBoundElement,
105 nsIXPConnectJSObjectHolder** aScriptObjectHolder,
106 void** aTargetClassObject)
108 nsresult rv = NS_OK;
109 *aScriptObjectHolder = nsnull;
111 if (!mClassObject) {
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.
115 if (NS_FAILED(rv))
116 return rv;
118 if (!mClassObject)
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,
135 aBoundElement,
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,
148 aTargetClassObject);
149 if (NS_FAILED(rv))
150 return rv;
152 aBoundElement->PreserveWrapper();
154 wrapper.swap(*aScriptObjectHolder);
156 return rv;
159 nsresult
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
164 // context.
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();
177 void* classObject;
178 nsresult rv = aBinding->InitClass(mClassName, cx, global, global,
179 &classObject);
180 if (NS_FAILED(rv))
181 return rv;
183 mClassObject = (JSObject*) classObject;
184 if (!mClassObject)
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;
193 curr;
194 curr = curr->GetNext()) {
195 nsresult rv = curr->CompileMember(context, mClassName, mClassObject);
196 if (NS_FAILED(rv)) {
197 DestroyMembers();
198 ::JS_SetVersion(cx, oldVersion);
199 return rv;
203 ::JS_SetVersion(cx, oldVersion);
204 return NS_OK;
207 void
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
212 // members.
213 if (!mClassObject) {
214 return;
217 nsXBLProtoImplMember *member;
218 for (member = mMembers; member; member = member->GetNext()) {
219 member->Trace(aCallback, aClosure);
223 void
224 nsXBLProtoImpl::UnlinkJSObjects()
226 if (mClassObject) {
227 DestroyMembers();
231 nsXBLProtoImplField*
232 nsXBLProtoImpl::FindField(const nsString& aFieldName) const
234 for (nsXBLProtoImplField* f = mFields; f; f = f->GetNext()) {
235 if (aFieldName.Equals(f->GetName())) {
236 return f;
240 return nsnull;
243 PRBool
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
251 // all.
252 nsDependentString name(f->GetName());
253 jsval dummy;
254 if (!::JS_LookupUCProperty(cx, obj,
255 reinterpret_cast<const jschar*>(name.get()),
256 name.Length(), &dummy)) {
257 ::JS_SetVersion(cx, oldVersion);
258 return PR_FALSE;
262 ::JS_SetVersion(cx, oldVersion);
263 return PR_TRUE;
266 void
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());
274 JSBool hasProp;
275 if (::JS_AlreadyHasOwnUCProperty(cx, obj, s, name.Length(), &hasProp) &&
276 hasProp) {
277 jsval dummy;
278 ::JS_DeleteUCProperty2(cx, obj, s, name.Length(), &dummy);
283 void
284 nsXBLProtoImpl::DestroyMembers()
286 NS_ASSERTION(mClassObject, "This should never be called when there is no class object");
288 delete mMembers;
289 mMembers = nsnull;
290 mConstructor = nsnull;
291 mDestructor = nsnull;
294 nsresult
295 NS_NewXBLProtoImpl(nsXBLPrototypeBinding* aBinding,
296 const PRUnichar* aClassName,
297 nsXBLProtoImpl** aResult)
299 nsXBLProtoImpl* impl = new nsXBLProtoImpl();
300 if (!impl)
301 return NS_ERROR_OUT_OF_MEMORY;
302 if (aClassName)
303 impl->mClassName.AssignWithConversion(aClassName);
304 else
305 aBinding->BindingURI()->GetSpec(impl->mClassName);
306 aBinding->SetImplementation(impl);
307 *aResult = impl;
309 return NS_OK;