/src/mozilla-central/dom/xbl/nsXBLProtoImpl.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 "mozilla/DebugOnly.h" |
8 | | |
9 | | #include "nsXBLProtoImpl.h" |
10 | | #include "nsIContent.h" |
11 | | #include "nsIDocument.h" |
12 | | #include "nsContentUtils.h" |
13 | | #include "nsIXPConnect.h" |
14 | | #include "nsIServiceManager.h" |
15 | | #include "nsXBLPrototypeBinding.h" |
16 | | #include "nsXBLProtoImplProperty.h" |
17 | | #include "nsIURI.h" |
18 | | #include "mozilla/dom/ScriptSettings.h" |
19 | | #include "mozilla/dom/XULElementBinding.h" |
20 | | #include "xpcpublic.h" |
21 | | #include "js/CharacterEncoding.h" |
22 | | |
23 | | using namespace mozilla; |
24 | | using namespace mozilla::dom; |
25 | | using js::AssertSameCompartment; |
26 | | |
27 | | nsresult |
28 | | nsXBLProtoImpl::InstallImplementation(nsXBLPrototypeBinding* aPrototypeBinding, |
29 | | nsXBLBinding* aBinding) |
30 | 0 | { |
31 | 0 | // This function is called to install a concrete implementation on a bound element using |
32 | 0 | // this prototype implementation as a guide. The prototype implementation is compiled lazily, |
33 | 0 | // so for the first bound element that needs a concrete implementation, we also build the |
34 | 0 | // prototype implementation. |
35 | 0 | if (!mMembers && !mFields) // Constructor and destructor also live in mMembers |
36 | 0 | return NS_OK; // Nothing to do, so let's not waste time. |
37 | 0 | |
38 | 0 | // If the way this gets the script context changes, fix |
39 | 0 | // nsXBLProtoImplAnonymousMethod::Execute |
40 | 0 | nsIDocument* document = aBinding->GetBoundElement()->OwnerDoc(); |
41 | 0 |
|
42 | 0 | // This sometimes gets called when we have no outer window and if we don't |
43 | 0 | // catch this, we get leaks during crashtests and reftests. |
44 | 0 | if (NS_WARN_IF(!document->GetWindow())) { |
45 | 0 | return NS_OK; |
46 | 0 | } |
47 | 0 | |
48 | 0 | // |propertyHolder| (below) can be an existing object, so in theory we might |
49 | 0 | // hit something that could end up running script. We never want that to |
50 | 0 | // happen here, so we use an AutoJSAPI instead of an AutoEntryScript. |
51 | 0 | dom::AutoJSAPI jsapi; |
52 | 0 | if (NS_WARN_IF(!jsapi.Init(document->GetScopeObject()))) { |
53 | 0 | return NS_OK; |
54 | 0 | } |
55 | 0 | JSContext* cx = jsapi.cx(); |
56 | 0 |
|
57 | 0 | // InitTarget objects gives us back the JS object that represents the bound element and the |
58 | 0 | // class object in the bound document that represents the concrete version of this implementation. |
59 | 0 | // This function also has the side effect of building up the prototype implementation if it has |
60 | 0 | // not been built already. |
61 | 0 | JS::Rooted<JSObject*> targetClassObject(cx, nullptr); |
62 | 0 | bool targetObjectIsNew = false; |
63 | 0 | nsresult rv = InitTargetObjects(aPrototypeBinding, |
64 | 0 | aBinding->GetBoundElement(), |
65 | 0 | &targetClassObject, |
66 | 0 | &targetObjectIsNew); |
67 | 0 | NS_ENSURE_SUCCESS(rv, rv); // kick out if we were unable to properly intialize our target objects |
68 | 0 | MOZ_ASSERT(targetClassObject); |
69 | 0 |
|
70 | 0 | // If the prototype already existed, we don't need to install anything. return early. |
71 | 0 | if (!targetObjectIsNew) |
72 | 0 | return NS_OK; |
73 | 0 | |
74 | 0 | // We want to define the canonical set of members in a safe place. If we're |
75 | 0 | // using a separate XBL scope, we want to define them there first (so that |
76 | 0 | // they'll be available for Xray lookups, among other things), and then copy |
77 | 0 | // the properties to the content-side prototype as needed. We don't need to |
78 | 0 | // bother about the field accessors here, since we don't use/support those |
79 | 0 | // for in-content bindings. |
80 | 0 | |
81 | 0 | // First, start by entering the realm of the XBL scope. This may or may |
82 | 0 | // not be the same realm as globalObject. |
83 | 0 | JS::Rooted<JSObject*> globalObject(cx, |
84 | 0 | JS::GetNonCCWObjectGlobal(targetClassObject)); |
85 | 0 | JS::Rooted<JSObject*> scopeObject(cx, xpc::GetXBLScopeOrGlobal(cx, globalObject)); |
86 | 0 | NS_ENSURE_TRUE(scopeObject, NS_ERROR_OUT_OF_MEMORY); |
87 | 0 | MOZ_ASSERT(JS_IsGlobalObject(scopeObject)); |
88 | 0 | JSAutoRealm ar(cx, scopeObject); |
89 | 0 |
|
90 | 0 | // Determine the appropriate property holder. |
91 | 0 | // |
92 | 0 | // Note: If |targetIsNew| is false, we'll early-return above. However, that only |
93 | 0 | // tells us if the content-side object is new, which may be the case even if |
94 | 0 | // we've already set up the binding on the XBL side. For example, if we apply |
95 | 0 | // a binding #foo to a <span> when we've already applied it to a <div>, we'll |
96 | 0 | // end up with a different content prototype, but we'll already have a property |
97 | 0 | // holder called |foo| in the XBL scope. Check for that to avoid wasteful and |
98 | 0 | // weird property holder duplication. |
99 | 0 | const char16_t* className = aPrototypeBinding->ClassName().get(); |
100 | 0 | JS::Rooted<JSObject*> propertyHolder(cx); |
101 | 0 | JS::Rooted<JS::PropertyDescriptor> existingHolder(cx); |
102 | 0 | if (scopeObject != globalObject && |
103 | 0 | !JS_GetOwnUCPropertyDescriptor(cx, scopeObject, className, &existingHolder)) { |
104 | 0 | return NS_ERROR_FAILURE; |
105 | 0 | } |
106 | 0 | bool propertyHolderIsNew = !existingHolder.object() || !existingHolder.value().isObject(); |
107 | 0 |
|
108 | 0 | if (!propertyHolderIsNew) { |
109 | 0 | propertyHolder = &existingHolder.value().toObject(); |
110 | 0 | } else if (scopeObject != globalObject) { |
111 | 0 |
|
112 | 0 | // This is just a property holder, so it doesn't need any special JSClass. |
113 | 0 | propertyHolder = JS_NewObjectWithGivenProto(cx, nullptr, nullptr); |
114 | 0 | NS_ENSURE_TRUE(propertyHolder, NS_ERROR_OUT_OF_MEMORY); |
115 | 0 |
|
116 | 0 | // Define it as a property on the scopeObject, using the same name used on |
117 | 0 | // the content side. |
118 | 0 | bool ok = JS_DefineUCProperty(cx, scopeObject, className, -1, propertyHolder, |
119 | 0 | JSPROP_PERMANENT | JSPROP_READONLY); |
120 | 0 | NS_ENSURE_TRUE(ok, NS_ERROR_UNEXPECTED); |
121 | 0 | } else { |
122 | 0 | propertyHolder = targetClassObject; |
123 | 0 | } |
124 | 0 |
|
125 | 0 | // Walk our member list and install each one in turn on the XBL scope object. |
126 | 0 | if (propertyHolderIsNew) { |
127 | 0 | for (nsXBLProtoImplMember* curr = mMembers; |
128 | 0 | curr; |
129 | 0 | curr = curr->GetNext()) |
130 | 0 | curr->InstallMember(cx, propertyHolder); |
131 | 0 | } |
132 | 0 |
|
133 | 0 | // Now, if we're using a separate XBL scope, enter the compartment of the |
134 | 0 | // bound node and copy exposable properties to the prototype there. This |
135 | 0 | // rewraps them appropriately, which should result in cross-compartment |
136 | 0 | // function wrappers. |
137 | 0 | if (propertyHolder != targetClassObject) { |
138 | 0 | AssertSameCompartment(propertyHolder, scopeObject); |
139 | 0 | AssertSameCompartment(targetClassObject, globalObject); |
140 | 0 | bool inContentXBLScope = xpc::IsInContentXBLScope(scopeObject); |
141 | 0 | for (nsXBLProtoImplMember* curr = mMembers; curr; curr = curr->GetNext()) { |
142 | 0 | if (!inContentXBLScope || curr->ShouldExposeToUntrustedContent()) { |
143 | 0 | JS::Rooted<jsid> id(cx); |
144 | 0 | JS::TwoByteChars chars(curr->GetName(), NS_strlen(curr->GetName())); |
145 | 0 | bool ok = JS_CharsToId(cx, chars, &id); |
146 | 0 | NS_ENSURE_TRUE(ok, NS_ERROR_UNEXPECTED); |
147 | 0 |
|
148 | 0 | bool found; |
149 | 0 | ok = JS_HasPropertyById(cx, propertyHolder, id, &found); |
150 | 0 | NS_ENSURE_TRUE(ok, NS_ERROR_UNEXPECTED); |
151 | 0 | if (!found) { |
152 | 0 | // Some members don't install anything in InstallMember (e.g., |
153 | 0 | // nsXBLProtoImplAnonymousMethod). We need to skip copying in |
154 | 0 | // those cases. |
155 | 0 | continue; |
156 | 0 | } |
157 | 0 | |
158 | 0 | ok = JS_CopyPropertyFrom(cx, id, targetClassObject, propertyHolder); |
159 | 0 | NS_ENSURE_TRUE(ok, NS_ERROR_UNEXPECTED); |
160 | 0 | } |
161 | 0 | } |
162 | 0 | } |
163 | 0 |
|
164 | 0 | // From here on out, work in the scope of the bound element. |
165 | 0 | JSAutoRealm ar2(cx, targetClassObject); |
166 | 0 |
|
167 | 0 | // Install all of our field accessors. |
168 | 0 | for (nsXBLProtoImplField* curr = mFields; |
169 | 0 | curr; |
170 | 0 | curr = curr->GetNext()) |
171 | 0 | curr->InstallAccessors(cx, targetClassObject); |
172 | 0 |
|
173 | 0 | return NS_OK; |
174 | 0 | } |
175 | | |
176 | | nsresult |
177 | | nsXBLProtoImpl::InitTargetObjects(nsXBLPrototypeBinding* aBinding, |
178 | | nsIContent* aBoundElement, |
179 | | JS::MutableHandle<JSObject*> aTargetClassObject, |
180 | | bool* aTargetIsNew) |
181 | 0 | { |
182 | 0 | nsresult rv = NS_OK; |
183 | 0 |
|
184 | 0 | if (!mPrecompiledMemberHolder) { |
185 | 0 | rv = CompilePrototypeMembers(aBinding); // This is the first time we've ever installed this binding on an element. |
186 | 0 | // We need to go ahead and compile all methods and properties on a class |
187 | 0 | // in our prototype binding. |
188 | 0 | if (NS_FAILED(rv)) |
189 | 0 | return rv; |
190 | 0 | |
191 | 0 | MOZ_ASSERT(mPrecompiledMemberHolder); |
192 | 0 | } |
193 | 0 |
|
194 | 0 | nsIDocument *ownerDoc = aBoundElement->OwnerDoc(); |
195 | 0 | nsIGlobalObject *sgo; |
196 | 0 |
|
197 | 0 | if (!(sgo = ownerDoc->GetScopeObject())) { |
198 | 0 | return NS_ERROR_UNEXPECTED; |
199 | 0 | } |
200 | 0 | |
201 | 0 | // Because our prototype implementation has a class, we need to build up a corresponding |
202 | 0 | // class for the concrete implementation in the bound document. |
203 | 0 | AutoJSContext cx; |
204 | 0 | JS::Rooted<JSObject*> global(cx, sgo->GetGlobalJSObject()); |
205 | 0 | JS::Rooted<JS::Value> v(cx); |
206 | 0 |
|
207 | 0 | JSAutoRealm ar(cx, global); |
208 | 0 | // Make sure the interface object is created before the prototype object |
209 | 0 | // so that XULElement is hidden from content. See bug 909340. |
210 | 0 | bool defineOnGlobal = dom::XULElement_Binding::ConstructorEnabled(cx, global); |
211 | 0 | dom::XULElement_Binding::GetConstructorObjectHandle(cx, defineOnGlobal); |
212 | 0 |
|
213 | 0 | rv = nsContentUtils::WrapNative(cx, aBoundElement, &v, |
214 | 0 | /* aAllowWrapping = */ false); |
215 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
216 | 0 |
|
217 | 0 | JS::Rooted<JSObject*> value(cx, &v.toObject()); |
218 | 0 |
|
219 | 0 | // We passed aAllowWrapping = false to nsContentUtils::WrapNative so we |
220 | 0 | // should not have a wrapper. |
221 | 0 | MOZ_ASSERT(!js::IsWrapper(value)); |
222 | 0 |
|
223 | 0 | JSAutoRealm ar2(cx, value); |
224 | 0 |
|
225 | 0 | // All of the above code was just obtaining the bound element's script object and its immediate |
226 | 0 | // concrete base class. We need to alter the object so that our concrete class is interposed |
227 | 0 | // between the object and its base class. We become the new base class of the object, and the |
228 | 0 | // object's old base class becomes the new class' base class. |
229 | 0 | rv = aBinding->InitClass(mClassName, cx, value, aTargetClassObject, aTargetIsNew); |
230 | 0 | if (NS_FAILED(rv)) { |
231 | 0 | return rv; |
232 | 0 | } |
233 | 0 | |
234 | 0 | aBoundElement->PreserveWrapper(aBoundElement); |
235 | 0 |
|
236 | 0 | return rv; |
237 | 0 | } |
238 | | |
239 | | nsresult |
240 | | nsXBLProtoImpl::CompilePrototypeMembers(nsXBLPrototypeBinding* aBinding) |
241 | 0 | { |
242 | 0 | // We want to pre-compile our implementation's members against a "prototype context". Then when we actually |
243 | 0 | // bind the prototype to a real xbl instance, we'll clone the pre-compiled JS into the real instance's |
244 | 0 | // context. |
245 | 0 | AutoJSAPI jsapi; |
246 | 0 | if (NS_WARN_IF(!jsapi.Init(xpc::CompilationScope()))) |
247 | 0 | return NS_ERROR_FAILURE; |
248 | 0 | JSContext* cx = jsapi.cx(); |
249 | 0 |
|
250 | 0 | mPrecompiledMemberHolder = JS_NewObjectWithGivenProto(cx, nullptr, nullptr); |
251 | 0 | if (!mPrecompiledMemberHolder) |
252 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
253 | 0 | |
254 | 0 | // Now that we have a class object installed, we walk our member list and compile each of our |
255 | 0 | // properties and methods in turn. |
256 | 0 | JS::Rooted<JSObject*> rootedHolder(cx, mPrecompiledMemberHolder); |
257 | 0 | for (nsXBLProtoImplMember* curr = mMembers; |
258 | 0 | curr; |
259 | 0 | curr = curr->GetNext()) { |
260 | 0 | nsresult rv = curr->CompileMember(jsapi, mClassName, rootedHolder); |
261 | 0 | if (NS_FAILED(rv)) { |
262 | 0 | DestroyMembers(); |
263 | 0 | return rv; |
264 | 0 | } |
265 | 0 | } |
266 | 0 |
|
267 | 0 | return NS_OK; |
268 | 0 | } |
269 | | |
270 | | bool |
271 | | nsXBLProtoImpl::LookupMember(JSContext* aCx, nsString& aName, |
272 | | JS::Handle<jsid> aNameAsId, |
273 | | JS::MutableHandle<JS::PropertyDescriptor> aDesc, |
274 | | JS::Handle<JSObject*> aClassObject) |
275 | 0 | { |
276 | 0 | for (nsXBLProtoImplMember* m = mMembers; m; m = m->GetNext()) { |
277 | 0 | if (aName.Equals(m->GetName())) { |
278 | 0 | return JS_GetPropertyDescriptorById(aCx, aClassObject, aNameAsId, aDesc); |
279 | 0 | } |
280 | 0 | } |
281 | 0 | return true; |
282 | 0 | } |
283 | | |
284 | | void |
285 | | nsXBLProtoImpl::Trace(const TraceCallbacks& aCallbacks, void *aClosure) |
286 | 0 | { |
287 | 0 | // If we don't have a class object then we either didn't compile members |
288 | 0 | // or we only have fields, in both cases there are no cycles through our |
289 | 0 | // members. |
290 | 0 | if (!mPrecompiledMemberHolder) { |
291 | 0 | return; |
292 | 0 | } |
293 | 0 | |
294 | 0 | nsXBLProtoImplMember *member; |
295 | 0 | for (member = mMembers; member; member = member->GetNext()) { |
296 | 0 | member->Trace(aCallbacks, aClosure); |
297 | 0 | } |
298 | 0 | } |
299 | | |
300 | | void |
301 | | nsXBLProtoImpl::UnlinkJSObjects() |
302 | 0 | { |
303 | 0 | if (mPrecompiledMemberHolder) { |
304 | 0 | DestroyMembers(); |
305 | 0 | } |
306 | 0 | } |
307 | | |
308 | | nsXBLProtoImplField* |
309 | | nsXBLProtoImpl::FindField(const nsString& aFieldName) const |
310 | 0 | { |
311 | 0 | for (nsXBLProtoImplField* f = mFields; f; f = f->GetNext()) { |
312 | 0 | if (aFieldName.Equals(f->GetName())) { |
313 | 0 | return f; |
314 | 0 | } |
315 | 0 | } |
316 | 0 |
|
317 | 0 | return nullptr; |
318 | 0 | } |
319 | | |
320 | | bool |
321 | | nsXBLProtoImpl::ResolveAllFields(JSContext *cx, JS::Handle<JSObject*> obj) const |
322 | 0 | { |
323 | 0 | for (nsXBLProtoImplField* f = mFields; f; f = f->GetNext()) { |
324 | 0 | nsDependentString name(f->GetName()); |
325 | 0 | bool dummy; |
326 | 0 | if (!::JS_HasUCProperty(cx, obj, name.get(), name.Length(), &dummy)) { |
327 | 0 | return false; |
328 | 0 | } |
329 | 0 | } |
330 | 0 |
|
331 | 0 | return true; |
332 | 0 | } |
333 | | |
334 | | void |
335 | | nsXBLProtoImpl::UndefineFields(JSContext *cx, JS::Handle<JSObject*> obj) const |
336 | 0 | { |
337 | 0 | for (nsXBLProtoImplField* f = mFields; f; f = f->GetNext()) { |
338 | 0 | nsDependentString name(f->GetName()); |
339 | 0 |
|
340 | 0 | const char16_t* s = name.get(); |
341 | 0 | bool hasProp; |
342 | 0 | if (::JS_AlreadyHasOwnUCProperty(cx, obj, s, name.Length(), &hasProp) && |
343 | 0 | hasProp) { |
344 | 0 | JS::ObjectOpResult ignored; |
345 | 0 | ::JS_DeleteUCProperty(cx, obj, s, name.Length(), ignored); |
346 | 0 | } |
347 | 0 | } |
348 | 0 | } |
349 | | |
350 | | void |
351 | | nsXBLProtoImpl::DestroyMembers() |
352 | 0 | { |
353 | 0 | MOZ_ASSERT(mPrecompiledMemberHolder); |
354 | 0 |
|
355 | 0 | delete mMembers; |
356 | 0 | mMembers = nullptr; |
357 | 0 | mConstructor = nullptr; |
358 | 0 | mDestructor = nullptr; |
359 | 0 | } |
360 | | |
361 | | nsresult |
362 | | nsXBLProtoImpl::Read(nsIObjectInputStream* aStream, |
363 | | nsXBLPrototypeBinding* aBinding) |
364 | 0 | { |
365 | 0 | AssertInCompilationScope(); |
366 | 0 | AutoJSContext cx; |
367 | 0 | // Set up a class object first so that deserialization is possible |
368 | 0 | mPrecompiledMemberHolder = JS_NewObjectWithGivenProto(cx, nullptr, nullptr); |
369 | 0 | if (!mPrecompiledMemberHolder) |
370 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
371 | 0 | |
372 | 0 | nsXBLProtoImplField* previousField = nullptr; |
373 | 0 | nsXBLProtoImplMember* previousMember = nullptr; |
374 | 0 |
|
375 | 0 | do { |
376 | 0 | XBLBindingSerializeDetails type; |
377 | 0 | nsresult rv = aStream->Read8(&type); |
378 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
379 | 0 | if (type == XBLBinding_Serialize_NoMoreItems) |
380 | 0 | break; |
381 | 0 | |
382 | 0 | switch (type & XBLBinding_Serialize_Mask) { |
383 | 0 | case XBLBinding_Serialize_Field: |
384 | 0 | { |
385 | 0 | nsXBLProtoImplField* field = |
386 | 0 | new nsXBLProtoImplField(type & XBLBinding_Serialize_ReadOnly); |
387 | 0 | rv = field->Read(aStream); |
388 | 0 | if (NS_FAILED(rv)) { |
389 | 0 | delete field; |
390 | 0 | return rv; |
391 | 0 | } |
392 | 0 | |
393 | 0 | if (previousField) { |
394 | 0 | previousField->SetNext(field); |
395 | 0 | } |
396 | 0 | else { |
397 | 0 | mFields = field; |
398 | 0 | } |
399 | 0 | previousField = field; |
400 | 0 |
|
401 | 0 | break; |
402 | 0 | } |
403 | 0 | case XBLBinding_Serialize_GetterProperty: |
404 | 0 | case XBLBinding_Serialize_SetterProperty: |
405 | 0 | case XBLBinding_Serialize_GetterSetterProperty: |
406 | 0 | { |
407 | 0 | nsAutoString name; |
408 | 0 | nsresult rv = aStream->ReadString(name); |
409 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
410 | 0 |
|
411 | 0 | nsXBLProtoImplProperty* prop = |
412 | 0 | new nsXBLProtoImplProperty(name.get(), type & XBLBinding_Serialize_ReadOnly); |
413 | 0 | rv = prop->Read(aStream, type & XBLBinding_Serialize_Mask); |
414 | 0 | if (NS_FAILED(rv)) { |
415 | 0 | delete prop; |
416 | 0 | return rv; |
417 | 0 | } |
418 | 0 | |
419 | 0 | previousMember = AddMember(prop, previousMember); |
420 | 0 | break; |
421 | 0 | } |
422 | 0 | case XBLBinding_Serialize_Method: |
423 | 0 | { |
424 | 0 | nsAutoString name; |
425 | 0 | rv = aStream->ReadString(name); |
426 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
427 | 0 |
|
428 | 0 | nsXBLProtoImplMethod* method = new nsXBLProtoImplMethod(name.get()); |
429 | 0 | rv = method->Read(aStream); |
430 | 0 | if (NS_FAILED(rv)) { |
431 | 0 | delete method; |
432 | 0 | return rv; |
433 | 0 | } |
434 | 0 | |
435 | 0 | previousMember = AddMember(method, previousMember); |
436 | 0 | break; |
437 | 0 | } |
438 | 0 | case XBLBinding_Serialize_Constructor: |
439 | 0 | { |
440 | 0 | nsAutoString name; |
441 | 0 | rv = aStream->ReadString(name); |
442 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
443 | 0 |
|
444 | 0 | mConstructor = new nsXBLProtoImplAnonymousMethod(name.get()); |
445 | 0 | rv = mConstructor->Read(aStream); |
446 | 0 | if (NS_FAILED(rv)) { |
447 | 0 | delete mConstructor; |
448 | 0 | mConstructor = nullptr; |
449 | 0 | return rv; |
450 | 0 | } |
451 | 0 | |
452 | 0 | previousMember = AddMember(mConstructor, previousMember); |
453 | 0 | break; |
454 | 0 | } |
455 | 0 | case XBLBinding_Serialize_Destructor: |
456 | 0 | { |
457 | 0 | nsAutoString name; |
458 | 0 | rv = aStream->ReadString(name); |
459 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
460 | 0 |
|
461 | 0 | mDestructor = new nsXBLProtoImplAnonymousMethod(name.get()); |
462 | 0 | rv = mDestructor->Read(aStream); |
463 | 0 | if (NS_FAILED(rv)) { |
464 | 0 | delete mDestructor; |
465 | 0 | mDestructor = nullptr; |
466 | 0 | return rv; |
467 | 0 | } |
468 | 0 | |
469 | 0 | previousMember = AddMember(mDestructor, previousMember); |
470 | 0 | break; |
471 | 0 | } |
472 | 0 | default: |
473 | 0 | NS_ERROR("Unexpected binding member type"); |
474 | 0 | break; |
475 | 0 | } |
476 | 0 | } while (1); |
477 | 0 |
|
478 | 0 | return NS_OK; |
479 | 0 | } |
480 | | |
481 | | nsresult |
482 | | nsXBLProtoImpl::Write(nsIObjectOutputStream* aStream, |
483 | | nsXBLPrototypeBinding* aBinding) |
484 | 0 | { |
485 | 0 | nsresult rv; |
486 | 0 |
|
487 | 0 | if (!mPrecompiledMemberHolder) { |
488 | 0 | rv = CompilePrototypeMembers(aBinding); |
489 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
490 | 0 | } |
491 | 0 |
|
492 | 0 | rv = aStream->WriteUtf8Z(mClassName.get()); |
493 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
494 | 0 |
|
495 | 0 | for (nsXBLProtoImplField* curr = mFields; curr; curr = curr->GetNext()) { |
496 | 0 | rv = curr->Write(aStream); |
497 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
498 | 0 | } |
499 | 0 | for (nsXBLProtoImplMember* curr = mMembers; curr; curr = curr->GetNext()) { |
500 | 0 | if (curr == mConstructor) { |
501 | 0 | rv = mConstructor->Write(aStream, XBLBinding_Serialize_Constructor); |
502 | 0 | } |
503 | 0 | else if (curr == mDestructor) { |
504 | 0 | rv = mDestructor->Write(aStream, XBLBinding_Serialize_Destructor); |
505 | 0 | } |
506 | 0 | else { |
507 | 0 | rv = curr->Write(aStream); |
508 | 0 | } |
509 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
510 | 0 | } |
511 | 0 |
|
512 | 0 | return aStream->Write8(XBLBinding_Serialize_NoMoreItems); |
513 | 0 | } |
514 | | |
515 | | void |
516 | | NS_NewXBLProtoImpl(nsXBLPrototypeBinding* aBinding, |
517 | | const char16_t* aClassName, |
518 | | nsXBLProtoImpl** aResult) |
519 | 0 | { |
520 | 0 | nsXBLProtoImpl* impl = new nsXBLProtoImpl(); |
521 | 0 | if (aClassName) { |
522 | 0 | impl->mClassName = aClassName; |
523 | 0 | } else { |
524 | 0 | nsCString spec; |
525 | 0 | nsresult rv = aBinding->BindingURI()->GetSpec(spec); |
526 | 0 | // XXX: should handle this better |
527 | 0 | MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv)); |
528 | 0 | impl->mClassName = NS_ConvertUTF8toUTF16(spec); |
529 | 0 | } |
530 | 0 |
|
531 | 0 | aBinding->SetImplementation(impl); |
532 | 0 | *aResult = impl; |
533 | 0 | } |
534 | | |