зеркало из https://github.com/mozilla/gecko-dev.git
Bug 94199, part 1, move handling of base binding to nsXBLPrototypeBinding, r=bz
This commit is contained in:
Родитель
5fe264ae0b
Коммит
1ae84aa1c7
|
@ -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,
|
||||
|
|
Загрузка…
Ссылка в новой задаче