Bumping manifests a=b2g-bump
[gecko.git] / dom / xbl / nsXBLProtoImplMethod.cpp
blobb7ba28841ec044a05b05f9d5d8adc53cff6065cb
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/. */
6 #include "nsIAtom.h"
7 #include "nsString.h"
8 #include "jsapi.h"
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),
29 mMethod()
31 MOZ_COUNT_CTOR(nsXBLProtoImplMethod);
34 nsXBLProtoImplMethod::~nsXBLProtoImplMethod()
36 MOZ_COUNT_DTOR(nsXBLProtoImplMethod);
38 if (!IsCompiled()) {
39 delete GetUncompiledMethod();
43 void
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)
53 return;
54 SetUncompiledMethod(uncompiledMethod);
57 uncompiledMethod->AppendBodyText(aText);
60 void
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!");
68 return;
71 nsXBLUncompiledMethod* uncompiledMethod = GetUncompiledMethod();
72 if (!uncompiledMethod) {
73 uncompiledMethod = new nsXBLUncompiledMethod();
74 if (!uncompiledMethod)
75 return;
76 SetUncompiledMethod(uncompiledMethod);
79 uncompiledMethod->AddParameter(aText);
82 void
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)
92 return;
93 SetUncompiledMethod(uncompiledMethod);
96 uncompiledMethod->SetLineNumber(aLineNumber);
99 nsresult
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));
107 #ifdef DEBUG
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);
115 #endif
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,
127 JSPROP_ENUMERATE)) {
128 return NS_ERROR_OUT_OF_MEMORY;
131 return NS_OK;
134 nsresult
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);
151 return NS_OK;
154 // Don't install method if no name was supplied.
155 if (!mName) {
156 delete uncompiledMethod;
158 // Early return after which we consider ourselves compiled.
159 SetCompiledMethod(nullptr);
161 return NS_OK;
164 // We have a method.
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];
170 if (!args)
171 return NS_ERROR_OUT_OF_MEMORY;
173 // Add our parameters to our args array.
174 int32_t argPos = 0;
175 for (nsXBLParameter* curr = uncompiledMethod->mParameters;
176 curr;
177 curr = curr->mNext) {
178 args[argPos] = curr->mName;
179 argPos++;
183 // Get the body
184 nsDependentString body;
185 char16_t *bodyText = uncompiledMethod->mBodyText.GetText();
186 if (bodyText)
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,
207 paramCount,
208 const_cast<const char**>(args),
209 body, methodObject.address());
211 // Destroy our uncompiled method and delete our arg list.
212 delete uncompiledMethod;
213 delete [] args;
214 if (NS_FAILED(rv)) {
215 SetUncompiledMethod(nullptr);
216 return rv;
219 SetCompiledMethod(methodObject);
221 return NS_OK;
224 void
225 nsXBLProtoImplMethod::Trace(const TraceCallbacks& aCallbacks, void *aClosure)
227 if (IsCompiled() && GetCompiledMethodPreserveColor()) {
228 aCallbacks.Trace(&mMethod.AsHeapObject(), "mMethod", aClosure);
232 nsresult
233 nsXBLProtoImplMethod::Read(nsIObjectInputStream* aStream)
235 AssertInCompilationScope();
236 MOZ_ASSERT(!IsCompiled() && !GetUncompiledMethod());
238 AutoJSContext cx;
239 JS::Rooted<JSObject*> methodObject(cx);
240 nsresult rv = XBL_DeserializeFunction(aStream, &methodObject);
241 if (NS_FAILED(rv)) {
242 SetUncompiledMethod(nullptr);
243 return rv;
246 SetCompiledMethod(methodObject);
248 return NS_OK;
251 nsresult
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);
271 return NS_OK;
274 nsresult
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
282 return NS_OK;
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());
291 if (!global) {
292 return NS_OK;
295 nsAutoMicroTask mt;
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(),
311 scopeChain)) {
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,
320 scopeChain));
321 if (!method)
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));
330 if (scriptAllowed) {
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);
338 return NS_OK;
341 nsresult
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);
363 return NS_OK;