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
4 : *
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/
9 : *
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.
14 : *
15 : * The Original Code is Mozilla Communicator client code.
16 : *
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.
21 : *
22 : * Contributor(s):
23 : * David Hyatt <hyatt@netscape.com> (Original Author)
24 : *
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.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
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 "mozilla/FunctionTimer.h"
47 : #include "nsUnicharUtils.h"
48 : #include "nsReadableUtils.h"
49 : #include "nsXBLProtoImplMethod.h"
50 : #include "nsIScriptContext.h"
51 : #include "nsContentUtils.h"
52 : #include "nsIScriptSecurityManager.h"
53 : #include "nsIXPConnect.h"
54 : #include "nsXBLPrototypeBinding.h"
55 :
56 0 : nsXBLProtoImplMethod::nsXBLProtoImplMethod(const PRUnichar* aName) :
57 : nsXBLProtoImplMember(aName),
58 0 : mUncompiledMethod(BIT_UNCOMPILED)
59 : {
60 0 : MOZ_COUNT_CTOR(nsXBLProtoImplMethod);
61 0 : }
62 :
63 0 : nsXBLProtoImplMethod::~nsXBLProtoImplMethod()
64 : {
65 0 : MOZ_COUNT_DTOR(nsXBLProtoImplMethod);
66 :
67 0 : if (!IsCompiled()) {
68 0 : delete GetUncompiledMethod();
69 : }
70 0 : }
71 :
72 : void
73 0 : nsXBLProtoImplMethod::AppendBodyText(const nsAString& aText)
74 : {
75 0 : NS_PRECONDITION(!IsCompiled(),
76 : "Must not be compiled when accessing uncompiled method");
77 :
78 0 : nsXBLUncompiledMethod* uncompiledMethod = GetUncompiledMethod();
79 0 : if (!uncompiledMethod) {
80 0 : uncompiledMethod = new nsXBLUncompiledMethod();
81 0 : if (!uncompiledMethod)
82 0 : return;
83 0 : SetUncompiledMethod(uncompiledMethod);
84 : }
85 :
86 0 : uncompiledMethod->AppendBodyText(aText);
87 : }
88 :
89 : void
90 0 : nsXBLProtoImplMethod::AddParameter(const nsAString& aText)
91 : {
92 0 : NS_PRECONDITION(!IsCompiled(),
93 : "Must not be compiled when accessing uncompiled method");
94 :
95 0 : nsXBLUncompiledMethod* uncompiledMethod = GetUncompiledMethod();
96 0 : if (!uncompiledMethod) {
97 0 : uncompiledMethod = new nsXBLUncompiledMethod();
98 0 : if (!uncompiledMethod)
99 0 : return;
100 0 : SetUncompiledMethod(uncompiledMethod);
101 : }
102 :
103 0 : uncompiledMethod->AddParameter(aText);
104 : }
105 :
106 : void
107 0 : nsXBLProtoImplMethod::SetLineNumber(PRUint32 aLineNumber)
108 : {
109 0 : NS_PRECONDITION(!IsCompiled(),
110 : "Must not be compiled when accessing uncompiled method");
111 :
112 0 : nsXBLUncompiledMethod* uncompiledMethod = GetUncompiledMethod();
113 0 : if (!uncompiledMethod) {
114 0 : uncompiledMethod = new nsXBLUncompiledMethod();
115 0 : if (!uncompiledMethod)
116 0 : return;
117 0 : SetUncompiledMethod(uncompiledMethod);
118 : }
119 :
120 0 : uncompiledMethod->SetLineNumber(aLineNumber);
121 : }
122 :
123 : nsresult
124 0 : nsXBLProtoImplMethod::InstallMember(nsIScriptContext* aContext,
125 : nsIContent* aBoundElement,
126 : void* aScriptObject,
127 : void* aTargetClassObject,
128 : const nsCString& aClassStr)
129 : {
130 0 : NS_PRECONDITION(IsCompiled(),
131 : "Should not be installing an uncompiled method");
132 0 : JSContext* cx = aContext->GetNativeContext();
133 :
134 0 : nsIDocument *ownerDoc = aBoundElement->OwnerDoc();
135 : nsIScriptGlobalObject *sgo;
136 :
137 0 : if (!(sgo = ownerDoc->GetScopeObject())) {
138 0 : return NS_ERROR_UNEXPECTED;
139 : }
140 :
141 0 : JSObject * scriptObject = (JSObject *) aScriptObject;
142 0 : NS_ASSERTION(scriptObject, "uh-oh, script Object should NOT be null or bad things will happen");
143 0 : if (!scriptObject)
144 0 : return NS_ERROR_FAILURE;
145 :
146 0 : JSObject * targetClassObject = (JSObject *) aTargetClassObject;
147 0 : JSObject * globalObject = sgo->GetGlobalJSObject();
148 :
149 : // now we want to reevaluate our property using aContext and the script object for this window...
150 0 : if (mJSMethodObject && targetClassObject) {
151 0 : nsDependentString name(mName);
152 0 : JSAutoRequest ar(cx);
153 0 : JSAutoEnterCompartment ac;
154 :
155 0 : if (!ac.enter(cx, globalObject)) {
156 0 : return NS_ERROR_UNEXPECTED;
157 : }
158 :
159 0 : JSObject * method = ::JS_CloneFunctionObject(cx, mJSMethodObject, globalObject);
160 0 : if (!method) {
161 0 : return NS_ERROR_OUT_OF_MEMORY;
162 : }
163 :
164 0 : if (!::JS_DefineUCProperty(cx, targetClassObject,
165 : reinterpret_cast<const jschar*>(mName),
166 : name.Length(), OBJECT_TO_JSVAL(method),
167 0 : NULL, NULL, JSPROP_ENUMERATE)) {
168 0 : return NS_ERROR_OUT_OF_MEMORY;
169 : }
170 : }
171 0 : return NS_OK;
172 : }
173 :
174 : nsresult
175 0 : nsXBLProtoImplMethod::CompileMember(nsIScriptContext* aContext, const nsCString& aClassStr,
176 : JSObject* aClassObject)
177 : {
178 : NS_TIME_FUNCTION_MIN(5);
179 0 : NS_PRECONDITION(!IsCompiled(),
180 : "Trying to compile an already-compiled method");
181 0 : NS_PRECONDITION(aClassObject,
182 : "Must have class object to compile");
183 :
184 0 : nsXBLUncompiledMethod* uncompiledMethod = GetUncompiledMethod();
185 :
186 : // No parameters or body was supplied, so don't install method.
187 0 : if (!uncompiledMethod) {
188 : // Early return after which we consider ourselves compiled.
189 0 : mJSMethodObject = nsnull;
190 :
191 0 : return NS_OK;
192 : }
193 :
194 : // Don't install method if no name was supplied.
195 0 : if (!mName) {
196 0 : delete uncompiledMethod;
197 :
198 : // Early return after which we consider ourselves compiled.
199 0 : mJSMethodObject = nsnull;
200 :
201 0 : return NS_OK;
202 : }
203 :
204 : // We have a method.
205 : // Allocate an array for our arguments.
206 0 : PRInt32 paramCount = uncompiledMethod->GetParameterCount();
207 0 : char** args = nsnull;
208 0 : if (paramCount > 0) {
209 0 : args = new char*[paramCount];
210 0 : if (!args)
211 0 : return NS_ERROR_OUT_OF_MEMORY;
212 :
213 : // Add our parameters to our args array.
214 0 : PRInt32 argPos = 0;
215 0 : for (nsXBLParameter* curr = uncompiledMethod->mParameters;
216 : curr;
217 : curr = curr->mNext) {
218 0 : args[argPos] = curr->mName;
219 0 : argPos++;
220 : }
221 : }
222 :
223 : // Get the body
224 0 : nsDependentString body;
225 0 : PRUnichar *bodyText = uncompiledMethod->mBodyText.GetText();
226 0 : if (bodyText)
227 0 : body.Rebind(bodyText);
228 :
229 : // Now that we have a body and args, compile the function
230 : // and then define it.
231 0 : NS_ConvertUTF16toUTF8 cname(mName);
232 0 : nsCAutoString functionUri(aClassStr);
233 0 : PRInt32 hash = functionUri.RFindChar('#');
234 0 : if (hash != kNotFound) {
235 0 : functionUri.Truncate(hash);
236 : }
237 :
238 0 : JSObject* methodObject = nsnull;
239 : nsresult rv = aContext->CompileFunction(aClassObject,
240 : cname,
241 : paramCount,
242 : const_cast<const char**>(args),
243 : body,
244 : functionUri.get(),
245 : uncompiledMethod->mBodyText.GetLineNumber(),
246 : JSVERSION_LATEST,
247 : true,
248 0 : &methodObject);
249 :
250 : // Destroy our uncompiled method and delete our arg list.
251 0 : delete uncompiledMethod;
252 0 : delete [] args;
253 0 : if (NS_FAILED(rv)) {
254 0 : SetUncompiledMethod(nsnull);
255 0 : return rv;
256 : }
257 :
258 0 : mJSMethodObject = methodObject;
259 :
260 0 : return NS_OK;
261 : }
262 :
263 : void
264 0 : nsXBLProtoImplMethod::Trace(TraceCallback aCallback, void *aClosure) const
265 : {
266 0 : if (IsCompiled() && mJSMethodObject) {
267 0 : aCallback(nsIProgrammingLanguage::JAVASCRIPT, mJSMethodObject, "mJSMethodObject", aClosure);
268 : }
269 0 : }
270 :
271 : nsresult
272 0 : nsXBLProtoImplMethod::Read(nsIScriptContext* aContext,
273 : nsIObjectInputStream* aStream)
274 : {
275 0 : nsresult rv = XBL_DeserializeFunction(aContext, aStream, &mJSMethodObject);
276 0 : if (NS_FAILED(rv)) {
277 0 : SetUncompiledMethod(nsnull);
278 0 : return rv;
279 : }
280 :
281 : #ifdef DEBUG
282 0 : mIsCompiled = true;
283 : #endif
284 :
285 0 : return NS_OK;
286 : }
287 :
288 : nsresult
289 0 : nsXBLProtoImplMethod::Write(nsIScriptContext* aContext,
290 : nsIObjectOutputStream* aStream)
291 : {
292 0 : if (mJSMethodObject) {
293 0 : nsresult rv = aStream->Write8(XBLBinding_Serialize_Method);
294 0 : NS_ENSURE_SUCCESS(rv, rv);
295 :
296 0 : rv = aStream->WriteWStringZ(mName);
297 0 : NS_ENSURE_SUCCESS(rv, rv);
298 :
299 0 : return XBL_SerializeFunction(aContext, aStream, mJSMethodObject);
300 : }
301 :
302 0 : return NS_OK;
303 : }
304 :
305 : nsresult
306 0 : nsXBLProtoImplAnonymousMethod::Execute(nsIContent* aBoundElement)
307 : {
308 0 : NS_PRECONDITION(IsCompiled(), "Can't execute uncompiled method");
309 :
310 0 : if (!mJSMethodObject) {
311 : // Nothing to do here
312 0 : return NS_OK;
313 : }
314 :
315 : // Get the script context the same way
316 : // nsXBLProtoImpl::InstallImplementation does.
317 0 : nsIDocument* document = aBoundElement->OwnerDoc();
318 :
319 0 : nsIScriptGlobalObject* global = document->GetScriptGlobalObject();
320 0 : if (!global) {
321 0 : return NS_OK;
322 : }
323 :
324 0 : nsCOMPtr<nsIScriptContext> context = global->GetContext();
325 0 : if (!context) {
326 0 : return NS_OK;
327 : }
328 :
329 0 : JSContext* cx = context->GetNativeContext();
330 :
331 0 : JSObject* globalObject = global->GetGlobalJSObject();
332 :
333 0 : nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
334 : jsval v;
335 : nsresult rv =
336 : nsContentUtils::WrapNative(cx, globalObject, aBoundElement, &v,
337 0 : getter_AddRefs(wrapper));
338 0 : NS_ENSURE_SUCCESS(rv, rv);
339 :
340 0 : JSObject* thisObject = JSVAL_TO_OBJECT(v);
341 :
342 0 : JSAutoRequest ar(cx);
343 0 : JSAutoEnterCompartment ac;
344 :
345 0 : if (!ac.enter(cx, thisObject))
346 0 : return NS_ERROR_UNEXPECTED;
347 :
348 : // Clone the function object, using thisObject as the parent so "this" is in
349 : // the scope chain of the resulting function (for backwards compat to the
350 : // days when this was an event handler).
351 0 : JSObject* method = ::JS_CloneFunctionObject(cx, mJSMethodObject, thisObject);
352 0 : if (!method)
353 0 : return NS_ERROR_OUT_OF_MEMORY;
354 :
355 : // Now call the method
356 :
357 : // Use nsCxPusher to make sure we call ScriptEvaluated when we're done.
358 0 : nsCxPusher pusher;
359 0 : NS_ENSURE_STATE(pusher.Push(aBoundElement));
360 :
361 : // Check whether it's OK to call the method.
362 0 : rv = nsContentUtils::GetSecurityManager()->CheckFunctionAccess(cx, method,
363 0 : thisObject);
364 :
365 0 : JSBool ok = JS_TRUE;
366 0 : if (NS_SUCCEEDED(rv)) {
367 : jsval retval;
368 : ok = ::JS_CallFunctionValue(cx, thisObject, OBJECT_TO_JSVAL(method),
369 0 : 0 /* argc */, nsnull /* argv */, &retval);
370 : }
371 :
372 0 : if (!ok) {
373 : // If a constructor or destructor threw an exception, it doesn't stop
374 : // anything else. We just report it. Note that we need to set aside the
375 : // frame chain here, since the constructor invocation is not related to
376 : // whatever is on the stack right now, really.
377 0 : JSBool saved = JS_SaveFrameChain(cx);
378 0 : JS_ReportPendingException(cx);
379 0 : if (saved)
380 0 : JS_RestoreFrameChain(cx);
381 0 : return NS_ERROR_FAILURE;
382 : }
383 :
384 0 : return NS_OK;
385 : }
386 :
387 : nsresult
388 0 : nsXBLProtoImplAnonymousMethod::Write(nsIScriptContext* aContext,
389 : nsIObjectOutputStream* aStream,
390 : XBLBindingSerializeDetails aType)
391 : {
392 0 : if (mJSMethodObject) {
393 0 : nsresult rv = aStream->Write8(aType);
394 0 : NS_ENSURE_SUCCESS(rv, rv);
395 :
396 0 : rv = XBL_SerializeFunction(aContext, aStream, mJSMethodObject);
397 0 : NS_ENSURE_SUCCESS(rv, rv);
398 : }
399 :
400 0 : return NS_OK;
401 : }
|