1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 sw=2 et tw=79: */
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/. */
9 #include "nsXBLDocumentInfo.h"
10 #include "nsIInputStream.h"
11 #include "nsNameSpaceManager.h"
14 #include "nsIChannel.h"
15 #include "nsXPIDLString.h"
16 #include "nsReadableUtils.h"
17 #include "nsNetUtil.h"
19 #include "nsIContent.h"
20 #include "nsIDocument.h"
21 #include "nsContentUtils.h"
22 #include "ChildIterator.h"
24 #include "nsIXULDocument.h"
26 #include "nsIXMLContentSink.h"
27 #include "nsContentCID.h"
28 #include "mozilla/dom/XMLDocument.h"
30 #include "nsXBLService.h"
31 #include "nsIXPConnect.h"
32 #include "nsIScriptContext.h"
36 #include "mozilla/EventListenerManager.h"
37 #include "nsIDOMEventListener.h"
38 #include "nsAttrName.h"
40 #include "nsGkAtoms.h"
42 #include "nsXBLPrototypeHandler.h"
44 #include "nsXBLPrototypeBinding.h"
45 #include "nsXBLBinding.h"
46 #include "nsIPrincipal.h"
47 #include "nsIScriptSecurityManager.h"
48 #include "mozilla/dom/XBLChildrenElement.h"
51 #include "nsNodeUtils.h"
52 #include "nsJSUtils.h"
53 #include "nsCycleCollector.h"
55 // Nasty hack. Maybe we could move some of the classinfo utility methods
56 // (e.g. WrapNative) over to nsContentUtils?
57 #include "nsDOMClassInfo.h"
59 #include "mozilla/dom/Element.h"
60 #include "mozilla/dom/ScriptSettings.h"
61 #include "mozilla/dom/ShadowRoot.h"
63 using namespace mozilla
;
64 using namespace mozilla::dom
;
68 /***********************************************************************/
70 // The JS class for XBLBinding
73 XBLFinalize(JSFreeOp
*fop
, JSObject
*obj
)
75 nsXBLDocumentInfo
* docInfo
=
76 static_cast<nsXBLDocumentInfo
*>(::JS_GetPrivate(obj
));
77 cyclecollector::DeferredFinalize(docInfo
);
81 XBLEnumerate(JSContext
*cx
, JS::Handle
<JSObject
*> obj
)
83 nsXBLPrototypeBinding
* protoBinding
=
84 static_cast<nsXBLPrototypeBinding
*>(::JS_GetReservedSlot(obj
, 0).toPrivate());
85 MOZ_ASSERT(protoBinding
);
87 return protoBinding
->ResolveAllFields(cx
, obj
);
90 static const JSClass gPrototypeJSClass
= {
91 "XBL prototype JSClass",
92 JSCLASS_HAS_PRIVATE
| JSCLASS_PRIVATE_IS_NSISUPPORTS
|
93 // Our one reserved slot holds the relevant nsXBLPrototypeBinding
94 JSCLASS_HAS_RESERVED_SLOTS(1),
95 nullptr, nullptr, nullptr, nullptr,
96 XBLEnumerate
, nullptr,
98 nullptr, nullptr, nullptr, nullptr
101 // Implementation /////////////////////////////////////////////////////////////////
103 // Constructors/Destructors
104 nsXBLBinding::nsXBLBinding(nsXBLPrototypeBinding
* aBinding
)
105 : mMarkedForDeath(false)
106 , mUsingContentXBLScope(false)
107 , mIsShadowRootBinding(false)
108 , mPrototypeBinding(aBinding
)
110 NS_ASSERTION(mPrototypeBinding
, "Must have a prototype binding!");
111 // Grab a ref to the document info so the prototype binding won't die
112 NS_ADDREF(mPrototypeBinding
->XBLDocumentInfo());
115 // Constructor used by web components.
116 nsXBLBinding::nsXBLBinding(ShadowRoot
* aShadowRoot
, nsXBLPrototypeBinding
* aBinding
)
117 : mMarkedForDeath(false),
118 mUsingContentXBLScope(false),
119 mIsShadowRootBinding(true),
120 mPrototypeBinding(aBinding
),
121 mContent(aShadowRoot
)
123 NS_ASSERTION(mPrototypeBinding
, "Must have a prototype binding!");
124 // Grab a ref to the document info so the prototype binding won't die
125 NS_ADDREF(mPrototypeBinding
->XBLDocumentInfo());
128 nsXBLBinding::~nsXBLBinding(void)
130 if (mContent
&& !mIsShadowRootBinding
) {
131 // It is unnecessary to uninstall anonymous content in a shadow tree
132 // because the ShadowRoot itself is a DocumentFragment and does not
133 // need any additional cleanup.
134 nsXBLBinding::UninstallAnonymousContent(mContent
->OwnerDoc(), mContent
);
136 nsXBLDocumentInfo
* info
= mPrototypeBinding
->XBLDocumentInfo();
140 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXBLBinding
)
142 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXBLBinding
)
143 // XXX Probably can't unlink mPrototypeBinding->XBLDocumentInfo(), because
144 // mPrototypeBinding is weak.
145 if (tmp
->mContent
&& !tmp
->mIsShadowRootBinding
) {
146 nsXBLBinding::UninstallAnonymousContent(tmp
->mContent
->OwnerDoc(),
149 NS_IMPL_CYCLE_COLLECTION_UNLINK(mContent
)
150 NS_IMPL_CYCLE_COLLECTION_UNLINK(mNextBinding
)
151 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDefaultInsertionPoint
)
152 NS_IMPL_CYCLE_COLLECTION_UNLINK(mInsertionPoints
)
153 NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnonymousContentList
)
154 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
155 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXBLBinding
)
156 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
,
157 "mPrototypeBinding->XBLDocumentInfo()");
158 cb
.NoteXPCOMChild(tmp
->mPrototypeBinding
->XBLDocumentInfo());
159 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContent
)
160 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNextBinding
)
161 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDefaultInsertionPoint
)
162 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInsertionPoints
)
163 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnonymousContentList
)
164 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
165 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsXBLBinding
, AddRef
)
166 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsXBLBinding
, Release
)
169 nsXBLBinding::SetBaseBinding(nsXBLBinding
* aBinding
)
172 NS_ERROR("Base XBL binding is already defined!");
176 mNextBinding
= aBinding
; // Comptr handles rel/add
180 nsXBLBinding::GetBindingWithContent()
186 return mNextBinding
? mNextBinding
->GetBindingWithContent() : nullptr;
190 nsXBLBinding::InstallAnonymousContent(nsIContent
* aAnonParent
, nsIContent
* aElement
,
191 bool aChromeOnlyContent
)
193 // We need to ensure two things.
194 // (1) The anonymous content should be fooled into thinking it's in the bound
195 // element's document, assuming that the bound element is in a document
196 // Note that we don't change the current doc of aAnonParent here, since that
197 // quite simply does not matter. aAnonParent is just a way of keeping refs
198 // to all its kids, which are anonymous content from the point of view of
200 // (2) The children's parent back pointer should not be to this synthetic root
201 // but should instead point to the enclosing parent element.
202 nsIDocument
* doc
= aElement
->GetCurrentDoc();
203 bool allowScripts
= AllowScripts();
205 nsAutoScriptBlocker scriptBlocker
;
206 for (nsIContent
* child
= aAnonParent
->GetFirstChild();
208 child
= child
->GetNextSibling()) {
209 child
->UnbindFromTree();
210 if (aChromeOnlyContent
) {
211 child
->SetFlags(NODE_CHROME_ONLY_ACCESS
|
212 NODE_IS_ROOT_OF_CHROME_ONLY_ACCESS
);
215 child
->BindToTree(doc
, aElement
, mBoundElement
, allowScripts
);
217 // Oh, well... Just give up.
218 // XXXbz This really shouldn't be a void method!
219 child
->UnbindFromTree();
223 child
->SetFlags(NODE_IS_ANONYMOUS_ROOT
);
226 // To make XUL templates work (and other goodies that happen when
227 // an element is added to a XUL document), we need to notify the
228 // XUL document using its special API.
229 nsCOMPtr
<nsIXULDocument
> xuldoc(do_QueryInterface(doc
));
231 xuldoc
->AddSubtreeToDocument(child
);
237 nsXBLBinding::UninstallAnonymousContent(nsIDocument
* aDocument
,
238 nsIContent
* aAnonParent
)
240 nsAutoScriptBlocker scriptBlocker
;
241 // Hold a strong ref while doing this, just in case.
242 nsCOMPtr
<nsIContent
> anonParent
= aAnonParent
;
244 nsCOMPtr
<nsIXULDocument
> xuldoc
=
245 do_QueryInterface(aDocument
);
247 for (nsIContent
* child
= aAnonParent
->GetFirstChild();
249 child
= child
->GetNextSibling()) {
250 child
->UnbindFromTree();
253 xuldoc
->RemoveSubtreeFromDocument(child
);
260 nsXBLBinding::SetBoundElement(nsIContent
* aElement
)
262 mBoundElement
= aElement
;
264 mNextBinding
->SetBoundElement(aElement
);
266 if (!mBoundElement
) {
270 // Compute whether we're using an XBL scope.
272 // We disable XBL scopes for remote XUL, where we care about compat more
273 // than security. So we need to know whether we're using an XBL scope so that
274 // we can decide what to do about untrusted events when "allowuntrusted"
275 // is not given in the handler declaration.
276 nsCOMPtr
<nsIGlobalObject
> go
= mBoundElement
->OwnerDoc()->GetScopeObject();
277 NS_ENSURE_TRUE_VOID(go
&& go
->GetGlobalJSObject());
278 mUsingContentXBLScope
= xpc::UseContentXBLScope(js::GetObjectCompartment(go
->GetGlobalJSObject()));
282 nsXBLBinding::HasStyleSheets() const
284 // Find out if we need to re-resolve style. We'll need to do this
285 // if we have additional stylesheets in our binding document.
286 if (mPrototypeBinding
->HasStyleSheets())
289 return mNextBinding
? mNextBinding
->HasStyleSheets() : false;
293 nsXBLBinding::GenerateAnonymousContent()
295 NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
296 "Someone forgot a script blocker");
298 // Fetch the content element for this binding.
299 nsIContent
* content
=
300 mPrototypeBinding
->GetImmediateChild(nsGkAtoms::content
);
303 // We have no anonymous content.
305 mNextBinding
->GenerateAnonymousContent();
310 // Find out if we're really building kids or if we're just
311 // using the attribute-setting shorthand hack.
312 uint32_t contentCount
= content
->GetChildCount();
314 // Plan to build the content by default.
315 bool hasContent
= (contentCount
> 0);
317 nsIDocument
* doc
= mBoundElement
->OwnerDoc();
319 nsCOMPtr
<nsINode
> clonedNode
;
320 nsCOMArray
<nsINode
> nodesWithProperties
;
321 nsNodeUtils::Clone(content
, true, doc
->NodeInfoManager(),
322 nodesWithProperties
, getter_AddRefs(clonedNode
));
323 mContent
= clonedNode
->AsElement();
325 // Search for <xbl:children> elements in the XBL content. In the presence
326 // of multiple default insertion points, we use the last one in document
328 for (nsIContent
* child
= mContent
; child
; child
= child
->GetNextNode(mContent
)) {
329 if (child
->NodeInfo()->Equals(nsGkAtoms::children
, kNameSpaceID_XBL
)) {
330 XBLChildrenElement
* point
= static_cast<XBLChildrenElement
*>(child
);
331 if (point
->IsDefaultInsertion()) {
332 mDefaultInsertionPoint
= point
;
334 mInsertionPoints
.AppendElement(point
);
339 // Do this after looking for <children> as this messes up the parent
340 // pointer which would make the GetNextNode call above fail
341 InstallAnonymousContent(mContent
, mBoundElement
,
342 mPrototypeBinding
->ChromeOnlyContent());
344 // Insert explicit children into insertion points
345 if (mDefaultInsertionPoint
&& mInsertionPoints
.IsEmpty()) {
346 ExplicitChildIterator
iter(mBoundElement
);
347 for (nsIContent
* child
= iter
.GetNextChild(); child
; child
= iter
.GetNextChild()) {
348 mDefaultInsertionPoint
->AppendInsertedChild(child
);
351 // It is odd to come into this code if mInsertionPoints is not empty, but
352 // we need to make sure to do the compatibility hack below if the bound
353 // node has any non <xul:template> or <xul:observes> children.
354 ExplicitChildIterator
iter(mBoundElement
);
355 for (nsIContent
* child
= iter
.GetNextChild(); child
; child
= iter
.GetNextChild()) {
356 XBLChildrenElement
* point
= FindInsertionPointForInternal(child
);
358 point
->AppendInsertedChild(child
);
360 NodeInfo
*ni
= child
->NodeInfo();
361 if (ni
->NamespaceID() != kNameSpaceID_XUL
||
362 (!ni
->Equals(nsGkAtoms::_template
) &&
363 !ni
->Equals(nsGkAtoms::observes
))) {
364 // Compatibility hack. For some reason the original XBL
365 // implementation dropped the content of a binding if any child of
366 // the bound element didn't match any of the <children> in the
367 // binding. This became a pseudo-API that we have to maintain.
369 // Undo InstallAnonymousContent
370 UninstallAnonymousContent(doc
, mContent
);
372 // Clear out our children elements to avoid dangling references.
373 ClearInsertionPoints();
375 // Pretend as though there was no content in the binding.
383 // Set binding parent on default content if need
384 if (mDefaultInsertionPoint
) {
385 mDefaultInsertionPoint
->MaybeSetupDefaultContent();
387 for (uint32_t i
= 0; i
< mInsertionPoints
.Length(); ++i
) {
388 mInsertionPoints
[i
]->MaybeSetupDefaultContent();
391 mPrototypeBinding
->SetInitialAttributes(mBoundElement
, mContent
);
394 // Always check the content element for potential attributes.
395 // This shorthand hack always happens, even when we didn't
396 // build anonymous content.
397 const nsAttrName
* attrName
;
398 for (uint32_t i
= 0; (attrName
= content
->GetAttrNameAt(i
)); ++i
) {
399 int32_t namespaceID
= attrName
->NamespaceID();
400 // Hold a strong reference here so that the atom doesn't go away during
402 nsCOMPtr
<nsIAtom
> name
= attrName
->LocalName();
404 if (name
!= nsGkAtoms::includes
) {
405 if (!nsContentUtils::HasNonEmptyAttr(mBoundElement
, namespaceID
, name
)) {
407 content
->GetAttr(namespaceID
, name
, value2
);
408 mBoundElement
->SetAttr(namespaceID
, name
, attrName
->GetPrefix(),
413 // Conserve space by wiping the attributes off the clone.
415 mContent
->UnsetAttr(namespaceID
, name
, false);
420 nsXBLBinding::FindInsertionPointFor(nsIContent
* aChild
)
422 // XXX We should get rid of this function as it causes us to traverse the
423 // binding chain multiple times
425 return FindInsertionPointForInternal(aChild
);
428 return mNextBinding
? mNextBinding
->FindInsertionPointFor(aChild
)
433 nsXBLBinding::FindInsertionPointForInternal(nsIContent
* aChild
)
435 for (uint32_t i
= 0; i
< mInsertionPoints
.Length(); ++i
) {
436 XBLChildrenElement
* point
= mInsertionPoints
[i
];
437 if (point
->Includes(aChild
)) {
442 return mDefaultInsertionPoint
;
446 nsXBLBinding::ClearInsertionPoints()
448 if (mDefaultInsertionPoint
) {
449 mDefaultInsertionPoint
->ClearInsertedChildren();
452 for (uint32_t i
= 0; i
< mInsertionPoints
.Length(); ++i
) {
453 mInsertionPoints
[i
]->ClearInsertedChildren();
457 nsAnonymousContentList
*
458 nsXBLBinding::GetAnonymousNodeList()
461 return mNextBinding
? mNextBinding
->GetAnonymousNodeList() : nullptr;
464 if (!mAnonymousContentList
) {
465 mAnonymousContentList
= new nsAnonymousContentList(mContent
);
468 return mAnonymousContentList
;
472 nsXBLBinding::InstallEventHandlers()
474 // Don't install handlers if scripts aren't allowed.
475 if (AllowScripts()) {
476 // Fetch the handlers prototypes for this binding.
477 nsXBLPrototypeHandler
* handlerChain
= mPrototypeBinding
->GetPrototypeHandlers();
480 EventListenerManager
* manager
= mBoundElement
->GetOrCreateListenerManager();
485 nsContentUtils::IsChromeDoc(mBoundElement
->OwnerDoc());
486 bool isChromeBinding
= mPrototypeBinding
->IsChrome();
487 nsXBLPrototypeHandler
* curr
;
488 for (curr
= handlerChain
; curr
; curr
= curr
->GetNextHandler()) {
489 // Fetch the event type.
490 nsCOMPtr
<nsIAtom
> eventAtom
= curr
->GetEventName();
492 eventAtom
== nsGkAtoms::keyup
||
493 eventAtom
== nsGkAtoms::keydown
||
494 eventAtom
== nsGkAtoms::keypress
)
497 nsXBLEventHandler
* handler
= curr
->GetEventHandler();
499 // Figure out if we're using capturing or not.
500 EventListenerFlags flags
;
501 flags
.mCapture
= (curr
->GetPhase() == NS_PHASE_CAPTURING
);
503 // If this is a command, add it in the system event group
504 if ((curr
->GetType() & (NS_HANDLER_TYPE_XBL_COMMAND
|
505 NS_HANDLER_TYPE_SYSTEM
)) &&
506 (isChromeBinding
|| mBoundElement
->IsInNativeAnonymousSubtree())) {
507 flags
.mInSystemGroup
= true;
510 bool hasAllowUntrustedAttr
= curr
->HasAllowUntrustedAttr();
511 if ((hasAllowUntrustedAttr
&& curr
->AllowUntrustedEvents()) ||
512 (!hasAllowUntrustedAttr
&& !isChromeDoc
&& !mUsingContentXBLScope
)) {
513 flags
.mAllowUntrustedEvents
= true;
516 manager
->AddEventListenerByType(handler
,
517 nsDependentAtomString(eventAtom
),
522 const nsCOMArray
<nsXBLKeyEventHandler
>* keyHandlers
=
523 mPrototypeBinding
->GetKeyEventHandlers();
525 for (i
= 0; i
< keyHandlers
->Count(); ++i
) {
526 nsXBLKeyEventHandler
* handler
= keyHandlers
->ObjectAt(i
);
527 handler
->SetIsBoundToChrome(isChromeDoc
);
528 handler
->SetUsingContentXBLScope(mUsingContentXBLScope
);
531 handler
->GetEventName(type
);
533 // If this is a command, add it in the system event group, otherwise
534 // add it to the standard event group.
536 // Figure out if we're using capturing or not.
537 EventListenerFlags flags
;
538 flags
.mCapture
= (handler
->GetPhase() == NS_PHASE_CAPTURING
);
540 if ((handler
->GetType() & (NS_HANDLER_TYPE_XBL_COMMAND
|
541 NS_HANDLER_TYPE_SYSTEM
)) &&
542 (isChromeBinding
|| mBoundElement
->IsInNativeAnonymousSubtree())) {
543 flags
.mInSystemGroup
= true;
546 // For key handlers we have to set mAllowUntrustedEvents flag.
547 // Whether the handling of the event is allowed or not is handled in
548 // nsXBLKeyEventHandler::HandleEvent
549 flags
.mAllowUntrustedEvents
= true;
551 manager
->AddEventListenerByType(handler
, type
, flags
);
557 mNextBinding
->InstallEventHandlers();
561 nsXBLBinding::InstallImplementation()
563 // Always install the base class properties first, so that
564 // derived classes can reference the base class properties.
567 nsresult rv
= mNextBinding
->InstallImplementation();
568 NS_ENSURE_SUCCESS(rv
, rv
);
571 // iterate through each property in the prototype's list and install the property.
573 return mPrototypeBinding
->InstallImplementation(this);
579 nsXBLBinding::GetBaseTag(int32_t* aNameSpaceID
)
581 nsIAtom
*tag
= mPrototypeBinding
->GetBaseTag(aNameSpaceID
);
582 if (!tag
&& mNextBinding
)
583 return mNextBinding
->GetBaseTag(aNameSpaceID
);
589 nsXBLBinding::AttributeChanged(nsIAtom
* aAttribute
, int32_t aNameSpaceID
,
590 bool aRemoveFlag
, bool aNotify
)
592 // XXX Change if we ever allow multiple bindings in a chain to contribute anonymous content
595 mNextBinding
->AttributeChanged(aAttribute
, aNameSpaceID
,
596 aRemoveFlag
, aNotify
);
598 mPrototypeBinding
->AttributeChanged(aAttribute
, aNameSpaceID
, aRemoveFlag
,
599 mBoundElement
, mContent
, aNotify
);
604 nsXBLBinding::ExecuteAttachedHandler()
607 mNextBinding
->ExecuteAttachedHandler();
610 mPrototypeBinding
->BindingAttached(mBoundElement
);
614 nsXBLBinding::ExecuteDetachedHandler()
617 mPrototypeBinding
->BindingDetached(mBoundElement
);
620 mNextBinding
->ExecuteDetachedHandler();
624 nsXBLBinding::UnhookEventHandlers()
626 nsXBLPrototypeHandler
* handlerChain
= mPrototypeBinding
->GetPrototypeHandlers();
629 EventListenerManager
* manager
= mBoundElement
->GetExistingListenerManager();
634 bool isChromeBinding
= mPrototypeBinding
->IsChrome();
635 nsXBLPrototypeHandler
* curr
;
636 for (curr
= handlerChain
; curr
; curr
= curr
->GetNextHandler()) {
637 nsXBLEventHandler
* handler
= curr
->GetCachedEventHandler();
642 nsCOMPtr
<nsIAtom
> eventAtom
= curr
->GetEventName();
644 eventAtom
== nsGkAtoms::keyup
||
645 eventAtom
== nsGkAtoms::keydown
||
646 eventAtom
== nsGkAtoms::keypress
)
649 // Figure out if we're using capturing or not.
650 EventListenerFlags flags
;
651 flags
.mCapture
= (curr
->GetPhase() == NS_PHASE_CAPTURING
);
653 // If this is a command, remove it from the system event group,
654 // otherwise remove it from the standard event group.
656 if ((curr
->GetType() & (NS_HANDLER_TYPE_XBL_COMMAND
|
657 NS_HANDLER_TYPE_SYSTEM
)) &&
658 (isChromeBinding
|| mBoundElement
->IsInNativeAnonymousSubtree())) {
659 flags
.mInSystemGroup
= true;
662 manager
->RemoveEventListenerByType(handler
,
663 nsDependentAtomString(eventAtom
),
667 const nsCOMArray
<nsXBLKeyEventHandler
>* keyHandlers
=
668 mPrototypeBinding
->GetKeyEventHandlers();
670 for (i
= 0; i
< keyHandlers
->Count(); ++i
) {
671 nsXBLKeyEventHandler
* handler
= keyHandlers
->ObjectAt(i
);
674 handler
->GetEventName(type
);
676 // Figure out if we're using capturing or not.
677 EventListenerFlags flags
;
678 flags
.mCapture
= (handler
->GetPhase() == NS_PHASE_CAPTURING
);
680 // If this is a command, remove it from the system event group, otherwise
681 // remove it from the standard event group.
683 if ((handler
->GetType() & (NS_HANDLER_TYPE_XBL_COMMAND
| NS_HANDLER_TYPE_SYSTEM
)) &&
684 (isChromeBinding
|| mBoundElement
->IsInNativeAnonymousSubtree())) {
685 flags
.mInSystemGroup
= true;
688 manager
->RemoveEventListenerByType(handler
, type
, flags
);
694 UpdateInsertionParent(XBLChildrenElement
* aPoint
,
695 nsIContent
* aOldBoundElement
)
697 if (aPoint
->IsDefaultInsertion()) {
701 for (size_t i
= 0; i
< aPoint
->InsertedChildrenLength(); ++i
) {
702 nsIContent
* child
= aPoint
->InsertedChild(i
);
704 MOZ_ASSERT(child
->GetParentNode());
706 // Here, we're iterating children that we inserted. There are two cases:
707 // either |child| is an explicit child of |aOldBoundElement| and is no
708 // longer inserted anywhere or it's a child of a <children> element
709 // parented to |aOldBoundElement|. In the former case, the child is no
710 // longer inserted anywhere, so we set its insertion parent to null. In the
711 // latter case, the child is now inserted into |aOldBoundElement| from some
712 // binding above us, so we set its insertion parent to aOldBoundElement.
713 if (child
->GetParentNode() == aOldBoundElement
) {
714 child
->SetXBLInsertionParent(nullptr);
716 child
->SetXBLInsertionParent(aOldBoundElement
);
722 nsXBLBinding::ChangeDocument(nsIDocument
* aOldDocument
, nsIDocument
* aNewDocument
)
724 if (aOldDocument
== aNewDocument
)
727 // Now the binding dies. Unhook our prototypes.
728 if (mPrototypeBinding
->HasImplementation()) {
730 // Init might fail here if we've cycle-collected the global object, since
731 // the Unlink phase of cycle collection happens after JS GC finalization.
732 // But in that case, we don't care about fixing the prototype chain, since
733 // everything's going away immediately.
734 if (jsapi
.Init(aOldDocument
->GetScopeObject())) {
735 JSContext
* cx
= jsapi
.cx();
737 JS::Rooted
<JSObject
*> scriptObject(cx
, mBoundElement
->GetWrapper());
739 // XXX Stay in sync! What if a layered binding has an
741 // XXXbz what does that comment mean, really? It seems to date
742 // back to when there was such a thing as an <interface>, whever
745 // Find the right prototype.
746 JSAutoCompartment
ac(cx
, scriptObject
);
748 JS::Rooted
<JSObject
*> base(cx
, scriptObject
);
749 JS::Rooted
<JSObject
*> proto(cx
);
750 for ( ; true; base
= proto
) { // Will break out on null proto
751 if (!JS_GetPrototype(cx
, base
, &proto
)) {
758 if (JS_GetClass(proto
) != &gPrototypeJSClass
) {
759 // Clearly not the right class
763 nsRefPtr
<nsXBLDocumentInfo
> docInfo
=
764 static_cast<nsXBLDocumentInfo
*>(::JS_GetPrivate(proto
));
766 // Not the proto we seek
770 JS::Value protoBinding
= ::JS_GetReservedSlot(proto
, 0);
772 if (protoBinding
.toPrivate() != mPrototypeBinding
) {
773 // Not the right binding
777 // Alright! This is the right prototype. Pull it out of the
779 JS::Rooted
<JSObject
*> grandProto(cx
);
780 if (!JS_GetPrototype(cx
, proto
, &grandProto
)) {
783 ::JS_SetPrototype(cx
, base
, grandProto
);
787 mPrototypeBinding
->UndefineFields(cx
, scriptObject
);
789 // Don't remove the reference from the document to the
790 // wrapper here since it'll be removed by the element
791 // itself when that's taken out of the document.
796 // Remove our event handlers
797 UnhookEventHandlers();
800 nsAutoScriptBlocker scriptBlocker
;
802 // Then do our ancestors. This reverses the construction order, so that at
803 // all times things are consistent as far as everyone is concerned.
805 mNextBinding
->ChangeDocument(aOldDocument
, aNewDocument
);
808 // Update the anonymous content.
809 // XXXbz why not only for style bindings?
810 if (mContent
&& !mIsShadowRootBinding
) {
811 nsXBLBinding::UninstallAnonymousContent(aOldDocument
, mContent
);
814 // Now that we've unbound our anonymous content from the tree and updated
815 // its binding parent, update the insertion parent for content inserted
816 // into our <children> elements.
817 if (mDefaultInsertionPoint
) {
818 UpdateInsertionParent(mDefaultInsertionPoint
, mBoundElement
);
821 for (size_t i
= 0; i
< mInsertionPoints
.Length(); ++i
) {
822 UpdateInsertionParent(mInsertionPoints
[i
], mBoundElement
);
825 // Now that our inserted children no longer think they're inserted
826 // anywhere, make sure our internal state reflects that as well.
827 ClearInsertionPoints();
832 nsXBLBinding::InheritsStyle() const
834 // XXX Will have to change if we ever allow multiple bindings to contribute anonymous content.
835 // Most derived binding with anonymous content determines style inheritance for now.
837 // XXX What about bindings with <content> but no kids, e.g., my treecell-text binding?
839 return mPrototypeBinding
->InheritsStyle();
842 return mNextBinding
->InheritsStyle();
848 nsXBLBinding::WalkRules(nsIStyleRuleProcessor::EnumFunc aFunc
, void* aData
)
851 mNextBinding
->WalkRules(aFunc
, aData
);
853 nsIStyleRuleProcessor
*rules
= mPrototypeBinding
->GetRuleProcessor();
855 (*aFunc
)(rules
, aData
);
858 // Internal helper methods ////////////////////////////////////////////////////////////////
860 // Get or create a WeakMap object on a given XBL-hosting global.
862 // The scheme is as follows. XBL-hosting globals (either privileged content
863 // Windows or XBL scopes) get two lazily-defined WeakMap properties. Each
864 // WeakMap is keyed by the grand-proto - i.e. the original prototype of the
865 // content before it was bound, and the prototype of the class object that we
866 // splice in. The values in the WeakMap are simple dictionary-style objects,
867 // mapping from XBL class names to class objects.
869 GetOrCreateClassObjectMap(JSContext
*cx
, JS::Handle
<JSObject
*> scope
, const char *mapName
)
871 AssertSameCompartment(cx
, scope
);
872 MOZ_ASSERT(JS_IsGlobalObject(scope
));
873 MOZ_ASSERT(scope
== xpc::GetXBLScopeOrGlobal(cx
, scope
));
875 // First, see if the map is already defined.
876 JS::Rooted
<JSPropertyDescriptor
> desc(cx
);
877 if (!JS_GetOwnPropertyDescriptor(cx
, scope
, mapName
, &desc
)) {
880 if (desc
.object() && desc
.value().isObject() &&
881 JS::IsWeakMapObject(&desc
.value().toObject())) {
882 return &desc
.value().toObject();
885 // It's not there. Create and define it.
886 JS::Rooted
<JSObject
*> map(cx
, JS::NewWeakMapObject(cx
));
887 if (!map
|| !JS_DefineProperty(cx
, scope
, mapName
, map
,
888 JSPROP_PERMANENT
| JSPROP_READONLY
,
889 JS_STUBGETTER
, JS_STUBSETTER
))
897 GetOrCreateMapEntryForPrototype(JSContext
*cx
, JS::Handle
<JSObject
*> proto
)
899 AssertSameCompartment(cx
, proto
);
900 // We want to hang our class objects off the XBL scope. But since we also
901 // hoist anonymous content into the XBL scope, this creates the potential for
902 // tricky collisions, since we can simultaneously have a bound in-content
903 // node with grand-proto HTMLDivElement and a bound anonymous node whose
904 // grand-proto is the XBL scope's cross-compartment wrapper to HTMLDivElement.
905 // Since we have to wrap the WeakMap keys into its scope, this distinction
906 // would be lost if we don't do something about it.
908 // So we define two maps - one class objects that live in content (prototyped
909 // to content prototypes), and the other for class objects that live in the
910 // XBL scope (prototyped to cross-compartment-wrapped content prototypes).
911 const char* name
= xpc::IsInContentXBLScope(proto
) ? "__ContentClassObjectMap__"
912 : "__XBLClassObjectMap__";
914 // Now, enter the XBL scope, since that's where we need to operate, and wrap
915 // the proto accordingly. We hang the map off of the content XBL scope for
916 // content, and the Window for chrome (whether add-ons are involved or not).
917 JS::Rooted
<JSObject
*> scope(cx
, xpc::GetXBLScopeOrGlobal(cx
, proto
));
918 NS_ENSURE_TRUE(scope
, nullptr);
919 JS::Rooted
<JSObject
*> wrappedProto(cx
, proto
);
920 JSAutoCompartment
ac(cx
, scope
);
921 if (!JS_WrapObject(cx
, &wrappedProto
)) {
925 // Grab the appropriate WeakMap.
926 JS::Rooted
<JSObject
*> map(cx
, GetOrCreateClassObjectMap(cx
, scope
, name
));
931 // See if we already have a map entry for that prototype.
932 JS::Rooted
<JS::Value
> val(cx
);
933 if (!JS::GetWeakMapEntry(cx
, map
, wrappedProto
, &val
)) {
936 if (val
.isObject()) {
937 return &val
.toObject();
940 // We don't have an entry. Create one and stick it in the map.
941 JS::Rooted
<JSObject
*> entry(cx
);
942 entry
= JS_NewObjectWithGivenProto(cx
, nullptr, JS::NullPtr(), scope
);
946 JS::Rooted
<JS::Value
> entryVal(cx
, JS::ObjectValue(*entry
));
947 if (!JS::SetWeakMapEntry(cx
, map
, wrappedProto
, entryVal
)) {
948 NS_WARNING("SetWeakMapEntry failed, probably due to non-preservable WeakMap "
949 "key. XBL binding will fail for this element.");
957 nsXBLBinding::DoInitJSClass(JSContext
*cx
,
958 JS::Handle
<JSObject
*> obj
,
959 const nsAFlatCString
& aClassName
,
960 nsXBLPrototypeBinding
* aProtoBinding
,
961 JS::MutableHandle
<JSObject
*> aClassObject
,
966 // Note that, now that NAC reflectors are created in the XBL scope, the
967 // reflector is not necessarily same-compartment with the document. So we'll
968 // end up creating a separate instance of the oddly-named XBL class object
969 // and defining it as a property on the XBL scope's global. This works fine,
970 // but we need to make sure never to assume that the the reflector and
971 // prototype are same-compartment with the bound document.
972 JS::Rooted
<JSObject
*> global(cx
, js::GetGlobalForObjectCrossCompartment(obj
));
974 // We never store class objects in add-on scopes.
975 JS::Rooted
<JSObject
*> xblScope(cx
, xpc::GetXBLScopeOrGlobal(cx
, global
));
976 NS_ENSURE_TRUE(xblScope
, NS_ERROR_UNEXPECTED
);
978 JS::Rooted
<JSObject
*> parent_proto(cx
);
979 if (!JS_GetPrototype(cx
, obj
, &parent_proto
)) {
980 return NS_ERROR_FAILURE
;
983 // Get the map entry for the parent prototype. In the one-off case that the
984 // parent prototype is null, we somewhat hackily just use the WeakMap itself
985 // as a property holder.
986 JS::Rooted
<JSObject
*> holder(cx
);
988 holder
= GetOrCreateMapEntryForPrototype(cx
, parent_proto
);
990 JSAutoCompartment
innerAC(cx
, xblScope
);
991 holder
= GetOrCreateClassObjectMap(cx
, xblScope
, "__ContentClassObjectMap__");
993 if (NS_WARN_IF(!holder
)) {
994 return NS_ERROR_FAILURE
;
996 js::AssertSameCompartment(holder
, xblScope
);
997 JSAutoCompartment
ac(cx
, holder
);
999 // Look up the class on the property holder. The only properties on the
1000 // holder should be class objects. If we don't find the class object, we need
1001 // to create and define it.
1002 JS::Rooted
<JSObject
*> proto(cx
);
1003 JS::Rooted
<JSPropertyDescriptor
> desc(cx
);
1004 if (!JS_GetOwnPropertyDescriptor(cx
, holder
, aClassName
.get(), &desc
)) {
1005 return NS_ERROR_OUT_OF_MEMORY
;
1007 *aNew
= !desc
.object();
1008 if (desc
.object()) {
1009 proto
= &desc
.value().toObject();
1010 MOZ_ASSERT(JS_GetClass(js::UncheckedUnwrap(proto
)) == &gPrototypeJSClass
);
1013 // We need to create the prototype. First, enter the compartment where it's
1014 // going to live, and create it.
1015 JSAutoCompartment
ac2(cx
, global
);
1016 proto
= JS_NewObjectWithGivenProto(cx
, &gPrototypeJSClass
, parent_proto
, global
);
1018 return NS_ERROR_OUT_OF_MEMORY
;
1021 // Keep this proto binding alive while we're alive. Do this first so that
1022 // we can guarantee that in XBLFinalize this will be non-null.
1023 // Note that we can't just store aProtoBinding in the private and
1024 // addref/release the nsXBLDocumentInfo through it, because cycle
1025 // collection doesn't seem to work right if the private is not an
1027 nsXBLDocumentInfo
* docInfo
= aProtoBinding
->XBLDocumentInfo();
1028 ::JS_SetPrivate(proto
, docInfo
);
1030 JS_SetReservedSlot(proto
, 0, PRIVATE_TO_JSVAL(aProtoBinding
));
1032 // Next, enter the compartment of the property holder, wrap the proto, and
1034 JSAutoCompartment
ac3(cx
, holder
);
1035 if (!JS_WrapObject(cx
, &proto
) ||
1036 !JS_DefineProperty(cx
, holder
, aClassName
.get(), proto
,
1037 JSPROP_READONLY
| JSPROP_PERMANENT
,
1038 JS_STUBGETTER
, JS_STUBSETTER
))
1040 return NS_ERROR_OUT_OF_MEMORY
;
1044 // Whew. We have the proto. Wrap it back into the compartment of |obj|,
1045 // splice it in, and return it.
1046 JSAutoCompartment
ac4(cx
, obj
);
1047 if (!JS_WrapObject(cx
, &proto
) || !JS_SetPrototype(cx
, obj
, proto
)) {
1048 return NS_ERROR_FAILURE
;
1050 aClassObject
.set(proto
);
1055 nsXBLBinding::AllowScripts()
1057 return mBoundElement
&& mPrototypeBinding
->GetAllowScripts();
1061 nsXBLBinding::RootBinding()
1064 return mNextBinding
->RootBinding();
1070 nsXBLBinding::ResolveAllFields(JSContext
*cx
, JS::Handle
<JSObject
*> obj
) const
1072 if (!mPrototypeBinding
->ResolveAllFields(cx
, obj
)) {
1077 return mNextBinding
->ResolveAllFields(cx
, obj
);
1084 nsXBLBinding::LookupMember(JSContext
* aCx
, JS::Handle
<jsid
> aId
,
1085 JS::MutableHandle
<JSPropertyDescriptor
> aDesc
)
1087 // We should never enter this function with a pre-filled property descriptor.
1088 MOZ_ASSERT(!aDesc
.object());
1090 // Get the string as an nsString before doing anything, so we can make
1091 // convenient comparisons during our search.
1092 if (!JSID_IS_STRING(aId
)) {
1095 nsAutoJSString name
;
1096 if (!name
.init(aCx
, JSID_TO_STRING(aId
))) {
1100 // We have a weak reference to our bound element, so make sure it's alive.
1101 if (!mBoundElement
|| !mBoundElement
->GetWrapper()) {
1105 // Get the scope of mBoundElement and the associated XBL scope. We should only
1106 // be calling into this machinery if we're running in a separate XBL scope.
1108 // Note that we only end up in LookupMember for XrayWrappers from XBL scopes
1109 // into content. So for NAC reflectors that live in the XBL scope, we should
1110 // never get here. But on the off-chance that someone adds new callsites to
1111 // LookupMember, we do a release-mode assertion as belt-and-braces.
1112 // We do a release-mode assertion here to be extra safe.
1114 // This code is only called for content XBL, so we don't have to worry about
1115 // add-on scopes here.
1116 JS::Rooted
<JSObject
*> boundScope(aCx
,
1117 js::GetGlobalForObjectCrossCompartment(mBoundElement
->GetWrapper()));
1118 MOZ_RELEASE_ASSERT(!xpc::IsInAddonScope(boundScope
));
1119 MOZ_RELEASE_ASSERT(!xpc::IsInContentXBLScope(boundScope
));
1120 JS::Rooted
<JSObject
*> xblScope(aCx
, xpc::GetXBLScope(aCx
, boundScope
));
1121 NS_ENSURE_TRUE(xblScope
, false);
1122 MOZ_ASSERT(boundScope
!= xblScope
);
1124 // Enter the xbl scope and invoke the internal version.
1126 JSAutoCompartment
ac(aCx
, xblScope
);
1127 JS::Rooted
<jsid
> id(aCx
, aId
);
1128 if (!LookupMemberInternal(aCx
, name
, id
, aDesc
, xblScope
)) {
1133 // Wrap into the caller's scope.
1134 return JS_WrapPropertyDescriptor(aCx
, aDesc
);
1138 nsXBLBinding::LookupMemberInternal(JSContext
* aCx
, nsString
& aName
,
1139 JS::Handle
<jsid
> aNameAsId
,
1140 JS::MutableHandle
<JSPropertyDescriptor
> aDesc
,
1141 JS::Handle
<JSObject
*> aXBLScope
)
1143 // First, see if we have an implementation. If we don't, it means that this
1144 // binding doesn't have a class object, and thus doesn't have any members.
1146 if (!PrototypeBinding()->HasImplementation()) {
1147 if (!mNextBinding
) {
1150 return mNextBinding
->LookupMemberInternal(aCx
, aName
, aNameAsId
,
1154 // Find our class object. It's in a protected scope and permanent just in case,
1155 // so should be there no matter what.
1156 JS::Rooted
<JS::Value
> classObject(aCx
);
1157 if (!JS_GetProperty(aCx
, aXBLScope
, PrototypeBinding()->ClassName().get(),
1162 // The bound element may have been adoped by a document and have a different
1163 // wrapper (and different xbl scope) than when the binding was applied, in
1164 // this case getting the class object will fail. Behave as if the class
1165 // object did not exist.
1166 if (classObject
.isUndefined()) {
1170 MOZ_ASSERT(classObject
.isObject());
1172 // Look for the property on this binding. If it's not there, try the next
1173 // binding on the chain.
1174 nsXBLProtoImpl
* impl
= mPrototypeBinding
->GetImplementation();
1175 JS::Rooted
<JSObject
*> object(aCx
, &classObject
.toObject());
1176 if (impl
&& !impl
->LookupMember(aCx
, aName
, aNameAsId
, aDesc
, object
)) {
1179 if (aDesc
.object() || !mNextBinding
) {
1183 return mNextBinding
->LookupMemberInternal(aCx
, aName
, aNameAsId
, aDesc
,
1188 nsXBLBinding::HasField(nsString
& aName
)
1190 // See if this binding has such a field.
1191 return mPrototypeBinding
->FindField(aName
) ||
1192 (mNextBinding
&& mNextBinding
->HasField(aName
));
1196 nsXBLBinding::MarkForDeath()
1198 mMarkedForDeath
= true;
1199 ExecuteDetachedHandler();
1203 nsXBLBinding::ImplementsInterface(REFNSIID aIID
) const
1205 return mPrototypeBinding
->ImplementsInterface(aIID
) ||
1206 (mNextBinding
&& mNextBinding
->ImplementsInterface(aIID
));