Bumping manifests a=b2g-bump
[gecko.git] / dom / xbl / nsXBLProtoImplProperty.cpp
blob5cf37dabe65e81dd2e16bb9d652eb9197d1c32b4
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 "nsXBLProtoImplProperty.h"
11 #include "nsUnicharUtils.h"
12 #include "nsReadableUtils.h"
13 #include "nsJSUtils.h"
14 #include "nsXBLPrototypeBinding.h"
15 #include "nsXBLSerialize.h"
16 #include "xpcpublic.h"
18 using namespace mozilla;
19 using namespace mozilla::dom;
21 nsXBLProtoImplProperty::nsXBLProtoImplProperty(const char16_t* aName,
22 const char16_t* aGetter,
23 const char16_t* aSetter,
24 const char16_t* aReadOnly,
25 uint32_t aLineNumber) :
26 nsXBLProtoImplMember(aName),
27 mJSAttributes(JSPROP_ENUMERATE)
28 #ifdef DEBUG
29 , mIsCompiled(false)
30 #endif
32 MOZ_COUNT_CTOR(nsXBLProtoImplProperty);
34 if (aReadOnly) {
35 nsAutoString readOnly; readOnly.Assign(*aReadOnly);
36 if (readOnly.LowerCaseEqualsLiteral("true"))
37 mJSAttributes |= JSPROP_READONLY;
40 if (aGetter) {
41 AppendGetterText(nsDependentString(aGetter));
42 SetGetterLineNumber(aLineNumber);
44 if (aSetter) {
45 AppendSetterText(nsDependentString(aSetter));
46 SetSetterLineNumber(aLineNumber);
50 nsXBLProtoImplProperty::nsXBLProtoImplProperty(const char16_t* aName,
51 const bool aIsReadOnly)
52 : nsXBLProtoImplMember(aName),
53 mJSAttributes(JSPROP_ENUMERATE)
54 #ifdef DEBUG
55 , mIsCompiled(false)
56 #endif
58 MOZ_COUNT_CTOR(nsXBLProtoImplProperty);
60 if (aIsReadOnly)
61 mJSAttributes |= JSPROP_READONLY;
64 nsXBLProtoImplProperty::~nsXBLProtoImplProperty()
66 MOZ_COUNT_DTOR(nsXBLProtoImplProperty);
68 if (!mGetter.IsCompiled()) {
69 delete mGetter.GetUncompiled();
72 if (!mSetter.IsCompiled()) {
73 delete mSetter.GetUncompiled();
77 void nsXBLProtoImplProperty::EnsureUncompiledText(PropertyOp& aPropertyOp)
79 if (!aPropertyOp.GetUncompiled()) {
80 nsXBLTextWithLineNumber* text = new nsXBLTextWithLineNumber();
81 aPropertyOp.SetUncompiled(text);
85 void
86 nsXBLProtoImplProperty::AppendGetterText(const nsAString& aText)
88 NS_PRECONDITION(!mIsCompiled,
89 "Must not be compiled when accessing getter text");
90 EnsureUncompiledText(mGetter);
91 mGetter.GetUncompiled()->AppendText(aText);
94 void
95 nsXBLProtoImplProperty::AppendSetterText(const nsAString& aText)
97 NS_PRECONDITION(!mIsCompiled,
98 "Must not be compiled when accessing setter text");
99 EnsureUncompiledText(mSetter);
100 mSetter.GetUncompiled()->AppendText(aText);
103 void
104 nsXBLProtoImplProperty::SetGetterLineNumber(uint32_t aLineNumber)
106 NS_PRECONDITION(!mIsCompiled,
107 "Must not be compiled when accessing getter text");
108 EnsureUncompiledText(mGetter);
109 mGetter.GetUncompiled()->SetLineNumber(aLineNumber);
112 void
113 nsXBLProtoImplProperty::SetSetterLineNumber(uint32_t aLineNumber)
115 NS_PRECONDITION(!mIsCompiled,
116 "Must not be compiled when accessing setter text");
117 EnsureUncompiledText(mSetter);
118 mSetter.GetUncompiled()->SetLineNumber(aLineNumber);
121 const char* gPropertyArgs[] = { "val" };
123 nsresult
124 nsXBLProtoImplProperty::InstallMember(JSContext *aCx,
125 JS::Handle<JSObject*> aTargetClassObject)
127 NS_PRECONDITION(mIsCompiled,
128 "Should not be installing an uncompiled property");
129 MOZ_ASSERT(mGetter.IsCompiled() && mSetter.IsCompiled());
130 MOZ_ASSERT(js::IsObjectInContextCompartment(aTargetClassObject, aCx));
132 #ifdef DEBUG
134 JS::Rooted<JSObject*> globalObject(aCx, JS_GetGlobalForObject(aCx, aTargetClassObject));
135 MOZ_ASSERT(xpc::IsInContentXBLScope(globalObject) ||
136 xpc::IsInAddonScope(globalObject) ||
137 globalObject == xpc::GetXBLScope(aCx, globalObject));
138 MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx) == globalObject);
140 #endif
142 JS::Rooted<JSObject*> getter(aCx, mGetter.GetJSFunction());
143 JS::Rooted<JSObject*> setter(aCx, mSetter.GetJSFunction());
144 if (getter || setter) {
145 if (getter) {
146 if (!(getter = JS::CloneFunctionObject(aCx, getter)))
147 return NS_ERROR_OUT_OF_MEMORY;
150 if (setter) {
151 if (!(setter = JS::CloneFunctionObject(aCx, setter)))
152 return NS_ERROR_OUT_OF_MEMORY;
155 nsDependentString name(mName);
156 if (!::JS_DefineUCProperty(aCx, aTargetClassObject,
157 static_cast<const char16_t*>(mName),
158 name.Length(), JS::UndefinedHandleValue, mJSAttributes,
159 JS_DATA_TO_FUNC_PTR(JSNative, getter.get()),
160 JS_DATA_TO_FUNC_PTR(JSNative, setter.get())))
161 return NS_ERROR_OUT_OF_MEMORY;
163 return NS_OK;
166 nsresult
167 nsXBLProtoImplProperty::CompileMember(AutoJSAPI& jsapi, const nsCString& aClassStr,
168 JS::Handle<JSObject*> aClassObject)
170 AssertInCompilationScope();
171 NS_PRECONDITION(!mIsCompiled,
172 "Trying to compile an already-compiled property");
173 NS_PRECONDITION(aClassObject,
174 "Must have class object to compile");
175 MOZ_ASSERT(!mGetter.IsCompiled() && !mSetter.IsCompiled());
176 JSContext *cx = jsapi.cx();
178 if (!mName)
179 return NS_ERROR_FAILURE; // Without a valid name, we can't install the member.
181 // We have a property.
182 nsresult rv = NS_OK;
184 nsAutoCString functionUri;
185 if (mGetter.GetUncompiled() || mSetter.GetUncompiled()) {
186 functionUri = aClassStr;
187 int32_t hash = functionUri.RFindChar('#');
188 if (hash != kNotFound) {
189 functionUri.Truncate(hash);
193 bool deletedGetter = false;
194 nsXBLTextWithLineNumber *getterText = mGetter.GetUncompiled();
195 if (getterText && getterText->GetText()) {
196 nsDependentString getter(getterText->GetText());
197 if (!getter.IsEmpty()) {
198 JSAutoCompartment ac(cx, aClassObject);
199 JS::CompileOptions options(cx);
200 options.setFileAndLine(functionUri.get(), getterText->GetLineNumber())
201 .setVersion(JSVERSION_LATEST);
202 nsCString name = NS_LITERAL_CSTRING("get_") + NS_ConvertUTF16toUTF8(mName);
203 JS::Rooted<JSObject*> getterObject(cx);
204 JS::AutoObjectVector emptyVector(cx);
205 rv = nsJSUtils::CompileFunction(jsapi, emptyVector, options, name, 0,
206 nullptr, getter, getterObject.address());
208 delete getterText;
209 deletedGetter = true;
211 mGetter.SetJSFunction(getterObject);
213 if (mGetter.GetJSFunction() && NS_SUCCEEDED(rv)) {
214 mJSAttributes |= JSPROP_GETTER | JSPROP_SHARED;
216 if (NS_FAILED(rv)) {
217 mGetter.SetJSFunction(nullptr);
218 mJSAttributes &= ~JSPROP_GETTER;
219 /*chaining to return failure*/
222 } // if getter is not empty
224 if (!deletedGetter) { // Empty getter
225 delete getterText;
226 mGetter.SetJSFunction(nullptr);
229 if (NS_FAILED(rv)) {
230 // We failed to compile our getter. So either we've set it to null, or
231 // it's still set to the text object. In either case, it's safe to return
232 // the error here, since then we'll be cleaned up as uncompiled and that
233 // will be ok. Going on and compiling the setter and _then_ returning an
234 // error, on the other hand, will try to clean up a compiled setter as
235 // uncompiled and crash.
236 return rv;
239 bool deletedSetter = false;
240 nsXBLTextWithLineNumber *setterText = mSetter.GetUncompiled();
241 if (setterText && setterText->GetText()) {
242 nsDependentString setter(setterText->GetText());
243 if (!setter.IsEmpty()) {
244 JSAutoCompartment ac(cx, aClassObject);
245 JS::CompileOptions options(cx);
246 options.setFileAndLine(functionUri.get(), setterText->GetLineNumber())
247 .setVersion(JSVERSION_LATEST);
248 nsCString name = NS_LITERAL_CSTRING("set_") + NS_ConvertUTF16toUTF8(mName);
249 JS::Rooted<JSObject*> setterObject(cx);
250 JS::AutoObjectVector emptyVector(cx);
251 rv = nsJSUtils::CompileFunction(jsapi, emptyVector, options, name, 1,
252 gPropertyArgs, setter,
253 setterObject.address());
255 delete setterText;
256 deletedSetter = true;
257 mSetter.SetJSFunction(setterObject);
259 if (mSetter.GetJSFunction() && NS_SUCCEEDED(rv)) {
260 mJSAttributes |= JSPROP_SETTER | JSPROP_SHARED;
262 if (NS_FAILED(rv)) {
263 mSetter.SetJSFunction(nullptr);
264 mJSAttributes &= ~JSPROP_SETTER;
265 /*chaining to return failure*/
268 } // if setter wasn't empty....
270 if (!deletedSetter) { // Empty setter
271 delete setterText;
272 mSetter.SetJSFunction(nullptr);
275 #ifdef DEBUG
276 mIsCompiled = NS_SUCCEEDED(rv);
277 #endif
279 return rv;
282 void
283 nsXBLProtoImplProperty::Trace(const TraceCallbacks& aCallbacks, void *aClosure)
285 if (mJSAttributes & JSPROP_GETTER) {
286 aCallbacks.Trace(&mGetter.AsHeapObject(), "mGetter", aClosure);
289 if (mJSAttributes & JSPROP_SETTER) {
290 aCallbacks.Trace(&mSetter.AsHeapObject(), "mSetter", aClosure);
294 nsresult
295 nsXBLProtoImplProperty::Read(nsIObjectInputStream* aStream,
296 XBLBindingSerializeDetails aType)
298 AssertInCompilationScope();
299 MOZ_ASSERT(!mIsCompiled);
300 MOZ_ASSERT(!mGetter.GetUncompiled() && !mSetter.GetUncompiled());
302 AutoJSContext cx;
303 JS::Rooted<JSObject*> getterObject(cx);
304 if (aType == XBLBinding_Serialize_GetterProperty ||
305 aType == XBLBinding_Serialize_GetterSetterProperty) {
306 nsresult rv = XBL_DeserializeFunction(aStream, &getterObject);
307 NS_ENSURE_SUCCESS(rv, rv);
309 mJSAttributes |= JSPROP_GETTER | JSPROP_SHARED;
311 mGetter.SetJSFunction(getterObject);
313 JS::Rooted<JSObject*> setterObject(cx);
314 if (aType == XBLBinding_Serialize_SetterProperty ||
315 aType == XBLBinding_Serialize_GetterSetterProperty) {
316 nsresult rv = XBL_DeserializeFunction(aStream, &setterObject);
317 NS_ENSURE_SUCCESS(rv, rv);
319 mJSAttributes |= JSPROP_SETTER | JSPROP_SHARED;
321 mSetter.SetJSFunction(setterObject);
323 #ifdef DEBUG
324 mIsCompiled = true;
325 #endif
327 return NS_OK;
330 nsresult
331 nsXBLProtoImplProperty::Write(nsIObjectOutputStream* aStream)
333 AssertInCompilationScope();
334 XBLBindingSerializeDetails type;
336 if (mJSAttributes & JSPROP_GETTER) {
337 type = mJSAttributes & JSPROP_SETTER ?
338 XBLBinding_Serialize_GetterSetterProperty :
339 XBLBinding_Serialize_GetterProperty;
341 else {
342 type = XBLBinding_Serialize_SetterProperty;
345 if (mJSAttributes & JSPROP_READONLY) {
346 type |= XBLBinding_Serialize_ReadOnly;
349 nsresult rv = aStream->Write8(type);
350 NS_ENSURE_SUCCESS(rv, rv);
351 rv = aStream->WriteWStringZ(mName);
352 NS_ENSURE_SUCCESS(rv, rv);
354 // The calls to fromMarkedLocation() below are safe because mSetter and
355 // mGetter are traced by the Trace() method above, and because their values
356 // are never changed after they have been set to a compiled function.
357 MOZ_ASSERT_IF(mJSAttributes & (JSPROP_GETTER | JSPROP_SETTER), mIsCompiled);
359 if (mJSAttributes & JSPROP_GETTER) {
360 JS::Handle<JSObject*> function =
361 JS::Handle<JSObject*>::fromMarkedLocation(mGetter.AsHeapObject().address());
362 rv = XBL_SerializeFunction(aStream, function);
363 NS_ENSURE_SUCCESS(rv, rv);
366 if (mJSAttributes & JSPROP_SETTER) {
367 JS::Handle<JSObject*> function =
368 JS::Handle<JSObject*>::fromMarkedLocation(mSetter.AsHeapObject().address());
369 rv = XBL_SerializeFunction(aStream, function);
370 NS_ENSURE_SUCCESS(rv, rv);
373 return NS_OK;