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
|
94 // Our one reserved slot holds the relevant nsXBLPrototypeBinding
95 JSCLASS_HAS_RESERVED_SLOTS(1),
96 JS_PropertyStub
, JS_DeletePropertyStub
,
97 JS_PropertyStub
, JS_StrictPropertyStub
,
98 XBLEnumerate
, JS_ResolveStub
,
99 JS_ConvertStub
, XBLFinalize
,
100 nullptr, nullptr, nullptr, nullptr
103 // Implementation /////////////////////////////////////////////////////////////////
105 // Constructors/Destructors
106 nsXBLBinding::nsXBLBinding(nsXBLPrototypeBinding
* aBinding
)
107 : mMarkedForDeath(false)
108 , mUsingContentXBLScope(false)
109 , mPrototypeBinding(aBinding
)
111 NS_ASSERTION(mPrototypeBinding
, "Must have a prototype binding!");
112 // Grab a ref to the document info so the prototype binding won't die
113 NS_ADDREF(mPrototypeBinding
->XBLDocumentInfo());
116 // Constructor used by web components.
117 nsXBLBinding::nsXBLBinding(ShadowRoot
* aShadowRoot
, nsXBLPrototypeBinding
* aBinding
)
118 : mMarkedForDeath(false),
119 mUsingContentXBLScope(false),
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)
131 nsXBLBinding::UninstallAnonymousContent(mContent
->OwnerDoc(), mContent
);
133 nsXBLDocumentInfo
* info
= mPrototypeBinding
->XBLDocumentInfo();
137 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXBLBinding
)
139 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXBLBinding
)
140 // XXX Probably can't unlink mPrototypeBinding->XBLDocumentInfo(), because
141 // mPrototypeBinding is weak.
143 nsXBLBinding::UninstallAnonymousContent(tmp
->mContent
->OwnerDoc(),
146 NS_IMPL_CYCLE_COLLECTION_UNLINK(mContent
)
147 NS_IMPL_CYCLE_COLLECTION_UNLINK(mNextBinding
)
148 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDefaultInsertionPoint
)
149 NS_IMPL_CYCLE_COLLECTION_UNLINK(mInsertionPoints
)
150 NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnonymousContentList
)
151 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
152 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXBLBinding
)
153 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
,
154 "mPrototypeBinding->XBLDocumentInfo()");
155 cb
.NoteXPCOMChild(tmp
->mPrototypeBinding
->XBLDocumentInfo());
156 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContent
)
157 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNextBinding
)
158 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDefaultInsertionPoint
)
159 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInsertionPoints
)
160 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnonymousContentList
)
161 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
162 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsXBLBinding
, AddRef
)
163 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsXBLBinding
, Release
)
166 nsXBLBinding::SetBaseBinding(nsXBLBinding
* aBinding
)
169 NS_ERROR("Base XBL binding is already defined!");
173 mNextBinding
= aBinding
; // Comptr handles rel/add
177 nsXBLBinding::GetBindingWithContent()
183 return mNextBinding
? mNextBinding
->GetBindingWithContent() : nullptr;
187 nsXBLBinding::InstallAnonymousContent(nsIContent
* aAnonParent
, nsIContent
* aElement
,
188 bool aChromeOnlyContent
)
190 // We need to ensure two things.
191 // (1) The anonymous content should be fooled into thinking it's in the bound
192 // element's document, assuming that the bound element is in a document
193 // Note that we don't change the current doc of aAnonParent here, since that
194 // quite simply does not matter. aAnonParent is just a way of keeping refs
195 // to all its kids, which are anonymous content from the point of view of
197 // (2) The children's parent back pointer should not be to this synthetic root
198 // but should instead point to the enclosing parent element.
199 nsIDocument
* doc
= aElement
->GetCurrentDoc();
200 bool allowScripts
= AllowScripts();
202 nsAutoScriptBlocker scriptBlocker
;
203 for (nsIContent
* child
= aAnonParent
->GetFirstChild();
205 child
= child
->GetNextSibling()) {
206 child
->UnbindFromTree();
207 if (aChromeOnlyContent
) {
208 child
->SetFlags(NODE_CHROME_ONLY_ACCESS
|
209 NODE_IS_ROOT_OF_CHROME_ONLY_ACCESS
);
212 child
->BindToTree(doc
, aElement
, mBoundElement
, allowScripts
);
214 // Oh, well... Just give up.
215 // XXXbz This really shouldn't be a void method!
216 child
->UnbindFromTree();
220 child
->SetFlags(NODE_IS_ANONYMOUS_ROOT
);
223 // To make XUL templates work (and other goodies that happen when
224 // an element is added to a XUL document), we need to notify the
225 // XUL document using its special API.
226 nsCOMPtr
<nsIXULDocument
> xuldoc(do_QueryInterface(doc
));
228 xuldoc
->AddSubtreeToDocument(child
);
234 nsXBLBinding::UninstallAnonymousContent(nsIDocument
* aDocument
,
235 nsIContent
* aAnonParent
)
237 if (aAnonParent
->HasFlag(NODE_IS_IN_SHADOW_TREE
)) {
238 // It is unnecessary to uninstall anonymous content in a shadow tree
239 // because the ShadowRoot itself is a DocumentFragment and does not
240 // need any additional cleanup.
244 nsAutoScriptBlocker scriptBlocker
;
245 // Hold a strong ref while doing this, just in case.
246 nsCOMPtr
<nsIContent
> anonParent
= aAnonParent
;
248 nsCOMPtr
<nsIXULDocument
> xuldoc
=
249 do_QueryInterface(aDocument
);
251 for (nsIContent
* child
= aAnonParent
->GetFirstChild();
253 child
= child
->GetNextSibling()) {
254 child
->UnbindFromTree();
257 xuldoc
->RemoveSubtreeFromDocument(child
);
264 nsXBLBinding::SetBoundElement(nsIContent
* aElement
)
266 mBoundElement
= aElement
;
268 mNextBinding
->SetBoundElement(aElement
);
270 if (!mBoundElement
) {
274 // Compute whether we're using an XBL scope.
276 // We disable XBL scopes for remote XUL, where we care about compat more
277 // than security. So we need to know whether we're using an XBL scope so that
278 // we can decide what to do about untrusted events when "allowuntrusted"
279 // is not given in the handler declaration.
280 nsCOMPtr
<nsIGlobalObject
> go
= mBoundElement
->OwnerDoc()->GetScopeObject();
281 NS_ENSURE_TRUE_VOID(go
&& go
->GetGlobalJSObject());
282 mUsingContentXBLScope
= xpc::UseContentXBLScope(js::GetObjectCompartment(go
->GetGlobalJSObject()));
286 nsXBLBinding::HasStyleSheets() const
288 // Find out if we need to re-resolve style. We'll need to do this
289 // if we have additional stylesheets in our binding document.
290 if (mPrototypeBinding
->HasStyleSheets())
293 return mNextBinding
? mNextBinding
->HasStyleSheets() : false;
297 nsXBLBinding::GenerateAnonymousContent()
299 NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
300 "Someone forgot a script blocker");
302 // Fetch the content element for this binding.
303 nsIContent
* content
=
304 mPrototypeBinding
->GetImmediateChild(nsGkAtoms::content
);
307 // We have no anonymous content.
309 mNextBinding
->GenerateAnonymousContent();
314 // Find out if we're really building kids or if we're just
315 // using the attribute-setting shorthand hack.
316 uint32_t contentCount
= content
->GetChildCount();
318 // Plan to build the content by default.
319 bool hasContent
= (contentCount
> 0);
321 nsIDocument
* doc
= mBoundElement
->OwnerDoc();
323 nsCOMPtr
<nsINode
> clonedNode
;
324 nsCOMArray
<nsINode
> nodesWithProperties
;
325 nsNodeUtils::Clone(content
, true, doc
->NodeInfoManager(),
326 nodesWithProperties
, getter_AddRefs(clonedNode
));
327 mContent
= clonedNode
->AsElement();
329 // Search for <xbl:children> elements in the XBL content. In the presence
330 // of multiple default insertion points, we use the last one in document
332 for (nsIContent
* child
= mContent
; child
; child
= child
->GetNextNode(mContent
)) {
333 if (child
->NodeInfo()->Equals(nsGkAtoms::children
, kNameSpaceID_XBL
)) {
334 XBLChildrenElement
* point
= static_cast<XBLChildrenElement
*>(child
);
335 if (point
->IsDefaultInsertion()) {
336 mDefaultInsertionPoint
= point
;
338 mInsertionPoints
.AppendElement(point
);
343 // Do this after looking for <children> as this messes up the parent
344 // pointer which would make the GetNextNode call above fail
345 InstallAnonymousContent(mContent
, mBoundElement
,
346 mPrototypeBinding
->ChromeOnlyContent());
348 // Insert explicit children into insertion points
349 if (mDefaultInsertionPoint
&& mInsertionPoints
.IsEmpty()) {
350 ExplicitChildIterator
iter(mBoundElement
);
351 for (nsIContent
* child
= iter
.GetNextChild(); child
; child
= iter
.GetNextChild()) {
352 mDefaultInsertionPoint
->AppendInsertedChild(child
);
355 // It is odd to come into this code if mInsertionPoints is not empty, but
356 // we need to make sure to do the compatibility hack below if the bound
357 // node has any non <xul:template> or <xul:observes> children.
358 ExplicitChildIterator
iter(mBoundElement
);
359 for (nsIContent
* child
= iter
.GetNextChild(); child
; child
= iter
.GetNextChild()) {
360 XBLChildrenElement
* point
= FindInsertionPointForInternal(child
);
362 point
->AppendInsertedChild(child
);
364 NodeInfo
*ni
= child
->NodeInfo();
365 if (ni
->NamespaceID() != kNameSpaceID_XUL
||
366 (!ni
->Equals(nsGkAtoms::_template
) &&
367 !ni
->Equals(nsGkAtoms::observes
))) {
368 // Compatibility hack. For some reason the original XBL
369 // implementation dropped the content of a binding if any child of
370 // the bound element didn't match any of the <children> in the
371 // binding. This became a pseudo-API that we have to maintain.
373 // Undo InstallAnonymousContent
374 UninstallAnonymousContent(doc
, mContent
);
376 // Clear out our children elements to avoid dangling references.
377 ClearInsertionPoints();
379 // Pretend as though there was no content in the binding.
387 // Set binding parent on default content if need
388 if (mDefaultInsertionPoint
) {
389 mDefaultInsertionPoint
->MaybeSetupDefaultContent();
391 for (uint32_t i
= 0; i
< mInsertionPoints
.Length(); ++i
) {
392 mInsertionPoints
[i
]->MaybeSetupDefaultContent();
395 mPrototypeBinding
->SetInitialAttributes(mBoundElement
, mContent
);
398 // Always check the content element for potential attributes.
399 // This shorthand hack always happens, even when we didn't
400 // build anonymous content.
401 const nsAttrName
* attrName
;
402 for (uint32_t i
= 0; (attrName
= content
->GetAttrNameAt(i
)); ++i
) {
403 int32_t namespaceID
= attrName
->NamespaceID();
404 // Hold a strong reference here so that the atom doesn't go away during
406 nsCOMPtr
<nsIAtom
> name
= attrName
->LocalName();
408 if (name
!= nsGkAtoms::includes
) {
409 if (!nsContentUtils::HasNonEmptyAttr(mBoundElement
, namespaceID
, name
)) {
411 content
->GetAttr(namespaceID
, name
, value2
);
412 mBoundElement
->SetAttr(namespaceID
, name
, attrName
->GetPrefix(),
417 // Conserve space by wiping the attributes off the clone.
419 mContent
->UnsetAttr(namespaceID
, name
, false);
424 nsXBLBinding::FindInsertionPointFor(nsIContent
* aChild
)
426 // XXX We should get rid of this function as it causes us to traverse the
427 // binding chain multiple times
429 return FindInsertionPointForInternal(aChild
);
432 return mNextBinding
? mNextBinding
->FindInsertionPointFor(aChild
)
437 nsXBLBinding::FindInsertionPointForInternal(nsIContent
* aChild
)
439 for (uint32_t i
= 0; i
< mInsertionPoints
.Length(); ++i
) {
440 XBLChildrenElement
* point
= mInsertionPoints
[i
];
441 if (point
->Includes(aChild
)) {
446 return mDefaultInsertionPoint
;
450 nsXBLBinding::ClearInsertionPoints()
452 if (mDefaultInsertionPoint
) {
453 mDefaultInsertionPoint
->ClearInsertedChildren();
456 for (uint32_t i
= 0; i
< mInsertionPoints
.Length(); ++i
) {
457 mInsertionPoints
[i
]->ClearInsertedChildren();
461 nsAnonymousContentList
*
462 nsXBLBinding::GetAnonymousNodeList()
465 return mNextBinding
? mNextBinding
->GetAnonymousNodeList() : nullptr;
468 if (!mAnonymousContentList
) {
469 mAnonymousContentList
= new nsAnonymousContentList(mContent
);
472 return mAnonymousContentList
;
476 nsXBLBinding::InstallEventHandlers()
478 // Don't install handlers if scripts aren't allowed.
479 if (AllowScripts()) {
480 // Fetch the handlers prototypes for this binding.
481 nsXBLPrototypeHandler
* handlerChain
= mPrototypeBinding
->GetPrototypeHandlers();
484 EventListenerManager
* manager
= mBoundElement
->GetOrCreateListenerManager();
489 nsContentUtils::IsChromeDoc(mBoundElement
->OwnerDoc());
490 bool isChromeBinding
= mPrototypeBinding
->IsChrome();
491 nsXBLPrototypeHandler
* curr
;
492 for (curr
= handlerChain
; curr
; curr
= curr
->GetNextHandler()) {
493 // Fetch the event type.
494 nsCOMPtr
<nsIAtom
> eventAtom
= curr
->GetEventName();
496 eventAtom
== nsGkAtoms::keyup
||
497 eventAtom
== nsGkAtoms::keydown
||
498 eventAtom
== nsGkAtoms::keypress
)
501 nsXBLEventHandler
* handler
= curr
->GetEventHandler();
503 // Figure out if we're using capturing or not.
504 EventListenerFlags flags
;
505 flags
.mCapture
= (curr
->GetPhase() == NS_PHASE_CAPTURING
);
507 // If this is a command, add it in the system event group
508 if ((curr
->GetType() & (NS_HANDLER_TYPE_XBL_COMMAND
|
509 NS_HANDLER_TYPE_SYSTEM
)) &&
510 (isChromeBinding
|| mBoundElement
->IsInNativeAnonymousSubtree())) {
511 flags
.mInSystemGroup
= true;
514 bool hasAllowUntrustedAttr
= curr
->HasAllowUntrustedAttr();
515 if ((hasAllowUntrustedAttr
&& curr
->AllowUntrustedEvents()) ||
516 (!hasAllowUntrustedAttr
&& !isChromeDoc
&& !mUsingContentXBLScope
)) {
517 flags
.mAllowUntrustedEvents
= true;
520 manager
->AddEventListenerByType(handler
,
521 nsDependentAtomString(eventAtom
),
526 const nsCOMArray
<nsXBLKeyEventHandler
>* keyHandlers
=
527 mPrototypeBinding
->GetKeyEventHandlers();
529 for (i
= 0; i
< keyHandlers
->Count(); ++i
) {
530 nsXBLKeyEventHandler
* handler
= keyHandlers
->ObjectAt(i
);
531 handler
->SetIsBoundToChrome(isChromeDoc
);
532 handler
->SetUsingContentXBLScope(mUsingContentXBLScope
);
535 handler
->GetEventName(type
);
537 // If this is a command, add it in the system event group, otherwise
538 // add it to the standard event group.
540 // Figure out if we're using capturing or not.
541 EventListenerFlags flags
;
542 flags
.mCapture
= (handler
->GetPhase() == NS_PHASE_CAPTURING
);
544 if ((handler
->GetType() & (NS_HANDLER_TYPE_XBL_COMMAND
|
545 NS_HANDLER_TYPE_SYSTEM
)) &&
546 (isChromeBinding
|| mBoundElement
->IsInNativeAnonymousSubtree())) {
547 flags
.mInSystemGroup
= true;
550 // For key handlers we have to set mAllowUntrustedEvents flag.
551 // Whether the handling of the event is allowed or not is handled in
552 // nsXBLKeyEventHandler::HandleEvent
553 flags
.mAllowUntrustedEvents
= true;
555 manager
->AddEventListenerByType(handler
, type
, flags
);
561 mNextBinding
->InstallEventHandlers();
565 nsXBLBinding::InstallImplementation()
567 // Always install the base class properties first, so that
568 // derived classes can reference the base class properties.
571 nsresult rv
= mNextBinding
->InstallImplementation();
572 NS_ENSURE_SUCCESS(rv
, rv
);
575 // iterate through each property in the prototype's list and install the property.
577 return mPrototypeBinding
->InstallImplementation(this);
583 nsXBLBinding::GetBaseTag(int32_t* aNameSpaceID
)
585 nsIAtom
*tag
= mPrototypeBinding
->GetBaseTag(aNameSpaceID
);
586 if (!tag
&& mNextBinding
)
587 return mNextBinding
->GetBaseTag(aNameSpaceID
);
593 nsXBLBinding::AttributeChanged(nsIAtom
* aAttribute
, int32_t aNameSpaceID
,
594 bool aRemoveFlag
, bool aNotify
)
596 // XXX Change if we ever allow multiple bindings in a chain to contribute anonymous content
599 mNextBinding
->AttributeChanged(aAttribute
, aNameSpaceID
,
600 aRemoveFlag
, aNotify
);
602 mPrototypeBinding
->AttributeChanged(aAttribute
, aNameSpaceID
, aRemoveFlag
,
603 mBoundElement
, mContent
, aNotify
);
608 nsXBLBinding::ExecuteAttachedHandler()
611 mNextBinding
->ExecuteAttachedHandler();
614 mPrototypeBinding
->BindingAttached(mBoundElement
);
618 nsXBLBinding::ExecuteDetachedHandler()
621 mPrototypeBinding
->BindingDetached(mBoundElement
);
624 mNextBinding
->ExecuteDetachedHandler();
628 nsXBLBinding::UnhookEventHandlers()
630 nsXBLPrototypeHandler
* handlerChain
= mPrototypeBinding
->GetPrototypeHandlers();
633 EventListenerManager
* manager
= mBoundElement
->GetExistingListenerManager();
638 bool isChromeBinding
= mPrototypeBinding
->IsChrome();
639 nsXBLPrototypeHandler
* curr
;
640 for (curr
= handlerChain
; curr
; curr
= curr
->GetNextHandler()) {
641 nsXBLEventHandler
* handler
= curr
->GetCachedEventHandler();
646 nsCOMPtr
<nsIAtom
> eventAtom
= curr
->GetEventName();
648 eventAtom
== nsGkAtoms::keyup
||
649 eventAtom
== nsGkAtoms::keydown
||
650 eventAtom
== nsGkAtoms::keypress
)
653 // Figure out if we're using capturing or not.
654 EventListenerFlags flags
;
655 flags
.mCapture
= (curr
->GetPhase() == NS_PHASE_CAPTURING
);
657 // If this is a command, remove it from the system event group,
658 // otherwise remove it from the standard event group.
660 if ((curr
->GetType() & (NS_HANDLER_TYPE_XBL_COMMAND
|
661 NS_HANDLER_TYPE_SYSTEM
)) &&
662 (isChromeBinding
|| mBoundElement
->IsInNativeAnonymousSubtree())) {
663 flags
.mInSystemGroup
= true;
666 manager
->RemoveEventListenerByType(handler
,
667 nsDependentAtomString(eventAtom
),
671 const nsCOMArray
<nsXBLKeyEventHandler
>* keyHandlers
=
672 mPrototypeBinding
->GetKeyEventHandlers();
674 for (i
= 0; i
< keyHandlers
->Count(); ++i
) {
675 nsXBLKeyEventHandler
* handler
= keyHandlers
->ObjectAt(i
);
678 handler
->GetEventName(type
);
680 // Figure out if we're using capturing or not.
681 EventListenerFlags flags
;
682 flags
.mCapture
= (handler
->GetPhase() == NS_PHASE_CAPTURING
);
684 // If this is a command, remove it from the system event group, otherwise
685 // remove it from the standard event group.
687 if ((handler
->GetType() & (NS_HANDLER_TYPE_XBL_COMMAND
| NS_HANDLER_TYPE_SYSTEM
)) &&
688 (isChromeBinding
|| mBoundElement
->IsInNativeAnonymousSubtree())) {
689 flags
.mInSystemGroup
= true;
692 manager
->RemoveEventListenerByType(handler
, type
, flags
);
698 UpdateInsertionParent(XBLChildrenElement
* aPoint
,
699 nsIContent
* aOldBoundElement
)
701 if (aPoint
->IsDefaultInsertion()) {
705 for (size_t i
= 0; i
< aPoint
->InsertedChildrenLength(); ++i
) {
706 nsIContent
* child
= aPoint
->InsertedChild(i
);
708 MOZ_ASSERT(child
->GetParentNode());
710 // Here, we're iterating children that we inserted. There are two cases:
711 // either |child| is an explicit child of |aOldBoundElement| and is no
712 // longer inserted anywhere or it's a child of a <children> element
713 // parented to |aOldBoundElement|. In the former case, the child is no
714 // longer inserted anywhere, so we set its insertion parent to null. In the
715 // latter case, the child is now inserted into |aOldBoundElement| from some
716 // binding above us, so we set its insertion parent to aOldBoundElement.
717 if (child
->GetParentNode() == aOldBoundElement
) {
718 child
->SetXBLInsertionParent(nullptr);
720 child
->SetXBLInsertionParent(aOldBoundElement
);
726 nsXBLBinding::ChangeDocument(nsIDocument
* aOldDocument
, nsIDocument
* aNewDocument
)
728 if (aOldDocument
== aNewDocument
)
731 // Now the binding dies. Unhook our prototypes.
732 if (mPrototypeBinding
->HasImplementation()) {
734 // Init might fail here if we've cycle-collected the global object, since
735 // the Unlink phase of cycle collection happens after JS GC finalization.
736 // But in that case, we don't care about fixing the prototype chain, since
737 // everything's going away immediately.
738 if (jsapi
.Init(aOldDocument
->GetScopeObject())) {
739 JSContext
* cx
= jsapi
.cx();
741 JS::Rooted
<JSObject
*> scriptObject(cx
, mBoundElement
->GetWrapper());
743 // XXX Stay in sync! What if a layered binding has an
745 // XXXbz what does that comment mean, really? It seems to date
746 // back to when there was such a thing as an <interface>, whever
749 // Find the right prototype.
750 JSAutoCompartment
ac(cx
, scriptObject
);
752 JS::Rooted
<JSObject
*> base(cx
, scriptObject
);
753 JS::Rooted
<JSObject
*> proto(cx
);
754 for ( ; true; base
= proto
) { // Will break out on null proto
755 if (!JS_GetPrototype(cx
, base
, &proto
)) {
762 if (JS_GetClass(proto
) != &gPrototypeJSClass
) {
763 // Clearly not the right class
767 nsRefPtr
<nsXBLDocumentInfo
> docInfo
=
768 static_cast<nsXBLDocumentInfo
*>(::JS_GetPrivate(proto
));
770 // Not the proto we seek
774 JS::Value protoBinding
= ::JS_GetReservedSlot(proto
, 0);
776 if (protoBinding
.toPrivate() != mPrototypeBinding
) {
777 // Not the right binding
781 // Alright! This is the right prototype. Pull it out of the
783 JS::Rooted
<JSObject
*> grandProto(cx
);
784 if (!JS_GetPrototype(cx
, proto
, &grandProto
)) {
787 ::JS_SetPrototype(cx
, base
, grandProto
);
791 mPrototypeBinding
->UndefineFields(cx
, scriptObject
);
793 // Don't remove the reference from the document to the
794 // wrapper here since it'll be removed by the element
795 // itself when that's taken out of the document.
800 // Remove our event handlers
801 UnhookEventHandlers();
804 nsAutoScriptBlocker scriptBlocker
;
806 // Then do our ancestors. This reverses the construction order, so that at
807 // all times things are consistent as far as everyone is concerned.
809 mNextBinding
->ChangeDocument(aOldDocument
, aNewDocument
);
812 // Update the anonymous content.
813 // XXXbz why not only for style bindings?
815 nsXBLBinding::UninstallAnonymousContent(aOldDocument
, mContent
);
818 // Now that we've unbound our anonymous content from the tree and updated
819 // its binding parent, update the insertion parent for content inserted
820 // into our <children> elements.
821 if (mDefaultInsertionPoint
) {
822 UpdateInsertionParent(mDefaultInsertionPoint
, mBoundElement
);
825 for (size_t i
= 0; i
< mInsertionPoints
.Length(); ++i
) {
826 UpdateInsertionParent(mInsertionPoints
[i
], mBoundElement
);
829 // Now that our inserted children no longer think they're inserted
830 // anywhere, make sure our internal state reflects that as well.
831 ClearInsertionPoints();
836 nsXBLBinding::InheritsStyle() const
838 // XXX Will have to change if we ever allow multiple bindings to contribute anonymous content.
839 // Most derived binding with anonymous content determines style inheritance for now.
841 // XXX What about bindings with <content> but no kids, e.g., my treecell-text binding?
843 return mPrototypeBinding
->InheritsStyle();
846 return mNextBinding
->InheritsStyle();
852 nsXBLBinding::WalkRules(nsIStyleRuleProcessor::EnumFunc aFunc
, void* aData
)
855 mNextBinding
->WalkRules(aFunc
, aData
);
857 nsIStyleRuleProcessor
*rules
= mPrototypeBinding
->GetRuleProcessor();
859 (*aFunc
)(rules
, aData
);
862 // Internal helper methods ////////////////////////////////////////////////////////////////
864 // Get or create a WeakMap object on a given XBL-hosting global.
866 // The scheme is as follows. XBL-hosting globals (either privileged content
867 // Windows or XBL scopes) get two lazily-defined WeakMap properties. Each
868 // WeakMap is keyed by the grand-proto - i.e. the original prototype of the
869 // content before it was bound, and the prototype of the class object that we
870 // splice in. The values in the WeakMap are simple dictionary-style objects,
871 // mapping from XBL class names to class objects.
873 GetOrCreateClassObjectMap(JSContext
*cx
, JS::Handle
<JSObject
*> scope
, const char *mapName
)
875 AssertSameCompartment(cx
, scope
);
876 MOZ_ASSERT(JS_IsGlobalObject(scope
));
877 MOZ_ASSERT(scope
== xpc::GetXBLScopeOrGlobal(cx
, scope
));
879 // First, see if the map is already defined.
880 JS::Rooted
<JSPropertyDescriptor
> desc(cx
);
881 if (!JS_GetOwnPropertyDescriptor(cx
, scope
, mapName
, &desc
)) {
884 if (desc
.object() && desc
.value().isObject() &&
885 JS::IsWeakMapObject(&desc
.value().toObject())) {
886 return &desc
.value().toObject();
889 // It's not there. Create and define it.
890 JS::Rooted
<JSObject
*> map(cx
, JS::NewWeakMapObject(cx
));
891 if (!map
|| !JS_DefineProperty(cx
, scope
, mapName
, map
,
892 JSPROP_PERMANENT
| JSPROP_READONLY
,
893 JS_PropertyStub
, JS_StrictPropertyStub
))
901 GetOrCreateMapEntryForPrototype(JSContext
*cx
, JS::Handle
<JSObject
*> proto
)
903 AssertSameCompartment(cx
, proto
);
904 // We want to hang our class objects off the XBL scope. But since we also
905 // hoist anonymous content into the XBL scope, this creates the potential for
906 // tricky collisions, since we can simultaneously have a bound in-content
907 // node with grand-proto HTMLDivElement and a bound anonymous node whose
908 // grand-proto is the XBL scope's cross-compartment wrapper to HTMLDivElement.
909 // Since we have to wrap the WeakMap keys into its scope, this distinction
910 // would be lost if we don't do something about it.
912 // So we define two maps - one class objects that live in content (prototyped
913 // to content prototypes), and the other for class objects that live in the
914 // XBL scope (prototyped to cross-compartment-wrapped content prototypes).
915 const char* name
= xpc::IsInContentXBLScope(proto
) ? "__ContentClassObjectMap__"
916 : "__XBLClassObjectMap__";
918 // Now, enter the XBL scope, since that's where we need to operate, and wrap
919 // the proto accordingly. We hang the map off of the content XBL scope for
920 // content, and the Window for chrome (whether add-ons are involved or not).
921 JS::Rooted
<JSObject
*> scope(cx
, xpc::GetXBLScopeOrGlobal(cx
, proto
));
922 NS_ENSURE_TRUE(scope
, nullptr);
923 JS::Rooted
<JSObject
*> wrappedProto(cx
, proto
);
924 JSAutoCompartment
ac(cx
, scope
);
925 if (!JS_WrapObject(cx
, &wrappedProto
)) {
929 // Grab the appropriate WeakMap.
930 JS::Rooted
<JSObject
*> map(cx
, GetOrCreateClassObjectMap(cx
, scope
, name
));
935 // See if we already have a map entry for that prototype.
936 JS::Rooted
<JS::Value
> val(cx
);
937 if (!JS::GetWeakMapEntry(cx
, map
, wrappedProto
, &val
)) {
940 if (val
.isObject()) {
941 return &val
.toObject();
944 // We don't have an entry. Create one and stick it in the map.
945 JS::Rooted
<JSObject
*> entry(cx
);
946 entry
= JS_NewObjectWithGivenProto(cx
, nullptr, JS::NullPtr(), scope
);
950 JS::Rooted
<JS::Value
> entryVal(cx
, JS::ObjectValue(*entry
));
951 if (!JS::SetWeakMapEntry(cx
, map
, wrappedProto
, entryVal
)) {
952 NS_WARNING("SetWeakMapEntry failed, probably due to non-preservable WeakMap "
953 "key. XBL binding will fail for this element.");
961 nsXBLBinding::DoInitJSClass(JSContext
*cx
,
962 JS::Handle
<JSObject
*> obj
,
963 const nsAFlatCString
& aClassName
,
964 nsXBLPrototypeBinding
* aProtoBinding
,
965 JS::MutableHandle
<JSObject
*> aClassObject
,
970 // Note that, now that NAC reflectors are created in the XBL scope, the
971 // reflector is not necessarily same-compartment with the document. So we'll
972 // end up creating a separate instance of the oddly-named XBL class object
973 // and defining it as a property on the XBL scope's global. This works fine,
974 // but we need to make sure never to assume that the the reflector and
975 // prototype are same-compartment with the bound document.
976 JS::Rooted
<JSObject
*> global(cx
, js::GetGlobalForObjectCrossCompartment(obj
));
978 // We never store class objects in add-on scopes.
979 JS::Rooted
<JSObject
*> xblScope(cx
, xpc::GetXBLScopeOrGlobal(cx
, global
));
980 NS_ENSURE_TRUE(xblScope
, NS_ERROR_UNEXPECTED
);
982 JS::Rooted
<JSObject
*> parent_proto(cx
);
983 if (!JS_GetPrototype(cx
, obj
, &parent_proto
)) {
984 return NS_ERROR_FAILURE
;
987 // Get the map entry for the parent prototype. In the one-off case that the
988 // parent prototype is null, we somewhat hackily just use the WeakMap itself
989 // as a property holder.
990 JS::Rooted
<JSObject
*> holder(cx
);
992 holder
= GetOrCreateMapEntryForPrototype(cx
, parent_proto
);
994 JSAutoCompartment
innerAC(cx
, xblScope
);
995 holder
= GetOrCreateClassObjectMap(cx
, xblScope
, "__ContentClassObjectMap__");
997 if (NS_WARN_IF(!holder
)) {
998 return NS_ERROR_FAILURE
;
1000 js::AssertSameCompartment(holder
, xblScope
);
1001 JSAutoCompartment
ac(cx
, holder
);
1003 // Look up the class on the property holder. The only properties on the
1004 // holder should be class objects. If we don't find the class object, we need
1005 // to create and define it.
1006 JS::Rooted
<JSObject
*> proto(cx
);
1007 JS::Rooted
<JSPropertyDescriptor
> desc(cx
);
1008 if (!JS_GetOwnPropertyDescriptor(cx
, holder
, aClassName
.get(), &desc
)) {
1009 return NS_ERROR_OUT_OF_MEMORY
;
1011 *aNew
= !desc
.object();
1012 if (desc
.object()) {
1013 proto
= &desc
.value().toObject();
1014 MOZ_ASSERT(JS_GetClass(js::UncheckedUnwrap(proto
)) == &gPrototypeJSClass
);
1017 // We need to create the prototype. First, enter the compartment where it's
1018 // going to live, and create it.
1019 JSAutoCompartment
ac2(cx
, global
);
1020 proto
= JS_NewObjectWithGivenProto(cx
, &gPrototypeJSClass
, parent_proto
, global
);
1022 return NS_ERROR_OUT_OF_MEMORY
;
1025 // Keep this proto binding alive while we're alive. Do this first so that
1026 // we can guarantee that in XBLFinalize this will be non-null.
1027 // Note that we can't just store aProtoBinding in the private and
1028 // addref/release the nsXBLDocumentInfo through it, because cycle
1029 // collection doesn't seem to work right if the private is not an
1031 nsXBLDocumentInfo
* docInfo
= aProtoBinding
->XBLDocumentInfo();
1032 ::JS_SetPrivate(proto
, docInfo
);
1034 JS_SetReservedSlot(proto
, 0, PRIVATE_TO_JSVAL(aProtoBinding
));
1036 // Next, enter the compartment of the property holder, wrap the proto, and
1038 JSAutoCompartment
ac3(cx
, holder
);
1039 if (!JS_WrapObject(cx
, &proto
) ||
1040 !JS_DefineProperty(cx
, holder
, aClassName
.get(), proto
,
1041 JSPROP_READONLY
| JSPROP_PERMANENT
,
1042 JS_PropertyStub
, JS_StrictPropertyStub
))
1044 return NS_ERROR_OUT_OF_MEMORY
;
1048 // Whew. We have the proto. Wrap it back into the compartment of |obj|,
1049 // splice it in, and return it.
1050 JSAutoCompartment
ac4(cx
, obj
);
1051 if (!JS_WrapObject(cx
, &proto
) || !JS_SetPrototype(cx
, obj
, proto
)) {
1052 return NS_ERROR_FAILURE
;
1054 aClassObject
.set(proto
);
1059 nsXBLBinding::AllowScripts()
1061 return mBoundElement
&& mPrototypeBinding
->GetAllowScripts();
1065 nsXBLBinding::RootBinding()
1068 return mNextBinding
->RootBinding();
1074 nsXBLBinding::ResolveAllFields(JSContext
*cx
, JS::Handle
<JSObject
*> obj
) const
1076 if (!mPrototypeBinding
->ResolveAllFields(cx
, obj
)) {
1081 return mNextBinding
->ResolveAllFields(cx
, obj
);
1088 nsXBLBinding::LookupMember(JSContext
* aCx
, JS::Handle
<jsid
> aId
,
1089 JS::MutableHandle
<JSPropertyDescriptor
> aDesc
)
1091 // We should never enter this function with a pre-filled property descriptor.
1092 MOZ_ASSERT(!aDesc
.object());
1094 // Get the string as an nsString before doing anything, so we can make
1095 // convenient comparisons during our search.
1096 if (!JSID_IS_STRING(aId
)) {
1099 nsAutoJSString name
;
1100 if (!name
.init(aCx
, JSID_TO_STRING(aId
))) {
1104 // We have a weak reference to our bound element, so make sure it's alive.
1105 if (!mBoundElement
|| !mBoundElement
->GetWrapper()) {
1109 // Get the scope of mBoundElement and the associated XBL scope. We should only
1110 // be calling into this machinery if we're running in a separate XBL scope.
1112 // Note that we only end up in LookupMember for XrayWrappers from XBL scopes
1113 // into content. So for NAC reflectors that live in the XBL scope, we should
1114 // never get here. But on the off-chance that someone adds new callsites to
1115 // LookupMember, we do a release-mode assertion as belt-and-braces.
1116 // We do a release-mode assertion here to be extra safe.
1118 // This code is only called for content XBL, so we don't have to worry about
1119 // add-on scopes here.
1120 JS::Rooted
<JSObject
*> boundScope(aCx
,
1121 js::GetGlobalForObjectCrossCompartment(mBoundElement
->GetWrapper()));
1122 MOZ_RELEASE_ASSERT(!xpc::IsInAddonScope(boundScope
));
1123 MOZ_RELEASE_ASSERT(!xpc::IsInContentXBLScope(boundScope
));
1124 JS::Rooted
<JSObject
*> xblScope(aCx
, xpc::GetXBLScope(aCx
, boundScope
));
1125 NS_ENSURE_TRUE(xblScope
, false);
1126 MOZ_ASSERT(boundScope
!= xblScope
);
1128 // Enter the xbl scope and invoke the internal version.
1130 JSAutoCompartment
ac(aCx
, xblScope
);
1131 JS::Rooted
<jsid
> id(aCx
, aId
);
1132 if (!LookupMemberInternal(aCx
, name
, id
, aDesc
, xblScope
)) {
1137 // Wrap into the caller's scope.
1138 return JS_WrapPropertyDescriptor(aCx
, aDesc
);
1142 nsXBLBinding::LookupMemberInternal(JSContext
* aCx
, nsString
& aName
,
1143 JS::Handle
<jsid
> aNameAsId
,
1144 JS::MutableHandle
<JSPropertyDescriptor
> aDesc
,
1145 JS::Handle
<JSObject
*> aXBLScope
)
1147 // First, see if we have an implementation. If we don't, it means that this
1148 // binding doesn't have a class object, and thus doesn't have any members.
1150 if (!PrototypeBinding()->HasImplementation()) {
1151 if (!mNextBinding
) {
1154 return mNextBinding
->LookupMemberInternal(aCx
, aName
, aNameAsId
,
1158 // Find our class object. It's in a protected scope and permanent just in case,
1159 // so should be there no matter what.
1160 JS::Rooted
<JS::Value
> classObject(aCx
);
1161 if (!JS_GetProperty(aCx
, aXBLScope
, PrototypeBinding()->ClassName().get(),
1166 // The bound element may have been adoped by a document and have a different
1167 // wrapper (and different xbl scope) than when the binding was applied, in
1168 // this case getting the class object will fail. Behave as if the class
1169 // object did not exist.
1170 if (classObject
.isUndefined()) {
1174 MOZ_ASSERT(classObject
.isObject());
1176 // Look for the property on this binding. If it's not there, try the next
1177 // binding on the chain.
1178 nsXBLProtoImpl
* impl
= mPrototypeBinding
->GetImplementation();
1179 JS::Rooted
<JSObject
*> object(aCx
, &classObject
.toObject());
1180 if (impl
&& !impl
->LookupMember(aCx
, aName
, aNameAsId
, aDesc
, object
)) {
1183 if (aDesc
.object() || !mNextBinding
) {
1187 return mNextBinding
->LookupMemberInternal(aCx
, aName
, aNameAsId
, aDesc
,
1192 nsXBLBinding::HasField(nsString
& aName
)
1194 // See if this binding has such a field.
1195 return mPrototypeBinding
->FindField(aName
) ||
1196 (mNextBinding
&& mNextBinding
->HasField(aName
));
1200 nsXBLBinding::MarkForDeath()
1202 mMarkedForDeath
= true;
1203 ExecuteDetachedHandler();
1207 nsXBLBinding::ImplementsInterface(REFNSIID aIID
) const
1209 return mPrototypeBinding
->ImplementsInterface(aIID
) ||
1210 (mNextBinding
&& mNextBinding
->ImplementsInterface(aIID
));