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/. */
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
)
31 MOZ_COUNT_CTOR(nsXBLProtoImplProperty
);
34 nsAutoString readOnly
; readOnly
.Assign(*aReadOnly
);
35 if (readOnly
.LowerCaseEqualsLiteral("true"))
36 mJSAttributes
|= JSPROP_READONLY
;
40 AppendGetterText(nsDependentString(aGetter
));
41 SetGetterLineNumber(aLineNumber
);
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
)
57 MOZ_COUNT_CTOR(nsXBLProtoImplProperty
);
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
);
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
);
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
);
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
);
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" };
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
) {
139 if (!(getter
= ::JS_CloneFunctionObject(aCx
, getter
, globalObject
)))
140 return NS_ERROR_OUT_OF_MEMORY
;
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
;
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());
171 return NS_ERROR_FAILURE
; // Without a valid name, we can't install the member.
173 // We have a property.
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()) {
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());
201 deletedGetter
= true;
203 mGetter
.SetJSFunction(getterObject
);
205 if (mGetter
.GetJSFunction() && NS_SUCCEEDED(rv
)) {
206 mJSAttributes
|= JSPROP_GETTER
| JSPROP_SHARED
;
209 mGetter
.SetJSFunction(nullptr);
210 mJSAttributes
&= ~JSPROP_GETTER
;
211 /*chaining to return failure*/
214 } // if getter is not empty
216 if (!deletedGetter
) { // Empty getter
218 mGetter
.SetJSFunction(nullptr);
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.
231 bool deletedSetter
= false;
232 nsXBLTextWithLineNumber
*setterText
= mSetter
.GetUncompiled();
233 if (setterText
&& setterText
->GetText()) {
234 nsDependentString
setter(setterText
->GetText());
235 if (!setter
.IsEmpty()) {
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());
248 deletedSetter
= true;
249 mSetter
.SetJSFunction(setterObject
);
251 if (mSetter
.GetJSFunction() && NS_SUCCEEDED(rv
)) {
252 mJSAttributes
|= JSPROP_SETTER
| JSPROP_SHARED
;
255 mSetter
.SetJSFunction(nullptr);
256 mJSAttributes
&= ~JSPROP_SETTER
;
257 /*chaining to return failure*/
260 } // if setter wasn't empty....
262 if (!deletedSetter
) { // Empty setter
264 mSetter
.SetJSFunction(nullptr);
268 mIsCompiled
= NS_SUCCEEDED(rv
);
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
);
287 nsXBLProtoImplProperty::Read(nsIObjectInputStream
* aStream
,
288 XBLBindingSerializeDetails aType
)
290 AssertInCompilationScope();
291 MOZ_ASSERT(!mIsCompiled
);
292 MOZ_ASSERT(!mGetter
.GetUncompiled() && !mSetter
.GetUncompiled());
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
);
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
;
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
);