dom/xbl/nsXBLPrototypeBinding.cpp

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:fb0a0ef36e24
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #include "mozilla/ArrayUtils.h"
7
8 #include "nsCOMPtr.h"
9 #include "nsIAtom.h"
10 #include "nsIInputStream.h"
11 #include "nsNameSpaceManager.h"
12 #include "nsIURI.h"
13 #include "nsIURL.h"
14 #include "nsIChannel.h"
15 #include "nsXPIDLString.h"
16 #include "nsReadableUtils.h"
17 #include "nsNetUtil.h"
18 #include "plstr.h"
19 #include "nsContentCreatorFunctions.h"
20 #include "nsIDocument.h"
21 #include "nsIXMLContentSink.h"
22 #include "nsContentCID.h"
23 #include "mozilla/dom/XMLDocument.h"
24 #include "nsXBLService.h"
25 #include "nsXBLBinding.h"
26 #include "nsXBLPrototypeBinding.h"
27 #include "nsXBLContentSink.h"
28 #include "xptinfo.h"
29 #include "nsIInterfaceInfoManager.h"
30 #include "nsIDocumentObserver.h"
31 #include "nsGkAtoms.h"
32 #include "nsXBLProtoImpl.h"
33 #include "nsCRT.h"
34 #include "nsContentUtils.h"
35 #include "nsTextFragment.h"
36 #include "nsTextNode.h"
37 #include "nsIInterfaceInfo.h"
38 #include "nsIScriptError.h"
39
40 #include "nsIStyleRuleProcessor.h"
41 #include "nsXBLResourceLoader.h"
42 #include "mozilla/dom/CDATASection.h"
43 #include "mozilla/dom/Comment.h"
44 #include "mozilla/dom/Element.h"
45
46 #ifdef MOZ_XUL
47 #include "nsXULElement.h"
48 #endif
49
50 using namespace mozilla;
51 using namespace mozilla::dom;
52
53 // Helper Classes =====================================================================
54
55 // nsXBLAttributeEntry and helpers. This class is used to efficiently handle
56 // attribute changes in anonymous content.
57
58 class nsXBLAttributeEntry {
59 public:
60 nsXBLAttributeEntry(nsIAtom* aSrcAtom, nsIAtom* aDstAtom,
61 int32_t aDstNameSpace, nsIContent* aContent)
62 : mElement(aContent),
63 mSrcAttribute(aSrcAtom),
64 mDstAttribute(aDstAtom),
65 mDstNameSpace(aDstNameSpace),
66 mNext(nullptr) { }
67
68 ~nsXBLAttributeEntry() {
69 NS_CONTENT_DELETE_LIST_MEMBER(nsXBLAttributeEntry, this, mNext);
70 }
71
72 nsIAtom* GetSrcAttribute() { return mSrcAttribute; }
73 nsIAtom* GetDstAttribute() { return mDstAttribute; }
74 int32_t GetDstNameSpace() { return mDstNameSpace; }
75
76 nsIContent* GetElement() { return mElement; }
77
78 nsXBLAttributeEntry* GetNext() { return mNext; }
79 void SetNext(nsXBLAttributeEntry* aEntry) { mNext = aEntry; }
80
81 protected:
82 nsIContent* mElement;
83
84 nsCOMPtr<nsIAtom> mSrcAttribute;
85 nsCOMPtr<nsIAtom> mDstAttribute;
86 int32_t mDstNameSpace;
87 nsXBLAttributeEntry* mNext;
88 };
89
90 // =============================================================================
91
92 // Implementation /////////////////////////////////////////////////////////////////
93
94 // Constructors/Destructors
95 nsXBLPrototypeBinding::nsXBLPrototypeBinding()
96 : mImplementation(nullptr),
97 mBaseBinding(nullptr),
98 mInheritStyle(true),
99 mCheckedBaseProto(false),
100 mKeyHandlersRegistered(false),
101 mChromeOnlyContent(false),
102 mResources(nullptr),
103 mBaseNameSpaceID(kNameSpaceID_None)
104 {
105 MOZ_COUNT_CTOR(nsXBLPrototypeBinding);
106 }
107
108 nsresult
109 nsXBLPrototypeBinding::Init(const nsACString& aID,
110 nsXBLDocumentInfo* aInfo,
111 nsIContent* aElement,
112 bool aFirstBinding)
113 {
114 nsresult rv = aInfo->DocumentURI()->Clone(getter_AddRefs(mBindingURI));
115 NS_ENSURE_SUCCESS(rv, rv);
116
117 // The binding URI might be an immutable URI (e.g. for about: URIs). In that case,
118 // we'll fail in SetRef below, but that doesn't matter much for now.
119 if (aFirstBinding) {
120 rv = mBindingURI->Clone(getter_AddRefs(mAlternateBindingURI));
121 NS_ENSURE_SUCCESS(rv, rv);
122 }
123 mBindingURI->SetRef(aID);
124
125 mXBLDocInfoWeak = aInfo;
126
127 // aElement will be null when reading from the cache, but the element will
128 // still be set later.
129 if (aElement) {
130 SetBindingElement(aElement);
131 }
132 return NS_OK;
133 }
134
135 bool nsXBLPrototypeBinding::CompareBindingURI(nsIURI* aURI) const
136 {
137 bool equal = false;
138 mBindingURI->Equals(aURI, &equal);
139 if (!equal && mAlternateBindingURI) {
140 mAlternateBindingURI->Equals(aURI, &equal);
141 }
142 return equal;
143 }
144
145 void
146 nsXBLPrototypeBinding::Traverse(nsCycleCollectionTraversalCallback &cb) const
147 {
148 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "proto mBinding");
149 cb.NoteXPCOMChild(mBinding);
150 if (mResources) {
151 mResources->Traverse(cb);
152 }
153 ImplCycleCollectionTraverse(cb, mInterfaceTable, "proto mInterfaceTable");
154 }
155
156 void
157 nsXBLPrototypeBinding::UnlinkJSObjects()
158 {
159 if (mImplementation)
160 mImplementation->UnlinkJSObjects();
161 }
162
163 void
164 nsXBLPrototypeBinding::Trace(const TraceCallbacks& aCallbacks, void *aClosure) const
165 {
166 if (mImplementation)
167 mImplementation->Trace(aCallbacks, aClosure);
168 }
169
170 void
171 nsXBLPrototypeBinding::Initialize()
172 {
173 nsIContent* content = GetImmediateChild(nsGkAtoms::content);
174 if (content) {
175 ConstructAttributeTable(content);
176 }
177 }
178
179 nsXBLPrototypeBinding::~nsXBLPrototypeBinding(void)
180 {
181 delete mImplementation;
182 MOZ_COUNT_DTOR(nsXBLPrototypeBinding);
183 }
184
185 void
186 nsXBLPrototypeBinding::SetBasePrototype(nsXBLPrototypeBinding* aBinding)
187 {
188 if (mBaseBinding == aBinding)
189 return;
190
191 if (mBaseBinding) {
192 NS_ERROR("Base XBL prototype binding is already defined!");
193 return;
194 }
195
196 mBaseBinding = aBinding;
197 }
198
199 void
200 nsXBLPrototypeBinding::SetBindingElement(nsIContent* aElement)
201 {
202 mBinding = aElement;
203 if (mBinding->AttrValueIs(kNameSpaceID_None, nsGkAtoms::inheritstyle,
204 nsGkAtoms::_false, eCaseMatters))
205 mInheritStyle = false;
206
207 mChromeOnlyContent = mBinding->AttrValueIs(kNameSpaceID_None,
208 nsGkAtoms::chromeOnlyContent,
209 nsGkAtoms::_true, eCaseMatters);
210 }
211
212 bool
213 nsXBLPrototypeBinding::GetAllowScripts() const
214 {
215 return mXBLDocInfoWeak->GetScriptAccess();
216 }
217
218 bool
219 nsXBLPrototypeBinding::LoadResources()
220 {
221 if (mResources) {
222 bool result;
223 mResources->LoadResources(&result);
224 return result;
225 }
226
227 return true;
228 }
229
230 nsresult
231 nsXBLPrototypeBinding::AddResource(nsIAtom* aResourceType, const nsAString& aSrc)
232 {
233 if (!mResources) {
234 mResources = new nsXBLPrototypeResources(this);
235 }
236
237 mResources->AddResource(aResourceType, aSrc);
238 return NS_OK;
239 }
240
241 nsresult
242 nsXBLPrototypeBinding::FlushSkinSheets()
243 {
244 if (mResources)
245 return mResources->FlushSkinSheets();
246 return NS_OK;
247 }
248
249 nsresult
250 nsXBLPrototypeBinding::BindingAttached(nsIContent* aBoundElement)
251 {
252 if (mImplementation && mImplementation->CompiledMembers() &&
253 mImplementation->mConstructor)
254 return mImplementation->mConstructor->Execute(aBoundElement);
255 return NS_OK;
256 }
257
258 nsresult
259 nsXBLPrototypeBinding::BindingDetached(nsIContent* aBoundElement)
260 {
261 if (mImplementation && mImplementation->CompiledMembers() &&
262 mImplementation->mDestructor)
263 return mImplementation->mDestructor->Execute(aBoundElement);
264 return NS_OK;
265 }
266
267 nsXBLProtoImplAnonymousMethod*
268 nsXBLPrototypeBinding::GetConstructor()
269 {
270 if (mImplementation)
271 return mImplementation->mConstructor;
272
273 return nullptr;
274 }
275
276 nsXBLProtoImplAnonymousMethod*
277 nsXBLPrototypeBinding::GetDestructor()
278 {
279 if (mImplementation)
280 return mImplementation->mDestructor;
281
282 return nullptr;
283 }
284
285 nsresult
286 nsXBLPrototypeBinding::SetConstructor(nsXBLProtoImplAnonymousMethod* aMethod)
287 {
288 if (!mImplementation)
289 return NS_ERROR_FAILURE;
290 mImplementation->mConstructor = aMethod;
291 return NS_OK;
292 }
293
294 nsresult
295 nsXBLPrototypeBinding::SetDestructor(nsXBLProtoImplAnonymousMethod* aMethod)
296 {
297 if (!mImplementation)
298 return NS_ERROR_FAILURE;
299 mImplementation->mDestructor = aMethod;
300 return NS_OK;
301 }
302
303 nsresult
304 nsXBLPrototypeBinding::InstallImplementation(nsXBLBinding* aBinding)
305 {
306 if (mImplementation)
307 return mImplementation->InstallImplementation(this, aBinding);
308 return NS_OK;
309 }
310
311 // XXXbz this duplicates lots of SetAttrs
312 void
313 nsXBLPrototypeBinding::AttributeChanged(nsIAtom* aAttribute,
314 int32_t aNameSpaceID,
315 bool aRemoveFlag,
316 nsIContent* aChangedElement,
317 nsIContent* aAnonymousContent,
318 bool aNotify)
319 {
320 if (!mAttributeTable)
321 return;
322
323 InnerAttributeTable *attributesNS = mAttributeTable->Get(aNameSpaceID);
324 if (!attributesNS)
325 return;
326
327 nsXBLAttributeEntry* xblAttr = attributesNS->Get(aAttribute);
328 if (!xblAttr)
329 return;
330
331 // Iterate over the elements in the array.
332 nsCOMPtr<nsIContent> content = GetImmediateChild(nsGkAtoms::content);
333 while (xblAttr) {
334 nsIContent* element = xblAttr->GetElement();
335
336 nsCOMPtr<nsIContent> realElement = LocateInstance(aChangedElement, content,
337 aAnonymousContent,
338 element);
339
340 if (realElement) {
341 // Hold a strong reference here so that the atom doesn't go away during
342 // UnsetAttr.
343 nsCOMPtr<nsIAtom> dstAttr = xblAttr->GetDstAttribute();
344 int32_t dstNs = xblAttr->GetDstNameSpace();
345
346 if (aRemoveFlag)
347 realElement->UnsetAttr(dstNs, dstAttr, aNotify);
348 else {
349 bool attrPresent = true;
350 nsAutoString value;
351 // Check to see if the src attribute is xbl:text. If so, then we need to obtain the
352 // children of the real element and get the text nodes' values.
353 if (aAttribute == nsGkAtoms::text && aNameSpaceID == kNameSpaceID_XBL) {
354 if (!nsContentUtils::GetNodeTextContent(aChangedElement, false, value)) {
355 NS_RUNTIMEABORT("OOM");
356 }
357 value.StripChar(char16_t('\n'));
358 value.StripChar(char16_t('\r'));
359 nsAutoString stripVal(value);
360 stripVal.StripWhitespace();
361 if (stripVal.IsEmpty())
362 attrPresent = false;
363 }
364 else {
365 attrPresent = aChangedElement->GetAttr(aNameSpaceID, aAttribute, value);
366 }
367
368 if (attrPresent)
369 realElement->SetAttr(dstNs, dstAttr, value, aNotify);
370 }
371
372 // See if we're the <html> tag in XUL, and see if value is being
373 // set or unset on us. We may also be a tag that is having
374 // xbl:text set on us.
375
376 if ((dstAttr == nsGkAtoms::text && dstNs == kNameSpaceID_XBL) ||
377 (realElement->NodeInfo()->Equals(nsGkAtoms::html,
378 kNameSpaceID_XUL) &&
379 dstAttr == nsGkAtoms::value)) {
380 // Flush out all our kids.
381 uint32_t childCount = realElement->GetChildCount();
382 for (uint32_t i = 0; i < childCount; i++)
383 realElement->RemoveChildAt(0, aNotify);
384
385 if (!aRemoveFlag) {
386 // Construct a new text node and insert it.
387 nsAutoString value;
388 aChangedElement->GetAttr(aNameSpaceID, aAttribute, value);
389 if (!value.IsEmpty()) {
390 nsRefPtr<nsTextNode> textContent =
391 new nsTextNode(realElement->NodeInfo()->NodeInfoManager());
392
393 textContent->SetText(value, true);
394 realElement->AppendChildTo(textContent, true);
395 }
396 }
397 }
398 }
399
400 xblAttr = xblAttr->GetNext();
401 }
402 }
403
404 void
405 nsXBLPrototypeBinding::SetBaseTag(int32_t aNamespaceID, nsIAtom* aTag)
406 {
407 mBaseNameSpaceID = aNamespaceID;
408 mBaseTag = aTag;
409 }
410
411 nsIAtom*
412 nsXBLPrototypeBinding::GetBaseTag(int32_t* aNamespaceID)
413 {
414 if (mBaseTag) {
415 *aNamespaceID = mBaseNameSpaceID;
416 return mBaseTag;
417 }
418
419 return nullptr;
420 }
421
422 bool
423 nsXBLPrototypeBinding::ImplementsInterface(REFNSIID aIID) const
424 {
425 // Check our IID table.
426 return !!mInterfaceTable.GetWeak(aIID);
427 }
428
429 // Internal helpers ///////////////////////////////////////////////////////////////////////
430
431 nsIContent*
432 nsXBLPrototypeBinding::GetImmediateChild(nsIAtom* aTag)
433 {
434 for (nsIContent* child = mBinding->GetFirstChild();
435 child;
436 child = child->GetNextSibling()) {
437 if (child->NodeInfo()->Equals(aTag, kNameSpaceID_XBL)) {
438 return child;
439 }
440 }
441
442 return nullptr;
443 }
444
445 nsresult
446 nsXBLPrototypeBinding::InitClass(const nsCString& aClassName,
447 JSContext * aContext,
448 JS::Handle<JSObject*> aScriptObject,
449 JS::MutableHandle<JSObject*> aClassObject,
450 bool* aNew)
451 {
452 return nsXBLBinding::DoInitJSClass(aContext, aScriptObject,
453 aClassName, this, aClassObject, aNew);
454 }
455
456 nsIContent*
457 nsXBLPrototypeBinding::LocateInstance(nsIContent* aBoundElement,
458 nsIContent* aTemplRoot,
459 nsIContent* aCopyRoot,
460 nsIContent* aTemplChild)
461 {
462 // XXX We will get in trouble if the binding instantiation deviates from the template
463 // in the prototype.
464 if (aTemplChild == aTemplRoot || !aTemplChild)
465 return nullptr;
466
467 nsIContent* templParent = aTemplChild->GetParent();
468
469 // We may be disconnected from our parent during cycle collection.
470 if (!templParent)
471 return nullptr;
472
473 nsIContent *copyParent =
474 templParent == aTemplRoot ? aCopyRoot :
475 LocateInstance(aBoundElement, aTemplRoot, aCopyRoot, templParent);
476
477 if (!copyParent)
478 return nullptr;
479
480 return copyParent->GetChildAt(templParent->IndexOf(aTemplChild));
481 }
482
483 struct nsXBLAttrChangeData
484 {
485 nsXBLPrototypeBinding* mProto;
486 nsIContent* mBoundElement;
487 nsIContent* mContent;
488 int32_t mSrcNamespace;
489
490 nsXBLAttrChangeData(nsXBLPrototypeBinding* aProto,
491 nsIContent* aElt, nsIContent* aContent)
492 :mProto(aProto), mBoundElement(aElt), mContent(aContent) {}
493 };
494
495 // XXXbz this duplicates lots of AttributeChanged
496 static PLDHashOperator
497 SetAttrs(nsISupports* aKey, nsXBLAttributeEntry* aEntry, void* aClosure)
498 {
499 nsXBLAttrChangeData* changeData = static_cast<nsXBLAttrChangeData*>(aClosure);
500
501 nsIAtom* src = aEntry->GetSrcAttribute();
502 int32_t srcNs = changeData->mSrcNamespace;
503 nsAutoString value;
504 bool attrPresent = true;
505
506 if (src == nsGkAtoms::text && srcNs == kNameSpaceID_XBL) {
507 if (!nsContentUtils::GetNodeTextContent(changeData->mBoundElement, false,
508 value)) {
509 NS_RUNTIMEABORT("OOM");
510 }
511 value.StripChar(char16_t('\n'));
512 value.StripChar(char16_t('\r'));
513 nsAutoString stripVal(value);
514 stripVal.StripWhitespace();
515
516 if (stripVal.IsEmpty())
517 attrPresent = false;
518 }
519 else {
520 attrPresent = changeData->mBoundElement->GetAttr(srcNs, src, value);
521 }
522
523 if (attrPresent) {
524 nsIContent* content =
525 changeData->mProto->GetImmediateChild(nsGkAtoms::content);
526
527 nsXBLAttributeEntry* curr = aEntry;
528 while (curr) {
529 nsIAtom* dst = curr->GetDstAttribute();
530 int32_t dstNs = curr->GetDstNameSpace();
531 nsIContent* element = curr->GetElement();
532
533 nsIContent *realElement =
534 changeData->mProto->LocateInstance(changeData->mBoundElement, content,
535 changeData->mContent, element);
536
537 if (realElement) {
538 realElement->SetAttr(dstNs, dst, value, false);
539
540 // XXXndeakin shouldn't this be done in lieu of SetAttr?
541 if ((dst == nsGkAtoms::text && dstNs == kNameSpaceID_XBL) ||
542 (realElement->NodeInfo()->Equals(nsGkAtoms::html,
543 kNameSpaceID_XUL) &&
544 dst == nsGkAtoms::value && !value.IsEmpty())) {
545
546 nsRefPtr<nsTextNode> textContent =
547 new nsTextNode(realElement->NodeInfo()->NodeInfoManager());
548
549 textContent->SetText(value, false);
550 realElement->AppendChildTo(textContent, false);
551 }
552 }
553
554 curr = curr->GetNext();
555 }
556 }
557
558 return PL_DHASH_NEXT;
559 }
560
561 static PLDHashOperator
562 SetAttrsNS(const uint32_t &aNamespace,
563 nsXBLPrototypeBinding::InnerAttributeTable* aXBLAttributes,
564 void* aClosure)
565 {
566 if (aXBLAttributes && aClosure) {
567 nsXBLAttrChangeData* changeData = static_cast<nsXBLAttrChangeData*>(aClosure);
568 changeData->mSrcNamespace = aNamespace;
569 aXBLAttributes->EnumerateRead(SetAttrs, aClosure);
570 }
571 return PL_DHASH_NEXT;
572 }
573
574 void
575 nsXBLPrototypeBinding::SetInitialAttributes(nsIContent* aBoundElement, nsIContent* aAnonymousContent)
576 {
577 if (mAttributeTable) {
578 nsXBLAttrChangeData data(this, aBoundElement, aAnonymousContent);
579 mAttributeTable->EnumerateRead(SetAttrsNS, &data);
580 }
581 }
582
583 nsIStyleRuleProcessor*
584 nsXBLPrototypeBinding::GetRuleProcessor()
585 {
586 if (mResources) {
587 return mResources->mRuleProcessor;
588 }
589
590 return nullptr;
591 }
592
593 nsXBLPrototypeResources::sheet_array_type*
594 nsXBLPrototypeBinding::GetOrCreateStyleSheets()
595 {
596 if (!mResources) {
597 mResources = new nsXBLPrototypeResources(this);
598 }
599
600 return &mResources->mStyleSheetList;
601 }
602
603 nsXBLPrototypeResources::sheet_array_type*
604 nsXBLPrototypeBinding::GetStyleSheets()
605 {
606 if (mResources) {
607 return &mResources->mStyleSheetList;
608 }
609
610 return nullptr;
611 }
612
613 void
614 nsXBLPrototypeBinding::EnsureAttributeTable()
615 {
616 if (!mAttributeTable) {
617 mAttributeTable = new nsClassHashtable<nsUint32HashKey, InnerAttributeTable>(4);
618 }
619 }
620
621 void
622 nsXBLPrototypeBinding::AddToAttributeTable(int32_t aSourceNamespaceID, nsIAtom* aSourceTag,
623 int32_t aDestNamespaceID, nsIAtom* aDestTag,
624 nsIContent* aContent)
625 {
626 InnerAttributeTable* attributesNS = mAttributeTable->Get(aSourceNamespaceID);
627 if (!attributesNS) {
628 attributesNS = new InnerAttributeTable(4);
629 mAttributeTable->Put(aSourceNamespaceID, attributesNS);
630 }
631
632 nsXBLAttributeEntry* xblAttr =
633 new nsXBLAttributeEntry(aSourceTag, aDestTag, aDestNamespaceID, aContent);
634
635 nsXBLAttributeEntry* entry = attributesNS->Get(aSourceTag);
636 if (!entry) {
637 attributesNS->Put(aSourceTag, xblAttr);
638 } else {
639 while (entry->GetNext())
640 entry = entry->GetNext();
641 entry->SetNext(xblAttr);
642 }
643 }
644
645 void
646 nsXBLPrototypeBinding::ConstructAttributeTable(nsIContent* aElement)
647 {
648 // Don't add entries for <children> elements, since those will get
649 // removed from the DOM when we construct the insertion point table.
650 if (!aElement->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) {
651 nsAutoString inherits;
652 aElement->GetAttr(kNameSpaceID_XBL, nsGkAtoms::inherits, inherits);
653
654 if (!inherits.IsEmpty()) {
655 EnsureAttributeTable();
656
657 // The user specified at least one attribute.
658 char* str = ToNewCString(inherits);
659 char* newStr;
660 // XXX We should use a strtok function that tokenizes PRUnichars
661 // so that we don't have to convert from Unicode to ASCII and then back
662
663 char* token = nsCRT::strtok( str, ", ", &newStr );
664 while( token != nullptr ) {
665 // Build an atom out of this attribute.
666 nsCOMPtr<nsIAtom> atom;
667 int32_t atomNsID = kNameSpaceID_None;
668 nsCOMPtr<nsIAtom> attribute;
669 int32_t attributeNsID = kNameSpaceID_None;
670
671 // Figure out if this token contains a :.
672 nsAutoString attrTok; attrTok.AssignWithConversion(token);
673 int32_t index = attrTok.Find("=", true);
674 nsresult rv;
675 if (index != -1) {
676 // This attribute maps to something different.
677 nsAutoString left, right;
678 attrTok.Left(left, index);
679 attrTok.Right(right, attrTok.Length()-index-1);
680
681 rv = nsContentUtils::SplitQName(aElement, left, &attributeNsID,
682 getter_AddRefs(attribute));
683 if (NS_FAILED(rv))
684 return;
685
686 rv = nsContentUtils::SplitQName(aElement, right, &atomNsID,
687 getter_AddRefs(atom));
688 if (NS_FAILED(rv))
689 return;
690 }
691 else {
692 nsAutoString tok;
693 tok.AssignWithConversion(token);
694 rv = nsContentUtils::SplitQName(aElement, tok, &atomNsID,
695 getter_AddRefs(atom));
696 if (NS_FAILED(rv))
697 return;
698 attribute = atom;
699 attributeNsID = atomNsID;
700 }
701
702 AddToAttributeTable(atomNsID, atom, attributeNsID, attribute, aElement);
703
704 // Now remove the inherits attribute from the element so that it doesn't
705 // show up on clones of the element. It is used
706 // by the template only, and we don't need it anymore.
707 // XXXdwh Don't do this for XUL elements, since it faults them into heavyweight
708 // elements. Should nuke from the prototype instead.
709 // aElement->UnsetAttr(kNameSpaceID_XBL, nsGkAtoms::inherits, false);
710
711 token = nsCRT::strtok( newStr, ", ", &newStr );
712 }
713
714 nsMemory::Free(str);
715 }
716 }
717
718 // Recur into our children.
719 for (nsIContent* child = aElement->GetFirstChild();
720 child;
721 child = child->GetNextSibling()) {
722 ConstructAttributeTable(child);
723 }
724 }
725
726 nsresult
727 nsXBLPrototypeBinding::ConstructInterfaceTable(const nsAString& aImpls)
728 {
729 if (!aImpls.IsEmpty()) {
730 // Obtain the interface info manager that can tell us the IID
731 // for a given interface name.
732 nsCOMPtr<nsIInterfaceInfoManager>
733 infoManager(do_GetService(NS_INTERFACEINFOMANAGER_SERVICE_CONTRACTID));
734 if (!infoManager)
735 return NS_ERROR_FAILURE;
736
737 // The user specified at least one attribute.
738 NS_ConvertUTF16toUTF8 utf8impl(aImpls);
739 char* str = utf8impl.BeginWriting();
740 char* newStr;
741 // XXX We should use a strtok function that tokenizes PRUnichars
742 // so that we don't have to convert from Unicode to ASCII and then back
743
744 char* token = nsCRT::strtok( str, ", ", &newStr );
745 while( token != nullptr ) {
746 // get the InterfaceInfo for the name
747 nsCOMPtr<nsIInterfaceInfo> iinfo;
748 infoManager->GetInfoForName(token, getter_AddRefs(iinfo));
749
750 if (iinfo) {
751 // obtain an IID.
752 const nsIID* iid = nullptr;
753 iinfo->GetIIDShared(&iid);
754
755 if (iid) {
756 // We found a valid iid. Add it to our table.
757 mInterfaceTable.Put(*iid, mBinding);
758
759 // this block adds the parent interfaces of each interface
760 // defined in the xbl definition (implements="nsI...")
761 nsCOMPtr<nsIInterfaceInfo> parentInfo;
762 // if it has a parent, add it to the table
763 while (NS_SUCCEEDED(iinfo->GetParent(getter_AddRefs(parentInfo))) && parentInfo) {
764 // get the iid
765 parentInfo->GetIIDShared(&iid);
766
767 // don't add nsISupports to the table
768 if (!iid || iid->Equals(NS_GET_IID(nsISupports)))
769 break;
770
771 // add the iid to the table
772 mInterfaceTable.Put(*iid, mBinding);
773
774 // look for the next parent
775 iinfo = parentInfo;
776 }
777 }
778 }
779
780 token = nsCRT::strtok( newStr, ", ", &newStr );
781 }
782 }
783
784 return NS_OK;
785 }
786
787 nsresult
788 nsXBLPrototypeBinding::AddResourceListener(nsIContent* aBoundElement)
789 {
790 if (!mResources)
791 return NS_ERROR_FAILURE; // Makes no sense to add a listener when the binding
792 // has no resources.
793
794 mResources->AddResourceListener(aBoundElement);
795 return NS_OK;
796 }
797
798 void
799 nsXBLPrototypeBinding::CreateKeyHandlers()
800 {
801 nsXBLPrototypeHandler* curr = mPrototypeHandler;
802 while (curr) {
803 nsCOMPtr<nsIAtom> eventAtom = curr->GetEventName();
804 if (eventAtom == nsGkAtoms::keyup ||
805 eventAtom == nsGkAtoms::keydown ||
806 eventAtom == nsGkAtoms::keypress) {
807 uint8_t phase = curr->GetPhase();
808 uint8_t type = curr->GetType();
809
810 int32_t count = mKeyHandlers.Count();
811 int32_t i;
812 nsXBLKeyEventHandler* handler = nullptr;
813 for (i = 0; i < count; ++i) {
814 handler = mKeyHandlers[i];
815 if (handler->Matches(eventAtom, phase, type))
816 break;
817 }
818
819 if (i == count) {
820 nsRefPtr<nsXBLKeyEventHandler> newHandler;
821 NS_NewXBLKeyEventHandler(eventAtom, phase, type,
822 getter_AddRefs(newHandler));
823 if (newHandler)
824 mKeyHandlers.AppendObject(newHandler);
825 handler = newHandler;
826 }
827
828 if (handler)
829 handler->AddProtoHandler(curr);
830 }
831
832 curr = curr->GetNextHandler();
833 }
834 }
835
836 class XBLPrototypeSetupCleanup
837 {
838 public:
839 XBLPrototypeSetupCleanup(nsXBLDocumentInfo* aDocInfo, const nsACString& aID)
840 : mDocInfo(aDocInfo), mID(aID) {}
841
842 ~XBLPrototypeSetupCleanup()
843 {
844 if (mDocInfo) {
845 mDocInfo->RemovePrototypeBinding(mID);
846 }
847 }
848
849 void Disconnect()
850 {
851 mDocInfo = nullptr;
852 }
853
854 nsXBLDocumentInfo* mDocInfo;
855 nsAutoCString mID;
856 };
857
858 nsresult
859 nsXBLPrototypeBinding::Read(nsIObjectInputStream* aStream,
860 nsXBLDocumentInfo* aDocInfo,
861 nsIDocument* aDocument,
862 uint8_t aFlags)
863 {
864 mInheritStyle = (aFlags & XBLBinding_Serialize_InheritStyle) ? true : false;
865 mChromeOnlyContent =
866 (aFlags & XBLBinding_Serialize_ChromeOnlyContent) ? true : false;
867
868 // nsXBLContentSink::ConstructBinding doesn't create a binding with an empty
869 // id, so we don't here either.
870 nsAutoCString id;
871 nsresult rv = aStream->ReadCString(id);
872
873 NS_ENSURE_SUCCESS(rv, rv);
874 NS_ENSURE_TRUE(!id.IsEmpty(), NS_ERROR_FAILURE);
875
876 nsAutoCString baseBindingURI;
877 rv = aStream->ReadCString(baseBindingURI);
878 NS_ENSURE_SUCCESS(rv, rv);
879 mCheckedBaseProto = true;
880
881 if (!baseBindingURI.IsEmpty()) {
882 rv = NS_NewURI(getter_AddRefs(mBaseBindingURI), baseBindingURI);
883 NS_ENSURE_SUCCESS(rv, rv);
884 }
885
886 rv = ReadNamespace(aStream, mBaseNameSpaceID);
887 NS_ENSURE_SUCCESS(rv, rv);
888
889 nsAutoString baseTag;
890 rv = aStream->ReadString(baseTag);
891 NS_ENSURE_SUCCESS(rv, rv);
892 if (!baseTag.IsEmpty()) {
893 mBaseTag = do_GetAtom(baseTag);
894 }
895
896 aDocument->CreateElem(NS_LITERAL_STRING("binding"), nullptr, kNameSpaceID_XBL,
897 getter_AddRefs(mBinding));
898
899 nsCOMPtr<nsIContent> child;
900 rv = ReadContentNode(aStream, aDocument, aDocument->NodeInfoManager(), getter_AddRefs(child));
901 NS_ENSURE_SUCCESS(rv, rv);
902
903 Element* rootElement = aDocument->GetRootElement();
904 if (rootElement)
905 rootElement->AppendChildTo(mBinding, false);
906
907 if (child) {
908 mBinding->AppendChildTo(child, false);
909 }
910
911 uint32_t interfaceCount;
912 rv = aStream->Read32(&interfaceCount);
913 NS_ENSURE_SUCCESS(rv, rv);
914
915 for (; interfaceCount > 0; interfaceCount--) {
916 nsIID iid;
917 aStream->ReadID(&iid);
918 mInterfaceTable.Put(iid, mBinding);
919 }
920
921 AutoSafeJSContext cx;
922 JS::Rooted<JSObject*> compilationGlobal(cx, xpc::GetCompilationScope());
923 NS_ENSURE_TRUE(compilationGlobal, NS_ERROR_UNEXPECTED);
924 JSAutoCompartment ac(cx, compilationGlobal);
925
926 bool isFirstBinding = aFlags & XBLBinding_Serialize_IsFirstBinding;
927 rv = Init(id, aDocInfo, nullptr, isFirstBinding);
928 NS_ENSURE_SUCCESS(rv, rv);
929
930 // We need to set the prototype binding before reading the nsXBLProtoImpl,
931 // as it may be retrieved within.
932 rv = aDocInfo->SetPrototypeBinding(id, this);
933 NS_ENSURE_SUCCESS(rv, rv);
934
935 XBLPrototypeSetupCleanup cleanup(aDocInfo, id);
936
937 nsAutoCString className;
938 rv = aStream->ReadCString(className);
939 NS_ENSURE_SUCCESS(rv, rv);
940
941 if (!className.IsEmpty()) {
942 nsXBLProtoImpl* impl; // NS_NewXBLProtoImpl will set mImplementation for us
943 NS_NewXBLProtoImpl(this, NS_ConvertUTF8toUTF16(className).get(), &impl);
944
945 // This needs to happen after SetPrototypeBinding as calls are made to
946 // retrieve the mapped bindings from within here. However, if an error
947 // occurs, the mapping should be removed again so that we don't keep an
948 // invalid binding around.
949 rv = mImplementation->Read(aStream, this);
950 NS_ENSURE_SUCCESS(rv, rv);
951 }
952
953 // Next read in the handlers.
954 nsXBLPrototypeHandler* previousHandler = nullptr;
955
956 do {
957 XBLBindingSerializeDetails type;
958 rv = aStream->Read8(&type);
959 NS_ENSURE_SUCCESS(rv, rv);
960
961 if (type == XBLBinding_Serialize_NoMoreItems)
962 break;
963
964 NS_ASSERTION((type & XBLBinding_Serialize_Mask) == XBLBinding_Serialize_Handler,
965 "invalid handler type");
966
967 nsXBLPrototypeHandler* handler = new nsXBLPrototypeHandler(this);
968 rv = handler->Read(aStream);
969 if (NS_FAILED(rv)) {
970 delete handler;
971 return rv;
972 }
973
974 if (previousHandler) {
975 previousHandler->SetNextHandler(handler);
976 }
977 else {
978 SetPrototypeHandlers(handler);
979 }
980 previousHandler = handler;
981 } while (1);
982
983 if (mBinding) {
984 while (true) {
985 XBLBindingSerializeDetails type;
986 rv = aStream->Read8(&type);
987 NS_ENSURE_SUCCESS(rv, rv);
988
989 if (type != XBLBinding_Serialize_Attribute) {
990 break;
991 }
992
993 int32_t attrNamespace;
994 rv = ReadNamespace(aStream, attrNamespace);
995 NS_ENSURE_SUCCESS(rv, rv);
996
997 nsAutoString attrPrefix, attrName, attrValue;
998 rv = aStream->ReadString(attrPrefix);
999 NS_ENSURE_SUCCESS(rv, rv);
1000
1001 rv = aStream->ReadString(attrName);
1002 NS_ENSURE_SUCCESS(rv, rv);
1003
1004 rv = aStream->ReadString(attrValue);
1005 NS_ENSURE_SUCCESS(rv, rv);
1006
1007 nsCOMPtr<nsIAtom> atomPrefix = do_GetAtom(attrPrefix);
1008 nsCOMPtr<nsIAtom> atomName = do_GetAtom(attrName);
1009 mBinding->SetAttr(attrNamespace, atomName, atomPrefix, attrValue, false);
1010 }
1011 }
1012
1013 // Finally, read in the resources.
1014 while (true) {
1015 XBLBindingSerializeDetails type;
1016 rv = aStream->Read8(&type);
1017 NS_ENSURE_SUCCESS(rv, rv);
1018
1019 if (type == XBLBinding_Serialize_NoMoreItems)
1020 break;
1021
1022 NS_ASSERTION((type & XBLBinding_Serialize_Mask) == XBLBinding_Serialize_Stylesheet ||
1023 (type & XBLBinding_Serialize_Mask) == XBLBinding_Serialize_Image, "invalid resource type");
1024
1025 nsAutoString src;
1026 rv = aStream->ReadString(src);
1027 NS_ENSURE_SUCCESS(rv, rv);
1028
1029 AddResource(type == XBLBinding_Serialize_Stylesheet ? nsGkAtoms::stylesheet :
1030 nsGkAtoms::image, src);
1031 }
1032
1033 if (isFirstBinding) {
1034 aDocInfo->SetFirstPrototypeBinding(this);
1035 }
1036
1037 cleanup.Disconnect();
1038 return NS_OK;
1039 }
1040
1041 // static
1042 nsresult
1043 nsXBLPrototypeBinding::ReadNewBinding(nsIObjectInputStream* aStream,
1044 nsXBLDocumentInfo* aDocInfo,
1045 nsIDocument* aDocument,
1046 uint8_t aFlags)
1047 {
1048 // If the Read() succeeds, |binding| will end up being owned by aDocInfo's
1049 // binding table. Otherwise, we must manually delete it.
1050 nsXBLPrototypeBinding* binding = new nsXBLPrototypeBinding();
1051 nsresult rv = binding->Read(aStream, aDocInfo, aDocument, aFlags);
1052 if (NS_FAILED(rv)) {
1053 delete binding;
1054 }
1055 return rv;
1056 }
1057
1058 static PLDHashOperator
1059 WriteInterfaceID(const nsIID& aKey, nsIContent* aData, void* aClosure)
1060 {
1061 // We can just write out the ids. The cache will be invalidated when a
1062 // different build is used, so we don't need to worry about ids changing.
1063 static_cast<nsIObjectOutputStream *>(aClosure)->WriteID(aKey);
1064 return PL_DHASH_NEXT;
1065 }
1066
1067 nsresult
1068 nsXBLPrototypeBinding::Write(nsIObjectOutputStream* aStream)
1069 {
1070 // This writes out the binding. Note that mCheckedBaseProto,
1071 // mKeyHandlersRegistered and mKeyHandlers are not serialized as they are
1072 // computed on demand.
1073
1074 AutoSafeJSContext cx;
1075 JS::Rooted<JSObject*> compilationGlobal(cx, xpc::GetCompilationScope());
1076 NS_ENSURE_TRUE(compilationGlobal, NS_ERROR_UNEXPECTED);
1077 JSAutoCompartment ac(cx, compilationGlobal);
1078
1079 uint8_t flags = mInheritStyle ? XBLBinding_Serialize_InheritStyle : 0;
1080
1081 // mAlternateBindingURI is only set on the first binding.
1082 if (mAlternateBindingURI) {
1083 flags |= XBLBinding_Serialize_IsFirstBinding;
1084 }
1085
1086 if (mChromeOnlyContent) {
1087 flags |= XBLBinding_Serialize_ChromeOnlyContent;
1088 }
1089
1090 nsresult rv = aStream->Write8(flags);
1091 NS_ENSURE_SUCCESS(rv, rv);
1092
1093 nsAutoCString id;
1094 mBindingURI->GetRef(id);
1095 rv = aStream->WriteStringZ(id.get());
1096 NS_ENSURE_SUCCESS(rv, rv);
1097
1098 // write out the extends and display attribute values
1099 nsAutoCString extends;
1100 ResolveBaseBinding();
1101 if (mBaseBindingURI)
1102 mBaseBindingURI->GetSpec(extends);
1103
1104 rv = aStream->WriteStringZ(extends.get());
1105 NS_ENSURE_SUCCESS(rv, rv);
1106
1107 rv = WriteNamespace(aStream, mBaseNameSpaceID);
1108 NS_ENSURE_SUCCESS(rv, rv);
1109
1110 nsAutoString baseTag;
1111 if (mBaseTag) {
1112 mBaseTag->ToString(baseTag);
1113 }
1114 rv = aStream->WriteWStringZ(baseTag.get());
1115 NS_ENSURE_SUCCESS(rv, rv);
1116
1117 nsIContent* content = GetImmediateChild(nsGkAtoms::content);
1118 if (content) {
1119 rv = WriteContentNode(aStream, content);
1120 NS_ENSURE_SUCCESS(rv, rv);
1121 }
1122 else {
1123 // Write a marker to indicate that there is no content.
1124 rv = aStream->Write8(XBLBinding_Serialize_NoContent);
1125 NS_ENSURE_SUCCESS(rv, rv);
1126 }
1127
1128 // Enumerate and write out the implemented interfaces.
1129 rv = aStream->Write32(mInterfaceTable.Count());
1130 NS_ENSURE_SUCCESS(rv, rv);
1131
1132 mInterfaceTable.EnumerateRead(WriteInterfaceID, aStream);
1133
1134 // Write out the implementation details.
1135 if (mImplementation) {
1136 rv = mImplementation->Write(aStream, this);
1137 NS_ENSURE_SUCCESS(rv, rv);
1138 }
1139 else {
1140 // Write out an empty classname. This indicates that the binding does not
1141 // define an implementation.
1142 rv = aStream->WriteWStringZ(EmptyString().get());
1143 NS_ENSURE_SUCCESS(rv, rv);
1144 }
1145
1146 // Write out the handlers.
1147 nsXBLPrototypeHandler* handler = mPrototypeHandler;
1148 while (handler) {
1149 rv = handler->Write(aStream);
1150 NS_ENSURE_SUCCESS(rv, rv);
1151
1152 handler = handler->GetNextHandler();
1153 }
1154
1155 aStream->Write8(XBLBinding_Serialize_NoMoreItems);
1156 NS_ENSURE_SUCCESS(rv, rv);
1157
1158 if (mBinding) {
1159 uint32_t attributes = mBinding->GetAttrCount();
1160 nsAutoString attrValue;
1161 for (uint32_t i = 0; i < attributes; ++i) {
1162 const nsAttrName* attr = mBinding->GetAttrNameAt(i);
1163 nsDependentAtomString attrName = attr->LocalName();
1164 mBinding->GetAttr(attr->NamespaceID(), attr->LocalName(), attrValue);
1165 rv = aStream->Write8(XBLBinding_Serialize_Attribute);
1166 NS_ENSURE_SUCCESS(rv, rv);
1167
1168 rv = WriteNamespace(aStream, attr->NamespaceID());
1169 NS_ENSURE_SUCCESS(rv, rv);
1170
1171 nsIAtom* prefix = attr->GetPrefix();
1172 nsAutoString prefixString;
1173 if (prefix) {
1174 prefix->ToString(prefixString);
1175 }
1176
1177 rv = aStream->WriteWStringZ(prefixString.get());
1178 NS_ENSURE_SUCCESS(rv, rv);
1179
1180 rv = aStream->WriteWStringZ(attrName.get());
1181 NS_ENSURE_SUCCESS(rv, rv);
1182
1183 rv = aStream->WriteWStringZ(attrValue.get());
1184 NS_ENSURE_SUCCESS(rv, rv);
1185 }
1186 }
1187
1188 aStream->Write8(XBLBinding_Serialize_NoMoreItems);
1189 NS_ENSURE_SUCCESS(rv, rv);
1190
1191 // Write out the resources
1192 if (mResources) {
1193 rv = mResources->Write(aStream);
1194 NS_ENSURE_SUCCESS(rv, rv);
1195 }
1196
1197 // Write out an end mark at the end.
1198 return aStream->Write8(XBLBinding_Serialize_NoMoreItems);
1199 }
1200
1201 nsresult
1202 nsXBLPrototypeBinding::ReadContentNode(nsIObjectInputStream* aStream,
1203 nsIDocument* aDocument,
1204 nsNodeInfoManager* aNim,
1205 nsIContent** aContent)
1206 {
1207 *aContent = nullptr;
1208
1209 int32_t namespaceID;
1210 nsresult rv = ReadNamespace(aStream, namespaceID);
1211 NS_ENSURE_SUCCESS(rv, rv);
1212
1213 // There is no content to read so just return.
1214 if (namespaceID == XBLBinding_Serialize_NoContent)
1215 return NS_OK;
1216
1217 nsCOMPtr<nsIContent> content;
1218
1219 // If this is a text type, just read the string and return.
1220 if (namespaceID == XBLBinding_Serialize_TextNode ||
1221 namespaceID == XBLBinding_Serialize_CDATANode ||
1222 namespaceID == XBLBinding_Serialize_CommentNode) {
1223 switch (namespaceID) {
1224 case XBLBinding_Serialize_TextNode:
1225 content = new nsTextNode(aNim);
1226 break;
1227 case XBLBinding_Serialize_CDATANode:
1228 content = new CDATASection(aNim);
1229 break;
1230 case XBLBinding_Serialize_CommentNode:
1231 content = new Comment(aNim);
1232 break;
1233 default:
1234 break;
1235 }
1236
1237 nsAutoString text;
1238 rv = aStream->ReadString(text);
1239 NS_ENSURE_SUCCESS(rv, rv);
1240
1241 content->SetText(text, false);
1242 content.swap(*aContent);
1243 return NS_OK;
1244 }
1245
1246 // Otherwise, it's an element, so read its tag, attributes and children.
1247 nsAutoString prefix, tag;
1248 rv = aStream->ReadString(prefix);
1249 NS_ENSURE_SUCCESS(rv, rv);
1250
1251 nsCOMPtr<nsIAtom> prefixAtom;
1252 if (!prefix.IsEmpty())
1253 prefixAtom = do_GetAtom(prefix);
1254
1255 rv = aStream->ReadString(tag);
1256 NS_ENSURE_SUCCESS(rv, rv);
1257
1258 nsCOMPtr<nsIAtom> tagAtom = do_GetAtom(tag);
1259 nsCOMPtr<nsINodeInfo> nodeInfo =
1260 aNim->GetNodeInfo(tagAtom, prefixAtom, namespaceID, nsIDOMNode::ELEMENT_NODE);
1261
1262 uint32_t attrCount;
1263 rv = aStream->Read32(&attrCount);
1264 NS_ENSURE_SUCCESS(rv, rv);
1265
1266 // Create XUL prototype elements, or regular elements for other namespaces.
1267 // This needs to match the code in nsXBLContentSink::CreateElement.
1268 #ifdef MOZ_XUL
1269 if (namespaceID == kNameSpaceID_XUL) {
1270 nsIURI* documentURI = aDocument->GetDocumentURI();
1271
1272 nsRefPtr<nsXULPrototypeElement> prototype = new nsXULPrototypeElement();
1273 NS_ENSURE_TRUE(prototype, NS_ERROR_OUT_OF_MEMORY);
1274
1275 prototype->mNodeInfo = nodeInfo;
1276
1277 nsXULPrototypeAttribute* attrs = nullptr;
1278 if (attrCount > 0) {
1279 attrs = new nsXULPrototypeAttribute[attrCount];
1280 }
1281
1282 prototype->mAttributes = attrs;
1283 prototype->mNumAttributes = attrCount;
1284
1285 for (uint32_t i = 0; i < attrCount; i++) {
1286 rv = ReadNamespace(aStream, namespaceID);
1287 NS_ENSURE_SUCCESS(rv, rv);
1288
1289 nsAutoString prefix, name, val;
1290 rv = aStream->ReadString(prefix);
1291 NS_ENSURE_SUCCESS(rv, rv);
1292 rv = aStream->ReadString(name);
1293 NS_ENSURE_SUCCESS(rv, rv);
1294 rv = aStream->ReadString(val);
1295 NS_ENSURE_SUCCESS(rv, rv);
1296
1297 nsCOMPtr<nsIAtom> nameAtom = do_GetAtom(name);
1298 if (namespaceID == kNameSpaceID_None) {
1299 attrs[i].mName.SetTo(nameAtom);
1300 }
1301 else {
1302 nsCOMPtr<nsIAtom> prefixAtom;
1303 if (!prefix.IsEmpty())
1304 prefixAtom = do_GetAtom(prefix);
1305
1306 nsCOMPtr<nsINodeInfo> ni =
1307 aNim->GetNodeInfo(nameAtom, prefixAtom,
1308 namespaceID, nsIDOMNode::ATTRIBUTE_NODE);
1309 attrs[i].mName.SetTo(ni);
1310 }
1311
1312 rv = prototype->SetAttrAt(i, val, documentURI);
1313 NS_ENSURE_SUCCESS(rv, rv);
1314 }
1315
1316 nsCOMPtr<Element> result;
1317 nsresult rv =
1318 nsXULElement::Create(prototype, aDocument, false, false, getter_AddRefs(result));
1319 NS_ENSURE_SUCCESS(rv, rv);
1320 content = result;
1321 }
1322 else {
1323 #endif
1324 nsCOMPtr<Element> element;
1325 NS_NewElement(getter_AddRefs(element), nodeInfo.forget(), NOT_FROM_PARSER);
1326 content = element;
1327
1328 for (uint32_t i = 0; i < attrCount; i++) {
1329 rv = ReadNamespace(aStream, namespaceID);
1330 NS_ENSURE_SUCCESS(rv, rv);
1331
1332 nsAutoString prefix, name, val;
1333 rv = aStream->ReadString(prefix);
1334 NS_ENSURE_SUCCESS(rv, rv);
1335 rv = aStream->ReadString(name);
1336 NS_ENSURE_SUCCESS(rv, rv);
1337 rv = aStream->ReadString(val);
1338 NS_ENSURE_SUCCESS(rv, rv);
1339
1340 nsCOMPtr<nsIAtom> prefixAtom;
1341 if (!prefix.IsEmpty())
1342 prefixAtom = do_GetAtom(prefix);
1343
1344 nsCOMPtr<nsIAtom> nameAtom = do_GetAtom(name);
1345 content->SetAttr(namespaceID, nameAtom, prefixAtom, val, false);
1346 }
1347
1348 #ifdef MOZ_XUL
1349 }
1350 #endif
1351
1352 // Now read the attribute forwarding entries (xbl:inherits)
1353
1354 int32_t srcNamespaceID, destNamespaceID;
1355 rv = ReadNamespace(aStream, srcNamespaceID);
1356 NS_ENSURE_SUCCESS(rv, rv);
1357
1358 while (srcNamespaceID != XBLBinding_Serialize_NoMoreAttributes) {
1359 nsAutoString srcAttribute, destAttribute;
1360 rv = aStream->ReadString(srcAttribute);
1361 NS_ENSURE_SUCCESS(rv, rv);
1362 rv = ReadNamespace(aStream, destNamespaceID);
1363 NS_ENSURE_SUCCESS(rv, rv);
1364 rv = aStream->ReadString(destAttribute);
1365 NS_ENSURE_SUCCESS(rv, rv);
1366
1367 nsCOMPtr<nsIAtom> srcAtom = do_GetAtom(srcAttribute);
1368 nsCOMPtr<nsIAtom> destAtom = do_GetAtom(destAttribute);
1369
1370 EnsureAttributeTable();
1371 AddToAttributeTable(srcNamespaceID, srcAtom, destNamespaceID, destAtom, content);
1372
1373 rv = ReadNamespace(aStream, srcNamespaceID);
1374 NS_ENSURE_SUCCESS(rv, rv);
1375 }
1376
1377 // Finally, read in the child nodes.
1378 uint32_t childCount;
1379 rv = aStream->Read32(&childCount);
1380 NS_ENSURE_SUCCESS(rv, rv);
1381
1382 for (uint32_t i = 0; i < childCount; i++) {
1383 nsCOMPtr<nsIContent> child;
1384 ReadContentNode(aStream, aDocument, aNim, getter_AddRefs(child));
1385
1386 // Child may be null if this was a comment for example and can just be ignored.
1387 if (child) {
1388 content->AppendChildTo(child, false);
1389 }
1390 }
1391
1392 content.swap(*aContent);
1393 return NS_OK;
1394 }
1395
1396 // This structure holds information about a forwarded attribute that needs to be
1397 // written out. This is used because we need several fields passed within the
1398 // enumeration closure.
1399 struct WriteAttributeData
1400 {
1401 nsXBLPrototypeBinding* binding;
1402 nsIObjectOutputStream* stream;
1403 nsIContent* content;
1404 int32_t srcNamespace;
1405
1406 WriteAttributeData(nsXBLPrototypeBinding* aBinding,
1407 nsIObjectOutputStream* aStream,
1408 nsIContent* aContent)
1409 : binding(aBinding), stream(aStream), content(aContent)
1410 { }
1411 };
1412
1413 static PLDHashOperator
1414 WriteAttribute(nsISupports* aKey, nsXBLAttributeEntry* aEntry, void* aClosure)
1415 {
1416 WriteAttributeData* data = static_cast<WriteAttributeData *>(aClosure);
1417 nsIObjectOutputStream* stream = data->stream;
1418 const int32_t srcNamespace = data->srcNamespace;
1419
1420 do {
1421 if (aEntry->GetElement() == data->content) {
1422 data->binding->WriteNamespace(stream, srcNamespace);
1423 stream->WriteWStringZ(nsDependentAtomString(aEntry->GetSrcAttribute()).get());
1424 data->binding->WriteNamespace(stream, aEntry->GetDstNameSpace());
1425 stream->WriteWStringZ(nsDependentAtomString(aEntry->GetDstAttribute()).get());
1426 }
1427
1428 aEntry = aEntry->GetNext();
1429 } while (aEntry);
1430
1431 return PL_DHASH_NEXT;
1432 }
1433
1434 // WriteAttributeNS is the callback to enumerate over the attribute
1435 // forwarding entries. Since these are stored in a hash of hashes,
1436 // we need to iterate over the inner hashes, calling WriteAttribute
1437 // to do the actual work.
1438 static PLDHashOperator
1439 WriteAttributeNS(const uint32_t &aNamespace,
1440 nsXBLPrototypeBinding::InnerAttributeTable* aXBLAttributes,
1441 void* aClosure)
1442 {
1443 WriteAttributeData* data = static_cast<WriteAttributeData *>(aClosure);
1444 data->srcNamespace = aNamespace;
1445 aXBLAttributes->EnumerateRead(WriteAttribute, data);
1446
1447 return PL_DHASH_NEXT;
1448 }
1449
1450 nsresult
1451 nsXBLPrototypeBinding::WriteContentNode(nsIObjectOutputStream* aStream,
1452 nsIContent* aNode)
1453 {
1454 nsresult rv;
1455
1456 if (!aNode->IsElement()) {
1457 // Text is writen out as a single byte for the type, followed by the text.
1458 uint8_t type = XBLBinding_Serialize_NoContent;
1459 switch (aNode->NodeType()) {
1460 case nsIDOMNode::TEXT_NODE:
1461 type = XBLBinding_Serialize_TextNode;
1462 break;
1463 case nsIDOMNode::CDATA_SECTION_NODE:
1464 type = XBLBinding_Serialize_CDATANode;
1465 break;
1466 case nsIDOMNode::COMMENT_NODE:
1467 type = XBLBinding_Serialize_CommentNode;
1468 break;
1469 default:
1470 break;
1471 }
1472
1473 rv = aStream->Write8(type);
1474 NS_ENSURE_SUCCESS(rv, rv);
1475
1476 nsAutoString content;
1477 aNode->GetText()->AppendTo(content);
1478 return aStream->WriteWStringZ(content.get());
1479 }
1480
1481 // Otherwise, this is an element.
1482
1483 // Write the namespace id followed by the tag name
1484 rv = WriteNamespace(aStream, aNode->GetNameSpaceID());
1485 NS_ENSURE_SUCCESS(rv, rv);
1486
1487 nsAutoString prefixStr;
1488 aNode->NodeInfo()->GetPrefix(prefixStr);
1489 rv = aStream->WriteWStringZ(prefixStr.get());
1490 NS_ENSURE_SUCCESS(rv, rv);
1491
1492 rv = aStream->WriteWStringZ(nsDependentAtomString(aNode->Tag()).get());
1493 NS_ENSURE_SUCCESS(rv, rv);
1494
1495 // Write attributes
1496 uint32_t count = aNode->GetAttrCount();
1497 rv = aStream->Write32(count);
1498 NS_ENSURE_SUCCESS(rv, rv);
1499
1500 uint32_t i;
1501 for (i = 0; i < count; i++) {
1502 // Write out the namespace id, the namespace prefix, the local tag name,
1503 // and the value, in that order.
1504
1505 const nsAttrName* attr = aNode->GetAttrNameAt(i);
1506
1507 // XXXndeakin don't write out xbl:inherits?
1508 int32_t namespaceID = attr->NamespaceID();
1509 rv = WriteNamespace(aStream, namespaceID);
1510 NS_ENSURE_SUCCESS(rv, rv);
1511
1512 nsAutoString prefixStr;
1513 nsIAtom* prefix = attr->GetPrefix();
1514 if (prefix)
1515 prefix->ToString(prefixStr);
1516 rv = aStream->WriteWStringZ(prefixStr.get());
1517 NS_ENSURE_SUCCESS(rv, rv);
1518
1519 rv = aStream->WriteWStringZ(nsDependentAtomString(attr->LocalName()).get());
1520 NS_ENSURE_SUCCESS(rv, rv);
1521
1522 nsAutoString val;
1523 aNode->GetAttr(attr->NamespaceID(), attr->LocalName(), val);
1524 rv = aStream->WriteWStringZ(val.get());
1525 NS_ENSURE_SUCCESS(rv, rv);
1526 }
1527
1528 // Write out the attribute fowarding information
1529 if (mAttributeTable) {
1530 WriteAttributeData data(this, aStream, aNode);
1531 mAttributeTable->EnumerateRead(WriteAttributeNS, &data);
1532 }
1533 rv = aStream->Write8(XBLBinding_Serialize_NoMoreAttributes);
1534 NS_ENSURE_SUCCESS(rv, rv);
1535
1536 // Finally, write out the child nodes.
1537 count = aNode->GetChildCount();
1538 rv = aStream->Write32(count);
1539 NS_ENSURE_SUCCESS(rv, rv);
1540
1541 for (i = 0; i < count; i++) {
1542 rv = WriteContentNode(aStream, aNode->GetChildAt(i));
1543 NS_ENSURE_SUCCESS(rv, rv);
1544 }
1545
1546 return NS_OK;
1547 }
1548
1549 nsresult
1550 nsXBLPrototypeBinding::ReadNamespace(nsIObjectInputStream* aStream,
1551 int32_t& aNameSpaceID)
1552 {
1553 uint8_t namespaceID;
1554 nsresult rv = aStream->Read8(&namespaceID);
1555 NS_ENSURE_SUCCESS(rv, rv);
1556
1557 if (namespaceID == XBLBinding_Serialize_CustomNamespace) {
1558 nsAutoString namesp;
1559 rv = aStream->ReadString(namesp);
1560 NS_ENSURE_SUCCESS(rv, rv);
1561
1562 nsContentUtils::NameSpaceManager()->RegisterNameSpace(namesp, aNameSpaceID);
1563 }
1564 else {
1565 aNameSpaceID = namespaceID;
1566 }
1567
1568 return NS_OK;
1569 }
1570
1571 nsresult
1572 nsXBLPrototypeBinding::WriteNamespace(nsIObjectOutputStream* aStream,
1573 int32_t aNameSpaceID)
1574 {
1575 // Namespaces are stored as a single byte id for well-known namespaces.
1576 // This saves time and space as other namespaces aren't very common in
1577 // XBL. If another namespace is used however, the namespace id will be
1578 // XBLBinding_Serialize_CustomNamespace and the string namespace written
1579 // out directly afterwards.
1580 nsresult rv;
1581
1582 if (aNameSpaceID <= kNameSpaceID_LastBuiltin) {
1583 rv = aStream->Write8((int8_t)aNameSpaceID);
1584 NS_ENSURE_SUCCESS(rv, rv);
1585 }
1586 else {
1587 rv = aStream->Write8(XBLBinding_Serialize_CustomNamespace);
1588 NS_ENSURE_SUCCESS(rv, rv);
1589
1590 nsAutoString namesp;
1591 nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNameSpaceID, namesp);
1592 aStream->WriteWStringZ(namesp.get());
1593 }
1594
1595 return NS_OK;
1596 }
1597
1598
1599 bool CheckTagNameWhiteList(int32_t aNameSpaceID, nsIAtom *aTagName)
1600 {
1601 static nsIContent::AttrValuesArray kValidXULTagNames[] = {
1602 &nsGkAtoms::autorepeatbutton, &nsGkAtoms::box, &nsGkAtoms::browser,
1603 &nsGkAtoms::button, &nsGkAtoms::hbox, &nsGkAtoms::image, &nsGkAtoms::menu,
1604 &nsGkAtoms::menubar, &nsGkAtoms::menuitem, &nsGkAtoms::menupopup,
1605 &nsGkAtoms::row, &nsGkAtoms::slider, &nsGkAtoms::spacer,
1606 &nsGkAtoms::splitter, &nsGkAtoms::text, &nsGkAtoms::tree, nullptr};
1607
1608 uint32_t i;
1609 if (aNameSpaceID == kNameSpaceID_XUL) {
1610 for (i = 0; kValidXULTagNames[i]; ++i) {
1611 if (aTagName == *(kValidXULTagNames[i])) {
1612 return true;
1613 }
1614 }
1615 }
1616 else if (aNameSpaceID == kNameSpaceID_SVG &&
1617 aTagName == nsGkAtoms::generic_) {
1618 return true;
1619 }
1620
1621 return false;
1622 }
1623
1624 nsresult
1625 nsXBLPrototypeBinding::ResolveBaseBinding()
1626 {
1627 if (mCheckedBaseProto)
1628 return NS_OK;
1629 mCheckedBaseProto = true;
1630
1631 nsCOMPtr<nsIDocument> doc = mXBLDocInfoWeak->GetDocument();
1632
1633 // Check for the presence of 'extends' and 'display' attributes
1634 nsAutoString display, extends;
1635 mBinding->GetAttr(kNameSpaceID_None, nsGkAtoms::extends, extends);
1636 if (extends.IsEmpty())
1637 return NS_OK;
1638
1639 mBinding->GetAttr(kNameSpaceID_None, nsGkAtoms::display, display);
1640 bool hasDisplay = !display.IsEmpty();
1641
1642 nsAutoString value(extends);
1643
1644 // Now slice 'em up to see what we've got.
1645 nsAutoString prefix;
1646 int32_t offset;
1647 if (hasDisplay) {
1648 offset = display.FindChar(':');
1649 if (-1 != offset) {
1650 display.Left(prefix, offset);
1651 display.Cut(0, offset+1);
1652 }
1653 }
1654 else {
1655 offset = extends.FindChar(':');
1656 if (-1 != offset) {
1657 extends.Left(prefix, offset);
1658 extends.Cut(0, offset+1);
1659 display = extends;
1660 }
1661 }
1662
1663 nsAutoString nameSpace;
1664
1665 if (!prefix.IsEmpty()) {
1666 mBinding->LookupNamespaceURI(prefix, nameSpace);
1667 if (!nameSpace.IsEmpty()) {
1668 int32_t nameSpaceID =
1669 nsContentUtils::NameSpaceManager()->GetNameSpaceID(nameSpace);
1670
1671 nsCOMPtr<nsIAtom> tagName = do_GetAtom(display);
1672 // Check the white list
1673 if (!CheckTagNameWhiteList(nameSpaceID, tagName)) {
1674 const char16_t* params[] = { display.get() };
1675 nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
1676 NS_LITERAL_CSTRING("XBL"), nullptr,
1677 nsContentUtils::eXBL_PROPERTIES,
1678 "InvalidExtendsBinding",
1679 params, ArrayLength(params),
1680 doc->GetDocumentURI());
1681 NS_ASSERTION(!nsXBLService::IsChromeOrResourceURI(doc->GetDocumentURI()),
1682 "Invalid extends value");
1683 return NS_ERROR_ILLEGAL_VALUE;
1684 }
1685
1686 SetBaseTag(nameSpaceID, tagName);
1687 }
1688 }
1689
1690 if (hasDisplay || nameSpace.IsEmpty()) {
1691 mBinding->UnsetAttr(kNameSpaceID_None, nsGkAtoms::extends, false);
1692 mBinding->UnsetAttr(kNameSpaceID_None, nsGkAtoms::display, false);
1693
1694 return NS_NewURI(getter_AddRefs(mBaseBindingURI), value,
1695 doc->GetDocumentCharacterSet().get(),
1696 doc->GetDocBaseURI());
1697 }
1698
1699 return NS_OK;
1700 }

mercurial