1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
9 #include "nsIContent.h"
10 #include "nsIDocument.h"
11 #include "nsIGlobalObject.h"
12 #include "nsUnicharUtils.h"
13 #include "nsReadableUtils.h"
14 #include "nsXBLProtoImplMethod.h"
15 #include "nsJSUtils.h"
16 #include "nsContentUtils.h"
17 #include "nsIScriptSecurityManager.h"
18 #include "nsIXPConnect.h"
19 #include "xpcpublic.h"
20 #include "nsXBLPrototypeBinding.h"
21 #include "mozilla/dom/Element.h"
22 #include "mozilla/dom/ScriptSettings.h"
24 using namespace mozilla
;
25 using namespace mozilla::dom
;
27 nsXBLProtoImplMethod::nsXBLProtoImplMethod(const char16_t
* aName
) :
28 nsXBLProtoImplMember(aName
),
31 MOZ_COUNT_CTOR(nsXBLProtoImplMethod
);
34 nsXBLProtoImplMethod::~nsXBLProtoImplMethod()
36 MOZ_COUNT_DTOR(nsXBLProtoImplMethod
);
39 delete GetUncompiledMethod();
44 nsXBLProtoImplMethod::AppendBodyText(const nsAString
& aText
)
46 NS_PRECONDITION(!IsCompiled(),
47 "Must not be compiled when accessing uncompiled method");
49 nsXBLUncompiledMethod
* uncompiledMethod
= GetUncompiledMethod();
50 if (!uncompiledMethod
) {
51 uncompiledMethod
= new nsXBLUncompiledMethod();
52 if (!uncompiledMethod
)
54 SetUncompiledMethod(uncompiledMethod
);
57 uncompiledMethod
->AppendBodyText(aText
);
61 nsXBLProtoImplMethod::AddParameter(const nsAString
& aText
)
63 NS_PRECONDITION(!IsCompiled(),
64 "Must not be compiled when accessing uncompiled method");
66 if (aText
.IsEmpty()) {
67 NS_WARNING("Empty name attribute in xbl:parameter!");
71 nsXBLUncompiledMethod
* uncompiledMethod
= GetUncompiledMethod();
72 if (!uncompiledMethod
) {
73 uncompiledMethod
= new nsXBLUncompiledMethod();
74 if (!uncompiledMethod
)
76 SetUncompiledMethod(uncompiledMethod
);
79 uncompiledMethod
->AddParameter(aText
);
83 nsXBLProtoImplMethod::SetLineNumber(uint32_t aLineNumber
)
85 NS_PRECONDITION(!IsCompiled(),
86 "Must not be compiled when accessing uncompiled method");
88 nsXBLUncompiledMethod
* uncompiledMethod
= GetUncompiledMethod();
89 if (!uncompiledMethod
) {
90 uncompiledMethod
= new nsXBLUncompiledMethod();
91 if (!uncompiledMethod
)
93 SetUncompiledMethod(uncompiledMethod
);
96 uncompiledMethod
->SetLineNumber(aLineNumber
);
100 nsXBLProtoImplMethod::InstallMember(JSContext
* aCx
,
101 JS::Handle
<JSObject
*> aTargetClassObject
)
103 NS_PRECONDITION(IsCompiled(),
104 "Should not be installing an uncompiled method");
105 MOZ_ASSERT(js::IsObjectInContextCompartment(aTargetClassObject
, aCx
));
109 JS::Rooted
<JSObject
*> globalObject(aCx
, JS_GetGlobalForObject(aCx
, aTargetClassObject
));
110 MOZ_ASSERT(xpc::IsInContentXBLScope(globalObject
) ||
111 xpc::IsInAddonScope(globalObject
) ||
112 globalObject
== xpc::GetXBLScope(aCx
, globalObject
));
113 MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx
) == globalObject
);
117 JS::Rooted
<JSObject
*> jsMethodObject(aCx
, GetCompiledMethod());
118 if (jsMethodObject
) {
119 nsDependentString
name(mName
);
121 JS::Rooted
<JSObject
*> method(aCx
, JS::CloneFunctionObject(aCx
, jsMethodObject
));
122 NS_ENSURE_TRUE(method
, NS_ERROR_OUT_OF_MEMORY
);
124 if (!::JS_DefineUCProperty(aCx
, aTargetClassObject
,
125 static_cast<const char16_t
*>(mName
),
126 name
.Length(), method
,
128 return NS_ERROR_OUT_OF_MEMORY
;
135 nsXBLProtoImplMethod::CompileMember(AutoJSAPI
& jsapi
, const nsCString
& aClassStr
,
136 JS::Handle
<JSObject
*> aClassObject
)
138 AssertInCompilationScope();
139 NS_PRECONDITION(!IsCompiled(),
140 "Trying to compile an already-compiled method");
141 NS_PRECONDITION(aClassObject
,
142 "Must have class object to compile");
144 nsXBLUncompiledMethod
* uncompiledMethod
= GetUncompiledMethod();
146 // No parameters or body was supplied, so don't install method.
147 if (!uncompiledMethod
) {
148 // Early return after which we consider ourselves compiled.
149 SetCompiledMethod(nullptr);
154 // Don't install method if no name was supplied.
156 delete uncompiledMethod
;
158 // Early return after which we consider ourselves compiled.
159 SetCompiledMethod(nullptr);
165 // Allocate an array for our arguments.
166 int32_t paramCount
= uncompiledMethod
->GetParameterCount();
167 char** args
= nullptr;
168 if (paramCount
> 0) {
169 args
= new char*[paramCount
];
171 return NS_ERROR_OUT_OF_MEMORY
;
173 // Add our parameters to our args array.
175 for (nsXBLParameter
* curr
= uncompiledMethod
->mParameters
;
177 curr
= curr
->mNext
) {
178 args
[argPos
] = curr
->mName
;
184 nsDependentString body
;
185 char16_t
*bodyText
= uncompiledMethod
->mBodyText
.GetText();
187 body
.Rebind(bodyText
);
189 // Now that we have a body and args, compile the function
190 // and then define it.
191 NS_ConvertUTF16toUTF8
cname(mName
);
192 nsAutoCString
functionUri(aClassStr
);
193 int32_t hash
= functionUri
.RFindChar('#');
194 if (hash
!= kNotFound
) {
195 functionUri
.Truncate(hash
);
198 JSContext
*cx
= jsapi
.cx();
199 JSAutoCompartment
ac(cx
, aClassObject
);
200 JS::CompileOptions
options(cx
);
201 options
.setFileAndLine(functionUri
.get(),
202 uncompiledMethod
->mBodyText
.GetLineNumber())
203 .setVersion(JSVERSION_LATEST
);
204 JS::Rooted
<JSObject
*> methodObject(cx
);
205 JS::AutoObjectVector
emptyVector(cx
);
206 nsresult rv
= nsJSUtils::CompileFunction(jsapi
, emptyVector
, options
, cname
,
208 const_cast<const char**>(args
),
209 body
, methodObject
.address());
211 // Destroy our uncompiled method and delete our arg list.
212 delete uncompiledMethod
;
215 SetUncompiledMethod(nullptr);
219 SetCompiledMethod(methodObject
);
225 nsXBLProtoImplMethod::Trace(const TraceCallbacks
& aCallbacks
, void *aClosure
)
227 if (IsCompiled() && GetCompiledMethodPreserveColor()) {
228 aCallbacks
.Trace(&mMethod
.AsHeapObject(), "mMethod", aClosure
);
233 nsXBLProtoImplMethod::Read(nsIObjectInputStream
* aStream
)
235 AssertInCompilationScope();
236 MOZ_ASSERT(!IsCompiled() && !GetUncompiledMethod());
239 JS::Rooted
<JSObject
*> methodObject(cx
);
240 nsresult rv
= XBL_DeserializeFunction(aStream
, &methodObject
);
242 SetUncompiledMethod(nullptr);
246 SetCompiledMethod(methodObject
);
252 nsXBLProtoImplMethod::Write(nsIObjectOutputStream
* aStream
)
254 AssertInCompilationScope();
255 MOZ_ASSERT(IsCompiled());
256 if (GetCompiledMethodPreserveColor()) {
257 nsresult rv
= aStream
->Write8(XBLBinding_Serialize_Method
);
258 NS_ENSURE_SUCCESS(rv
, rv
);
260 rv
= aStream
->WriteWStringZ(mName
);
261 NS_ENSURE_SUCCESS(rv
, rv
);
263 // Calling fromMarkedLocation() is safe because mMethod is traced by the
264 // Trace() method above, and because its value is never changed after it has
265 // been set to a compiled method.
266 JS::Handle
<JSObject
*> method
=
267 JS::Handle
<JSObject
*>::fromMarkedLocation(mMethod
.AsHeapObject().address());
268 return XBL_SerializeFunction(aStream
, method
);
275 nsXBLProtoImplAnonymousMethod::Execute(nsIContent
* aBoundElement
, JSAddonId
* aAddonId
)
277 MOZ_ASSERT(aBoundElement
->IsElement());
278 NS_PRECONDITION(IsCompiled(), "Can't execute uncompiled method");
280 if (!GetCompiledMethod()) {
281 // Nothing to do here
285 // Get the script context the same way
286 // nsXBLProtoImpl::InstallImplementation does.
287 nsIDocument
* document
= aBoundElement
->OwnerDoc();
289 nsCOMPtr
<nsIGlobalObject
> global
=
290 do_QueryInterface(document
->GetInnerWindow());
297 // We are going to run script via JS::Call, so we need a script entry point,
298 // but as this is XBL related it does not appear in the HTML spec.
299 dom::AutoEntryScript
aes(global
);
300 aes
.TakeOwnershipOfErrorReporting();
301 JSContext
* cx
= aes
.cx();
303 JS::Rooted
<JSObject
*> globalObject(cx
, global
->GetGlobalJSObject());
305 JS::Rooted
<JSObject
*> scopeObject(cx
, xpc::GetScopeForXBLExecution(cx
, globalObject
, aAddonId
));
306 NS_ENSURE_TRUE(scopeObject
, NS_ERROR_OUT_OF_MEMORY
);
308 JSAutoCompartment
ac(cx
, scopeObject
);
309 JS::AutoObjectVector
scopeChain(cx
);
310 if (!nsJSUtils::GetScopeChainForElement(cx
, aBoundElement
->AsElement(),
312 return NS_ERROR_OUT_OF_MEMORY
;
314 MOZ_ASSERT(scopeChain
.length() != 0);
316 // Clone the function object, using our scope chain (for backwards
317 // compat to the days when this was an event handler).
318 JS::Rooted
<JSObject
*> jsMethodObject(cx
, GetCompiledMethod());
319 JS::Rooted
<JSObject
*> method(cx
, JS::CloneFunctionObject(cx
, jsMethodObject
,
322 return NS_ERROR_OUT_OF_MEMORY
;
324 // Now call the method
326 // Check whether script is enabled.
327 bool scriptAllowed
= nsContentUtils::GetSecurityManager()->
328 ScriptAllowed(js::GetGlobalForObjectCrossCompartment(method
));
331 JS::Rooted
<JS::Value
> retval(cx
);
332 JS::Rooted
<JS::Value
> methodVal(cx
, JS::ObjectValue(*method
));
333 // No need to check the return here as AutoEntryScript has taken ownership
334 // of error reporting.
335 ::JS::Call(cx
, scopeChain
[0], methodVal
, JS::HandleValueArray::empty(), &retval
);
342 nsXBLProtoImplAnonymousMethod::Write(nsIObjectOutputStream
* aStream
,
343 XBLBindingSerializeDetails aType
)
345 AssertInCompilationScope();
346 MOZ_ASSERT(IsCompiled());
347 if (GetCompiledMethodPreserveColor()) {
348 nsresult rv
= aStream
->Write8(aType
);
349 NS_ENSURE_SUCCESS(rv
, rv
);
351 rv
= aStream
->WriteWStringZ(mName
);
352 NS_ENSURE_SUCCESS(rv
, rv
);
354 // Calling fromMarkedLocation() is safe because mMethod is traced by the
355 // Trace() method above, and because its value is never changed after it has
356 // been set to a compiled method.
357 JS::Handle
<JSObject
*> method
=
358 JS::Handle
<JSObject
*>::fromMarkedLocation(mMethod
.AsHeapObject().address());
359 rv
= XBL_SerializeFunction(aStream
, method
);
360 NS_ENSURE_SUCCESS(rv
, rv
);