From b3e255230a7ecab4d3ee11647a03dd666e15b30d Mon Sep 17 00:00:00 2001 From: Justin Lebar Date: Thu, 24 Sep 2009 13:59:43 -0400 Subject: [PATCH] Bug 209275 - Links' hrefs should be updated when a 's href changes. r=bzbarsky --- content/base/public/nsIContent.h | 17 +- content/base/public/nsIDocument.h | 22 +- content/base/src/nsDocument.cpp | 164 ++++++++++- content/base/src/nsDocument.h | 10 +- .../html/content/src/nsGenericHTMLElement.cpp | 6 +- .../html/content/src/nsHTMLAnchorElement.cpp | 14 + .../html/content/src/nsHTMLSharedElement.cpp | 171 +++++++++++- content/html/content/test/Makefile.in | 4 + .../html/content/test/file_bug209275_1.html | 28 ++ .../html/content/test/file_bug209275_2.html | 23 ++ .../html/content/test/file_bug209275_3.html | 23 ++ .../html/content/test/test_bug209275.xhtml | 261 ++++++++++++++++++ 12 files changed, 725 insertions(+), 18 deletions(-) create mode 100644 content/html/content/test/file_bug209275_1.html create mode 100644 content/html/content/test/file_bug209275_2.html create mode 100644 content/html/content/test/file_bug209275_3.html create mode 100644 content/html/content/test/test_bug209275.xhtml diff --git a/content/base/public/nsIContent.h b/content/base/public/nsIContent.h index 12ecd12d463..345c883402e 100644 --- a/content/base/public/nsIContent.h +++ b/content/base/public/nsIContent.h @@ -70,10 +70,10 @@ enum nsLinkState { }; // IID for the nsIContent interface -// b877753c-316a-422d-9aec-a1d0cf0928b0 +// d510382f-f5eb-48bb-9ad9-b3dc4806faaf #define NS_ICONTENT_IID \ -{ 0xb877753c, 0x316a, 0x422d, \ - { 0x9a, 0xec, 0xa1, 0xd0, 0xcf, 0x09, 0x28, 0xb0 } } +{ 0xd510382f, 0xf5eb, 0x48bb, \ + { 0x9a, 0xd9, 0xb3, 0xdc, 0x48, 0x06, 0xfa, 0xaf } } /** * A node of content in a document's content model. This interface @@ -607,6 +607,17 @@ public: */ virtual PRBool IsLink(nsIURI** aURI) const = 0; + /** + * If the implementing element is a link, calling this method forces it to + * clear its cached href, if it has one. + * + * This function does not notify the document that it may need to restyle the + * link. + */ + virtual void DropCachedHref() + { + } + /** * Get the cached state of the link. If the state is unknown, * return eLinkState_Unknown. diff --git a/content/base/public/nsIDocument.h b/content/base/public/nsIDocument.h index 0dcdd101c00..ae37cc39a4c 100644 --- a/content/base/public/nsIDocument.h +++ b/content/base/public/nsIDocument.h @@ -105,8 +105,8 @@ class nsIBoxObject; // IID for the nsIDocument interface #define NS_IDOCUMENT_IID \ -{ 0xe0ca6723, 0x1efa, 0x4819, \ - { 0x84, 0xbb, 0xfa, 0x48, 0x39, 0xe8, 0xef, 0x19 } } + {0x1666cc78, 0x54ad, 0x4672, \ + {0x93, 0x79, 0x9b, 0x6a, 0x61, 0x78, 0x94, 0x1a } } // Flag for AddStyleSheet(). #define NS_STYLESHEET_FROM_CATALOG (1 << 0) @@ -1165,6 +1165,24 @@ public: */ virtual PRBool IsDocumentRightToLeft() { return PR_FALSE; } + /** + * Gets the document's cached pointer to the first element in this + * document which has an href attribute. If the document doesn't contain any + * elements with an href, returns null. + */ + virtual nsIContent* GetFirstBaseNodeWithHref() = 0; + + /** + * Sets the document's cached pointer to the first element with an + * href attribute in this document and updates the document's base URI + * according to the element's href. + * + * If the given node is the same as the current first base node, this + * function still updates the document's base URI according to the node's + * href, if it changed. + */ + virtual nsresult SetFirstBaseNodeWithHref(nsIContent *node) = 0; + protected: ~nsIDocument() { diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp index b1a1b94937d..154d7043c82 100644 --- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -1768,6 +1768,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDocument) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mXPathEvaluatorTearoff) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mLayoutHistoryState) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnloadBlocker) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mFirstBaseNodeWithHref) // An element will only be in the linkmap as long as it's in the // document, so we'll traverse the table here instead of from the element. @@ -1819,6 +1820,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCachedRootContent) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDisplayDocument) + NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mFirstBaseNodeWithHref) NS_IMPL_CYCLE_COLLECTION_UNLINK_USERDATA @@ -1971,12 +1973,9 @@ nsDocument::ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup, for (PRInt32 i = PRInt32(count) - 1; i >= 0; i--) { nsCOMPtr content = mChildren.ChildAt(i); - // XXXbz this is backwards from how ContentRemoved normally works. That - // is, usually it's dispatched after the content has been removed from - // the tree. + mChildren.RemoveChildAt(i); nsNodeUtils::ContentRemoved(this, content, i); content->UnbindFromTree(); - mChildren.RemoveChildAt(i); } } mCachedRootContent = nsnull; @@ -1994,7 +1993,9 @@ nsDocument::ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup, mDOMStyleSheets = nsnull; SetDocumentURI(aURI); - mDocumentBaseURI = mDocumentURI; + // If mDocumentBaseURI is null, nsIDocument::GetBaseURI() returns + // mDocumentURI. + mDocumentBaseURI = nsnull; if (aLoadGroup) { mDocumentLoadGroup = do_GetWeakReference(aLoadGroup); @@ -2249,7 +2250,23 @@ nsDocument::StopDocumentLoad() void nsDocument::SetDocumentURI(nsIURI* aURI) { + nsCOMPtr oldBase = nsIDocument::GetBaseURI(); mDocumentURI = NS_TryToMakeImmutable(aURI); + nsIURI* newBase = nsIDocument::GetBaseURI(); + + PRBool equalBases = PR_FALSE; + if (oldBase && newBase) { + oldBase->Equals(newBase, &equalBases); + } + else { + equalBases = !oldBase && !newBase; + } + + // If changing the document's URI changed the base URI of the document, we + // need to refresh the hrefs of all the links on the page. + if (!equalBases) { + RefreshLinkHrefs(); + } } NS_IMETHODIMP @@ -2789,6 +2806,7 @@ nsDocument::SetBaseURI(nsIURI* aURI) { nsresult rv = NS_OK; + nsCOMPtr oldBase = nsIDocument::GetBaseURI(); if (aURI) { rv = nsContentUtils::GetSecurityManager()-> CheckLoadURIWithPrincipal(NodePrincipal(), aURI, @@ -2800,6 +2818,21 @@ nsDocument::SetBaseURI(nsIURI* aURI) mDocumentBaseURI = nsnull; } + nsIURI* newBase = nsIDocument::GetBaseURI(); + PRBool equalBases = PR_FALSE; + if (oldBase && newBase) { + oldBase->Equals(newBase, &equalBases); + } + else { + equalBases = !oldBase && !newBase; + } + + // If the document's base URI has changed, we need to re-resolve all the + // cached link hrefs relative to the new base. + if (!equalBases) { + RefreshLinkHrefs(); + } + return rv; } @@ -5578,8 +5611,8 @@ NS_IMETHODIMP nsDocument::GetBaseURI(nsAString &aURI) { nsCAutoString spec; - if (mDocumentBaseURI) { - mDocumentBaseURI->GetSpec(spec); + if (nsIDocument::GetBaseURI()) { + nsIDocument::GetBaseURI()->GetSpec(spec); } CopyUTF8toUTF16(spec, aURI); @@ -7342,7 +7375,9 @@ public: return; // Throw away the cached link state so it gets refetched by the style - // system + // system. We can't call ContentStatesChanged here, because that might + // modify the hashtable. Instead, we'll just insert into this array and + // leave it to our caller to call ContentStatesChanged. aContent->SetLinkState(eLinkState_Unknown); contentVisited.AppendObject(aContent); } @@ -7359,10 +7394,12 @@ nsDocument::NotifyURIVisitednessChanged(nsIURI* aURI) nsUint32ToContentHashEntry* entry = mLinkMap.GetEntry(GetURIHash(aURI)); if (!entry) return; - + URIVisitNotifier visitor; aURI->GetSpec(visitor.matchURISpec); entry->VisitContent(&visitor); + + MOZ_AUTO_DOC_UPDATE(this, UPDATE_CONTENT_STATE, PR_TRUE); for (PRUint32 count = visitor.contentVisited.Count(), i = 0; i < count; ++i) { ContentStatesChanged(visitor.contentVisited[i], nsnull, NS_EVENT_STATE_VISITED); @@ -7383,7 +7420,7 @@ nsDocument::UpdateLinkMap() "Should only be updating the link map in visible documents"); if (!mVisible) return; - + PRInt32 count = mVisitednessChangedURIs.Count(); for (PRInt32 i = 0; i < count; ++i) { NotifyURIVisitednessChanged(mVisitednessChangedURIs[i]); @@ -7391,6 +7428,113 @@ nsDocument::UpdateLinkMap() mVisitednessChangedURIs.Clear(); } +class RefreshLinkStateVisitor : public nsUint32ToContentHashEntry::Visitor +{ +public: + nsCOMArray contentVisited; + + virtual void Visit(nsIContent* aContent) { + // We can't call ContentStatesChanged here, because that may modify the link + // map. Instead, we just add to an array and call ContentStatesChanged + // later. + aContent->SetLinkState(eLinkState_Unknown); + contentVisited.AppendObject(aContent); + } +}; + +static PLDHashOperator +RefreshLinkStateTraverser(nsUint32ToContentHashEntry* aEntry, + void* userArg) +{ + RefreshLinkStateVisitor *visitor = + static_cast(userArg); + + aEntry->VisitContent(visitor); + return PL_DHASH_NEXT; +} + + +// Helper function for nsDocument::RefreshLinkHrefs +static void +DropCachedHrefsRecursive(nsIContent * const elem) +{ + // Drop the element's cached href, if it has one. (If it doesn't have + // one, this call does nothing.) We could check first that elem is an + // tag to avoid making a virtual call, but it turns out not to make a + // substantial perf difference either way. This doesn't restyle the link, + // but we do that later. + elem->DropCachedHref(); + + PRUint32 childCount; + nsIContent * const * child = elem->GetChildArray(&childCount); + nsIContent * const * end = child + childCount; + for ( ; child != end; ++child) { + DropCachedHrefsRecursive(*child); + } +} + +void +nsDocument::RefreshLinkHrefs() +{ + if (!GetRootContent()) + return; + + // First, walk the DOM and clear the cached hrefs of all the tags. + DropCachedHrefsRecursive(GetRootContent()); + + // Now update the styles of everything in the linkmap. + RefreshLinkStateVisitor visitor; + mLinkMap.EnumerateEntries(RefreshLinkStateTraverser, &visitor); + + MOZ_AUTO_DOC_UPDATE(this, UPDATE_CONTENT_STATE, PR_TRUE); + for (PRUint32 count = visitor.contentVisited.Count(), i = 0; i < count; i++) { + ContentStatesChanged(visitor.contentVisited[i], + nsnull, NS_EVENT_STATE_VISITED); + } +} + +nsIContent* +nsDocument::GetFirstBaseNodeWithHref() +{ + return mFirstBaseNodeWithHref; +} + +nsresult +nsDocument::SetFirstBaseNodeWithHref(nsIContent *elem) +{ + mFirstBaseNodeWithHref = elem; + + if (!elem) { + SetBaseURI(nsnull); + return NS_OK; + } + + NS_ASSERTION(elem->Tag() == nsGkAtoms::base, + "Setting base node to a non element?"); + NS_ASSERTION(elem->GetNameSpaceID() == kNameSpaceID_XHTML, + "Setting base node to a non XHTML element?"); + + nsIDocument* doc = elem->GetOwnerDoc(); + nsIURI* currentURI = nsIDocument::GetDocumentURI(); + + // Resolve the element's href relative to our current URI + nsAutoString href; + PRBool hasHref = elem->GetAttr(kNameSpaceID_None, nsGkAtoms::href, href); + NS_ASSERTION(hasHref, + "Setting first base node to a node with no href attr?"); + + nsCOMPtr newBaseURI; + nsContentUtils::NewURIWithDocumentCharset( + getter_AddRefs(newBaseURI), href, doc, currentURI); + + // Try to set our base URI. If that fails, try to set our base URI to null. + nsresult rv = SetBaseURI(newBaseURI); + if (NS_FAILED(rv)) { + return SetBaseURI(nsnull); + } + return rv; +} + NS_IMETHODIMP nsDocument::GetScriptTypeID(PRUint32 *aScriptType) { diff --git a/content/base/src/nsDocument.h b/content/base/src/nsDocument.h index 640ebf19944..8126cee63d0 100644 --- a/content/base/src/nsDocument.h +++ b/content/base/src/nsDocument.h @@ -1033,12 +1033,18 @@ protected: static PRBool TryChannelCharset(nsIChannel *aChannel, PRInt32& aCharsetSource, nsACString& aCharset); - + void UpdateLinkMap(); // Call this before the document does something that will unbind all content. // That will stop us from resolving URIs for all links as they are removed. void DestroyLinkMap(); + // Refreshes the hrefs of all the links in the document. + void RefreshLinkHrefs(); + + nsIContent* GetFirstBaseNodeWithHref(); + nsresult SetFirstBaseNodeWithHref(nsIContent *node); + // Get the root element, or return null if there isn't one (e.g. // if the root isn't ) nsIContent* GetHtmlContent(); @@ -1213,6 +1219,8 @@ protected: // any. This can change during the lifetime of the document. nsCOMPtr mApplicationCache; + nsCOMPtr mFirstBaseNodeWithHref; + private: friend class nsUnblockOnloadEvent; diff --git a/content/html/content/src/nsGenericHTMLElement.cpp b/content/html/content/src/nsGenericHTMLElement.cpp index e9248679a24..7795dcfc56e 100644 --- a/content/html/content/src/nsGenericHTMLElement.cpp +++ b/content/html/content/src/nsGenericHTMLElement.cpp @@ -2160,7 +2160,11 @@ nsGenericHTMLElement::GetURIAttr(nsIAtom* aAttr, nsIAtom* aBaseAttr, attr->GetStringValue(), GetOwnerDoc(), baseURI); - if (isURIAttr) { + // We may have to re-resolve all our cached hrefs when the document's base + // URI changes. The base URI depends on the owner document, but it's the + // current document that keeps track of links. If the two documents don't + // match, we shouldn't cache. + if (isURIAttr && GetOwnerDoc() == GetCurrentDoc()) { const_cast(attr)->CacheURIValue(*aURI); } return PR_TRUE; diff --git a/content/html/content/src/nsHTMLAnchorElement.cpp b/content/html/content/src/nsHTMLAnchorElement.cpp index ccdef3a5932..dfa9dc9b770 100644 --- a/content/html/content/src/nsHTMLAnchorElement.cpp +++ b/content/html/content/src/nsHTMLAnchorElement.cpp @@ -127,6 +127,8 @@ public: virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const; + virtual void DropCachedHref(); + protected: void ResetLinkCacheState(); @@ -486,6 +488,18 @@ nsHTMLAnchorElement::ParseAttribute(PRInt32 aNamespaceID, aResult); } +void +nsHTMLAnchorElement::DropCachedHref() +{ + nsAttrValue* attr = + const_cast(mAttrsAndChildren.GetAttr(nsGkAtoms::href)); + + if (!attr || attr->Type() != nsAttrValue::eLazyURIValue) + return; + + attr->DropCachedURI(); +} + void nsHTMLAnchorElement::ResetLinkCacheState() { diff --git a/content/html/content/src/nsHTMLSharedElement.cpp b/content/html/content/src/nsHTMLSharedElement.cpp index 1b0dcd3c522..e5c07438a9a 100644 --- a/content/html/content/src/nsHTMLSharedElement.cpp +++ b/content/html/content/src/nsHTMLSharedElement.cpp @@ -47,6 +47,7 @@ #include "nsPresContext.h" #include "nsRuleData.h" #include "nsMappedAttributes.h" +#include "nsNetUtil.h" // XXX nav4 has type= start= (same as OL/UL) extern nsAttrValue::EnumTable kListTypeTable[]; @@ -102,13 +103,31 @@ public: nsIAtom* aAttribute, const nsAString& aValue, nsAttrValue& aResult); + nsresult SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName, + const nsAString& aValue, PRBool aNotify) + { + return SetAttr(aNameSpaceID, aName, nsnull, aValue, aNotify); + } + virtual nsresult SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName, + nsIAtom* aPrefix, const nsAString& aValue, + PRBool aNotify); + + virtual nsresult UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aName, + PRBool aNotify); + + virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent, + nsIContent* aBindingParent, + PRBool aCompileEventHandlers); + + virtual void UnbindFromTree(PRBool aDeep = PR_TRUE, + PRBool aNullParent = PR_TRUE); + virtual nsMapRuleToAttributesFunc GetAttributeMappingFunction() const; NS_IMETHOD_(PRBool) IsAttributeMapped(const nsIAtom* aAttribute) const; virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const; }; - NS_IMPL_NS_NEW_HTML_ELEMENT(Shared) @@ -385,6 +404,156 @@ nsHTMLSharedElement::IsAttributeMapped(const nsIAtom* aAttribute) const return nsGenericHTMLElement::IsAttributeMapped(aAttribute); } +nsresult +nsHTMLSharedElement::SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName, + nsIAtom* aPrefix, const nsAString& aValue, + PRBool aNotify) +{ + nsresult rv = nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix, + aValue, aNotify); + + // If the href attribute of a tag is changing, we may need to update + // the document's base URI, which will cause all the links on the page to be + // re-resolved given the new base. + if (NS_SUCCEEDED(rv) && + mNodeInfo->Equals(nsGkAtoms::base, kNameSpaceID_XHTML) && + aName == nsGkAtoms::href && + aNameSpaceID == kNameSpaceID_None && + GetOwnerDoc() == GetCurrentDoc()) { + + nsIDocument* doc = GetCurrentDoc(); + NS_ENSURE_TRUE(doc, NS_OK); + + // We become the first base node with an href if + // * there's no other base node with an href, or + // * we come before the first base node with an href (this would happen + // if we didn't have an href before this call to SetAttr). + // Additionally, we call doc->SetFirstBaseNodeWithHref if we're the first + // base node with an href so the document updates its base URI with our new + // href. + nsIContent* firstBase = doc->GetFirstBaseNodeWithHref(); + if (!firstBase || this == firstBase || + nsContentUtils::PositionIsBefore(this, firstBase)) { + + return doc->SetFirstBaseNodeWithHref(this); + } + } + + return rv; +} + +// Helper function for nsHTMLSharedElement::UnbindFromTree. Finds and returns +// the first tag with an href attribute which is a child of elem, if one +// exists. +static nsIContent* +FindBaseRecursive(nsINode * const elem) +{ + // We can't use NS_GetContentList to get the list of elements, because + // that flushes content notifications, and we need this function to work in + // UnbindFromTree. Once we land the HTML5 parser and get rid of content + // notifications, we should fix this up. (bug 515819) + + PRUint32 childCount; + nsIContent * const * child = elem->GetChildArray(&childCount); + nsIContent * const * end = child + childCount; + for ( ; child != end; child++) { + nsIContent *childElem = *child; + + if (childElem->NodeInfo()->Equals(nsGkAtoms::base, kNameSpaceID_XHTML) && + childElem->HasAttr(kNameSpaceID_None, nsGkAtoms::href)) + return childElem; + + nsIContent* base = FindBaseRecursive(childElem); + if (base) + return base; + } + + return nsnull; +} + +nsresult +nsHTMLSharedElement::UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aName, + PRBool aNotify) +{ + nsresult rv = nsGenericHTMLElement::UnsetAttr(aNameSpaceID, aName, aNotify); + + // If we're the first with an href and our href attribute is being + // unset, then we're no longer the first with an href, and we need to + // find the new one. + if (NS_SUCCEEDED(rv) && + mNodeInfo->Equals(nsGkAtoms::base, kNameSpaceID_XHTML) && + aName == nsGkAtoms::href && + aNameSpaceID == kNameSpaceID_None && + GetOwnerDoc() == GetCurrentDoc()) { + + nsIDocument* doc = GetCurrentDoc(); + NS_ENSURE_TRUE(doc, NS_OK); + + // If we're not the first in the document, then unsetting our href + // doesn't affect the document's base URI. + if (this != doc->GetFirstBaseNodeWithHref()) + return NS_OK; + + // We're the first base, but we don't have an href; find the first base + // which does have an href, and set the document's first base to that. + nsIContent* newBaseNode = FindBaseRecursive(doc); + return doc->SetFirstBaseNodeWithHref(newBaseNode); + } + + return rv; +} + +nsresult +nsHTMLSharedElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, + nsIContent* aBindingParent, + PRBool aCompileEventHandlers) +{ + nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent, + aBindingParent, + aCompileEventHandlers); + + // The document stores a pointer to its first element, which we may + // need to update here. + if (NS_SUCCEEDED(rv) && + mNodeInfo->Equals(nsGkAtoms::base, kNameSpaceID_XHTML) && + HasAttr(kNameSpaceID_None, nsGkAtoms::href) && + aDocument) { + + // If there's no in the document, or if this comes before the one + // that's currently there, set the document's first to this. + nsINode* curBaseNode = aDocument->GetFirstBaseNodeWithHref(); + if (!curBaseNode || + nsContentUtils::PositionIsBefore(this, curBaseNode)) { + + aDocument->SetFirstBaseNodeWithHref(this); + } + } + + return rv; +} + +void +nsHTMLSharedElement::UnbindFromTree(PRBool aDeep, PRBool aNullParent) +{ + nsCOMPtr doc = GetCurrentDoc(); + + nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent); + + // If we're removing a from a document, we may need to update the + // document's record of the first base node. + if (doc && mNodeInfo->Equals(nsGkAtoms::base, kNameSpaceID_XHTML)) { + + // If we're not the first base node, then we don't need to do anything. + if (this != doc->GetFirstBaseNodeWithHref()) + return; + + // If we were the first base node, we need to find the new first base. + + nsIContent* newBaseNode = FindBaseRecursive(doc); + doc->SetFirstBaseNodeWithHref(newBaseNode); + } +} + nsMapRuleToAttributesFunc nsHTMLSharedElement::GetAttributeMappingFunction() const { diff --git a/content/html/content/test/Makefile.in b/content/html/content/test/Makefile.in index 90376825cb1..1ad604beb89 100644 --- a/content/html/content/test/Makefile.in +++ b/content/html/content/test/Makefile.in @@ -71,6 +71,10 @@ _TEST_FILES = test_bug589.html \ bug277890_load.html \ test_bug277890.html \ test_bug287465.html \ + test_bug209275.xhtml \ + file_bug209275_1.html \ + file_bug209275_2.html \ + file_bug209275_3.html \ test_bug295561.html \ test_bug300691-1.html \ test_bug300691-2.html \ diff --git a/content/html/content/test/file_bug209275_1.html b/content/html/content/test/file_bug209275_1.html new file mode 100644 index 00000000000..3f7233876b5 --- /dev/null +++ b/content/html/content/test/file_bug209275_1.html @@ -0,0 +1,28 @@ + + + + + + +Initial state + + + + + diff --git a/content/html/content/test/file_bug209275_2.html b/content/html/content/test/file_bug209275_2.html new file mode 100644 index 00000000000..36e9ff4672e --- /dev/null +++ b/content/html/content/test/file_bug209275_2.html @@ -0,0 +1,23 @@ + + + + + + +Page 2 initial state + + + + + diff --git a/content/html/content/test/file_bug209275_3.html b/content/html/content/test/file_bug209275_3.html new file mode 100644 index 00000000000..b28536c0763 --- /dev/null +++ b/content/html/content/test/file_bug209275_3.html @@ -0,0 +1,23 @@ + + + + + + +Initial state + + + + + diff --git a/content/html/content/test/test_bug209275.xhtml b/content/html/content/test/test_bug209275.xhtml new file mode 100644 index 00000000000..a52986b88a5 --- /dev/null +++ b/content/html/content/test/test_bug209275.xhtml @@ -0,0 +1,261 @@ + +]> + + + + Test for Bug 209275 + + + + + + + + + + + + + + + + + + + +Mozilla Bug 209275 +

+

+
+ link1 +
+ link2 +
+ + link4 + colorlink + link5 + + + + + + + + +
+
+
+
+ + +