mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-28 23:31:56 +00:00
2febd96e7e
This is a best effort attempt at ensuring that the adverse impact of reformatting the entire tree over the comments would be minimal. I've used a combination of strategies including disabling of formatting, some manual formatting and some changes to formatting to work around some clang-format limitations. Differential Revision: https://phabricator.services.mozilla.com/D13073 --HG-- extra : moz-landing-system : lando
1193 lines
38 KiB
C++
1193 lines
38 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "mozilla/ArrayUtils.h"
|
|
#include "mozilla/ComputedStyle.h"
|
|
|
|
#include "nsCOMPtr.h"
|
|
#include "nsNetUtil.h"
|
|
#include "nsXBLService.h"
|
|
#include "nsXBLWindowKeyHandler.h"
|
|
#include "nsIInputStream.h"
|
|
#include "nsNameSpaceManager.h"
|
|
#include "nsIURI.h"
|
|
#include "nsIURL.h"
|
|
#include "nsIChannel.h"
|
|
#include "nsString.h"
|
|
#include "plstr.h"
|
|
#include "nsIContent.h"
|
|
#include "nsIDocument.h"
|
|
#include "nsIXMLContentSink.h"
|
|
#include "nsContentCID.h"
|
|
#include "mozilla/dom/XMLDocument.h"
|
|
#include "nsGkAtoms.h"
|
|
#include "nsIObserverService.h"
|
|
#include "nsXBLContentSink.h"
|
|
#include "nsXBLBinding.h"
|
|
#include "nsXBLPrototypeBinding.h"
|
|
#include "nsXBLDocumentInfo.h"
|
|
#include "nsCRT.h"
|
|
#include "nsContentUtils.h"
|
|
#include "nsSyncLoadService.h"
|
|
#include "nsContentPolicyUtils.h"
|
|
#include "nsTArray.h"
|
|
#include "nsError.h"
|
|
|
|
#include "nsIPresShell.h"
|
|
#include "nsIDocumentObserver.h"
|
|
#include "nsFrameManager.h"
|
|
#include "nsIScriptSecurityManager.h"
|
|
#include "nsIScriptError.h"
|
|
#include "nsXBLSerialize.h"
|
|
|
|
#ifdef MOZ_XUL
|
|
#include "nsXULPrototypeCache.h"
|
|
#endif
|
|
#include "nsIDOMEventListener.h"
|
|
#include "mozilla/Attributes.h"
|
|
#include "mozilla/EventListenerManager.h"
|
|
#include "mozilla/Preferences.h"
|
|
#include "mozilla/ServoStyleSet.h"
|
|
#include "mozilla/RestyleManager.h"
|
|
#include "mozilla/dom/ChildIterator.h"
|
|
#include "mozilla/dom/Event.h"
|
|
#include "mozilla/dom/Element.h"
|
|
|
|
using namespace mozilla;
|
|
using namespace mozilla::dom;
|
|
|
|
#define NS_MAX_XBL_BINDING_RECURSION 20
|
|
|
|
nsXBLService* nsXBLService::gInstance = nullptr;
|
|
|
|
static bool
|
|
IsAncestorBinding(nsIDocument* aDocument,
|
|
nsIURI* aChildBindingURI,
|
|
nsIContent* aChild)
|
|
{
|
|
NS_ASSERTION(aDocument, "expected a document");
|
|
NS_ASSERTION(aChildBindingURI, "expected a binding URI");
|
|
NS_ASSERTION(aChild, "expected a child content");
|
|
|
|
uint32_t bindingRecursion = 0;
|
|
for (nsIContent *bindingParent = aChild->GetBindingParent();
|
|
bindingParent;
|
|
bindingParent = bindingParent->GetBindingParent()) {
|
|
nsXBLBinding* binding = bindingParent->GetXBLBinding();
|
|
if (!binding) {
|
|
continue;
|
|
}
|
|
|
|
if (binding->PrototypeBinding()->CompareBindingURI(aChildBindingURI)) {
|
|
++bindingRecursion;
|
|
if (bindingRecursion < NS_MAX_XBL_BINDING_RECURSION) {
|
|
continue;
|
|
}
|
|
NS_ConvertUTF8toUTF16 bindingURI(aChildBindingURI->GetSpecOrDefault());
|
|
const char16_t* params[] = { bindingURI.get() };
|
|
nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
|
|
NS_LITERAL_CSTRING("XBL"), aDocument,
|
|
nsContentUtils::eXBL_PROPERTIES,
|
|
"TooDeepBindingRecursion",
|
|
params, ArrayLength(params));
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Individual binding requests.
|
|
class nsXBLBindingRequest
|
|
{
|
|
public:
|
|
nsCOMPtr<nsIURI> mBindingURI;
|
|
nsCOMPtr<nsIContent> mBoundElement;
|
|
|
|
void DocumentLoaded(nsIDocument* aBindingDoc)
|
|
{
|
|
// We only need the document here to cause frame construction, so
|
|
// we need the current doc, not the owner doc.
|
|
nsIDocument* doc = mBoundElement->GetUncomposedDoc();
|
|
if (!doc)
|
|
return;
|
|
|
|
// Get the binding.
|
|
bool ready = false;
|
|
nsXBLService::GetInstance()->BindingReady(mBoundElement, mBindingURI, &ready);
|
|
if (!ready)
|
|
return;
|
|
|
|
// Destroy the frames for mBoundElement. Do this after getting the binding,
|
|
// since if the binding fetch fails then we don't want to destroy the
|
|
// frames.
|
|
if (nsIPresShell* shell = doc->GetShell()) {
|
|
shell->DestroyFramesForAndRestyle(mBoundElement->AsElement());
|
|
}
|
|
MOZ_ASSERT(!mBoundElement->GetPrimaryFrame());
|
|
}
|
|
|
|
nsXBLBindingRequest(nsIURI* aURI, nsIContent* aBoundElement)
|
|
: mBindingURI(aURI),
|
|
mBoundElement(aBoundElement)
|
|
{
|
|
}
|
|
};
|
|
|
|
// nsXBLStreamListener, a helper class used for
|
|
// asynchronous parsing of URLs
|
|
/* Header file */
|
|
class nsXBLStreamListener final : public nsIStreamListener,
|
|
public nsIDOMEventListener
|
|
{
|
|
public:
|
|
NS_DECL_ISUPPORTS
|
|
NS_DECL_NSISTREAMLISTENER
|
|
NS_DECL_NSIREQUESTOBSERVER
|
|
NS_DECL_NSIDOMEVENTLISTENER
|
|
|
|
nsXBLStreamListener(nsIDocument* aBoundDocument,
|
|
nsIXMLContentSink* aSink,
|
|
nsIDocument* aBindingDocument);
|
|
|
|
void AddRequest(nsXBLBindingRequest* aRequest) { mBindingRequests.AppendElement(aRequest); }
|
|
bool HasRequest(nsIURI* aURI, nsIContent* aBoundElement);
|
|
|
|
private:
|
|
~nsXBLStreamListener();
|
|
|
|
nsCOMPtr<nsIStreamListener> mInner;
|
|
AutoTArray<nsXBLBindingRequest*, 8> mBindingRequests;
|
|
|
|
nsWeakPtr mBoundDocument;
|
|
nsCOMPtr<nsIXMLContentSink> mSink; // Only set until OnStartRequest
|
|
nsCOMPtr<nsIDocument> mBindingDocument; // Only set until OnStartRequest
|
|
};
|
|
|
|
/* Implementation file */
|
|
NS_IMPL_ISUPPORTS(nsXBLStreamListener,
|
|
nsIStreamListener,
|
|
nsIRequestObserver,
|
|
nsIDOMEventListener)
|
|
|
|
nsXBLStreamListener::nsXBLStreamListener(nsIDocument* aBoundDocument,
|
|
nsIXMLContentSink* aSink,
|
|
nsIDocument* aBindingDocument)
|
|
: mSink(aSink), mBindingDocument(aBindingDocument)
|
|
{
|
|
/* member initializers and constructor code */
|
|
mBoundDocument = do_GetWeakReference(aBoundDocument);
|
|
}
|
|
|
|
nsXBLStreamListener::~nsXBLStreamListener()
|
|
{
|
|
for (uint32_t i = 0; i < mBindingRequests.Length(); i++) {
|
|
nsXBLBindingRequest* req = mBindingRequests.ElementAt(i);
|
|
delete req;
|
|
}
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsXBLStreamListener::OnDataAvailable(nsIRequest *request, nsISupports* aCtxt,
|
|
nsIInputStream* aInStr,
|
|
uint64_t aSourceOffset, uint32_t aCount)
|
|
{
|
|
if (mInner)
|
|
return mInner->OnDataAvailable(request, aCtxt, aInStr, aSourceOffset, aCount);
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsXBLStreamListener::OnStartRequest(nsIRequest* request, nsISupports* aCtxt)
|
|
{
|
|
// Make sure we don't hold on to the sink and binding document past this point
|
|
nsCOMPtr<nsIXMLContentSink> sink;
|
|
mSink.swap(sink);
|
|
nsCOMPtr<nsIDocument> doc;
|
|
mBindingDocument.swap(doc);
|
|
|
|
nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
|
|
NS_ENSURE_TRUE(channel, NS_ERROR_UNEXPECTED);
|
|
|
|
nsCOMPtr<nsILoadGroup> group;
|
|
request->GetLoadGroup(getter_AddRefs(group));
|
|
|
|
nsresult rv = doc->StartDocumentLoad("loadAsInteractiveData",
|
|
channel,
|
|
group,
|
|
nullptr,
|
|
getter_AddRefs(mInner),
|
|
true,
|
|
sink);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// Make sure to add ourselves as a listener after StartDocumentLoad,
|
|
// since that resets the event listners on the document.
|
|
doc->AddEventListener(NS_LITERAL_STRING("load"), this, false);
|
|
|
|
return mInner->OnStartRequest(request, aCtxt);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsXBLStreamListener::OnStopRequest(nsIRequest* request, nsISupports* aCtxt, nsresult aStatus)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
if (mInner) {
|
|
rv = mInner->OnStopRequest(request, aCtxt, aStatus);
|
|
}
|
|
|
|
// Don't hold onto the inner listener; holding onto it can create a cycle
|
|
// with the document
|
|
mInner = nullptr;
|
|
|
|
return rv;
|
|
}
|
|
|
|
bool
|
|
nsXBLStreamListener::HasRequest(nsIURI* aURI, nsIContent* aElt)
|
|
{
|
|
// XXX Could be more efficient.
|
|
uint32_t count = mBindingRequests.Length();
|
|
for (uint32_t i = 0; i < count; i++) {
|
|
nsXBLBindingRequest* req = mBindingRequests.ElementAt(i);
|
|
bool eq;
|
|
if (req->mBoundElement == aElt &&
|
|
NS_SUCCEEDED(req->mBindingURI->Equals(aURI, &eq)) && eq)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
nsresult
|
|
nsXBLStreamListener::HandleEvent(Event* aEvent)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
uint32_t i;
|
|
uint32_t count = mBindingRequests.Length();
|
|
|
|
// Get the binding document; note that we don't hold onto it in this object
|
|
// to avoid creating a cycle
|
|
EventTarget* target = aEvent->GetCurrentTarget();
|
|
nsCOMPtr<nsIDocument> bindingDocument = do_QueryInterface(target);
|
|
NS_ASSERTION(bindingDocument, "Event not targeted at document?!");
|
|
|
|
// See if we're still alive.
|
|
nsCOMPtr<nsIDocument> doc(do_QueryReferent(mBoundDocument));
|
|
if (!doc) {
|
|
NS_WARNING("XBL load did not complete until after document went away! Modal dialog bug?\n");
|
|
}
|
|
else {
|
|
// We have to do a flush prior to notification of the document load.
|
|
// This has to happen since the HTML content sink can be holding on
|
|
// to notifications related to our children (e.g., if you bind to the
|
|
// <body> tag) that result in duplication of content.
|
|
// We need to get the sink's notifications flushed and then make the binding
|
|
// ready.
|
|
if (count > 0) {
|
|
nsXBLBindingRequest* req = mBindingRequests.ElementAt(0);
|
|
nsIDocument* document = req->mBoundElement->GetUncomposedDoc();
|
|
if (document)
|
|
document->FlushPendingNotifications(FlushType::ContentAndNotify);
|
|
}
|
|
|
|
// Remove ourselves from the set of pending docs.
|
|
nsBindingManager *bindingManager = doc->BindingManager();
|
|
nsIURI* documentURI = bindingDocument->GetDocumentURI();
|
|
bindingManager->RemoveLoadingDocListener(documentURI);
|
|
|
|
if (!bindingDocument->GetRootElement()) {
|
|
// FIXME: How about an error console warning?
|
|
NS_WARNING("XBL doc with no root element - this usually shouldn't happen");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
// Put our doc info in the doc table.
|
|
nsBindingManager *xblDocBindingManager = bindingDocument->BindingManager();
|
|
RefPtr<nsXBLDocumentInfo> info =
|
|
xblDocBindingManager->GetXBLDocumentInfo(documentURI);
|
|
xblDocBindingManager->RemoveXBLDocumentInfo(info); // Break the self-imposed cycle.
|
|
if (!info) {
|
|
if (nsXBLService::IsChromeOrResourceURI(documentURI)) {
|
|
NS_WARNING("An XBL file is malformed. Did you forget the XBL namespace on the bindings tag?");
|
|
}
|
|
nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
|
|
NS_LITERAL_CSTRING("XBL"), nullptr,
|
|
nsContentUtils::eXBL_PROPERTIES,
|
|
"MalformedXBL",
|
|
nullptr, 0, documentURI);
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
// If the doc is a chrome URI, then we put it into the XUL cache.
|
|
#ifdef MOZ_XUL
|
|
if (nsXBLService::IsChromeOrResourceURI(documentURI)) {
|
|
nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
|
|
if (cache && cache->IsEnabled())
|
|
cache->PutXBLDocumentInfo(info);
|
|
}
|
|
#endif
|
|
|
|
bindingManager->PutXBLDocumentInfo(info);
|
|
|
|
// Notify all pending requests that their bindings are
|
|
// ready and can be installed.
|
|
for (i = 0; i < count; i++) {
|
|
nsXBLBindingRequest* req = mBindingRequests.ElementAt(i);
|
|
req->DocumentLoaded(bindingDocument);
|
|
}
|
|
}
|
|
|
|
target->RemoveEventListener(NS_LITERAL_STRING("load"), this, false);
|
|
|
|
return rv;
|
|
}
|
|
|
|
// Implementation //////////////////////////////////////////////////////////////
|
|
|
|
// Implement our nsISupports methods
|
|
NS_IMPL_ISUPPORTS(nsXBLService, nsISupportsWeakReference)
|
|
|
|
void
|
|
nsXBLService::Init()
|
|
{
|
|
gInstance = new nsXBLService();
|
|
NS_ADDREF(gInstance);
|
|
}
|
|
|
|
// Constructors/Destructors
|
|
nsXBLService::nsXBLService(void)
|
|
{
|
|
}
|
|
|
|
nsXBLService::~nsXBLService(void)
|
|
{
|
|
}
|
|
|
|
// static
|
|
bool
|
|
nsXBLService::IsChromeOrResourceURI(nsIURI* aURI)
|
|
{
|
|
bool isChrome = false;
|
|
bool isResource = false;
|
|
if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &isChrome)) &&
|
|
NS_SUCCEEDED(aURI->SchemeIs("resource", &isResource)))
|
|
return (isChrome || isResource);
|
|
return false;
|
|
}
|
|
|
|
// Servo avoids wasting work styling subtrees of elements with XBL bindings by
|
|
// default, so whenever we leave LoadBindings in a way that doesn't guarantee
|
|
// that the subtree is styled we need to take care of doing it manually.
|
|
static void
|
|
EnsureSubtreeStyled(Element* aElement)
|
|
{
|
|
if (!aElement->HasServoData()) {
|
|
return;
|
|
}
|
|
|
|
if (Servo_Element_IsDisplayNone(aElement)) {
|
|
return;
|
|
}
|
|
|
|
nsIPresShell* presShell = aElement->OwnerDoc()->GetShell();
|
|
if (!presShell || !presShell->DidInitialize()) {
|
|
return;
|
|
}
|
|
|
|
ServoStyleSet* servoSet = presShell->StyleSet();
|
|
StyleChildrenIterator iter(aElement);
|
|
for (nsIContent* child = iter.GetNextChild();
|
|
child;
|
|
child = iter.GetNextChild()) {
|
|
Element* element = Element::FromNode(child);
|
|
if (!element) {
|
|
continue;
|
|
}
|
|
|
|
if (element->HasServoData()) {
|
|
// If any child was styled, all of them should be styled already, so we
|
|
// can bail out.
|
|
return;
|
|
}
|
|
|
|
servoSet->StyleNewSubtree(element);
|
|
}
|
|
}
|
|
|
|
// Ensures that EnsureSubtreeStyled is called on the element on destruction.
|
|
class MOZ_RAII AutoEnsureSubtreeStyled
|
|
{
|
|
public:
|
|
explicit AutoEnsureSubtreeStyled(Element* aElement)
|
|
: mElement(aElement)
|
|
{
|
|
}
|
|
|
|
~AutoEnsureSubtreeStyled()
|
|
{
|
|
EnsureSubtreeStyled(mElement);
|
|
}
|
|
|
|
private:
|
|
Element* mElement;
|
|
};
|
|
|
|
// RAII class to restyle the XBL bound element when it shuffles the flat tree.
|
|
class MOZ_RAII AutoStyleElement
|
|
{
|
|
public:
|
|
AutoStyleElement(Element* aElement, bool* aResolveStyle)
|
|
: mElement(aElement)
|
|
, mHadData(aElement->HasServoData())
|
|
, mResolveStyle(aResolveStyle)
|
|
{
|
|
MOZ_ASSERT(mResolveStyle);
|
|
if (mHadData) {
|
|
RestyleManager::ClearServoDataFromSubtree(
|
|
mElement, RestyleManager::IncludeRoot::No);
|
|
}
|
|
}
|
|
|
|
~AutoStyleElement()
|
|
{
|
|
nsIPresShell* presShell = mElement->OwnerDoc()->GetShell();
|
|
if (!mHadData || !presShell || !presShell->DidInitialize()) {
|
|
return;
|
|
}
|
|
|
|
if (*mResolveStyle) {
|
|
mElement->ClearServoData();
|
|
|
|
ServoStyleSet* servoSet = presShell->StyleSet();
|
|
servoSet->StyleNewSubtree(mElement);
|
|
}
|
|
}
|
|
|
|
private:
|
|
Element* mElement;
|
|
bool mHadData;
|
|
bool* mResolveStyle;
|
|
};
|
|
|
|
static bool
|
|
IsSystemOrChromeURLPrincipal(nsIPrincipal* aPrincipal)
|
|
{
|
|
if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
|
|
return true;
|
|
}
|
|
|
|
nsCOMPtr<nsIURI> uri;
|
|
aPrincipal->GetURI(getter_AddRefs(uri));
|
|
NS_ENSURE_TRUE(uri, false);
|
|
|
|
bool isChrome = false;
|
|
return NS_SUCCEEDED(uri->SchemeIs("chrome", &isChrome)) && isChrome;
|
|
}
|
|
|
|
// This function loads a particular XBL file and installs all of the bindings
|
|
// onto the element.
|
|
nsresult
|
|
nsXBLService::LoadBindings(Element* aElement, nsIURI* aURL,
|
|
nsIPrincipal* aOriginPrincipal,
|
|
nsXBLBinding** aBinding, bool* aResolveStyle)
|
|
{
|
|
MOZ_ASSERT(aOriginPrincipal, "Must have an origin principal");
|
|
|
|
*aBinding = nullptr;
|
|
*aResolveStyle = false;
|
|
|
|
AutoEnsureSubtreeStyled subtreeStyled(aElement);
|
|
|
|
if (MOZ_UNLIKELY(!aURL)) {
|
|
return NS_OK;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
// Ensures that only the whitelisted bindings are used in the following
|
|
// conditions:
|
|
//
|
|
// 1) In the content process
|
|
// 2) In a document that disallows XUL/XBL which only loads bindings
|
|
// referenced in a chrome stylesheet.
|
|
//
|
|
// If the conditions are met, assert that:
|
|
//
|
|
// a) The binding is XMLPrettyPrint (since it may be bound to any XML)
|
|
// b) The binding is bound to one of the whitelisted element.
|
|
//
|
|
// The assertion might not catch all violations because (2) is needed
|
|
// for the current test setup. Someone may unknownly using a binding
|
|
// in AllowXULXBL() documents in content process in production without
|
|
// knowing.
|
|
if (XRE_IsContentProcess() &&
|
|
IsSystemOrChromeURLPrincipal(aOriginPrincipal) &&
|
|
aElement->OwnerDoc() && !aElement->OwnerDoc()->AllowXULXBL() &&
|
|
!aURL->GetSpecOrDefault().EqualsLiteral(
|
|
"chrome://global/content/xml/XMLPrettyPrint.xml#prettyprint")) {
|
|
nsAtom* tag = aElement->NodeInfo()->NameAtom();
|
|
MOZ_ASSERT(
|
|
// datetimebox
|
|
tag == nsGkAtoms::datetimebox ||
|
|
// videocontrols
|
|
tag == nsGkAtoms::videocontrols ||
|
|
// pluginProblem
|
|
tag == nsGkAtoms::embed ||
|
|
tag == nsGkAtoms::applet ||
|
|
tag == nsGkAtoms::object ||
|
|
// xbl-marquee
|
|
tag == nsGkAtoms::marquee,
|
|
"Unexpected XBL binding used in the content process"
|
|
);
|
|
}
|
|
#endif
|
|
|
|
// Easy case: The binding was already loaded.
|
|
nsXBLBinding* binding = aElement->GetXBLBinding();
|
|
if (binding && !binding->MarkedForDeath() &&
|
|
binding->PrototypeBinding()->CompareBindingURI(aURL)) {
|
|
return NS_OK;
|
|
}
|
|
|
|
nsCOMPtr<nsIDocument> document = aElement->OwnerDoc();
|
|
|
|
nsAutoCString urlspec;
|
|
nsresult rv = aURL->GetSpec(urlspec);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
if (binding) {
|
|
FlushStyleBindings(aElement);
|
|
binding = nullptr;
|
|
}
|
|
|
|
bool ready;
|
|
RefPtr<nsXBLBinding> newBinding;
|
|
if (NS_FAILED(rv = GetBinding(aElement, aURL, false, aOriginPrincipal,
|
|
&ready, getter_AddRefs(newBinding)))) {
|
|
return rv;
|
|
}
|
|
|
|
if (!newBinding) {
|
|
#ifdef DEBUG
|
|
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: ") + aURL->GetSpecOrDefault());
|
|
NS_ERROR(str.get());
|
|
#endif
|
|
return NS_OK;
|
|
}
|
|
|
|
if (::IsAncestorBinding(document, aURL, aElement)) {
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
}
|
|
|
|
AutoStyleElement styleElement(aElement, aResolveStyle);
|
|
|
|
// We loaded a style binding. It goes on the end.
|
|
// Install the binding on the content node.
|
|
aElement->SetXBLBinding(newBinding);
|
|
|
|
{
|
|
nsAutoScriptBlocker scriptBlocker;
|
|
|
|
// Set the binding's bound element.
|
|
newBinding->SetBoundElement(aElement);
|
|
|
|
// Tell the binding to build the anonymous content.
|
|
newBinding->GenerateAnonymousContent();
|
|
|
|
// Tell the binding to install event handlers
|
|
newBinding->InstallEventHandlers();
|
|
|
|
// Set up our properties
|
|
rv = newBinding->InstallImplementation();
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// Figure out if we have any scoped sheets. If so, we do a second resolve.
|
|
*aResolveStyle = newBinding->HasStyleSheets();
|
|
|
|
newBinding.forget(aBinding);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
nsXBLService::FlushStyleBindings(Element* aElement)
|
|
{
|
|
nsCOMPtr<nsIDocument> document = aElement->OwnerDoc();
|
|
|
|
nsXBLBinding* binding = aElement->GetXBLBinding();
|
|
if (binding) {
|
|
// Clear out the script references.
|
|
binding->ChangeDocument(document, nullptr);
|
|
|
|
aElement->SetXBLBinding(nullptr); // Flush old style bindings
|
|
}
|
|
}
|
|
|
|
//
|
|
// AttachGlobalKeyHandler
|
|
//
|
|
// Creates a new key handler and prepares to listen to key events on the given
|
|
// event receiver (either a document or an content node). If the receiver is content,
|
|
// then extra work needs to be done to hook it up to the document (XXX WHY??)
|
|
//
|
|
nsresult
|
|
nsXBLService::AttachGlobalKeyHandler(EventTarget* aTarget)
|
|
{
|
|
// check if the receiver is a content node (not a document), and hook
|
|
// it to the document if that is the case.
|
|
nsCOMPtr<EventTarget> piTarget = aTarget;
|
|
nsCOMPtr<nsIContent> contentNode(do_QueryInterface(aTarget));
|
|
if (contentNode) {
|
|
// Only attach if we're really in a document
|
|
nsCOMPtr<nsIDocument> doc = contentNode->GetUncomposedDoc();
|
|
if (doc)
|
|
piTarget = doc; // We're a XUL keyset. Attach to our document.
|
|
}
|
|
|
|
if (!piTarget)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
EventListenerManager* manager = piTarget->GetOrCreateListenerManager();
|
|
if (!manager)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
// the listener already exists, so skip this
|
|
if (contentNode && contentNode->GetProperty(nsGkAtoms::listener))
|
|
return NS_OK;
|
|
|
|
Element* elt = Element::FromNodeOrNull(contentNode);
|
|
|
|
// Create the key handler
|
|
RefPtr<nsXBLWindowKeyHandler> handler =
|
|
NS_NewXBLWindowKeyHandler(elt, piTarget);
|
|
|
|
handler->InstallKeyboardEventListenersTo(manager);
|
|
|
|
if (contentNode)
|
|
return contentNode->SetProperty(nsGkAtoms::listener,
|
|
handler.forget().take(),
|
|
nsPropertyTable::SupportsDtorFunc, true);
|
|
|
|
// The reference to the handler will be maintained by the event target,
|
|
// and, if there is a content node, the property.
|
|
return NS_OK;
|
|
}
|
|
|
|
//
|
|
// DetachGlobalKeyHandler
|
|
//
|
|
// Removes a key handler added by DeatchGlobalKeyHandler.
|
|
//
|
|
nsresult
|
|
nsXBLService::DetachGlobalKeyHandler(EventTarget* aTarget)
|
|
{
|
|
nsCOMPtr<EventTarget> piTarget = aTarget;
|
|
nsCOMPtr<nsIContent> contentNode(do_QueryInterface(aTarget));
|
|
if (!contentNode) // detaching is only supported for content nodes
|
|
return NS_ERROR_FAILURE;
|
|
|
|
// Only attach if we're really in a document
|
|
nsCOMPtr<nsIDocument> doc = contentNode->GetUncomposedDoc();
|
|
if (doc)
|
|
piTarget = doc;
|
|
|
|
if (!piTarget)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
EventListenerManager* manager = piTarget->GetOrCreateListenerManager();
|
|
if (!manager)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
nsIDOMEventListener* handler =
|
|
static_cast<nsIDOMEventListener*>(contentNode->GetProperty(nsGkAtoms::listener));
|
|
if (!handler)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
static_cast<nsXBLWindowKeyHandler*>(handler)->
|
|
RemoveKeyboardEventListenersFrom(manager);
|
|
|
|
contentNode->DeleteProperty(nsGkAtoms::listener);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
// Internal helper methods /////////////////////////////////////////////////////
|
|
|
|
nsresult
|
|
nsXBLService::BindingReady(nsIContent* aBoundElement,
|
|
nsIURI* aURI,
|
|
bool* aIsReady)
|
|
{
|
|
// Don't do a security check here; we know this binding is set to go.
|
|
return GetBinding(aBoundElement, aURI, true, nullptr, aIsReady, nullptr);
|
|
}
|
|
|
|
nsresult
|
|
nsXBLService::GetBinding(nsIContent* aBoundElement, nsIURI* aURI,
|
|
bool aPeekOnly, nsIPrincipal* aOriginPrincipal,
|
|
bool* aIsReady, nsXBLBinding** aResult)
|
|
{
|
|
// More than 6 binding URIs are rare, see bug 55070 comment 18.
|
|
AutoTArray<nsCOMPtr<nsIURI>, 6> uris;
|
|
return GetBinding(aBoundElement, aURI, aPeekOnly, aOriginPrincipal, aIsReady,
|
|
aResult, uris);
|
|
}
|
|
|
|
static bool
|
|
MayBindToContent(nsXBLPrototypeBinding* aProtoBinding, nsIContent* aBoundElement,
|
|
nsIURI* aURI)
|
|
{
|
|
// If this binding explicitly allows untrusted content, we're done.
|
|
if (aProtoBinding->BindToUntrustedContent()) {
|
|
return true;
|
|
}
|
|
|
|
// We let XUL content and content in XUL documents through, since XUL is
|
|
// restricted anyway and we want to minimize remote XUL breakage.
|
|
if (aBoundElement->IsXULElement() ||
|
|
aBoundElement->OwnerDoc()->IsXULElement()) {
|
|
return true;
|
|
}
|
|
|
|
// Similarly, we make an exception for anonymous content (which
|
|
// lives in the XBL scope), because it's already protected from content,
|
|
// and tends to use a lot of bindings that we wouldn't otherwise need to
|
|
// whitelist.
|
|
if (aBoundElement->IsInAnonymousSubtree()) {
|
|
return true;
|
|
}
|
|
|
|
// Allow if the bound content subsumes the binding.
|
|
nsCOMPtr<nsIDocument> bindingDoc = aProtoBinding->XBLDocumentInfo()->GetDocument();
|
|
NS_ENSURE_TRUE(bindingDoc, false);
|
|
if (aBoundElement->NodePrincipal()->Subsumes(bindingDoc->NodePrincipal())) {
|
|
return true;
|
|
}
|
|
|
|
// One last special case: we need to watch out for in-document data: URI
|
|
// bindings from remote-XUL-whitelisted domains (especially tests), because
|
|
// they end up with a null principal (rather than inheriting the document's
|
|
// principal), which causes them to fail the check above.
|
|
if (nsContentUtils::AllowXULXBLForPrincipal(aBoundElement->NodePrincipal())) {
|
|
bool isDataURI = false;
|
|
nsresult rv = aURI->SchemeIs("data", &isDataURI);
|
|
NS_ENSURE_SUCCESS(rv, false);
|
|
if (isDataURI) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Disallow.
|
|
return false;
|
|
}
|
|
|
|
nsresult
|
|
nsXBLService::GetBinding(nsIContent* aBoundElement, nsIURI* aURI,
|
|
bool aPeekOnly, nsIPrincipal* aOriginPrincipal,
|
|
bool* aIsReady, nsXBLBinding** aResult,
|
|
nsTArray<nsCOMPtr<nsIURI>>& aDontExtendURIs)
|
|
{
|
|
NS_ASSERTION(aPeekOnly || aResult,
|
|
"Must have non-null out param if not just peeking to see "
|
|
"whether the binding is ready");
|
|
|
|
if (aResult)
|
|
*aResult = nullptr;
|
|
|
|
if (!aURI)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
nsAutoCString ref;
|
|
aURI->GetRef(ref);
|
|
|
|
nsCOMPtr<nsIDocument> boundDocument = aBoundElement->OwnerDoc();
|
|
|
|
RefPtr<nsXBLDocumentInfo> docInfo;
|
|
nsresult rv = LoadBindingDocumentInfo(aBoundElement, boundDocument, aURI,
|
|
aOriginPrincipal,
|
|
false, getter_AddRefs(docInfo));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
if (!docInfo)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
WeakPtr<nsXBLPrototypeBinding> protoBinding =
|
|
docInfo->GetPrototypeBinding(ref);
|
|
|
|
if (!protoBinding) {
|
|
#ifdef DEBUG
|
|
nsAutoCString message("Unable to locate an XBL binding for URI ");
|
|
message += aURI->GetSpecOrDefault();
|
|
message += " in document ";
|
|
message += boundDocument->GetDocumentURI()->GetSpecOrDefault();
|
|
NS_WARNING(message.get());
|
|
#endif
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
// If the binding isn't whitelisted, refuse to apply it to content that
|
|
// doesn't subsume it (modulo a few exceptions).
|
|
if (!MayBindToContent(protoBinding, aBoundElement, aURI)) {
|
|
#ifdef DEBUG
|
|
nsAutoCString message("Permission denied to apply binding ");
|
|
message += aURI->GetSpecOrDefault();
|
|
message += " to unprivileged content. Set bindToUntrustedContent=true on "
|
|
"the binding to override this restriction.";
|
|
NS_WARNING(message.get());
|
|
#endif
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
aDontExtendURIs.AppendElement(protoBinding->BindingURI());
|
|
nsCOMPtr<nsIURI> altBindingURI = protoBinding->AlternateBindingURI();
|
|
if (altBindingURI) {
|
|
aDontExtendURIs.AppendElement(altBindingURI);
|
|
}
|
|
|
|
// Our prototype binding must have all its resources loaded.
|
|
bool ready = protoBinding->LoadResources(aBoundElement);
|
|
if (!ready) {
|
|
// Add our bound element to the protos list of elts that should
|
|
// be notified when the stylesheets and scripts finish loading.
|
|
protoBinding->AddResourceListener(aBoundElement);
|
|
return NS_ERROR_FAILURE; // The binding isn't ready yet.
|
|
}
|
|
|
|
rv = protoBinding->ResolveBaseBinding();
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsCOMPtr<nsIURI> baseBindingURI;
|
|
WeakPtr<nsXBLPrototypeBinding> baseProto = protoBinding->GetBasePrototype();
|
|
if (baseProto) {
|
|
baseBindingURI = baseProto->BindingURI();
|
|
}
|
|
else {
|
|
baseBindingURI = protoBinding->GetBaseBindingURI();
|
|
if (baseBindingURI) {
|
|
uint32_t count = aDontExtendURIs.Length();
|
|
for (uint32_t index = 0; index < count; ++index) {
|
|
bool equal;
|
|
rv = aDontExtendURIs[index]->Equals(baseBindingURI, &equal);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
if (equal) {
|
|
NS_ConvertUTF8toUTF16
|
|
protoSpec(protoBinding->BindingURI()->GetSpecOrDefault());
|
|
NS_ConvertUTF8toUTF16 baseSpec(baseBindingURI->GetSpecOrDefault());
|
|
const char16_t* params[] = { protoSpec.get(), baseSpec.get() };
|
|
nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
|
|
NS_LITERAL_CSTRING("XBL"), nullptr,
|
|
nsContentUtils::eXBL_PROPERTIES,
|
|
"CircularExtendsBinding",
|
|
params, ArrayLength(params),
|
|
boundDocument->GetDocumentURI());
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
RefPtr<nsXBLBinding> baseBinding;
|
|
if (baseBindingURI) {
|
|
nsCOMPtr<nsIContent> child = protoBinding->GetBindingElement();
|
|
rv = GetBinding(aBoundElement, baseBindingURI, aPeekOnly,
|
|
child->NodePrincipal(), aIsReady,
|
|
getter_AddRefs(baseBinding), aDontExtendURIs);
|
|
if (NS_FAILED(rv))
|
|
return rv; // We aren't ready yet.
|
|
}
|
|
|
|
*aIsReady = true;
|
|
|
|
if (!aPeekOnly) {
|
|
// Make a new binding
|
|
NS_ENSURE_STATE(protoBinding);
|
|
nsXBLBinding *newBinding = new nsXBLBinding(protoBinding);
|
|
|
|
if (baseBinding) {
|
|
if (!baseProto) {
|
|
protoBinding->SetBasePrototype(baseBinding->PrototypeBinding());
|
|
}
|
|
newBinding->SetBaseBinding(baseBinding);
|
|
}
|
|
|
|
NS_ADDREF(*aResult = newBinding);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsXBLService::LoadBindingDocumentInfo(nsIContent* aBoundElement,
|
|
nsIDocument* aBoundDocument,
|
|
nsIURI* aBindingURI,
|
|
nsIPrincipal* aOriginPrincipal,
|
|
bool aForceSyncLoad,
|
|
nsXBLDocumentInfo** aResult)
|
|
{
|
|
MOZ_ASSERT(aBindingURI, "Must have a binding URI");
|
|
MOZ_ASSERT(!aOriginPrincipal || aBoundDocument,
|
|
"If we're doing a security check, we better have a document!");
|
|
|
|
*aResult = nullptr;
|
|
// Allow XBL in unprivileged documents if it's specified in a privileged or
|
|
// chrome: stylesheet. This allows themes to specify XBL bindings.
|
|
if (aOriginPrincipal && !IsSystemOrChromeURLPrincipal(aOriginPrincipal)) {
|
|
NS_ENSURE_TRUE(!aBoundDocument || aBoundDocument->AllowXULXBL(),
|
|
NS_ERROR_XBL_BLOCKED);
|
|
}
|
|
|
|
RefPtr<nsXBLDocumentInfo> info;
|
|
|
|
nsCOMPtr<nsIURI> documentURI;
|
|
nsresult rv = NS_GetURIWithoutRef(aBindingURI, getter_AddRefs(documentURI));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsBindingManager *bindingManager = nullptr;
|
|
|
|
// The first thing to check is the binding manager, which (if it exists)
|
|
// should have a reference to the nsXBLDocumentInfo if this document
|
|
// has ever loaded this binding before.
|
|
if (aBoundDocument) {
|
|
bindingManager = aBoundDocument->BindingManager();
|
|
info = bindingManager->GetXBLDocumentInfo(documentURI);
|
|
if (aBoundDocument->IsStaticDocument() &&
|
|
IsChromeOrResourceURI(aBindingURI)) {
|
|
aForceSyncLoad = true;
|
|
}
|
|
}
|
|
|
|
// It's possible the document is already being loaded. If so, there's no
|
|
// document yet, but we need to glom on our request so that it will be
|
|
// processed whenever the doc does finish loading.
|
|
NodeInfo *ni = nullptr;
|
|
if (aBoundElement)
|
|
ni = aBoundElement->NodeInfo();
|
|
|
|
if (!info && bindingManager &&
|
|
(!ni || !(ni->Equals(nsGkAtoms::scrollbar, kNameSpaceID_XUL) ||
|
|
ni->Equals(nsGkAtoms::thumb, kNameSpaceID_XUL) ||
|
|
((ni->Equals(nsGkAtoms::input) ||
|
|
ni->Equals(nsGkAtoms::select)) &&
|
|
aBoundElement->IsHTMLElement()))) && !aForceSyncLoad) {
|
|
nsCOMPtr<nsIStreamListener> listener;
|
|
if (bindingManager)
|
|
listener = bindingManager->GetLoadingDocListener(documentURI);
|
|
if (listener) {
|
|
nsXBLStreamListener* xblListener =
|
|
static_cast<nsXBLStreamListener*>(listener.get());
|
|
// Create a new load observer.
|
|
if (!xblListener->HasRequest(aBindingURI, aBoundElement)) {
|
|
nsXBLBindingRequest* req = new nsXBLBindingRequest(aBindingURI, aBoundElement);
|
|
xblListener->AddRequest(req);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
}
|
|
|
|
#ifdef MOZ_XUL
|
|
// The second line of defense is the global nsXULPrototypeCache,
|
|
// if it's being used.
|
|
nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
|
|
bool useXULCache = cache && cache->IsEnabled();
|
|
|
|
if (!info && useXULCache) {
|
|
// This cache crosses the entire product, so that any XBL bindings that are
|
|
// part of chrome will be reused across all XUL documents.
|
|
info = cache->GetXBLDocumentInfo(documentURI);
|
|
}
|
|
|
|
bool useStartupCache = useXULCache && IsChromeOrResourceURI(documentURI);
|
|
|
|
if (!info) {
|
|
// Next, look in the startup cache
|
|
if (!info && useStartupCache) {
|
|
rv = nsXBLDocumentInfo::ReadPrototypeBindings(documentURI, getter_AddRefs(info),
|
|
aBoundDocument);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
cache->PutXBLDocumentInfo(info);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (!info) {
|
|
// Finally, if all lines of defense fail, we go and fetch the binding
|
|
// document.
|
|
|
|
// Always load chrome synchronously
|
|
bool chrome;
|
|
if (NS_SUCCEEDED(documentURI->SchemeIs("chrome", &chrome)) && chrome)
|
|
aForceSyncLoad = true;
|
|
|
|
nsCOMPtr<nsIDocument> document;
|
|
rv = FetchBindingDocument(aBoundElement, aBoundDocument, documentURI,
|
|
aBindingURI, aOriginPrincipal, aForceSyncLoad,
|
|
getter_AddRefs(document));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
if (document) {
|
|
nsBindingManager *xblDocBindingManager = document->BindingManager();
|
|
info = xblDocBindingManager->GetXBLDocumentInfo(documentURI);
|
|
if (!info) {
|
|
NS_ERROR("An XBL file is malformed. Did you forget the XBL namespace on the bindings tag?");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
xblDocBindingManager->RemoveXBLDocumentInfo(info); // Break the self-imposed cycle.
|
|
|
|
// If the doc is a chrome URI, then we put it into the XUL cache.
|
|
#ifdef MOZ_XUL
|
|
if (useStartupCache) {
|
|
cache->PutXBLDocumentInfo(info);
|
|
|
|
// now write the bindings into the startup cache
|
|
info->WritePrototypeBindings();
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (info && bindingManager) {
|
|
// Cache it in our binding manager's document table. This way,
|
|
// we can ensure that if the document has loaded this binding
|
|
// before, it can continue to use it even if the XUL prototype
|
|
// cache gets flushed. That way, if a flush does occur, we
|
|
// don't get into a weird state where we're using different
|
|
// XBLDocumentInfos for the same XBL document in a single
|
|
// document that has loaded some bindings.
|
|
bindingManager->PutXBLDocumentInfo(info);
|
|
}
|
|
|
|
info.forget(aResult);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsXBLService::FetchBindingDocument(nsIContent* aBoundElement, nsIDocument* aBoundDocument,
|
|
nsIURI* aDocumentURI, nsIURI* aBindingURI,
|
|
nsIPrincipal* aOriginPrincipal, bool aForceSyncLoad,
|
|
nsIDocument** aResult)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
// Initialize our out pointer to nullptr
|
|
*aResult = nullptr;
|
|
|
|
// Now we have to synchronously load the binding file.
|
|
// Create an XML content sink and a parser.
|
|
nsCOMPtr<nsILoadGroup> loadGroup;
|
|
if (aBoundDocument)
|
|
loadGroup = aBoundDocument->GetDocumentLoadGroup();
|
|
|
|
// We really shouldn't have to force a sync load for anything here... could
|
|
// we get away with not doing that? Not sure.
|
|
if (IsChromeOrResourceURI(aDocumentURI))
|
|
aForceSyncLoad = true;
|
|
|
|
// Create document and contentsink and set them up.
|
|
nsCOMPtr<nsIDocument> doc;
|
|
rv = NS_NewXMLDocument(getter_AddRefs(doc));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// XBL documents must allow XUL and XBL elements in them but the usual check
|
|
// only checks if the document is loaded in the system principal which is
|
|
// sometimes not the case.
|
|
doc->ForceEnableXULXBL();
|
|
|
|
nsCOMPtr<nsIXMLContentSink> xblSink;
|
|
rv = NS_NewXBLContentSink(getter_AddRefs(xblSink), doc, aDocumentURI, nullptr);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// Open channel
|
|
// Note: There are some cases where aOriginPrincipal and aBoundDocument are purposely
|
|
// set to null (to bypass security checks) when calling LoadBindingDocumentInfo() which calls
|
|
// FetchBindingDocument(). LoadInfo will end up with no principal or node in those cases,
|
|
// so we use systemPrincipal. This achieves the same result of bypassing security checks,
|
|
// but it gives the wrong information to potential future consumers of loadInfo.
|
|
nsCOMPtr<nsIChannel> channel;
|
|
|
|
if (aOriginPrincipal) {
|
|
// if there is an originPrincipal we should also have aBoundDocument
|
|
MOZ_ASSERT(aBoundDocument, "can not create a channel without aBoundDocument");
|
|
|
|
rv = NS_NewChannelWithTriggeringPrincipal(getter_AddRefs(channel),
|
|
aDocumentURI,
|
|
aBoundDocument,
|
|
aOriginPrincipal,
|
|
nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS |
|
|
nsILoadInfo::SEC_ALLOW_CHROME,
|
|
nsIContentPolicy::TYPE_XBL,
|
|
nullptr, // aPerformanceStorage
|
|
loadGroup);
|
|
}
|
|
else {
|
|
rv = NS_NewChannel(getter_AddRefs(channel),
|
|
aDocumentURI,
|
|
nsContentUtils::GetSystemPrincipal(),
|
|
nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS,
|
|
nsIContentPolicy::TYPE_XBL,
|
|
nullptr, // PerformanceStorage
|
|
loadGroup);
|
|
}
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
if (!aForceSyncLoad) {
|
|
// We can be asynchronous
|
|
nsXBLStreamListener* xblListener =
|
|
new nsXBLStreamListener(aBoundDocument, xblSink, doc);
|
|
|
|
// Add ourselves to the list of loading docs.
|
|
nsBindingManager *bindingManager;
|
|
if (aBoundDocument)
|
|
bindingManager = aBoundDocument->BindingManager();
|
|
else
|
|
bindingManager = nullptr;
|
|
|
|
if (bindingManager)
|
|
bindingManager->PutLoadingDocListener(aDocumentURI, xblListener);
|
|
|
|
// Add our request.
|
|
nsXBLBindingRequest* req = new nsXBLBindingRequest(aBindingURI,
|
|
aBoundElement);
|
|
xblListener->AddRequest(req);
|
|
|
|
// Now kick off the async read.
|
|
rv = channel->AsyncOpen2(xblListener);
|
|
if (NS_FAILED(rv)) {
|
|
// Well, we won't be getting a load. Make sure to clean up our stuff!
|
|
if (bindingManager) {
|
|
bindingManager->RemoveLoadingDocListener(aDocumentURI);
|
|
}
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
nsCOMPtr<nsIStreamListener> listener;
|
|
rv = doc->StartDocumentLoad("loadAsInteractiveData",
|
|
channel,
|
|
loadGroup,
|
|
nullptr,
|
|
getter_AddRefs(listener),
|
|
true,
|
|
xblSink);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// Now do a blocking synchronous parse of the file.
|
|
nsCOMPtr<nsIInputStream> in;
|
|
rv = channel->Open2(getter_AddRefs(in));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
rv = nsSyncLoadService::PushSyncStreamToListener(in.forget(), listener,
|
|
channel);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
doc.swap(*aResult);
|
|
|
|
return NS_OK;
|
|
}
|