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