dom/xbl/nsXBLPrototypeBinding.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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/. */
     6 #include "mozilla/ArrayUtils.h"
     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"
    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"
    46 #ifdef MOZ_XUL
    47 #include "nsXULElement.h"
    48 #endif
    50 using namespace mozilla;
    51 using namespace mozilla::dom;
    53 // Helper Classes =====================================================================
    55 // nsXBLAttributeEntry and helpers.  This class is used to efficiently handle
    56 // attribute changes in anonymous content.
    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) { }
    68   ~nsXBLAttributeEntry() {
    69     NS_CONTENT_DELETE_LIST_MEMBER(nsXBLAttributeEntry, this, mNext);
    70   }
    72   nsIAtom* GetSrcAttribute() { return mSrcAttribute; }
    73   nsIAtom* GetDstAttribute() { return mDstAttribute; }
    74   int32_t GetDstNameSpace() { return mDstNameSpace; }
    76   nsIContent* GetElement() { return mElement; }
    78   nsXBLAttributeEntry* GetNext() { return mNext; }
    79   void SetNext(nsXBLAttributeEntry* aEntry) { mNext = aEntry; }
    81 protected:
    82   nsIContent* mElement;
    84   nsCOMPtr<nsIAtom> mSrcAttribute;
    85   nsCOMPtr<nsIAtom> mDstAttribute;
    86   int32_t mDstNameSpace;
    87   nsXBLAttributeEntry* mNext;
    88 };
    90 // =============================================================================
    92 // Implementation /////////////////////////////////////////////////////////////////
    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 }
   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);
   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);
   125   mXBLDocInfoWeak = aInfo;
   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 }
   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 }
   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 }
   156 void
   157 nsXBLPrototypeBinding::UnlinkJSObjects()
   158 {
   159   if (mImplementation)
   160     mImplementation->UnlinkJSObjects();
   161 }
   163 void
   164 nsXBLPrototypeBinding::Trace(const TraceCallbacks& aCallbacks, void *aClosure) const
   165 {
   166   if (mImplementation)
   167     mImplementation->Trace(aCallbacks, aClosure);
   168 }
   170 void
   171 nsXBLPrototypeBinding::Initialize()
   172 {
   173   nsIContent* content = GetImmediateChild(nsGkAtoms::content);
   174   if (content) {
   175     ConstructAttributeTable(content);
   176   }
   177 }
   179 nsXBLPrototypeBinding::~nsXBLPrototypeBinding(void)
   180 {
   181   delete mImplementation;
   182   MOZ_COUNT_DTOR(nsXBLPrototypeBinding);
   183 }
   185 void
   186 nsXBLPrototypeBinding::SetBasePrototype(nsXBLPrototypeBinding* aBinding)
   187 {
   188   if (mBaseBinding == aBinding)
   189     return;
   191   if (mBaseBinding) {
   192     NS_ERROR("Base XBL prototype binding is already defined!");
   193     return;
   194   }
   196   mBaseBinding = aBinding;
   197 }
   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;
   207   mChromeOnlyContent = mBinding->AttrValueIs(kNameSpaceID_None,
   208                                              nsGkAtoms::chromeOnlyContent,
   209                                              nsGkAtoms::_true, eCaseMatters);
   210 }
   212 bool
   213 nsXBLPrototypeBinding::GetAllowScripts() const
   214 {
   215   return mXBLDocInfoWeak->GetScriptAccess();
   216 }
   218 bool
   219 nsXBLPrototypeBinding::LoadResources()
   220 {
   221   if (mResources) {
   222     bool result;
   223     mResources->LoadResources(&result);
   224     return result;
   225   }
   227   return true;
   228 }
   230 nsresult
   231 nsXBLPrototypeBinding::AddResource(nsIAtom* aResourceType, const nsAString& aSrc)
   232 {
   233   if (!mResources) {
   234     mResources = new nsXBLPrototypeResources(this);
   235   }
   237   mResources->AddResource(aResourceType, aSrc);
   238   return NS_OK;
   239 }
   241 nsresult
   242 nsXBLPrototypeBinding::FlushSkinSheets()
   243 {
   244   if (mResources)
   245     return mResources->FlushSkinSheets();
   246   return NS_OK;
   247 }
   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 }
   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 }
   267 nsXBLProtoImplAnonymousMethod*
   268 nsXBLPrototypeBinding::GetConstructor()
   269 {
   270   if (mImplementation)
   271     return mImplementation->mConstructor;
   273   return nullptr;
   274 }
   276 nsXBLProtoImplAnonymousMethod*
   277 nsXBLPrototypeBinding::GetDestructor()
   278 {
   279   if (mImplementation)
   280     return mImplementation->mDestructor;
   282   return nullptr;
   283 }
   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 }
   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 }
   303 nsresult
   304 nsXBLPrototypeBinding::InstallImplementation(nsXBLBinding* aBinding)
   305 {
   306   if (mImplementation)
   307     return mImplementation->InstallImplementation(this, aBinding);
   308   return NS_OK;
   309 }
   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;
   323   InnerAttributeTable *attributesNS = mAttributeTable->Get(aNameSpaceID);
   324   if (!attributesNS)
   325     return;
   327   nsXBLAttributeEntry* xblAttr = attributesNS->Get(aAttribute);
   328   if (!xblAttr)
   329     return;
   331   // Iterate over the elements in the array.
   332   nsCOMPtr<nsIContent> content = GetImmediateChild(nsGkAtoms::content);
   333   while (xblAttr) {
   334     nsIContent* element = xblAttr->GetElement();
   336     nsCOMPtr<nsIContent> realElement = LocateInstance(aChangedElement, content,
   337                                                       aAnonymousContent,
   338                                                       element);
   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();
   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         }
   368         if (attrPresent)
   369           realElement->SetAttr(dstNs, dstAttr, value, aNotify);
   370       }
   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.
   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);
   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());
   393             textContent->SetText(value, true);
   394             realElement->AppendChildTo(textContent, true);
   395           }
   396         }
   397       }
   398     }
   400     xblAttr = xblAttr->GetNext();
   401   }
   402 }
   404 void
   405 nsXBLPrototypeBinding::SetBaseTag(int32_t aNamespaceID, nsIAtom* aTag)
   406 {
   407   mBaseNameSpaceID = aNamespaceID;
   408   mBaseTag = aTag;
   409 }
   411 nsIAtom*
   412 nsXBLPrototypeBinding::GetBaseTag(int32_t* aNamespaceID)
   413 {
   414   if (mBaseTag) {
   415     *aNamespaceID = mBaseNameSpaceID;
   416     return mBaseTag;
   417   }
   419   return nullptr;
   420 }
   422 bool
   423 nsXBLPrototypeBinding::ImplementsInterface(REFNSIID aIID) const
   424 {
   425   // Check our IID table.
   426   return !!mInterfaceTable.GetWeak(aIID);
   427 }
   429 // Internal helpers ///////////////////////////////////////////////////////////////////////
   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   }
   442   return nullptr;
   443 }
   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 }
   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;
   467   nsIContent* templParent = aTemplChild->GetParent();
   469   // We may be disconnected from our parent during cycle collection.
   470   if (!templParent)
   471     return nullptr;
   473   nsIContent *copyParent =
   474     templParent == aTemplRoot ? aCopyRoot :
   475                    LocateInstance(aBoundElement, aTemplRoot, aCopyRoot, templParent);
   477   if (!copyParent)
   478     return nullptr;
   480   return copyParent->GetChildAt(templParent->IndexOf(aTemplChild));
   481 }
   483 struct nsXBLAttrChangeData
   484 {
   485   nsXBLPrototypeBinding* mProto;
   486   nsIContent* mBoundElement;
   487   nsIContent* mContent;
   488   int32_t mSrcNamespace;
   490   nsXBLAttrChangeData(nsXBLPrototypeBinding* aProto,
   491                       nsIContent* aElt, nsIContent* aContent) 
   492   :mProto(aProto), mBoundElement(aElt), mContent(aContent) {}
   493 };
   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);
   501   nsIAtom* src = aEntry->GetSrcAttribute();
   502   int32_t srcNs = changeData->mSrcNamespace;
   503   nsAutoString value;
   504   bool attrPresent = true;
   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();
   516     if (stripVal.IsEmpty()) 
   517       attrPresent = false;
   518   }
   519   else {
   520     attrPresent = changeData->mBoundElement->GetAttr(srcNs, src, value);
   521   }
   523   if (attrPresent) {
   524     nsIContent* content =
   525       changeData->mProto->GetImmediateChild(nsGkAtoms::content);
   527     nsXBLAttributeEntry* curr = aEntry;
   528     while (curr) {
   529       nsIAtom* dst = curr->GetDstAttribute();
   530       int32_t dstNs = curr->GetDstNameSpace();
   531       nsIContent* element = curr->GetElement();
   533       nsIContent *realElement =
   534         changeData->mProto->LocateInstance(changeData->mBoundElement, content,
   535                                            changeData->mContent, element);
   537       if (realElement) {
   538         realElement->SetAttr(dstNs, dst, value, false);
   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())) {
   546           nsRefPtr<nsTextNode> textContent =
   547             new nsTextNode(realElement->NodeInfo()->NodeInfoManager());
   549           textContent->SetText(value, false);
   550           realElement->AppendChildTo(textContent, false);
   551         }
   552       }
   554       curr = curr->GetNext();
   555     }
   556   }
   558   return PL_DHASH_NEXT;
   559 }
   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 }
   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 }
   583 nsIStyleRuleProcessor*
   584 nsXBLPrototypeBinding::GetRuleProcessor()
   585 {
   586   if (mResources) {
   587     return mResources->mRuleProcessor;
   588   }
   590   return nullptr;
   591 }
   593 nsXBLPrototypeResources::sheet_array_type*
   594 nsXBLPrototypeBinding::GetOrCreateStyleSheets()
   595 {
   596   if (!mResources) {
   597     mResources = new nsXBLPrototypeResources(this);
   598   }
   600   return &mResources->mStyleSheetList;
   601 }
   603 nsXBLPrototypeResources::sheet_array_type*
   604 nsXBLPrototypeBinding::GetStyleSheets()
   605 {
   606   if (mResources) {
   607     return &mResources->mStyleSheetList;
   608   }
   610   return nullptr;
   611 }
   613 void
   614 nsXBLPrototypeBinding::EnsureAttributeTable()
   615 {
   616   if (!mAttributeTable) {
   617     mAttributeTable = new nsClassHashtable<nsUint32HashKey, InnerAttributeTable>(4);
   618   }
   619 }
   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     }
   632     nsXBLAttributeEntry* xblAttr =
   633       new nsXBLAttributeEntry(aSourceTag, aDestTag, aDestNamespaceID, aContent);
   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 }
   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);
   654     if (!inherits.IsEmpty()) {
   655       EnsureAttributeTable();
   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
   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;
   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);
   681           rv = nsContentUtils::SplitQName(aElement, left, &attributeNsID,
   682                                           getter_AddRefs(attribute));
   683           if (NS_FAILED(rv))
   684             return;
   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         }
   702         AddToAttributeTable(atomNsID, atom, attributeNsID, attribute, aElement);
   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);
   711         token = nsCRT::strtok( newStr, ", ", &newStr );
   712       }
   714       nsMemory::Free(str);
   715     }
   716   }
   718   // Recur into our children.
   719   for (nsIContent* child = aElement->GetFirstChild();
   720        child;
   721        child = child->GetNextSibling()) {
   722     ConstructAttributeTable(child);
   723   }
   724 }
   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;
   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
   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));
   750       if (iinfo) {
   751         // obtain an IID.
   752         const nsIID* iid = nullptr;
   753         iinfo->GetIIDShared(&iid);
   755         if (iid) {
   756           // We found a valid iid.  Add it to our table.
   757           mInterfaceTable.Put(*iid, mBinding);
   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);
   767             // don't add nsISupports to the table
   768             if (!iid || iid->Equals(NS_GET_IID(nsISupports)))
   769               break;
   771             // add the iid to the table
   772             mInterfaceTable.Put(*iid, mBinding);
   774             // look for the next parent
   775             iinfo = parentInfo;
   776           }
   777         }
   778       }
   780       token = nsCRT::strtok( newStr, ", ", &newStr );
   781     }
   782   }
   784   return NS_OK;
   785 }
   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.
   794   mResources->AddResourceListener(aBoundElement);
   795   return NS_OK;
   796 }
   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();
   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       }
   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       }
   828       if (handler)
   829         handler->AddProtoHandler(curr);
   830     }
   832     curr = curr->GetNextHandler();
   833   }
   834 }
   836 class XBLPrototypeSetupCleanup
   837 {
   838 public:
   839   XBLPrototypeSetupCleanup(nsXBLDocumentInfo* aDocInfo, const nsACString& aID)
   840   : mDocInfo(aDocInfo), mID(aID) {}
   842   ~XBLPrototypeSetupCleanup()
   843   {
   844     if (mDocInfo) {
   845       mDocInfo->RemovePrototypeBinding(mID);
   846     }
   847   }
   849   void Disconnect()
   850   {
   851     mDocInfo = nullptr;
   852   }
   854   nsXBLDocumentInfo* mDocInfo;
   855   nsAutoCString mID;
   856 };
   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;
   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);
   873   NS_ENSURE_SUCCESS(rv, rv);
   874   NS_ENSURE_TRUE(!id.IsEmpty(), NS_ERROR_FAILURE);
   876   nsAutoCString baseBindingURI;
   877   rv = aStream->ReadCString(baseBindingURI);
   878   NS_ENSURE_SUCCESS(rv, rv);
   879   mCheckedBaseProto = true;
   881   if (!baseBindingURI.IsEmpty()) {
   882     rv = NS_NewURI(getter_AddRefs(mBaseBindingURI), baseBindingURI);
   883     NS_ENSURE_SUCCESS(rv, rv);
   884   }
   886   rv = ReadNamespace(aStream, mBaseNameSpaceID);
   887   NS_ENSURE_SUCCESS(rv, rv);
   889   nsAutoString baseTag;
   890   rv = aStream->ReadString(baseTag);
   891   NS_ENSURE_SUCCESS(rv, rv);
   892   if (!baseTag.IsEmpty()) {
   893     mBaseTag = do_GetAtom(baseTag);
   894   }
   896   aDocument->CreateElem(NS_LITERAL_STRING("binding"), nullptr, kNameSpaceID_XBL,
   897                         getter_AddRefs(mBinding));
   899   nsCOMPtr<nsIContent> child;
   900   rv = ReadContentNode(aStream, aDocument, aDocument->NodeInfoManager(), getter_AddRefs(child));
   901   NS_ENSURE_SUCCESS(rv, rv);
   903   Element* rootElement = aDocument->GetRootElement();
   904   if (rootElement)
   905     rootElement->AppendChildTo(mBinding, false);
   907   if (child) {
   908     mBinding->AppendChildTo(child, false);
   909   }
   911   uint32_t interfaceCount;
   912   rv = aStream->Read32(&interfaceCount);
   913   NS_ENSURE_SUCCESS(rv, rv);
   915   for (; interfaceCount > 0; interfaceCount--) {
   916     nsIID iid;
   917     aStream->ReadID(&iid);
   918     mInterfaceTable.Put(iid, mBinding);
   919   }
   921   AutoSafeJSContext cx;
   922   JS::Rooted<JSObject*> compilationGlobal(cx, xpc::GetCompilationScope());
   923   NS_ENSURE_TRUE(compilationGlobal, NS_ERROR_UNEXPECTED);
   924   JSAutoCompartment ac(cx, compilationGlobal);
   926   bool isFirstBinding = aFlags & XBLBinding_Serialize_IsFirstBinding;
   927   rv = Init(id, aDocInfo, nullptr, isFirstBinding);
   928   NS_ENSURE_SUCCESS(rv, rv);
   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);
   935   XBLPrototypeSetupCleanup cleanup(aDocInfo, id);  
   937   nsAutoCString className;
   938   rv = aStream->ReadCString(className);
   939   NS_ENSURE_SUCCESS(rv, rv);
   941   if (!className.IsEmpty()) {
   942     nsXBLProtoImpl* impl; // NS_NewXBLProtoImpl will set mImplementation for us
   943     NS_NewXBLProtoImpl(this, NS_ConvertUTF8toUTF16(className).get(), &impl);
   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   }
   953   // Next read in the handlers.
   954   nsXBLPrototypeHandler* previousHandler = nullptr;
   956   do {
   957     XBLBindingSerializeDetails type;
   958     rv = aStream->Read8(&type);
   959     NS_ENSURE_SUCCESS(rv, rv);
   961     if (type == XBLBinding_Serialize_NoMoreItems)
   962       break;
   964     NS_ASSERTION((type & XBLBinding_Serialize_Mask) == XBLBinding_Serialize_Handler,
   965                  "invalid handler type");
   967     nsXBLPrototypeHandler* handler = new nsXBLPrototypeHandler(this);
   968     rv = handler->Read(aStream);
   969     if (NS_FAILED(rv)) {
   970       delete handler;
   971       return rv;
   972     }
   974     if (previousHandler) {
   975       previousHandler->SetNextHandler(handler);
   976     }
   977     else {
   978       SetPrototypeHandlers(handler);
   979     }
   980     previousHandler = handler;
   981   } while (1);
   983   if (mBinding) {
   984     while (true) {
   985       XBLBindingSerializeDetails type;
   986       rv = aStream->Read8(&type);
   987       NS_ENSURE_SUCCESS(rv, rv);
   989       if (type != XBLBinding_Serialize_Attribute) {
   990         break;
   991       }
   993       int32_t attrNamespace;
   994       rv = ReadNamespace(aStream, attrNamespace);
   995       NS_ENSURE_SUCCESS(rv, rv);
   997       nsAutoString attrPrefix, attrName, attrValue;
   998       rv = aStream->ReadString(attrPrefix);
   999       NS_ENSURE_SUCCESS(rv, rv);
  1001       rv = aStream->ReadString(attrName);
  1002       NS_ENSURE_SUCCESS(rv, rv);
  1004       rv = aStream->ReadString(attrValue);
  1005       NS_ENSURE_SUCCESS(rv, rv);
  1007       nsCOMPtr<nsIAtom> atomPrefix = do_GetAtom(attrPrefix);
  1008       nsCOMPtr<nsIAtom> atomName = do_GetAtom(attrName);
  1009       mBinding->SetAttr(attrNamespace, atomName, atomPrefix, attrValue, false);
  1013   // Finally, read in the resources.
  1014   while (true) {
  1015     XBLBindingSerializeDetails type;
  1016     rv = aStream->Read8(&type);
  1017     NS_ENSURE_SUCCESS(rv, rv);
  1019     if (type == XBLBinding_Serialize_NoMoreItems)
  1020       break;
  1022     NS_ASSERTION((type & XBLBinding_Serialize_Mask) == XBLBinding_Serialize_Stylesheet ||
  1023                  (type & XBLBinding_Serialize_Mask) == XBLBinding_Serialize_Image, "invalid resource type");
  1025     nsAutoString src;
  1026     rv = aStream->ReadString(src);
  1027     NS_ENSURE_SUCCESS(rv, rv);
  1029     AddResource(type == XBLBinding_Serialize_Stylesheet ? nsGkAtoms::stylesheet :
  1030                                                           nsGkAtoms::image, src);
  1033   if (isFirstBinding) {
  1034     aDocInfo->SetFirstPrototypeBinding(this);
  1037   cleanup.Disconnect();
  1038   return NS_OK;
  1041 // static
  1042 nsresult
  1043 nsXBLPrototypeBinding::ReadNewBinding(nsIObjectInputStream* aStream,
  1044                                       nsXBLDocumentInfo* aDocInfo,
  1045                                       nsIDocument* aDocument,
  1046                                       uint8_t aFlags)
  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;
  1055   return rv;
  1058 static PLDHashOperator
  1059 WriteInterfaceID(const nsIID& aKey, nsIContent* aData, void* aClosure)
  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;
  1067 nsresult
  1068 nsXBLPrototypeBinding::Write(nsIObjectOutputStream* aStream)
  1070   // This writes out the binding. Note that mCheckedBaseProto,
  1071   // mKeyHandlersRegistered and mKeyHandlers are not serialized as they are
  1072   // computed on demand.
  1074   AutoSafeJSContext cx;
  1075   JS::Rooted<JSObject*> compilationGlobal(cx, xpc::GetCompilationScope());
  1076   NS_ENSURE_TRUE(compilationGlobal, NS_ERROR_UNEXPECTED);
  1077   JSAutoCompartment ac(cx, compilationGlobal);
  1079   uint8_t flags = mInheritStyle ? XBLBinding_Serialize_InheritStyle : 0;
  1081   // mAlternateBindingURI is only set on the first binding.
  1082   if (mAlternateBindingURI) {
  1083     flags |= XBLBinding_Serialize_IsFirstBinding;
  1086   if (mChromeOnlyContent) {
  1087     flags |= XBLBinding_Serialize_ChromeOnlyContent;
  1090   nsresult rv = aStream->Write8(flags);
  1091   NS_ENSURE_SUCCESS(rv, rv);
  1093   nsAutoCString id;
  1094   mBindingURI->GetRef(id);
  1095   rv = aStream->WriteStringZ(id.get());
  1096   NS_ENSURE_SUCCESS(rv, rv);
  1098   // write out the extends and display attribute values
  1099   nsAutoCString extends;
  1100   ResolveBaseBinding();
  1101   if (mBaseBindingURI)
  1102     mBaseBindingURI->GetSpec(extends);
  1104   rv = aStream->WriteStringZ(extends.get());
  1105   NS_ENSURE_SUCCESS(rv, rv);
  1107   rv = WriteNamespace(aStream, mBaseNameSpaceID);
  1108   NS_ENSURE_SUCCESS(rv, rv);
  1110   nsAutoString baseTag;
  1111   if (mBaseTag) {
  1112     mBaseTag->ToString(baseTag);
  1114   rv = aStream->WriteWStringZ(baseTag.get());
  1115   NS_ENSURE_SUCCESS(rv, rv);
  1117   nsIContent* content = GetImmediateChild(nsGkAtoms::content);
  1118   if (content) {
  1119     rv = WriteContentNode(aStream, content);
  1120     NS_ENSURE_SUCCESS(rv, rv);
  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);
  1128   // Enumerate and write out the implemented interfaces.
  1129   rv = aStream->Write32(mInterfaceTable.Count());
  1130   NS_ENSURE_SUCCESS(rv, rv);
  1132   mInterfaceTable.EnumerateRead(WriteInterfaceID, aStream);
  1134   // Write out the implementation details.
  1135   if (mImplementation) {
  1136     rv = mImplementation->Write(aStream, this);
  1137     NS_ENSURE_SUCCESS(rv, rv);
  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);
  1146   // Write out the handlers.
  1147   nsXBLPrototypeHandler* handler = mPrototypeHandler;
  1148   while (handler) {
  1149     rv = handler->Write(aStream);
  1150     NS_ENSURE_SUCCESS(rv, rv);
  1152     handler = handler->GetNextHandler();
  1155   aStream->Write8(XBLBinding_Serialize_NoMoreItems);
  1156   NS_ENSURE_SUCCESS(rv, rv);
  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);
  1168       rv = WriteNamespace(aStream, attr->NamespaceID());
  1169       NS_ENSURE_SUCCESS(rv, rv);
  1171       nsIAtom* prefix = attr->GetPrefix();
  1172       nsAutoString prefixString;
  1173       if (prefix) {
  1174         prefix->ToString(prefixString);
  1177       rv = aStream->WriteWStringZ(prefixString.get());
  1178       NS_ENSURE_SUCCESS(rv, rv);
  1180       rv = aStream->WriteWStringZ(attrName.get());
  1181       NS_ENSURE_SUCCESS(rv, rv);
  1183       rv = aStream->WriteWStringZ(attrValue.get());
  1184       NS_ENSURE_SUCCESS(rv, rv);
  1188   aStream->Write8(XBLBinding_Serialize_NoMoreItems);
  1189   NS_ENSURE_SUCCESS(rv, rv);
  1191   // Write out the resources
  1192   if (mResources) {
  1193     rv = mResources->Write(aStream);
  1194     NS_ENSURE_SUCCESS(rv, rv);
  1197   // Write out an end mark at the end.
  1198   return aStream->Write8(XBLBinding_Serialize_NoMoreItems);
  1201 nsresult
  1202 nsXBLPrototypeBinding::ReadContentNode(nsIObjectInputStream* aStream,
  1203                                        nsIDocument* aDocument,
  1204                                        nsNodeInfoManager* aNim,
  1205                                        nsIContent** aContent)
  1207   *aContent = nullptr;
  1209   int32_t namespaceID;
  1210   nsresult rv = ReadNamespace(aStream, namespaceID);
  1211   NS_ENSURE_SUCCESS(rv, rv);
  1213   // There is no content to read so just return.
  1214   if (namespaceID == XBLBinding_Serialize_NoContent)
  1215     return NS_OK;
  1217   nsCOMPtr<nsIContent> content;
  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;
  1237     nsAutoString text;
  1238     rv = aStream->ReadString(text);
  1239     NS_ENSURE_SUCCESS(rv, rv);
  1241     content->SetText(text, false);
  1242     content.swap(*aContent);
  1243     return NS_OK;
  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);
  1251   nsCOMPtr<nsIAtom> prefixAtom;
  1252   if (!prefix.IsEmpty())
  1253     prefixAtom = do_GetAtom(prefix);
  1255   rv = aStream->ReadString(tag);
  1256   NS_ENSURE_SUCCESS(rv, rv);
  1258   nsCOMPtr<nsIAtom> tagAtom = do_GetAtom(tag);
  1259   nsCOMPtr<nsINodeInfo> nodeInfo =
  1260     aNim->GetNodeInfo(tagAtom, prefixAtom, namespaceID, nsIDOMNode::ELEMENT_NODE);
  1262   uint32_t attrCount;
  1263   rv = aStream->Read32(&attrCount);
  1264   NS_ENSURE_SUCCESS(rv, rv);
  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();
  1272     nsRefPtr<nsXULPrototypeElement> prototype = new nsXULPrototypeElement();
  1273     NS_ENSURE_TRUE(prototype, NS_ERROR_OUT_OF_MEMORY);
  1275     prototype->mNodeInfo = nodeInfo;
  1277     nsXULPrototypeAttribute* attrs = nullptr;
  1278     if (attrCount > 0) {
  1279       attrs = new nsXULPrototypeAttribute[attrCount];
  1282     prototype->mAttributes = attrs;
  1283     prototype->mNumAttributes = attrCount;
  1285     for (uint32_t i = 0; i < attrCount; i++) {
  1286       rv = ReadNamespace(aStream, namespaceID);
  1287       NS_ENSURE_SUCCESS(rv, rv);
  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);
  1297       nsCOMPtr<nsIAtom> nameAtom = do_GetAtom(name);
  1298       if (namespaceID == kNameSpaceID_None) {
  1299         attrs[i].mName.SetTo(nameAtom);
  1301       else {
  1302         nsCOMPtr<nsIAtom> prefixAtom;
  1303         if (!prefix.IsEmpty())
  1304           prefixAtom = do_GetAtom(prefix);
  1306         nsCOMPtr<nsINodeInfo> ni =
  1307           aNim->GetNodeInfo(nameAtom, prefixAtom,
  1308                             namespaceID, nsIDOMNode::ATTRIBUTE_NODE);
  1309         attrs[i].mName.SetTo(ni);
  1312       rv = prototype->SetAttrAt(i, val, documentURI);
  1313       NS_ENSURE_SUCCESS(rv, rv);
  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;
  1322   else {
  1323 #endif
  1324     nsCOMPtr<Element> element;
  1325     NS_NewElement(getter_AddRefs(element), nodeInfo.forget(), NOT_FROM_PARSER);
  1326     content = element;
  1328     for (uint32_t i = 0; i < attrCount; i++) {
  1329       rv = ReadNamespace(aStream, namespaceID);
  1330       NS_ENSURE_SUCCESS(rv, rv);
  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);
  1340       nsCOMPtr<nsIAtom> prefixAtom;
  1341       if (!prefix.IsEmpty())
  1342         prefixAtom = do_GetAtom(prefix);
  1344       nsCOMPtr<nsIAtom> nameAtom = do_GetAtom(name);
  1345       content->SetAttr(namespaceID, nameAtom, prefixAtom, val, false);
  1348 #ifdef MOZ_XUL
  1350 #endif
  1352   // Now read the attribute forwarding entries (xbl:inherits)
  1354   int32_t srcNamespaceID, destNamespaceID;
  1355   rv = ReadNamespace(aStream, srcNamespaceID);
  1356   NS_ENSURE_SUCCESS(rv, rv);
  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);
  1367     nsCOMPtr<nsIAtom> srcAtom = do_GetAtom(srcAttribute);
  1368     nsCOMPtr<nsIAtom> destAtom = do_GetAtom(destAttribute);
  1370     EnsureAttributeTable();
  1371     AddToAttributeTable(srcNamespaceID, srcAtom, destNamespaceID, destAtom, content);
  1373     rv = ReadNamespace(aStream, srcNamespaceID);
  1374     NS_ENSURE_SUCCESS(rv, rv);
  1377   // Finally, read in the child nodes.
  1378   uint32_t childCount;
  1379   rv = aStream->Read32(&childCount);
  1380   NS_ENSURE_SUCCESS(rv, rv);
  1382   for (uint32_t i = 0; i < childCount; i++) {
  1383     nsCOMPtr<nsIContent> child;
  1384     ReadContentNode(aStream, aDocument, aNim, getter_AddRefs(child));
  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);
  1392   content.swap(*aContent);
  1393   return NS_OK;
  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
  1401   nsXBLPrototypeBinding* binding;
  1402   nsIObjectOutputStream* stream;
  1403   nsIContent* content;
  1404   int32_t srcNamespace;
  1406   WriteAttributeData(nsXBLPrototypeBinding* aBinding,
  1407                      nsIObjectOutputStream* aStream,
  1408                      nsIContent* aContent)
  1409     : binding(aBinding), stream(aStream), content(aContent)
  1410   { }
  1411 };
  1413 static PLDHashOperator
  1414 WriteAttribute(nsISupports* aKey, nsXBLAttributeEntry* aEntry, void* aClosure)
  1416   WriteAttributeData* data = static_cast<WriteAttributeData *>(aClosure);
  1417   nsIObjectOutputStream* stream = data->stream;
  1418   const int32_t srcNamespace = data->srcNamespace;
  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());
  1428     aEntry = aEntry->GetNext();
  1429   } while (aEntry);
  1431   return PL_DHASH_NEXT;
  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)
  1443   WriteAttributeData* data = static_cast<WriteAttributeData *>(aClosure);
  1444   data->srcNamespace = aNamespace;
  1445   aXBLAttributes->EnumerateRead(WriteAttribute, data);
  1447   return PL_DHASH_NEXT;
  1450 nsresult
  1451 nsXBLPrototypeBinding::WriteContentNode(nsIObjectOutputStream* aStream,
  1452                                         nsIContent* aNode)
  1454   nsresult rv;
  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;
  1473     rv = aStream->Write8(type);
  1474     NS_ENSURE_SUCCESS(rv, rv);
  1476     nsAutoString content;
  1477     aNode->GetText()->AppendTo(content);
  1478     return aStream->WriteWStringZ(content.get());
  1481   // Otherwise, this is an element.
  1483   // Write the namespace id followed by the tag name
  1484   rv = WriteNamespace(aStream, aNode->GetNameSpaceID());
  1485   NS_ENSURE_SUCCESS(rv, rv);
  1487   nsAutoString prefixStr;
  1488   aNode->NodeInfo()->GetPrefix(prefixStr);
  1489   rv = aStream->WriteWStringZ(prefixStr.get());
  1490   NS_ENSURE_SUCCESS(rv, rv);
  1492   rv = aStream->WriteWStringZ(nsDependentAtomString(aNode->Tag()).get());
  1493   NS_ENSURE_SUCCESS(rv, rv);
  1495   // Write attributes
  1496   uint32_t count = aNode->GetAttrCount();
  1497   rv = aStream->Write32(count);
  1498   NS_ENSURE_SUCCESS(rv, rv);
  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.
  1505     const nsAttrName* attr = aNode->GetAttrNameAt(i);
  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);
  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);
  1519     rv = aStream->WriteWStringZ(nsDependentAtomString(attr->LocalName()).get());
  1520     NS_ENSURE_SUCCESS(rv, rv);
  1522     nsAutoString val;
  1523     aNode->GetAttr(attr->NamespaceID(), attr->LocalName(), val);
  1524     rv = aStream->WriteWStringZ(val.get());
  1525     NS_ENSURE_SUCCESS(rv, rv);
  1528   // Write out the attribute fowarding information
  1529   if (mAttributeTable) {
  1530     WriteAttributeData data(this, aStream, aNode);
  1531     mAttributeTable->EnumerateRead(WriteAttributeNS, &data);
  1533   rv = aStream->Write8(XBLBinding_Serialize_NoMoreAttributes);
  1534   NS_ENSURE_SUCCESS(rv, rv);
  1536   // Finally, write out the child nodes.
  1537   count = aNode->GetChildCount();
  1538   rv = aStream->Write32(count);
  1539   NS_ENSURE_SUCCESS(rv, rv);
  1541   for (i = 0; i < count; i++) {
  1542     rv = WriteContentNode(aStream, aNode->GetChildAt(i));
  1543     NS_ENSURE_SUCCESS(rv, rv);
  1546   return NS_OK;
  1549 nsresult
  1550 nsXBLPrototypeBinding::ReadNamespace(nsIObjectInputStream* aStream,
  1551                                      int32_t& aNameSpaceID)
  1553   uint8_t namespaceID;
  1554   nsresult rv = aStream->Read8(&namespaceID);
  1555   NS_ENSURE_SUCCESS(rv, rv);
  1557   if (namespaceID == XBLBinding_Serialize_CustomNamespace) {
  1558     nsAutoString namesp;
  1559     rv = aStream->ReadString(namesp);
  1560     NS_ENSURE_SUCCESS(rv, rv);
  1562     nsContentUtils::NameSpaceManager()->RegisterNameSpace(namesp, aNameSpaceID);
  1564   else {
  1565     aNameSpaceID = namespaceID;
  1568   return NS_OK;
  1571 nsresult
  1572 nsXBLPrototypeBinding::WriteNamespace(nsIObjectOutputStream* aStream,
  1573                                       int32_t aNameSpaceID)
  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;
  1582   if (aNameSpaceID <= kNameSpaceID_LastBuiltin) {
  1583     rv = aStream->Write8((int8_t)aNameSpaceID);
  1584     NS_ENSURE_SUCCESS(rv, rv);
  1586   else {
  1587     rv = aStream->Write8(XBLBinding_Serialize_CustomNamespace);
  1588     NS_ENSURE_SUCCESS(rv, rv);
  1590     nsAutoString namesp;
  1591     nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNameSpaceID, namesp);
  1592     aStream->WriteWStringZ(namesp.get());
  1595   return NS_OK;
  1599 bool CheckTagNameWhiteList(int32_t aNameSpaceID, nsIAtom *aTagName)
  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};
  1608   uint32_t i;
  1609   if (aNameSpaceID == kNameSpaceID_XUL) {
  1610     for (i = 0; kValidXULTagNames[i]; ++i) {
  1611       if (aTagName == *(kValidXULTagNames[i])) {
  1612         return true;
  1616   else if (aNameSpaceID == kNameSpaceID_SVG &&
  1617            aTagName == nsGkAtoms::generic_) {
  1618     return true;
  1621   return false;
  1624 nsresult
  1625 nsXBLPrototypeBinding::ResolveBaseBinding()
  1627   if (mCheckedBaseProto)
  1628     return NS_OK;
  1629   mCheckedBaseProto = true;
  1631   nsCOMPtr<nsIDocument> doc = mXBLDocInfoWeak->GetDocument();
  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;
  1639   mBinding->GetAttr(kNameSpaceID_None, nsGkAtoms::display, display);
  1640   bool hasDisplay = !display.IsEmpty();
  1642   nsAutoString value(extends);
  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);
  1654   else {
  1655     offset = extends.FindChar(':');
  1656     if (-1 != offset) {
  1657       extends.Left(prefix, offset);
  1658       extends.Cut(0, offset+1);
  1659       display = extends;
  1663   nsAutoString nameSpace;
  1665   if (!prefix.IsEmpty()) {
  1666     mBinding->LookupNamespaceURI(prefix, nameSpace);
  1667     if (!nameSpace.IsEmpty()) {
  1668       int32_t nameSpaceID =
  1669         nsContentUtils::NameSpaceManager()->GetNameSpaceID(nameSpace);
  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;
  1686       SetBaseTag(nameSpaceID, tagName);
  1690   if (hasDisplay || nameSpace.IsEmpty()) {
  1691     mBinding->UnsetAttr(kNameSpaceID_None, nsGkAtoms::extends, false);
  1692     mBinding->UnsetAttr(kNameSpaceID_None, nsGkAtoms::display, false);
  1694     return NS_NewURI(getter_AddRefs(mBaseBindingURI), value,
  1695                      doc->GetDocumentCharacterSet().get(),
  1696                      doc->GetDocBaseURI());
  1699   return NS_OK;

mercurial