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
;
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
)
32 MOZ_COUNT_CTOR(nsXBLProtoImplProperty
);
35 nsAutoString readOnly
; readOnly
.Assign(*aReadOnly
);
36 if (readOnly
.LowerCaseEqualsLiteral("true"))
37 mJSAttributes
|= JSPROP_READONLY
;
41 AppendGetterText(nsDependentString(aGetter
));
42 SetGetterLineNumber(aLineNumber
);
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
)
58 MOZ_COUNT_CTOR(nsXBLProtoImplProperty
);
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
);
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
);
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
);
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
);
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" };
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
));
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
);
142 JS::Rooted
<JSObject
*> getter(aCx
, mGetter
.GetJSFunction());
143 JS::Rooted
<JSObject
*> setter(aCx
, mSetter
.GetJSFunction());
144 if (getter
|| setter
) {
146 if (!(getter
= JS::CloneFunctionObject(aCx
, getter
)))
147 return NS_ERROR_OUT_OF_MEMORY
;
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
;
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();
179 return NS_ERROR_FAILURE
; // Without a valid name, we can't install the member.
181 // We have a property.
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());
209 deletedGetter
= true;
211 mGetter
.SetJSFunction(getterObject
);
213 if (mGetter
.GetJSFunction() && NS_SUCCEEDED(rv
)) {
214 mJSAttributes
|= JSPROP_GETTER
| JSPROP_SHARED
;
217 mGetter
.SetJSFunction(nullptr);
218 mJSAttributes
&= ~JSPROP_GETTER
;
219 /*chaining to return failure*/
222 } // if getter is not empty
224 if (!deletedGetter
) { // Empty getter
226 mGetter
.SetJSFunction(nullptr);
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.
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());
256 deletedSetter
= true;
257 mSetter
.SetJSFunction(setterObject
);
259 if (mSetter
.GetJSFunction() && NS_SUCCEEDED(rv
)) {
260 mJSAttributes
|= JSPROP_SETTER
| JSPROP_SHARED
;
263 mSetter
.SetJSFunction(nullptr);
264 mJSAttributes
&= ~JSPROP_SETTER
;
265 /*chaining to return failure*/
268 } // if setter wasn't empty....
270 if (!deletedSetter
) { // Empty setter
272 mSetter
.SetJSFunction(nullptr);
276 mIsCompiled
= NS_SUCCEEDED(rv
);
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
);
295 nsXBLProtoImplProperty::Read(nsIObjectInputStream
* aStream
,
296 XBLBindingSerializeDetails aType
)
298 AssertInCompilationScope();
299 MOZ_ASSERT(!mIsCompiled
);
300 MOZ_ASSERT(!mGetter
.GetUncompiled() && !mSetter
.GetUncompiled());
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
);
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
;
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
);