Bug 94199, part 1, move handling of base binding to nsXBLPrototypeBinding, r=bz

This commit is contained in:
Neil Deakin 2011-11-03 16:39:07 -04:00
Родитель 5fe264ae0b
Коммит 1ae84aa1c7
4 изменённых файлов: 175 добавлений и 167 удалений

Просмотреть файл

@ -71,6 +71,7 @@
#include "nsContentUtils.h"
#include "nsIScriptContext.h"
#include "nsIScriptError.h"
#include "nsIStyleRuleProcessor.h"
#include "nsXBLResourceLoader.h"
@ -276,7 +277,7 @@ nsXBLPrototypeBinding::nsXBLPrototypeBinding()
: mImplementation(nsnull),
mBaseBinding(nsnull),
mInheritStyle(true),
mHasBaseProto(true),
mCheckedBaseProto(false),
mKeyHandlersRegistered(false),
mResources(nsnull),
mAttributeTable(nsnull),
@ -1426,3 +1427,107 @@ nsXBLPrototypeBinding::CreateKeyHandlers()
curr = curr->GetNextHandler();
}
}
bool CheckTagNameWhiteList(PRInt32 aNameSpaceID, nsIAtom *aTagName)
{
static nsIContent::AttrValuesArray kValidXULTagNames[] = {
&nsGkAtoms::autorepeatbutton, &nsGkAtoms::box, &nsGkAtoms::browser,
&nsGkAtoms::button, &nsGkAtoms::hbox, &nsGkAtoms::image, &nsGkAtoms::menu,
&nsGkAtoms::menubar, &nsGkAtoms::menuitem, &nsGkAtoms::menupopup,
&nsGkAtoms::row, &nsGkAtoms::slider, &nsGkAtoms::spacer,
&nsGkAtoms::splitter, &nsGkAtoms::text, &nsGkAtoms::tree, nsnull};
PRUint32 i;
if (aNameSpaceID == kNameSpaceID_XUL) {
for (i = 0; kValidXULTagNames[i]; ++i) {
if (aTagName == *(kValidXULTagNames[i])) {
return true;
}
}
}
else if (aNameSpaceID == kNameSpaceID_SVG &&
aTagName == nsGkAtoms::generic) {
return true;
}
return false;
}
nsresult
nsXBLPrototypeBinding::ResolveBaseBinding()
{
if (mCheckedBaseProto)
return NS_OK;
mCheckedBaseProto = true;
nsCOMPtr<nsIDocument> doc = mXBLDocInfoWeak->GetDocument();
// Check for the presence of 'extends' and 'display' attributes
nsAutoString display, extends;
mBinding->GetAttr(kNameSpaceID_None, nsGkAtoms::extends, extends);
if (extends.IsEmpty())
return NS_OK;
mBinding->GetAttr(kNameSpaceID_None, nsGkAtoms::display, display);
bool hasDisplay = !display.IsEmpty();
nsAutoString value(extends);
// Now slice 'em up to see what we've got.
nsAutoString prefix;
PRInt32 offset;
if (hasDisplay) {
offset = display.FindChar(':');
if (-1 != offset) {
display.Left(prefix, offset);
display.Cut(0, offset+1);
}
}
else {
offset = extends.FindChar(':');
if (-1 != offset) {
extends.Left(prefix, offset);
extends.Cut(0, offset+1);
display = extends;
}
}
nsAutoString nameSpace;
if (!prefix.IsEmpty()) {
mBinding->LookupNamespaceURI(prefix, nameSpace);
if (!nameSpace.IsEmpty()) {
PRInt32 nameSpaceID =
nsContentUtils::NameSpaceManager()->GetNameSpaceID(nameSpace);
nsCOMPtr<nsIAtom> tagName = do_GetAtom(display);
// Check the white list
if (!CheckTagNameWhiteList(nameSpaceID, tagName)) {
const PRUnichar* params[] = { display.get() };
nsContentUtils::ReportToConsole(nsContentUtils::eXBL_PROPERTIES,
"InvalidExtendsBinding",
params, NS_ARRAY_LENGTH(params),
doc->GetDocumentURI(),
EmptyString(), 0, 0,
nsIScriptError::errorFlag,
"XBL");
NS_ASSERTION(!nsXBLService::IsChromeOrResourceURI(doc->GetDocumentURI()),
"Invalid extends value");
return NS_ERROR_ILLEGAL_VALUE;
}
SetBaseTag(nameSpaceID, tagName);
}
}
if (hasDisplay || nameSpace.IsEmpty()) {
mBinding->UnsetAttr(kNameSpaceID_None, nsGkAtoms::extends, false);
mBinding->UnsetAttr(kNameSpaceID_None, nsGkAtoms::display, false);
return NS_NewURI(getter_AddRefs(mBaseBindingURI), value,
doc->GetDocumentCharacterSet().get(),
doc->GetDocBaseURI());
}
return NS_OK;
}

Просмотреть файл

@ -76,6 +76,7 @@ public:
nsIURI* BindingURI() const { return mBindingURI; }
nsIURI* AlternateBindingURI() const { return mAlternateBindingURI; }
nsIURI* DocURI() const { return mXBLDocInfoWeak->DocumentURI(); }
nsIURI* GetBaseBindingURI() const { return mBaseBindingURI; }
// Checks if aURI refers to this binding by comparing to both possible
// binding URIs.
@ -143,9 +144,6 @@ public:
nsXBLDocumentInfo* XBLDocumentInfo() const { return mXBLDocInfoWeak; }
bool IsChrome() { return mXBLDocInfoWeak->IsChrome(); }
bool HasBasePrototype() { return mHasBaseProto; }
void SetHasBasePrototype(bool aHasBase) { mHasBaseProto = aHasBase; }
void SetInitialAttributes(nsIContent* aBoundElement, nsIContent* aAnonymousContent);
nsIStyleRuleProcessor* GetRuleProcessor();
@ -181,6 +179,8 @@ public:
void Initialize();
nsresult ResolveBaseBinding();
const nsCOMArray<nsXBLKeyEventHandler>* GetKeyEventHandlers()
{
if (!mKeyHandlersRegistered) {
@ -266,13 +266,16 @@ protected:
nsCOMPtr<nsIURI> mAlternateBindingURI; // Alternate id-less URI that is only non-null on the first binding.
nsCOMPtr<nsIContent> mBinding; // Strong. We own a ref to our content element in the binding doc.
nsAutoPtr<nsXBLPrototypeHandler> mPrototypeHandler; // Strong. DocInfo owns us, and we own the handlers.
// the url of the base binding
nsCOMPtr<nsIURI> mBaseBindingURI;
nsXBLProtoImpl* mImplementation; // Our prototype implementation (includes methods, properties, fields,
// the constructor, and the destructor).
nsXBLPrototypeBinding* mBaseBinding; // Weak. The docinfo will own our base binding.
bool mInheritStyle;
bool mHasBaseProto;
bool mCheckedBaseProto;
bool mKeyHandlersRegistered;
nsXBLPrototypeResources* mResources; // If we have any resources, this will be non-null.

Просмотреть файл

@ -97,16 +97,6 @@ using namespace mozilla;
#define NS_MAX_XBL_BINDING_RECURSION 20
static bool 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;
}
static bool
IsAncestorBinding(nsIDocument* aDocument,
nsIURI* aChildBindingURI,
@ -149,31 +139,6 @@ IsAncestorBinding(nsIDocument* aDocument,
return false;
}
bool CheckTagNameWhiteList(PRInt32 aNameSpaceID, nsIAtom *aTagName)
{
static nsIContent::AttrValuesArray kValidXULTagNames[] = {
&nsGkAtoms::autorepeatbutton, &nsGkAtoms::box, &nsGkAtoms::browser,
&nsGkAtoms::button, &nsGkAtoms::hbox, &nsGkAtoms::image, &nsGkAtoms::menu,
&nsGkAtoms::menubar, &nsGkAtoms::menuitem, &nsGkAtoms::menupopup,
&nsGkAtoms::row, &nsGkAtoms::slider, &nsGkAtoms::spacer,
&nsGkAtoms::splitter, &nsGkAtoms::text, &nsGkAtoms::tree, nsnull};
PRUint32 i;
if (aNameSpaceID == kNameSpaceID_XUL) {
for (i = 0; kValidXULTagNames[i]; ++i) {
if (aTagName == *(kValidXULTagNames[i])) {
return true;
}
}
}
else if (aNameSpaceID == kNameSpaceID_SVG &&
aTagName == nsGkAtoms::generic) {
return true;
}
return false;
}
// Individual binding requests.
class nsXBLBindingRequest
{
@ -450,7 +415,7 @@ nsXBLStreamListener::HandleEvent(nsIDOMEvent* aEvent)
xblDocBindingManager->GetXBLDocumentInfo(documentURI);
xblDocBindingManager->RemoveXBLDocumentInfo(info); // Break the self-imposed cycle.
if (!info) {
if (IsChromeOrResourceURI(documentURI)) {
if (nsXBLService::IsChromeOrResourceURI(documentURI)) {
NS_WARNING("An XBL file is malformed. Did you forget the XBL namespace on the bindings tag?");
}
nsContentUtils::ReportToConsole(nsContentUtils::eXBL_PROPERTIES,
@ -464,7 +429,7 @@ nsXBLStreamListener::HandleEvent(nsIDOMEvent* aEvent)
// If the doc is a chrome URI, then we put it into the XUL cache.
#ifdef MOZ_XUL
if (IsChromeOrResourceURI(documentURI)) {
if (nsXBLService::IsChromeOrResourceURI(documentURI)) {
nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
if (cache && cache->IsEnabled())
cache->PutXBLDocumentInfo(info);
@ -533,6 +498,19 @@ 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;
}
// This function loads a particular XBL file and installs all of the bindings
// onto the element.
NS_IMETHODIMP
@ -879,8 +857,6 @@ nsXBLService::GetBinding(nsIContent* aBoundElement, nsIURI* aURI,
NS_ERROR_OUT_OF_MEMORY);
}
nsCOMPtr<nsIContent> child = protoBinding->GetBindingElement();
// Our prototype binding must have all its resources loaded.
bool ready = protoBinding->LoadResources();
if (!ready) {
@ -890,143 +866,65 @@ nsXBLService::GetBinding(nsIContent* aBoundElement, nsIURI* aURI,
return NS_ERROR_FAILURE; // The binding isn't ready yet.
}
// If our prototype already has a base, then don't check for an "extends" attribute.
nsRefPtr<nsXBLBinding> baseBinding;
bool hasBase = protoBinding->HasBasePrototype();
rv = protoBinding->ResolveBaseBinding();
NS_ENSURE_SUCCESS(rv, rv);
nsIURI* baseBindingURI;
nsXBLPrototypeBinding* baseProto = protoBinding->GetBasePrototype();
if (baseProto) {
// Use the NodePrincipal() of the <binding> element in question
// for the security check.
rv = GetBinding(aBoundElement, baseProto->BindingURI(), aPeekOnly,
child->NodePrincipal(), aIsReady,
getter_AddRefs(baseBinding), aDontExtendURIs);
if (NS_FAILED(rv))
return rv; // We aren't ready yet.
baseBindingURI = baseProto->BindingURI();
}
else if (hasBase) {
// Check for the presence of 'extends' and 'display' attributes
nsAutoString display, extends;
child->GetAttr(kNameSpaceID_None, nsGkAtoms::display, display);
child->GetAttr(kNameSpaceID_None, nsGkAtoms::extends, extends);
bool hasDisplay = !display.IsEmpty();
bool hasExtends = !extends.IsEmpty();
nsAutoString value(extends);
if (!hasExtends)
protoBinding->SetHasBasePrototype(false);
else {
// Now slice 'em up to see what we've got.
nsAutoString prefix;
PRInt32 offset;
if (hasDisplay) {
offset = display.FindChar(':');
if (-1 != offset) {
display.Left(prefix, offset);
display.Cut(0, offset+1);
}
}
else if (hasExtends) {
offset = extends.FindChar(':');
if (-1 != offset) {
extends.Left(prefix, offset);
extends.Cut(0, offset+1);
display = extends;
}
}
nsAutoString nameSpace;
if (!prefix.IsEmpty()) {
child->LookupNamespaceURI(prefix, nameSpace);
if (!nameSpace.IsEmpty()) {
if (!hasDisplay) {
// We extend some widget/frame. We don't really have a
// base binding.
protoBinding->SetHasBasePrototype(false);
//child->UnsetAttr(kNameSpaceID_None, nsGkAtoms::extends, false);
}
PRInt32 nameSpaceID =
nsContentUtils::NameSpaceManager()->GetNameSpaceID(nameSpace);
nsCOMPtr<nsIAtom> tagName = do_GetAtom(display);
// Check the white list
if (!CheckTagNameWhiteList(nameSpaceID, tagName)) {
const PRUnichar* params[] = { display.get() };
nsContentUtils::ReportToConsole(nsContentUtils::eXBL_PROPERTIES,
"InvalidExtendsBinding",
params, ArrayLength(params),
nsnull,
EmptyString(), 0, 0,
nsIScriptError::errorFlag,
"XBL", doc);
NS_ASSERTION(!IsChromeOrResourceURI(aURI),
"Invalid extends value");
return NS_ERROR_ILLEGAL_VALUE;
}
protoBinding->SetBaseTag(nameSpaceID, tagName);
}
}
if (hasExtends && (hasDisplay || nameSpace.IsEmpty())) {
// Look up the prefix.
// We have a base class binding. Load it right now.
nsCOMPtr<nsIURI> bindingURI;
rv = NS_NewURI(getter_AddRefs(bindingURI), value,
doc->GetDocumentCharacterSet().get(),
doc->GetDocBaseURI());
else {
baseBindingURI = protoBinding->GetBaseBindingURI();
if (baseBindingURI) {
PRUint32 count = aDontExtendURIs.Length();
for (PRUint32 index = 0; index < count; ++index) {
bool equal;
rv = aDontExtendURIs[index]->Equals(baseBindingURI, &equal);
NS_ENSURE_SUCCESS(rv, rv);
PRUint32 count = aDontExtendURIs.Length();
for (PRUint32 index = 0; index < count; ++index) {
bool equal;
rv = aDontExtendURIs[index]->Equals(bindingURI, &equal);
NS_ENSURE_SUCCESS(rv, rv);
if (equal) {
nsCAutoString spec;
protoBinding->BindingURI()->GetSpec(spec);
NS_ConvertUTF8toUTF16 protoSpec(spec);
const PRUnichar* params[] = { protoSpec.get(), value.get() };
nsContentUtils::ReportToConsole(nsContentUtils::eXBL_PROPERTIES,
"CircularExtendsBinding",
params, ArrayLength(params),
nsnull,
EmptyString(), 0, 0,
nsIScriptError::warningFlag,
"XBL", boundDocument);
return NS_ERROR_ILLEGAL_VALUE;
}
}
// Use the NodePrincipal() of the <binding> element in question
// for the security check.
rv = GetBinding(aBoundElement, bindingURI, aPeekOnly,
child->NodePrincipal(), aIsReady,
getter_AddRefs(baseBinding), aDontExtendURIs);
if (NS_FAILED(rv))
return rv; // Binding not yet ready or an error occurred.
if (!aPeekOnly) {
// Make sure to set the base prototype.
baseProto = baseBinding->PrototypeBinding();
protoBinding->SetBasePrototype(baseProto);
child->UnsetAttr(kNameSpaceID_None, nsGkAtoms::extends, false);
child->UnsetAttr(kNameSpaceID_None, nsGkAtoms::display, false);
if (equal) {
nsCAutoString spec, basespec;
protoBinding->BindingURI()->GetSpec(spec);
NS_ConvertUTF8toUTF16 protoSpec(spec);
baseBindingURI->GetSpec(basespec);
NS_ConvertUTF8toUTF16 baseSpecUTF16(basespec);
const PRUnichar* params[] = { protoSpec.get(), baseSpecUTF16.get() };
nsContentUtils::ReportToConsole(nsContentUtils::eXBL_PROPERTIES,
"CircularExtendsBinding",
params, NS_ARRAY_LENGTH(params),
boundDocument->GetDocumentURI(),
EmptyString(), 0, 0,
nsIScriptError::warningFlag,
"XBL");
return NS_ERROR_ILLEGAL_VALUE;
}
}
}
}
nsRefPtr<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
nsXBLBinding *newBinding = new nsXBLBinding(protoBinding);
NS_ENSURE_TRUE(newBinding, NS_ERROR_OUT_OF_MEMORY);
if (baseBinding)
newBinding->SetBaseBinding(baseBinding);
if (baseBinding) {
if (!baseProto) {
protoBinding->SetBasePrototype(baseBinding->PrototypeBinding());
}
newBinding->SetBaseBinding(baseBinding);
}
NS_ADDREF(*aResult = newBinding);
}

Просмотреть файл

@ -64,6 +64,8 @@ class nsXBLService : public nsIXBLService,
{
NS_DECL_ISUPPORTS
static bool IsChromeOrResourceURI(nsIURI* aURI);
// This function loads a particular XBL file and installs all of the bindings
// onto the element. aOriginPrincipal must not be null here.
NS_IMETHOD LoadBindings(nsIContent* aContent, nsIURI* aURL,