diff --git a/content/base/src/nsAttrValue.cpp b/content/base/src/nsAttrValue.cpp index e62fd1df538c..b07d13020263 100644 --- a/content/base/src/nsAttrValue.cpp +++ b/content/base/src/nsAttrValue.cpp @@ -51,10 +51,14 @@ #include "nsTPtrArray.h" #include "nsContentUtils.h" #include "nsReadableUtils.h" +#include "nsIURI.h" #ifdef MOZ_SVG #include "nsISVGValue.h" #endif +#define MISC_STR_PTR(_cont) \ + reinterpret_cast((_cont)->mStringBits & NS_ATTRVALUE_POINTERVALUE_MASK) + nsTPtrArray* nsAttrValue::sEnumTableArray = nsnull; nsAttrValue::nsAttrValue() @@ -250,6 +254,16 @@ nsAttrValue::SetTo(const nsAttrValue& aOther) break; } #endif + case eFloatValue: + { + cont->mFloatValue = otherCont->mFloatValue; + break; + } + case eLazyURIValue: + { + NS_IF_ADDREF(cont->mURI = otherCont->mURI); + break; + } default: { NS_NOTREACHED("unknown type stored in MiscContainer"); @@ -257,8 +271,7 @@ nsAttrValue::SetTo(const nsAttrValue& aOther) } } - void* otherPtr = - reinterpret_cast(otherCont->mStringBits & NS_ATTRVALUE_POINTERVALUE_MASK); + void* otherPtr = MISC_STR_PTR(otherCont); if (otherPtr) { if (static_cast(otherCont->mStringBits & NS_ATTRVALUE_BASETYPE_MASK) == eStringBase) { @@ -326,8 +339,7 @@ nsAttrValue::ToString(nsAString& aResult) const MiscContainer* cont = nsnull; if (BaseType() == eOtherBase) { cont = GetMiscContainer(); - void* ptr = - reinterpret_cast(cont->mStringBits & NS_ATTRVALUE_POINTERVALUE_MASK); + void* ptr = MISC_STR_PTR(cont); if (ptr) { if (static_cast(cont->mStringBits & NS_ATTRVALUE_BASETYPE_MASK) == eStringBase) { @@ -434,6 +446,16 @@ nsAttrValue::ToString(nsAString& aResult) const aResult = str; break; } + // No need to do for eLazyURIValue, since that always stores the + // original string. +#ifdef DEBUG + case eLazyURIValue: + { + NS_NOTREACHED("Shouldn't get here"); + aResult.Truncate(); + break; + } +#endif default: { aResult.Truncate(); @@ -577,6 +599,22 @@ nsAttrValue::HashValue() const return NS_PTR_TO_INT32(cont->mSVGValue); } #endif + case eFloatValue: + { + // XXX this is crappy, but oh well + return cont->mFloatValue; + } + case eLazyURIValue: + { + NS_ASSERTION(static_cast(cont->mStringBits & + NS_ATTRVALUE_BASETYPE_MASK) == + eStringBase, + "Unexpected type"); + nsStringBuffer* str = static_cast(MISC_STR_PTR(cont)); + NS_ASSERTION(str, "How did that happen?"); + PRUint32 len = str->StorageSize()/sizeof(PRUnichar) - 1; + return nsCRT::BufferHashCode(static_cast(str->Data()), len); + } default: { NS_NOTREACHED("unknown type stored in MiscContainer"); @@ -672,6 +710,15 @@ nsAttrValue::Equals(const nsAttrValue& aOther) const return thisCont->mSVGValue == otherCont->mSVGValue; } #endif + case eFloatValue: + { + return thisCont->mFloatValue == otherCont->mFloatValue; + } + case eLazyURIValue: + { + needsStringComparison = PR_TRUE; + break; + } default: { NS_NOTREACHED("unknown type stored in MiscContainer"); @@ -1117,6 +1164,58 @@ PRBool nsAttrValue::ParseFloatValue(const nsAString& aString) return PR_FALSE; } +PRBool nsAttrValue::ParseLazyURIValue(const nsAString& aString) +{ + ResetIfSet(); + + if (EnsureEmptyMiscContainer()) { + MiscContainer* cont = GetMiscContainer(); + cont->mURI = nsnull; + cont->mType = eLazyURIValue; + + // Don't use SetMiscAtomOrString because atomizing URIs is not + // likely to do us much good. + nsStringBuffer* buf = GetStringBuffer(aString); + if (!buf) { + return PR_FALSE; + } + cont->mStringBits = reinterpret_cast(buf) | eStringBase; + + return PR_TRUE; + } + + return PR_FALSE; +} + +void +nsAttrValue::CacheURIValue(nsIURI* aURI) +{ + NS_PRECONDITION(Type() == eLazyURIValue, "wrong type"); + NS_PRECONDITION(!GetMiscContainer()->mURI, "Why are we being called?"); + NS_IF_ADDREF(GetMiscContainer()->mURI = aURI); +} + +void +nsAttrValue::DropCachedURI() +{ + NS_PRECONDITION(Type() == eLazyURIValue, "wrong type"); + NS_IF_RELEASE(GetMiscContainer()->mURI); +} + +const nsCheapString +nsAttrValue::GetURIStringValue() const +{ + NS_PRECONDITION(Type() == eLazyURIValue, "wrong type"); + NS_PRECONDITION(static_cast(GetMiscContainer()->mStringBits & + NS_ATTRVALUE_BASETYPE_MASK) == + eStringBase, + "Unexpected type"); + NS_PRECONDITION(MISC_STR_PTR(GetMiscContainer()), + "Should have a string buffer here!"); + return nsCheapString(static_cast + (MISC_STR_PTR(GetMiscContainer()))); +} + void nsAttrValue::SetMiscAtomOrString(const nsAString* aValue) { @@ -1145,7 +1244,7 @@ void nsAttrValue::ResetMiscAtomOrString() { MiscContainer* cont = GetMiscContainer(); - void* ptr = reinterpret_cast(cont->mStringBits & NS_ATTRVALUE_POINTERVALUE_MASK); + void* ptr = MISC_STR_PTR(cont); if (ptr) { if (static_cast(cont->mStringBits & NS_ATTRVALUE_BASETYPE_MASK) == eStringBase) { @@ -1182,6 +1281,11 @@ nsAttrValue::EnsureEmptyMiscContainer() break; } #endif + case eLazyURIValue: + { + NS_IF_RELEASE(cont->mURI); + break; + } default: { break; diff --git a/content/base/src/nsAttrValue.h b/content/base/src/nsAttrValue.h index 2b31cc70c077..594d34642134 100644 --- a/content/base/src/nsAttrValue.h +++ b/content/base/src/nsAttrValue.h @@ -54,6 +54,7 @@ typedef unsigned long PtrBits; class nsAString; class nsIAtom; class nsICSSStyleRule; +class nsIURI; class nsISVGValue; class nsIDocument; template class nsCOMArray; @@ -120,6 +121,7 @@ public: ,eSVGValue = 0x12 #endif ,eFloatValue = 0x13 + ,eLazyURIValue = 0x14 }; ValueType Type() const; @@ -153,6 +155,10 @@ public: inline nsISVGValue* GetSVGValue() const; #endif inline float GetFloatValue() const; + inline nsIURI* GetURIValue() const; + const nsCheapString GetURIStringValue() const; + void CacheURIValue(nsIURI* aURI); + void DropCachedURI(); // Methods to get access to atoms we may have // Returns the number of atoms we have; 0 if we have none. It's OK @@ -257,6 +263,12 @@ public: */ PRBool ParseFloatValue(const nsAString& aString); + /** + * Parse a lazy URI. This just sets up the storage for the URI; it + * doesn't actually allocate it. + */ + PRBool ParseLazyURIValue(const nsAString& aString); + private: // These have to be the same as in ValueType enum ValueBaseType { @@ -285,6 +297,7 @@ private: nsISVGValue* mSVGValue; #endif float mFloatValue; + nsIURI* mURI; }; }; @@ -390,6 +403,13 @@ nsAttrValue::GetFloatValue() const return GetMiscContainer()->mFloatValue; } +inline nsIURI* +nsAttrValue::GetURIValue() const +{ + NS_PRECONDITION(Type() == eLazyURIValue, "wrong type"); + return GetMiscContainer()->mURI; +} + inline nsAttrValue::ValueBaseType nsAttrValue::BaseType() const { diff --git a/content/html/content/src/nsGenericHTMLElement.cpp b/content/html/content/src/nsGenericHTMLElement.cpp index 42eb94ad41c9..4713f1dcc487 100644 --- a/content/html/content/src/nsGenericHTMLElement.cpp +++ b/content/html/content/src/nsGenericHTMLElement.cpp @@ -1060,24 +1060,7 @@ nsGenericHTMLElement::GetHrefURIForAnchors(nsIURI** aURI) const // Get href= attribute (relative URI). // We use the nsAttrValue's copy of the URI string to avoid copying. - const nsAttrValue* attr = mAttrsAndChildren.GetAttr(nsGkAtoms::href); - if (attr) { - // Get base URI. - nsCOMPtr baseURI = GetBaseURI(); - - // Get absolute URI. - nsresult rv = nsContentUtils::NewURIWithDocumentCharset(aURI, - attr->GetStringValue(), - GetOwnerDoc(), - baseURI); - if (NS_FAILED(rv)) { - *aURI = nsnull; - } - } - else { - // Absolute URI is null to say we have no HREF. - *aURI = nsnull; - } + GetURIAttr(nsGkAtoms::href, nsnull, aURI); return NS_OK; } @@ -2156,51 +2139,72 @@ nsGenericHTMLElement::SetFloatAttr(nsIAtom* aAttr, float aValue) nsresult nsGenericHTMLElement::GetURIAttr(nsIAtom* aAttr, nsIAtom* aBaseAttr, nsAString& aResult) { - nsAutoString attrValue; - if (!GetAttr(kNameSpaceID_None, aAttr, attrValue)) { + nsCOMPtr uri; + PRBool hadAttr = GetURIAttr(aAttr, aBaseAttr, getter_AddRefs(uri)); + if (!hadAttr) { aResult.Truncate(); - return NS_OK; } + if (!uri) { + // Just return the attr value + GetAttr(kNameSpaceID_None, aAttr, aResult); + return NS_OK; + } + + nsCAutoString spec; + uri->GetSpec(spec); + CopyUTF8toUTF16(spec, aResult); + return NS_OK; +} + +PRBool +nsGenericHTMLElement::GetURIAttr(nsIAtom* aAttr, nsIAtom* aBaseAttr, + nsIURI** aURI) const +{ + *aURI = nsnull; + + const nsAttrValue* attr = mAttrsAndChildren.GetAttr(aAttr); + if (!attr) { + return PR_FALSE; + } + + PRBool isURIAttr = (attr->Type() == nsAttrValue::eLazyURIValue); + + if (isURIAttr && (*aURI = attr->GetURIValue())) { + NS_ADDREF(*aURI); + return PR_TRUE; + } + nsCOMPtr baseURI = GetBaseURI(); - nsresult rv; if (aBaseAttr) { nsAutoString baseAttrValue; if (GetAttr(kNameSpaceID_None, aBaseAttr, baseAttrValue)) { nsCOMPtr baseAttrURI; - rv = nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(baseAttrURI), - baseAttrValue, GetOwnerDoc(), - baseURI); + nsresult rv = + nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(baseAttrURI), + baseAttrValue, GetOwnerDoc(), + baseURI); if (NS_FAILED(rv)) { - // Just use the attr value as the result... - aResult = attrValue; - - return NS_OK; + return PR_TRUE; } baseURI.swap(baseAttrURI); } } - nsCOMPtr attrURI; - rv = nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(attrURI), - attrValue, GetOwnerDoc(), - baseURI); - if (NS_FAILED(rv)) { - // Just use the attr value as the result... - aResult = attrValue; + // Don't care about return value. If it fails, we still want to + // return PR_TRUE, and *aURI will be null. + nsContentUtils::NewURIWithDocumentCharset(aURI, + isURIAttr ? + attr->GetURIStringValue() : + attr->GetStringValue(), + GetOwnerDoc(), baseURI); - return NS_OK; + if (isURIAttr) { + const_cast(attr)->CacheURIValue(*aURI); } - - NS_ASSERTION(attrURI, - "nsContentUtils::NewURIWithDocumentCharset return value lied"); - - nsCAutoString spec; - attrURI->GetSpec(spec); - CopyUTF8toUTF16(spec, aResult); - return NS_OK; + return PR_TRUE; } nsresult diff --git a/content/html/content/src/nsGenericHTMLElement.h b/content/html/content/src/nsGenericHTMLElement.h index b260ff16df89..eb03fe42c9be 100644 --- a/content/html/content/src/nsGenericHTMLElement.h +++ b/content/html/content/src/nsGenericHTMLElement.h @@ -687,6 +687,15 @@ protected: */ NS_HIDDEN_(nsresult) GetURIAttr(nsIAtom* aAttr, nsIAtom* aBaseAttr, nsAString& aResult); + /** + * Helper for GetURIAttr and GetHrefURIForAnchors which returns an + * nsIURI in the out param.. + * + * @return PR_TRUE if we had the attr, PR_FALSE otherwise. + */ + NS_HIDDEN_(PRBool) GetURIAttr(nsIAtom* aAttr, nsIAtom* aBaseAttr, + nsIURI** aURI) const; + /** * This method works like GetURIAttr, except that it supports multiple * URIs separated by whitespace (one or more U+0020 SPACE characters). diff --git a/content/html/content/src/nsHTMLAnchorElement.cpp b/content/html/content/src/nsHTMLAnchorElement.cpp index 429d2bce04e2..aa07c4ecd38f 100644 --- a/content/html/content/src/nsHTMLAnchorElement.cpp +++ b/content/html/content/src/nsHTMLAnchorElement.cpp @@ -131,10 +131,16 @@ public: PRBool aNotify); virtual nsresult UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute, PRBool aNotify); + virtual PRBool ParseAttribute(PRInt32 aNamespaceID, + nsIAtom* aAttribute, + const nsAString& aValue, + nsAttrValue& aResult); virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const; protected: + void ResetLinkCacheState(); + // The cached visited state nsLinkState mLinkState; }; @@ -223,13 +229,9 @@ nsHTMLAnchorElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, void nsHTMLAnchorElement::UnbindFromTree(PRBool aDeep, PRBool aNullParent) { - nsIDocument* doc = GetCurrentDoc(); - if (doc) { + if (IsInDoc()) { RegUnRegAccessKey(PR_FALSE); - doc->ForgetLink(this); - // If this link is ever reinserted into a document, it might - // be under a different xml:base, so forget the cached state now - mLinkState = eLinkState_Unknown; + ResetLinkCacheState(); } nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent); @@ -464,14 +466,7 @@ nsHTMLAnchorElement::SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName, nsAutoString val; GetHref(val); if (!val.Equals(aValue)) { - nsIDocument* doc = GetCurrentDoc(); - if (doc) { - doc->ForgetLink(this); - // The change to 'href' will cause style reresolution which will - // eventually recompute the link state and re-add this element - // to the link map if necessary. - } - SetLinkState(eLinkState_Unknown); + ResetLinkCacheState(); } } @@ -495,11 +490,7 @@ nsHTMLAnchorElement::UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute, PRBool aNotify) { if (aAttribute == nsGkAtoms::href && kNameSpaceID_None == aNameSpaceID) { - nsIDocument* doc = GetCurrentDoc(); - if (doc) { - doc->ForgetLink(this); - } - SetLinkState(eLinkState_Unknown); + ResetLinkCacheState(); } if (aAttribute == nsGkAtoms::accesskey && @@ -509,3 +500,34 @@ nsHTMLAnchorElement::UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute, return nsGenericHTMLElement::UnsetAttr(aNameSpaceID, aAttribute, aNotify); } + +PRBool +nsHTMLAnchorElement::ParseAttribute(PRInt32 aNamespaceID, + nsIAtom* aAttribute, + const nsAString& aValue, + nsAttrValue& aResult) +{ + if (aNamespaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::href) { + return aResult.ParseLazyURIValue(aValue); + } + + return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue, + aResult); +} + +void +nsHTMLAnchorElement::ResetLinkCacheState() +{ + nsIDocument* doc = GetCurrentDoc(); + if (doc) { + doc->ForgetLink(this); + } + mLinkState = eLinkState_Unknown; + + // Clear our cached URI _after_ we ForgetLink(), since ForgetLink() + // wants that URI. + const nsAttrValue* attr = mAttrsAndChildren.GetAttr(nsGkAtoms::href); + if (attr && attr->Type() == nsAttrValue::eLazyURIValue) { + const_cast(attr)->DropCachedURI(); + } +} diff --git a/content/html/content/test/Makefile.in b/content/html/content/test/Makefile.in index 51c91435f4e8..aad17f2ef5d3 100644 --- a/content/html/content/test/Makefile.in +++ b/content/html/content/test/Makefile.in @@ -130,6 +130,7 @@ _TEST_FILES = test_bug589.html \ test_bug347174_xslp.html \ 347174transformable.xml \ 347174transform.xsl \ + test_bug481335.xhtml \ $(NULL) libs:: $(_TEST_FILES) diff --git a/content/html/content/test/test_bug481335.xhtml b/content/html/content/test/test_bug481335.xhtml new file mode 100644 index 000000000000..b2a4bd7b2e5b --- /dev/null +++ b/content/html/content/test/test_bug481335.xhtml @@ -0,0 +1,71 @@ + + + + Test for Bug 481335 + + + + + +Mozilla Bug 481335 +

+ A link + +

+

+ +
+
+
+ +