Bug 460926 A11y hierachy is broken on Ubuntu 8.10 (GNOME 2.24), r=Evan.Yan sr=roc
[wine-gecko.git] / content / xbl / src / nsXBLProtoImplMethod.cpp
blob2a5b418debb8594af51d5bf6d036ef33b0cc15f9
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 "nsIAtom.h"
40 #include "nsString.h"
41 #include "jsapi.h"
42 #include "nsIContent.h"
43 #include "nsIDocument.h"
44 #include "nsIScriptGlobalObject.h"
45 #include "nsString.h"
46 #include "nsUnicharUtils.h"
47 #include "nsReadableUtils.h"
48 #include "nsXBLProtoImplMethod.h"
49 #include "nsIScriptContext.h"
50 #include "nsContentUtils.h"
51 #include "nsIScriptSecurityManager.h"
52 #include "nsIXPConnect.h"
54 nsXBLProtoImplMethod::nsXBLProtoImplMethod(const PRUnichar* aName) :
55 nsXBLProtoImplMember(aName),
56 mUncompiledMethod(BIT_UNCOMPILED)
58 MOZ_COUNT_CTOR(nsXBLProtoImplMethod);
61 nsXBLProtoImplMethod::~nsXBLProtoImplMethod()
63 MOZ_COUNT_DTOR(nsXBLProtoImplMethod);
65 if (!IsCompiled()) {
66 delete GetUncompiledMethod();
70 void
71 nsXBLProtoImplMethod::AppendBodyText(const nsAString& aText)
73 NS_PRECONDITION(!IsCompiled(),
74 "Must not be compiled when accessing uncompiled method");
76 nsXBLUncompiledMethod* uncompiledMethod = GetUncompiledMethod();
77 if (!uncompiledMethod) {
78 uncompiledMethod = new nsXBLUncompiledMethod();
79 if (!uncompiledMethod)
80 return;
81 SetUncompiledMethod(uncompiledMethod);
84 uncompiledMethod->AppendBodyText(aText);
87 void
88 nsXBLProtoImplMethod::AddParameter(const nsAString& aText)
90 NS_PRECONDITION(!IsCompiled(),
91 "Must not be compiled when accessing uncompiled method");
93 nsXBLUncompiledMethod* uncompiledMethod = GetUncompiledMethod();
94 if (!uncompiledMethod) {
95 uncompiledMethod = new nsXBLUncompiledMethod();
96 if (!uncompiledMethod)
97 return;
98 SetUncompiledMethod(uncompiledMethod);
101 uncompiledMethod->AddParameter(aText);
104 void
105 nsXBLProtoImplMethod::SetLineNumber(PRUint32 aLineNumber)
107 NS_PRECONDITION(!IsCompiled(),
108 "Must not be compiled when accessing uncompiled method");
110 nsXBLUncompiledMethod* uncompiledMethod = GetUncompiledMethod();
111 if (!uncompiledMethod) {
112 uncompiledMethod = new nsXBLUncompiledMethod();
113 if (!uncompiledMethod)
114 return;
115 SetUncompiledMethod(uncompiledMethod);
118 uncompiledMethod->SetLineNumber(aLineNumber);
121 nsresult
122 nsXBLProtoImplMethod::InstallMember(nsIScriptContext* aContext,
123 nsIContent* aBoundElement,
124 void* aScriptObject,
125 void* aTargetClassObject,
126 const nsCString& aClassStr)
128 NS_PRECONDITION(IsCompiled(),
129 "Should not be installing an uncompiled method");
130 JSContext* cx = (JSContext*) aContext->GetNativeContext();
132 nsIDocument *ownerDoc = aBoundElement->GetOwnerDoc();
133 nsIScriptGlobalObject *sgo;
135 if (!ownerDoc || !(sgo = ownerDoc->GetScopeObject())) {
136 return NS_ERROR_UNEXPECTED;
139 JSObject * scriptObject = (JSObject *) aScriptObject;
140 NS_ASSERTION(scriptObject, "uh-oh, script Object should NOT be null or bad things will happen");
141 if (!scriptObject)
142 return NS_ERROR_FAILURE;
144 JSObject * targetClassObject = (JSObject *) aTargetClassObject;
145 JSObject * globalObject = sgo->GetGlobalJSObject();
147 // now we want to reevaluate our property using aContext and the script object for this window...
148 if (mJSMethodObject && targetClassObject) {
149 nsDependentString name(mName);
150 JSAutoRequest ar(cx);
151 JSObject * method = ::JS_CloneFunctionObject(cx, mJSMethodObject, globalObject);
152 if (!method) {
153 return NS_ERROR_OUT_OF_MEMORY;
156 nsresult rv;
157 nsAutoGCRoot root(&method, &rv);
158 NS_ENSURE_SUCCESS(rv, rv);
160 if (!::JS_DefineUCProperty(cx, targetClassObject,
161 reinterpret_cast<const jschar*>(mName),
162 name.Length(), OBJECT_TO_JSVAL(method),
163 NULL, NULL, JSPROP_ENUMERATE)) {
164 return NS_ERROR_OUT_OF_MEMORY;
167 return NS_OK;
170 nsresult
171 nsXBLProtoImplMethod::CompileMember(nsIScriptContext* aContext, const nsCString& aClassStr,
172 void* aClassObject)
174 NS_PRECONDITION(!IsCompiled(),
175 "Trying to compile an already-compiled method");
176 NS_PRECONDITION(aClassObject,
177 "Must have class object to compile");
179 nsXBLUncompiledMethod* uncompiledMethod = GetUncompiledMethod();
181 // No parameters or body was supplied, so don't install method.
182 if (!uncompiledMethod) {
183 // Early return after which we consider ourselves compiled.
184 mJSMethodObject = nsnull;
186 return NS_OK;
189 // Don't install method if no name was supplied.
190 if (!mName) {
191 delete uncompiledMethod;
193 // Early return after which we consider ourselves compiled.
194 mJSMethodObject = nsnull;
196 return NS_OK;
199 // We have a method.
200 // Allocate an array for our arguments.
201 PRInt32 paramCount = uncompiledMethod->GetParameterCount();
202 char** args = nsnull;
203 if (paramCount > 0) {
204 args = new char*[paramCount];
205 if (!args)
206 return NS_ERROR_OUT_OF_MEMORY;
209 // Add our parameters to our args array.
210 PRInt32 argPos = 0;
211 for (nsXBLParameter* curr = uncompiledMethod->mParameters;
212 curr;
213 curr = curr->mNext) {
214 args[argPos] = curr->mName;
215 argPos++;
218 // Get the body
219 nsDependentString body;
220 PRUnichar *bodyText = uncompiledMethod->mBodyText.GetText();
221 if (bodyText)
222 body.Rebind(bodyText);
224 // Now that we have a body and args, compile the function
225 // and then define it.
226 NS_ConvertUTF16toUTF8 cname(mName);
227 nsCAutoString functionUri(aClassStr);
228 PRInt32 hash = functionUri.RFindChar('#');
229 if (hash != kNotFound) {
230 functionUri.Truncate(hash);
233 JSObject* methodObject = nsnull;
234 nsresult rv = aContext->CompileFunction(aClassObject,
235 cname,
236 paramCount,
237 (const char**)args,
238 body,
239 functionUri.get(),
240 uncompiledMethod->mBodyText.GetLineNumber(),
241 JSVERSION_LATEST,
242 PR_TRUE,
243 (void **) &methodObject);
245 // Destroy our uncompiled method and delete our arg list.
246 delete uncompiledMethod;
247 delete [] args;
248 if (NS_FAILED(rv)) {
249 SetUncompiledMethod(nsnull);
250 return rv;
253 mJSMethodObject = methodObject;
255 return NS_OK;
258 void
259 nsXBLProtoImplMethod::Trace(TraceCallback aCallback, void *aClosure) const
261 if (IsCompiled() && mJSMethodObject) {
262 aCallback(nsIProgrammingLanguage::JAVASCRIPT, mJSMethodObject, aClosure);
266 nsresult
267 nsXBLProtoImplAnonymousMethod::Execute(nsIContent* aBoundElement)
269 NS_PRECONDITION(IsCompiled(), "Can't execute uncompiled method");
271 if (!mJSMethodObject) {
272 // Nothing to do here
273 return NS_OK;
276 // Get the script context the same way
277 // nsXBLProtoImpl::InstallImplementation does.
278 nsIDocument* document = aBoundElement->GetOwnerDoc();
279 if (!document) {
280 return NS_OK;
283 nsIScriptGlobalObject* global = document->GetScriptGlobalObject();
284 if (!global) {
285 return NS_OK;
288 nsCOMPtr<nsIScriptContext> context = global->GetContext();
289 if (!context) {
290 return NS_OK;
293 JSContext* cx = (JSContext*) context->GetNativeContext();
295 JSObject* globalObject = global->GetGlobalJSObject();
297 nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
298 nsresult rv =
299 nsContentUtils::XPConnect()->WrapNative(cx, globalObject,
300 aBoundElement,
301 NS_GET_IID(nsISupports),
302 getter_AddRefs(wrapper));
303 NS_ENSURE_SUCCESS(rv, rv);
305 JSObject* thisObject;
306 rv = wrapper->GetJSObject(&thisObject);
307 NS_ENSURE_SUCCESS(rv, rv);
309 JSAutoRequest ar(cx);
311 // Clone the function object, using thisObject as the parent so "this" is in
312 // the scope chain of the resulting function (for backwards compat to the
313 // days when this was an event handler).
314 JSObject* method = ::JS_CloneFunctionObject(cx, mJSMethodObject, thisObject);
315 if (!method)
316 return NS_ERROR_OUT_OF_MEMORY;
318 // Now call the method
320 // Use nsCxPusher to make sure we call ScriptEvaluated when we're done.
321 nsCxPusher pusher;
322 NS_ENSURE_STATE(pusher.Push(aBoundElement));
324 // Check whether it's OK to call the method.
325 rv = nsContentUtils::GetSecurityManager()->CheckFunctionAccess(cx, method,
326 thisObject);
328 JSBool ok = JS_TRUE;
329 if (NS_SUCCEEDED(rv)) {
330 jsval retval;
331 ok = ::JS_CallFunctionValue(cx, thisObject, OBJECT_TO_JSVAL(method),
332 0 /* argc */, nsnull /* argv */, &retval);
335 if (!ok) {
336 // If a constructor or destructor threw an exception, it doesn't
337 // stop anything else. We just report it.
338 ::JS_ReportPendingException(cx);
339 return NS_ERROR_FAILURE;
342 return NS_OK;