Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "nsIAtom.h"
8 : #include "nsString.h"
9 : #include "jsapi.h"
10 : #include "nsIContent.h"
11 : #include "nsXBLProtoImplProperty.h"
12 : #include "nsUnicharUtils.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 : using namespace mozilla::dom;
21 :
22 0 : nsXBLProtoImplProperty::nsXBLProtoImplProperty(const char16_t* aName,
23 : const char16_t* aGetter,
24 : const char16_t* aSetter,
25 : const char16_t* aReadOnly,
26 0 : uint32_t aLineNumber) :
27 : nsXBLProtoImplMember(aName),
28 : mJSAttributes(JSPROP_ENUMERATE)
29 : #ifdef DEBUG
30 0 : , mIsCompiled(false)
31 : #endif
32 : {
33 0 : MOZ_COUNT_CTOR(nsXBLProtoImplProperty);
34 :
35 0 : if (aReadOnly) {
36 0 : nsAutoString readOnly; readOnly.Assign(*aReadOnly);
37 0 : if (readOnly.LowerCaseEqualsLiteral("true"))
38 0 : mJSAttributes |= JSPROP_READONLY;
39 : }
40 :
41 0 : if (aGetter) {
42 0 : AppendGetterText(nsDependentString(aGetter));
43 0 : SetGetterLineNumber(aLineNumber);
44 : }
45 0 : if (aSetter) {
46 0 : AppendSetterText(nsDependentString(aSetter));
47 0 : SetSetterLineNumber(aLineNumber);
48 : }
49 0 : }
50 :
51 355 : nsXBLProtoImplProperty::nsXBLProtoImplProperty(const char16_t* aName,
52 355 : const bool aIsReadOnly)
53 : : nsXBLProtoImplMember(aName),
54 : mJSAttributes(JSPROP_ENUMERATE)
55 : #ifdef DEBUG
56 355 : , mIsCompiled(false)
57 : #endif
58 : {
59 355 : MOZ_COUNT_CTOR(nsXBLProtoImplProperty);
60 :
61 355 : if (aIsReadOnly)
62 0 : mJSAttributes |= JSPROP_READONLY;
63 355 : }
64 :
65 0 : nsXBLProtoImplProperty::~nsXBLProtoImplProperty()
66 : {
67 0 : MOZ_COUNT_DTOR(nsXBLProtoImplProperty);
68 :
69 0 : if (!mGetter.IsCompiled()) {
70 0 : delete mGetter.GetUncompiled();
71 : }
72 :
73 0 : if (!mSetter.IsCompiled()) {
74 0 : delete mSetter.GetUncompiled();
75 : }
76 0 : }
77 :
78 0 : void nsXBLProtoImplProperty::EnsureUncompiledText(PropertyOp& aPropertyOp)
79 : {
80 0 : if (!aPropertyOp.GetUncompiled()) {
81 0 : nsXBLTextWithLineNumber* text = new nsXBLTextWithLineNumber();
82 0 : aPropertyOp.SetUncompiled(text);
83 : }
84 0 : }
85 :
86 : void
87 0 : nsXBLProtoImplProperty::AppendGetterText(const nsAString& aText)
88 : {
89 0 : NS_PRECONDITION(!mIsCompiled,
90 : "Must not be compiled when accessing getter text");
91 0 : EnsureUncompiledText(mGetter);
92 0 : mGetter.GetUncompiled()->AppendText(aText);
93 0 : }
94 :
95 : void
96 0 : nsXBLProtoImplProperty::AppendSetterText(const nsAString& aText)
97 : {
98 0 : NS_PRECONDITION(!mIsCompiled,
99 : "Must not be compiled when accessing setter text");
100 0 : EnsureUncompiledText(mSetter);
101 0 : mSetter.GetUncompiled()->AppendText(aText);
102 0 : }
103 :
104 : void
105 0 : nsXBLProtoImplProperty::SetGetterLineNumber(uint32_t aLineNumber)
106 : {
107 0 : NS_PRECONDITION(!mIsCompiled,
108 : "Must not be compiled when accessing getter text");
109 0 : EnsureUncompiledText(mGetter);
110 0 : mGetter.GetUncompiled()->SetLineNumber(aLineNumber);
111 0 : }
112 :
113 : void
114 0 : nsXBLProtoImplProperty::SetSetterLineNumber(uint32_t aLineNumber)
115 : {
116 0 : NS_PRECONDITION(!mIsCompiled,
117 : "Must not be compiled when accessing setter text");
118 0 : EnsureUncompiledText(mSetter);
119 0 : mSetter.GetUncompiled()->SetLineNumber(aLineNumber);
120 0 : }
121 :
122 : const char* gPropertyArgs[] = { "val" };
123 :
124 : nsresult
125 303 : nsXBLProtoImplProperty::InstallMember(JSContext *aCx,
126 : JS::Handle<JSObject*> aTargetClassObject)
127 : {
128 303 : NS_PRECONDITION(mIsCompiled,
129 : "Should not be installing an uncompiled property");
130 303 : MOZ_ASSERT(mGetter.IsCompiled() && mSetter.IsCompiled());
131 303 : MOZ_ASSERT(js::IsObjectInContextCompartment(aTargetClassObject, aCx));
132 :
133 : #ifdef DEBUG
134 : {
135 606 : JS::Rooted<JSObject*> globalObject(aCx, JS_GetGlobalForObject(aCx, aTargetClassObject));
136 303 : MOZ_ASSERT(xpc::IsInContentXBLScope(globalObject) ||
137 : xpc::IsInAddonScope(globalObject) ||
138 : globalObject == xpc::GetXBLScope(aCx, globalObject));
139 303 : MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx) == globalObject);
140 : }
141 : #endif
142 :
143 606 : JS::Rooted<JSObject*> getter(aCx, mGetter.GetJSFunction());
144 606 : JS::Rooted<JSObject*> setter(aCx, mSetter.GetJSFunction());
145 303 : if (getter || setter) {
146 303 : if (getter) {
147 300 : if (!(getter = JS::CloneFunctionObject(aCx, getter)))
148 0 : return NS_ERROR_OUT_OF_MEMORY;
149 : }
150 :
151 303 : if (setter) {
152 128 : if (!(setter = JS::CloneFunctionObject(aCx, setter)))
153 0 : return NS_ERROR_OUT_OF_MEMORY;
154 : }
155 :
156 606 : nsDependentString name(mName);
157 1212 : if (!::JS_DefineUCProperty(aCx, aTargetClassObject,
158 303 : static_cast<const char16_t*>(mName),
159 303 : name.Length(), JS::UndefinedHandleValue, mJSAttributes,
160 303 : JS_DATA_TO_FUNC_PTR(JSNative, getter.get()),
161 303 : JS_DATA_TO_FUNC_PTR(JSNative, setter.get())))
162 0 : return NS_ERROR_OUT_OF_MEMORY;
163 : }
164 303 : return NS_OK;
165 : }
166 :
167 : nsresult
168 0 : nsXBLProtoImplProperty::CompileMember(AutoJSAPI& jsapi, const nsString& aClassStr,
169 : JS::Handle<JSObject*> aClassObject)
170 : {
171 0 : AssertInCompilationScope();
172 0 : NS_PRECONDITION(!mIsCompiled,
173 : "Trying to compile an already-compiled property");
174 0 : NS_PRECONDITION(aClassObject,
175 : "Must have class object to compile");
176 0 : MOZ_ASSERT(!mGetter.IsCompiled() && !mSetter.IsCompiled());
177 0 : JSContext *cx = jsapi.cx();
178 :
179 0 : if (!mName)
180 0 : return NS_ERROR_FAILURE; // Without a valid name, we can't install the member.
181 :
182 : // We have a property.
183 0 : nsresult rv = NS_OK;
184 :
185 0 : nsAutoCString functionUri;
186 0 : if (mGetter.GetUncompiled() || mSetter.GetUncompiled()) {
187 0 : functionUri = NS_ConvertUTF16toUTF8(aClassStr);
188 0 : int32_t hash = functionUri.RFindChar('#');
189 0 : if (hash != kNotFound) {
190 0 : functionUri.Truncate(hash);
191 : }
192 : }
193 :
194 0 : bool deletedGetter = false;
195 0 : nsXBLTextWithLineNumber *getterText = mGetter.GetUncompiled();
196 0 : if (getterText && getterText->GetText()) {
197 0 : nsDependentString getter(getterText->GetText());
198 0 : if (!getter.IsEmpty()) {
199 0 : JSAutoCompartment ac(cx, aClassObject);
200 0 : JS::CompileOptions options(cx);
201 0 : options.setFileAndLine(functionUri.get(), getterText->GetLineNumber())
202 0 : .setVersion(JSVERSION_LATEST);
203 0 : nsCString name = NS_LITERAL_CSTRING("get_") + NS_ConvertUTF16toUTF8(mName);
204 0 : JS::Rooted<JSObject*> getterObject(cx);
205 0 : JS::AutoObjectVector emptyVector(cx);
206 0 : rv = nsJSUtils::CompileFunction(jsapi, emptyVector, options, name, 0,
207 0 : nullptr, getter, getterObject.address());
208 :
209 0 : delete getterText;
210 0 : deletedGetter = true;
211 :
212 0 : mGetter.SetJSFunction(getterObject);
213 :
214 0 : if (mGetter.GetJSFunction() && NS_SUCCEEDED(rv)) {
215 0 : mJSAttributes |= JSPROP_GETTER | JSPROP_SHARED;
216 : }
217 0 : if (NS_FAILED(rv)) {
218 0 : mGetter.SetJSFunction(nullptr);
219 0 : mJSAttributes &= ~JSPROP_GETTER;
220 : /*chaining to return failure*/
221 : }
222 : }
223 : } // if getter is not empty
224 :
225 0 : if (!deletedGetter) { // Empty getter
226 0 : delete getterText;
227 0 : mGetter.SetJSFunction(nullptr);
228 : }
229 :
230 0 : if (NS_FAILED(rv)) {
231 : // We failed to compile our getter. So either we've set it to null, or
232 : // it's still set to the text object. In either case, it's safe to return
233 : // the error here, since then we'll be cleaned up as uncompiled and that
234 : // will be ok. Going on and compiling the setter and _then_ returning an
235 : // error, on the other hand, will try to clean up a compiled setter as
236 : // uncompiled and crash.
237 0 : return rv;
238 : }
239 :
240 0 : bool deletedSetter = false;
241 0 : nsXBLTextWithLineNumber *setterText = mSetter.GetUncompiled();
242 0 : if (setterText && setterText->GetText()) {
243 0 : nsDependentString setter(setterText->GetText());
244 0 : if (!setter.IsEmpty()) {
245 0 : JSAutoCompartment ac(cx, aClassObject);
246 0 : JS::CompileOptions options(cx);
247 0 : options.setFileAndLine(functionUri.get(), setterText->GetLineNumber())
248 0 : .setVersion(JSVERSION_LATEST);
249 0 : nsCString name = NS_LITERAL_CSTRING("set_") + NS_ConvertUTF16toUTF8(mName);
250 0 : JS::Rooted<JSObject*> setterObject(cx);
251 0 : JS::AutoObjectVector emptyVector(cx);
252 0 : rv = nsJSUtils::CompileFunction(jsapi, emptyVector, options, name, 1,
253 : gPropertyArgs, setter,
254 0 : setterObject.address());
255 :
256 0 : delete setterText;
257 0 : deletedSetter = true;
258 0 : mSetter.SetJSFunction(setterObject);
259 :
260 0 : if (mSetter.GetJSFunction() && NS_SUCCEEDED(rv)) {
261 0 : mJSAttributes |= JSPROP_SETTER | JSPROP_SHARED;
262 : }
263 0 : if (NS_FAILED(rv)) {
264 0 : mSetter.SetJSFunction(nullptr);
265 0 : mJSAttributes &= ~JSPROP_SETTER;
266 : /*chaining to return failure*/
267 : }
268 : }
269 : } // if setter wasn't empty....
270 :
271 0 : if (!deletedSetter) { // Empty setter
272 0 : delete setterText;
273 0 : mSetter.SetJSFunction(nullptr);
274 : }
275 :
276 : #ifdef DEBUG
277 0 : mIsCompiled = NS_SUCCEEDED(rv);
278 : #endif
279 :
280 0 : return rv;
281 : }
282 :
283 : void
284 355 : nsXBLProtoImplProperty::Trace(const TraceCallbacks& aCallbacks, void *aClosure)
285 : {
286 355 : if (mJSAttributes & JSPROP_GETTER) {
287 352 : aCallbacks.Trace(&mGetter.AsHeapObject(), "mGetter", aClosure);
288 : }
289 :
290 355 : if (mJSAttributes & JSPROP_SETTER) {
291 160 : aCallbacks.Trace(&mSetter.AsHeapObject(), "mSetter", aClosure);
292 : }
293 355 : }
294 :
295 : nsresult
296 355 : nsXBLProtoImplProperty::Read(nsIObjectInputStream* aStream,
297 : XBLBindingSerializeDetails aType)
298 : {
299 355 : AssertInCompilationScope();
300 355 : MOZ_ASSERT(!mIsCompiled);
301 355 : MOZ_ASSERT(!mGetter.GetUncompiled() && !mSetter.GetUncompiled());
302 :
303 710 : AutoJSContext cx;
304 710 : JS::Rooted<JSObject*> getterObject(cx);
305 355 : if (aType == XBLBinding_Serialize_GetterProperty ||
306 : aType == XBLBinding_Serialize_GetterSetterProperty) {
307 352 : nsresult rv = XBL_DeserializeFunction(aStream, &getterObject);
308 352 : NS_ENSURE_SUCCESS(rv, rv);
309 :
310 352 : mJSAttributes |= JSPROP_GETTER | JSPROP_SHARED;
311 : }
312 355 : mGetter.SetJSFunction(getterObject);
313 :
314 710 : JS::Rooted<JSObject*> setterObject(cx);
315 355 : if (aType == XBLBinding_Serialize_SetterProperty ||
316 : aType == XBLBinding_Serialize_GetterSetterProperty) {
317 160 : nsresult rv = XBL_DeserializeFunction(aStream, &setterObject);
318 160 : NS_ENSURE_SUCCESS(rv, rv);
319 :
320 160 : mJSAttributes |= JSPROP_SETTER | JSPROP_SHARED;
321 : }
322 355 : mSetter.SetJSFunction(setterObject);
323 :
324 : #ifdef DEBUG
325 355 : mIsCompiled = true;
326 : #endif
327 :
328 355 : return NS_OK;
329 : }
330 :
331 : nsresult
332 0 : nsXBLProtoImplProperty::Write(nsIObjectOutputStream* aStream)
333 : {
334 0 : AssertInCompilationScope();
335 : XBLBindingSerializeDetails type;
336 :
337 0 : if (mJSAttributes & JSPROP_GETTER) {
338 0 : type = mJSAttributes & JSPROP_SETTER ?
339 : XBLBinding_Serialize_GetterSetterProperty :
340 0 : XBLBinding_Serialize_GetterProperty;
341 : }
342 : else {
343 0 : type = XBLBinding_Serialize_SetterProperty;
344 : }
345 :
346 0 : if (mJSAttributes & JSPROP_READONLY) {
347 0 : type |= XBLBinding_Serialize_ReadOnly;
348 : }
349 :
350 0 : nsresult rv = aStream->Write8(type);
351 0 : NS_ENSURE_SUCCESS(rv, rv);
352 0 : rv = aStream->WriteWStringZ(mName);
353 0 : NS_ENSURE_SUCCESS(rv, rv);
354 :
355 0 : MOZ_ASSERT_IF(mJSAttributes & (JSPROP_GETTER | JSPROP_SETTER), mIsCompiled);
356 :
357 0 : if (mJSAttributes & JSPROP_GETTER) {
358 0 : JS::Rooted<JSObject*> function(RootingCx(), mGetter.GetJSFunction());
359 0 : rv = XBL_SerializeFunction(aStream, function);
360 0 : NS_ENSURE_SUCCESS(rv, rv);
361 : }
362 :
363 0 : if (mJSAttributes & JSPROP_SETTER) {
364 0 : JS::Rooted<JSObject*> function(RootingCx(), mSetter.GetJSFunction());
365 0 : rv = XBL_SerializeFunction(aStream, function);
366 0 : NS_ENSURE_SUCCESS(rv, rv);
367 : }
368 :
369 0 : return NS_OK;
370 : }
|