/src/mozilla-central/dom/xbl/nsXBLProtoImplField.cpp
Line | Count | Source (jump to first uncovered line) |
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 "nsAtom.h" |
8 | | #include "nsIContent.h" |
9 | | #include "nsString.h" |
10 | | #include "nsJSUtils.h" |
11 | | #include "jsapi.h" |
12 | | #include "js/CharacterEncoding.h" |
13 | | #include "nsUnicharUtils.h" |
14 | | #include "nsReadableUtils.h" |
15 | | #include "nsXBLProtoImplField.h" |
16 | | #include "nsIScriptContext.h" |
17 | | #include "nsIURI.h" |
18 | | #include "nsXBLSerialize.h" |
19 | | #include "nsXBLPrototypeBinding.h" |
20 | | #include "mozilla/dom/BindingUtils.h" |
21 | | #include "mozilla/dom/ElementBinding.h" |
22 | | #include "mozilla/dom/ScriptSettings.h" |
23 | | #include "nsGlobalWindow.h" |
24 | | #include "xpcpublic.h" |
25 | | #include "WrapperFactory.h" |
26 | | |
27 | | using namespace mozilla; |
28 | | using namespace mozilla::dom; |
29 | | |
30 | | nsXBLProtoImplField::nsXBLProtoImplField(const char16_t* aName, const char16_t* aReadOnly) |
31 | | : mNext(nullptr), |
32 | | mFieldText(nullptr), |
33 | | mFieldTextLength(0), |
34 | | mLineNumber(0) |
35 | 0 | { |
36 | 0 | MOZ_COUNT_CTOR(nsXBLProtoImplField); |
37 | 0 | mName = NS_xstrdup(aName); // XXXbz make more sense to use a stringbuffer? |
38 | 0 |
|
39 | 0 | mJSAttributes = JSPROP_ENUMERATE; |
40 | 0 | if (aReadOnly) { |
41 | 0 | nsAutoString readOnly; readOnly.Assign(aReadOnly); |
42 | 0 | if (readOnly.LowerCaseEqualsLiteral("true")) |
43 | 0 | mJSAttributes |= JSPROP_READONLY; |
44 | 0 | } |
45 | 0 | } |
46 | | |
47 | | |
48 | | nsXBLProtoImplField::nsXBLProtoImplField(const bool aIsReadOnly) |
49 | | : mNext(nullptr), |
50 | | mName(nullptr), |
51 | | mFieldText(nullptr), |
52 | | mFieldTextLength(0), |
53 | | mLineNumber(0) |
54 | 0 | { |
55 | 0 | MOZ_COUNT_CTOR(nsXBLProtoImplField); |
56 | 0 |
|
57 | 0 | mJSAttributes = JSPROP_ENUMERATE; |
58 | 0 | if (aIsReadOnly) |
59 | 0 | mJSAttributes |= JSPROP_READONLY; |
60 | 0 | } |
61 | | |
62 | | nsXBLProtoImplField::~nsXBLProtoImplField() |
63 | 0 | { |
64 | 0 | MOZ_COUNT_DTOR(nsXBLProtoImplField); |
65 | 0 | if (mFieldText) |
66 | 0 | free(mFieldText); |
67 | 0 | free(mName); |
68 | 0 | NS_CONTENT_DELETE_LIST_MEMBER(nsXBLProtoImplField, this, mNext); |
69 | 0 | } |
70 | | |
71 | | void |
72 | | nsXBLProtoImplField::AppendFieldText(const nsAString& aText) |
73 | 0 | { |
74 | 0 | if (mFieldText) { |
75 | 0 | nsDependentString fieldTextStr(mFieldText, mFieldTextLength); |
76 | 0 | nsAutoString newFieldText = fieldTextStr + aText; |
77 | 0 | char16_t* temp = mFieldText; |
78 | 0 | mFieldText = ToNewUnicode(newFieldText); |
79 | 0 | mFieldTextLength = newFieldText.Length(); |
80 | 0 | free(temp); |
81 | 0 | } |
82 | 0 | else { |
83 | 0 | mFieldText = ToNewUnicode(aText); |
84 | 0 | mFieldTextLength = aText.Length(); |
85 | 0 | } |
86 | 0 | } |
87 | | |
88 | | // XBL fields are represented on elements inheriting that field a bit trickily. |
89 | | // When setting up the XBL prototype object, we install accessors for the fields |
90 | | // on the prototype object. Those accessors, when used, will then (via |
91 | | // InstallXBLField below) reify a property for the field onto the actual XBL-backed |
92 | | // element. |
93 | | // |
94 | | // The accessor property is a plain old property backed by a getter function and |
95 | | // a setter function. These properties are backed by the FieldGetter and |
96 | | // FieldSetter natives; they're created by InstallAccessors. The precise field to be |
97 | | // reified is identified using two extra slots on the getter/setter functions. |
98 | | // XBLPROTO_SLOT stores the XBL prototype object that provides the field. |
99 | | // FIELD_SLOT stores the name of the field, i.e. its JavaScript property name. |
100 | | // |
101 | | // This two-step field installation process -- creating an accessor on the |
102 | | // prototype, then have that reify an own property on the actual element -- is |
103 | | // admittedly convoluted. Better would be for XBL-backed elements to be proxies |
104 | | // that could resolve fields onto themselves. But given that XBL bindings are |
105 | | // associated with elements mutably -- you can add/remove/change -moz-binding |
106 | | // whenever you want, alas -- doing so would require all elements to be proxies, |
107 | | // which isn't performant now. So we do this two-step instead. |
108 | | static const uint32_t XBLPROTO_SLOT = 0; |
109 | | static const uint32_t FIELD_SLOT = 1; |
110 | | |
111 | | bool |
112 | | ValueHasISupportsPrivate(JS::Handle<JS::Value> v) |
113 | 0 | { |
114 | 0 | if (!v.isObject()) { |
115 | 0 | return false; |
116 | 0 | } |
117 | 0 | |
118 | 0 | const DOMJSClass* domClass = GetDOMClass(&v.toObject()); |
119 | 0 | if (domClass) { |
120 | 0 | return domClass->mDOMObjectIsISupports; |
121 | 0 | } |
122 | 0 | |
123 | 0 | const JSClass* clasp = ::JS_GetClass(&v.toObject()); |
124 | 0 | const uint32_t HAS_PRIVATE_NSISUPPORTS = |
125 | 0 | JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS; |
126 | 0 | return (clasp->flags & HAS_PRIVATE_NSISUPPORTS) == HAS_PRIVATE_NSISUPPORTS; |
127 | 0 | } |
128 | | |
129 | | #ifdef DEBUG |
130 | | static bool |
131 | | ValueHasISupportsPrivate(JSContext* cx, const JS::Value& aVal) |
132 | | { |
133 | | JS::Rooted<JS::Value> v(cx, aVal); |
134 | | return ValueHasISupportsPrivate(v); |
135 | | } |
136 | | #endif |
137 | | |
138 | | // Define a shadowing property on |this| for the XBL field defined by the |
139 | | // contents of the callee's reserved slots. If the property was defined, |
140 | | // *installed will be true, and idp will be set to the property name that was |
141 | | // defined. |
142 | | static bool |
143 | | InstallXBLField(JSContext* cx, |
144 | | JS::Handle<JSObject*> callee, JS::Handle<JSObject*> thisObj, |
145 | | JS::MutableHandle<jsid> idp, bool* installed) |
146 | 0 | { |
147 | 0 | *installed = false; |
148 | 0 |
|
149 | 0 | // First ensure |this| is a reasonable XBL bound node. |
150 | 0 | // |
151 | 0 | // FieldAccessorGuard already determined whether |thisObj| was acceptable as |
152 | 0 | // |this| in terms of not throwing a TypeError. Assert this for good measure. |
153 | 0 | MOZ_ASSERT(ValueHasISupportsPrivate(cx, JS::ObjectValue(*thisObj))); |
154 | 0 |
|
155 | 0 | // But there are some cases where we must accept |thisObj| but not install a |
156 | 0 | // property on it, or otherwise touch it. Hence this split of |this|-vetting |
157 | 0 | // duties. |
158 | 0 | nsCOMPtr<nsISupports> native = xpc::UnwrapReflectorToISupports(thisObj); |
159 | 0 | if (!native) { |
160 | 0 | // Looks like whatever |thisObj| is it's not our nsIContent. It might well |
161 | 0 | // be the proto our binding installed, however, where the private is the |
162 | 0 | // nsXBLDocumentInfo, so just baul out quietly. Do NOT throw an exception |
163 | 0 | // here. |
164 | 0 | // |
165 | 0 | // We could make this stricter by checking the class maybe, but whatever. |
166 | 0 | return true; |
167 | 0 | } |
168 | 0 | |
169 | 0 | nsCOMPtr<nsIContent> xblNode = do_QueryInterface(native); |
170 | 0 | if (!xblNode) { |
171 | 0 | xpc::Throw(cx, NS_ERROR_UNEXPECTED); |
172 | 0 | return false; |
173 | 0 | } |
174 | 0 | |
175 | 0 | // Now that |this| is okay, actually install the field. |
176 | 0 | |
177 | 0 | // Because of the possibility (due to XBL binding inheritance, because each |
178 | 0 | // XBL binding lives in its own global object) that |this| might be in a |
179 | 0 | // different realm from the callee (not to mention that this method can |
180 | 0 | // be called with an arbitrary |this| regardless of how insane XBL is), and |
181 | 0 | // because in this method we've entered |this|'s realm (see in |
182 | 0 | // Field[GS]etter where we attempt a cross-realm call), we must enter |
183 | 0 | // the callee's realm to access its reserved slots. |
184 | 0 | nsXBLPrototypeBinding* protoBinding; |
185 | 0 | nsAutoJSString fieldName; |
186 | 0 | { |
187 | 0 | JSAutoRealm ar(cx, callee); |
188 | 0 |
|
189 | 0 | JS::Rooted<JSObject*> xblProto(cx); |
190 | 0 | xblProto = &js::GetFunctionNativeReserved(callee, XBLPROTO_SLOT).toObject(); |
191 | 0 |
|
192 | 0 | JS::Rooted<JS::Value> name(cx, js::GetFunctionNativeReserved(callee, FIELD_SLOT)); |
193 | 0 | if (!fieldName.init(cx, name.toString())) { |
194 | 0 | return false; |
195 | 0 | } |
196 | 0 | |
197 | 0 | MOZ_ALWAYS_TRUE(JS_ValueToId(cx, name, idp)); |
198 | 0 |
|
199 | 0 | // If a separate XBL scope is being used, the callee is not same-realm |
200 | 0 | // with the xbl prototype, and the object is a cross-compartment wrapper. |
201 | 0 | xblProto = js::UncheckedUnwrap(xblProto); |
202 | 0 | JSAutoRealm ar2(cx, xblProto); |
203 | 0 | JS::Value slotVal = ::JS_GetReservedSlot(xblProto, 0); |
204 | 0 | protoBinding = static_cast<nsXBLPrototypeBinding*>(slotVal.toPrivate()); |
205 | 0 | MOZ_ASSERT(protoBinding); |
206 | 0 | } |
207 | 0 |
|
208 | 0 | nsXBLProtoImplField* field = protoBinding->FindField(fieldName); |
209 | 0 | MOZ_ASSERT(field); |
210 | 0 |
|
211 | 0 | nsresult rv = field->InstallField(thisObj, *protoBinding, installed); |
212 | 0 | if (NS_SUCCEEDED(rv)) { |
213 | 0 | return true; |
214 | 0 | } |
215 | 0 | |
216 | 0 | if (!::JS_IsExceptionPending(cx)) { |
217 | 0 | xpc::Throw(cx, rv); |
218 | 0 | } |
219 | 0 | return false; |
220 | 0 | } |
221 | | |
222 | | bool |
223 | | FieldGetterImpl(JSContext *cx, const JS::CallArgs& args) |
224 | 0 | { |
225 | 0 | JS::Handle<JS::Value> thisv = args.thisv(); |
226 | 0 | MOZ_ASSERT(ValueHasISupportsPrivate(thisv)); |
227 | 0 |
|
228 | 0 | JS::Rooted<JSObject*> thisObj(cx, &thisv.toObject()); |
229 | 0 |
|
230 | 0 | // We should be in the realm of |this|. If we got here via nativeCall, |
231 | 0 | // |this| is not same-compartment with |callee|, and it's possible via |
232 | 0 | // asymmetric security semantics that |args.calleev()| is actually a security |
233 | 0 | // wrapper. In this case, we know we want to do an unsafe unwrap, and |
234 | 0 | // InstallXBLField knows how to handle cross-compartment pointers. |
235 | 0 | bool installed = false; |
236 | 0 | JS::Rooted<JSObject*> callee(cx, js::UncheckedUnwrap(&args.calleev().toObject())); |
237 | 0 | JS::Rooted<jsid> id(cx); |
238 | 0 | if (!InstallXBLField(cx, callee, thisObj, &id, &installed)) { |
239 | 0 | return false; |
240 | 0 | } |
241 | 0 | |
242 | 0 | if (!installed) { |
243 | 0 | args.rval().setUndefined(); |
244 | 0 | return true; |
245 | 0 | } |
246 | 0 | |
247 | 0 | return JS_GetPropertyById(cx, thisObj, id, args.rval()); |
248 | 0 | } |
249 | | |
250 | | static bool |
251 | | FieldGetter(JSContext *cx, unsigned argc, JS::Value *vp) |
252 | 0 | { |
253 | 0 | JS::CallArgs args = JS::CallArgsFromVp(argc, vp); |
254 | 0 | return JS::CallNonGenericMethod<ValueHasISupportsPrivate, FieldGetterImpl> |
255 | 0 | (cx, args); |
256 | 0 | } |
257 | | |
258 | | bool |
259 | | FieldSetterImpl(JSContext *cx, const JS::CallArgs& args) |
260 | 0 | { |
261 | 0 | JS::Handle<JS::Value> thisv = args.thisv(); |
262 | 0 | MOZ_ASSERT(ValueHasISupportsPrivate(thisv)); |
263 | 0 |
|
264 | 0 | JS::Rooted<JSObject*> thisObj(cx, &thisv.toObject()); |
265 | 0 |
|
266 | 0 | // We should be in the realm of |this|. If we got here via nativeCall, |
267 | 0 | // |this| is not same-compartment with |callee|, and it's possible via |
268 | 0 | // asymmetric security semantics that |args.calleev()| is actually a security |
269 | 0 | // wrapper. In this case, we know we want to do an unsafe unwrap, and |
270 | 0 | // InstallXBLField knows how to handle cross-compartment pointers. |
271 | 0 | bool installed = false; |
272 | 0 | JS::Rooted<JSObject*> callee(cx, js::UncheckedUnwrap(&args.calleev().toObject())); |
273 | 0 | JS::Rooted<jsid> id(cx); |
274 | 0 | if (!InstallXBLField(cx, callee, thisObj, &id, &installed)) { |
275 | 0 | return false; |
276 | 0 | } |
277 | 0 | |
278 | 0 | if (installed) { |
279 | 0 | if (!::JS_SetPropertyById(cx, thisObj, id, args.get(0))) { |
280 | 0 | return false; |
281 | 0 | } |
282 | 0 | } |
283 | 0 | args.rval().setUndefined(); |
284 | 0 | return true; |
285 | 0 | } |
286 | | |
287 | | static bool |
288 | | FieldSetter(JSContext *cx, unsigned argc, JS::Value *vp) |
289 | 0 | { |
290 | 0 | JS::CallArgs args = JS::CallArgsFromVp(argc, vp); |
291 | 0 | return JS::CallNonGenericMethod<ValueHasISupportsPrivate, FieldSetterImpl> |
292 | 0 | (cx, args); |
293 | 0 | } |
294 | | |
295 | | nsresult |
296 | | nsXBLProtoImplField::InstallAccessors(JSContext* aCx, |
297 | | JS::Handle<JSObject*> aTargetClassObject) |
298 | 0 | { |
299 | 0 | MOZ_ASSERT(js::IsObjectInContextCompartment(aTargetClassObject, aCx)); |
300 | 0 | JS::Rooted<JSObject*> globalObject(aCx, JS::GetNonCCWObjectGlobal(aTargetClassObject)); |
301 | 0 | JS::Rooted<JSObject*> scopeObject(aCx, xpc::GetXBLScopeOrGlobal(aCx, globalObject)); |
302 | 0 | NS_ENSURE_TRUE(scopeObject, NS_ERROR_OUT_OF_MEMORY); |
303 | 0 |
|
304 | 0 | // Don't install it if the field is empty; see also InstallField which also must |
305 | 0 | // implement the not-empty requirement. |
306 | 0 | if (IsEmpty()) { |
307 | 0 | return NS_OK; |
308 | 0 | } |
309 | 0 | |
310 | 0 | // Install a getter/setter pair which will resolve the field onto the actual |
311 | 0 | // object, when invoked. |
312 | 0 | |
313 | 0 | // Get the field name as an id. |
314 | 0 | JS::Rooted<jsid> id(aCx); |
315 | 0 | JS::TwoByteChars chars(mName, NS_strlen(mName)); |
316 | 0 | if (!JS_CharsToId(aCx, chars, &id)) |
317 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
318 | 0 | |
319 | 0 | // Properties/Methods have historically taken precendence over fields. We |
320 | 0 | // install members first, so just bounce here if the property is already |
321 | 0 | // defined. |
322 | 0 | bool found = false; |
323 | 0 | if (!JS_AlreadyHasOwnPropertyById(aCx, aTargetClassObject, id, &found)) |
324 | 0 | return NS_ERROR_FAILURE; |
325 | 0 | if (found) |
326 | 0 | return NS_OK; |
327 | 0 | |
328 | 0 | // FieldGetter and FieldSetter need to run in the XBL scope so that they can |
329 | 0 | // see through any SOWs on their targets. |
330 | 0 | |
331 | 0 | // First, enter the XBL scope, and compile the functions there. |
332 | 0 | JSAutoRealm ar(aCx, scopeObject); |
333 | 0 | JS::Rooted<JS::Value> wrappedClassObj(aCx, JS::ObjectValue(*aTargetClassObject)); |
334 | 0 | if (!JS_WrapValue(aCx, &wrappedClassObj)) |
335 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
336 | 0 | |
337 | 0 | JS::Rooted<JSObject*> get(aCx, |
338 | 0 | JS_GetFunctionObject(js::NewFunctionByIdWithReserved(aCx, FieldGetter, |
339 | 0 | 0, 0, id))); |
340 | 0 | if (!get) { |
341 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
342 | 0 | } |
343 | 0 | js::SetFunctionNativeReserved(get, XBLPROTO_SLOT, wrappedClassObj); |
344 | 0 | js::SetFunctionNativeReserved(get, FIELD_SLOT, |
345 | 0 | JS::StringValue(JSID_TO_STRING(id))); |
346 | 0 |
|
347 | 0 | JS::Rooted<JSObject*> set(aCx, |
348 | 0 | JS_GetFunctionObject(js::NewFunctionByIdWithReserved(aCx, FieldSetter, |
349 | 0 | 1, 0, id))); |
350 | 0 | if (!set) { |
351 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
352 | 0 | } |
353 | 0 | js::SetFunctionNativeReserved(set, XBLPROTO_SLOT, wrappedClassObj); |
354 | 0 | js::SetFunctionNativeReserved(set, FIELD_SLOT, |
355 | 0 | JS::StringValue(JSID_TO_STRING(id))); |
356 | 0 |
|
357 | 0 | // Now, re-enter the class object's scope, wrap the getters/setters, and define |
358 | 0 | // them there. |
359 | 0 | JSAutoRealm ar2(aCx, aTargetClassObject); |
360 | 0 | if (!JS_WrapObject(aCx, &get) || !JS_WrapObject(aCx, &set)) { |
361 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
362 | 0 | } |
363 | 0 | |
364 | 0 | if (!::JS_DefinePropertyById(aCx, aTargetClassObject, id, get, set, |
365 | 0 | AccessorAttributes())) { |
366 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
367 | 0 | } |
368 | 0 | |
369 | 0 | return NS_OK; |
370 | 0 | } |
371 | | |
372 | | nsresult |
373 | | nsXBLProtoImplField::InstallField(JS::Handle<JSObject*> aBoundNode, |
374 | | const nsXBLPrototypeBinding& aProtoBinding, |
375 | | bool* aDidInstall) const |
376 | 0 | { |
377 | 0 | MOZ_ASSERT(aBoundNode, |
378 | 0 | "uh-oh, bound node should NOT be null or bad things will happen"); |
379 | 0 |
|
380 | 0 | *aDidInstall = false; |
381 | 0 |
|
382 | 0 | // Empty fields are treated as not actually present. |
383 | 0 | if (IsEmpty()) { |
384 | 0 | return NS_OK; |
385 | 0 | } |
386 | 0 | |
387 | 0 | nsAutoMicroTask mt; |
388 | 0 |
|
389 | 0 | nsAutoCString uriSpec; |
390 | 0 | nsresult rv = aProtoBinding.DocURI()->GetSpec(uriSpec); |
391 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
392 | 0 | return rv; |
393 | 0 | } |
394 | 0 | |
395 | 0 | nsIGlobalObject* globalObject = xpc::WindowGlobalOrNull(aBoundNode); |
396 | 0 | if (!globalObject) { |
397 | 0 | return NS_OK; |
398 | 0 | } |
399 | 0 | |
400 | 0 | // We are going to run script via EvaluateString, so we need a script entry |
401 | 0 | // point, but as this is XBL related it does not appear in the HTML spec. |
402 | 0 | // We need an actual JSContext to do GetXBLScopeOrGlobal, and it needs to |
403 | 0 | // be in the realm of globalObject. But we want our XBL execution scope |
404 | 0 | // to be our entry global. |
405 | 0 | AutoJSAPI jsapi; |
406 | 0 | if (!jsapi.Init(globalObject)) { |
407 | 0 | return NS_ERROR_UNEXPECTED; |
408 | 0 | } |
409 | 0 | MOZ_ASSERT(!::JS_IsExceptionPending(jsapi.cx()), |
410 | 0 | "Shouldn't get here when an exception is pending!"); |
411 | 0 |
|
412 | 0 | // Note: the UNWRAP_OBJECT may mutate boundNode; don't use it after that call. |
413 | 0 | JS::Rooted<JSObject*> boundNode(jsapi.cx(), aBoundNode); |
414 | 0 | Element* boundElement = nullptr; |
415 | 0 | rv = UNWRAP_OBJECT(Element, &boundNode, boundElement); |
416 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
417 | 0 | return rv; |
418 | 0 | } |
419 | 0 | |
420 | 0 | // First, enter the xbl scope, build the element's scope chain, and use |
421 | 0 | // that as the scope chain for the evaluation. |
422 | 0 | JS::Rooted<JSObject*> scopeObject(jsapi.cx(), |
423 | 0 | xpc::GetXBLScopeOrGlobal(jsapi.cx(), aBoundNode)); |
424 | 0 | NS_ENSURE_TRUE(scopeObject, NS_ERROR_OUT_OF_MEMORY); |
425 | 0 |
|
426 | 0 | AutoEntryScript aes(scopeObject, "XBL <field> initialization", true); |
427 | 0 | JSContext* cx = aes.cx(); |
428 | 0 |
|
429 | 0 | JS::Rooted<JS::Value> result(cx); |
430 | 0 | JS::CompileOptions options(cx); |
431 | 0 | options.setFileAndLine(uriSpec.get(), mLineNumber); |
432 | 0 | JS::AutoObjectVector scopeChain(cx); |
433 | 0 | if (!nsJSUtils::GetScopeChainForXBL(cx, boundElement, aProtoBinding, scopeChain)) { |
434 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
435 | 0 | } |
436 | 0 | rv = NS_OK; |
437 | 0 | { |
438 | 0 | nsJSUtils::ExecutionContext exec(cx, scopeObject); |
439 | 0 | exec.SetScopeChain(scopeChain); |
440 | 0 | exec.CompileAndExec(options, nsDependentString(mFieldText, |
441 | 0 | mFieldTextLength)); |
442 | 0 | rv = exec.ExtractReturnValue(&result); |
443 | 0 | } |
444 | 0 |
|
445 | 0 | if (NS_FAILED(rv)) { |
446 | 0 | return rv; |
447 | 0 | } |
448 | 0 | |
449 | 0 | if (rv == NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW) { |
450 | 0 | // Report the exception now, before we try using the JSContext for |
451 | 0 | // the JS_DefineUCProperty call. Note that this reports in our current |
452 | 0 | // realm, which is the XBL scope. |
453 | 0 | aes.ReportException(); |
454 | 0 | } |
455 | 0 |
|
456 | 0 | // Now, enter the node's realm, wrap the eval result, and define it on |
457 | 0 | // the bound node. |
458 | 0 | JSAutoRealm ar2(cx, aBoundNode); |
459 | 0 | nsDependentString name(mName); |
460 | 0 | if (!JS_WrapValue(cx, &result) || |
461 | 0 | !::JS_DefineUCProperty(cx, aBoundNode, |
462 | 0 | reinterpret_cast<const char16_t*>(mName), |
463 | 0 | name.Length(), result, mJSAttributes)) { |
464 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
465 | 0 | } |
466 | 0 | |
467 | 0 | *aDidInstall = true; |
468 | 0 | return NS_OK; |
469 | 0 | } |
470 | | |
471 | | nsresult |
472 | | nsXBLProtoImplField::Read(nsIObjectInputStream* aStream) |
473 | 0 | { |
474 | 0 | nsAutoString name; |
475 | 0 | nsresult rv = aStream->ReadString(name); |
476 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
477 | 0 | mName = ToNewUnicode(name); |
478 | 0 |
|
479 | 0 | rv = aStream->Read32(&mLineNumber); |
480 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
481 | 0 |
|
482 | 0 | nsAutoString fieldText; |
483 | 0 | rv = aStream->ReadString(fieldText); |
484 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
485 | 0 | mFieldTextLength = fieldText.Length(); |
486 | 0 | if (mFieldTextLength) |
487 | 0 | mFieldText = ToNewUnicode(fieldText); |
488 | 0 |
|
489 | 0 | return NS_OK; |
490 | 0 | } |
491 | | |
492 | | nsresult |
493 | | nsXBLProtoImplField::Write(nsIObjectOutputStream* aStream) |
494 | 0 | { |
495 | 0 | XBLBindingSerializeDetails type = XBLBinding_Serialize_Field; |
496 | 0 |
|
497 | 0 | if (mJSAttributes & JSPROP_READONLY) { |
498 | 0 | type |= XBLBinding_Serialize_ReadOnly; |
499 | 0 | } |
500 | 0 |
|
501 | 0 | nsresult rv = aStream->Write8(type); |
502 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
503 | 0 | rv = aStream->WriteWStringZ(mName); |
504 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
505 | 0 | rv = aStream->Write32(mLineNumber); |
506 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
507 | 0 |
|
508 | 0 | return aStream->WriteWStringZ(mFieldText ? mFieldText : u""); |
509 | 0 | } |