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();
119 // Destroy the frames for mBoundElement.
120 nsIContent
* destroyedFramesFor
= nullptr;
121 nsIPresShell
* shell
= doc
->GetShell();
123 shell
->DestroyFramesFor(mBoundElement
, &destroyedFramesFor
);
125 MOZ_ASSERT(!mBoundElement
->GetPrimaryFrame());
129 nsXBLService::GetInstance()->BindingReady(mBoundElement
, mBindingURI
, &ready
);
133 // If |mBoundElement| is (in addition to having binding |mBinding|)
134 // also a descendant of another element with binding |mBinding|,
135 // then we might have just constructed it due to the
136 // notification of its parent. (We can know about both if the
137 // binding loads were triggered from the DOM rather than frame
138 // construction.) So we have to check both whether the element
139 // has a primary frame and whether it's in the frame manager maps
140 // before sending a ContentInserted notification, or bad things
142 MOZ_ASSERT(shell
== doc
->GetShell());
144 nsIFrame
* childFrame
= mBoundElement
->GetPrimaryFrame();
146 // Check to see if it's in the undisplayed content map...
147 nsFrameManager
* fm
= shell
->FrameManager();
148 nsStyleContext
* sc
= fm
->GetUndisplayedContent(mBoundElement
);
150 // or in the display:contents map.
151 sc
= fm
->GetDisplayContentsStyleFor(mBoundElement
);
154 shell
->CreateFramesFor(destroyedFramesFor
);
160 nsXBLBindingRequest(nsIURI
* aURI
, nsIContent
* aBoundElement
)
162 mBoundElement(aBoundElement
)
167 // nsXBLStreamListener, a helper class used for
168 // asynchronous parsing of URLs
170 class nsXBLStreamListener MOZ_FINAL
: public nsIStreamListener
,
171 public nsIDOMEventListener
175 NS_DECL_NSISTREAMLISTENER
176 NS_DECL_NSIREQUESTOBSERVER
177 NS_DECL_NSIDOMEVENTLISTENER
179 nsXBLStreamListener(nsIDocument
* aBoundDocument
,
180 nsIXMLContentSink
* aSink
,
181 nsIDocument
* aBindingDocument
);
183 void AddRequest(nsXBLBindingRequest
* aRequest
) { mBindingRequests
.AppendElement(aRequest
); }
184 bool HasRequest(nsIURI
* aURI
, nsIContent
* aBoundElement
);
187 ~nsXBLStreamListener();
189 nsCOMPtr
<nsIStreamListener
> mInner
;
190 nsAutoTArray
<nsXBLBindingRequest
*, 8> mBindingRequests
;
192 nsCOMPtr
<nsIWeakReference
> mBoundDocument
;
193 nsCOMPtr
<nsIXMLContentSink
> mSink
; // Only set until OnStartRequest
194 nsCOMPtr
<nsIDocument
> mBindingDocument
; // Only set until OnStartRequest
197 /* Implementation file */
198 NS_IMPL_ISUPPORTS(nsXBLStreamListener
,
203 nsXBLStreamListener::nsXBLStreamListener(nsIDocument
* aBoundDocument
,
204 nsIXMLContentSink
* aSink
,
205 nsIDocument
* aBindingDocument
)
206 : mSink(aSink
), mBindingDocument(aBindingDocument
)
208 /* member initializers and constructor code */
209 mBoundDocument
= do_GetWeakReference(aBoundDocument
);
212 nsXBLStreamListener::~nsXBLStreamListener()
214 for (uint32_t i
= 0; i
< mBindingRequests
.Length(); i
++) {
215 nsXBLBindingRequest
* req
= mBindingRequests
.ElementAt(i
);
221 nsXBLStreamListener::OnDataAvailable(nsIRequest
*request
, nsISupports
* aCtxt
,
222 nsIInputStream
* aInStr
,
223 uint64_t aSourceOffset
, uint32_t aCount
)
226 return mInner
->OnDataAvailable(request
, aCtxt
, aInStr
, aSourceOffset
, aCount
);
227 return NS_ERROR_FAILURE
;
231 nsXBLStreamListener::OnStartRequest(nsIRequest
* request
, nsISupports
* aCtxt
)
233 // Make sure we don't hold on to the sink and binding document past this point
234 nsCOMPtr
<nsIXMLContentSink
> sink
;
236 nsCOMPtr
<nsIDocument
> doc
;
237 mBindingDocument
.swap(doc
);
239 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(request
);
240 NS_ENSURE_TRUE(channel
, NS_ERROR_UNEXPECTED
);
242 nsCOMPtr
<nsILoadGroup
> group
;
243 request
->GetLoadGroup(getter_AddRefs(group
));
245 nsresult rv
= doc
->StartDocumentLoad("loadAsInteractiveData",
249 getter_AddRefs(mInner
),
252 NS_ENSURE_SUCCESS(rv
, rv
);
254 // Make sure to add ourselves as a listener after StartDocumentLoad,
255 // since that resets the event listners on the document.
256 doc
->AddEventListener(NS_LITERAL_STRING("load"), this, false);
258 return mInner
->OnStartRequest(request
, aCtxt
);
262 nsXBLStreamListener::OnStopRequest(nsIRequest
* request
, nsISupports
* aCtxt
, nsresult aStatus
)
266 rv
= mInner
->OnStopRequest(request
, aCtxt
, aStatus
);
269 // Don't hold onto the inner listener; holding onto it can create a cycle
277 nsXBLStreamListener::HasRequest(nsIURI
* aURI
, nsIContent
* aElt
)
279 // XXX Could be more efficient.
280 uint32_t count
= mBindingRequests
.Length();
281 for (uint32_t i
= 0; i
< count
; i
++) {
282 nsXBLBindingRequest
* req
= mBindingRequests
.ElementAt(i
);
284 if (req
->mBoundElement
== aElt
&&
285 NS_SUCCEEDED(req
->mBindingURI
->Equals(aURI
, &eq
)) && eq
)
293 nsXBLStreamListener::HandleEvent(nsIDOMEvent
* aEvent
)
297 uint32_t count
= mBindingRequests
.Length();
299 // Get the binding document; note that we don't hold onto it in this object
300 // to avoid creating a cycle
301 Event
* event
= aEvent
->InternalDOMEvent();
302 EventTarget
* target
= event
->GetCurrentTarget();
303 nsCOMPtr
<nsIDocument
> bindingDocument
= do_QueryInterface(target
);
304 NS_ASSERTION(bindingDocument
, "Event not targeted at document?!");
306 // See if we're still alive.
307 nsCOMPtr
<nsIDocument
> doc(do_QueryReferent(mBoundDocument
));
309 NS_WARNING("XBL load did not complete until after document went away! Modal dialog bug?\n");
312 // We have to do a flush prior to notification of the document load.
313 // This has to happen since the HTML content sink can be holding on
314 // to notifications related to our children (e.g., if you bind to the
315 // <body> tag) that result in duplication of content.
316 // We need to get the sink's notifications flushed and then make the binding
319 nsXBLBindingRequest
* req
= mBindingRequests
.ElementAt(0);
320 nsIDocument
* document
= req
->mBoundElement
->GetCurrentDoc();
322 document
->FlushPendingNotifications(Flush_ContentAndNotify
);
325 // Remove ourselves from the set of pending docs.
326 nsBindingManager
*bindingManager
= doc
->BindingManager();
327 nsIURI
* documentURI
= bindingDocument
->GetDocumentURI();
328 bindingManager
->RemoveLoadingDocListener(documentURI
);
330 if (!bindingDocument
->GetRootElement()) {
331 // FIXME: How about an error console warning?
332 NS_WARNING("XBL doc with no root element - this usually shouldn't happen");
333 return NS_ERROR_FAILURE
;
336 // Put our doc info in the doc table.
337 nsBindingManager
*xblDocBindingManager
= bindingDocument
->BindingManager();
338 nsRefPtr
<nsXBLDocumentInfo
> info
=
339 xblDocBindingManager
->GetXBLDocumentInfo(documentURI
);
340 xblDocBindingManager
->RemoveXBLDocumentInfo(info
); // Break the self-imposed cycle.
342 if (nsXBLService::IsChromeOrResourceURI(documentURI
)) {
343 NS_WARNING("An XBL file is malformed. Did you forget the XBL namespace on the bindings tag?");
345 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag
,
346 NS_LITERAL_CSTRING("XBL"), nullptr,
347 nsContentUtils::eXBL_PROPERTIES
,
349 nullptr, 0, documentURI
);
350 return NS_ERROR_FAILURE
;
353 // If the doc is a chrome URI, then we put it into the XUL cache.
355 if (nsXBLService::IsChromeOrResourceURI(documentURI
)) {
356 nsXULPrototypeCache
* cache
= nsXULPrototypeCache::GetInstance();
357 if (cache
&& cache
->IsEnabled())
358 cache
->PutXBLDocumentInfo(info
);
362 bindingManager
->PutXBLDocumentInfo(info
);
364 // Notify all pending requests that their bindings are
365 // ready and can be installed.
366 for (i
= 0; i
< count
; i
++) {
367 nsXBLBindingRequest
* req
= mBindingRequests
.ElementAt(i
);
368 req
->DocumentLoaded(bindingDocument
);
372 target
->RemoveEventListener(NS_LITERAL_STRING("load"), this, false);
377 // Implementation /////////////////////////////////////////////////////////////////
379 // Static member variable initialization
380 bool nsXBLService::gAllowDataURIs
= false;
382 // Implement our nsISupports methods
383 NS_IMPL_ISUPPORTS(nsXBLService
, nsISupportsWeakReference
)
388 gInstance
= new nsXBLService();
389 NS_ADDREF(gInstance
);
392 // Constructors/Destructors
393 nsXBLService::nsXBLService(void)
395 Preferences::AddBoolVarCache(&gAllowDataURIs
, "layout.debug.enable_data_xbl");
398 nsXBLService::~nsXBLService(void)
404 nsXBLService::IsChromeOrResourceURI(nsIURI
* aURI
)
406 bool isChrome
= false;
407 bool isResource
= false;
408 if (NS_SUCCEEDED(aURI
->SchemeIs("chrome", &isChrome
)) &&
409 NS_SUCCEEDED(aURI
->SchemeIs("resource", &isResource
)))
410 return (isChrome
|| isResource
);
415 // This function loads a particular XBL file and installs all of the bindings
418 nsXBLService::LoadBindings(nsIContent
* aContent
, nsIURI
* aURL
,
419 nsIPrincipal
* aOriginPrincipal
,
420 nsXBLBinding
** aBinding
, bool* aResolveStyle
)
422 NS_PRECONDITION(aOriginPrincipal
, "Must have an origin principal");
425 *aResolveStyle
= false;
429 nsCOMPtr
<nsIDocument
> document
= aContent
->OwnerDoc();
431 nsAutoCString urlspec
;
432 if (nsContentUtils::GetWrapperSafeScriptFilename(document
, aURL
, urlspec
)) {
433 // Block an attempt to load a binding that has special wrapper
439 nsXBLBinding
*binding
= aContent
->GetXBLBinding();
441 if (binding
->MarkedForDeath()) {
442 FlushStyleBindings(aContent
);
446 // See if the URIs match.
447 if (binding
->PrototypeBinding()->CompareBindingURI(aURL
))
449 FlushStyleBindings(aContent
);
455 nsRefPtr
<nsXBLBinding
> newBinding
;
456 if (NS_FAILED(rv
= GetBinding(aContent
, aURL
, false, aOriginPrincipal
,
457 &ready
, getter_AddRefs(newBinding
)))) {
465 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
);
471 if (::IsAncestorBinding(document
, aURL
, aContent
)) {
472 return NS_ERROR_ILLEGAL_VALUE
;
475 // We loaded a style binding. It goes on the end.
477 // Get the last binding that is in the append layer.
478 binding
->RootBinding()->SetBaseBinding(newBinding
);
481 // Install the binding on the content node.
482 aContent
->SetXBLBinding(newBinding
);
486 nsAutoScriptBlocker scriptBlocker
;
488 // Set the binding's bound element.
489 newBinding
->SetBoundElement(aContent
);
491 // Tell the binding to build the anonymous content.
492 newBinding
->GenerateAnonymousContent();
494 // Tell the binding to install event handlers
495 newBinding
->InstallEventHandlers();
497 // Set up our properties
498 rv
= newBinding
->InstallImplementation();
499 NS_ENSURE_SUCCESS(rv
, rv
);
501 // Figure out if we have any scoped sheets. If so, we do a second resolve.
502 *aResolveStyle
= newBinding
->HasStyleSheets();
504 newBinding
.swap(*aBinding
);
511 nsXBLService::FlushStyleBindings(nsIContent
* aContent
)
513 nsCOMPtr
<nsIDocument
> document
= aContent
->OwnerDoc();
515 nsXBLBinding
*binding
= aContent
->GetXBLBinding();
517 // Clear out the script references.
518 binding
->ChangeDocument(document
, nullptr);
520 aContent
->SetXBLBinding(nullptr); // Flush old style bindings
527 // AttachGlobalKeyHandler
529 // Creates a new key handler and prepares to listen to key events on the given
530 // event receiver (either a document or an content node). If the receiver is content,
531 // then extra work needs to be done to hook it up to the document (XXX WHY??)
534 nsXBLService::AttachGlobalKeyHandler(EventTarget
* aTarget
)
536 // check if the receiver is a content node (not a document), and hook
537 // it to the document if that is the case.
538 nsCOMPtr
<EventTarget
> piTarget
= aTarget
;
539 nsCOMPtr
<nsIContent
> contentNode(do_QueryInterface(aTarget
));
541 // Only attach if we're really in a document
542 nsCOMPtr
<nsIDocument
> doc
= contentNode
->GetCurrentDoc();
544 piTarget
= doc
; // We're a XUL keyset. Attach to our document.
547 EventListenerManager
* manager
= piTarget
->GetOrCreateListenerManager();
549 if (!piTarget
|| !manager
)
550 return NS_ERROR_FAILURE
;
552 // the listener already exists, so skip this
553 if (contentNode
&& contentNode
->GetProperty(nsGkAtoms::listener
))
556 nsCOMPtr
<nsIDOMElement
> elt(do_QueryInterface(contentNode
));
558 // Create the key handler
559 nsRefPtr
<nsXBLWindowKeyHandler
> handler
=
560 NS_NewXBLWindowKeyHandler(elt
, piTarget
);
562 // listen to these events
563 manager
->AddEventListenerByType(handler
, NS_LITERAL_STRING("keydown"),
564 TrustedEventsAtSystemGroupBubble());
565 manager
->AddEventListenerByType(handler
, NS_LITERAL_STRING("keyup"),
566 TrustedEventsAtSystemGroupBubble());
567 manager
->AddEventListenerByType(handler
, NS_LITERAL_STRING("keypress"),
568 TrustedEventsAtSystemGroupBubble());
570 // The capturing listener is only used for XUL keysets to properly handle
571 // shortcut keys in a multi-process environment.
572 manager
->AddEventListenerByType(handler
, NS_LITERAL_STRING("keydown"),
573 TrustedEventsAtSystemGroupCapture());
574 manager
->AddEventListenerByType(handler
, NS_LITERAL_STRING("keyup"),
575 TrustedEventsAtSystemGroupCapture());
576 manager
->AddEventListenerByType(handler
, NS_LITERAL_STRING("keypress"),
577 TrustedEventsAtSystemGroupCapture());
580 return contentNode
->SetProperty(nsGkAtoms::listener
,
581 handler
.forget().take(),
582 nsPropertyTable::SupportsDtorFunc
, true);
584 // The reference to the handler will be maintained by the event target,
585 // and, if there is a content node, the property.
590 // DetachGlobalKeyHandler
592 // Removes a key handler added by DeatchGlobalKeyHandler.
595 nsXBLService::DetachGlobalKeyHandler(EventTarget
* aTarget
)
597 nsCOMPtr
<EventTarget
> piTarget
= aTarget
;
598 nsCOMPtr
<nsIContent
> contentNode(do_QueryInterface(aTarget
));
599 if (!contentNode
) // detaching is only supported for content nodes
600 return NS_ERROR_FAILURE
;
602 // Only attach if we're really in a document
603 nsCOMPtr
<nsIDocument
> doc
= contentNode
->GetCurrentDoc();
605 piTarget
= do_QueryInterface(doc
);
607 EventListenerManager
* manager
= piTarget
->GetOrCreateListenerManager();
609 if (!piTarget
|| !manager
)
610 return NS_ERROR_FAILURE
;
612 nsIDOMEventListener
* handler
=
613 static_cast<nsIDOMEventListener
*>(contentNode
->GetProperty(nsGkAtoms::listener
));
615 return NS_ERROR_FAILURE
;
617 manager
->RemoveEventListenerByType(handler
, NS_LITERAL_STRING("keydown"),
618 TrustedEventsAtSystemGroupBubble());
619 manager
->RemoveEventListenerByType(handler
, NS_LITERAL_STRING("keyup"),
620 TrustedEventsAtSystemGroupBubble());
621 manager
->RemoveEventListenerByType(handler
, NS_LITERAL_STRING("keypress"),
622 TrustedEventsAtSystemGroupBubble());
624 manager
->RemoveEventListenerByType(handler
, NS_LITERAL_STRING("keydown"),
625 TrustedEventsAtSystemGroupCapture());
626 manager
->RemoveEventListenerByType(handler
, NS_LITERAL_STRING("keyup"),
627 TrustedEventsAtSystemGroupCapture());
628 manager
->RemoveEventListenerByType(handler
, NS_LITERAL_STRING("keypress"),
629 TrustedEventsAtSystemGroupCapture());
631 contentNode
->DeleteProperty(nsGkAtoms::listener
);
636 // Internal helper methods ////////////////////////////////////////////////////////////////
639 nsXBLService::BindingReady(nsIContent
* aBoundElement
,
643 // Don't do a security check here; we know this binding is set to go.
644 return GetBinding(aBoundElement
, aURI
, true, nullptr, aIsReady
, nullptr);
648 nsXBLService::GetBinding(nsIContent
* aBoundElement
, nsIURI
* aURI
,
649 bool aPeekOnly
, nsIPrincipal
* aOriginPrincipal
,
650 bool* aIsReady
, nsXBLBinding
** aResult
)
652 // More than 6 binding URIs are rare, see bug 55070 comment 18.
653 nsAutoTArray
<nsIURI
*, 6> uris
;
654 return GetBinding(aBoundElement
, aURI
, aPeekOnly
, aOriginPrincipal
, aIsReady
,
659 MayBindToContent(nsXBLPrototypeBinding
* aProtoBinding
, nsIContent
* aBoundElement
,
662 // If this binding explicitly allows untrusted content, we're done.
663 if (aProtoBinding
->BindToUntrustedContent()) {
667 // We let XUL content and content in XUL documents through, since XUL is
668 // restricted anyway and we want to minimize remote XUL breakage.
669 if (aBoundElement
->IsXUL() || aBoundElement
->OwnerDoc()->IsXUL()) {
673 // Similarly, we make an exception for anonymous content (which
674 // lives in the XBL scope), because it's already protected from content,
675 // and tends to use a lot of bindings that we wouldn't otherwise need to
677 if (aBoundElement
->IsInAnonymousSubtree()) {
681 // Allow if the bound content subsumes the binding.
682 nsCOMPtr
<nsIDocument
> bindingDoc
= aProtoBinding
->XBLDocumentInfo()->GetDocument();
683 NS_ENSURE_TRUE(bindingDoc
, false);
684 if (aBoundElement
->NodePrincipal()->Subsumes(bindingDoc
->NodePrincipal())) {
688 // One last special case: we need to watch out for in-document data: URI
689 // bindings from remote-XUL-whitelisted domains (especially tests), because
690 // they end up with a null principal (rather than inheriting the document's
691 // principal), which causes them to fail the check above.
692 if (nsContentUtils::AllowXULXBLForPrincipal(aBoundElement
->NodePrincipal())) {
693 bool isDataURI
= false;
694 nsresult rv
= aURI
->SchemeIs("data", &isDataURI
);
695 NS_ENSURE_SUCCESS(rv
, false);
706 nsXBLService::GetBinding(nsIContent
* aBoundElement
, nsIURI
* aURI
,
707 bool aPeekOnly
, nsIPrincipal
* aOriginPrincipal
,
708 bool* aIsReady
, nsXBLBinding
** aResult
,
709 nsTArray
<nsIURI
*>& aDontExtendURIs
)
711 NS_ASSERTION(aPeekOnly
|| aResult
,
712 "Must have non-null out param if not just peeking to see "
713 "whether the binding is ready");
719 return NS_ERROR_FAILURE
;
724 nsCOMPtr
<nsIDocument
> boundDocument
= aBoundElement
->OwnerDoc();
726 nsRefPtr
<nsXBLDocumentInfo
> docInfo
;
727 nsresult rv
= LoadBindingDocumentInfo(aBoundElement
, boundDocument
, aURI
,
729 false, getter_AddRefs(docInfo
));
730 NS_ENSURE_SUCCESS(rv
, rv
);
733 return NS_ERROR_FAILURE
;
735 nsXBLPrototypeBinding
* protoBinding
= docInfo
->GetPrototypeBinding(ref
);
739 nsAutoCString uriSpec
;
740 aURI
->GetSpec(uriSpec
);
742 boundDocument
->GetDocumentURI()->GetSpec(doc
);
743 nsAutoCString
message("Unable to locate an XBL binding for URI ");
745 message
+= " in document ";
747 NS_WARNING(message
.get());
749 return NS_ERROR_FAILURE
;
752 // If the binding isn't whitelisted, refuse to apply it to content that
753 // doesn't subsume it (modulo a few exceptions).
754 if (!MayBindToContent(protoBinding
, aBoundElement
, aURI
)) {
756 nsAutoCString uriSpec
;
757 aURI
->GetSpec(uriSpec
);
758 nsAutoCString
message("Permission denied to apply binding ");
760 message
+= " to unprivileged content. Set bindToUntrustedContent=true on "
761 "the binding to override this restriction.";
762 NS_WARNING(message
.get());
764 return NS_ERROR_FAILURE
;
767 NS_ENSURE_TRUE(aDontExtendURIs
.AppendElement(protoBinding
->BindingURI()),
768 NS_ERROR_OUT_OF_MEMORY
);
769 nsCOMPtr
<nsIURI
> altBindingURI
= protoBinding
->AlternateBindingURI();
771 NS_ENSURE_TRUE(aDontExtendURIs
.AppendElement(altBindingURI
),
772 NS_ERROR_OUT_OF_MEMORY
);
775 // Our prototype binding must have all its resources loaded.
776 bool ready
= protoBinding
->LoadResources();
778 // Add our bound element to the protos list of elts that should
779 // be notified when the stylesheets and scripts finish loading.
780 protoBinding
->AddResourceListener(aBoundElement
);
781 return NS_ERROR_FAILURE
; // The binding isn't ready yet.
784 rv
= protoBinding
->ResolveBaseBinding();
785 NS_ENSURE_SUCCESS(rv
, rv
);
787 nsIURI
* baseBindingURI
;
788 nsXBLPrototypeBinding
* baseProto
= protoBinding
->GetBasePrototype();
790 baseBindingURI
= baseProto
->BindingURI();
793 baseBindingURI
= protoBinding
->GetBaseBindingURI();
794 if (baseBindingURI
) {
795 uint32_t count
= aDontExtendURIs
.Length();
796 for (uint32_t index
= 0; index
< count
; ++index
) {
798 rv
= aDontExtendURIs
[index
]->Equals(baseBindingURI
, &equal
);
799 NS_ENSURE_SUCCESS(rv
, rv
);
801 nsAutoCString spec
, basespec
;
802 protoBinding
->BindingURI()->GetSpec(spec
);
803 NS_ConvertUTF8toUTF16
protoSpec(spec
);
804 baseBindingURI
->GetSpec(basespec
);
805 NS_ConvertUTF8toUTF16
baseSpecUTF16(basespec
);
806 const char16_t
* params
[] = { protoSpec
.get(), baseSpecUTF16
.get() };
807 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag
,
808 NS_LITERAL_CSTRING("XBL"), nullptr,
809 nsContentUtils::eXBL_PROPERTIES
,
810 "CircularExtendsBinding",
811 params
, ArrayLength(params
),
812 boundDocument
->GetDocumentURI());
813 return NS_ERROR_ILLEGAL_VALUE
;
819 nsRefPtr
<nsXBLBinding
> baseBinding
;
820 if (baseBindingURI
) {
821 nsIContent
* child
= protoBinding
->GetBindingElement();
822 rv
= GetBinding(aBoundElement
, baseBindingURI
, aPeekOnly
,
823 child
->NodePrincipal(), aIsReady
,
824 getter_AddRefs(baseBinding
), aDontExtendURIs
);
826 return rv
; // We aren't ready yet.
832 // Make a new binding
833 nsXBLBinding
*newBinding
= new nsXBLBinding(protoBinding
);
834 NS_ENSURE_TRUE(newBinding
, NS_ERROR_OUT_OF_MEMORY
);
838 protoBinding
->SetBasePrototype(baseBinding
->PrototypeBinding());
840 newBinding
->SetBaseBinding(baseBinding
);
843 NS_ADDREF(*aResult
= newBinding
);
849 static bool SchemeIs(nsIURI
* aURI
, const char* aScheme
)
851 nsCOMPtr
<nsIURI
> baseURI
= NS_GetInnermostURI(aURI
);
852 NS_ENSURE_TRUE(baseURI
, false);
854 bool isScheme
= false;
855 return NS_SUCCEEDED(baseURI
->SchemeIs(aScheme
, &isScheme
)) && isScheme
;
859 IsSystemOrChromeURLPrincipal(nsIPrincipal
* aPrincipal
)
861 if (nsContentUtils::IsSystemPrincipal(aPrincipal
)) {
865 nsCOMPtr
<nsIURI
> uri
;
866 aPrincipal
->GetURI(getter_AddRefs(uri
));
867 NS_ENSURE_TRUE(uri
, false);
869 bool isChrome
= false;
870 return NS_SUCCEEDED(uri
->SchemeIs("chrome", &isChrome
)) && isChrome
;
874 nsXBLService::LoadBindingDocumentInfo(nsIContent
* aBoundElement
,
875 nsIDocument
* aBoundDocument
,
877 nsIPrincipal
* aOriginPrincipal
,
879 nsXBLDocumentInfo
** aResult
)
881 NS_PRECONDITION(aBindingURI
, "Must have a binding URI");
882 NS_PRECONDITION(!aOriginPrincipal
|| aBoundDocument
,
883 "If we're doing a security check, we better have a document!");
886 if (aOriginPrincipal
) {
887 // Security check - Enforce same-origin policy, except to chrome.
888 // We have to be careful to not pass aContent as the context here.
889 // Otherwise, if there is a JS-implemented content policy, we will attempt
890 // to wrap the content node, which will try to load XBL bindings for it, if
891 // any. Since we're not done loading this binding yet, that will reenter
892 // this method and we'll end up creating a binding and then immediately
893 // clobbering it in our table. That makes things very confused, leading to
894 // misbehavior and crashes.
895 rv
= nsContentUtils::
896 CheckSecurityBeforeLoad(aBindingURI
, aOriginPrincipal
,
897 nsIScriptSecurityManager::ALLOW_CHROME
,
899 nsIContentPolicy::TYPE_XBL
,
901 NS_ENSURE_SUCCESS(rv
, NS_ERROR_XBL_BLOCKED
);
903 if (!IsSystemOrChromeURLPrincipal(aOriginPrincipal
)) {
904 // Also make sure that we're same-origin with the bound document
905 // except if the stylesheet has the system principal.
906 if (!(gAllowDataURIs
&& SchemeIs(aBindingURI
, "data")) &&
907 !SchemeIs(aBindingURI
, "chrome")) {
908 rv
= aBoundDocument
->NodePrincipal()->CheckMayLoad(aBindingURI
,
910 NS_ENSURE_SUCCESS(rv
, NS_ERROR_XBL_BLOCKED
);
913 // Finally check if this document is allowed to use XBL at all.
914 NS_ENSURE_TRUE(aBoundDocument
->AllowXULXBL(),
915 NS_ERROR_XBL_BLOCKED
);
920 nsRefPtr
<nsXBLDocumentInfo
> info
;
922 nsCOMPtr
<nsIURI
> documentURI
;
923 rv
= aBindingURI
->CloneIgnoringRef(getter_AddRefs(documentURI
));
924 NS_ENSURE_SUCCESS(rv
, rv
);
927 // We've got a file. Check our XBL document cache.
928 nsXULPrototypeCache
* cache
= nsXULPrototypeCache::GetInstance();
929 bool useXULCache
= cache
&& cache
->IsEnabled();
932 // The first line of defense is the chrome cache.
933 // This cache crosses the entire product, so that any XBL bindings that are
934 // part of chrome will be reused across all XUL documents.
935 info
= cache
->GetXBLDocumentInfo(documentURI
);
940 // The second line of defense is the binding manager's document table.
941 nsBindingManager
*bindingManager
= nullptr;
943 if (aBoundDocument
) {
944 bindingManager
= aBoundDocument
->BindingManager();
945 info
= bindingManager
->GetXBLDocumentInfo(documentURI
);
946 if (aBoundDocument
->IsStaticDocument() &&
947 IsChromeOrResourceURI(aBindingURI
)) {
948 aForceSyncLoad
= true;
952 NodeInfo
*ni
= nullptr;
954 ni
= aBoundElement
->NodeInfo();
956 if (!info
&& bindingManager
&&
957 (!ni
|| !(ni
->Equals(nsGkAtoms::scrollbar
, kNameSpaceID_XUL
) ||
958 ni
->Equals(nsGkAtoms::thumb
, kNameSpaceID_XUL
) ||
959 ((ni
->Equals(nsGkAtoms::input
) ||
960 ni
->Equals(nsGkAtoms::select
)) &&
961 aBoundElement
->IsHTML()))) && !aForceSyncLoad
) {
962 // The third line of defense is to investigate whether or not the
963 // document is currently being loaded asynchronously. If so, there's no
964 // document yet, but we need to glom on our request so that it will be
965 // processed whenever the doc does finish loading.
966 nsCOMPtr
<nsIStreamListener
> listener
;
968 listener
= bindingManager
->GetLoadingDocListener(documentURI
);
970 nsXBLStreamListener
* xblListener
=
971 static_cast<nsXBLStreamListener
*>(listener
.get());
972 // Create a new load observer.
973 if (!xblListener
->HasRequest(aBindingURI
, aBoundElement
)) {
974 nsXBLBindingRequest
* req
= new nsXBLBindingRequest(aBindingURI
, aBoundElement
);
975 xblListener
->AddRequest(req
);
982 // Next, look in the startup cache
983 bool useStartupCache
= useXULCache
&& IsChromeOrResourceURI(documentURI
);
984 if (!info
&& useStartupCache
) {
985 rv
= nsXBLDocumentInfo::ReadPrototypeBindings(documentURI
, getter_AddRefs(info
));
986 if (NS_SUCCEEDED(rv
)) {
987 cache
->PutXBLDocumentInfo(info
);
989 if (bindingManager
) {
990 // Cache it in our binding manager's document table.
991 bindingManager
->PutXBLDocumentInfo(info
);
998 // Finally, if all lines of defense fail, we go and fetch the binding
1001 // Always load chrome synchronously
1003 if (NS_SUCCEEDED(documentURI
->SchemeIs("chrome", &chrome
)) && chrome
)
1004 aForceSyncLoad
= true;
1006 nsCOMPtr
<nsIDocument
> document
;
1007 FetchBindingDocument(aBoundElement
, aBoundDocument
, documentURI
,
1008 aBindingURI
, aOriginPrincipal
, aForceSyncLoad
,
1009 getter_AddRefs(document
));
1012 nsBindingManager
*xblDocBindingManager
= document
->BindingManager();
1013 info
= xblDocBindingManager
->GetXBLDocumentInfo(documentURI
);
1015 NS_ERROR("An XBL file is malformed. Did you forget the XBL namespace on the bindings tag?");
1016 return NS_ERROR_FAILURE
;
1018 xblDocBindingManager
->RemoveXBLDocumentInfo(info
); // Break the self-imposed cycle.
1020 // If the doc is a chrome URI, then we put it into the XUL cache.
1022 if (useStartupCache
) {
1023 cache
->PutXBLDocumentInfo(info
);
1025 // now write the bindings into the startup cache
1026 info
->WritePrototypeBindings();
1030 if (bindingManager
) {
1031 // Also put it in our binding manager's document table.
1032 bindingManager
->PutXBLDocumentInfo(info
);
1038 info
.forget(aResult
);
1044 nsXBLService::FetchBindingDocument(nsIContent
* aBoundElement
, nsIDocument
* aBoundDocument
,
1045 nsIURI
* aDocumentURI
, nsIURI
* aBindingURI
,
1046 nsIPrincipal
* aOriginPrincipal
, bool aForceSyncLoad
,
1047 nsIDocument
** aResult
)
1049 nsresult rv
= NS_OK
;
1050 // Initialize our out pointer to nullptr
1053 // Now we have to synchronously load the binding file.
1054 // Create an XML content sink and a parser.
1055 nsCOMPtr
<nsILoadGroup
> loadGroup
;
1057 loadGroup
= aBoundDocument
->GetDocumentLoadGroup();
1059 // We really shouldn't have to force a sync load for anything here... could
1060 // we get away with not doing that? Not sure.
1061 if (IsChromeOrResourceURI(aDocumentURI
))
1062 aForceSyncLoad
= true;
1064 // Create document and contentsink and set them up.
1065 nsCOMPtr
<nsIDocument
> doc
;
1066 rv
= NS_NewXMLDocument(getter_AddRefs(doc
));
1067 NS_ENSURE_SUCCESS(rv
, rv
);
1069 nsCOMPtr
<nsIXMLContentSink
> xblSink
;
1070 rv
= NS_NewXBLContentSink(getter_AddRefs(xblSink
), doc
, aDocumentURI
, nullptr);
1071 NS_ENSURE_SUCCESS(rv
, rv
);
1074 // Note: There are some cases where aOriginPrincipal and aBoundDocument are purposely
1075 // set to null (to bypass security checks) when calling LoadBindingDocumentInfo() which calls
1076 // FetchBindingDocument(). LoadInfo will end up with no principal or node in those cases,
1077 // so we use systemPrincipal. This achieves the same result of bypassing security checks,
1078 // but it gives the wrong information to potential future consumers of loadInfo.
1079 nsCOMPtr
<nsIChannel
> channel
;
1081 if (aOriginPrincipal
) {
1082 // if there is an originPrincipal we should also have aBoundDocument
1083 NS_ASSERTION(aBoundDocument
, "can not create a channel without aBoundDocument");
1084 rv
= NS_NewChannelWithTriggeringPrincipal(getter_AddRefs(channel
),
1088 nsILoadInfo::SEC_NORMAL
,
1089 nsIContentPolicy::TYPE_OTHER
,
1093 rv
= NS_NewChannel(getter_AddRefs(channel
),
1095 nsContentUtils::GetSystemPrincipal(),
1096 nsILoadInfo::SEC_NORMAL
,
1097 nsIContentPolicy::TYPE_OTHER
,
1101 NS_ENSURE_SUCCESS(rv
, rv
);
1103 nsCOMPtr
<nsIInterfaceRequestor
> sameOriginChecker
= nsContentUtils::GetSameOriginChecker();
1104 NS_ENSURE_TRUE(sameOriginChecker
, NS_ERROR_OUT_OF_MEMORY
);
1106 channel
->SetNotificationCallbacks(sameOriginChecker
);
1108 if (!aForceSyncLoad
) {
1109 // We can be asynchronous
1110 nsXBLStreamListener
* xblListener
=
1111 new nsXBLStreamListener(aBoundDocument
, xblSink
, doc
);
1112 NS_ENSURE_TRUE(xblListener
,NS_ERROR_OUT_OF_MEMORY
);
1114 // Add ourselves to the list of loading docs.
1115 nsBindingManager
*bindingManager
;
1117 bindingManager
= aBoundDocument
->BindingManager();
1119 bindingManager
= nullptr;
1122 bindingManager
->PutLoadingDocListener(aDocumentURI
, xblListener
);
1125 nsXBLBindingRequest
* req
= new nsXBLBindingRequest(aBindingURI
,
1127 xblListener
->AddRequest(req
);
1129 // Now kick off the async read.
1130 rv
= channel
->AsyncOpen(xblListener
, nullptr);
1131 if (NS_FAILED(rv
)) {
1132 // Well, we won't be getting a load. Make sure to clean up our stuff!
1133 if (bindingManager
) {
1134 bindingManager
->RemoveLoadingDocListener(aDocumentURI
);
1140 nsCOMPtr
<nsIStreamListener
> listener
;
1141 rv
= doc
->StartDocumentLoad("loadAsInteractiveData",
1145 getter_AddRefs(listener
),
1148 NS_ENSURE_SUCCESS(rv
, rv
);
1150 // Now do a blocking synchronous parse of the file.
1151 nsCOMPtr
<nsIInputStream
> in
;
1152 rv
= channel
->Open(getter_AddRefs(in
));
1153 NS_ENSURE_SUCCESS(rv
, rv
);
1155 rv
= nsSyncLoadService::PushSyncStreamToListener(in
, listener
, channel
);
1156 NS_ENSURE_SUCCESS(rv
, rv
);