1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is Mozilla Communicator client code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Original Author: David W. Hyatt (hyatt@netscape.com)
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either of the GNU General Public License Version 2 or later (the "GPL"),
27 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "mozilla/Util.h"
40 :
41 : #include "nsCOMPtr.h"
42 : #include "nsIAtom.h"
43 : #include "nsIInputStream.h"
44 : #include "nsINameSpaceManager.h"
45 : #include "nsIURI.h"
46 : #include "nsIURL.h"
47 : #include "nsIDOMEventTarget.h"
48 : #include "nsIChannel.h"
49 : #include "nsXPIDLString.h"
50 : #include "nsReadableUtils.h"
51 : #include "nsIParser.h"
52 : #include "nsParserCIID.h"
53 : #include "nsNetUtil.h"
54 : #include "plstr.h"
55 : #include "nsContentCreatorFunctions.h"
56 : #include "nsIDocument.h"
57 : #include "nsIXMLContentSink.h"
58 : #include "nsContentCID.h"
59 : #include "nsXMLDocument.h"
60 : #include "nsXBLService.h"
61 : #include "nsXBLBinding.h"
62 : #include "nsXBLInsertionPoint.h"
63 : #include "nsXBLPrototypeBinding.h"
64 : #include "nsXBLContentSink.h"
65 : #include "nsFixedSizeAllocator.h"
66 : #include "xptinfo.h"
67 : #include "nsIInterfaceInfoManager.h"
68 : #include "nsIDocumentObserver.h"
69 : #include "nsGkAtoms.h"
70 : #include "nsXBLProtoImpl.h"
71 : #include "nsCRT.h"
72 : #include "nsContentUtils.h"
73 :
74 : #include "nsIScriptContext.h"
75 : #include "nsIScriptError.h"
76 :
77 : #include "nsIStyleRuleProcessor.h"
78 : #include "nsXBLResourceLoader.h"
79 : #include "mozilla/dom/Element.h"
80 :
81 : #ifdef MOZ_XUL
82 : #include "nsXULElement.h"
83 : #endif
84 :
85 : using namespace mozilla;
86 : using namespace mozilla::dom;
87 :
88 : // Helper Classes =====================================================================
89 :
90 : // Internal helper class for managing our IID table.
91 : class nsIIDKey : public nsHashKey {
92 : public:
93 : nsIID mKey;
94 :
95 : public:
96 0 : nsIIDKey(REFNSIID key) : mKey(key) {}
97 0 : ~nsIIDKey(void) {}
98 :
99 0 : PRUint32 HashCode(void) const {
100 : // Just use the 32-bit m0 field.
101 0 : return mKey.m0;
102 : }
103 :
104 0 : bool Equals(const nsHashKey *aKey) const {
105 0 : return mKey.Equals( ((nsIIDKey*) aKey)->mKey);
106 : }
107 :
108 0 : nsHashKey *Clone(void) const {
109 0 : return new nsIIDKey(mKey);
110 : }
111 : };
112 :
113 : // nsXBLAttributeEntry and helpers. This class is used to efficiently handle
114 : // attribute changes in anonymous content.
115 :
116 : class nsXBLAttributeEntry {
117 : public:
118 0 : nsIAtom* GetSrcAttribute() { return mSrcAttribute; }
119 0 : nsIAtom* GetDstAttribute() { return mDstAttribute; }
120 0 : PRInt32 GetDstNameSpace() { return mDstNameSpace; }
121 :
122 0 : nsIContent* GetElement() { return mElement; }
123 :
124 0 : nsXBLAttributeEntry* GetNext() { return mNext; }
125 0 : void SetNext(nsXBLAttributeEntry* aEntry) { mNext = aEntry; }
126 :
127 : static nsXBLAttributeEntry*
128 0 : Create(nsIAtom* aSrcAtom, nsIAtom* aDstAtom, PRInt32 aDstNameSpace, nsIContent* aContent) {
129 0 : void* place = nsXBLPrototypeBinding::kAttrPool->Alloc(sizeof(nsXBLAttributeEntry));
130 : return place ? ::new (place) nsXBLAttributeEntry(aSrcAtom, aDstAtom, aDstNameSpace,
131 0 : aContent) : nsnull;
132 : }
133 :
134 : static void
135 0 : Destroy(nsXBLAttributeEntry* aSelf) {
136 0 : aSelf->~nsXBLAttributeEntry();
137 0 : nsXBLPrototypeBinding::kAttrPool->Free(aSelf, sizeof(*aSelf));
138 0 : }
139 :
140 : protected:
141 : nsIContent* mElement;
142 :
143 : nsCOMPtr<nsIAtom> mSrcAttribute;
144 : nsCOMPtr<nsIAtom> mDstAttribute;
145 : PRInt32 mDstNameSpace;
146 : nsXBLAttributeEntry* mNext;
147 :
148 0 : nsXBLAttributeEntry(nsIAtom* aSrcAtom, nsIAtom* aDstAtom, PRInt32 aDstNameSpace,
149 : nsIContent* aContent)
150 : : mElement(aContent),
151 : mSrcAttribute(aSrcAtom),
152 : mDstAttribute(aDstAtom),
153 : mDstNameSpace(aDstNameSpace),
154 0 : mNext(nsnull) { }
155 :
156 0 : ~nsXBLAttributeEntry() {
157 0 : NS_CONTENT_DELETE_LIST_MEMBER(nsXBLAttributeEntry, this, mNext);
158 0 : }
159 :
160 : private:
161 : // Hide so that only Create() and Destroy() can be used to
162 : // allocate and deallocate from the heap
163 : static void* operator new(size_t) CPP_THROW_NEW { return 0; }
164 0 : static void operator delete(void*, size_t) {}
165 : };
166 :
167 : // nsXBLInsertionPointEntry and helpers. This class stores all the necessary
168 : // info to figure out the position of an insertion point.
169 : // The same insertion point may be in the insertion point table for multiple
170 : // keys, so we refcount the entries.
171 :
172 : class nsXBLInsertionPointEntry {
173 : public:
174 0 : ~nsXBLInsertionPointEntry() {
175 0 : if (mDefaultContent) {
176 0 : nsAutoScriptBlocker scriptBlocker;
177 : // mDefaultContent is a sort of anonymous content within the XBL
178 : // document, and we own and manage it. Unhook it here, since we're going
179 : // away.
180 0 : mDefaultContent->UnbindFromTree();
181 : }
182 0 : }
183 :
184 1464 : NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(nsXBLInsertionPointEntry)
185 :
186 0 : nsIContent* GetInsertionParent() { return mInsertionParent; }
187 0 : PRUint32 GetInsertionIndex() { return mInsertionIndex; }
188 0 : void SetInsertionIndex(PRUint32 aIndex) { mInsertionIndex = aIndex; }
189 :
190 0 : nsIContent* GetDefaultContent() { return mDefaultContent; }
191 0 : void SetDefaultContent(nsIContent* aChildren) { mDefaultContent = aChildren; }
192 :
193 :
194 : // We keep kPool alive as long as there is at least either a
195 : // nsXBLPrototypeBinding or a nsXBLInsertionPointEntry alive.
196 : // nsXBLPrototypeBinding has its own global refcount so it only adds 1 to
197 : // nsXBLInsertionPointEntry::gRefCnt as long as there's at least one
198 : // nsXBLPrototypeBinding alive.
199 :
200 0 : static void InitPool(PRInt32 aInitialSize)
201 : {
202 0 : if (++gRefCnt == 1) {
203 0 : kPool = new nsFixedSizeAllocator();
204 0 : if (kPool) {
205 : static const size_t kBucketSizes[] = {
206 : sizeof(nsXBLInsertionPointEntry)
207 : };
208 : kPool->Init("XBL Insertion Point Entries", kBucketSizes,
209 0 : ArrayLength(kBucketSizes), aInitialSize);
210 : }
211 : }
212 0 : }
213 0 : static bool PoolInited()
214 : {
215 0 : return kPool != nsnull;
216 : }
217 0 : static void ReleasePool()
218 : {
219 0 : if (--gRefCnt == 0) {
220 0 : delete kPool;
221 : }
222 0 : }
223 :
224 : static nsXBLInsertionPointEntry*
225 0 : Create(nsIContent* aParent) {
226 0 : void* place = kPool->Alloc(sizeof(nsXBLInsertionPointEntry));
227 0 : if (!place) {
228 0 : return nsnull;
229 : }
230 0 : ++gRefCnt;
231 0 : return ::new (place) nsXBLInsertionPointEntry(aParent);
232 : }
233 :
234 : static void
235 : Destroy(nsXBLInsertionPointEntry* aSelf) {
236 : aSelf->~nsXBLInsertionPointEntry();
237 : kPool->Free(aSelf, sizeof(*aSelf));
238 : nsXBLInsertionPointEntry::ReleasePool();
239 : }
240 :
241 0 : NS_INLINE_DECL_REFCOUNTING(nsXBLInsertionPointEntry)
242 :
243 : protected:
244 : nsCOMPtr<nsIContent> mInsertionParent;
245 : nsCOMPtr<nsIContent> mDefaultContent;
246 : PRUint32 mInsertionIndex;
247 :
248 0 : nsXBLInsertionPointEntry(nsIContent* aParent)
249 : : mInsertionParent(aParent),
250 0 : mInsertionIndex(0) { }
251 :
252 : private:
253 : // Hide so that only Create() and Destroy() can be used to
254 : // allocate and deallocate from the heap
255 : static void* operator new(size_t) CPP_THROW_NEW { return 0; }
256 0 : static void operator delete(void*, size_t) {}
257 :
258 : static nsFixedSizeAllocator* kPool;
259 : static PRUint32 gRefCnt;
260 : };
261 :
262 : PRUint32 nsXBLInsertionPointEntry::gRefCnt = 0;
263 : nsFixedSizeAllocator* nsXBLInsertionPointEntry::kPool;
264 :
265 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsXBLInsertionPointEntry)
266 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_NATIVE(nsXBLInsertionPointEntry)
267 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mInsertionParent)
268 0 : if (tmp->mDefaultContent) {
269 0 : nsAutoScriptBlocker scriptBlocker;
270 : // mDefaultContent is a sort of anonymous content within the XBL
271 : // document, and we own and manage it. Unhook it here, since we're going
272 : // away.
273 0 : tmp->mDefaultContent->UnbindFromTree();
274 0 : tmp->mDefaultContent = nsnull;
275 : }
276 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
277 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_BEGIN(nsXBLInsertionPointEntry)
278 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mInsertionParent)
279 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDefaultContent)
280 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
281 0 : NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsXBLInsertionPointEntry, AddRef)
282 0 : NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsXBLInsertionPointEntry, Release)
283 :
284 : // =============================================================================
285 :
286 : // Static initialization
287 : PRUint32 nsXBLPrototypeBinding::gRefCnt = 0;
288 :
289 : nsFixedSizeAllocator* nsXBLPrototypeBinding::kAttrPool;
290 :
291 : static const PRInt32 kNumElements = 128;
292 :
293 : static const size_t kAttrBucketSizes[] = {
294 : sizeof(nsXBLAttributeEntry)
295 : };
296 :
297 : static const PRInt32 kAttrNumBuckets = sizeof(kAttrBucketSizes)/sizeof(size_t);
298 : static const PRInt32 kAttrInitialSize = (NS_SIZE_IN_HEAP(sizeof(nsXBLAttributeEntry))) * kNumElements;
299 :
300 : static const PRInt32 kInsInitialSize = (NS_SIZE_IN_HEAP(sizeof(nsXBLInsertionPointEntry))) * kNumElements;
301 :
302 : // Implementation /////////////////////////////////////////////////////////////////
303 :
304 : // Constructors/Destructors
305 0 : nsXBLPrototypeBinding::nsXBLPrototypeBinding()
306 : : mImplementation(nsnull),
307 : mBaseBinding(nsnull),
308 : mInheritStyle(true),
309 : mCheckedBaseProto(false),
310 : mKeyHandlersRegistered(false),
311 : mResources(nsnull),
312 : mAttributeTable(nsnull),
313 : mInsertionPointTable(nsnull),
314 : mInterfaceTable(nsnull),
315 0 : mBaseNameSpaceID(kNameSpaceID_None)
316 : {
317 0 : MOZ_COUNT_CTOR(nsXBLPrototypeBinding);
318 0 : gRefCnt++;
319 :
320 0 : if (gRefCnt == 1) {
321 0 : kAttrPool = new nsFixedSizeAllocator();
322 0 : if (kAttrPool) {
323 0 : kAttrPool->Init("XBL Attribute Entries", kAttrBucketSizes, kAttrNumBuckets, kAttrInitialSize);
324 : }
325 0 : nsXBLInsertionPointEntry::InitPool(kInsInitialSize);
326 : }
327 0 : }
328 :
329 : nsresult
330 0 : nsXBLPrototypeBinding::Init(const nsACString& aID,
331 : nsXBLDocumentInfo* aInfo,
332 : nsIContent* aElement,
333 : bool aFirstBinding)
334 : {
335 0 : if (!kAttrPool || !nsXBLInsertionPointEntry::PoolInited()) {
336 0 : return NS_ERROR_OUT_OF_MEMORY;
337 : }
338 :
339 0 : nsresult rv = aInfo->DocumentURI()->Clone(getter_AddRefs(mBindingURI));
340 0 : NS_ENSURE_SUCCESS(rv, rv);
341 :
342 : // The binding URI might be an immutable URI (e.g. for about: URIs). In that case,
343 : // we'll fail in SetRef below, but that doesn't matter much for now.
344 0 : if (aFirstBinding) {
345 0 : rv = mBindingURI->Clone(getter_AddRefs(mAlternateBindingURI));
346 0 : NS_ENSURE_SUCCESS(rv, rv);
347 : }
348 0 : mBindingURI->SetRef(aID);
349 :
350 0 : mXBLDocInfoWeak = aInfo;
351 :
352 : // aElement will be null when reading from the cache, but the element will
353 : // still be set later.
354 0 : if (aElement) {
355 0 : SetBindingElement(aElement);
356 : }
357 0 : return NS_OK;
358 : }
359 :
360 0 : bool nsXBLPrototypeBinding::CompareBindingURI(nsIURI* aURI) const
361 : {
362 0 : bool equal = false;
363 0 : mBindingURI->Equals(aURI, &equal);
364 0 : if (!equal && mAlternateBindingURI) {
365 0 : mAlternateBindingURI->Equals(aURI, &equal);
366 : }
367 0 : return equal;
368 : }
369 :
370 : static bool
371 0 : TraverseInsertionPoint(nsHashKey* aKey, void* aData, void* aClosure)
372 : {
373 : nsCycleCollectionTraversalCallback &cb =
374 0 : *static_cast<nsCycleCollectionTraversalCallback*>(aClosure);
375 : nsXBLInsertionPointEntry* entry =
376 0 : static_cast<nsXBLInsertionPointEntry*>(aData);
377 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_PTR(entry,
378 : nsXBLInsertionPointEntry,
379 : "[insertion point table] value")
380 0 : return kHashEnumerateNext;
381 : }
382 :
383 : static bool
384 0 : TraverseBinding(nsHashKey *aKey, void *aData, void* aClosure)
385 : {
386 : nsCycleCollectionTraversalCallback *cb =
387 0 : static_cast<nsCycleCollectionTraversalCallback*>(aClosure);
388 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME((*cb), "proto mInterfaceTable data");
389 0 : cb->NoteXPCOMChild(static_cast<nsISupports*>(aData));
390 0 : return kHashEnumerateNext;
391 : }
392 :
393 : void
394 0 : nsXBLPrototypeBinding::Traverse(nsCycleCollectionTraversalCallback &cb) const
395 : {
396 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "proto mBinding");
397 0 : cb.NoteXPCOMChild(mBinding);
398 0 : if (mResources) {
399 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "proto mResources mLoader");
400 0 : cb.NoteXPCOMChild(mResources->mLoader);
401 : }
402 0 : if (mInsertionPointTable)
403 0 : mInsertionPointTable->Enumerate(TraverseInsertionPoint, &cb);
404 0 : if (mInterfaceTable)
405 0 : mInterfaceTable->Enumerate(TraverseBinding, &cb);
406 0 : }
407 :
408 : void
409 0 : nsXBLPrototypeBinding::UnlinkJSObjects()
410 : {
411 0 : if (mImplementation)
412 0 : mImplementation->UnlinkJSObjects();
413 0 : }
414 :
415 : void
416 0 : nsXBLPrototypeBinding::Trace(TraceCallback aCallback, void *aClosure) const
417 : {
418 0 : if (mImplementation)
419 0 : mImplementation->Trace(aCallback, aClosure);
420 0 : }
421 :
422 : void
423 0 : nsXBLPrototypeBinding::Initialize()
424 : {
425 0 : nsIContent* content = GetImmediateChild(nsGkAtoms::content);
426 0 : if (content) {
427 : // Make sure to construct the attribute table first, since constructing the
428 : // insertion point table removes some of the subtrees, which makes them
429 : // unreachable by walking our DOM.
430 0 : ConstructAttributeTable(content);
431 0 : ConstructInsertionTable(content);
432 : }
433 0 : }
434 :
435 0 : nsXBLPrototypeBinding::~nsXBLPrototypeBinding(void)
436 : {
437 0 : delete mResources;
438 0 : delete mAttributeTable;
439 0 : delete mInsertionPointTable;
440 0 : delete mInterfaceTable;
441 0 : delete mImplementation;
442 0 : gRefCnt--;
443 0 : if (gRefCnt == 0) {
444 0 : delete kAttrPool;
445 0 : nsXBLInsertionPointEntry::ReleasePool();
446 : }
447 0 : MOZ_COUNT_DTOR(nsXBLPrototypeBinding);
448 0 : }
449 :
450 : void
451 0 : nsXBLPrototypeBinding::SetBasePrototype(nsXBLPrototypeBinding* aBinding)
452 : {
453 0 : if (mBaseBinding == aBinding)
454 0 : return;
455 :
456 0 : if (mBaseBinding) {
457 0 : NS_ERROR("Base XBL prototype binding is already defined!");
458 0 : return;
459 : }
460 :
461 0 : mBaseBinding = aBinding;
462 : }
463 :
464 : already_AddRefed<nsIContent>
465 0 : nsXBLPrototypeBinding::GetBindingElement()
466 : {
467 0 : nsIContent* result = mBinding;
468 0 : NS_IF_ADDREF(result);
469 0 : return result;
470 : }
471 :
472 : void
473 0 : nsXBLPrototypeBinding::SetBindingElement(nsIContent* aElement)
474 : {
475 0 : mBinding = aElement;
476 0 : if (mBinding->AttrValueIs(kNameSpaceID_None, nsGkAtoms::inheritstyle,
477 0 : nsGkAtoms::_false, eCaseMatters))
478 0 : mInheritStyle = false;
479 0 : }
480 :
481 : bool
482 0 : nsXBLPrototypeBinding::GetAllowScripts()
483 : {
484 0 : return mXBLDocInfoWeak->GetScriptAccess();
485 : }
486 :
487 : bool
488 0 : nsXBLPrototypeBinding::LoadResources()
489 : {
490 0 : if (mResources) {
491 : bool result;
492 0 : mResources->LoadResources(&result);
493 0 : return result;
494 : }
495 :
496 0 : return true;
497 : }
498 :
499 : nsresult
500 0 : nsXBLPrototypeBinding::AddResource(nsIAtom* aResourceType, const nsAString& aSrc)
501 : {
502 0 : if (!mResources) {
503 0 : mResources = new nsXBLPrototypeResources(this);
504 0 : if (!mResources)
505 0 : return NS_ERROR_OUT_OF_MEMORY;
506 : }
507 :
508 0 : mResources->AddResource(aResourceType, aSrc);
509 0 : return NS_OK;
510 : }
511 :
512 : nsresult
513 0 : nsXBLPrototypeBinding::FlushSkinSheets()
514 : {
515 0 : if (mResources)
516 0 : return mResources->FlushSkinSheets();
517 0 : return NS_OK;
518 : }
519 :
520 : nsresult
521 0 : nsXBLPrototypeBinding::BindingAttached(nsIContent* aBoundElement)
522 : {
523 0 : if (mImplementation && mImplementation->CompiledMembers() &&
524 : mImplementation->mConstructor)
525 0 : return mImplementation->mConstructor->Execute(aBoundElement);
526 0 : return NS_OK;
527 : }
528 :
529 : nsresult
530 0 : nsXBLPrototypeBinding::BindingDetached(nsIContent* aBoundElement)
531 : {
532 0 : if (mImplementation && mImplementation->CompiledMembers() &&
533 : mImplementation->mDestructor)
534 0 : return mImplementation->mDestructor->Execute(aBoundElement);
535 0 : return NS_OK;
536 : }
537 :
538 : nsXBLProtoImplAnonymousMethod*
539 0 : nsXBLPrototypeBinding::GetConstructor()
540 : {
541 0 : if (mImplementation)
542 0 : return mImplementation->mConstructor;
543 :
544 0 : return nsnull;
545 : }
546 :
547 : nsXBLProtoImplAnonymousMethod*
548 0 : nsXBLPrototypeBinding::GetDestructor()
549 : {
550 0 : if (mImplementation)
551 0 : return mImplementation->mDestructor;
552 :
553 0 : return nsnull;
554 : }
555 :
556 : nsresult
557 0 : nsXBLPrototypeBinding::SetConstructor(nsXBLProtoImplAnonymousMethod* aMethod)
558 : {
559 0 : if (!mImplementation)
560 0 : return NS_ERROR_FAILURE;
561 0 : mImplementation->mConstructor = aMethod;
562 0 : return NS_OK;
563 : }
564 :
565 : nsresult
566 0 : nsXBLPrototypeBinding::SetDestructor(nsXBLProtoImplAnonymousMethod* aMethod)
567 : {
568 0 : if (!mImplementation)
569 0 : return NS_ERROR_FAILURE;
570 0 : mImplementation->mDestructor = aMethod;
571 0 : return NS_OK;
572 : }
573 :
574 : nsresult
575 0 : nsXBLPrototypeBinding::InstallImplementation(nsIContent* aBoundElement)
576 : {
577 0 : if (mImplementation)
578 0 : return mImplementation->InstallImplementation(this, aBoundElement);
579 0 : return NS_OK;
580 : }
581 :
582 : // XXXbz this duplicates lots of SetAttrs
583 : void
584 0 : nsXBLPrototypeBinding::AttributeChanged(nsIAtom* aAttribute,
585 : PRInt32 aNameSpaceID,
586 : bool aRemoveFlag,
587 : nsIContent* aChangedElement,
588 : nsIContent* aAnonymousContent,
589 : bool aNotify)
590 : {
591 0 : if (!mAttributeTable)
592 0 : return;
593 0 : nsPRUint32Key nskey(aNameSpaceID);
594 0 : nsObjectHashtable *attributesNS = static_cast<nsObjectHashtable*>(mAttributeTable->Get(&nskey));
595 0 : if (!attributesNS)
596 : return;
597 :
598 0 : nsISupportsKey key(aAttribute);
599 : nsXBLAttributeEntry* xblAttr = static_cast<nsXBLAttributeEntry*>
600 0 : (attributesNS->Get(&key));
601 0 : if (!xblAttr)
602 : return;
603 :
604 : // Iterate over the elements in the array.
605 0 : nsCOMPtr<nsIContent> content = GetImmediateChild(nsGkAtoms::content);
606 0 : while (xblAttr) {
607 0 : nsIContent* element = xblAttr->GetElement();
608 :
609 : nsCOMPtr<nsIContent> realElement = LocateInstance(aChangedElement, content,
610 : aAnonymousContent,
611 0 : element);
612 :
613 0 : if (realElement) {
614 : // Hold a strong reference here so that the atom doesn't go away during
615 : // UnsetAttr.
616 0 : nsCOMPtr<nsIAtom> dstAttr = xblAttr->GetDstAttribute();
617 0 : PRInt32 dstNs = xblAttr->GetDstNameSpace();
618 :
619 0 : if (aRemoveFlag)
620 0 : realElement->UnsetAttr(dstNs, dstAttr, aNotify);
621 : else {
622 0 : bool attrPresent = true;
623 0 : nsAutoString value;
624 : // Check to see if the src attribute is xbl:text. If so, then we need to obtain the
625 : // children of the real element and get the text nodes' values.
626 0 : if (aAttribute == nsGkAtoms::text && aNameSpaceID == kNameSpaceID_XBL) {
627 0 : nsContentUtils::GetNodeTextContent(aChangedElement, false, value);
628 0 : value.StripChar(PRUnichar('\n'));
629 0 : value.StripChar(PRUnichar('\r'));
630 0 : nsAutoString stripVal(value);
631 0 : stripVal.StripWhitespace();
632 0 : if (stripVal.IsEmpty())
633 0 : attrPresent = false;
634 : }
635 : else {
636 0 : attrPresent = aChangedElement->GetAttr(aNameSpaceID, aAttribute, value);
637 : }
638 :
639 0 : if (attrPresent)
640 0 : realElement->SetAttr(dstNs, dstAttr, value, aNotify);
641 : }
642 :
643 : // See if we're the <html> tag in XUL, and see if value is being
644 : // set or unset on us. We may also be a tag that is having
645 : // xbl:text set on us.
646 :
647 0 : if ((dstAttr == nsGkAtoms::text && dstNs == kNameSpaceID_XBL) ||
648 : (realElement->NodeInfo()->Equals(nsGkAtoms::html,
649 0 : kNameSpaceID_XUL) &&
650 0 : dstAttr == nsGkAtoms::value)) {
651 : // Flush out all our kids.
652 0 : PRUint32 childCount = realElement->GetChildCount();
653 0 : for (PRUint32 i = 0; i < childCount; i++)
654 0 : realElement->RemoveChildAt(0, aNotify);
655 :
656 0 : if (!aRemoveFlag) {
657 : // Construct a new text node and insert it.
658 0 : nsAutoString value;
659 0 : aChangedElement->GetAttr(aNameSpaceID, aAttribute, value);
660 0 : if (!value.IsEmpty()) {
661 0 : nsCOMPtr<nsIContent> textContent;
662 0 : NS_NewTextNode(getter_AddRefs(textContent),
663 0 : realElement->NodeInfo()->NodeInfoManager());
664 0 : if (!textContent) {
665 0 : continue;
666 : }
667 :
668 0 : textContent->SetText(value, true);
669 0 : realElement->AppendChildTo(textContent, true);
670 : }
671 : }
672 : }
673 : }
674 :
675 0 : xblAttr = xblAttr->GetNext();
676 : }
677 : }
678 :
679 : struct InsertionData {
680 : nsXBLBinding* mBinding;
681 : nsXBLPrototypeBinding* mPrototype;
682 :
683 0 : InsertionData(nsXBLBinding* aBinding,
684 : nsXBLPrototypeBinding* aPrototype)
685 0 : :mBinding(aBinding), mPrototype(aPrototype) {}
686 : };
687 :
688 0 : bool InstantiateInsertionPoint(nsHashKey* aKey, void* aData, void* aClosure)
689 : {
690 0 : nsXBLInsertionPointEntry* entry = static_cast<nsXBLInsertionPointEntry*>(aData);
691 0 : InsertionData* data = static_cast<InsertionData*>(aClosure);
692 0 : nsXBLBinding* binding = data->mBinding;
693 0 : nsXBLPrototypeBinding* proto = data->mPrototype;
694 :
695 : // Get the insertion parent.
696 0 : nsIContent* content = entry->GetInsertionParent();
697 0 : PRUint32 index = entry->GetInsertionIndex();
698 0 : nsIContent* defContent = entry->GetDefaultContent();
699 :
700 : // Locate the real content.
701 0 : nsIContent *instanceRoot = binding->GetAnonymousContent();
702 0 : nsIContent *templRoot = proto->GetImmediateChild(nsGkAtoms::content);
703 : nsIContent *realContent = proto->LocateInstance(nsnull, templRoot,
704 0 : instanceRoot, content);
705 0 : if (!realContent)
706 0 : realContent = binding->GetBoundElement();
707 :
708 : // Now that we have the real content, look it up in our table.
709 0 : nsInsertionPointList* points = nsnull;
710 0 : binding->GetInsertionPointsFor(realContent, &points);
711 0 : nsXBLInsertionPoint* insertionPoint = nsnull;
712 0 : PRInt32 count = points->Length();
713 0 : PRInt32 i = 0;
714 0 : PRInt32 currIndex = 0;
715 :
716 0 : for ( ; i < count; i++) {
717 0 : nsXBLInsertionPoint* currPoint = points->ElementAt(i);
718 0 : currIndex = currPoint->GetInsertionIndex();
719 0 : if (currIndex == (PRInt32)index) {
720 : // This is a match. Break out of the loop and set our variable.
721 0 : insertionPoint = currPoint;
722 0 : break;
723 : }
724 :
725 0 : if (currIndex > (PRInt32)index)
726 : // There was no match. Break.
727 0 : break;
728 : }
729 :
730 0 : if (!insertionPoint) {
731 : // We need to make a new insertion point.
732 0 : insertionPoint = new nsXBLInsertionPoint(realContent, index, defContent);
733 0 : if (insertionPoint) {
734 0 : points->InsertElementAt(i, insertionPoint);
735 : }
736 : }
737 :
738 0 : return true;
739 : }
740 :
741 : void
742 0 : nsXBLPrototypeBinding::InstantiateInsertionPoints(nsXBLBinding* aBinding)
743 : {
744 0 : InsertionData data(aBinding, this);
745 0 : if (mInsertionPointTable)
746 0 : mInsertionPointTable->Enumerate(InstantiateInsertionPoint, &data);
747 0 : }
748 :
749 : nsIContent*
750 0 : nsXBLPrototypeBinding::GetInsertionPoint(nsIContent* aBoundElement,
751 : nsIContent* aCopyRoot,
752 : const nsIContent* aChild,
753 : PRUint32* aIndex)
754 : {
755 0 : if (!mInsertionPointTable)
756 0 : return nsnull;
757 :
758 0 : nsISupportsKey key(aChild->Tag());
759 0 : nsXBLInsertionPointEntry* entry = static_cast<nsXBLInsertionPointEntry*>(mInsertionPointTable->Get(&key));
760 0 : if (!entry) {
761 0 : nsISupportsKey key2(nsGkAtoms::children);
762 0 : entry = static_cast<nsXBLInsertionPointEntry*>(mInsertionPointTable->Get(&key2));
763 : }
764 :
765 0 : nsIContent *realContent = nsnull;
766 0 : if (entry) {
767 0 : nsIContent* content = entry->GetInsertionParent();
768 0 : *aIndex = entry->GetInsertionIndex();
769 0 : nsIContent* templContent = GetImmediateChild(nsGkAtoms::content);
770 0 : realContent = LocateInstance(nsnull, templContent, aCopyRoot, content);
771 : }
772 : else {
773 : // We got nothin'. Bail.
774 0 : return nsnull;
775 : }
776 :
777 0 : return realContent ? realContent : aBoundElement;
778 : }
779 :
780 : nsIContent*
781 0 : nsXBLPrototypeBinding::GetSingleInsertionPoint(nsIContent* aBoundElement,
782 : nsIContent* aCopyRoot,
783 : PRUint32* aIndex,
784 : bool* aMultipleInsertionPoints)
785 : {
786 0 : *aMultipleInsertionPoints = false;
787 0 : *aIndex = 0;
788 :
789 0 : if (!mInsertionPointTable)
790 0 : return nsnull;
791 :
792 0 : if (mInsertionPointTable->Count() != 1) {
793 0 : *aMultipleInsertionPoints = true;
794 0 : return nsnull;
795 : }
796 :
797 0 : nsISupportsKey key(nsGkAtoms::children);
798 : nsXBLInsertionPointEntry* entry =
799 0 : static_cast<nsXBLInsertionPointEntry*>(mInsertionPointTable->Get(&key));
800 :
801 0 : if (!entry) {
802 : // The only insertion point specified was actually a filtered insertion
803 : // point. This means (strictly speaking) that we actually have multiple
804 : // insertion points: the filtered one and a generic insertion point
805 : // (content that doesn't match the filter will just go right underneath the
806 : // bound element).
807 :
808 0 : *aMultipleInsertionPoints = true;
809 0 : *aIndex = 0;
810 0 : return nsnull;
811 : }
812 :
813 0 : *aMultipleInsertionPoints = false;
814 0 : *aIndex = entry->GetInsertionIndex();
815 :
816 0 : nsIContent* templContent = GetImmediateChild(nsGkAtoms::content);
817 0 : nsIContent* content = entry->GetInsertionParent();
818 : nsIContent *realContent = LocateInstance(nsnull, templContent, aCopyRoot,
819 0 : content);
820 :
821 0 : return realContent ? realContent : aBoundElement;
822 : }
823 :
824 : void
825 0 : nsXBLPrototypeBinding::SetBaseTag(PRInt32 aNamespaceID, nsIAtom* aTag)
826 : {
827 0 : mBaseNameSpaceID = aNamespaceID;
828 0 : mBaseTag = aTag;
829 0 : }
830 :
831 : nsIAtom*
832 0 : nsXBLPrototypeBinding::GetBaseTag(PRInt32* aNamespaceID)
833 : {
834 0 : if (mBaseTag) {
835 0 : *aNamespaceID = mBaseNameSpaceID;
836 0 : return mBaseTag;
837 : }
838 :
839 0 : return nsnull;
840 : }
841 :
842 : bool
843 0 : nsXBLPrototypeBinding::ImplementsInterface(REFNSIID aIID) const
844 : {
845 : // Check our IID table.
846 0 : if (mInterfaceTable) {
847 0 : nsIIDKey key(aIID);
848 0 : nsCOMPtr<nsISupports> supports = getter_AddRefs(static_cast<nsISupports*>(mInterfaceTable->Get(&key)));
849 0 : return supports != nsnull;
850 : }
851 :
852 0 : return false;
853 : }
854 :
855 : // Internal helpers ///////////////////////////////////////////////////////////////////////
856 :
857 : nsIContent*
858 0 : nsXBLPrototypeBinding::GetImmediateChild(nsIAtom* aTag)
859 : {
860 0 : for (nsIContent* child = mBinding->GetFirstChild();
861 : child;
862 0 : child = child->GetNextSibling()) {
863 0 : if (child->NodeInfo()->Equals(aTag, kNameSpaceID_XBL)) {
864 0 : return child;
865 : }
866 : }
867 :
868 0 : return nsnull;
869 : }
870 :
871 : nsresult
872 0 : nsXBLPrototypeBinding::InitClass(const nsCString& aClassName,
873 : JSContext * aContext, JSObject * aGlobal,
874 : JSObject * aScriptObject,
875 : JSObject** aClassObject)
876 : {
877 0 : NS_ENSURE_ARG_POINTER(aClassObject);
878 :
879 0 : *aClassObject = nsnull;
880 :
881 : return nsXBLBinding::DoInitJSClass(aContext, aGlobal, aScriptObject,
882 0 : aClassName, this, aClassObject);
883 : }
884 :
885 : nsIContent*
886 0 : nsXBLPrototypeBinding::LocateInstance(nsIContent* aBoundElement,
887 : nsIContent* aTemplRoot,
888 : nsIContent* aCopyRoot,
889 : nsIContent* aTemplChild)
890 : {
891 : // XXX We will get in trouble if the binding instantiation deviates from the template
892 : // in the prototype.
893 0 : if (aTemplChild == aTemplRoot || !aTemplChild)
894 0 : return nsnull;
895 :
896 0 : nsCOMPtr<nsIContent> templParent = aTemplChild->GetParent();
897 0 : nsCOMPtr<nsIContent> childPoint;
898 :
899 : // We may be disconnected from our parent during cycle collection.
900 0 : if (!templParent)
901 0 : return nsnull;
902 :
903 0 : if (aBoundElement) {
904 0 : if (templParent->NodeInfo()->Equals(nsGkAtoms::children,
905 0 : kNameSpaceID_XBL)) {
906 0 : childPoint = templParent;
907 0 : templParent = childPoint->GetParent();
908 : }
909 : }
910 :
911 0 : if (!templParent)
912 0 : return nsnull;
913 :
914 0 : nsIContent* result = nsnull;
915 : nsIContent *copyParent;
916 :
917 0 : if (templParent == aTemplRoot)
918 0 : copyParent = aCopyRoot;
919 : else
920 0 : copyParent = LocateInstance(aBoundElement, aTemplRoot, aCopyRoot, templParent);
921 :
922 0 : if (childPoint && aBoundElement) {
923 : // First we have to locate this insertion point and use its index and its
924 : // count to detemine our precise position within the template.
925 0 : nsIDocument* doc = aBoundElement->OwnerDoc();
926 0 : nsXBLBinding *binding = doc->BindingManager()->GetBinding(aBoundElement);
927 0 : nsIContent *anonContent = nsnull;
928 :
929 0 : while (binding) {
930 0 : anonContent = binding->GetAnonymousContent();
931 0 : if (anonContent)
932 0 : break;
933 :
934 0 : binding = binding->GetBaseBinding();
935 : }
936 :
937 0 : NS_ABORT_IF_FALSE(binding, "Bug 620181 this is unexpected");
938 0 : if (!binding)
939 0 : return nsnull;
940 :
941 0 : nsInsertionPointList* points = nsnull;
942 0 : if (anonContent == copyParent)
943 0 : binding->GetInsertionPointsFor(aBoundElement, &points);
944 : else
945 0 : binding->GetInsertionPointsFor(copyParent, &points);
946 0 : PRInt32 count = points->Length();
947 0 : for (PRInt32 i = 0; i < count; i++) {
948 : // Next we have to find the real insertion point for this proto insertion
949 : // point. If it does not contain any default content, then we should
950 : // return null, since the content is not in the clone.
951 0 : nsXBLInsertionPoint* currPoint = static_cast<nsXBLInsertionPoint*>(points->ElementAt(i));
952 0 : nsCOMPtr<nsIContent> defContent = currPoint->GetDefaultContentTemplate();
953 0 : if (defContent == childPoint) {
954 : // Now check to see if we even built default content at this
955 : // insertion point.
956 0 : defContent = currPoint->GetDefaultContent();
957 0 : if (defContent) {
958 : // Find out the index of the template element within the <children> elt.
959 0 : PRInt32 index = childPoint->IndexOf(aTemplChild);
960 :
961 : // Now we just have to find the corresponding elt underneath the cloned
962 : // default content.
963 0 : result = defContent->GetChildAt(index);
964 : }
965 : break;
966 : }
967 : }
968 : }
969 0 : else if (copyParent)
970 : {
971 0 : PRInt32 index = templParent->IndexOf(aTemplChild);
972 0 : result = copyParent->GetChildAt(index);
973 : }
974 :
975 0 : return result;
976 : }
977 :
978 : struct nsXBLAttrChangeData
979 : {
980 : nsXBLPrototypeBinding* mProto;
981 : nsIContent* mBoundElement;
982 : nsIContent* mContent;
983 : PRInt32 mSrcNamespace;
984 :
985 0 : nsXBLAttrChangeData(nsXBLPrototypeBinding* aProto,
986 : nsIContent* aElt, nsIContent* aContent)
987 0 : :mProto(aProto), mBoundElement(aElt), mContent(aContent) {}
988 : };
989 :
990 : // XXXbz this duplicates lots of AttributeChanged
991 0 : bool SetAttrs(nsHashKey* aKey, void* aData, void* aClosure)
992 : {
993 0 : nsXBLAttributeEntry* entry = static_cast<nsXBLAttributeEntry*>(aData);
994 0 : nsXBLAttrChangeData* changeData = static_cast<nsXBLAttrChangeData*>(aClosure);
995 :
996 0 : nsIAtom* src = entry->GetSrcAttribute();
997 0 : PRInt32 srcNs = changeData->mSrcNamespace;
998 0 : nsAutoString value;
999 0 : bool attrPresent = true;
1000 :
1001 0 : if (src == nsGkAtoms::text && srcNs == kNameSpaceID_XBL) {
1002 : nsContentUtils::GetNodeTextContent(changeData->mBoundElement, false,
1003 0 : value);
1004 0 : value.StripChar(PRUnichar('\n'));
1005 0 : value.StripChar(PRUnichar('\r'));
1006 0 : nsAutoString stripVal(value);
1007 0 : stripVal.StripWhitespace();
1008 :
1009 0 : if (stripVal.IsEmpty())
1010 0 : attrPresent = false;
1011 : }
1012 : else {
1013 0 : attrPresent = changeData->mBoundElement->GetAttr(srcNs, src, value);
1014 : }
1015 :
1016 0 : if (attrPresent) {
1017 : nsIContent* content =
1018 0 : changeData->mProto->GetImmediateChild(nsGkAtoms::content);
1019 :
1020 0 : nsXBLAttributeEntry* curr = entry;
1021 0 : while (curr) {
1022 0 : nsIAtom* dst = curr->GetDstAttribute();
1023 0 : PRInt32 dstNs = curr->GetDstNameSpace();
1024 0 : nsIContent* element = curr->GetElement();
1025 :
1026 : nsIContent *realElement =
1027 : changeData->mProto->LocateInstance(changeData->mBoundElement, content,
1028 0 : changeData->mContent, element);
1029 :
1030 0 : if (realElement) {
1031 0 : realElement->SetAttr(dstNs, dst, value, false);
1032 :
1033 : // XXXndeakin shouldn't this be done in lieu of SetAttr?
1034 0 : if ((dst == nsGkAtoms::text && dstNs == kNameSpaceID_XBL) ||
1035 : (realElement->NodeInfo()->Equals(nsGkAtoms::html,
1036 0 : kNameSpaceID_XUL) &&
1037 0 : dst == nsGkAtoms::value && !value.IsEmpty())) {
1038 :
1039 0 : nsCOMPtr<nsIContent> textContent;
1040 0 : NS_NewTextNode(getter_AddRefs(textContent),
1041 0 : realElement->NodeInfo()->NodeInfoManager());
1042 0 : if (!textContent) {
1043 0 : continue;
1044 : }
1045 :
1046 0 : textContent->SetText(value, false);
1047 0 : realElement->AppendChildTo(textContent, false);
1048 : }
1049 : }
1050 :
1051 0 : curr = curr->GetNext();
1052 : }
1053 : }
1054 :
1055 0 : return true;
1056 : }
1057 :
1058 0 : bool SetAttrsNS(nsHashKey* aKey, void* aData, void* aClosure)
1059 : {
1060 0 : if (aData && aClosure) {
1061 0 : nsPRUint32Key * key = static_cast<nsPRUint32Key*>(aKey);
1062 : nsObjectHashtable* xblAttributes =
1063 0 : static_cast<nsObjectHashtable*>(aData);
1064 : nsXBLAttrChangeData * changeData = static_cast<nsXBLAttrChangeData *>
1065 0 : (aClosure);
1066 0 : changeData->mSrcNamespace = key->GetValue();
1067 0 : xblAttributes->Enumerate(SetAttrs, (void*)changeData);
1068 : }
1069 0 : return true;
1070 : }
1071 :
1072 : void
1073 0 : nsXBLPrototypeBinding::SetInitialAttributes(nsIContent* aBoundElement, nsIContent* aAnonymousContent)
1074 : {
1075 0 : if (mAttributeTable) {
1076 0 : nsXBLAttrChangeData data(this, aBoundElement, aAnonymousContent);
1077 0 : mAttributeTable->Enumerate(SetAttrsNS, (void*)&data);
1078 : }
1079 0 : }
1080 :
1081 : nsIStyleRuleProcessor*
1082 0 : nsXBLPrototypeBinding::GetRuleProcessor()
1083 : {
1084 0 : if (mResources) {
1085 0 : return mResources->mRuleProcessor;
1086 : }
1087 :
1088 0 : return nsnull;
1089 : }
1090 :
1091 : nsXBLPrototypeResources::sheet_array_type*
1092 0 : nsXBLPrototypeBinding::GetStyleSheets()
1093 : {
1094 0 : if (mResources) {
1095 0 : return &mResources->mStyleSheetList;
1096 : }
1097 :
1098 0 : return nsnull;
1099 : }
1100 :
1101 : static bool
1102 0 : DeleteAttributeEntry(nsHashKey* aKey, void* aData, void* aClosure)
1103 : {
1104 0 : nsXBLAttributeEntry::Destroy(static_cast<nsXBLAttributeEntry*>(aData));
1105 0 : return true;
1106 : }
1107 :
1108 : static bool
1109 0 : DeleteAttributeTable(nsHashKey* aKey, void* aData, void* aClosure)
1110 : {
1111 0 : delete static_cast<nsObjectHashtable*>(aData);
1112 0 : return true;
1113 : }
1114 :
1115 : void
1116 0 : nsXBLPrototypeBinding::EnsureAttributeTable()
1117 : {
1118 0 : if (!mAttributeTable) {
1119 : mAttributeTable = new nsObjectHashtable(nsnull, nsnull,
1120 : DeleteAttributeTable,
1121 0 : nsnull, 4);
1122 : }
1123 0 : }
1124 :
1125 : void
1126 0 : nsXBLPrototypeBinding::AddToAttributeTable(PRInt32 aSourceNamespaceID, nsIAtom* aSourceTag,
1127 : PRInt32 aDestNamespaceID, nsIAtom* aDestTag,
1128 : nsIContent* aContent)
1129 : {
1130 0 : nsPRUint32Key nskey(aSourceNamespaceID);
1131 : nsObjectHashtable* attributesNS =
1132 0 : static_cast<nsObjectHashtable*>(mAttributeTable->Get(&nskey));
1133 0 : if (!attributesNS) {
1134 : attributesNS = new nsObjectHashtable(nsnull, nsnull,
1135 : DeleteAttributeEntry,
1136 0 : nsnull, 4);
1137 0 : mAttributeTable->Put(&nskey, attributesNS);
1138 : }
1139 :
1140 : nsXBLAttributeEntry* xblAttr =
1141 0 : nsXBLAttributeEntry::Create(aSourceTag, aDestTag, aDestNamespaceID, aContent);
1142 :
1143 0 : nsISupportsKey key(aSourceTag);
1144 : nsXBLAttributeEntry* entry = static_cast<nsXBLAttributeEntry*>
1145 0 : (attributesNS->Get(&key));
1146 0 : if (!entry) {
1147 0 : attributesNS->Put(&key, xblAttr);
1148 : } else {
1149 0 : while (entry->GetNext())
1150 0 : entry = entry->GetNext();
1151 0 : entry->SetNext(xblAttr);
1152 : }
1153 0 : }
1154 :
1155 : void
1156 0 : nsXBLPrototypeBinding::ConstructAttributeTable(nsIContent* aElement)
1157 : {
1158 : // Don't add entries for <children> elements, since those will get
1159 : // removed from the DOM when we construct the insertion point table.
1160 0 : if (!aElement->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) {
1161 0 : nsAutoString inherits;
1162 0 : aElement->GetAttr(kNameSpaceID_XBL, nsGkAtoms::inherits, inherits);
1163 :
1164 0 : if (!inherits.IsEmpty()) {
1165 0 : EnsureAttributeTable();
1166 :
1167 : // The user specified at least one attribute.
1168 0 : char* str = ToNewCString(inherits);
1169 : char* newStr;
1170 : // XXX We should use a strtok function that tokenizes PRUnichars
1171 : // so that we don't have to convert from Unicode to ASCII and then back
1172 :
1173 0 : char* token = nsCRT::strtok( str, ", ", &newStr );
1174 0 : while( token != NULL ) {
1175 : // Build an atom out of this attribute.
1176 0 : nsCOMPtr<nsIAtom> atom;
1177 0 : PRInt32 atomNsID = kNameSpaceID_None;
1178 0 : nsCOMPtr<nsIAtom> attribute;
1179 0 : PRInt32 attributeNsID = kNameSpaceID_None;
1180 :
1181 : // Figure out if this token contains a :.
1182 0 : nsAutoString attrTok; attrTok.AssignWithConversion(token);
1183 0 : PRInt32 index = attrTok.Find("=", true);
1184 : nsresult rv;
1185 0 : if (index != -1) {
1186 : // This attribute maps to something different.
1187 0 : nsAutoString left, right;
1188 0 : attrTok.Left(left, index);
1189 0 : attrTok.Right(right, attrTok.Length()-index-1);
1190 :
1191 : rv = nsContentUtils::SplitQName(aElement, left, &attributeNsID,
1192 0 : getter_AddRefs(attribute));
1193 0 : if (NS_FAILED(rv))
1194 : return;
1195 :
1196 : rv = nsContentUtils::SplitQName(aElement, right, &atomNsID,
1197 0 : getter_AddRefs(atom));
1198 0 : if (NS_FAILED(rv))
1199 : return;
1200 : }
1201 : else {
1202 0 : nsAutoString tok;
1203 0 : tok.AssignWithConversion(token);
1204 : rv = nsContentUtils::SplitQName(aElement, tok, &atomNsID,
1205 0 : getter_AddRefs(atom));
1206 0 : if (NS_FAILED(rv))
1207 : return;
1208 0 : attribute = atom;
1209 0 : attributeNsID = atomNsID;
1210 : }
1211 :
1212 0 : AddToAttributeTable(atomNsID, atom, attributeNsID, attribute, aElement);
1213 :
1214 : // Now remove the inherits attribute from the element so that it doesn't
1215 : // show up on clones of the element. It is used
1216 : // by the template only, and we don't need it anymore.
1217 : // XXXdwh Don't do this for XUL elements, since it faults them into heavyweight
1218 : // elements. Should nuke from the prototype instead.
1219 : // aElement->UnsetAttr(kNameSpaceID_XBL, nsGkAtoms::inherits, false);
1220 :
1221 0 : token = nsCRT::strtok( newStr, ", ", &newStr );
1222 : }
1223 :
1224 0 : nsMemory::Free(str);
1225 : }
1226 : }
1227 :
1228 : // Recur into our children.
1229 0 : for (nsIContent* child = aElement->GetFirstChild();
1230 : child;
1231 0 : child = child->GetNextSibling()) {
1232 0 : ConstructAttributeTable(child);
1233 : }
1234 : }
1235 :
1236 : static bool
1237 0 : DeleteInsertionPointEntry(nsHashKey* aKey, void* aData, void* aClosure)
1238 : {
1239 0 : static_cast<nsXBLInsertionPointEntry*>(aData)->Release();
1240 0 : return true;
1241 : }
1242 :
1243 : void
1244 0 : nsXBLPrototypeBinding::ConstructInsertionTable(nsIContent* aContent)
1245 : {
1246 0 : nsCOMArray<nsIContent> childrenElements;
1247 : GetNestedChildren(nsGkAtoms::children, kNameSpaceID_XBL, aContent,
1248 0 : childrenElements);
1249 :
1250 0 : PRInt32 count = childrenElements.Count();
1251 0 : if (count == 0)
1252 : return;
1253 :
1254 : mInsertionPointTable = new nsObjectHashtable(nsnull, nsnull,
1255 : DeleteInsertionPointEntry,
1256 0 : nsnull, 4);
1257 0 : if (!mInsertionPointTable)
1258 : return;
1259 :
1260 : PRInt32 i;
1261 0 : for (i = 0; i < count; i++) {
1262 0 : nsIContent* child = childrenElements[i];
1263 0 : nsCOMPtr<nsIContent> parent = child->GetParent();
1264 :
1265 : // Create an XBL insertion point entry.
1266 0 : nsXBLInsertionPointEntry* xblIns = nsXBLInsertionPointEntry::Create(parent);
1267 :
1268 0 : nsAutoString includes;
1269 0 : child->GetAttr(kNameSpaceID_None, nsGkAtoms::includes, includes);
1270 0 : if (includes.IsEmpty()) {
1271 0 : nsISupportsKey key(nsGkAtoms::children);
1272 0 : xblIns->AddRef();
1273 0 : mInsertionPointTable->Put(&key, xblIns);
1274 : }
1275 : else {
1276 : // The user specified at least one attribute.
1277 0 : char* str = ToNewCString(includes);
1278 : char* newStr;
1279 : // XXX We should use a strtok function that tokenizes PRUnichar's
1280 : // so that we don't have to convert from Unicode to ASCII and then back
1281 :
1282 0 : char* token = nsCRT::strtok( str, "| ", &newStr );
1283 0 : while( token != NULL ) {
1284 0 : nsAutoString tok;
1285 0 : tok.AssignWithConversion(token);
1286 :
1287 : // Build an atom out of this string.
1288 0 : nsCOMPtr<nsIAtom> atom = do_GetAtom(tok);
1289 :
1290 0 : nsISupportsKey key(atom);
1291 0 : xblIns->AddRef();
1292 0 : mInsertionPointTable->Put(&key, xblIns);
1293 :
1294 0 : token = nsCRT::strtok( newStr, "| ", &newStr );
1295 : }
1296 :
1297 0 : nsMemory::Free(str);
1298 : }
1299 :
1300 : // Compute the index of the <children> element. This index is
1301 : // equal to the index of the <children> in the template minus the #
1302 : // of previous insertion point siblings removed. Because our childrenElements
1303 : // array was built in a DFS that went from left-to-right through siblings,
1304 : // if we dynamically obtain our index each time, then the removals of previous
1305 : // siblings will cause the index to adjust (and we won't have to take that into
1306 : // account explicitly).
1307 0 : PRInt32 index = parent->IndexOf(child);
1308 0 : xblIns->SetInsertionIndex((PRUint32)index);
1309 :
1310 : // Now remove the <children> element from the template. This ensures that the
1311 : // binding instantiation will not contain a clone of the <children> element when
1312 : // it clones the binding template.
1313 0 : parent->RemoveChildAt(index, false);
1314 :
1315 : // See if the insertion point contains default content. Default content must
1316 : // be cached in our insertion point entry, since it will need to be cloned
1317 : // in situations where no content ends up being placed at the insertion point.
1318 0 : PRUint32 defaultCount = child->GetChildCount();
1319 0 : if (defaultCount > 0) {
1320 0 : nsAutoScriptBlocker scriptBlocker;
1321 : // Annotate the insertion point with our default content.
1322 0 : xblIns->SetDefaultContent(child);
1323 :
1324 : // Reconnect back to our parent for access later. This makes "inherits" easier
1325 : // to work with on default content.
1326 : // XXXbz this is somewhat screwed up, since it's sort of like anonymous
1327 : // content... but not.
1328 : nsresult rv =
1329 0 : child->BindToTree(parent->GetCurrentDoc(), parent, nsnull, false);
1330 0 : if (NS_FAILED(rv)) {
1331 : // Well... now what? Just unbind and bail out, I guess...
1332 : // XXXbz This really shouldn't be a void method!
1333 0 : child->UnbindFromTree();
1334 : return;
1335 : }
1336 : }
1337 : }
1338 : }
1339 :
1340 : nsresult
1341 0 : nsXBLPrototypeBinding::ConstructInterfaceTable(const nsAString& aImpls)
1342 : {
1343 0 : if (!aImpls.IsEmpty()) {
1344 : // Obtain the interface info manager that can tell us the IID
1345 : // for a given interface name.
1346 : nsCOMPtr<nsIInterfaceInfoManager>
1347 0 : infoManager(do_GetService(NS_INTERFACEINFOMANAGER_SERVICE_CONTRACTID));
1348 0 : if (!infoManager)
1349 0 : return NS_ERROR_FAILURE;
1350 :
1351 : // Create the table.
1352 0 : if (!mInterfaceTable)
1353 0 : mInterfaceTable = new nsSupportsHashtable(4);
1354 :
1355 : // The user specified at least one attribute.
1356 0 : NS_ConvertUTF16toUTF8 utf8impl(aImpls);
1357 0 : char* str = utf8impl.BeginWriting();
1358 : char* newStr;
1359 : // XXX We should use a strtok function that tokenizes PRUnichars
1360 : // so that we don't have to convert from Unicode to ASCII and then back
1361 :
1362 0 : char* token = nsCRT::strtok( str, ", ", &newStr );
1363 0 : while( token != NULL ) {
1364 : // get the InterfaceInfo for the name
1365 0 : nsCOMPtr<nsIInterfaceInfo> iinfo;
1366 0 : infoManager->GetInfoForName(token, getter_AddRefs(iinfo));
1367 :
1368 0 : if (iinfo) {
1369 : // obtain an IID.
1370 0 : const nsIID* iid = nsnull;
1371 0 : iinfo->GetIIDShared(&iid);
1372 :
1373 0 : if (iid) {
1374 : // We found a valid iid. Add it to our table.
1375 0 : nsIIDKey key(*iid);
1376 0 : mInterfaceTable->Put(&key, mBinding);
1377 :
1378 : // this block adds the parent interfaces of each interface
1379 : // defined in the xbl definition (implements="nsI...")
1380 0 : nsCOMPtr<nsIInterfaceInfo> parentInfo;
1381 : // if it has a parent, add it to the table
1382 0 : while (NS_SUCCEEDED(iinfo->GetParent(getter_AddRefs(parentInfo))) && parentInfo) {
1383 : // get the iid
1384 0 : parentInfo->GetIIDShared(&iid);
1385 :
1386 : // don't add nsISupports to the table
1387 0 : if (!iid || iid->Equals(NS_GET_IID(nsISupports)))
1388 0 : break;
1389 :
1390 : // add the iid to the table
1391 0 : nsIIDKey parentKey(*iid);
1392 0 : mInterfaceTable->Put(&parentKey, mBinding);
1393 :
1394 : // look for the next parent
1395 0 : iinfo = parentInfo;
1396 : }
1397 : }
1398 : }
1399 :
1400 0 : token = nsCRT::strtok( newStr, ", ", &newStr );
1401 : }
1402 : }
1403 :
1404 0 : return NS_OK;
1405 : }
1406 :
1407 : void
1408 0 : nsXBLPrototypeBinding::GetNestedChildren(nsIAtom* aTag, PRInt32 aNamespace,
1409 : nsIContent* aContent,
1410 : nsCOMArray<nsIContent> & aList)
1411 : {
1412 0 : for (nsIContent* child = aContent->GetFirstChild();
1413 : child;
1414 0 : child = child->GetNextSibling()) {
1415 :
1416 0 : if (child->NodeInfo()->Equals(aTag, aNamespace)) {
1417 0 : aList.AppendObject(child);
1418 : }
1419 : else
1420 0 : GetNestedChildren(aTag, aNamespace, child, aList);
1421 : }
1422 0 : }
1423 :
1424 : nsresult
1425 0 : nsXBLPrototypeBinding::AddResourceListener(nsIContent* aBoundElement)
1426 : {
1427 0 : if (!mResources)
1428 0 : return NS_ERROR_FAILURE; // Makes no sense to add a listener when the binding
1429 : // has no resources.
1430 :
1431 0 : mResources->AddResourceListener(aBoundElement);
1432 0 : return NS_OK;
1433 : }
1434 :
1435 : void
1436 0 : nsXBLPrototypeBinding::CreateKeyHandlers()
1437 : {
1438 0 : nsXBLPrototypeHandler* curr = mPrototypeHandler;
1439 0 : while (curr) {
1440 0 : nsCOMPtr<nsIAtom> eventAtom = curr->GetEventName();
1441 0 : if (eventAtom == nsGkAtoms::keyup ||
1442 0 : eventAtom == nsGkAtoms::keydown ||
1443 0 : eventAtom == nsGkAtoms::keypress) {
1444 0 : PRUint8 phase = curr->GetPhase();
1445 0 : PRUint8 type = curr->GetType();
1446 :
1447 0 : PRInt32 count = mKeyHandlers.Count();
1448 : PRInt32 i;
1449 0 : nsXBLKeyEventHandler* handler = nsnull;
1450 0 : for (i = 0; i < count; ++i) {
1451 0 : handler = mKeyHandlers[i];
1452 0 : if (handler->Matches(eventAtom, phase, type))
1453 0 : break;
1454 : }
1455 :
1456 0 : if (i == count) {
1457 0 : nsRefPtr<nsXBLKeyEventHandler> newHandler;
1458 : NS_NewXBLKeyEventHandler(eventAtom, phase, type,
1459 0 : getter_AddRefs(newHandler));
1460 0 : if (newHandler)
1461 0 : mKeyHandlers.AppendObject(newHandler);
1462 0 : handler = newHandler;
1463 : }
1464 :
1465 0 : if (handler)
1466 0 : handler->AddProtoHandler(curr);
1467 : }
1468 :
1469 0 : curr = curr->GetNextHandler();
1470 : }
1471 0 : }
1472 :
1473 : class XBLPrototypeSetupCleanup
1474 : {
1475 : public:
1476 0 : XBLPrototypeSetupCleanup(nsXBLDocumentInfo* aDocInfo, const nsACString& aID)
1477 0 : : mDocInfo(aDocInfo), mID(aID) {}
1478 :
1479 0 : ~XBLPrototypeSetupCleanup()
1480 0 : {
1481 0 : if (mDocInfo) {
1482 0 : mDocInfo->RemovePrototypeBinding(mID);
1483 : }
1484 0 : }
1485 :
1486 0 : void Disconnect()
1487 : {
1488 0 : mDocInfo = nsnull;
1489 0 : }
1490 :
1491 : nsXBLDocumentInfo* mDocInfo;
1492 : nsCAutoString mID;
1493 : };
1494 :
1495 : nsresult
1496 0 : nsXBLPrototypeBinding::Read(nsIObjectInputStream* aStream,
1497 : nsXBLDocumentInfo* aDocInfo,
1498 : nsIDocument* aDocument,
1499 : PRUint8 aFlags)
1500 : {
1501 0 : mInheritStyle = (aFlags & XBLBinding_Serialize_InheritStyle) ? true : false;
1502 :
1503 : // nsXBLContentSink::ConstructBinding doesn't create a binding with an empty
1504 : // id, so we don't here either.
1505 0 : nsCAutoString id;
1506 0 : nsresult rv = aStream->ReadCString(id);
1507 :
1508 0 : NS_ENSURE_SUCCESS(rv, rv);
1509 0 : NS_ENSURE_TRUE(!id.IsEmpty(), NS_ERROR_FAILURE);
1510 :
1511 0 : nsCAutoString baseBindingURI;
1512 0 : rv = aStream->ReadCString(baseBindingURI);
1513 0 : NS_ENSURE_SUCCESS(rv, rv);
1514 0 : mCheckedBaseProto = true;
1515 :
1516 0 : if (!baseBindingURI.IsEmpty()) {
1517 0 : rv = NS_NewURI(getter_AddRefs(mBaseBindingURI), baseBindingURI);
1518 0 : NS_ENSURE_SUCCESS(rv, rv);
1519 : }
1520 :
1521 0 : rv = ReadNamespace(aStream, mBaseNameSpaceID);
1522 0 : NS_ENSURE_SUCCESS(rv, rv);
1523 :
1524 0 : nsAutoString baseTag;
1525 0 : rv = aStream->ReadString(baseTag);
1526 0 : NS_ENSURE_SUCCESS(rv, rv);
1527 0 : if (!baseTag.IsEmpty()) {
1528 0 : mBaseTag = do_GetAtom(baseTag);
1529 : }
1530 :
1531 0 : aDocument->CreateElem(NS_LITERAL_STRING("binding"), nsnull, kNameSpaceID_XBL,
1532 0 : getter_AddRefs(mBinding));
1533 :
1534 0 : nsCOMPtr<nsIContent> child;
1535 0 : rv = ReadContentNode(aStream, aDocument, aDocument->NodeInfoManager(), getter_AddRefs(child));
1536 0 : NS_ENSURE_SUCCESS(rv, rv);
1537 :
1538 0 : Element* rootElement = aDocument->GetRootElement();
1539 0 : if (rootElement)
1540 0 : rootElement->AppendChildTo(mBinding, false);
1541 :
1542 0 : if (child) {
1543 0 : mBinding->AppendChildTo(child, false);
1544 : }
1545 :
1546 : PRUint32 interfaceCount;
1547 0 : rv = aStream->Read32(&interfaceCount);
1548 0 : NS_ENSURE_SUCCESS(rv, rv);
1549 :
1550 0 : if (interfaceCount > 0) {
1551 0 : NS_ASSERTION(!mInterfaceTable, "non-null mInterfaceTable");
1552 0 : mInterfaceTable = new nsSupportsHashtable(interfaceCount);
1553 0 : NS_ENSURE_TRUE(mInterfaceTable, NS_ERROR_OUT_OF_MEMORY);
1554 :
1555 0 : for (; interfaceCount > 0; interfaceCount--) {
1556 : nsIID iid;
1557 0 : aStream->ReadID(&iid);
1558 0 : nsIIDKey key(iid);
1559 0 : mInterfaceTable->Put(&key, mBinding);
1560 : }
1561 : }
1562 :
1563 0 : nsCOMPtr<nsIScriptGlobalObjectOwner> globalOwner(do_QueryObject(aDocInfo));
1564 0 : nsIScriptGlobalObject* globalObject = globalOwner->GetScriptGlobalObject();
1565 0 : NS_ENSURE_TRUE(globalObject, NS_ERROR_UNEXPECTED);
1566 :
1567 0 : nsIScriptContext *context = globalObject->GetContext();
1568 0 : NS_ENSURE_TRUE(context, NS_ERROR_FAILURE);
1569 :
1570 0 : bool isFirstBinding = aFlags & XBLBinding_Serialize_IsFirstBinding;
1571 0 : rv = Init(id, aDocInfo, nsnull, isFirstBinding);
1572 0 : NS_ENSURE_SUCCESS(rv, rv);
1573 :
1574 : // We need to set the prototype binding before reading the nsXBLProtoImpl,
1575 : // as it may be retrieved within.
1576 0 : rv = aDocInfo->SetPrototypeBinding(id, this);
1577 0 : NS_ENSURE_SUCCESS(rv, rv);
1578 :
1579 0 : XBLPrototypeSetupCleanup cleanup(aDocInfo, id);
1580 :
1581 0 : nsCAutoString className;
1582 0 : rv = aStream->ReadCString(className);
1583 0 : NS_ENSURE_SUCCESS(rv, rv);
1584 :
1585 0 : if (!className.IsEmpty()) {
1586 : nsXBLProtoImpl* impl; // NS_NewXBLProtoImpl will set mImplementation for us
1587 0 : NS_NewXBLProtoImpl(this, NS_ConvertUTF8toUTF16(className).get(), &impl);
1588 :
1589 : // This needs to happen after SetPrototypeBinding as calls are made to
1590 : // retrieve the mapped bindings from within here. However, if an error
1591 : // occurs, the mapping should be removed again so that we don't keep an
1592 : // invalid binding around.
1593 0 : rv = mImplementation->Read(context, aStream, this, globalObject);
1594 0 : NS_ENSURE_SUCCESS(rv, rv);
1595 : }
1596 :
1597 : // Next read in the handlers.
1598 0 : nsXBLPrototypeHandler* previousHandler = nsnull;
1599 :
1600 0 : do {
1601 : XBLBindingSerializeDetails type;
1602 0 : rv = aStream->Read8(&type);
1603 0 : NS_ENSURE_SUCCESS(rv, rv);
1604 :
1605 0 : if (type == XBLBinding_Serialize_NoMoreItems)
1606 0 : break;
1607 :
1608 0 : NS_ASSERTION((type & XBLBinding_Serialize_Mask) == XBLBinding_Serialize_Handler,
1609 : "invalid handler type");
1610 :
1611 0 : nsXBLPrototypeHandler* handler = new nsXBLPrototypeHandler(this);
1612 0 : rv = handler->Read(context, aStream);
1613 0 : if (NS_FAILED(rv)) {
1614 0 : delete handler;
1615 0 : return rv;
1616 : }
1617 :
1618 0 : if (previousHandler) {
1619 0 : previousHandler->SetNextHandler(handler);
1620 : }
1621 : else {
1622 0 : SetPrototypeHandlers(handler);
1623 : }
1624 0 : previousHandler = handler;
1625 : } while (1);
1626 :
1627 : // Finally, read in the resources.
1628 0 : do {
1629 : XBLBindingSerializeDetails type;
1630 0 : rv = aStream->Read8(&type);
1631 0 : NS_ENSURE_SUCCESS(rv, rv);
1632 :
1633 0 : if (type == XBLBinding_Serialize_NoMoreItems)
1634 : break;
1635 :
1636 0 : NS_ASSERTION((type & XBLBinding_Serialize_Mask) == XBLBinding_Serialize_Stylesheet ||
1637 : (type & XBLBinding_Serialize_Mask) == XBLBinding_Serialize_Image, "invalid resource type");
1638 :
1639 0 : nsAutoString src;
1640 0 : rv = aStream->ReadString(src);
1641 0 : NS_ENSURE_SUCCESS(rv, rv);
1642 :
1643 : AddResource(type == XBLBinding_Serialize_Stylesheet ? nsGkAtoms::stylesheet :
1644 0 : nsGkAtoms::image, src);
1645 : } while (1);
1646 :
1647 0 : if (isFirstBinding) {
1648 0 : aDocInfo->SetFirstPrototypeBinding(this);
1649 : }
1650 :
1651 0 : cleanup.Disconnect();
1652 0 : return NS_OK;
1653 : }
1654 :
1655 : static
1656 : bool
1657 0 : GatherInsertionPoints(nsHashKey *aKey, void *aData, void* aClosure)
1658 : {
1659 : ArrayOfInsertionPointsByContent* insertionPointsByContent =
1660 0 : static_cast<ArrayOfInsertionPointsByContent *>(aClosure);
1661 :
1662 0 : nsXBLInsertionPointEntry* entry = static_cast<nsXBLInsertionPointEntry *>(aData);
1663 :
1664 : // Add a new blank array if it isn't already there.
1665 : nsAutoTArray<InsertionItem, 1>* list;
1666 0 : if (!insertionPointsByContent->Get(entry->GetInsertionParent(), &list)) {
1667 0 : list = new nsAutoTArray<InsertionItem, 1>;
1668 0 : insertionPointsByContent->Put(entry->GetInsertionParent(), list);
1669 : }
1670 :
1671 : // Add the item to the array and ensure it stays sorted by insertion index.
1672 : nsIAtom* atom = static_cast<nsIAtom *>(
1673 0 : static_cast<nsISupportsKey *>(aKey)->GetValue());
1674 0 : InsertionItem newitem(entry->GetInsertionIndex(), atom, entry->GetDefaultContent());
1675 0 : list->InsertElementSorted(newitem);
1676 :
1677 0 : return kHashEnumerateNext;
1678 : }
1679 :
1680 : static
1681 : bool
1682 0 : WriteInterfaceID(nsHashKey *aKey, void *aData, void* aClosure)
1683 : {
1684 : // We can just write out the ids. The cache will be invalidated when a
1685 : // different build is used, so we don't need to worry about ids changing.
1686 0 : nsID iid = ((nsIIDKey *)aKey)->mKey;
1687 0 : static_cast<nsIObjectOutputStream *>(aClosure)->WriteID(iid);
1688 0 : return kHashEnumerateNext;
1689 : }
1690 :
1691 : nsresult
1692 0 : nsXBLPrototypeBinding::Write(nsIObjectOutputStream* aStream)
1693 : {
1694 : // This writes out the binding. Note that mCheckedBaseProto,
1695 : // mKeyHandlersRegistered and mKeyHandlers are not serialized as they are
1696 : // computed on demand.
1697 :
1698 0 : nsCOMPtr<nsIScriptGlobalObjectOwner> globalOwner(do_QueryObject(mXBLDocInfoWeak));
1699 0 : nsIScriptGlobalObject* globalObject = globalOwner->GetScriptGlobalObject();
1700 0 : NS_ENSURE_TRUE(globalObject, NS_ERROR_UNEXPECTED);
1701 :
1702 0 : nsIScriptContext *context = globalObject->GetContext();
1703 0 : NS_ENSURE_TRUE(context, NS_ERROR_FAILURE);
1704 :
1705 0 : PRUint8 flags = mInheritStyle ? XBLBinding_Serialize_InheritStyle : 0;
1706 :
1707 : // mAlternateBindingURI is only set on the first binding.
1708 0 : if (mAlternateBindingURI) {
1709 0 : flags |= XBLBinding_Serialize_IsFirstBinding;
1710 : }
1711 :
1712 0 : nsresult rv = aStream->Write8(flags);
1713 0 : NS_ENSURE_SUCCESS(rv, rv);
1714 :
1715 0 : nsCAutoString id;
1716 0 : mBindingURI->GetRef(id);
1717 0 : rv = aStream->WriteStringZ(id.get());
1718 0 : NS_ENSURE_SUCCESS(rv, rv);
1719 :
1720 : // write out the extends and display attribute values
1721 0 : nsCAutoString extends;
1722 0 : ResolveBaseBinding();
1723 0 : if (mBaseBindingURI)
1724 0 : mBaseBindingURI->GetSpec(extends);
1725 :
1726 0 : rv = aStream->WriteStringZ(extends.get());
1727 0 : NS_ENSURE_SUCCESS(rv, rv);
1728 :
1729 0 : rv = WriteNamespace(aStream, mBaseNameSpaceID);
1730 0 : NS_ENSURE_SUCCESS(rv, rv);
1731 :
1732 0 : nsAutoString baseTag;
1733 0 : if (mBaseTag) {
1734 0 : mBaseTag->ToString(baseTag);
1735 : }
1736 0 : rv = aStream->WriteWStringZ(baseTag.get());
1737 0 : NS_ENSURE_SUCCESS(rv, rv);
1738 :
1739 : // The binding holds insertions points keyed by the tag that is going to be
1740 : // inserted, however, the cache would prefer to know insertion points keyed
1741 : // by where they are in the content hierarchy. GatherInsertionPoints is used
1742 : // to iterate over the insertion points and store them temporarily in this
1743 : // latter hashtable.
1744 0 : ArrayOfInsertionPointsByContent insertionPointsByContent;
1745 0 : insertionPointsByContent.Init();
1746 0 : if (mInsertionPointTable) {
1747 0 : mInsertionPointTable->Enumerate(GatherInsertionPoints, &insertionPointsByContent);
1748 : }
1749 :
1750 0 : nsIContent* content = GetImmediateChild(nsGkAtoms::content);
1751 0 : if (content) {
1752 0 : rv = WriteContentNode(aStream, content, insertionPointsByContent);
1753 0 : NS_ENSURE_SUCCESS(rv, rv);
1754 : }
1755 : else {
1756 : // Write a marker to indicate that there is no content.
1757 0 : rv = aStream->Write8(XBLBinding_Serialize_NoContent);
1758 0 : NS_ENSURE_SUCCESS(rv, rv);
1759 : }
1760 :
1761 : // Enumerate and write out the implemented interfaces.
1762 0 : if (mInterfaceTable) {
1763 0 : rv = aStream->Write32(mInterfaceTable->Count());
1764 0 : NS_ENSURE_SUCCESS(rv, rv);
1765 :
1766 0 : mInterfaceTable->Enumerate(WriteInterfaceID, aStream);
1767 : }
1768 : else {
1769 0 : rv = aStream->Write32(0);
1770 0 : NS_ENSURE_SUCCESS(rv, rv);
1771 : }
1772 :
1773 : // Write out the implementation details.
1774 0 : if (mImplementation) {
1775 0 : rv = mImplementation->Write(context, aStream, this);
1776 0 : NS_ENSURE_SUCCESS(rv, rv);
1777 : }
1778 : else {
1779 : // Write out an empty classname. This indicates that the binding does not
1780 : // define an implementation.
1781 0 : rv = aStream->WriteWStringZ(EmptyString().get());
1782 0 : NS_ENSURE_SUCCESS(rv, rv);
1783 : }
1784 :
1785 : // Write out the handlers.
1786 0 : nsXBLPrototypeHandler* handler = mPrototypeHandler;
1787 0 : while (handler) {
1788 0 : rv = handler->Write(context, aStream);
1789 0 : NS_ENSURE_SUCCESS(rv, rv);
1790 :
1791 0 : handler = handler->GetNextHandler();
1792 : }
1793 :
1794 0 : aStream->Write8(XBLBinding_Serialize_NoMoreItems);
1795 0 : NS_ENSURE_SUCCESS(rv, rv);
1796 :
1797 : // Write out the resources
1798 0 : if (mResources) {
1799 0 : rv = mResources->Write(aStream);
1800 0 : NS_ENSURE_SUCCESS(rv, rv);
1801 : }
1802 :
1803 : // Write out an end mark at the end.
1804 0 : return aStream->Write8(XBLBinding_Serialize_NoMoreItems);
1805 : }
1806 :
1807 : nsresult
1808 0 : nsXBLPrototypeBinding::ReadContentNode(nsIObjectInputStream* aStream,
1809 : nsIDocument* aDocument,
1810 : nsNodeInfoManager* aNim,
1811 : nsIContent** aContent)
1812 : {
1813 0 : *aContent = nsnull;
1814 :
1815 : PRInt32 namespaceID;
1816 0 : nsresult rv = ReadNamespace(aStream, namespaceID);
1817 0 : NS_ENSURE_SUCCESS(rv, rv);
1818 :
1819 : // There is no content to read so just return.
1820 0 : if (namespaceID == XBLBinding_Serialize_NoContent)
1821 0 : return NS_OK;
1822 :
1823 0 : nsCOMPtr<nsIContent> content;
1824 :
1825 : // If this is a text type, just read the string and return.
1826 0 : if (namespaceID == XBLBinding_Serialize_TextNode ||
1827 : namespaceID == XBLBinding_Serialize_CDATANode ||
1828 : namespaceID == XBLBinding_Serialize_CommentNode) {
1829 0 : switch (namespaceID) {
1830 : case XBLBinding_Serialize_TextNode:
1831 0 : rv = NS_NewTextNode(getter_AddRefs(content), aNim);
1832 0 : break;
1833 : case XBLBinding_Serialize_CDATANode:
1834 0 : rv = NS_NewXMLCDATASection(getter_AddRefs(content), aNim);
1835 0 : break;
1836 : case XBLBinding_Serialize_CommentNode:
1837 0 : rv = NS_NewCommentNode(getter_AddRefs(content), aNim);
1838 0 : break;
1839 : default:
1840 0 : break;
1841 : }
1842 0 : NS_ENSURE_SUCCESS(rv, rv);
1843 :
1844 0 : nsAutoString text;
1845 0 : rv = aStream->ReadString(text);
1846 0 : NS_ENSURE_SUCCESS(rv, rv);
1847 :
1848 0 : content->SetText(text, false);
1849 0 : content.swap(*aContent);
1850 0 : return NS_OK;
1851 : }
1852 :
1853 : // Otherwise, it's an element, so read its tag, attributes and children.
1854 0 : nsAutoString prefix, tag;
1855 0 : rv = aStream->ReadString(prefix);
1856 0 : NS_ENSURE_SUCCESS(rv, rv);
1857 :
1858 0 : nsCOMPtr<nsIAtom> prefixAtom;
1859 0 : if (!prefix.IsEmpty())
1860 0 : prefixAtom = do_GetAtom(prefix);
1861 :
1862 0 : rv = aStream->ReadString(tag);
1863 0 : NS_ENSURE_SUCCESS(rv, rv);
1864 :
1865 0 : nsCOMPtr<nsIAtom> tagAtom = do_GetAtom(tag);
1866 : nsCOMPtr<nsINodeInfo> nodeInfo =
1867 0 : aNim->GetNodeInfo(tagAtom, prefixAtom, namespaceID, nsIDOMNode::ELEMENT_NODE);
1868 :
1869 : PRUint32 attrCount;
1870 0 : rv = aStream->Read32(&attrCount);
1871 0 : NS_ENSURE_SUCCESS(rv, rv);
1872 :
1873 : // Create XUL prototype elements, or regular elements for other namespaces.
1874 : // This needs to match the code in nsXBLContentSink::CreateElement.
1875 : #ifdef MOZ_XUL
1876 0 : if (namespaceID == kNameSpaceID_XUL) {
1877 0 : nsIURI* documentURI = aDocument->GetDocumentURI();
1878 :
1879 0 : nsRefPtr<nsXULPrototypeElement> prototype = new nsXULPrototypeElement();
1880 0 : NS_ENSURE_TRUE(prototype, NS_ERROR_OUT_OF_MEMORY);
1881 :
1882 0 : prototype->mNodeInfo = nodeInfo;
1883 0 : prototype->mScriptTypeID = nsIProgrammingLanguage::JAVASCRIPT;
1884 :
1885 0 : nsCOMPtr<Element> result;
1886 : nsresult rv =
1887 0 : nsXULElement::Create(prototype, aDocument, false, getter_AddRefs(result));
1888 0 : NS_ENSURE_SUCCESS(rv, rv);
1889 0 : content = result;
1890 :
1891 0 : nsXULPrototypeAttribute* attrs = nsnull;
1892 0 : if (attrCount > 0) {
1893 0 : attrs = new nsXULPrototypeAttribute[attrCount];
1894 : }
1895 :
1896 0 : prototype->mAttributes = attrs;
1897 0 : prototype->mNumAttributes = attrCount;
1898 :
1899 0 : for (PRUint32 i = 0; i < attrCount; i++) {
1900 0 : rv = ReadNamespace(aStream, namespaceID);
1901 0 : NS_ENSURE_SUCCESS(rv, rv);
1902 :
1903 0 : nsAutoString prefix, name, val;
1904 0 : rv = aStream->ReadString(prefix);
1905 0 : NS_ENSURE_SUCCESS(rv, rv);
1906 0 : rv = aStream->ReadString(name);
1907 0 : NS_ENSURE_SUCCESS(rv, rv);
1908 0 : rv = aStream->ReadString(val);
1909 0 : NS_ENSURE_SUCCESS(rv, rv);
1910 :
1911 0 : nsCOMPtr<nsIAtom> nameAtom = do_GetAtom(name);
1912 0 : if (namespaceID == kNameSpaceID_None) {
1913 0 : attrs[i].mName.SetTo(nameAtom);
1914 : }
1915 : else {
1916 0 : nsCOMPtr<nsIAtom> prefixAtom;
1917 0 : if (!prefix.IsEmpty())
1918 0 : prefixAtom = do_GetAtom(prefix);
1919 :
1920 : nsCOMPtr<nsINodeInfo> ni =
1921 : aNim->GetNodeInfo(nameAtom, prefixAtom,
1922 0 : namespaceID, nsIDOMNode::ATTRIBUTE_NODE);
1923 0 : attrs[i].mName.SetTo(ni);
1924 : }
1925 :
1926 0 : rv = prototype->SetAttrAt(i, val, documentURI);
1927 0 : NS_ENSURE_SUCCESS(rv, rv);
1928 : }
1929 : }
1930 : else {
1931 : #endif
1932 0 : NS_NewElement(getter_AddRefs(content), nodeInfo.forget(), NOT_FROM_PARSER);
1933 :
1934 0 : for (PRUint32 i = 0; i < attrCount; i++) {
1935 0 : rv = ReadNamespace(aStream, namespaceID);
1936 0 : NS_ENSURE_SUCCESS(rv, rv);
1937 :
1938 0 : nsAutoString prefix, name, val;
1939 0 : rv = aStream->ReadString(prefix);
1940 0 : NS_ENSURE_SUCCESS(rv, rv);
1941 0 : rv = aStream->ReadString(name);
1942 0 : NS_ENSURE_SUCCESS(rv, rv);
1943 0 : rv = aStream->ReadString(val);
1944 0 : NS_ENSURE_SUCCESS(rv, rv);
1945 :
1946 0 : nsCOMPtr<nsIAtom> prefixAtom;
1947 0 : if (!prefix.IsEmpty())
1948 0 : prefixAtom = do_GetAtom(prefix);
1949 :
1950 0 : nsCOMPtr<nsIAtom> nameAtom = do_GetAtom(name);
1951 0 : content->SetAttr(namespaceID, nameAtom, prefixAtom, val, false);
1952 : }
1953 :
1954 : #ifdef MOZ_XUL
1955 : }
1956 : #endif
1957 :
1958 : // Now read the attribute forwarding entries (xbl:inherits)
1959 :
1960 : PRInt32 srcNamespaceID, destNamespaceID;
1961 0 : rv = ReadNamespace(aStream, srcNamespaceID);
1962 0 : NS_ENSURE_SUCCESS(rv, rv);
1963 :
1964 0 : while (srcNamespaceID != XBLBinding_Serialize_NoMoreAttributes) {
1965 0 : nsAutoString srcAttribute, destAttribute;
1966 0 : rv = aStream->ReadString(srcAttribute);
1967 0 : NS_ENSURE_SUCCESS(rv, rv);
1968 0 : rv = ReadNamespace(aStream, destNamespaceID);
1969 0 : NS_ENSURE_SUCCESS(rv, rv);
1970 0 : rv = aStream->ReadString(destAttribute);
1971 0 : NS_ENSURE_SUCCESS(rv, rv);
1972 :
1973 0 : nsCOMPtr<nsIAtom> srcAtom = do_GetAtom(srcAttribute);
1974 0 : nsCOMPtr<nsIAtom> destAtom = do_GetAtom(destAttribute);
1975 :
1976 0 : EnsureAttributeTable();
1977 0 : AddToAttributeTable(srcNamespaceID, srcAtom, destNamespaceID, destAtom, content);
1978 :
1979 0 : rv = ReadNamespace(aStream, srcNamespaceID);
1980 0 : NS_ENSURE_SUCCESS(rv, rv);
1981 : }
1982 :
1983 : // Next, read the insertion points.
1984 : PRUint32 insertionPointIndex;
1985 0 : rv = aStream->Read32(&insertionPointIndex);
1986 0 : NS_ENSURE_SUCCESS(rv, rv);
1987 0 : while (insertionPointIndex != XBLBinding_Serialize_NoMoreInsertionPoints) {
1988 : nsRefPtr<nsXBLInsertionPointEntry> xblIns =
1989 0 : nsXBLInsertionPointEntry::Create(content);
1990 0 : xblIns->SetInsertionIndex(insertionPointIndex);
1991 :
1992 : // If the insertion point has default content, read it.
1993 0 : nsCOMPtr<nsIContent> defaultContent;
1994 0 : rv = ReadContentNode(aStream, aDocument, aNim, getter_AddRefs(defaultContent));
1995 0 : NS_ENSURE_SUCCESS(rv, rv);
1996 :
1997 0 : if (defaultContent) {
1998 0 : xblIns->SetDefaultContent(defaultContent);
1999 :
2000 0 : rv = defaultContent->BindToTree(nsnull, content, nsnull, false);
2001 0 : if (NS_FAILED(rv)) {
2002 0 : defaultContent->UnbindFromTree();
2003 0 : return rv;
2004 : }
2005 : }
2006 :
2007 0 : if (!mInsertionPointTable) {
2008 : mInsertionPointTable = new nsObjectHashtable(nsnull, nsnull,
2009 : DeleteInsertionPointEntry,
2010 0 : nsnull, 4);
2011 : }
2012 :
2013 : // Now read in the tags that can be inserted at this point, which is
2014 : // specified by the syntax includes="menupopup|panel", and add them to
2015 : // the hash.
2016 : PRUint32 count;
2017 0 : rv = aStream->Read32(&count);
2018 0 : NS_ENSURE_SUCCESS(rv, rv);
2019 :
2020 0 : for (; count > 0; count --) {
2021 0 : aStream->ReadString(tag);
2022 0 : nsCOMPtr<nsIAtom> tagAtom = do_GetAtom(tag);
2023 :
2024 0 : nsISupportsKey key(tagAtom);
2025 0 : NS_ADDREF(xblIns.get());
2026 0 : mInsertionPointTable->Put(&key, xblIns);
2027 : }
2028 :
2029 0 : rv = aStream->Read32(&insertionPointIndex);
2030 0 : NS_ENSURE_SUCCESS(rv, rv);
2031 : }
2032 :
2033 : // Finally, read in the child nodes.
2034 : PRUint32 childCount;
2035 0 : rv = aStream->Read32(&childCount);
2036 0 : NS_ENSURE_SUCCESS(rv, rv);
2037 :
2038 0 : for (PRUint32 i = 0; i < childCount; i++) {
2039 0 : nsCOMPtr<nsIContent> child;
2040 0 : ReadContentNode(aStream, aDocument, aNim, getter_AddRefs(child));
2041 :
2042 : // Child may be null if this was a comment for example and can just be ignored.
2043 0 : if (child) {
2044 0 : content->AppendChildTo(child, false);
2045 : }
2046 : }
2047 :
2048 0 : content.swap(*aContent);
2049 0 : return NS_OK;
2050 : }
2051 :
2052 : // This structure holds information about a forwarded attribute that needs to be
2053 : // written out. This is used because we need several fields passed within the
2054 : // enumeration closure.
2055 : struct WriteAttributeData
2056 : {
2057 : nsXBLPrototypeBinding* binding;
2058 : nsIObjectOutputStream* stream;
2059 : nsIContent* content;
2060 : PRInt32 srcNamespace;
2061 :
2062 0 : WriteAttributeData(nsXBLPrototypeBinding* aBinding,
2063 : nsIObjectOutputStream* aStream,
2064 : nsIContent* aContent)
2065 0 : : binding(aBinding), stream(aStream), content(aContent)
2066 0 : { }
2067 : };
2068 :
2069 : static
2070 : bool
2071 0 : WriteAttribute(nsHashKey *aKey, void *aData, void* aClosure)
2072 : {
2073 0 : WriteAttributeData* data = static_cast<WriteAttributeData *>(aClosure);
2074 0 : nsIObjectOutputStream* stream = data->stream;
2075 0 : const PRInt32 srcNamespace = data->srcNamespace;
2076 :
2077 0 : nsXBLAttributeEntry* entry = static_cast<nsXBLAttributeEntry *>(aData);
2078 0 : do {
2079 0 : if (entry->GetElement() == data->content) {
2080 0 : data->binding->WriteNamespace(stream, srcNamespace);
2081 0 : stream->WriteWStringZ(nsDependentAtomString(entry->GetSrcAttribute()).get());
2082 0 : data->binding->WriteNamespace(stream, entry->GetDstNameSpace());
2083 0 : stream->WriteWStringZ(nsDependentAtomString(entry->GetDstAttribute()).get());
2084 : }
2085 :
2086 0 : entry = entry->GetNext();
2087 : } while (entry);
2088 :
2089 0 : return kHashEnumerateNext;
2090 : }
2091 :
2092 : // WriteAttributeNS is the callback to enumerate over the attribute
2093 : // forwarding entries. Since these are stored in a hash of hashes,
2094 : // we need to iterate over the inner hashes, calling WriteAttribute
2095 : // to do the actual work.
2096 : static
2097 : bool
2098 0 : WriteAttributeNS(nsHashKey *aKey, void *aData, void* aClosure)
2099 : {
2100 0 : WriteAttributeData* data = static_cast<WriteAttributeData *>(aClosure);
2101 0 : data->srcNamespace = static_cast<nsPRUint32Key *>(aKey)->GetValue();
2102 :
2103 0 : nsObjectHashtable* attributes = static_cast<nsObjectHashtable*>(aData);
2104 0 : attributes->Enumerate(WriteAttribute, data);
2105 :
2106 0 : return kHashEnumerateNext;
2107 : }
2108 :
2109 : nsresult
2110 0 : nsXBLPrototypeBinding::WriteContentNode(nsIObjectOutputStream* aStream,
2111 : nsIContent* aNode,
2112 : ArrayOfInsertionPointsByContent& aInsertionPointsByContent)
2113 : {
2114 : nsresult rv;
2115 :
2116 0 : if (!aNode->IsElement()) {
2117 : // Text is writen out as a single byte for the type, followed by the text.
2118 0 : PRUint8 type = XBLBinding_Serialize_NoContent;
2119 0 : switch (aNode->NodeType()) {
2120 : case nsIDOMNode::TEXT_NODE:
2121 0 : type = XBLBinding_Serialize_TextNode;
2122 0 : break;
2123 : case nsIDOMNode::CDATA_SECTION_NODE:
2124 0 : type = XBLBinding_Serialize_CDATANode;
2125 0 : break;
2126 : case nsIDOMNode::COMMENT_NODE:
2127 0 : type = XBLBinding_Serialize_CommentNode;
2128 0 : break;
2129 : default:
2130 0 : break;
2131 : }
2132 :
2133 0 : rv = aStream->Write8(type);
2134 0 : NS_ENSURE_SUCCESS(rv, rv);
2135 :
2136 0 : nsAutoString content;
2137 0 : aNode->GetText()->AppendTo(content);
2138 0 : return aStream->WriteWStringZ(content.get());
2139 : }
2140 :
2141 : // Otherwise, this is an element.
2142 :
2143 : // Write the namespace id followed by the tag name
2144 0 : rv = WriteNamespace(aStream, aNode->GetNameSpaceID());
2145 0 : NS_ENSURE_SUCCESS(rv, rv);
2146 :
2147 0 : nsAutoString prefixStr;
2148 0 : aNode->NodeInfo()->GetPrefix(prefixStr);
2149 0 : rv = aStream->WriteWStringZ(prefixStr.get());
2150 0 : NS_ENSURE_SUCCESS(rv, rv);
2151 :
2152 0 : rv = aStream->WriteWStringZ(nsDependentAtomString(aNode->Tag()).get());
2153 0 : NS_ENSURE_SUCCESS(rv, rv);
2154 :
2155 : // Write attributes
2156 0 : PRUint32 count = aNode->GetAttrCount();
2157 0 : rv = aStream->Write32(count);
2158 0 : NS_ENSURE_SUCCESS(rv, rv);
2159 :
2160 : PRUint32 i;
2161 0 : for (i = 0; i < count; i++) {
2162 : // Write out the namespace id, the namespace prefix, the local tag name,
2163 : // and the value, in that order.
2164 :
2165 0 : const nsAttrName* attr = aNode->GetAttrNameAt(i);
2166 :
2167 : // XXXndeakin don't write out xbl:inherits?
2168 0 : PRInt32 namespaceID = attr->NamespaceID();
2169 0 : rv = WriteNamespace(aStream, namespaceID);
2170 0 : NS_ENSURE_SUCCESS(rv, rv);
2171 :
2172 0 : nsAutoString prefixStr;
2173 0 : nsIAtom* prefix = attr->GetPrefix();
2174 0 : if (prefix)
2175 0 : prefix->ToString(prefixStr);
2176 0 : rv = aStream->WriteWStringZ(prefixStr.get());
2177 0 : NS_ENSURE_SUCCESS(rv, rv);
2178 :
2179 0 : rv = aStream->WriteWStringZ(nsDependentAtomString(attr->LocalName()).get());
2180 0 : NS_ENSURE_SUCCESS(rv, rv);
2181 :
2182 0 : nsAutoString val;
2183 0 : aNode->GetAttr(attr->NamespaceID(), attr->LocalName(), val);
2184 0 : rv = aStream->WriteWStringZ(val.get());
2185 0 : NS_ENSURE_SUCCESS(rv, rv);
2186 : }
2187 :
2188 : // Write out the attribute fowarding information
2189 0 : if (mAttributeTable) {
2190 0 : WriteAttributeData data(this, aStream, aNode);
2191 0 : mAttributeTable->Enumerate(WriteAttributeNS, &data);
2192 : }
2193 0 : rv = aStream->Write8(XBLBinding_Serialize_NoMoreAttributes);
2194 0 : NS_ENSURE_SUCCESS(rv, rv);
2195 :
2196 : // Write out the insertion points.
2197 : nsAutoTArray<InsertionItem, 1>* list;
2198 0 : if (aInsertionPointsByContent.Get(aNode, &list)) {
2199 0 : PRUint32 lastInsertionIndex = 0xFFFFFFFF;
2200 :
2201 : // Iterate over the insertion points and see if they match this point
2202 : // in the content tree by comparing insertion indices.
2203 0 : for (PRUint32 l = 0; l < list->Length(); l++) {
2204 0 : InsertionItem item = list->ElementAt(l);
2205 : // The list is sorted by insertionIndex so all items pertaining to
2206 : // this point will be in the list in order. We only need to write out the
2207 : // default content and the number of tags once for each index.
2208 0 : if (item.insertionIndex != lastInsertionIndex) {
2209 0 : lastInsertionIndex = item.insertionIndex;
2210 0 : aStream->Write32(item.insertionIndex);
2211 : // If the insertion point has default content, write that out, or
2212 : // write out XBLBinding_Serialize_NoContent if there isn't any.
2213 0 : if (item.defaultContent) {
2214 : rv = WriteContentNode(aStream, item.defaultContent,
2215 0 : aInsertionPointsByContent);
2216 : }
2217 : else {
2218 0 : rv = aStream->Write8(XBLBinding_Serialize_NoContent);
2219 : }
2220 0 : NS_ENSURE_SUCCESS(rv, rv);
2221 :
2222 : // Determine how many insertion points share the same index, so that
2223 : // the total count can be written out.
2224 0 : PRUint32 icount = 1;
2225 0 : for (PRUint32 i = l + 1; i < list->Length(); i++) {
2226 0 : if (list->ElementAt(i).insertionIndex != lastInsertionIndex)
2227 0 : break;
2228 0 : icount++;
2229 : }
2230 :
2231 0 : rv = aStream->Write32(icount);
2232 0 : NS_ENSURE_SUCCESS(rv, rv);
2233 : }
2234 :
2235 0 : rv = aStream->WriteWStringZ(nsDependentAtomString(list->ElementAt(l).tag).get());
2236 0 : NS_ENSURE_SUCCESS(rv, rv);
2237 : }
2238 : }
2239 :
2240 : // Write out an end marker after the insertion points. If there weren't
2241 : // any, this will be all that is written out.
2242 0 : rv = aStream->Write32(XBLBinding_Serialize_NoMoreInsertionPoints);
2243 0 : NS_ENSURE_SUCCESS(rv, rv);
2244 :
2245 : // Finally, write out the child nodes.
2246 0 : count = aNode->GetChildCount();
2247 0 : rv = aStream->Write32(count);
2248 0 : NS_ENSURE_SUCCESS(rv, rv);
2249 :
2250 0 : for (i = 0; i < count; i++) {
2251 0 : rv = WriteContentNode(aStream, aNode->GetChildAt(i), aInsertionPointsByContent);
2252 0 : NS_ENSURE_SUCCESS(rv, rv);
2253 : }
2254 :
2255 0 : return NS_OK;
2256 : }
2257 :
2258 : nsresult
2259 0 : nsXBLPrototypeBinding::ReadNamespace(nsIObjectInputStream* aStream,
2260 : PRInt32& aNameSpaceID)
2261 : {
2262 : PRUint8 namespaceID;
2263 0 : nsresult rv = aStream->Read8(&namespaceID);
2264 0 : NS_ENSURE_SUCCESS(rv, rv);
2265 :
2266 0 : if (namespaceID == XBLBinding_Serialize_CustomNamespace) {
2267 0 : nsAutoString namesp;
2268 0 : rv = aStream->ReadString(namesp);
2269 0 : NS_ENSURE_SUCCESS(rv, rv);
2270 :
2271 0 : nsContentUtils::NameSpaceManager()->RegisterNameSpace(namesp, aNameSpaceID);
2272 : }
2273 : else {
2274 0 : aNameSpaceID = namespaceID;
2275 : }
2276 :
2277 0 : return NS_OK;
2278 : }
2279 :
2280 : nsresult
2281 0 : nsXBLPrototypeBinding::WriteNamespace(nsIObjectOutputStream* aStream,
2282 : PRInt32 aNameSpaceID)
2283 : {
2284 : // Namespaces are stored as a single byte id for well-known namespaces.
2285 : // This saves time and space as other namespaces aren't very common in
2286 : // XBL. If another namespace is used however, the namespace id will be
2287 : // XBLBinding_Serialize_CustomNamespace and the string namespace written
2288 : // out directly afterwards.
2289 : nsresult rv;
2290 :
2291 0 : if (aNameSpaceID <= kNameSpaceID_LastBuiltin) {
2292 0 : rv = aStream->Write8((PRInt8)aNameSpaceID);
2293 0 : NS_ENSURE_SUCCESS(rv, rv);
2294 : }
2295 : else {
2296 0 : rv = aStream->Write8(XBLBinding_Serialize_CustomNamespace);
2297 0 : NS_ENSURE_SUCCESS(rv, rv);
2298 :
2299 0 : nsAutoString namesp;
2300 0 : nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNameSpaceID, namesp);
2301 0 : aStream->WriteWStringZ(namesp.get());
2302 : }
2303 :
2304 0 : return NS_OK;
2305 : }
2306 :
2307 :
2308 0 : bool CheckTagNameWhiteList(PRInt32 aNameSpaceID, nsIAtom *aTagName)
2309 : {
2310 : static nsIContent::AttrValuesArray kValidXULTagNames[] = {
2311 : &nsGkAtoms::autorepeatbutton, &nsGkAtoms::box, &nsGkAtoms::browser,
2312 : &nsGkAtoms::button, &nsGkAtoms::hbox, &nsGkAtoms::image, &nsGkAtoms::menu,
2313 : &nsGkAtoms::menubar, &nsGkAtoms::menuitem, &nsGkAtoms::menupopup,
2314 : &nsGkAtoms::row, &nsGkAtoms::slider, &nsGkAtoms::spacer,
2315 : &nsGkAtoms::splitter, &nsGkAtoms::text, &nsGkAtoms::tree, nsnull};
2316 :
2317 : PRUint32 i;
2318 0 : if (aNameSpaceID == kNameSpaceID_XUL) {
2319 0 : for (i = 0; kValidXULTagNames[i]; ++i) {
2320 0 : if (aTagName == *(kValidXULTagNames[i])) {
2321 0 : return true;
2322 : }
2323 : }
2324 : }
2325 0 : else if (aNameSpaceID == kNameSpaceID_SVG &&
2326 : aTagName == nsGkAtoms::generic) {
2327 0 : return true;
2328 : }
2329 :
2330 0 : return false;
2331 : }
2332 :
2333 : nsresult
2334 0 : nsXBLPrototypeBinding::ResolveBaseBinding()
2335 : {
2336 0 : if (mCheckedBaseProto)
2337 0 : return NS_OK;
2338 0 : mCheckedBaseProto = true;
2339 :
2340 0 : nsCOMPtr<nsIDocument> doc = mXBLDocInfoWeak->GetDocument();
2341 :
2342 : // Check for the presence of 'extends' and 'display' attributes
2343 0 : nsAutoString display, extends;
2344 0 : mBinding->GetAttr(kNameSpaceID_None, nsGkAtoms::extends, extends);
2345 0 : if (extends.IsEmpty())
2346 0 : return NS_OK;
2347 :
2348 0 : mBinding->GetAttr(kNameSpaceID_None, nsGkAtoms::display, display);
2349 0 : bool hasDisplay = !display.IsEmpty();
2350 :
2351 0 : nsAutoString value(extends);
2352 :
2353 : // Now slice 'em up to see what we've got.
2354 0 : nsAutoString prefix;
2355 : PRInt32 offset;
2356 0 : if (hasDisplay) {
2357 0 : offset = display.FindChar(':');
2358 0 : if (-1 != offset) {
2359 0 : display.Left(prefix, offset);
2360 0 : display.Cut(0, offset+1);
2361 : }
2362 : }
2363 : else {
2364 0 : offset = extends.FindChar(':');
2365 0 : if (-1 != offset) {
2366 0 : extends.Left(prefix, offset);
2367 0 : extends.Cut(0, offset+1);
2368 0 : display = extends;
2369 : }
2370 : }
2371 :
2372 0 : nsAutoString nameSpace;
2373 :
2374 0 : if (!prefix.IsEmpty()) {
2375 0 : mBinding->LookupNamespaceURI(prefix, nameSpace);
2376 0 : if (!nameSpace.IsEmpty()) {
2377 : PRInt32 nameSpaceID =
2378 0 : nsContentUtils::NameSpaceManager()->GetNameSpaceID(nameSpace);
2379 :
2380 0 : nsCOMPtr<nsIAtom> tagName = do_GetAtom(display);
2381 : // Check the white list
2382 0 : if (!CheckTagNameWhiteList(nameSpaceID, tagName)) {
2383 0 : const PRUnichar* params[] = { display.get() };
2384 : nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
2385 : "XBL", nsnull,
2386 : nsContentUtils::eXBL_PROPERTIES,
2387 : "InvalidExtendsBinding",
2388 : params, ArrayLength(params),
2389 0 : doc->GetDocumentURI());
2390 0 : NS_ASSERTION(!nsXBLService::IsChromeOrResourceURI(doc->GetDocumentURI()),
2391 : "Invalid extends value");
2392 0 : return NS_ERROR_ILLEGAL_VALUE;
2393 : }
2394 :
2395 0 : SetBaseTag(nameSpaceID, tagName);
2396 : }
2397 : }
2398 :
2399 0 : if (hasDisplay || nameSpace.IsEmpty()) {
2400 0 : mBinding->UnsetAttr(kNameSpaceID_None, nsGkAtoms::extends, false);
2401 0 : mBinding->UnsetAttr(kNameSpaceID_None, nsGkAtoms::display, false);
2402 :
2403 0 : return NS_NewURI(getter_AddRefs(mBaseBindingURI), value,
2404 0 : doc->GetDocumentCharacterSet().get(),
2405 0 : doc->GetDocBaseURI());
2406 : }
2407 :
2408 0 : return NS_OK;
2409 4392 : }
|