1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "mozilla/ArrayUtils.h"
10 #include "nsXBLService.h"
11 #include "nsXBLWindowKeyHandler.h"
12 #include "nsIInputStream.h"
13 #include "nsNameSpaceManager.h"
15 #include "nsIDOMElement.h"
17 #include "nsIChannel.h"
18 #include "nsXPIDLString.h"
20 #include "nsIContent.h"
21 #include "nsIDocument.h"
22 #include "nsIXMLContentSink.h"
23 #include "nsContentCID.h"
24 #include "mozilla/dom/XMLDocument.h"
25 #include "nsGkAtoms.h"
26 #include "nsIMemory.h"
27 #include "nsIObserverService.h"
28 #include "nsIDOMNodeList.h"
29 #include "nsXBLContentSink.h"
30 #include "nsXBLBinding.h"
31 #include "nsXBLPrototypeBinding.h"
32 #include "nsXBLDocumentInfo.h"
34 #include "nsContentUtils.h"
35 #include "nsSyncLoadService.h"
36 #include "nsContentPolicyUtils.h"
40 #include "nsIPresShell.h"
41 #include "nsIDocumentObserver.h"
42 #include "nsFrameManager.h"
43 #include "nsStyleContext.h"
44 #include "nsIScriptSecurityManager.h"
45 #include "nsIScriptError.h"
46 #include "nsXBLSerialize.h"
49 #include "nsXULPrototypeCache.h"
51 #include "nsIDOMEventListener.h"
52 #include "mozilla/Attributes.h"
53 #include "mozilla/EventListenerManager.h"
54 #include "mozilla/Preferences.h"
55 #include "mozilla/dom/Event.h"
56 #include "mozilla/dom/Element.h"
58 using namespace mozilla
;
59 using namespace mozilla::dom
;
61 #define NS_MAX_XBL_BINDING_RECURSION 20
63 nsXBLService
* nsXBLService::gInstance
= nullptr;
66 IsAncestorBinding(nsIDocument
* aDocument
,
67 nsIURI
* aChildBindingURI
,
70 NS_ASSERTION(aDocument
, "expected a document");
71 NS_ASSERTION(aChildBindingURI
, "expected a binding URI");
72 NS_ASSERTION(aChild
, "expected a child content");
74 uint32_t bindingRecursion
= 0;
75 for (nsIContent
*bindingParent
= aChild
->GetBindingParent();
77 bindingParent
= bindingParent
->GetBindingParent()) {
78 nsXBLBinding
* binding
= bindingParent
->GetXBLBinding();
83 if (binding
->PrototypeBinding()->CompareBindingURI(aChildBindingURI
)) {
85 if (bindingRecursion
< NS_MAX_XBL_BINDING_RECURSION
) {
89 aChildBindingURI
->GetSpec(spec
);
90 NS_ConvertUTF8toUTF16
bindingURI(spec
);
91 const char16_t
* params
[] = { bindingURI
.get() };
92 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag
,
93 NS_LITERAL_CSTRING("XBL"), aDocument
,
94 nsContentUtils::eXBL_PROPERTIES
,
95 "TooDeepBindingRecursion",
96 params
, ArrayLength(params
));
104 // Individual binding requests.
105 class nsXBLBindingRequest
108 nsCOMPtr
<nsIURI
> mBindingURI
;
109 nsCOMPtr
<nsIContent
> mBoundElement
;
111 void DocumentLoaded(nsIDocument
* aBindingDoc
)
113 // We only need the document here to cause frame construction, so
114 // we need the current doc, not the owner doc.
115 nsIDocument
* doc
= mBoundElement
->GetCurrentDoc();
121 nsXBLService::GetInstance()->BindingReady(mBoundElement
, mBindingURI
, &ready
);
125 // If |mBoundElement| is (in addition to having binding |mBinding|)
126 // also a descendant of another element with binding |mBinding|,
127 // then we might have just constructed it due to the
128 // notification of its parent. (We can know about both if the
129 // binding loads were triggered from the DOM rather than frame
130 // construction.) So we have to check both whether the element
131 // has a primary frame and whether it's in the undisplayed map
132 // before sending a ContentInserted notification, or bad things
134 nsIPresShell
*shell
= doc
->GetShell();
136 nsIFrame
* childFrame
= mBoundElement
->GetPrimaryFrame();
138 // Check to see if it's in the undisplayed content map.
140 shell
->FrameManager()->GetUndisplayedContent(mBoundElement
);
143 shell
->RecreateFramesFor(mBoundElement
);
149 nsXBLBindingRequest(nsIURI
* aURI
, nsIContent
* aBoundElement
)
151 mBoundElement(aBoundElement
)
156 // nsXBLStreamListener, a helper class used for
157 // asynchronous parsing of URLs
159 class nsXBLStreamListener MOZ_FINAL
: public nsIStreamListener
,
160 public nsIDOMEventListener
164 NS_DECL_NSISTREAMLISTENER
165 NS_DECL_NSIREQUESTOBSERVER
166 NS_DECL_NSIDOMEVENTLISTENER
168 nsXBLStreamListener(nsIDocument
* aBoundDocument
,
169 nsIXMLContentSink
* aSink
,
170 nsIDocument
* aBindingDocument
);
172 void AddRequest(nsXBLBindingRequest
* aRequest
) { mBindingRequests
.AppendElement(aRequest
); }
173 bool HasRequest(nsIURI
* aURI
, nsIContent
* aBoundElement
);
176 ~nsXBLStreamListener();
178 nsCOMPtr
<nsIStreamListener
> mInner
;
179 nsAutoTArray
<nsXBLBindingRequest
*, 8> mBindingRequests
;
181 nsCOMPtr
<nsIWeakReference
> mBoundDocument
;
182 nsCOMPtr
<nsIXMLContentSink
> mSink
; // Only set until OnStartRequest
183 nsCOMPtr
<nsIDocument
> mBindingDocument
; // Only set until OnStartRequest
186 /* Implementation file */
187 NS_IMPL_ISUPPORTS(nsXBLStreamListener
,
192 nsXBLStreamListener::nsXBLStreamListener(nsIDocument
* aBoundDocument
,
193 nsIXMLContentSink
* aSink
,
194 nsIDocument
* aBindingDocument
)
195 : mSink(aSink
), mBindingDocument(aBindingDocument
)
197 /* member initializers and constructor code */
198 mBoundDocument
= do_GetWeakReference(aBoundDocument
);
201 nsXBLStreamListener::~nsXBLStreamListener()
203 for (uint32_t i
= 0; i
< mBindingRequests
.Length(); i
++) {
204 nsXBLBindingRequest
* req
= mBindingRequests
.ElementAt(i
);
210 nsXBLStreamListener::OnDataAvailable(nsIRequest
*request
, nsISupports
* aCtxt
,
211 nsIInputStream
* aInStr
,
212 uint64_t aSourceOffset
, uint32_t aCount
)
215 return mInner
->OnDataAvailable(request
, aCtxt
, aInStr
, aSourceOffset
, aCount
);
216 return NS_ERROR_FAILURE
;
220 nsXBLStreamListener::OnStartRequest(nsIRequest
* request
, nsISupports
* aCtxt
)
222 // Make sure we don't hold on to the sink and binding document past this point
223 nsCOMPtr
<nsIXMLContentSink
> sink
;
225 nsCOMPtr
<nsIDocument
> doc
;
226 mBindingDocument
.swap(doc
);
228 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(request
);
229 NS_ENSURE_TRUE(channel
, NS_ERROR_UNEXPECTED
);
231 nsCOMPtr
<nsILoadGroup
> group
;
232 request
->GetLoadGroup(getter_AddRefs(group
));
234 nsresult rv
= doc
->StartDocumentLoad("loadAsInteractiveData",
238 getter_AddRefs(mInner
),
241 NS_ENSURE_SUCCESS(rv
, rv
);
243 // Make sure to add ourselves as a listener after StartDocumentLoad,
244 // since that resets the event listners on the document.
245 doc
->AddEventListener(NS_LITERAL_STRING("load"), this, false);
247 return mInner
->OnStartRequest(request
, aCtxt
);
251 nsXBLStreamListener::OnStopRequest(nsIRequest
* request
, nsISupports
* aCtxt
, nsresult aStatus
)
255 rv
= mInner
->OnStopRequest(request
, aCtxt
, aStatus
);
258 // Don't hold onto the inner listener; holding onto it can create a cycle
266 nsXBLStreamListener::HasRequest(nsIURI
* aURI
, nsIContent
* aElt
)
268 // XXX Could be more efficient.
269 uint32_t count
= mBindingRequests
.Length();
270 for (uint32_t i
= 0; i
< count
; i
++) {
271 nsXBLBindingRequest
* req
= mBindingRequests
.ElementAt(i
);
273 if (req
->mBoundElement
== aElt
&&
274 NS_SUCCEEDED(req
->mBindingURI
->Equals(aURI
, &eq
)) && eq
)
282 nsXBLStreamListener::HandleEvent(nsIDOMEvent
* aEvent
)
286 uint32_t count
= mBindingRequests
.Length();
288 // Get the binding document; note that we don't hold onto it in this object
289 // to avoid creating a cycle
290 Event
* event
= aEvent
->InternalDOMEvent();
291 EventTarget
* target
= event
->GetCurrentTarget();
292 nsCOMPtr
<nsIDocument
> bindingDocument
= do_QueryInterface(target
);
293 NS_ASSERTION(bindingDocument
, "Event not targeted at document?!");
295 // See if we're still alive.
296 nsCOMPtr
<nsIDocument
> doc(do_QueryReferent(mBoundDocument
));
298 NS_WARNING("XBL load did not complete until after document went away! Modal dialog bug?\n");
301 // We have to do a flush prior to notification of the document load.
302 // This has to happen since the HTML content sink can be holding on
303 // to notifications related to our children (e.g., if you bind to the
304 // <body> tag) that result in duplication of content.
305 // We need to get the sink's notifications flushed and then make the binding
308 nsXBLBindingRequest
* req
= mBindingRequests
.ElementAt(0);
309 nsIDocument
* document
= req
->mBoundElement
->GetCurrentDoc();
311 document
->FlushPendingNotifications(Flush_ContentAndNotify
);
314 // Remove ourselves from the set of pending docs.
315 nsBindingManager
*bindingManager
= doc
->BindingManager();
316 nsIURI
* documentURI
= bindingDocument
->GetDocumentURI();
317 bindingManager
->RemoveLoadingDocListener(documentURI
);
319 if (!bindingDocument
->GetRootElement()) {
320 // FIXME: How about an error console warning?
321 NS_WARNING("XBL doc with no root element - this usually shouldn't happen");
322 return NS_ERROR_FAILURE
;
325 // Put our doc info in the doc table.
326 nsBindingManager
*xblDocBindingManager
= bindingDocument
->BindingManager();
327 nsRefPtr
<nsXBLDocumentInfo
> info
=
328 xblDocBindingManager
->GetXBLDocumentInfo(documentURI
);
329 xblDocBindingManager
->RemoveXBLDocumentInfo(info
); // Break the self-imposed cycle.
331 if (nsXBLService::IsChromeOrResourceURI(documentURI
)) {
332 NS_WARNING("An XBL file is malformed. Did you forget the XBL namespace on the bindings tag?");
334 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag
,
335 NS_LITERAL_CSTRING("XBL"), nullptr,
336 nsContentUtils::eXBL_PROPERTIES
,
338 nullptr, 0, documentURI
);
339 return NS_ERROR_FAILURE
;
342 // If the doc is a chrome URI, then we put it into the XUL cache.
344 if (nsXBLService::IsChromeOrResourceURI(documentURI
)) {
345 nsXULPrototypeCache
* cache
= nsXULPrototypeCache::GetInstance();
346 if (cache
&& cache
->IsEnabled())
347 cache
->PutXBLDocumentInfo(info
);
351 bindingManager
->PutXBLDocumentInfo(info
);
353 // Notify all pending requests that their bindings are
354 // ready and can be installed.
355 for (i
= 0; i
< count
; i
++) {
356 nsXBLBindingRequest
* req
= mBindingRequests
.ElementAt(i
);
357 req
->DocumentLoaded(bindingDocument
);
361 target
->RemoveEventListener(NS_LITERAL_STRING("load"), this, false);
366 // Implementation /////////////////////////////////////////////////////////////////
368 // Static member variable initialization
369 bool nsXBLService::gAllowDataURIs
= false;
371 // Implement our nsISupports methods
372 NS_IMPL_ISUPPORTS(nsXBLService
, nsISupportsWeakReference
)
377 gInstance
= new nsXBLService();
378 NS_ADDREF(gInstance
);
381 // Constructors/Destructors
382 nsXBLService::nsXBLService(void)
384 Preferences::AddBoolVarCache(&gAllowDataURIs
, "layout.debug.enable_data_xbl");
387 nsXBLService::~nsXBLService(void)
393 nsXBLService::IsChromeOrResourceURI(nsIURI
* aURI
)
395 bool isChrome
= false;
396 bool isResource
= false;
397 if (NS_SUCCEEDED(aURI
->SchemeIs("chrome", &isChrome
)) &&
398 NS_SUCCEEDED(aURI
->SchemeIs("resource", &isResource
)))
399 return (isChrome
|| isResource
);
404 // This function loads a particular XBL file and installs all of the bindings
407 nsXBLService::LoadBindings(nsIContent
* aContent
, nsIURI
* aURL
,
408 nsIPrincipal
* aOriginPrincipal
,
409 nsXBLBinding
** aBinding
, bool* aResolveStyle
)
411 NS_PRECONDITION(aOriginPrincipal
, "Must have an origin principal");
414 *aResolveStyle
= false;
418 nsCOMPtr
<nsIDocument
> document
= aContent
->OwnerDoc();
420 nsAutoCString urlspec
;
421 if (nsContentUtils::GetWrapperSafeScriptFilename(document
, aURL
, urlspec
)) {
422 // Block an attempt to load a binding that has special wrapper
428 nsXBLBinding
*binding
= aContent
->GetXBLBinding();
430 if (binding
->MarkedForDeath()) {
431 FlushStyleBindings(aContent
);
435 // See if the URIs match.
436 if (binding
->PrototypeBinding()->CompareBindingURI(aURL
))
438 FlushStyleBindings(aContent
);
444 nsRefPtr
<nsXBLBinding
> newBinding
;
445 if (NS_FAILED(rv
= GetBinding(aContent
, aURL
, false, aOriginPrincipal
,
446 &ready
, getter_AddRefs(newBinding
)))) {
454 nsAutoCString
str(NS_LITERAL_CSTRING("Failed to locate XBL binding. XBL is now using id instead of name to reference bindings. Make sure you have switched over. The invalid binding name is: ") + spec
);
460 if (::IsAncestorBinding(document
, aURL
, aContent
)) {
461 return NS_ERROR_ILLEGAL_VALUE
;
464 // We loaded a style binding. It goes on the end.
466 // Get the last binding that is in the append layer.
467 binding
->RootBinding()->SetBaseBinding(newBinding
);
470 // Install the binding on the content node.
471 aContent
->SetXBLBinding(newBinding
);
475 nsAutoScriptBlocker scriptBlocker
;
477 // Set the binding's bound element.
478 newBinding
->SetBoundElement(aContent
);
480 // Tell the binding to build the anonymous content.
481 newBinding
->GenerateAnonymousContent();
483 // Tell the binding to install event handlers
484 newBinding
->InstallEventHandlers();
486 // Set up our properties
487 rv
= newBinding
->InstallImplementation();
488 NS_ENSURE_SUCCESS(rv
, rv
);
490 // Figure out if we have any scoped sheets. If so, we do a second resolve.
491 *aResolveStyle
= newBinding
->HasStyleSheets();
493 newBinding
.swap(*aBinding
);
500 nsXBLService::FlushStyleBindings(nsIContent
* aContent
)
502 nsCOMPtr
<nsIDocument
> document
= aContent
->OwnerDoc();
504 nsXBLBinding
*binding
= aContent
->GetXBLBinding();
506 // Clear out the script references.
507 binding
->ChangeDocument(document
, nullptr);
509 aContent
->SetXBLBinding(nullptr); // Flush old style bindings
516 // AttachGlobalKeyHandler
518 // Creates a new key handler and prepares to listen to key events on the given
519 // event receiver (either a document or an content node). If the receiver is content,
520 // then extra work needs to be done to hook it up to the document (XXX WHY??)
523 nsXBLService::AttachGlobalKeyHandler(EventTarget
* aTarget
)
525 // check if the receiver is a content node (not a document), and hook
526 // it to the document if that is the case.
527 nsCOMPtr
<EventTarget
> piTarget
= aTarget
;
528 nsCOMPtr
<nsIContent
> contentNode(do_QueryInterface(aTarget
));
530 // Only attach if we're really in a document
531 nsCOMPtr
<nsIDocument
> doc
= contentNode
->GetCurrentDoc();
533 piTarget
= doc
; // We're a XUL keyset. Attach to our document.
536 EventListenerManager
* manager
= piTarget
->GetOrCreateListenerManager();
538 if (!piTarget
|| !manager
)
539 return NS_ERROR_FAILURE
;
541 // the listener already exists, so skip this
542 if (contentNode
&& contentNode
->GetProperty(nsGkAtoms::listener
))
545 nsCOMPtr
<nsIDOMElement
> elt(do_QueryInterface(contentNode
));
547 // Create the key handler
548 nsRefPtr
<nsXBLWindowKeyHandler
> handler
=
549 NS_NewXBLWindowKeyHandler(elt
, piTarget
);
551 // listen to these events
552 manager
->AddEventListenerByType(handler
, NS_LITERAL_STRING("keydown"),
553 TrustedEventsAtSystemGroupBubble());
554 manager
->AddEventListenerByType(handler
, NS_LITERAL_STRING("keyup"),
555 TrustedEventsAtSystemGroupBubble());
556 manager
->AddEventListenerByType(handler
, NS_LITERAL_STRING("keypress"),
557 TrustedEventsAtSystemGroupBubble());
559 // The capturing listener is only used for XUL keysets to properly handle
560 // shortcut keys in a multi-process environment.
561 manager
->AddEventListenerByType(handler
, NS_LITERAL_STRING("keydown"),
562 TrustedEventsAtSystemGroupCapture());
563 manager
->AddEventListenerByType(handler
, NS_LITERAL_STRING("keyup"),
564 TrustedEventsAtSystemGroupCapture());
565 manager
->AddEventListenerByType(handler
, NS_LITERAL_STRING("keypress"),
566 TrustedEventsAtSystemGroupCapture());
569 return contentNode
->SetProperty(nsGkAtoms::listener
,
570 handler
.forget().take(),
571 nsPropertyTable::SupportsDtorFunc
, true);
573 // The reference to the handler will be maintained by the event target,
574 // and, if there is a content node, the property.
579 // DetachGlobalKeyHandler
581 // Removes a key handler added by DeatchGlobalKeyHandler.
584 nsXBLService::DetachGlobalKeyHandler(EventTarget
* aTarget
)
586 nsCOMPtr
<EventTarget
> piTarget
= aTarget
;
587 nsCOMPtr
<nsIContent
> contentNode(do_QueryInterface(aTarget
));
588 if (!contentNode
) // detaching is only supported for content nodes
589 return NS_ERROR_FAILURE
;
591 // Only attach if we're really in a document
592 nsCOMPtr
<nsIDocument
> doc
= contentNode
->GetCurrentDoc();
594 piTarget
= do_QueryInterface(doc
);
596 EventListenerManager
* manager
= piTarget
->GetOrCreateListenerManager();
598 if (!piTarget
|| !manager
)
599 return NS_ERROR_FAILURE
;
601 nsIDOMEventListener
* handler
=
602 static_cast<nsIDOMEventListener
*>(contentNode
->GetProperty(nsGkAtoms::listener
));
604 return NS_ERROR_FAILURE
;
606 manager
->RemoveEventListenerByType(handler
, NS_LITERAL_STRING("keydown"),
607 TrustedEventsAtSystemGroupBubble());
608 manager
->RemoveEventListenerByType(handler
, NS_LITERAL_STRING("keyup"),
609 TrustedEventsAtSystemGroupBubble());
610 manager
->RemoveEventListenerByType(handler
, NS_LITERAL_STRING("keypress"),
611 TrustedEventsAtSystemGroupBubble());
613 manager
->RemoveEventListenerByType(handler
, NS_LITERAL_STRING("keydown"),
614 TrustedEventsAtSystemGroupCapture());
615 manager
->RemoveEventListenerByType(handler
, NS_LITERAL_STRING("keyup"),
616 TrustedEventsAtSystemGroupCapture());
617 manager
->RemoveEventListenerByType(handler
, NS_LITERAL_STRING("keypress"),
618 TrustedEventsAtSystemGroupCapture());
620 contentNode
->DeleteProperty(nsGkAtoms::listener
);
625 // Internal helper methods ////////////////////////////////////////////////////////////////
628 nsXBLService::BindingReady(nsIContent
* aBoundElement
,
632 // Don't do a security check here; we know this binding is set to go.
633 return GetBinding(aBoundElement
, aURI
, true, nullptr, aIsReady
, nullptr);
637 nsXBLService::GetBinding(nsIContent
* aBoundElement
, nsIURI
* aURI
,
638 bool aPeekOnly
, nsIPrincipal
* aOriginPrincipal
,
639 bool* aIsReady
, nsXBLBinding
** aResult
)
641 // More than 6 binding URIs are rare, see bug 55070 comment 18.
642 nsAutoTArray
<nsIURI
*, 6> uris
;
643 return GetBinding(aBoundElement
, aURI
, aPeekOnly
, aOriginPrincipal
, aIsReady
,
648 MayBindToContent(nsXBLPrototypeBinding
* aProtoBinding
, nsIContent
* aBoundElement
,
651 // If this binding explicitly allows untrusted content, we're done.
652 if (aProtoBinding
->BindToUntrustedContent()) {
656 // We let XUL content and content in XUL documents through, since XUL is
657 // restricted anyway and we want to minimize remote XUL breakage.
658 if (aBoundElement
->IsXUL() || aBoundElement
->OwnerDoc()->IsXUL()) {
662 // Similarly, we make an exception for anonymous content (which
663 // lives in the XBL scope), because it's already protected from content,
664 // and tends to use a lot of bindings that we wouldn't otherwise need to
666 if (aBoundElement
->IsInAnonymousSubtree()) {
670 // Allow if the bound content subsumes the binding.
671 nsCOMPtr
<nsIDocument
> bindingDoc
= aProtoBinding
->XBLDocumentInfo()->GetDocument();
672 NS_ENSURE_TRUE(bindingDoc
, false);
673 if (aBoundElement
->NodePrincipal()->Subsumes(bindingDoc
->NodePrincipal())) {
677 // One last special case: we need to watch out for in-document data: URI
678 // bindings from remote-XUL-whitelisted domains (especially tests), because
679 // they end up with a null principal (rather than inheriting the document's
680 // principal), which causes them to fail the check above.
681 if (nsContentUtils::AllowXULXBLForPrincipal(aBoundElement
->NodePrincipal())) {
682 bool isDataURI
= false;
683 nsresult rv
= aURI
->SchemeIs("data", &isDataURI
);
684 NS_ENSURE_SUCCESS(rv
, false);
695 nsXBLService::GetBinding(nsIContent
* aBoundElement
, nsIURI
* aURI
,
696 bool aPeekOnly
, nsIPrincipal
* aOriginPrincipal
,
697 bool* aIsReady
, nsXBLBinding
** aResult
,
698 nsTArray
<nsIURI
*>& aDontExtendURIs
)
700 NS_ASSERTION(aPeekOnly
|| aResult
,
701 "Must have non-null out param if not just peeking to see "
702 "whether the binding is ready");
708 return NS_ERROR_FAILURE
;
713 nsCOMPtr
<nsIDocument
> boundDocument
= aBoundElement
->OwnerDoc();
715 nsRefPtr
<nsXBLDocumentInfo
> docInfo
;
716 nsresult rv
= LoadBindingDocumentInfo(aBoundElement
, boundDocument
, aURI
,
718 false, getter_AddRefs(docInfo
));
719 NS_ENSURE_SUCCESS(rv
, rv
);
722 return NS_ERROR_FAILURE
;
724 nsXBLPrototypeBinding
* protoBinding
= docInfo
->GetPrototypeBinding(ref
);
728 nsAutoCString uriSpec
;
729 aURI
->GetSpec(uriSpec
);
731 boundDocument
->GetDocumentURI()->GetSpec(doc
);
732 nsAutoCString
message("Unable to locate an XBL binding for URI ");
734 message
+= " in document ";
736 NS_WARNING(message
.get());
738 return NS_ERROR_FAILURE
;
741 // If the binding isn't whitelisted, refuse to apply it to content that
742 // doesn't subsume it (modulo a few exceptions).
743 if (!MayBindToContent(protoBinding
, aBoundElement
, aURI
)) {
745 nsAutoCString uriSpec
;
746 aURI
->GetSpec(uriSpec
);
747 nsAutoCString
message("Permission denied to apply binding ");
749 message
+= " to unprivileged content. Set bindToUntrustedContent=true on "
750 "the binding to override this restriction.";
751 NS_WARNING(message
.get());
753 return NS_ERROR_FAILURE
;
756 NS_ENSURE_TRUE(aDontExtendURIs
.AppendElement(protoBinding
->BindingURI()),
757 NS_ERROR_OUT_OF_MEMORY
);
758 nsCOMPtr
<nsIURI
> altBindingURI
= protoBinding
->AlternateBindingURI();
760 NS_ENSURE_TRUE(aDontExtendURIs
.AppendElement(altBindingURI
),
761 NS_ERROR_OUT_OF_MEMORY
);
764 // Our prototype binding must have all its resources loaded.
765 bool ready
= protoBinding
->LoadResources();
767 // Add our bound element to the protos list of elts that should
768 // be notified when the stylesheets and scripts finish loading.
769 protoBinding
->AddResourceListener(aBoundElement
);
770 return NS_ERROR_FAILURE
; // The binding isn't ready yet.
773 rv
= protoBinding
->ResolveBaseBinding();
774 NS_ENSURE_SUCCESS(rv
, rv
);
776 nsIURI
* baseBindingURI
;
777 nsXBLPrototypeBinding
* baseProto
= protoBinding
->GetBasePrototype();
779 baseBindingURI
= baseProto
->BindingURI();
782 baseBindingURI
= protoBinding
->GetBaseBindingURI();
783 if (baseBindingURI
) {
784 uint32_t count
= aDontExtendURIs
.Length();
785 for (uint32_t index
= 0; index
< count
; ++index
) {
787 rv
= aDontExtendURIs
[index
]->Equals(baseBindingURI
, &equal
);
788 NS_ENSURE_SUCCESS(rv
, rv
);
790 nsAutoCString spec
, basespec
;
791 protoBinding
->BindingURI()->GetSpec(spec
);
792 NS_ConvertUTF8toUTF16
protoSpec(spec
);
793 baseBindingURI
->GetSpec(basespec
);
794 NS_ConvertUTF8toUTF16
baseSpecUTF16(basespec
);
795 const char16_t
* params
[] = { protoSpec
.get(), baseSpecUTF16
.get() };
796 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag
,
797 NS_LITERAL_CSTRING("XBL"), nullptr,
798 nsContentUtils::eXBL_PROPERTIES
,
799 "CircularExtendsBinding",
800 params
, ArrayLength(params
),
801 boundDocument
->GetDocumentURI());
802 return NS_ERROR_ILLEGAL_VALUE
;
808 nsRefPtr
<nsXBLBinding
> baseBinding
;
809 if (baseBindingURI
) {
810 nsIContent
* child
= protoBinding
->GetBindingElement();
811 rv
= GetBinding(aBoundElement
, baseBindingURI
, aPeekOnly
,
812 child
->NodePrincipal(), aIsReady
,
813 getter_AddRefs(baseBinding
), aDontExtendURIs
);
815 return rv
; // We aren't ready yet.
821 // Make a new binding
822 nsXBLBinding
*newBinding
= new nsXBLBinding(protoBinding
);
823 NS_ENSURE_TRUE(newBinding
, NS_ERROR_OUT_OF_MEMORY
);
827 protoBinding
->SetBasePrototype(baseBinding
->PrototypeBinding());
829 newBinding
->SetBaseBinding(baseBinding
);
832 NS_ADDREF(*aResult
= newBinding
);
838 static bool SchemeIs(nsIURI
* aURI
, const char* aScheme
)
840 nsCOMPtr
<nsIURI
> baseURI
= NS_GetInnermostURI(aURI
);
841 NS_ENSURE_TRUE(baseURI
, false);
843 bool isScheme
= false;
844 return NS_SUCCEEDED(baseURI
->SchemeIs(aScheme
, &isScheme
)) && isScheme
;
848 IsSystemOrChromeURLPrincipal(nsIPrincipal
* aPrincipal
)
850 if (nsContentUtils::IsSystemPrincipal(aPrincipal
)) {
854 nsCOMPtr
<nsIURI
> uri
;
855 aPrincipal
->GetURI(getter_AddRefs(uri
));
856 NS_ENSURE_TRUE(uri
, false);
858 bool isChrome
= false;
859 return NS_SUCCEEDED(uri
->SchemeIs("chrome", &isChrome
)) && isChrome
;
863 nsXBLService::LoadBindingDocumentInfo(nsIContent
* aBoundElement
,
864 nsIDocument
* aBoundDocument
,
866 nsIPrincipal
* aOriginPrincipal
,
868 nsXBLDocumentInfo
** aResult
)
870 NS_PRECONDITION(aBindingURI
, "Must have a binding URI");
871 NS_PRECONDITION(!aOriginPrincipal
|| aBoundDocument
,
872 "If we're doing a security check, we better have a document!");
875 if (aOriginPrincipal
) {
876 // Security check - Enforce same-origin policy, except to chrome.
877 // We have to be careful to not pass aContent as the context here.
878 // Otherwise, if there is a JS-implemented content policy, we will attempt
879 // to wrap the content node, which will try to load XBL bindings for it, if
880 // any. Since we're not done loading this binding yet, that will reenter
881 // this method and we'll end up creating a binding and then immediately
882 // clobbering it in our table. That makes things very confused, leading to
883 // misbehavior and crashes.
884 rv
= nsContentUtils::
885 CheckSecurityBeforeLoad(aBindingURI
, aOriginPrincipal
,
886 nsIScriptSecurityManager::ALLOW_CHROME
,
888 nsIContentPolicy::TYPE_XBL
,
890 NS_ENSURE_SUCCESS(rv
, NS_ERROR_XBL_BLOCKED
);
892 if (!IsSystemOrChromeURLPrincipal(aOriginPrincipal
)) {
893 // Also make sure that we're same-origin with the bound document
894 // except if the stylesheet has the system principal.
895 if (!(gAllowDataURIs
&& SchemeIs(aBindingURI
, "data")) &&
896 !SchemeIs(aBindingURI
, "chrome")) {
897 rv
= aBoundDocument
->NodePrincipal()->CheckMayLoad(aBindingURI
,
899 NS_ENSURE_SUCCESS(rv
, NS_ERROR_XBL_BLOCKED
);
902 // Finally check if this document is allowed to use XBL at all.
903 NS_ENSURE_TRUE(aBoundDocument
->AllowXULXBL(),
904 NS_ERROR_XBL_BLOCKED
);
909 nsRefPtr
<nsXBLDocumentInfo
> info
;
911 nsCOMPtr
<nsIURI
> documentURI
;
912 rv
= aBindingURI
->CloneIgnoringRef(getter_AddRefs(documentURI
));
913 NS_ENSURE_SUCCESS(rv
, rv
);
916 // We've got a file. Check our XBL document cache.
917 nsXULPrototypeCache
* cache
= nsXULPrototypeCache::GetInstance();
918 bool useXULCache
= cache
&& cache
->IsEnabled();
921 // The first line of defense is the chrome cache.
922 // This cache crosses the entire product, so that any XBL bindings that are
923 // part of chrome will be reused across all XUL documents.
924 info
= cache
->GetXBLDocumentInfo(documentURI
);
929 // The second line of defense is the binding manager's document table.
930 nsBindingManager
*bindingManager
= nullptr;
932 if (aBoundDocument
) {
933 bindingManager
= aBoundDocument
->BindingManager();
934 info
= bindingManager
->GetXBLDocumentInfo(documentURI
);
935 if (aBoundDocument
->IsStaticDocument() &&
936 IsChromeOrResourceURI(aBindingURI
)) {
937 aForceSyncLoad
= true;
941 NodeInfo
*ni
= nullptr;
943 ni
= aBoundElement
->NodeInfo();
945 if (!info
&& bindingManager
&&
946 (!ni
|| !(ni
->Equals(nsGkAtoms::scrollbar
, kNameSpaceID_XUL
) ||
947 ni
->Equals(nsGkAtoms::thumb
, kNameSpaceID_XUL
) ||
948 ((ni
->Equals(nsGkAtoms::input
) ||
949 ni
->Equals(nsGkAtoms::select
)) &&
950 aBoundElement
->IsHTML()))) && !aForceSyncLoad
) {
951 // The third line of defense is to investigate whether or not the
952 // document is currently being loaded asynchronously. If so, there's no
953 // document yet, but we need to glom on our request so that it will be
954 // processed whenever the doc does finish loading.
955 nsCOMPtr
<nsIStreamListener
> listener
;
957 listener
= bindingManager
->GetLoadingDocListener(documentURI
);
959 nsXBLStreamListener
* xblListener
=
960 static_cast<nsXBLStreamListener
*>(listener
.get());
961 // Create a new load observer.
962 if (!xblListener
->HasRequest(aBindingURI
, aBoundElement
)) {
963 nsXBLBindingRequest
* req
= new nsXBLBindingRequest(aBindingURI
, aBoundElement
);
964 xblListener
->AddRequest(req
);
971 // Next, look in the startup cache
972 bool useStartupCache
= useXULCache
&& IsChromeOrResourceURI(documentURI
);
973 if (!info
&& useStartupCache
) {
974 rv
= nsXBLDocumentInfo::ReadPrototypeBindings(documentURI
, getter_AddRefs(info
));
975 if (NS_SUCCEEDED(rv
)) {
976 cache
->PutXBLDocumentInfo(info
);
978 if (bindingManager
) {
979 // Cache it in our binding manager's document table.
980 bindingManager
->PutXBLDocumentInfo(info
);
987 // Finally, if all lines of defense fail, we go and fetch the binding
990 // Always load chrome synchronously
992 if (NS_SUCCEEDED(documentURI
->SchemeIs("chrome", &chrome
)) && chrome
)
993 aForceSyncLoad
= true;
995 nsCOMPtr
<nsIDocument
> document
;
996 FetchBindingDocument(aBoundElement
, aBoundDocument
, documentURI
,
997 aBindingURI
, aForceSyncLoad
, getter_AddRefs(document
));
1000 nsBindingManager
*xblDocBindingManager
= document
->BindingManager();
1001 info
= xblDocBindingManager
->GetXBLDocumentInfo(documentURI
);
1003 NS_ERROR("An XBL file is malformed. Did you forget the XBL namespace on the bindings tag?");
1004 return NS_ERROR_FAILURE
;
1006 xblDocBindingManager
->RemoveXBLDocumentInfo(info
); // Break the self-imposed cycle.
1008 // If the doc is a chrome URI, then we put it into the XUL cache.
1010 if (useStartupCache
) {
1011 cache
->PutXBLDocumentInfo(info
);
1013 // now write the bindings into the startup cache
1014 info
->WritePrototypeBindings();
1018 if (bindingManager
) {
1019 // Also put it in our binding manager's document table.
1020 bindingManager
->PutXBLDocumentInfo(info
);
1026 info
.forget(aResult
);
1032 nsXBLService::FetchBindingDocument(nsIContent
* aBoundElement
, nsIDocument
* aBoundDocument
,
1033 nsIURI
* aDocumentURI
, nsIURI
* aBindingURI
,
1034 bool aForceSyncLoad
, nsIDocument
** aResult
)
1036 nsresult rv
= NS_OK
;
1037 // Initialize our out pointer to nullptr
1040 // Now we have to synchronously load the binding file.
1041 // Create an XML content sink and a parser.
1042 nsCOMPtr
<nsILoadGroup
> loadGroup
;
1044 loadGroup
= aBoundDocument
->GetDocumentLoadGroup();
1046 // We really shouldn't have to force a sync load for anything here... could
1047 // we get away with not doing that? Not sure.
1048 if (IsChromeOrResourceURI(aDocumentURI
))
1049 aForceSyncLoad
= true;
1051 // Create document and contentsink and set them up.
1052 nsCOMPtr
<nsIDocument
> doc
;
1053 rv
= NS_NewXMLDocument(getter_AddRefs(doc
));
1054 NS_ENSURE_SUCCESS(rv
, rv
);
1056 nsCOMPtr
<nsIXMLContentSink
> xblSink
;
1057 rv
= NS_NewXBLContentSink(getter_AddRefs(xblSink
), doc
, aDocumentURI
, nullptr);
1058 NS_ENSURE_SUCCESS(rv
, rv
);
1061 nsCOMPtr
<nsIChannel
> channel
;
1062 rv
= NS_NewChannel(getter_AddRefs(channel
), aDocumentURI
, nullptr, loadGroup
);
1063 NS_ENSURE_SUCCESS(rv
, rv
);
1065 nsCOMPtr
<nsIInterfaceRequestor
> sameOriginChecker
= nsContentUtils::GetSameOriginChecker();
1066 NS_ENSURE_TRUE(sameOriginChecker
, NS_ERROR_OUT_OF_MEMORY
);
1068 channel
->SetNotificationCallbacks(sameOriginChecker
);
1070 if (!aForceSyncLoad
) {
1071 // We can be asynchronous
1072 nsXBLStreamListener
* xblListener
=
1073 new nsXBLStreamListener(aBoundDocument
, xblSink
, doc
);
1074 NS_ENSURE_TRUE(xblListener
,NS_ERROR_OUT_OF_MEMORY
);
1076 // Add ourselves to the list of loading docs.
1077 nsBindingManager
*bindingManager
;
1079 bindingManager
= aBoundDocument
->BindingManager();
1081 bindingManager
= nullptr;
1084 bindingManager
->PutLoadingDocListener(aDocumentURI
, xblListener
);
1087 nsXBLBindingRequest
* req
= new nsXBLBindingRequest(aBindingURI
,
1089 xblListener
->AddRequest(req
);
1091 // Now kick off the async read.
1092 rv
= channel
->AsyncOpen(xblListener
, nullptr);
1093 if (NS_FAILED(rv
)) {
1094 // Well, we won't be getting a load. Make sure to clean up our stuff!
1095 if (bindingManager
) {
1096 bindingManager
->RemoveLoadingDocListener(aDocumentURI
);
1102 nsCOMPtr
<nsIStreamListener
> listener
;
1103 rv
= doc
->StartDocumentLoad("loadAsInteractiveData",
1107 getter_AddRefs(listener
),
1110 NS_ENSURE_SUCCESS(rv
, rv
);
1112 // Now do a blocking synchronous parse of the file.
1113 nsCOMPtr
<nsIInputStream
> in
;
1114 rv
= channel
->Open(getter_AddRefs(in
));
1115 NS_ENSURE_SUCCESS(rv
, rv
);
1117 rv
= nsSyncLoadService::PushSyncStreamToListener(in
, listener
, channel
);
1118 NS_ENSURE_SUCCESS(rv
, rv
);