LCOV - code coverage report
Current view: top level - dom/xbl - nsXBLBinding.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 367 514 71.4 %
Date: 2017-07-14 16:53:18 Functions: 29 45 64.4 %
Legend: Lines: hit not hit

          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 "nsCOMPtr.h"
       8             : #include "nsIAtom.h"
       9             : #include "nsXBLDocumentInfo.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 "plstr.h"
      18             : #include "nsIContent.h"
      19             : #include "nsIDocument.h"
      20             : #include "nsContentUtils.h"
      21             : #include "ChildIterator.h"
      22             : #ifdef MOZ_XUL
      23             : #include "nsIXULDocument.h"
      24             : #endif
      25             : #include "nsIXMLContentSink.h"
      26             : #include "nsContentCID.h"
      27             : #include "mozilla/dom/XMLDocument.h"
      28             : #include "jsapi.h"
      29             : #include "nsXBLService.h"
      30             : #include "nsIXPConnect.h"
      31             : #include "nsIScriptContext.h"
      32             : #include "nsCRT.h"
      33             : 
      34             : // Event listeners
      35             : #include "mozilla/EventListenerManager.h"
      36             : #include "nsIDOMEventListener.h"
      37             : #include "nsAttrName.h"
      38             : 
      39             : #include "nsGkAtoms.h"
      40             : 
      41             : #include "nsXBLPrototypeHandler.h"
      42             : 
      43             : #include "nsXBLPrototypeBinding.h"
      44             : #include "nsXBLBinding.h"
      45             : #include "nsIPrincipal.h"
      46             : #include "nsIScriptSecurityManager.h"
      47             : #include "mozilla/dom/XBLChildrenElement.h"
      48             : 
      49             : #include "nsNodeUtils.h"
      50             : #include "nsJSUtils.h"
      51             : 
      52             : // Nasty hack.  Maybe we could move some of the classinfo utility methods
      53             : // (e.g. WrapNative) over to nsContentUtils?
      54             : #include "nsDOMClassInfo.h"
      55             : 
      56             : #include "mozilla/DeferredFinalize.h"
      57             : #include "mozilla/dom/Element.h"
      58             : #include "mozilla/dom/ScriptSettings.h"
      59             : #include "mozilla/dom/ShadowRoot.h"
      60             : 
      61             : using namespace mozilla;
      62             : using namespace mozilla::dom;
      63             : 
      64             : // Helper classes
      65             : 
      66             : /***********************************************************************/
      67             : //
      68             : // The JS class for XBLBinding
      69             : //
      70             : static void
      71           0 : XBLFinalize(JSFreeOp *fop, JSObject *obj)
      72             : {
      73             :   nsXBLDocumentInfo* docInfo =
      74           0 :     static_cast<nsXBLDocumentInfo*>(::JS_GetPrivate(obj));
      75           0 :   DeferredFinalize(docInfo);
      76           0 : }
      77             : 
      78             : static bool
      79           0 : XBLEnumerate(JSContext *cx, JS::Handle<JSObject*> obj)
      80             : {
      81             :   nsXBLPrototypeBinding* protoBinding =
      82           0 :     static_cast<nsXBLPrototypeBinding*>(::JS_GetReservedSlot(obj, 0).toPrivate());
      83           0 :   MOZ_ASSERT(protoBinding);
      84             : 
      85           0 :   return protoBinding->ResolveAllFields(cx, obj);
      86             : }
      87             : 
      88             : static const JSClassOps gPrototypeJSClassOps = {
      89             :     nullptr, nullptr, nullptr, nullptr,
      90             :     XBLEnumerate, nullptr, nullptr,
      91             :     nullptr, XBLFinalize,
      92             :     nullptr, nullptr, nullptr, nullptr
      93             : };
      94             : 
      95             : static const JSClass gPrototypeJSClass = {
      96             :     "XBL prototype JSClass",
      97             :     JSCLASS_HAS_PRIVATE |
      98             :     JSCLASS_PRIVATE_IS_NSISUPPORTS |
      99             :     JSCLASS_FOREGROUND_FINALIZE |
     100             :     // Our one reserved slot holds the relevant nsXBLPrototypeBinding
     101             :     JSCLASS_HAS_RESERVED_SLOTS(1),
     102             :     &gPrototypeJSClassOps
     103             : };
     104             : 
     105             : // Implementation /////////////////////////////////////////////////////////////////
     106             : 
     107             : // Constructors/Destructors
     108         633 : nsXBLBinding::nsXBLBinding(nsXBLPrototypeBinding* aBinding)
     109             :   : mMarkedForDeath(false)
     110             :   , mUsingContentXBLScope(false)
     111             :   , mIsShadowRootBinding(false)
     112         633 :   , mPrototypeBinding(aBinding)
     113             : {
     114         633 :   NS_ASSERTION(mPrototypeBinding, "Must have a prototype binding!");
     115             :   // Grab a ref to the document info so the prototype binding won't die
     116         633 :   NS_ADDREF(mPrototypeBinding->XBLDocumentInfo());
     117         633 : }
     118             : 
     119             : // Constructor used by web components.
     120           0 : nsXBLBinding::nsXBLBinding(ShadowRoot* aShadowRoot, nsXBLPrototypeBinding* aBinding)
     121             :   : mMarkedForDeath(false),
     122             :     mUsingContentXBLScope(false),
     123             :     mIsShadowRootBinding(true),
     124             :     mPrototypeBinding(aBinding),
     125           0 :     mContent(aShadowRoot)
     126             : {
     127           0 :   NS_ASSERTION(mPrototypeBinding, "Must have a prototype binding!");
     128             :   // Grab a ref to the document info so the prototype binding won't die
     129           0 :   NS_ADDREF(mPrototypeBinding->XBLDocumentInfo());
     130           0 : }
     131             : 
     132          24 : nsXBLBinding::~nsXBLBinding(void)
     133             : {
     134          12 :   if (mContent && !mIsShadowRootBinding) {
     135             :     // It is unnecessary to uninstall anonymous content in a shadow tree
     136             :     // because the ShadowRoot itself is a DocumentFragment and does not
     137             :     // need any additional cleanup.
     138           6 :     nsXBLBinding::UninstallAnonymousContent(mContent->OwnerDoc(), mContent);
     139             :   }
     140          12 :   nsXBLDocumentInfo* info = mPrototypeBinding->XBLDocumentInfo();
     141          12 :   NS_RELEASE(info);
     142          12 : }
     143             : 
     144             : NS_IMPL_CYCLE_COLLECTION_CLASS(nsXBLBinding)
     145             : 
     146           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXBLBinding)
     147             :   // XXX Probably can't unlink mPrototypeBinding->XBLDocumentInfo(), because
     148             :   //     mPrototypeBinding is weak.
     149           0 :   if (tmp->mContent && !tmp->mIsShadowRootBinding) {
     150           0 :     nsXBLBinding::UninstallAnonymousContent(tmp->mContent->OwnerDoc(),
     151           0 :                                             tmp->mContent);
     152             :   }
     153           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mContent)
     154           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mNextBinding)
     155           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDefaultInsertionPoint)
     156           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mInsertionPoints)
     157           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnonymousContentList)
     158           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     159           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXBLBinding)
     160             :   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
     161           0 :                                      "mPrototypeBinding->XBLDocumentInfo()");
     162           0 :   cb.NoteXPCOMChild(tmp->mPrototypeBinding->XBLDocumentInfo());
     163           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContent)
     164           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNextBinding)
     165           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDefaultInsertionPoint)
     166           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInsertionPoints)
     167           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnonymousContentList)
     168           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     169           0 : NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsXBLBinding, AddRef)
     170           0 : NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsXBLBinding, Release)
     171             : 
     172             : void
     173         361 : nsXBLBinding::SetBaseBinding(nsXBLBinding* aBinding)
     174             : {
     175         361 :   if (mNextBinding) {
     176           0 :     NS_ERROR("Base XBL binding is already defined!");
     177           0 :     return;
     178             :   }
     179             : 
     180         361 :   mNextBinding = aBinding; // Comptr handles rel/add
     181             : }
     182             : 
     183             : nsXBLBinding*
     184         410 : nsXBLBinding::GetBindingWithContent()
     185             : {
     186         410 :   if (mContent) {
     187         210 :     return this;
     188             :   }
     189             : 
     190         200 :   return mNextBinding ? mNextBinding->GetBindingWithContent() : nullptr;
     191             : }
     192             : 
     193             : void
     194         151 : nsXBLBinding::InstallAnonymousContent(nsIContent* aAnonParent, nsIContent* aElement,
     195             :                                       bool aChromeOnlyContent)
     196             : {
     197             :   // We need to ensure two things.
     198             :   // (1) The anonymous content should be fooled into thinking it's in the bound
     199             :   // element's document, assuming that the bound element is in a document
     200             :   // Note that we don't change the current doc of aAnonParent here, since that
     201             :   // quite simply does not matter.  aAnonParent is just a way of keeping refs
     202             :   // to all its kids, which are anonymous content from the point of view of
     203             :   // aElement.
     204             :   // (2) The children's parent back pointer should not be to this synthetic root
     205             :   // but should instead point to the enclosing parent element.
     206         151 :   nsIDocument* doc = aElement->GetUncomposedDoc();
     207         151 :   bool allowScripts = AllowScripts();
     208             : 
     209         302 :   nsAutoScriptBlocker scriptBlocker;
     210         498 :   for (nsIContent* child = aAnonParent->GetFirstChild();
     211         498 :        child;
     212         347 :        child = child->GetNextSibling()) {
     213         347 :     child->UnbindFromTree();
     214         347 :     if (aChromeOnlyContent) {
     215           0 :       child->SetFlags(NODE_CHROME_ONLY_ACCESS |
     216           0 :                       NODE_IS_ROOT_OF_CHROME_ONLY_ACCESS);
     217             :     }
     218         347 :     child->SetFlags(NODE_IS_ANONYMOUS_ROOT);
     219             :     nsresult rv =
     220         347 :       child->BindToTree(doc, aElement, mBoundElement, allowScripts);
     221         347 :     if (NS_FAILED(rv)) {
     222             :       // Oh, well... Just give up.
     223             :       // XXXbz This really shouldn't be a void method!
     224           0 :       child->UnbindFromTree();
     225           0 :       return;
     226             :     }
     227             : 
     228             : #ifdef MOZ_XUL
     229             :     // To make XUL templates work (and other goodies that happen when
     230             :     // an element is added to a XUL document), we need to notify the
     231             :     // XUL document using its special API.
     232         694 :     nsCOMPtr<nsIXULDocument> xuldoc(do_QueryInterface(doc));
     233         347 :     if (xuldoc)
     234         325 :       xuldoc->AddSubtreeToDocument(child);
     235             : #endif
     236             :   }
     237             : }
     238             : 
     239             : void
     240          14 : nsXBLBinding::UninstallAnonymousContent(nsIDocument* aDocument,
     241             :                                         nsIContent* aAnonParent)
     242             : {
     243          28 :   nsAutoScriptBlocker scriptBlocker;
     244             :   // Hold a strong ref while doing this, just in case.
     245          28 :   nsCOMPtr<nsIContent> anonParent = aAnonParent;
     246             : #ifdef MOZ_XUL
     247             :   nsCOMPtr<nsIXULDocument> xuldoc =
     248          28 :     do_QueryInterface(aDocument);
     249             : #endif
     250          66 :   for (nsIContent* child = aAnonParent->GetFirstChild();
     251          66 :        child;
     252          52 :        child = child->GetNextSibling()) {
     253          52 :     child->UnbindFromTree();
     254             : #ifdef MOZ_XUL
     255          52 :     if (xuldoc) {
     256          52 :       xuldoc->RemoveSubtreeFromDocument(child);
     257             :     }
     258             : #endif
     259             :   }
     260          14 : }
     261             : 
     262             : void
     263         664 : nsXBLBinding::SetBoundElement(nsIContent* aElement)
     264             : {
     265         664 :   mBoundElement = aElement;
     266         664 :   if (mNextBinding)
     267         380 :     mNextBinding->SetBoundElement(aElement);
     268             : 
     269         664 :   if (!mBoundElement) {
     270          62 :     return;
     271             :   }
     272             : 
     273             :   // Compute whether we're using an XBL scope.
     274             :   //
     275             :   // We disable XBL scopes for remote XUL, where we care about compat more
     276             :   // than security. So we need to know whether we're using an XBL scope so that
     277             :   // we can decide what to do about untrusted events when "allowuntrusted"
     278             :   // is not given in the handler declaration.
     279        1266 :   nsCOMPtr<nsIGlobalObject> go = mBoundElement->OwnerDoc()->GetScopeObject();
     280         633 :   NS_ENSURE_TRUE_VOID(go && go->GetGlobalJSObject());
     281         633 :   mUsingContentXBLScope = xpc::UseContentXBLScope(js::GetObjectCompartment(go->GetGlobalJSObject()));
     282             : }
     283             : 
     284             : bool
     285         439 : nsXBLBinding::HasStyleSheets() const
     286             : {
     287             :   // Find out if we need to re-resolve style.  We'll need to do this
     288             :   // if we have additional stylesheets in our binding document.
     289         439 :   if (mPrototypeBinding->HasStyleSheets())
     290         155 :     return true;
     291             : 
     292         284 :   return mNextBinding ? mNextBinding->HasStyleSheets() : false;
     293             : }
     294             : 
     295             : void
     296         313 : nsXBLBinding::GenerateAnonymousContent()
     297             : {
     298         313 :   NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
     299             :                "Someone forgot a script blocker");
     300             : 
     301             :   // Fetch the content element for this binding.
     302             :   nsIContent* content =
     303         313 :     mPrototypeBinding->GetImmediateChild(nsGkAtoms::content);
     304             : 
     305         313 :   if (!content) {
     306             :     // We have no anonymous content.
     307         162 :     if (mNextBinding)
     308          41 :       mNextBinding->GenerateAnonymousContent();
     309             : 
     310         325 :     return;
     311             :   }
     312             : 
     313             :   // Find out if we're really building kids or if we're just
     314             :   // using the attribute-setting shorthand hack.
     315         151 :   uint32_t contentCount = content->GetChildCount();
     316             : 
     317             :   // Plan to build the content by default.
     318         151 :   bool hasContent = (contentCount > 0);
     319         151 :   if (hasContent) {
     320         151 :     nsIDocument* doc = mBoundElement->OwnerDoc();
     321             : 
     322         301 :     nsCOMPtr<nsINode> clonedNode;
     323         151 :     nsNodeUtils::Clone(content, true, doc->NodeInfoManager(), nullptr,
     324         302 :                        getter_AddRefs(clonedNode));
     325         151 :     mContent = clonedNode->AsElement();
     326             : 
     327             :     // Search for <xbl:children> elements in the XBL content. In the presence
     328             :     // of multiple default insertion points, we use the last one in document
     329             :     // order.
     330        1032 :     for (nsIContent* child = mContent; child; child = child->GetNextNode(mContent)) {
     331         881 :       if (child->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) {
     332         163 :         XBLChildrenElement* point = static_cast<XBLChildrenElement*>(child);
     333         163 :         if (point->IsDefaultInsertion()) {
     334          78 :           mDefaultInsertionPoint = point;
     335             :         } else {
     336          85 :           mInsertionPoints.AppendElement(point);
     337             :         }
     338             :       }
     339             :     }
     340             : 
     341             :     // Do this after looking for <children> as this messes up the parent
     342             :     // pointer which would make the GetNextNode call above fail
     343         151 :     InstallAnonymousContent(mContent, mBoundElement,
     344         302 :                             mPrototypeBinding->ChromeOnlyContent());
     345             : 
     346             :     // Insert explicit children into insertion points
     347         151 :     if (mDefaultInsertionPoint && mInsertionPoints.IsEmpty()) {
     348         130 :       ExplicitChildIterator iter(mBoundElement);
     349         418 :       for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
     350         353 :         mDefaultInsertionPoint->AppendInsertedChild(child);
     351             :       }
     352             :     } else {
     353             :       // It is odd to come into this code if mInsertionPoints is not empty, but
     354             :       // we need to make sure to do the compatibility hack below if the bound
     355             :       // node has any non <xul:template> or <xul:observes> children.
     356         171 :       ExplicitChildIterator iter(mBoundElement);
     357         130 :       for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
     358          45 :         XBLChildrenElement* point = FindInsertionPointForInternal(child);
     359          45 :         if (point) {
     360          44 :           point->AppendInsertedChild(child);
     361             :         } else {
     362           1 :           NodeInfo *ni = child->NodeInfo();
     363           3 :           if (ni->NamespaceID() != kNameSpaceID_XUL ||
     364           2 :               (!ni->Equals(nsGkAtoms::_template) &&
     365           1 :                !ni->Equals(nsGkAtoms::observes))) {
     366             :             // Compatibility hack. For some reason the original XBL
     367             :             // implementation dropped the content of a binding if any child of
     368             :             // the bound element didn't match any of the <children> in the
     369             :             // binding. This became a pseudo-API that we have to maintain.
     370             : 
     371             :             // Undo InstallAnonymousContent
     372           1 :             UninstallAnonymousContent(doc, mContent);
     373             : 
     374             :             // Clear out our children elements to avoid dangling references.
     375           1 :             ClearInsertionPoints();
     376             : 
     377             :             // Pretend as though there was no content in the binding.
     378           1 :             mContent = nullptr;
     379           1 :             return;
     380             :           }
     381             :         }
     382             :       }
     383             :     }
     384             : 
     385             :     // Set binding parent on default content if need
     386         150 :     if (mDefaultInsertionPoint) {
     387          78 :       mDefaultInsertionPoint->MaybeSetupDefaultContent();
     388             :     }
     389         233 :     for (uint32_t i = 0; i < mInsertionPoints.Length(); ++i) {
     390          83 :       mInsertionPoints[i]->MaybeSetupDefaultContent();
     391             :     }
     392             : 
     393         150 :     mPrototypeBinding->SetInitialAttributes(mBoundElement, mContent);
     394             :   }
     395             : 
     396             :   // Always check the content element for potential attributes.
     397             :   // This shorthand hack always happens, even when we didn't
     398             :   // build anonymous content.
     399         150 :   BorrowedAttrInfo attrInfo;
     400         242 :   for (uint32_t i = 0; (attrInfo = content->GetAttrInfoAt(i)); ++i) {
     401          92 :     int32_t namespaceID = attrInfo.mName->NamespaceID();
     402             :     // Hold a strong reference here so that the atom doesn't go away during
     403             :     // UnsetAttr.
     404         184 :     nsCOMPtr<nsIAtom> name = attrInfo.mName->LocalName();
     405             : 
     406          92 :     if (name != nsGkAtoms::includes) {
     407          92 :       if (!nsContentUtils::HasNonEmptyAttr(mBoundElement, namespaceID, name)) {
     408         138 :         nsAutoString value2;
     409          69 :         attrInfo.mValue->ToString(value2);
     410         138 :         mBoundElement->SetAttr(namespaceID, name, attrInfo.mName->GetPrefix(),
     411         138 :                                value2, false);
     412             :       }
     413             :     }
     414             : 
     415             :     // Conserve space by wiping the attributes off the clone.
     416          92 :     if (mContent)
     417          92 :       mContent->UnsetAttr(namespaceID, name, false);
     418             :   }
     419             : }
     420             : 
     421             : nsIURI*
     422           0 : nsXBLBinding::GetSourceDocURI()
     423             : {
     424             :   nsIContent* targetContent =
     425           0 :     mPrototypeBinding->GetImmediateChild(nsGkAtoms::content);
     426           0 :   if (!targetContent) {
     427           0 :     return nullptr;
     428             :   }
     429             : 
     430           0 :   return targetContent->OwnerDoc()->GetDocumentURI();
     431             : }
     432             : 
     433             : XBLChildrenElement*
     434          60 : nsXBLBinding::FindInsertionPointFor(nsIContent* aChild)
     435             : {
     436             :   // XXX We should get rid of this function as it causes us to traverse the
     437             :   // binding chain multiple times
     438          60 :   if (mContent) {
     439          60 :     return FindInsertionPointForInternal(aChild);
     440             :   }
     441             : 
     442           0 :   return mNextBinding ? mNextBinding->FindInsertionPointFor(aChild)
     443           0 :                       : nullptr;
     444             : }
     445             : 
     446             : XBLChildrenElement*
     447         105 : nsXBLBinding::FindInsertionPointForInternal(nsIContent* aChild)
     448             : {
     449         131 :   for (uint32_t i = 0; i < mInsertionPoints.Length(); ++i) {
     450          89 :     XBLChildrenElement* point = mInsertionPoints[i];
     451          89 :     if (point->Includes(aChild)) {
     452          63 :       return point;
     453             :     }
     454             :   }
     455             : 
     456          42 :   return mDefaultInsertionPoint;
     457             : }
     458             : 
     459             : void
     460          32 : nsXBLBinding::ClearInsertionPoints()
     461             : {
     462          32 :   if (mDefaultInsertionPoint) {
     463           2 :     mDefaultInsertionPoint->ClearInsertedChildren();
     464             :   }
     465             : 
     466          42 :   for (uint32_t i = 0; i < mInsertionPoints.Length(); ++i) {
     467          10 :     mInsertionPoints[i]->ClearInsertedChildren();
     468             :   }
     469          32 : }
     470             : 
     471             : nsAnonymousContentList*
     472          19 : nsXBLBinding::GetAnonymousNodeList()
     473             : {
     474          19 :   if (!mContent) {
     475           0 :     return mNextBinding ? mNextBinding->GetAnonymousNodeList() : nullptr;
     476             :   }
     477             : 
     478          19 :   if (!mAnonymousContentList) {
     479          18 :     mAnonymousContentList = new nsAnonymousContentList(mContent);
     480             :   }
     481             : 
     482          19 :   return mAnonymousContentList;
     483             : }
     484             : 
     485             : void
     486         633 : nsXBLBinding::InstallEventHandlers()
     487             : {
     488             :   // Don't install handlers if scripts aren't allowed.
     489         633 :   if (AllowScripts()) {
     490             :     // Fetch the handlers prototypes for this binding.
     491         633 :     nsXBLPrototypeHandler* handlerChain = mPrototypeBinding->GetPrototypeHandlers();
     492             : 
     493         633 :     if (handlerChain) {
     494         156 :       EventListenerManager* manager = mBoundElement->GetOrCreateListenerManager();
     495         156 :       if (!manager)
     496           0 :         return;
     497             : 
     498             :       bool isChromeDoc =
     499         156 :         nsContentUtils::IsChromeDoc(mBoundElement->OwnerDoc());
     500         156 :       bool isChromeBinding = mPrototypeBinding->IsChrome();
     501             :       nsXBLPrototypeHandler* curr;
     502         705 :       for (curr = handlerChain; curr; curr = curr->GetNextHandler()) {
     503             :         // Fetch the event type.
     504         981 :         nsCOMPtr<nsIAtom> eventAtom = curr->GetEventName();
     505        1647 :         if (!eventAtom ||
     506        1097 :             eventAtom == nsGkAtoms::keyup ||
     507        1637 :             eventAtom == nsGkAtoms::keydown ||
     508         540 :             eventAtom == nsGkAtoms::keypress)
     509         117 :           continue;
     510             : 
     511         432 :         nsXBLEventHandler* handler = curr->GetEventHandler();
     512         432 :         if (handler) {
     513             :           // Figure out if we're using capturing or not.
     514         432 :           EventListenerFlags flags;
     515         432 :           flags.mCapture = (curr->GetPhase() == NS_PHASE_CAPTURING);
     516             : 
     517             :           // If this is a command, add it in the system event group
     518         864 :           if ((curr->GetType() & (NS_HANDLER_TYPE_XBL_COMMAND |
     519         436 :                                   NS_HANDLER_TYPE_SYSTEM)) &&
     520           0 :               (isChromeBinding || mBoundElement->IsInNativeAnonymousSubtree())) {
     521           4 :             flags.mInSystemGroup = true;
     522             :           }
     523             : 
     524         432 :           bool hasAllowUntrustedAttr = curr->HasAllowUntrustedAttr();
     525         864 :           if ((hasAllowUntrustedAttr && curr->AllowUntrustedEvents()) ||
     526         864 :               (!hasAllowUntrustedAttr && !isChromeDoc && !mUsingContentXBLScope)) {
     527           0 :             flags.mAllowUntrustedEvents = true;
     528             :           }
     529             : 
     530             :           manager->AddEventListenerByType(handler,
     531         864 :                                           nsDependentAtomString(eventAtom),
     532         432 :                                           flags);
     533             :         }
     534             :       }
     535             : 
     536             :       const nsCOMArray<nsXBLKeyEventHandler>* keyHandlers =
     537         156 :         mPrototypeBinding->GetKeyEventHandlers();
     538             :       int32_t i;
     539         200 :       for (i = 0; i < keyHandlers->Count(); ++i) {
     540          44 :         nsXBLKeyEventHandler* handler = keyHandlers->ObjectAt(i);
     541          44 :         handler->SetIsBoundToChrome(isChromeDoc);
     542          44 :         handler->SetUsingContentXBLScope(mUsingContentXBLScope);
     543             : 
     544          88 :         nsAutoString type;
     545          44 :         handler->GetEventName(type);
     546             : 
     547             :         // If this is a command, add it in the system event group, otherwise
     548             :         // add it to the standard event group.
     549             : 
     550             :         // Figure out if we're using capturing or not.
     551          44 :         EventListenerFlags flags;
     552          44 :         flags.mCapture = (handler->GetPhase() == NS_PHASE_CAPTURING);
     553             : 
     554          88 :         if ((handler->GetType() & (NS_HANDLER_TYPE_XBL_COMMAND |
     555          51 :                                    NS_HANDLER_TYPE_SYSTEM)) &&
     556           0 :             (isChromeBinding || mBoundElement->IsInNativeAnonymousSubtree())) {
     557           7 :           flags.mInSystemGroup = true;
     558             :         }
     559             : 
     560             :         // For key handlers we have to set mAllowUntrustedEvents flag.
     561             :         // Whether the handling of the event is allowed or not is handled in
     562             :         // nsXBLKeyEventHandler::HandleEvent
     563          44 :         flags.mAllowUntrustedEvents = true;
     564             : 
     565          44 :         manager->AddEventListenerByType(handler, type, flags);
     566             :       }
     567             :     }
     568             :   }
     569             : 
     570         633 :   if (mNextBinding)
     571         361 :     mNextBinding->InstallEventHandlers();
     572             : }
     573             : 
     574             : nsresult
     575         633 : nsXBLBinding::InstallImplementation()
     576             : {
     577             :   // Always install the base class properties first, so that
     578             :   // derived classes can reference the base class properties.
     579             : 
     580         633 :   if (mNextBinding) {
     581         361 :     nsresult rv = mNextBinding->InstallImplementation();
     582         361 :     NS_ENSURE_SUCCESS(rv, rv);
     583             :   }
     584             : 
     585             :   // iterate through each property in the prototype's list and install the property.
     586         633 :   if (AllowScripts())
     587         633 :     return mPrototypeBinding->InstallImplementation(this);
     588             : 
     589           0 :   return NS_OK;
     590             : }
     591             : 
     592             : nsIAtom*
     593         800 : nsXBLBinding::GetBaseTag(int32_t* aNameSpaceID)
     594             : {
     595         800 :   nsIAtom *tag = mPrototypeBinding->GetBaseTag(aNameSpaceID);
     596         800 :   if (!tag && mNextBinding)
     597         368 :     return mNextBinding->GetBaseTag(aNameSpaceID);
     598             : 
     599         432 :   return tag;
     600             : }
     601             : 
     602             : void
     603         255 : nsXBLBinding::AttributeChanged(nsIAtom* aAttribute, int32_t aNameSpaceID,
     604             :                                bool aRemoveFlag, bool aNotify)
     605             : {
     606             :   // XXX Change if we ever allow multiple bindings in a chain to contribute anonymous content
     607         255 :   if (!mContent) {
     608          78 :     if (mNextBinding)
     609          28 :       mNextBinding->AttributeChanged(aAttribute, aNameSpaceID,
     610          28 :                                      aRemoveFlag, aNotify);
     611             :   } else {
     612         177 :     mPrototypeBinding->AttributeChanged(aAttribute, aNameSpaceID, aRemoveFlag,
     613         177 :                                         mBoundElement, mContent, aNotify);
     614             :   }
     615         255 : }
     616             : 
     617             : void
     618         633 : nsXBLBinding::ExecuteAttachedHandler()
     619             : {
     620         633 :   if (mNextBinding)
     621         361 :     mNextBinding->ExecuteAttachedHandler();
     622             : 
     623         633 :   if (AllowScripts())
     624         633 :     mPrototypeBinding->BindingAttached(mBoundElement);
     625         633 : }
     626             : 
     627             : void
     628           0 : nsXBLBinding::ExecuteDetachedHandler()
     629             : {
     630           0 :   if (AllowScripts())
     631           0 :     mPrototypeBinding->BindingDetached(mBoundElement);
     632             : 
     633           0 :   if (mNextBinding)
     634           0 :     mNextBinding->ExecuteDetachedHandler();
     635           0 : }
     636             : 
     637             : void
     638          31 : nsXBLBinding::UnhookEventHandlers()
     639             : {
     640          31 :   nsXBLPrototypeHandler* handlerChain = mPrototypeBinding->GetPrototypeHandlers();
     641             : 
     642          31 :   if (handlerChain) {
     643           6 :     EventListenerManager* manager = mBoundElement->GetExistingListenerManager();
     644           6 :     if (!manager) {
     645           0 :       return;
     646             :     }
     647             : 
     648           6 :     bool isChromeBinding = mPrototypeBinding->IsChrome();
     649             :     nsXBLPrototypeHandler* curr;
     650          22 :     for (curr = handlerChain; curr; curr = curr->GetNextHandler()) {
     651          16 :       nsXBLEventHandler* handler = curr->GetCachedEventHandler();
     652          16 :       if (!handler) {
     653          18 :         continue;
     654             :       }
     655             : 
     656          14 :       nsCOMPtr<nsIAtom> eventAtom = curr->GetEventName();
     657          21 :       if (!eventAtom ||
     658          14 :           eventAtom == nsGkAtoms::keyup ||
     659          21 :           eventAtom == nsGkAtoms::keydown ||
     660           7 :           eventAtom == nsGkAtoms::keypress)
     661           0 :         continue;
     662             : 
     663             :       // Figure out if we're using capturing or not.
     664           7 :       EventListenerFlags flags;
     665           7 :       flags.mCapture = (curr->GetPhase() == NS_PHASE_CAPTURING);
     666             : 
     667             :       // If this is a command, remove it from the system event group,
     668             :       // otherwise remove it from the standard event group.
     669             : 
     670          14 :       if ((curr->GetType() & (NS_HANDLER_TYPE_XBL_COMMAND |
     671           9 :                               NS_HANDLER_TYPE_SYSTEM)) &&
     672           0 :           (isChromeBinding || mBoundElement->IsInNativeAnonymousSubtree())) {
     673           2 :         flags.mInSystemGroup = true;
     674             :       }
     675             : 
     676             :       manager->RemoveEventListenerByType(handler,
     677          14 :                                          nsDependentAtomString(eventAtom),
     678           7 :                                          flags);
     679             :     }
     680             : 
     681             :     const nsCOMArray<nsXBLKeyEventHandler>* keyHandlers =
     682           6 :       mPrototypeBinding->GetKeyEventHandlers();
     683             :     int32_t i;
     684          11 :     for (i = 0; i < keyHandlers->Count(); ++i) {
     685           5 :       nsXBLKeyEventHandler* handler = keyHandlers->ObjectAt(i);
     686             : 
     687          10 :       nsAutoString type;
     688           5 :       handler->GetEventName(type);
     689             : 
     690             :       // Figure out if we're using capturing or not.
     691           5 :       EventListenerFlags flags;
     692           5 :       flags.mCapture = (handler->GetPhase() == NS_PHASE_CAPTURING);
     693             : 
     694             :       // If this is a command, remove it from the system event group, otherwise
     695             :       // remove it from the standard event group.
     696             : 
     697           6 :       if ((handler->GetType() & (NS_HANDLER_TYPE_XBL_COMMAND | NS_HANDLER_TYPE_SYSTEM)) &&
     698           0 :           (isChromeBinding || mBoundElement->IsInNativeAnonymousSubtree())) {
     699           1 :         flags.mInSystemGroup = true;
     700             :       }
     701             : 
     702           5 :       manager->RemoveEventListenerByType(handler, type, flags);
     703             :     }
     704             :   }
     705             : }
     706             : 
     707             : static void
     708          10 : UpdateInsertionParent(XBLChildrenElement* aPoint,
     709             :                       nsIContent* aOldBoundElement)
     710             : {
     711          10 :   if (aPoint->IsDefaultInsertion()) {
     712           2 :     return;
     713             :   }
     714             : 
     715          12 :   for (size_t i = 0; i < aPoint->InsertedChildrenLength(); ++i) {
     716           4 :     nsIContent* child = aPoint->InsertedChild(i);
     717             : 
     718           4 :     MOZ_ASSERT(child->GetParentNode());
     719             : 
     720             :     // Here, we're iterating children that we inserted. There are two cases:
     721             :     // either |child| is an explicit child of |aOldBoundElement| and is no
     722             :     // longer inserted anywhere or it's a child of a <children> element
     723             :     // parented to |aOldBoundElement|. In the former case, the child is no
     724             :     // longer inserted anywhere, so we set its insertion parent to null. In the
     725             :     // latter case, the child is now inserted into |aOldBoundElement| from some
     726             :     // binding above us, so we set its insertion parent to aOldBoundElement.
     727           4 :     if (child->GetParentNode() == aOldBoundElement) {
     728           4 :       child->SetXBLInsertionParent(nullptr);
     729             :     } else {
     730           0 :       child->SetXBLInsertionParent(aOldBoundElement);
     731             :     }
     732             :   }
     733             : }
     734             : 
     735             : void
     736          31 : nsXBLBinding::ChangeDocument(nsIDocument* aOldDocument, nsIDocument* aNewDocument)
     737             : {
     738          31 :   if (aOldDocument == aNewDocument)
     739           0 :     return;
     740             : 
     741             :   // Now the binding dies.  Unhook our prototypes.
     742          31 :   if (mPrototypeBinding->HasImplementation()) {
     743          50 :     AutoJSAPI jsapi;
     744             :     // Init might fail here if we've cycle-collected the global object, since
     745             :     // the Unlink phase of cycle collection happens after JS GC finalization.
     746             :     // But in that case, we don't care about fixing the prototype chain, since
     747             :     // everything's going away immediately.
     748          25 :     if (jsapi.Init(aOldDocument->GetScopeObject())) {
     749          25 :       JSContext* cx = jsapi.cx();
     750             : 
     751          50 :       JS::Rooted<JSObject*> scriptObject(cx, mBoundElement->GetWrapper());
     752          25 :       if (scriptObject) {
     753             :         // XXX Stay in sync! What if a layered binding has an
     754             :         // <interface>?!
     755             :         // XXXbz what does that comment mean, really?  It seems to date
     756             :         // back to when there was such a thing as an <interface>, whever
     757             :         // that was...
     758             : 
     759             :         // Find the right prototype.
     760          50 :         JSAutoCompartment ac(cx, scriptObject);
     761             : 
     762          50 :         JS::Rooted<JSObject*> base(cx, scriptObject);
     763          50 :         JS::Rooted<JSObject*> proto(cx);
     764           0 :         for ( ; true; base = proto) { // Will break out on null proto
     765          25 :           if (!JS_GetPrototype(cx, base, &proto)) {
     766           0 :             return;
     767             :           }
     768          25 :           if (!proto) {
     769          25 :             break;
     770             :           }
     771             : 
     772          25 :           if (JS_GetClass(proto) != &gPrototypeJSClass) {
     773             :             // Clearly not the right class
     774           0 :             continue;
     775             :           }
     776             : 
     777             :           RefPtr<nsXBLDocumentInfo> docInfo =
     778          25 :             static_cast<nsXBLDocumentInfo*>(::JS_GetPrivate(proto));
     779          25 :           if (!docInfo) {
     780             :             // Not the proto we seek
     781           0 :             continue;
     782             :           }
     783             : 
     784          25 :           JS::Value protoBinding = ::JS_GetReservedSlot(proto, 0);
     785             : 
     786          25 :           if (protoBinding.toPrivate() != mPrototypeBinding) {
     787             :             // Not the right binding
     788           0 :             continue;
     789             :           }
     790             : 
     791             :           // Alright!  This is the right prototype.  Pull it out of the
     792             :           // proto chain.
     793          25 :           JS::Rooted<JSObject*> grandProto(cx);
     794          25 :           if (!JS_GetPrototype(cx, proto, &grandProto)) {
     795           0 :             return;
     796             :           }
     797          25 :           ::JS_SetPrototype(cx, base, grandProto);
     798          25 :           break;
     799           0 :         }
     800             : 
     801          25 :         mPrototypeBinding->UndefineFields(cx, scriptObject);
     802             : 
     803             :         // Don't remove the reference from the document to the
     804             :         // wrapper here since it'll be removed by the element
     805             :         // itself when that's taken out of the document.
     806             :       }
     807             :     }
     808             :   }
     809             : 
     810             :   // Remove our event handlers
     811          31 :   UnhookEventHandlers();
     812             : 
     813             :   {
     814          62 :     nsAutoScriptBlocker scriptBlocker;
     815             : 
     816             :     // Then do our ancestors.  This reverses the construction order, so that at
     817             :     // all times things are consistent as far as everyone is concerned.
     818          31 :     if (mNextBinding) {
     819          19 :       mNextBinding->ChangeDocument(aOldDocument, aNewDocument);
     820             :     }
     821             : 
     822             :     // Update the anonymous content.
     823             :     // XXXbz why not only for style bindings?
     824          31 :     if (mContent && !mIsShadowRootBinding) {
     825           7 :       nsXBLBinding::UninstallAnonymousContent(aOldDocument, mContent);
     826             :     }
     827             : 
     828             :     // Now that we've unbound our anonymous content from the tree and updated
     829             :     // its binding parent, update the insertion parent for content inserted
     830             :     // into our <children> elements.
     831          31 :     if (mDefaultInsertionPoint) {
     832           2 :       UpdateInsertionParent(mDefaultInsertionPoint, mBoundElement);
     833             :     }
     834             : 
     835          39 :     for (size_t i = 0; i < mInsertionPoints.Length(); ++i) {
     836           8 :       UpdateInsertionParent(mInsertionPoints[i], mBoundElement);
     837             :     }
     838             : 
     839             :     // Now that our inserted children no longer think they're inserted
     840             :     // anywhere, make sure our internal state reflects that as well.
     841          31 :     ClearInsertionPoints();
     842             :   }
     843             : }
     844             : 
     845             : bool
     846        1993 : nsXBLBinding::InheritsStyle() const
     847             : {
     848             :   // XXX Will have to change if we ever allow multiple bindings to contribute anonymous content.
     849             :   // Most derived binding with anonymous content determines style inheritance for now.
     850             : 
     851             :   // XXX What about bindings with <content> but no kids, e.g., my treecell-text binding?
     852        1993 :   if (mContent)
     853        1800 :     return mPrototypeBinding->InheritsStyle();
     854             : 
     855         193 :   if (mNextBinding)
     856         189 :     return mNextBinding->InheritsStyle();
     857             : 
     858           4 :   return true;
     859             : }
     860             : 
     861             : void
     862       10558 : nsXBLBinding::WalkRules(nsIStyleRuleProcessor::EnumFunc aFunc, void* aData)
     863             : {
     864       10558 :   if (mNextBinding)
     865        6955 :     mNextBinding->WalkRules(aFunc, aData);
     866             : 
     867       10558 :   nsIStyleRuleProcessor *rules = mPrototypeBinding->GetRuleProcessor();
     868       10558 :   if (rules)
     869        3436 :     (*aFunc)(rules, aData);
     870       10558 : }
     871             : 
     872             : const ServoStyleSet*
     873           0 : nsXBLBinding::GetServoStyleSet() const
     874             : {
     875           0 :   return mPrototypeBinding->GetServoStyleSet();
     876             : }
     877             : 
     878             : // Internal helper methods ////////////////////////////////////////////////////////////////
     879             : 
     880             : // Get or create a WeakMap object on a given XBL-hosting global.
     881             : //
     882             : // The scheme is as follows. XBL-hosting globals (either privileged content
     883             : // Windows or XBL scopes) get two lazily-defined WeakMap properties. Each
     884             : // WeakMap is keyed by the grand-proto - i.e. the original prototype of the
     885             : // content before it was bound, and the prototype of the class object that we
     886             : // splice in. The values in the WeakMap are simple dictionary-style objects,
     887             : // mapping from XBL class names to class objects.
     888             : static JSObject*
     889         523 : GetOrCreateClassObjectMap(JSContext *cx, JS::Handle<JSObject*> scope, const char *mapName)
     890             : {
     891         523 :   AssertSameCompartment(cx, scope);
     892         523 :   MOZ_ASSERT(JS_IsGlobalObject(scope));
     893         523 :   MOZ_ASSERT(scope == xpc::GetXBLScopeOrGlobal(cx, scope));
     894             : 
     895             :   // First, see if the map is already defined.
     896        1046 :   JS::Rooted<JS::PropertyDescriptor> desc(cx);
     897         523 :   if (!JS_GetOwnPropertyDescriptor(cx, scope, mapName, &desc)) {
     898           0 :     return nullptr;
     899             :   }
     900        2090 :   if (desc.object() && desc.value().isObject() &&
     901        1567 :       JS::IsWeakMapObject(&desc.value().toObject())) {
     902         522 :     return &desc.value().toObject();
     903             :   }
     904             : 
     905             :   // It's not there. Create and define it.
     906           2 :   JS::Rooted<JSObject*> map(cx, JS::NewWeakMapObject(cx));
     907           1 :   if (!map || !JS_DefineProperty(cx, scope, mapName, map,
     908             :                                  JSPROP_PERMANENT | JSPROP_READONLY,
     909             :                                  JS_STUBGETTER, JS_STUBSETTER))
     910             :   {
     911           0 :     return nullptr;
     912             :   }
     913           1 :   return map;
     914             : }
     915             : 
     916             : static JSObject*
     917         523 : GetOrCreateMapEntryForPrototype(JSContext *cx, JS::Handle<JSObject*> proto)
     918             : {
     919         523 :   AssertSameCompartment(cx, proto);
     920             :   // We want to hang our class objects off the XBL scope. But since we also
     921             :   // hoist anonymous content into the XBL scope, this creates the potential for
     922             :   // tricky collisions, since we can simultaneously  have a bound in-content
     923             :   // node with grand-proto HTMLDivElement and a bound anonymous node whose
     924             :   // grand-proto is the XBL scope's cross-compartment wrapper to HTMLDivElement.
     925             :   // Since we have to wrap the WeakMap keys into its scope, this distinction
     926             :   // would be lost if we don't do something about it.
     927             :   //
     928             :   // So we define two maps - one class objects that live in content (prototyped
     929             :   // to content prototypes), and the other for class objects that live in the
     930             :   // XBL scope (prototyped to cross-compartment-wrapped content prototypes).
     931         523 :   const char* name = xpc::IsInContentXBLScope(proto) ? "__ContentClassObjectMap__"
     932         523 :                                                      : "__XBLClassObjectMap__";
     933             : 
     934             :   // Now, enter the XBL scope, since that's where we need to operate, and wrap
     935             :   // the proto accordingly. We hang the map off of the content XBL scope for
     936             :   // content, and the Window for chrome (whether add-ons are involved or not).
     937        1046 :   JS::Rooted<JSObject*> scope(cx, xpc::GetXBLScopeOrGlobal(cx, proto));
     938         523 :   NS_ENSURE_TRUE(scope, nullptr);
     939         523 :   MOZ_ASSERT(js::GetGlobalForObjectCrossCompartment(scope) == scope);
     940             : 
     941        1046 :   JS::Rooted<JSObject*> wrappedProto(cx, proto);
     942        1046 :   JSAutoCompartment ac(cx, scope);
     943         523 :   if (!JS_WrapObject(cx, &wrappedProto)) {
     944           0 :     return nullptr;
     945             :   }
     946             : 
     947             :   // Grab the appropriate WeakMap.
     948        1046 :   JS::Rooted<JSObject*> map(cx, GetOrCreateClassObjectMap(cx, scope, name));
     949         523 :   if (!map) {
     950           0 :     return nullptr;
     951             :   }
     952             : 
     953             :   // See if we already have a map entry for that prototype.
     954        1046 :   JS::Rooted<JS::Value> val(cx);
     955         523 :   if (!JS::GetWeakMapEntry(cx, map, wrappedProto, &val)) {
     956           0 :     return nullptr;
     957             :   }
     958         523 :   if (val.isObject()) {
     959         498 :     return &val.toObject();
     960             :   }
     961             : 
     962             :   // We don't have an entry. Create one and stick it in the map.
     963          50 :   JS::Rooted<JSObject*> entry(cx);
     964          25 :   entry = JS_NewObjectWithGivenProto(cx, nullptr, nullptr);
     965          25 :   if (!entry) {
     966           0 :     return nullptr;
     967             :   }
     968          50 :   JS::Rooted<JS::Value> entryVal(cx, JS::ObjectValue(*entry));
     969          25 :   if (!JS::SetWeakMapEntry(cx, map, wrappedProto, entryVal)) {
     970             :     NS_WARNING("SetWeakMapEntry failed, probably due to non-preservable WeakMap "
     971           0 :                "key. XBL binding will fail for this element.");
     972           0 :     return nullptr;
     973             :   }
     974          25 :   return entry;
     975             : }
     976             : 
     977             : static
     978             : nsXBLPrototypeBinding*
     979         463 : GetProtoBindingFromClassObject(JSObject* obj)
     980             : {
     981         463 :   MOZ_ASSERT(JS_GetClass(obj) == &gPrototypeJSClass);
     982         463 :   return static_cast<nsXBLPrototypeBinding*>(::JS_GetReservedSlot(obj, 0).toPrivate());
     983             : }
     984             : 
     985             : 
     986             : // static
     987             : nsresult
     988         523 : nsXBLBinding::DoInitJSClass(JSContext *cx,
     989             :                             JS::Handle<JSObject*> obj,
     990             :                             const nsString& aClassName,
     991             :                             nsXBLPrototypeBinding* aProtoBinding,
     992             :                             JS::MutableHandle<JSObject*> aClassObject,
     993             :                             bool* aNew)
     994             : {
     995         523 :   MOZ_ASSERT(obj);
     996             : 
     997             :   // Note that, now that NAC reflectors are created in the XBL scope, the
     998             :   // reflector is not necessarily same-compartment with the document. So we'll
     999             :   // end up creating a separate instance of the oddly-named XBL class object
    1000             :   // and defining it as a property on the XBL scope's global. This works fine,
    1001             :   // but we need to make sure never to assume that the the reflector and
    1002             :   // prototype are same-compartment with the bound document.
    1003        1046 :   JS::Rooted<JSObject*> global(cx, js::GetGlobalForObjectCrossCompartment(obj));
    1004             : 
    1005             :   // We never store class objects in add-on scopes.
    1006        1046 :   JS::Rooted<JSObject*> xblScope(cx, xpc::GetXBLScopeOrGlobal(cx, global));
    1007         523 :   NS_ENSURE_TRUE(xblScope, NS_ERROR_UNEXPECTED);
    1008             : 
    1009        1046 :   JS::Rooted<JSObject*> parent_proto(cx);
    1010         523 :   if (!JS_GetPrototype(cx, obj, &parent_proto)) {
    1011           0 :     return NS_ERROR_FAILURE;
    1012             :   }
    1013             : 
    1014             :   // Get the map entry for the parent prototype. In the one-off case that the
    1015             :   // parent prototype is null, we somewhat hackily just use the WeakMap itself
    1016             :   // as a property holder.
    1017        1046 :   JS::Rooted<JSObject*> holder(cx);
    1018         523 :   if (parent_proto) {
    1019         523 :     holder = GetOrCreateMapEntryForPrototype(cx, parent_proto);
    1020             :   } else {
    1021           0 :     JSAutoCompartment innerAC(cx, xblScope);
    1022           0 :     holder = GetOrCreateClassObjectMap(cx, xblScope, "__ContentClassObjectMap__");
    1023             :   }
    1024         523 :   if (NS_WARN_IF(!holder)) {
    1025           0 :     return NS_ERROR_FAILURE;
    1026             :   }
    1027         523 :   js::AssertSameCompartment(holder, xblScope);
    1028        1046 :   JSAutoCompartment ac(cx, holder);
    1029             : 
    1030             :   // Look up the class on the property holder. The only properties on the
    1031             :   // holder should be class objects. If we don't find the class object, we need
    1032             :   // to create and define it.
    1033        1046 :   JS::Rooted<JSObject*> proto(cx);
    1034        1046 :   JS::Rooted<JS::PropertyDescriptor> desc(cx);
    1035         523 :   if (!JS_GetOwnUCPropertyDescriptor(cx, holder, aClassName.get(), &desc)) {
    1036           0 :     return NS_ERROR_OUT_OF_MEMORY;
    1037             :   }
    1038         523 :   *aNew = !desc.object();
    1039         523 :   if (desc.object()) {
    1040         463 :     proto = &desc.value().toObject();
    1041             :     DebugOnly<nsXBLPrototypeBinding*> cachedBinding =
    1042         926 :       GetProtoBindingFromClassObject(js::UncheckedUnwrap(proto));
    1043         463 :     MOZ_ASSERT(cachedBinding == aProtoBinding);
    1044             :   } else {
    1045             : 
    1046             :     // We need to create the prototype. First, enter the compartment where it's
    1047             :     // going to live, and create it.
    1048         120 :     JSAutoCompartment ac2(cx, global);
    1049          60 :     proto = JS_NewObjectWithGivenProto(cx, &gPrototypeJSClass, parent_proto);
    1050          60 :     if (!proto) {
    1051           0 :       return NS_ERROR_OUT_OF_MEMORY;
    1052             :     }
    1053             : 
    1054             :     // Keep this proto binding alive while we're alive.  Do this first so that
    1055             :     // we can guarantee that in XBLFinalize this will be non-null.
    1056             :     // Note that we can't just store aProtoBinding in the private and
    1057             :     // addref/release the nsXBLDocumentInfo through it, because cycle
    1058             :     // collection doesn't seem to work right if the private is not an
    1059             :     // nsISupports.
    1060          60 :     nsXBLDocumentInfo* docInfo = aProtoBinding->XBLDocumentInfo();
    1061          60 :     ::JS_SetPrivate(proto, docInfo);
    1062          60 :     NS_ADDREF(docInfo);
    1063          60 :     JS_SetReservedSlot(proto, 0, JS::PrivateValue(aProtoBinding));
    1064             : 
    1065             :     // Next, enter the compartment of the property holder, wrap the proto, and
    1066             :     // stick it on.
    1067         120 :     JSAutoCompartment ac3(cx, holder);
    1068         300 :     if (!JS_WrapObject(cx, &proto) ||
    1069         240 :         !JS_DefineUCProperty(cx, holder, aClassName.get(), -1, proto,
    1070             :                              JSPROP_READONLY | JSPROP_PERMANENT,
    1071             :                              JS_STUBGETTER, JS_STUBSETTER))
    1072             :     {
    1073           0 :       return NS_ERROR_OUT_OF_MEMORY;
    1074             :     }
    1075             :   }
    1076             : 
    1077             :   // Whew. We have the proto. Wrap it back into the compartment of |obj|,
    1078             :   // splice it in, and return it.
    1079        1046 :   JSAutoCompartment ac4(cx, obj);
    1080         523 :   if (!JS_WrapObject(cx, &proto) || !JS_SetPrototype(cx, obj, proto)) {
    1081           0 :     return NS_ERROR_FAILURE;
    1082             :   }
    1083         523 :   aClassObject.set(proto);
    1084         523 :   return NS_OK;
    1085             : }
    1086             : 
    1087             : bool
    1088        2052 : nsXBLBinding::AllowScripts()
    1089             : {
    1090        2052 :   return mBoundElement && mPrototypeBinding->GetAllowScripts();
    1091             : }
    1092             : 
    1093             : nsXBLBinding*
    1094           0 : nsXBLBinding::RootBinding()
    1095             : {
    1096           0 :   if (mNextBinding)
    1097           0 :     return mNextBinding->RootBinding();
    1098             : 
    1099           0 :   return this;
    1100             : }
    1101             : 
    1102             : bool
    1103           0 : nsXBLBinding::ResolveAllFields(JSContext *cx, JS::Handle<JSObject*> obj) const
    1104             : {
    1105           0 :   if (!mPrototypeBinding->ResolveAllFields(cx, obj)) {
    1106           0 :     return false;
    1107             :   }
    1108             : 
    1109           0 :   if (mNextBinding) {
    1110           0 :     return mNextBinding->ResolveAllFields(cx, obj);
    1111             :   }
    1112             : 
    1113           0 :   return true;
    1114             : }
    1115             : 
    1116             : bool
    1117           0 : nsXBLBinding::LookupMember(JSContext* aCx, JS::Handle<jsid> aId,
    1118             :                            JS::MutableHandle<JS::PropertyDescriptor> aDesc)
    1119             : {
    1120             :   // We should never enter this function with a pre-filled property descriptor.
    1121           0 :   MOZ_ASSERT(!aDesc.object());
    1122             : 
    1123             :   // Get the string as an nsString before doing anything, so we can make
    1124             :   // convenient comparisons during our search.
    1125           0 :   if (!JSID_IS_STRING(aId)) {
    1126           0 :     return true;
    1127             :   }
    1128           0 :   nsAutoJSString name;
    1129           0 :   if (!name.init(aCx, JSID_TO_STRING(aId))) {
    1130           0 :     return false;
    1131             :   }
    1132             : 
    1133             :   // We have a weak reference to our bound element, so make sure it's alive.
    1134           0 :   if (!mBoundElement || !mBoundElement->GetWrapper()) {
    1135           0 :     return false;
    1136             :   }
    1137             : 
    1138             :   // Get the scope of mBoundElement and the associated XBL scope. We should only
    1139             :   // be calling into this machinery if we're running in a separate XBL scope.
    1140             :   //
    1141             :   // Note that we only end up in LookupMember for XrayWrappers from XBL scopes
    1142             :   // into content. So for NAC reflectors that live in the XBL scope, we should
    1143             :   // never get here. But on the off-chance that someone adds new callsites to
    1144             :   // LookupMember, we do a release-mode assertion as belt-and-braces.
    1145             :   // We do a release-mode assertion here to be extra safe.
    1146             :   //
    1147             :   // This code is only called for content XBL, so we don't have to worry about
    1148             :   // add-on scopes here.
    1149             :   JS::Rooted<JSObject*> boundScope(aCx,
    1150           0 :     js::GetGlobalForObjectCrossCompartment(mBoundElement->GetWrapper()));
    1151           0 :   MOZ_RELEASE_ASSERT(!xpc::IsInAddonScope(boundScope));
    1152           0 :   MOZ_RELEASE_ASSERT(!xpc::IsInContentXBLScope(boundScope));
    1153           0 :   JS::Rooted<JSObject*> xblScope(aCx, xpc::GetXBLScope(aCx, boundScope));
    1154           0 :   NS_ENSURE_TRUE(xblScope, false);
    1155           0 :   MOZ_ASSERT(boundScope != xblScope);
    1156             : 
    1157             :   // Enter the xbl scope and invoke the internal version.
    1158             :   {
    1159           0 :     JSAutoCompartment ac(aCx, xblScope);
    1160           0 :     JS::Rooted<jsid> id(aCx, aId);
    1161           0 :     if (!LookupMemberInternal(aCx, name, id, aDesc, xblScope)) {
    1162           0 :       return false;
    1163             :     }
    1164             :   }
    1165             : 
    1166             :   // Wrap into the caller's scope.
    1167           0 :   return JS_WrapPropertyDescriptor(aCx, aDesc);
    1168             : }
    1169             : 
    1170             : bool
    1171           0 : nsXBLBinding::LookupMemberInternal(JSContext* aCx, nsString& aName,
    1172             :                                    JS::Handle<jsid> aNameAsId,
    1173             :                                    JS::MutableHandle<JS::PropertyDescriptor> aDesc,
    1174             :                                    JS::Handle<JSObject*> aXBLScope)
    1175             : {
    1176             :   // First, see if we have an implementation. If we don't, it means that this
    1177             :   // binding doesn't have a class object, and thus doesn't have any members.
    1178             :   // Skip it.
    1179           0 :   if (!PrototypeBinding()->HasImplementation()) {
    1180           0 :     if (!mNextBinding) {
    1181           0 :       return true;
    1182             :     }
    1183           0 :     return mNextBinding->LookupMemberInternal(aCx, aName, aNameAsId,
    1184           0 :                                               aDesc, aXBLScope);
    1185             :   }
    1186             : 
    1187             :   // Find our class object. It's in a protected scope and permanent just in case,
    1188             :   // so should be there no matter what.
    1189           0 :   JS::Rooted<JS::Value> classObject(aCx);
    1190           0 :   if (!JS_GetUCProperty(aCx, aXBLScope, PrototypeBinding()->ClassName().get(),
    1191             :                         -1, &classObject)) {
    1192           0 :     return false;
    1193             :   }
    1194             : 
    1195             :   // The bound element may have been adoped by a document and have a different
    1196             :   // wrapper (and different xbl scope) than when the binding was applied, in
    1197             :   // this case getting the class object will fail. Behave as if the class
    1198             :   // object did not exist.
    1199           0 :   if (classObject.isUndefined()) {
    1200           0 :     return true;
    1201             :   }
    1202             : 
    1203           0 :   MOZ_ASSERT(classObject.isObject());
    1204             : 
    1205             :   // Look for the property on this binding. If it's not there, try the next
    1206             :   // binding on the chain.
    1207           0 :   nsXBLProtoImpl* impl = mPrototypeBinding->GetImplementation();
    1208           0 :   JS::Rooted<JSObject*> object(aCx, &classObject.toObject());
    1209           0 :   if (impl && !impl->LookupMember(aCx, aName, aNameAsId, aDesc, object)) {
    1210           0 :     return false;
    1211             :   }
    1212           0 :   if (aDesc.object() || !mNextBinding) {
    1213           0 :     return true;
    1214             :   }
    1215             : 
    1216           0 :   return mNextBinding->LookupMemberInternal(aCx, aName, aNameAsId, aDesc,
    1217           0 :                                             aXBLScope);
    1218             : }
    1219             : 
    1220             : bool
    1221           0 : nsXBLBinding::HasField(nsString& aName)
    1222             : {
    1223             :   // See if this binding has such a field.
    1224           0 :   return mPrototypeBinding->FindField(aName) ||
    1225           0 :     (mNextBinding && mNextBinding->HasField(aName));
    1226             : }
    1227             : 
    1228             : void
    1229           0 : nsXBLBinding::MarkForDeath()
    1230             : {
    1231           0 :   mMarkedForDeath = true;
    1232           0 :   ExecuteDetachedHandler();
    1233           0 : }
    1234             : 
    1235             : bool
    1236         319 : nsXBLBinding::ImplementsInterface(REFNSIID aIID) const
    1237             : {
    1238         678 :   return mPrototypeBinding->ImplementsInterface(aIID) ||
    1239         751 :     (mNextBinding && mNextBinding->ImplementsInterface(aIID));
    1240             : }

Generated by: LCOV version 1.13