dom/xbl/nsXBLService.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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"
     8 #include "nsCOMPtr.h"
     9 #include "nsNetUtil.h"
    10 #include "nsXBLService.h"
    11 #include "nsXBLWindowKeyHandler.h"
    12 #include "nsIInputStream.h"
    13 #include "nsNameSpaceManager.h"
    14 #include "nsIURI.h"
    15 #include "nsIDOMElement.h"
    16 #include "nsIURL.h"
    17 #include "nsIChannel.h"
    18 #include "nsXPIDLString.h"
    19 #include "plstr.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"
    33 #include "nsCRT.h"
    34 #include "nsContentUtils.h"
    35 #include "nsSyncLoadService.h"
    36 #include "nsContentPolicyUtils.h"
    37 #include "nsTArray.h"
    38 #include "nsError.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"
    48 #ifdef MOZ_XUL
    49 #include "nsXULPrototypeCache.h"
    50 #endif
    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;
    65 static bool
    66 IsAncestorBinding(nsIDocument* aDocument,
    67                   nsIURI* aChildBindingURI,
    68                   nsIContent* aChild)
    69 {
    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();
    76        bindingParent;
    77        bindingParent = bindingParent->GetBindingParent()) {
    78     nsXBLBinding* binding = bindingParent->GetXBLBinding();
    79     if (!binding) {
    80       continue;
    81     }
    83     if (binding->PrototypeBinding()->CompareBindingURI(aChildBindingURI)) {
    84       ++bindingRecursion;
    85       if (bindingRecursion < NS_MAX_XBL_BINDING_RECURSION) {
    86         continue;
    87       }
    88       nsAutoCString spec;
    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));
    97       return true;
    98     }
    99   }
   101   return false;
   102 }
   104 // Individual binding requests.
   105 class nsXBLBindingRequest
   106 {
   107 public:
   108   nsCOMPtr<nsIURI> mBindingURI;
   109   nsCOMPtr<nsIContent> mBoundElement;
   111   void DocumentLoaded(nsIDocument* aBindingDoc)
   112   {
   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();
   116     if (!doc)
   117       return;
   119     // Get the binding.
   120     bool ready = false;
   121     nsXBLService::GetInstance()->BindingReady(mBoundElement, mBindingURI, &ready);
   122     if (!ready)
   123       return;
   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
   133     // will happen.
   134     nsIPresShell *shell = doc->GetShell();
   135     if (shell) {
   136       nsIFrame* childFrame = mBoundElement->GetPrimaryFrame();
   137       if (!childFrame) {
   138         // Check to see if it's in the undisplayed content map.
   139         nsStyleContext* sc =
   140           shell->FrameManager()->GetUndisplayedContent(mBoundElement);
   142         if (!sc) {
   143           shell->RecreateFramesFor(mBoundElement);
   144         }
   145       }
   146     }
   147   }
   149   nsXBLBindingRequest(nsIURI* aURI, nsIContent* aBoundElement)
   150     : mBindingURI(aURI),
   151       mBoundElement(aBoundElement)
   152   {
   153   }
   154 };
   156 // nsXBLStreamListener, a helper class used for
   157 // asynchronous parsing of URLs
   158 /* Header file */
   159 class nsXBLStreamListener MOZ_FINAL : public nsIStreamListener,
   160                                       public nsIDOMEventListener
   161 {
   162 public:
   163   NS_DECL_ISUPPORTS
   164   NS_DECL_NSISTREAMLISTENER
   165   NS_DECL_NSIREQUESTOBSERVER
   166   NS_DECL_NSIDOMEVENTLISTENER
   168   nsXBLStreamListener(nsIDocument* aBoundDocument,
   169                       nsIXMLContentSink* aSink,
   170                       nsIDocument* aBindingDocument);
   171   ~nsXBLStreamListener();
   173   void AddRequest(nsXBLBindingRequest* aRequest) { mBindingRequests.AppendElement(aRequest); }
   174   bool HasRequest(nsIURI* aURI, nsIContent* aBoundElement);
   176 private:
   177   nsCOMPtr<nsIStreamListener> mInner;
   178   nsAutoTArray<nsXBLBindingRequest*, 8> mBindingRequests;
   180   nsCOMPtr<nsIWeakReference> mBoundDocument;
   181   nsCOMPtr<nsIXMLContentSink> mSink; // Only set until OnStartRequest
   182   nsCOMPtr<nsIDocument> mBindingDocument; // Only set until OnStartRequest
   183 };
   185 /* Implementation file */
   186 NS_IMPL_ISUPPORTS(nsXBLStreamListener,
   187                   nsIStreamListener,
   188                   nsIRequestObserver,
   189                   nsIDOMEventListener)
   191 nsXBLStreamListener::nsXBLStreamListener(nsIDocument* aBoundDocument,
   192                                          nsIXMLContentSink* aSink,
   193                                          nsIDocument* aBindingDocument)
   194 : mSink(aSink), mBindingDocument(aBindingDocument)
   195 {
   196   /* member initializers and constructor code */
   197   mBoundDocument = do_GetWeakReference(aBoundDocument);
   198 }
   200 nsXBLStreamListener::~nsXBLStreamListener()
   201 {
   202   for (uint32_t i = 0; i < mBindingRequests.Length(); i++) {
   203     nsXBLBindingRequest* req = mBindingRequests.ElementAt(i);
   204     delete req;
   205   }
   206 }
   208 NS_IMETHODIMP
   209 nsXBLStreamListener::OnDataAvailable(nsIRequest *request, nsISupports* aCtxt,
   210                                      nsIInputStream* aInStr,
   211                                      uint64_t aSourceOffset, uint32_t aCount)
   212 {
   213   if (mInner)
   214     return mInner->OnDataAvailable(request, aCtxt, aInStr, aSourceOffset, aCount);
   215   return NS_ERROR_FAILURE;
   216 }
   218 NS_IMETHODIMP
   219 nsXBLStreamListener::OnStartRequest(nsIRequest* request, nsISupports* aCtxt)
   220 {
   221   // Make sure we don't hold on to the sink and binding document past this point
   222   nsCOMPtr<nsIXMLContentSink> sink;
   223   mSink.swap(sink);
   224   nsCOMPtr<nsIDocument> doc;
   225   mBindingDocument.swap(doc);
   227   nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
   228   NS_ENSURE_TRUE(channel, NS_ERROR_UNEXPECTED);
   230   nsCOMPtr<nsILoadGroup> group;
   231   request->GetLoadGroup(getter_AddRefs(group));
   233   nsresult rv = doc->StartDocumentLoad("loadAsInteractiveData",
   234                                        channel,
   235                                        group,
   236                                        nullptr,
   237                                        getter_AddRefs(mInner),
   238                                        true,
   239                                        sink);
   240   NS_ENSURE_SUCCESS(rv, rv);
   242   // Make sure to add ourselves as a listener after StartDocumentLoad,
   243   // since that resets the event listners on the document.
   244   doc->AddEventListener(NS_LITERAL_STRING("load"), this, false);
   246   return mInner->OnStartRequest(request, aCtxt);
   247 }
   249 NS_IMETHODIMP
   250 nsXBLStreamListener::OnStopRequest(nsIRequest* request, nsISupports* aCtxt, nsresult aStatus)
   251 {
   252   nsresult rv = NS_OK;
   253   if (mInner) {
   254      rv = mInner->OnStopRequest(request, aCtxt, aStatus);
   255   }
   257   // Don't hold onto the inner listener; holding onto it can create a cycle
   258   // with the document
   259   mInner = nullptr;
   261   return rv;
   262 }
   264 bool
   265 nsXBLStreamListener::HasRequest(nsIURI* aURI, nsIContent* aElt)
   266 {
   267   // XXX Could be more efficient.
   268   uint32_t count = mBindingRequests.Length();
   269   for (uint32_t i = 0; i < count; i++) {
   270     nsXBLBindingRequest* req = mBindingRequests.ElementAt(i);
   271     bool eq;
   272     if (req->mBoundElement == aElt &&
   273         NS_SUCCEEDED(req->mBindingURI->Equals(aURI, &eq)) && eq)
   274       return true;
   275   }
   277   return false;
   278 }
   280 nsresult
   281 nsXBLStreamListener::HandleEvent(nsIDOMEvent* aEvent)
   282 {
   283   nsresult rv = NS_OK;
   284   uint32_t i;
   285   uint32_t count = mBindingRequests.Length();
   287   // Get the binding document; note that we don't hold onto it in this object
   288   // to avoid creating a cycle
   289   Event* event = aEvent->InternalDOMEvent();
   290   EventTarget* target = event->GetCurrentTarget();
   291   nsCOMPtr<nsIDocument> bindingDocument = do_QueryInterface(target);
   292   NS_ASSERTION(bindingDocument, "Event not targeted at document?!");
   294   // See if we're still alive.
   295   nsCOMPtr<nsIDocument> doc(do_QueryReferent(mBoundDocument));
   296   if (!doc) {
   297     NS_WARNING("XBL load did not complete until after document went away! Modal dialog bug?\n");
   298   }
   299   else {
   300     // We have to do a flush prior to notification of the document load.
   301     // This has to happen since the HTML content sink can be holding on
   302     // to notifications related to our children (e.g., if you bind to the
   303     // <body> tag) that result in duplication of content.
   304     // We need to get the sink's notifications flushed and then make the binding
   305     // ready.
   306     if (count > 0) {
   307       nsXBLBindingRequest* req = mBindingRequests.ElementAt(0);
   308       nsIDocument* document = req->mBoundElement->GetCurrentDoc();
   309       if (document)
   310         document->FlushPendingNotifications(Flush_ContentAndNotify);
   311     }
   313     // Remove ourselves from the set of pending docs.
   314     nsBindingManager *bindingManager = doc->BindingManager();
   315     nsIURI* documentURI = bindingDocument->GetDocumentURI();
   316     bindingManager->RemoveLoadingDocListener(documentURI);
   318     if (!bindingDocument->GetRootElement()) {
   319       // FIXME: How about an error console warning?
   320       NS_WARNING("XBL doc with no root element - this usually shouldn't happen");
   321       return NS_ERROR_FAILURE;
   322     }
   324     // Put our doc info in the doc table.
   325     nsBindingManager *xblDocBindingManager = bindingDocument->BindingManager();
   326     nsRefPtr<nsXBLDocumentInfo> info =
   327       xblDocBindingManager->GetXBLDocumentInfo(documentURI);
   328     xblDocBindingManager->RemoveXBLDocumentInfo(info); // Break the self-imposed cycle.
   329     if (!info) {
   330       if (nsXBLService::IsChromeOrResourceURI(documentURI)) {
   331         NS_WARNING("An XBL file is malformed. Did you forget the XBL namespace on the bindings tag?");
   332       }
   333       nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
   334                                       NS_LITERAL_CSTRING("XBL"), nullptr,
   335                                       nsContentUtils::eXBL_PROPERTIES,
   336                                       "MalformedXBL",
   337                                       nullptr, 0, documentURI);
   338       return NS_ERROR_FAILURE;
   339     }
   341     // If the doc is a chrome URI, then we put it into the XUL cache.
   342 #ifdef MOZ_XUL
   343     if (nsXBLService::IsChromeOrResourceURI(documentURI)) {
   344       nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
   345       if (cache && cache->IsEnabled())
   346         cache->PutXBLDocumentInfo(info);
   347     }
   348 #endif
   350     bindingManager->PutXBLDocumentInfo(info);
   352     // Notify all pending requests that their bindings are
   353     // ready and can be installed.
   354     for (i = 0; i < count; i++) {
   355       nsXBLBindingRequest* req = mBindingRequests.ElementAt(i);
   356       req->DocumentLoaded(bindingDocument);
   357     }
   358   }
   360   target->RemoveEventListener(NS_LITERAL_STRING("load"), this, false);
   362   return rv;
   363 }
   365 // Implementation /////////////////////////////////////////////////////////////////
   367 // Static member variable initialization
   368 bool nsXBLService::gAllowDataURIs = false;
   370 // Implement our nsISupports methods
   371 NS_IMPL_ISUPPORTS(nsXBLService, nsISupportsWeakReference)
   373 void
   374 nsXBLService::Init()
   375 {
   376   gInstance = new nsXBLService();
   377   NS_ADDREF(gInstance);
   378 }
   380 // Constructors/Destructors
   381 nsXBLService::nsXBLService(void)
   382 {
   383   Preferences::AddBoolVarCache(&gAllowDataURIs, "layout.debug.enable_data_xbl");
   384 }
   386 nsXBLService::~nsXBLService(void)
   387 {
   388 }
   390 // static
   391 bool
   392 nsXBLService::IsChromeOrResourceURI(nsIURI* aURI)
   393 {
   394   bool isChrome = false;
   395   bool isResource = false;
   396   if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &isChrome)) &&
   397       NS_SUCCEEDED(aURI->SchemeIs("resource", &isResource)))
   398       return (isChrome || isResource);
   399   return false;
   400 }
   403 // This function loads a particular XBL file and installs all of the bindings
   404 // onto the element.
   405 nsresult
   406 nsXBLService::LoadBindings(nsIContent* aContent, nsIURI* aURL,
   407                            nsIPrincipal* aOriginPrincipal,
   408                            nsXBLBinding** aBinding, bool* aResolveStyle)
   409 {
   410   NS_PRECONDITION(aOriginPrincipal, "Must have an origin principal");
   412   *aBinding = nullptr;
   413   *aResolveStyle = false;
   415   nsresult rv;
   417   nsCOMPtr<nsIDocument> document = aContent->OwnerDoc();
   419   nsAutoCString urlspec;
   420   if (nsContentUtils::GetWrapperSafeScriptFilename(document, aURL, urlspec)) {
   421     // Block an attempt to load a binding that has special wrapper
   422     // automation needs.
   424     return NS_OK;
   425   }
   427   nsXBLBinding *binding = aContent->GetXBLBinding();
   428   if (binding) {
   429     if (binding->MarkedForDeath()) {
   430       FlushStyleBindings(aContent);
   431       binding = nullptr;
   432     }
   433     else {
   434       // See if the URIs match.
   435       if (binding->PrototypeBinding()->CompareBindingURI(aURL))
   436         return NS_OK;
   437       FlushStyleBindings(aContent);
   438       binding = nullptr;
   439     }
   440   }
   442   bool ready;
   443   nsRefPtr<nsXBLBinding> newBinding;
   444   if (NS_FAILED(rv = GetBinding(aContent, aURL, false, aOriginPrincipal,
   445                                 &ready, getter_AddRefs(newBinding)))) {
   446     return rv;
   447   }
   449   if (!newBinding) {
   450 #ifdef DEBUG
   451     nsAutoCString spec;
   452     aURL->GetSpec(spec);
   453     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);
   454     NS_ERROR(str.get());
   455 #endif
   456     return NS_OK;
   457   }
   459   if (::IsAncestorBinding(document, aURL, aContent)) {
   460     return NS_ERROR_ILLEGAL_VALUE;
   461   }
   463   // We loaded a style binding.  It goes on the end.
   464   if (binding) {
   465     // Get the last binding that is in the append layer.
   466     binding->RootBinding()->SetBaseBinding(newBinding);
   467   }
   468   else {
   469     // Install the binding on the content node.
   470     aContent->SetXBLBinding(newBinding);
   471   }
   473   {
   474     nsAutoScriptBlocker scriptBlocker;
   476     // Set the binding's bound element.
   477     newBinding->SetBoundElement(aContent);
   479     // Tell the binding to build the anonymous content.
   480     newBinding->GenerateAnonymousContent();
   482     // Tell the binding to install event handlers
   483     newBinding->InstallEventHandlers();
   485     // Set up our properties
   486     rv = newBinding->InstallImplementation();
   487     NS_ENSURE_SUCCESS(rv, rv);
   489     // Figure out if we have any scoped sheets.  If so, we do a second resolve.
   490     *aResolveStyle = newBinding->HasStyleSheets();
   492     newBinding.swap(*aBinding);
   493   }
   495   return NS_OK;
   496 }
   498 nsresult
   499 nsXBLService::FlushStyleBindings(nsIContent* aContent)
   500 {
   501   nsCOMPtr<nsIDocument> document = aContent->OwnerDoc();
   503   nsXBLBinding *binding = aContent->GetXBLBinding();
   504   if (binding) {
   505     // Clear out the script references.
   506     binding->ChangeDocument(document, nullptr);
   508     aContent->SetXBLBinding(nullptr); // Flush old style bindings
   509   }
   511   return NS_OK;
   512 }
   514 //
   515 // AttachGlobalKeyHandler
   516 //
   517 // Creates a new key handler and prepares to listen to key events on the given
   518 // event receiver (either a document or an content node). If the receiver is content,
   519 // then extra work needs to be done to hook it up to the document (XXX WHY??)
   520 //
   521 nsresult
   522 nsXBLService::AttachGlobalKeyHandler(EventTarget* aTarget)
   523 {
   524   // check if the receiver is a content node (not a document), and hook
   525   // it to the document if that is the case.
   526   nsCOMPtr<EventTarget> piTarget = aTarget;
   527   nsCOMPtr<nsIContent> contentNode(do_QueryInterface(aTarget));
   528   if (contentNode) {
   529     // Only attach if we're really in a document
   530     nsCOMPtr<nsIDocument> doc = contentNode->GetCurrentDoc();
   531     if (doc)
   532       piTarget = doc; // We're a XUL keyset. Attach to our document.
   533   }
   535   EventListenerManager* manager = piTarget->GetOrCreateListenerManager();
   537   if (!piTarget || !manager)
   538     return NS_ERROR_FAILURE;
   540   // the listener already exists, so skip this
   541   if (contentNode && contentNode->GetProperty(nsGkAtoms::listener))
   542     return NS_OK;
   544   nsCOMPtr<nsIDOMElement> elt(do_QueryInterface(contentNode));
   546   // Create the key handler
   547   nsRefPtr<nsXBLWindowKeyHandler> handler =
   548     NS_NewXBLWindowKeyHandler(elt, piTarget);
   550   // listen to these events
   551   manager->AddEventListenerByType(handler, NS_LITERAL_STRING("keydown"),
   552                                   TrustedEventsAtSystemGroupBubble());
   553   manager->AddEventListenerByType(handler, NS_LITERAL_STRING("keyup"),
   554                                   TrustedEventsAtSystemGroupBubble());
   555   manager->AddEventListenerByType(handler, NS_LITERAL_STRING("keypress"),
   556                                   TrustedEventsAtSystemGroupBubble());
   558   // The capturing listener is only used for XUL keysets to properly handle
   559   // shortcut keys in a multi-process environment.
   560   manager->AddEventListenerByType(handler, NS_LITERAL_STRING("keydown"),
   561                                   TrustedEventsAtSystemGroupCapture());
   562   manager->AddEventListenerByType(handler, NS_LITERAL_STRING("keyup"),
   563                                   TrustedEventsAtSystemGroupCapture());
   564   manager->AddEventListenerByType(handler, NS_LITERAL_STRING("keypress"),
   565                                   TrustedEventsAtSystemGroupCapture());
   567   if (contentNode)
   568     return contentNode->SetProperty(nsGkAtoms::listener,
   569                                     handler.forget().take(),
   570                                     nsPropertyTable::SupportsDtorFunc, true);
   572   // The reference to the handler will be maintained by the event target,
   573   // and, if there is a content node, the property.
   574   return NS_OK;
   575 }
   577 //
   578 // DetachGlobalKeyHandler
   579 //
   580 // Removes a key handler added by DeatchGlobalKeyHandler.
   581 //
   582 nsresult
   583 nsXBLService::DetachGlobalKeyHandler(EventTarget* aTarget)
   584 {
   585   nsCOMPtr<EventTarget> piTarget = aTarget;
   586   nsCOMPtr<nsIContent> contentNode(do_QueryInterface(aTarget));
   587   if (!contentNode) // detaching is only supported for content nodes
   588     return NS_ERROR_FAILURE;
   590   // Only attach if we're really in a document
   591   nsCOMPtr<nsIDocument> doc = contentNode->GetCurrentDoc();
   592   if (doc)
   593     piTarget = do_QueryInterface(doc);
   595   EventListenerManager* manager = piTarget->GetOrCreateListenerManager();
   597   if (!piTarget || !manager)
   598     return NS_ERROR_FAILURE;
   600   nsIDOMEventListener* handler =
   601     static_cast<nsIDOMEventListener*>(contentNode->GetProperty(nsGkAtoms::listener));
   602   if (!handler)
   603     return NS_ERROR_FAILURE;
   605   manager->RemoveEventListenerByType(handler, NS_LITERAL_STRING("keydown"),
   606                                      TrustedEventsAtSystemGroupBubble());
   607   manager->RemoveEventListenerByType(handler, NS_LITERAL_STRING("keyup"),
   608                                      TrustedEventsAtSystemGroupBubble());
   609   manager->RemoveEventListenerByType(handler, NS_LITERAL_STRING("keypress"),
   610                                      TrustedEventsAtSystemGroupBubble());
   612   manager->RemoveEventListenerByType(handler, NS_LITERAL_STRING("keydown"),
   613                                      TrustedEventsAtSystemGroupCapture());
   614   manager->RemoveEventListenerByType(handler, NS_LITERAL_STRING("keyup"),
   615                                      TrustedEventsAtSystemGroupCapture());
   616   manager->RemoveEventListenerByType(handler, NS_LITERAL_STRING("keypress"),
   617                                      TrustedEventsAtSystemGroupCapture());
   619   contentNode->DeleteProperty(nsGkAtoms::listener);
   621   return NS_OK;
   622 }
   624 // Internal helper methods ////////////////////////////////////////////////////////////////
   626 nsresult
   627 nsXBLService::BindingReady(nsIContent* aBoundElement,
   628                            nsIURI* aURI,
   629                            bool* aIsReady)
   630 {
   631   // Don't do a security check here; we know this binding is set to go.
   632   return GetBinding(aBoundElement, aURI, true, nullptr, aIsReady, nullptr);
   633 }
   635 nsresult
   636 nsXBLService::GetBinding(nsIContent* aBoundElement, nsIURI* aURI,
   637                          bool aPeekOnly, nsIPrincipal* aOriginPrincipal,
   638                          bool* aIsReady, nsXBLBinding** aResult)
   639 {
   640   // More than 6 binding URIs are rare, see bug 55070 comment 18.
   641   nsAutoTArray<nsIURI*, 6> uris;
   642   return GetBinding(aBoundElement, aURI, aPeekOnly, aOriginPrincipal, aIsReady,
   643                     aResult, uris);
   644 }
   646 nsresult
   647 nsXBLService::GetBinding(nsIContent* aBoundElement, nsIURI* aURI,
   648                          bool aPeekOnly, nsIPrincipal* aOriginPrincipal,
   649                          bool* aIsReady, nsXBLBinding** aResult,
   650                          nsTArray<nsIURI*>& aDontExtendURIs)
   651 {
   652   NS_ASSERTION(aPeekOnly || aResult,
   653                "Must have non-null out param if not just peeking to see "
   654                "whether the binding is ready");
   656   if (aResult)
   657     *aResult = nullptr;
   659   if (!aURI)
   660     return NS_ERROR_FAILURE;
   662   nsAutoCString ref;
   663   aURI->GetRef(ref);
   665   nsCOMPtr<nsIDocument> boundDocument = aBoundElement->OwnerDoc();
   667   nsRefPtr<nsXBLDocumentInfo> docInfo;
   668   nsresult rv = LoadBindingDocumentInfo(aBoundElement, boundDocument, aURI,
   669                                         aOriginPrincipal,
   670                                         false, getter_AddRefs(docInfo));
   671   NS_ENSURE_SUCCESS(rv, rv);
   673   if (!docInfo)
   674     return NS_ERROR_FAILURE;
   676   nsXBLPrototypeBinding* protoBinding = docInfo->GetPrototypeBinding(ref);
   678   if (!protoBinding) {
   679 #ifdef DEBUG
   680     nsAutoCString uriSpec;
   681     aURI->GetSpec(uriSpec);
   682     nsAutoCString doc;
   683     boundDocument->GetDocumentURI()->GetSpec(doc);
   684     nsAutoCString message("Unable to locate an XBL binding for URI ");
   685     message += uriSpec;
   686     message += " in document ";
   687     message += doc;
   688     NS_WARNING(message.get());
   689 #endif
   690     return NS_ERROR_FAILURE;
   691   }
   693   NS_ENSURE_TRUE(aDontExtendURIs.AppendElement(protoBinding->BindingURI()),
   694                  NS_ERROR_OUT_OF_MEMORY);
   695   nsCOMPtr<nsIURI> altBindingURI = protoBinding->AlternateBindingURI();
   696   if (altBindingURI) {
   697     NS_ENSURE_TRUE(aDontExtendURIs.AppendElement(altBindingURI),
   698                    NS_ERROR_OUT_OF_MEMORY);
   699   }
   701   // Our prototype binding must have all its resources loaded.
   702   bool ready = protoBinding->LoadResources();
   703   if (!ready) {
   704     // Add our bound element to the protos list of elts that should
   705     // be notified when the stylesheets and scripts finish loading.
   706     protoBinding->AddResourceListener(aBoundElement);
   707     return NS_ERROR_FAILURE; // The binding isn't ready yet.
   708   }
   710   rv = protoBinding->ResolveBaseBinding();
   711   NS_ENSURE_SUCCESS(rv, rv);
   713   nsIURI* baseBindingURI;
   714   nsXBLPrototypeBinding* baseProto = protoBinding->GetBasePrototype();
   715   if (baseProto) {
   716     baseBindingURI = baseProto->BindingURI();
   717   }
   718   else {
   719     baseBindingURI = protoBinding->GetBaseBindingURI();
   720     if (baseBindingURI) {
   721       uint32_t count = aDontExtendURIs.Length();
   722       for (uint32_t index = 0; index < count; ++index) {
   723         bool equal;
   724         rv = aDontExtendURIs[index]->Equals(baseBindingURI, &equal);
   725         NS_ENSURE_SUCCESS(rv, rv);
   726         if (equal) {
   727           nsAutoCString spec, basespec;
   728           protoBinding->BindingURI()->GetSpec(spec);
   729           NS_ConvertUTF8toUTF16 protoSpec(spec);
   730           baseBindingURI->GetSpec(basespec);
   731           NS_ConvertUTF8toUTF16 baseSpecUTF16(basespec);
   732           const char16_t* params[] = { protoSpec.get(), baseSpecUTF16.get() };
   733           nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
   734                                           NS_LITERAL_CSTRING("XBL"), nullptr,
   735                                           nsContentUtils::eXBL_PROPERTIES,
   736                                           "CircularExtendsBinding",
   737                                           params, ArrayLength(params),
   738                                           boundDocument->GetDocumentURI());
   739           return NS_ERROR_ILLEGAL_VALUE;
   740         }
   741       }
   742     }
   743   }
   745   nsRefPtr<nsXBLBinding> baseBinding;
   746   if (baseBindingURI) {
   747     nsIContent* child = protoBinding->GetBindingElement();
   748     rv = GetBinding(aBoundElement, baseBindingURI, aPeekOnly,
   749                     child->NodePrincipal(), aIsReady,
   750                     getter_AddRefs(baseBinding), aDontExtendURIs);
   751     if (NS_FAILED(rv))
   752       return rv; // We aren't ready yet.
   753   }
   755   *aIsReady = true;
   757   if (!aPeekOnly) {
   758     // Make a new binding
   759     nsXBLBinding *newBinding = new nsXBLBinding(protoBinding);
   760     NS_ENSURE_TRUE(newBinding, NS_ERROR_OUT_OF_MEMORY);
   762     if (baseBinding) {
   763       if (!baseProto) {
   764         protoBinding->SetBasePrototype(baseBinding->PrototypeBinding());
   765       }
   766        newBinding->SetBaseBinding(baseBinding);
   767     }
   769     NS_ADDREF(*aResult = newBinding);
   770   }
   772   return NS_OK;
   773 }
   775 static bool SchemeIs(nsIURI* aURI, const char* aScheme)
   776 {
   777   nsCOMPtr<nsIURI> baseURI = NS_GetInnermostURI(aURI);
   778   NS_ENSURE_TRUE(baseURI, false);
   780   bool isScheme = false;
   781   return NS_SUCCEEDED(baseURI->SchemeIs(aScheme, &isScheme)) && isScheme;
   782 }
   784 static bool
   785 IsSystemOrChromeURLPrincipal(nsIPrincipal* aPrincipal)
   786 {
   787   if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
   788     return true;
   789   }
   791   nsCOMPtr<nsIURI> uri;
   792   aPrincipal->GetURI(getter_AddRefs(uri));
   793   NS_ENSURE_TRUE(uri, false);
   795   bool isChrome = false;
   796   return NS_SUCCEEDED(uri->SchemeIs("chrome", &isChrome)) && isChrome;
   797 }
   799 nsresult
   800 nsXBLService::LoadBindingDocumentInfo(nsIContent* aBoundElement,
   801                                       nsIDocument* aBoundDocument,
   802                                       nsIURI* aBindingURI,
   803                                       nsIPrincipal* aOriginPrincipal,
   804                                       bool aForceSyncLoad,
   805                                       nsXBLDocumentInfo** aResult)
   806 {
   807   NS_PRECONDITION(aBindingURI, "Must have a binding URI");
   808   NS_PRECONDITION(!aOriginPrincipal || aBoundDocument,
   809                   "If we're doing a security check, we better have a document!");
   811   nsresult rv;
   812   if (aOriginPrincipal) {
   813     // Security check - Enforce same-origin policy, except to chrome.
   814     // We have to be careful to not pass aContent as the context here.
   815     // Otherwise, if there is a JS-implemented content policy, we will attempt
   816     // to wrap the content node, which will try to load XBL bindings for it, if
   817     // any. Since we're not done loading this binding yet, that will reenter
   818     // this method and we'll end up creating a binding and then immediately
   819     // clobbering it in our table.  That makes things very confused, leading to
   820     // misbehavior and crashes.
   821     rv = nsContentUtils::
   822       CheckSecurityBeforeLoad(aBindingURI, aOriginPrincipal,
   823                               nsIScriptSecurityManager::ALLOW_CHROME,
   824                               gAllowDataURIs,
   825                               nsIContentPolicy::TYPE_XBL,
   826                               aBoundDocument);
   827     NS_ENSURE_SUCCESS(rv, NS_ERROR_XBL_BLOCKED);
   829     if (!IsSystemOrChromeURLPrincipal(aOriginPrincipal)) {
   830       // Also make sure that we're same-origin with the bound document
   831       // except if the stylesheet has the system principal.
   832       if (!(gAllowDataURIs && SchemeIs(aBindingURI, "data")) &&
   833           !SchemeIs(aBindingURI, "chrome")) {
   834         rv = aBoundDocument->NodePrincipal()->CheckMayLoad(aBindingURI,
   835                                                            true, false);
   836         NS_ENSURE_SUCCESS(rv, NS_ERROR_XBL_BLOCKED);
   837       }
   839       // Finally check if this document is allowed to use XBL at all.
   840       NS_ENSURE_TRUE(aBoundDocument->AllowXULXBL(),
   841                      NS_ERROR_XBL_BLOCKED);
   842     }
   843   }
   845   *aResult = nullptr;
   846   nsRefPtr<nsXBLDocumentInfo> info;
   848   nsCOMPtr<nsIURI> documentURI;
   849   rv = aBindingURI->CloneIgnoringRef(getter_AddRefs(documentURI));
   850   NS_ENSURE_SUCCESS(rv, rv);
   852 #ifdef MOZ_XUL
   853   // We've got a file.  Check our XBL document cache.
   854   nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
   855   bool useXULCache = cache && cache->IsEnabled();
   857   if (useXULCache) {
   858     // The first line of defense is the chrome cache.
   859     // This cache crosses the entire product, so that any XBL bindings that are
   860     // part of chrome will be reused across all XUL documents.
   861     info = cache->GetXBLDocumentInfo(documentURI);
   862   }
   863 #endif
   865   if (!info) {
   866     // The second line of defense is the binding manager's document table.
   867     nsBindingManager *bindingManager = nullptr;
   869     if (aBoundDocument) {
   870       bindingManager = aBoundDocument->BindingManager();
   871       info = bindingManager->GetXBLDocumentInfo(documentURI);
   872       if (aBoundDocument->IsStaticDocument() &&
   873           IsChromeOrResourceURI(aBindingURI)) {
   874         aForceSyncLoad = true;
   875       }
   876     }
   878     nsINodeInfo *ni = nullptr;
   879     if (aBoundElement)
   880       ni = aBoundElement->NodeInfo();
   882     if (!info && bindingManager &&
   883         (!ni || !(ni->Equals(nsGkAtoms::scrollbar, kNameSpaceID_XUL) ||
   884                   ni->Equals(nsGkAtoms::thumb, kNameSpaceID_XUL) ||
   885                   ((ni->Equals(nsGkAtoms::input) ||
   886                     ni->Equals(nsGkAtoms::select)) &&
   887                    aBoundElement->IsHTML()))) && !aForceSyncLoad) {
   888       // The third line of defense is to investigate whether or not the
   889       // document is currently being loaded asynchronously.  If so, there's no
   890       // document yet, but we need to glom on our request so that it will be
   891       // processed whenever the doc does finish loading.
   892       nsCOMPtr<nsIStreamListener> listener;
   893       if (bindingManager)
   894         listener = bindingManager->GetLoadingDocListener(documentURI);
   895       if (listener) {
   896         nsXBLStreamListener* xblListener =
   897           static_cast<nsXBLStreamListener*>(listener.get());
   898         // Create a new load observer.
   899         if (!xblListener->HasRequest(aBindingURI, aBoundElement)) {
   900           nsXBLBindingRequest* req = new nsXBLBindingRequest(aBindingURI, aBoundElement);
   901           xblListener->AddRequest(req);
   902         }
   903         return NS_OK;
   904       }
   905     }
   907 #ifdef MOZ_XUL
   908     // Next, look in the startup cache
   909     bool useStartupCache = useXULCache && IsChromeOrResourceURI(documentURI);
   910     if (!info && useStartupCache) {
   911       rv = nsXBLDocumentInfo::ReadPrototypeBindings(documentURI, getter_AddRefs(info));
   912       if (NS_SUCCEEDED(rv)) {
   913         cache->PutXBLDocumentInfo(info);
   915         if (bindingManager) {
   916           // Cache it in our binding manager's document table.
   917           bindingManager->PutXBLDocumentInfo(info);
   918         }
   919       }
   920     }
   921 #endif
   923     if (!info) {
   924       // Finally, if all lines of defense fail, we go and fetch the binding
   925       // document.
   927       // Always load chrome synchronously
   928       bool chrome;
   929       if (NS_SUCCEEDED(documentURI->SchemeIs("chrome", &chrome)) && chrome)
   930         aForceSyncLoad = true;
   932       nsCOMPtr<nsIDocument> document;
   933       FetchBindingDocument(aBoundElement, aBoundDocument, documentURI,
   934                            aBindingURI, aForceSyncLoad, getter_AddRefs(document));
   936       if (document) {
   937         nsBindingManager *xblDocBindingManager = document->BindingManager();
   938         info = xblDocBindingManager->GetXBLDocumentInfo(documentURI);
   939         if (!info) {
   940           NS_ERROR("An XBL file is malformed.  Did you forget the XBL namespace on the bindings tag?");
   941           return NS_ERROR_FAILURE;
   942         }
   943         xblDocBindingManager->RemoveXBLDocumentInfo(info); // Break the self-imposed cycle.
   945         // If the doc is a chrome URI, then we put it into the XUL cache.
   946 #ifdef MOZ_XUL
   947         if (useStartupCache) {
   948           cache->PutXBLDocumentInfo(info);
   950           // now write the bindings into the startup cache
   951           info->WritePrototypeBindings();
   952         }
   953 #endif
   955         if (bindingManager) {
   956           // Also put it in our binding manager's document table.
   957           bindingManager->PutXBLDocumentInfo(info);
   958         }
   959       }
   960     }
   961   }
   963   info.forget(aResult);
   965   return NS_OK;
   966 }
   968 nsresult
   969 nsXBLService::FetchBindingDocument(nsIContent* aBoundElement, nsIDocument* aBoundDocument,
   970                                    nsIURI* aDocumentURI, nsIURI* aBindingURI,
   971                                    bool aForceSyncLoad, nsIDocument** aResult)
   972 {
   973   nsresult rv = NS_OK;
   974   // Initialize our out pointer to nullptr
   975   *aResult = nullptr;
   977   // Now we have to synchronously load the binding file.
   978   // Create an XML content sink and a parser.
   979   nsCOMPtr<nsILoadGroup> loadGroup;
   980   if (aBoundDocument)
   981     loadGroup = aBoundDocument->GetDocumentLoadGroup();
   983   // We really shouldn't have to force a sync load for anything here... could
   984   // we get away with not doing that?  Not sure.
   985   if (IsChromeOrResourceURI(aDocumentURI))
   986     aForceSyncLoad = true;
   988   // Create document and contentsink and set them up.
   989   nsCOMPtr<nsIDocument> doc;
   990   rv = NS_NewXMLDocument(getter_AddRefs(doc));
   991   NS_ENSURE_SUCCESS(rv, rv);
   993   nsCOMPtr<nsIXMLContentSink> xblSink;
   994   rv = NS_NewXBLContentSink(getter_AddRefs(xblSink), doc, aDocumentURI, nullptr);
   995   NS_ENSURE_SUCCESS(rv, rv);
   997   // Open channel
   998   nsCOMPtr<nsIChannel> channel;
   999   rv = NS_NewChannel(getter_AddRefs(channel), aDocumentURI, nullptr, loadGroup);
  1000   NS_ENSURE_SUCCESS(rv, rv);
  1002   nsCOMPtr<nsIInterfaceRequestor> sameOriginChecker = nsContentUtils::GetSameOriginChecker();
  1003   NS_ENSURE_TRUE(sameOriginChecker, NS_ERROR_OUT_OF_MEMORY);
  1005   channel->SetNotificationCallbacks(sameOriginChecker);
  1007   if (!aForceSyncLoad) {
  1008     // We can be asynchronous
  1009     nsXBLStreamListener* xblListener =
  1010       new nsXBLStreamListener(aBoundDocument, xblSink, doc);
  1011     NS_ENSURE_TRUE(xblListener,NS_ERROR_OUT_OF_MEMORY);
  1013     // Add ourselves to the list of loading docs.
  1014     nsBindingManager *bindingManager;
  1015     if (aBoundDocument)
  1016       bindingManager = aBoundDocument->BindingManager();
  1017     else
  1018       bindingManager = nullptr;
  1020     if (bindingManager)
  1021       bindingManager->PutLoadingDocListener(aDocumentURI, xblListener);
  1023     // Add our request.
  1024     nsXBLBindingRequest* req = new nsXBLBindingRequest(aBindingURI,
  1025                                                        aBoundElement);
  1026     xblListener->AddRequest(req);
  1028     // Now kick off the async read.
  1029     rv = channel->AsyncOpen(xblListener, nullptr);
  1030     if (NS_FAILED(rv)) {
  1031       // Well, we won't be getting a load.  Make sure to clean up our stuff!
  1032       if (bindingManager) {
  1033         bindingManager->RemoveLoadingDocListener(aDocumentURI);
  1036     return NS_OK;
  1039   nsCOMPtr<nsIStreamListener> listener;
  1040   rv = doc->StartDocumentLoad("loadAsInteractiveData",
  1041                               channel,
  1042                               loadGroup,
  1043                               nullptr,
  1044                               getter_AddRefs(listener),
  1045                               true,
  1046                               xblSink);
  1047   NS_ENSURE_SUCCESS(rv, rv);
  1049   // Now do a blocking synchronous parse of the file.
  1050   nsCOMPtr<nsIInputStream> in;
  1051   rv = channel->Open(getter_AddRefs(in));
  1052   NS_ENSURE_SUCCESS(rv, rv);
  1054   rv = nsSyncLoadService::PushSyncStreamToListener(in, listener, channel);
  1055   NS_ENSURE_SUCCESS(rv, rv);
  1057   doc.swap(*aResult);
  1059   return NS_OK;

mercurial