dom/xbl/nsXBLProtoImplProperty.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:9039e9a7bd75
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/. */
5
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 "nsCxPusher.h"
13 #include "nsReadableUtils.h"
14 #include "nsJSUtils.h"
15 #include "nsXBLPrototypeBinding.h"
16 #include "nsXBLSerialize.h"
17 #include "xpcpublic.h"
18
19 using namespace mozilla;
20
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
31 {
32 MOZ_COUNT_CTOR(nsXBLProtoImplProperty);
33
34 if (aReadOnly) {
35 nsAutoString readOnly; readOnly.Assign(*aReadOnly);
36 if (readOnly.LowerCaseEqualsLiteral("true"))
37 mJSAttributes |= JSPROP_READONLY;
38 }
39
40 if (aGetter) {
41 AppendGetterText(nsDependentString(aGetter));
42 SetGetterLineNumber(aLineNumber);
43 }
44 if (aSetter) {
45 AppendSetterText(nsDependentString(aSetter));
46 SetSetterLineNumber(aLineNumber);
47 }
48 }
49
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
57 {
58 MOZ_COUNT_CTOR(nsXBLProtoImplProperty);
59
60 if (aIsReadOnly)
61 mJSAttributes |= JSPROP_READONLY;
62 }
63
64 nsXBLProtoImplProperty::~nsXBLProtoImplProperty()
65 {
66 MOZ_COUNT_DTOR(nsXBLProtoImplProperty);
67
68 if (!mGetter.IsCompiled()) {
69 delete mGetter.GetUncompiled();
70 }
71
72 if (!mSetter.IsCompiled()) {
73 delete mSetter.GetUncompiled();
74 }
75 }
76
77 void nsXBLProtoImplProperty::EnsureUncompiledText(PropertyOp& aPropertyOp)
78 {
79 if (!aPropertyOp.GetUncompiled()) {
80 nsXBLTextWithLineNumber* text = new nsXBLTextWithLineNumber();
81 aPropertyOp.SetUncompiled(text);
82 }
83 }
84
85 void
86 nsXBLProtoImplProperty::AppendGetterText(const nsAString& aText)
87 {
88 NS_PRECONDITION(!mIsCompiled,
89 "Must not be compiled when accessing getter text");
90 EnsureUncompiledText(mGetter);
91 mGetter.GetUncompiled()->AppendText(aText);
92 }
93
94 void
95 nsXBLProtoImplProperty::AppendSetterText(const nsAString& aText)
96 {
97 NS_PRECONDITION(!mIsCompiled,
98 "Must not be compiled when accessing setter text");
99 EnsureUncompiledText(mSetter);
100 mSetter.GetUncompiled()->AppendText(aText);
101 }
102
103 void
104 nsXBLProtoImplProperty::SetGetterLineNumber(uint32_t aLineNumber)
105 {
106 NS_PRECONDITION(!mIsCompiled,
107 "Must not be compiled when accessing getter text");
108 EnsureUncompiledText(mGetter);
109 mGetter.GetUncompiled()->SetLineNumber(aLineNumber);
110 }
111
112 void
113 nsXBLProtoImplProperty::SetSetterLineNumber(uint32_t aLineNumber)
114 {
115 NS_PRECONDITION(!mIsCompiled,
116 "Must not be compiled when accessing setter text");
117 EnsureUncompiledText(mSetter);
118 mSetter.GetUncompiled()->SetLineNumber(aLineNumber);
119 }
120
121 const char* gPropertyArgs[] = { "val" };
122
123 nsresult
124 nsXBLProtoImplProperty::InstallMember(JSContext *aCx,
125 JS::Handle<JSObject*> aTargetClassObject)
126 {
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));
131 JS::Rooted<JSObject*> globalObject(aCx, JS_GetGlobalForObject(aCx, aTargetClassObject));
132 MOZ_ASSERT(xpc::IsInXBLScope(globalObject) ||
133 globalObject == xpc::GetXBLScope(aCx, globalObject));
134
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;
141 }
142
143 if (setter) {
144 if (!(setter = ::JS_CloneFunctionObject(aCx, setter, globalObject)))
145 return NS_ERROR_OUT_OF_MEMORY;
146 }
147
148 nsDependentString name(mName);
149 if (!::JS_DefineUCProperty(aCx, aTargetClassObject,
150 static_cast<const jschar*>(mName),
151 name.Length(), JSVAL_VOID,
152 JS_DATA_TO_FUNC_PTR(JSPropertyOp, getter.get()),
153 JS_DATA_TO_FUNC_PTR(JSStrictPropertyOp, setter.get()),
154 mJSAttributes))
155 return NS_ERROR_OUT_OF_MEMORY;
156 }
157 return NS_OK;
158 }
159
160 nsresult
161 nsXBLProtoImplProperty::CompileMember(const nsCString& aClassStr,
162 JS::Handle<JSObject*> aClassObject)
163 {
164 AssertInCompilationScope();
165 NS_PRECONDITION(!mIsCompiled,
166 "Trying to compile an already-compiled property");
167 NS_PRECONDITION(aClassObject,
168 "Must have class object to compile");
169 MOZ_ASSERT(!mGetter.IsCompiled() && !mSetter.IsCompiled());
170
171 if (!mName)
172 return NS_ERROR_FAILURE; // Without a valid name, we can't install the member.
173
174 // We have a property.
175 nsresult rv = NS_OK;
176
177 nsAutoCString functionUri;
178 if (mGetter.GetUncompiled() || mSetter.GetUncompiled()) {
179 functionUri = aClassStr;
180 int32_t hash = functionUri.RFindChar('#');
181 if (hash != kNotFound) {
182 functionUri.Truncate(hash);
183 }
184 }
185
186 bool deletedGetter = false;
187 nsXBLTextWithLineNumber *getterText = mGetter.GetUncompiled();
188 if (getterText && getterText->GetText()) {
189 nsDependentString getter(getterText->GetText());
190 if (!getter.IsEmpty()) {
191 AutoJSContext cx;
192 JSAutoCompartment ac(cx, aClassObject);
193 JS::CompileOptions options(cx);
194 options.setFileAndLine(functionUri.get(), getterText->GetLineNumber())
195 .setVersion(JSVERSION_LATEST);
196 nsCString name = NS_LITERAL_CSTRING("get_") + NS_ConvertUTF16toUTF8(mName);
197 JS::Rooted<JSObject*> getterObject(cx);
198 rv = nsJSUtils::CompileFunction(cx, JS::NullPtr(), options, name, 0,
199 nullptr, getter, getterObject.address());
200
201 delete getterText;
202 deletedGetter = true;
203
204 mGetter.SetJSFunction(getterObject);
205
206 if (mGetter.GetJSFunction() && NS_SUCCEEDED(rv)) {
207 mJSAttributes |= JSPROP_GETTER | JSPROP_SHARED;
208 }
209 if (NS_FAILED(rv)) {
210 mGetter.SetJSFunction(nullptr);
211 mJSAttributes &= ~JSPROP_GETTER;
212 /*chaining to return failure*/
213 }
214 }
215 } // if getter is not empty
216
217 if (!deletedGetter) { // Empty getter
218 delete getterText;
219 mGetter.SetJSFunction(nullptr);
220 }
221
222 if (NS_FAILED(rv)) {
223 // We failed to compile our getter. So either we've set it to null, or
224 // it's still set to the text object. In either case, it's safe to return
225 // the error here, since then we'll be cleaned up as uncompiled and that
226 // will be ok. Going on and compiling the setter and _then_ returning an
227 // error, on the other hand, will try to clean up a compiled setter as
228 // uncompiled and crash.
229 return rv;
230 }
231
232 bool deletedSetter = false;
233 nsXBLTextWithLineNumber *setterText = mSetter.GetUncompiled();
234 if (setterText && setterText->GetText()) {
235 nsDependentString setter(setterText->GetText());
236 if (!setter.IsEmpty()) {
237 AutoJSContext cx;
238 JSAutoCompartment ac(cx, aClassObject);
239 JS::CompileOptions options(cx);
240 options.setFileAndLine(functionUri.get(), setterText->GetLineNumber())
241 .setVersion(JSVERSION_LATEST);
242 nsCString name = NS_LITERAL_CSTRING("set_") + NS_ConvertUTF16toUTF8(mName);
243 JS::Rooted<JSObject*> setterObject(cx);
244 rv = nsJSUtils::CompileFunction(cx, JS::NullPtr(), options, name, 1,
245 gPropertyArgs, setter,
246 setterObject.address());
247
248 delete setterText;
249 deletedSetter = true;
250 mSetter.SetJSFunction(setterObject);
251
252 if (mSetter.GetJSFunction() && NS_SUCCEEDED(rv)) {
253 mJSAttributes |= JSPROP_SETTER | JSPROP_SHARED;
254 }
255 if (NS_FAILED(rv)) {
256 mSetter.SetJSFunction(nullptr);
257 mJSAttributes &= ~JSPROP_SETTER;
258 /*chaining to return failure*/
259 }
260 }
261 } // if setter wasn't empty....
262
263 if (!deletedSetter) { // Empty setter
264 delete setterText;
265 mSetter.SetJSFunction(nullptr);
266 }
267
268 #ifdef DEBUG
269 mIsCompiled = NS_SUCCEEDED(rv);
270 #endif
271
272 return rv;
273 }
274
275 void
276 nsXBLProtoImplProperty::Trace(const TraceCallbacks& aCallbacks, void *aClosure)
277 {
278 if (mJSAttributes & JSPROP_GETTER) {
279 aCallbacks.Trace(&mGetter.AsHeapObject(), "mGetter", aClosure);
280 }
281
282 if (mJSAttributes & JSPROP_SETTER) {
283 aCallbacks.Trace(&mSetter.AsHeapObject(), "mSetter", aClosure);
284 }
285 }
286
287 nsresult
288 nsXBLProtoImplProperty::Read(nsIObjectInputStream* aStream,
289 XBLBindingSerializeDetails aType)
290 {
291 AssertInCompilationScope();
292 MOZ_ASSERT(!mIsCompiled);
293 MOZ_ASSERT(!mGetter.GetUncompiled() && !mSetter.GetUncompiled());
294
295 AutoJSContext cx;
296 JS::Rooted<JSObject*> getterObject(cx);
297 if (aType == XBLBinding_Serialize_GetterProperty ||
298 aType == XBLBinding_Serialize_GetterSetterProperty) {
299 nsresult rv = XBL_DeserializeFunction(aStream, &getterObject);
300 NS_ENSURE_SUCCESS(rv, rv);
301
302 mJSAttributes |= JSPROP_GETTER | JSPROP_SHARED;
303 }
304 mGetter.SetJSFunction(getterObject);
305
306 JS::Rooted<JSObject*> setterObject(cx);
307 if (aType == XBLBinding_Serialize_SetterProperty ||
308 aType == XBLBinding_Serialize_GetterSetterProperty) {
309 nsresult rv = XBL_DeserializeFunction(aStream, &setterObject);
310 NS_ENSURE_SUCCESS(rv, rv);
311
312 mJSAttributes |= JSPROP_SETTER | JSPROP_SHARED;
313 }
314 mSetter.SetJSFunction(setterObject);
315
316 #ifdef DEBUG
317 mIsCompiled = true;
318 #endif
319
320 return NS_OK;
321 }
322
323 nsresult
324 nsXBLProtoImplProperty::Write(nsIObjectOutputStream* aStream)
325 {
326 AssertInCompilationScope();
327 XBLBindingSerializeDetails type;
328
329 if (mJSAttributes & JSPROP_GETTER) {
330 type = mJSAttributes & JSPROP_SETTER ?
331 XBLBinding_Serialize_GetterSetterProperty :
332 XBLBinding_Serialize_GetterProperty;
333 }
334 else {
335 type = XBLBinding_Serialize_SetterProperty;
336 }
337
338 if (mJSAttributes & JSPROP_READONLY) {
339 type |= XBLBinding_Serialize_ReadOnly;
340 }
341
342 nsresult rv = aStream->Write8(type);
343 NS_ENSURE_SUCCESS(rv, rv);
344 rv = aStream->WriteWStringZ(mName);
345 NS_ENSURE_SUCCESS(rv, rv);
346
347 // The calls to fromMarkedLocation() below are safe because mSetter and
348 // mGetter are traced by the Trace() method above, and because their values
349 // are never changed after they have been set to a compiled function.
350 MOZ_ASSERT_IF(mJSAttributes & (JSPROP_GETTER | JSPROP_SETTER), mIsCompiled);
351
352 if (mJSAttributes & JSPROP_GETTER) {
353 JS::Handle<JSObject*> function =
354 JS::Handle<JSObject*>::fromMarkedLocation(mGetter.AsHeapObject().address());
355 rv = XBL_SerializeFunction(aStream, function);
356 NS_ENSURE_SUCCESS(rv, rv);
357 }
358
359 if (mJSAttributes & JSPROP_SETTER) {
360 JS::Handle<JSObject*> function =
361 JS::Handle<JSObject*>::fromMarkedLocation(mSetter.AsHeapObject().address());
362 rv = XBL_SerializeFunction(aStream, function);
363 NS_ENSURE_SUCCESS(rv, rv);
364 }
365
366 return NS_OK;
367 }

mercurial