Bumping manifests a=b2g-bump
[gecko.git] / dom / xbl / nsXBLProtoImplProperty.cpp
blob295f63db05c012be73d49f836bc6d52a21a686a7
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;
20 nsXBLProtoImplProperty::nsXBLProtoImplProperty(const char16_t* aName,
21 const char16_t* aGetter,
22 const char16_t* aSetter,
23 const char16_t* aReadOnly,
24 uint32_t aLineNumber) :
25 nsXBLProtoImplMember(aName),
26 mJSAttributes(JSPROP_ENUMERATE)
27 #ifdef DEBUG
28 , mIsCompiled(false)
29 #endif
31 MOZ_COUNT_CTOR(nsXBLProtoImplProperty);
33 if (aReadOnly) {
34 nsAutoString readOnly; readOnly.Assign(*aReadOnly);
35 if (readOnly.LowerCaseEqualsLiteral("true"))
36 mJSAttributes |= JSPROP_READONLY;
39 if (aGetter) {
40 AppendGetterText(nsDependentString(aGetter));
41 SetGetterLineNumber(aLineNumber);
43 if (aSetter) {
44 AppendSetterText(nsDependentString(aSetter));
45 SetSetterLineNumber(aLineNumber);
49 nsXBLProtoImplProperty::nsXBLProtoImplProperty(const char16_t* aName,
50 const bool aIsReadOnly)
51 : nsXBLProtoImplMember(aName),
52 mJSAttributes(JSPROP_ENUMERATE)
53 #ifdef DEBUG
54 , mIsCompiled(false)
55 #endif
57 MOZ_COUNT_CTOR(nsXBLProtoImplProperty);
59 if (aIsReadOnly)
60 mJSAttributes |= JSPROP_READONLY;
63 nsXBLProtoImplProperty::~nsXBLProtoImplProperty()
65 MOZ_COUNT_DTOR(nsXBLProtoImplProperty);
67 if (!mGetter.IsCompiled()) {
68 delete mGetter.GetUncompiled();
71 if (!mSetter.IsCompiled()) {
72 delete mSetter.GetUncompiled();
76 void nsXBLProtoImplProperty::EnsureUncompiledText(PropertyOp& aPropertyOp)
78 if (!aPropertyOp.GetUncompiled()) {
79 nsXBLTextWithLineNumber* text = new nsXBLTextWithLineNumber();
80 aPropertyOp.SetUncompiled(text);
84 void
85 nsXBLProtoImplProperty::AppendGetterText(const nsAString& aText)
87 NS_PRECONDITION(!mIsCompiled,
88 "Must not be compiled when accessing getter text");
89 EnsureUncompiledText(mGetter);
90 mGetter.GetUncompiled()->AppendText(aText);
93 void
94 nsXBLProtoImplProperty::AppendSetterText(const nsAString& aText)
96 NS_PRECONDITION(!mIsCompiled,
97 "Must not be compiled when accessing setter text");
98 EnsureUncompiledText(mSetter);
99 mSetter.GetUncompiled()->AppendText(aText);
102 void
103 nsXBLProtoImplProperty::SetGetterLineNumber(uint32_t aLineNumber)
105 NS_PRECONDITION(!mIsCompiled,
106 "Must not be compiled when accessing getter text");
107 EnsureUncompiledText(mGetter);
108 mGetter.GetUncompiled()->SetLineNumber(aLineNumber);
111 void
112 nsXBLProtoImplProperty::SetSetterLineNumber(uint32_t aLineNumber)
114 NS_PRECONDITION(!mIsCompiled,
115 "Must not be compiled when accessing setter text");
116 EnsureUncompiledText(mSetter);
117 mSetter.GetUncompiled()->SetLineNumber(aLineNumber);
120 const char* gPropertyArgs[] = { "val" };
122 nsresult
123 nsXBLProtoImplProperty::InstallMember(JSContext *aCx,
124 JS::Handle<JSObject*> aTargetClassObject)
126 NS_PRECONDITION(mIsCompiled,
127 "Should not be installing an uncompiled property");
128 MOZ_ASSERT(mGetter.IsCompiled() && mSetter.IsCompiled());
129 MOZ_ASSERT(js::IsObjectInContextCompartment(aTargetClassObject, aCx));
130 JS::Rooted<JSObject*> globalObject(aCx, JS_GetGlobalForObject(aCx, aTargetClassObject));
131 MOZ_ASSERT(xpc::IsInContentXBLScope(globalObject) ||
132 xpc::IsInAddonScope(globalObject) ||
133 globalObject == xpc::GetXBLScope(aCx, globalObject));
135 JS::Rooted<JSObject*> getter(aCx, mGetter.GetJSFunction());
136 JS::Rooted<JSObject*> setter(aCx, mSetter.GetJSFunction());
137 if (getter || setter) {
138 if (getter) {
139 if (!(getter = ::JS_CloneFunctionObject(aCx, getter, globalObject)))
140 return NS_ERROR_OUT_OF_MEMORY;
143 if (setter) {
144 if (!(setter = ::JS_CloneFunctionObject(aCx, setter, globalObject)))
145 return NS_ERROR_OUT_OF_MEMORY;
148 nsDependentString name(mName);
149 if (!::JS_DefineUCProperty(aCx, aTargetClassObject,
150 static_cast<const jschar*>(mName),
151 name.Length(), JS::UndefinedHandleValue, mJSAttributes,
152 JS_DATA_TO_FUNC_PTR(JSPropertyOp, getter.get()),
153 JS_DATA_TO_FUNC_PTR(JSStrictPropertyOp, setter.get())))
154 return NS_ERROR_OUT_OF_MEMORY;
156 return NS_OK;
159 nsresult
160 nsXBLProtoImplProperty::CompileMember(const nsCString& aClassStr,
161 JS::Handle<JSObject*> aClassObject)
163 AssertInCompilationScope();
164 NS_PRECONDITION(!mIsCompiled,
165 "Trying to compile an already-compiled property");
166 NS_PRECONDITION(aClassObject,
167 "Must have class object to compile");
168 MOZ_ASSERT(!mGetter.IsCompiled() && !mSetter.IsCompiled());
170 if (!mName)
171 return NS_ERROR_FAILURE; // Without a valid name, we can't install the member.
173 // We have a property.
174 nsresult rv = NS_OK;
176 nsAutoCString functionUri;
177 if (mGetter.GetUncompiled() || mSetter.GetUncompiled()) {
178 functionUri = aClassStr;
179 int32_t hash = functionUri.RFindChar('#');
180 if (hash != kNotFound) {
181 functionUri.Truncate(hash);
185 bool deletedGetter = false;
186 nsXBLTextWithLineNumber *getterText = mGetter.GetUncompiled();
187 if (getterText && getterText->GetText()) {
188 nsDependentString getter(getterText->GetText());
189 if (!getter.IsEmpty()) {
190 AutoJSContext cx;
191 JSAutoCompartment ac(cx, aClassObject);
192 JS::CompileOptions options(cx);
193 options.setFileAndLine(functionUri.get(), getterText->GetLineNumber())
194 .setVersion(JSVERSION_LATEST);
195 nsCString name = NS_LITERAL_CSTRING("get_") + NS_ConvertUTF16toUTF8(mName);
196 JS::Rooted<JSObject*> getterObject(cx);
197 rv = nsJSUtils::CompileFunction(cx, JS::NullPtr(), options, name, 0,
198 nullptr, getter, getterObject.address());
200 delete getterText;
201 deletedGetter = true;
203 mGetter.SetJSFunction(getterObject);
205 if (mGetter.GetJSFunction() && NS_SUCCEEDED(rv)) {
206 mJSAttributes |= JSPROP_GETTER | JSPROP_SHARED;
208 if (NS_FAILED(rv)) {
209 mGetter.SetJSFunction(nullptr);
210 mJSAttributes &= ~JSPROP_GETTER;
211 /*chaining to return failure*/
214 } // if getter is not empty
216 if (!deletedGetter) { // Empty getter
217 delete getterText;
218 mGetter.SetJSFunction(nullptr);
221 if (NS_FAILED(rv)) {
222 // We failed to compile our getter. So either we've set it to null, or
223 // it's still set to the text object. In either case, it's safe to return
224 // the error here, since then we'll be cleaned up as uncompiled and that
225 // will be ok. Going on and compiling the setter and _then_ returning an
226 // error, on the other hand, will try to clean up a compiled setter as
227 // uncompiled and crash.
228 return rv;
231 bool deletedSetter = false;
232 nsXBLTextWithLineNumber *setterText = mSetter.GetUncompiled();
233 if (setterText && setterText->GetText()) {
234 nsDependentString setter(setterText->GetText());
235 if (!setter.IsEmpty()) {
236 AutoJSContext cx;
237 JSAutoCompartment ac(cx, aClassObject);
238 JS::CompileOptions options(cx);
239 options.setFileAndLine(functionUri.get(), setterText->GetLineNumber())
240 .setVersion(JSVERSION_LATEST);
241 nsCString name = NS_LITERAL_CSTRING("set_") + NS_ConvertUTF16toUTF8(mName);
242 JS::Rooted<JSObject*> setterObject(cx);
243 rv = nsJSUtils::CompileFunction(cx, JS::NullPtr(), options, name, 1,
244 gPropertyArgs, setter,
245 setterObject.address());
247 delete setterText;
248 deletedSetter = true;
249 mSetter.SetJSFunction(setterObject);
251 if (mSetter.GetJSFunction() && NS_SUCCEEDED(rv)) {
252 mJSAttributes |= JSPROP_SETTER | JSPROP_SHARED;
254 if (NS_FAILED(rv)) {
255 mSetter.SetJSFunction(nullptr);
256 mJSAttributes &= ~JSPROP_SETTER;
257 /*chaining to return failure*/
260 } // if setter wasn't empty....
262 if (!deletedSetter) { // Empty setter
263 delete setterText;
264 mSetter.SetJSFunction(nullptr);
267 #ifdef DEBUG
268 mIsCompiled = NS_SUCCEEDED(rv);
269 #endif
271 return rv;
274 void
275 nsXBLProtoImplProperty::Trace(const TraceCallbacks& aCallbacks, void *aClosure)
277 if (mJSAttributes & JSPROP_GETTER) {
278 aCallbacks.Trace(&mGetter.AsHeapObject(), "mGetter", aClosure);
281 if (mJSAttributes & JSPROP_SETTER) {
282 aCallbacks.Trace(&mSetter.AsHeapObject(), "mSetter", aClosure);
286 nsresult
287 nsXBLProtoImplProperty::Read(nsIObjectInputStream* aStream,
288 XBLBindingSerializeDetails aType)
290 AssertInCompilationScope();
291 MOZ_ASSERT(!mIsCompiled);
292 MOZ_ASSERT(!mGetter.GetUncompiled() && !mSetter.GetUncompiled());
294 AutoJSContext cx;
295 JS::Rooted<JSObject*> getterObject(cx);
296 if (aType == XBLBinding_Serialize_GetterProperty ||
297 aType == XBLBinding_Serialize_GetterSetterProperty) {
298 nsresult rv = XBL_DeserializeFunction(aStream, &getterObject);
299 NS_ENSURE_SUCCESS(rv, rv);
301 mJSAttributes |= JSPROP_GETTER | JSPROP_SHARED;
303 mGetter.SetJSFunction(getterObject);
305 JS::Rooted<JSObject*> setterObject(cx);
306 if (aType == XBLBinding_Serialize_SetterProperty ||
307 aType == XBLBinding_Serialize_GetterSetterProperty) {
308 nsresult rv = XBL_DeserializeFunction(aStream, &setterObject);
309 NS_ENSURE_SUCCESS(rv, rv);
311 mJSAttributes |= JSPROP_SETTER | JSPROP_SHARED;
313 mSetter.SetJSFunction(setterObject);
315 #ifdef DEBUG
316 mIsCompiled = true;
317 #endif
319 return NS_OK;
322 nsresult
323 nsXBLProtoImplProperty::Write(nsIObjectOutputStream* aStream)
325 AssertInCompilationScope();
326 XBLBindingSerializeDetails type;
328 if (mJSAttributes & JSPROP_GETTER) {
329 type = mJSAttributes & JSPROP_SETTER ?
330 XBLBinding_Serialize_GetterSetterProperty :
331 XBLBinding_Serialize_GetterProperty;
333 else {
334 type = XBLBinding_Serialize_SetterProperty;
337 if (mJSAttributes & JSPROP_READONLY) {
338 type |= XBLBinding_Serialize_ReadOnly;
341 nsresult rv = aStream->Write8(type);
342 NS_ENSURE_SUCCESS(rv, rv);
343 rv = aStream->WriteWStringZ(mName);
344 NS_ENSURE_SUCCESS(rv, rv);
346 // The calls to fromMarkedLocation() below are safe because mSetter and
347 // mGetter are traced by the Trace() method above, and because their values
348 // are never changed after they have been set to a compiled function.
349 MOZ_ASSERT_IF(mJSAttributes & (JSPROP_GETTER | JSPROP_SETTER), mIsCompiled);
351 if (mJSAttributes & JSPROP_GETTER) {
352 JS::Handle<JSObject*> function =
353 JS::Handle<JSObject*>::fromMarkedLocation(mGetter.AsHeapObject().address());
354 rv = XBL_SerializeFunction(aStream, function);
355 NS_ENSURE_SUCCESS(rv, rv);
358 if (mJSAttributes & JSPROP_SETTER) {
359 JS::Handle<JSObject*> function =
360 JS::Handle<JSObject*>::fromMarkedLocation(mSetter.AsHeapObject().address());
361 rv = XBL_SerializeFunction(aStream, function);
362 NS_ENSURE_SUCCESS(rv, rv);
365 return NS_OK;