mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 787134 - Make links not in a document or with an invalid href respond to :link selector. r=bzbarsky
This commit is contained in:
parent
4de5d23b78
commit
eef8ddc2e4
@ -27,10 +27,9 @@ struct IMEState;
|
||||
} // namespace mozilla
|
||||
|
||||
enum nsLinkState {
|
||||
eLinkState_Unknown = 0,
|
||||
eLinkState_Unvisited = 1,
|
||||
eLinkState_Visited = 2,
|
||||
eLinkState_NotLink = 3
|
||||
eLinkState_NotLink = 3
|
||||
};
|
||||
|
||||
// IID for the nsIContent interface
|
||||
|
@ -24,7 +24,8 @@ namespace dom {
|
||||
Link::Link(Element *aElement)
|
||||
: mElement(aElement)
|
||||
, mHistory(services::GetHistoryService())
|
||||
, mLinkState(defaultState)
|
||||
, mLinkState(eLinkState_NotLink)
|
||||
, mNeedsRegistration(false)
|
||||
, mRegistered(false)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(mElement, "Must have an element");
|
||||
@ -35,13 +36,18 @@ Link::~Link()
|
||||
UnregisterFromHistory();
|
||||
}
|
||||
|
||||
bool
|
||||
Link::ElementHasHref() const
|
||||
{
|
||||
return ((!mElement->IsSVG() && mElement->HasAttr(kNameSpaceID_None, nsGkAtoms::href))
|
||||
|| (!mElement->IsHTML() && mElement->HasAttr(kNameSpaceID_XLink, nsGkAtoms::href)));
|
||||
}
|
||||
|
||||
nsLinkState
|
||||
Link::GetLinkState() const
|
||||
{
|
||||
NS_ASSERTION(mRegistered,
|
||||
"Getting the link state of an unregistered Link!");
|
||||
NS_ASSERTION(mLinkState != eLinkState_Unknown,
|
||||
"Getting the link state with an unknown value!");
|
||||
return nsLinkState(mLinkState);
|
||||
}
|
||||
|
||||
@ -74,36 +80,28 @@ Link::LinkState() const
|
||||
// track that state. Cast away that constness!
|
||||
Link *self = const_cast<Link *>(this);
|
||||
|
||||
// If we are not in the document, default to not visited.
|
||||
Element *element = self->mElement;
|
||||
if (!element->IsInDoc()) {
|
||||
self->mLinkState = eLinkState_Unvisited;
|
||||
}
|
||||
|
||||
// If we have not yet registered for notifications and are in an unknown
|
||||
// state, register now!
|
||||
if (!mRegistered && mLinkState == eLinkState_Unknown) {
|
||||
// First, make sure the href attribute has a valid link (bug 23209).
|
||||
// If we have not yet registered for notifications and need to,
|
||||
// due to our href changing, register now!
|
||||
if (!mRegistered && mNeedsRegistration && element->IsInDoc()) {
|
||||
// Only try and register once.
|
||||
self->mNeedsRegistration = false;
|
||||
|
||||
nsCOMPtr<nsIURI> hrefURI(GetURI());
|
||||
if (!hrefURI) {
|
||||
self->mLinkState = eLinkState_NotLink;
|
||||
return nsEventStates();
|
||||
}
|
||||
|
||||
// Assume that we are not visited until we are told otherwise.
|
||||
self->mLinkState = eLinkState_Unvisited;
|
||||
|
||||
// We have a good href, so register with History.
|
||||
if (mHistory) {
|
||||
// Make sure the href attribute has a valid link (bug 23209).
|
||||
// If we have a good href, register with History if available.
|
||||
if (mHistory && hrefURI) {
|
||||
nsresult rv = mHistory->RegisterVisitedCallback(hrefURI, self);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
self->mRegistered = true;
|
||||
|
||||
// And make sure we are in the document's link map.
|
||||
nsIDocument *doc = element->GetCurrentDoc();
|
||||
if (doc) {
|
||||
doc->AddStyleRelevantLink(self);
|
||||
}
|
||||
doc->GetCurrentDoc()->AddStyleRelevantLink(self);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -135,8 +133,8 @@ Link::GetURI() const
|
||||
Element *element = self->mElement;
|
||||
uri = element->GetHrefURI();
|
||||
|
||||
// We want to cache the URI if the node is in the document.
|
||||
if (uri && element->IsInDoc()) {
|
||||
// We want to cache the URI if we have it
|
||||
if (uri) {
|
||||
mCachedURI = uri;
|
||||
}
|
||||
|
||||
@ -424,39 +422,56 @@ Link::GetHash(nsAString &_hash)
|
||||
}
|
||||
|
||||
void
|
||||
Link::ResetLinkState(bool aNotify)
|
||||
Link::ResetLinkState(bool aNotify, bool aHasHref)
|
||||
{
|
||||
// If we are in our default state, bail early.
|
||||
if (mLinkState == defaultState) {
|
||||
return;
|
||||
nsLinkState defaultState;
|
||||
|
||||
// The default state for links with an href is unvisited.
|
||||
if (aHasHref) {
|
||||
defaultState = eLinkState_Unvisited;
|
||||
} else {
|
||||
defaultState = eLinkState_NotLink;
|
||||
}
|
||||
|
||||
Element *element = mElement;
|
||||
// If !mNeedsRegstration, then either we've never registered, or we're
|
||||
// currently registered; in either case, we should remove ourself
|
||||
// from the doc and the history.
|
||||
if (!mNeedsRegistration && mLinkState != eLinkState_NotLink) {
|
||||
nsIDocument *doc = mElement->GetCurrentDoc();
|
||||
if (doc && (mRegistered || mLinkState == eLinkState_Visited)) {
|
||||
// Tell the document to forget about this link if we've registered
|
||||
// with it before.
|
||||
doc->ForgetLink(this);
|
||||
}
|
||||
|
||||
// Tell the document to forget about this link if we were a link before.
|
||||
nsIDocument *doc = element->GetCurrentDoc();
|
||||
if (doc && mLinkState != eLinkState_NotLink) {
|
||||
doc->ForgetLink(this);
|
||||
UnregisterFromHistory();
|
||||
}
|
||||
|
||||
UnregisterFromHistory();
|
||||
// If we have an href, we should register with the history.
|
||||
mNeedsRegistration = aHasHref;
|
||||
|
||||
// If we've cached the URI, reset always invalidates it.
|
||||
mCachedURI = nullptr;
|
||||
|
||||
// Update our state back to the default.
|
||||
mLinkState = defaultState;
|
||||
|
||||
// Get rid of our cached URI.
|
||||
mCachedURI = nullptr;
|
||||
|
||||
// We have to be very careful here: if aNotify is false we do NOT
|
||||
// want to call UpdateState, because that will call into LinkState()
|
||||
// and try to start off loads, etc. But ResetLinkState is called
|
||||
// with aNotify false when things are in inconsistent states, so
|
||||
// we'll get confused in that situation. Instead, just silently
|
||||
// update the link state on mElement.
|
||||
// update the link state on mElement. Since we might have set the
|
||||
// link state to unvisited, make sure to update with that state if
|
||||
// required.
|
||||
if (aNotify) {
|
||||
mElement->UpdateState(aNotify);
|
||||
} else {
|
||||
mElement->UpdateLinkState(nsEventStates());
|
||||
if (mLinkState == eLinkState_Unvisited) {
|
||||
mElement->UpdateLinkState(NS_EVENT_STATE_UNVISITED);
|
||||
} else {
|
||||
mElement->UpdateLinkState(nsEventStates());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,8 +28,6 @@ class Link : public nsISupports
|
||||
public:
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(MOZILLA_DOM_LINK_IMPLEMENTATION_IID)
|
||||
|
||||
static const nsLinkState defaultState = eLinkState_Unknown;
|
||||
|
||||
/**
|
||||
* aElement is the element pointer corresponding to this link.
|
||||
*/
|
||||
@ -77,7 +75,7 @@ public:
|
||||
* true if ResetLinkState should notify the owning document about style
|
||||
* changes or false if it should not.
|
||||
*/
|
||||
void ResetLinkState(bool aNotify);
|
||||
void ResetLinkState(bool aNotify, bool aHasHref);
|
||||
|
||||
// This method nevers returns a null element.
|
||||
Element* GetElement() const { return mElement; }
|
||||
@ -103,6 +101,8 @@ public:
|
||||
virtual size_t
|
||||
SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const;
|
||||
|
||||
bool ElementHasHref() const;
|
||||
|
||||
protected:
|
||||
virtual ~Link();
|
||||
|
||||
@ -141,6 +141,8 @@ private:
|
||||
|
||||
uint16_t mLinkState;
|
||||
|
||||
bool mNeedsRegistration;
|
||||
|
||||
bool mRegistered;
|
||||
};
|
||||
|
||||
|
@ -7269,7 +7269,7 @@ nsDocument::RefreshLinkHrefs()
|
||||
// Reset all of our styled links.
|
||||
nsAutoScriptBlocker scriptBlocker;
|
||||
for (LinkArray::size_type i = 0; i < linksToNotify.Length(); i++) {
|
||||
linksToNotify[i]->ResetLinkState(true);
|
||||
linksToNotify[i]->ResetLinkState(true, linksToNotify[i]->ElementHasHref());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -219,7 +219,7 @@ nsHTMLAnchorElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
||||
nsIContent* aBindingParent,
|
||||
bool aCompileEventHandlers)
|
||||
{
|
||||
Link::ResetLinkState(false);
|
||||
Link::ResetLinkState(false, Link::ElementHasHref());
|
||||
|
||||
nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
|
||||
aBindingParent,
|
||||
@ -257,7 +257,7 @@ nsHTMLAnchorElement::UnbindFromTree(bool aDeep, bool aNullParent)
|
||||
|
||||
// If this link is ever reinserted into a document, it might
|
||||
// be under a different xml:base, so forget the cached state now.
|
||||
Link::ResetLinkState(false);
|
||||
Link::ResetLinkState(false, Link::ElementHasHref());
|
||||
|
||||
nsIDocument* doc = GetCurrentDoc();
|
||||
if (doc) {
|
||||
@ -466,7 +466,7 @@ nsHTMLAnchorElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
|
||||
// that content states have changed will call IntrinsicState, which will try
|
||||
// to get updated information about the visitedness from Link.
|
||||
if (reset) {
|
||||
Link::ResetLinkState(!!aNotify);
|
||||
Link::ResetLinkState(!!aNotify, true);
|
||||
}
|
||||
|
||||
return rv;
|
||||
@ -485,7 +485,7 @@ nsHTMLAnchorElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute,
|
||||
// that content states have changed will call IntrinsicState, which will try
|
||||
// to get updated information about the visitedness from Link.
|
||||
if (aAttribute == nsGkAtoms::href && kNameSpaceID_None == aNameSpaceID) {
|
||||
Link::ResetLinkState(!!aNotify);
|
||||
Link::ResetLinkState(!!aNotify, false);
|
||||
}
|
||||
|
||||
return rv;
|
||||
|
@ -187,7 +187,7 @@ nsHTMLAreaElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
||||
nsIContent* aBindingParent,
|
||||
bool aCompileEventHandlers)
|
||||
{
|
||||
Link::ResetLinkState(false);
|
||||
Link::ResetLinkState(false, Link::ElementHasHref());
|
||||
if (aDocument) {
|
||||
aDocument->RegisterPendingLinkUpdate(this);
|
||||
}
|
||||
@ -202,7 +202,7 @@ nsHTMLAreaElement::UnbindFromTree(bool aDeep, bool aNullParent)
|
||||
{
|
||||
// If this link is ever reinserted into a document, it might
|
||||
// be under a different xml:base, so forget the cached state now.
|
||||
Link::ResetLinkState(false);
|
||||
Link::ResetLinkState(false, Link::ElementHasHref());
|
||||
|
||||
nsIDocument* doc = GetCurrentDoc();
|
||||
if (doc) {
|
||||
@ -226,7 +226,7 @@ nsHTMLAreaElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
|
||||
// that content states have changed will call IntrinsicState, which will try
|
||||
// to get updated information about the visitedness from Link.
|
||||
if (aName == nsGkAtoms::href && aNameSpaceID == kNameSpaceID_None) {
|
||||
Link::ResetLinkState(!!aNotify);
|
||||
Link::ResetLinkState(!!aNotify, true);
|
||||
}
|
||||
|
||||
return rv;
|
||||
@ -245,7 +245,7 @@ nsHTMLAreaElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute,
|
||||
// that content states have changed will call IntrinsicState, which will try
|
||||
// to get updated information about the visitedness from Link.
|
||||
if (aAttribute == nsGkAtoms::href && kNameSpaceID_None == aNameSpaceID) {
|
||||
Link::ResetLinkState(!!aNotify);
|
||||
Link::ResetLinkState(!!aNotify, false);
|
||||
}
|
||||
|
||||
return rv;
|
||||
|
@ -212,7 +212,7 @@ nsHTMLLinkElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
||||
nsIContent* aBindingParent,
|
||||
bool aCompileEventHandlers)
|
||||
{
|
||||
Link::ResetLinkState(false);
|
||||
Link::ResetLinkState(false, Link::ElementHasHref());
|
||||
|
||||
nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
|
||||
aBindingParent,
|
||||
@ -250,7 +250,7 @@ nsHTMLLinkElement::UnbindFromTree(bool aDeep, bool aNullParent)
|
||||
{
|
||||
// If this link is ever reinserted into a document, it might
|
||||
// be under a different xml:base, so forget the cached state now.
|
||||
Link::ResetLinkState(false);
|
||||
Link::ResetLinkState(false, Link::ElementHasHref());
|
||||
|
||||
// Once we have XPCOMGC we shouldn't need to call UnbindFromTree during Unlink
|
||||
// and so this messy event dispatch can go away.
|
||||
@ -322,7 +322,7 @@ nsHTMLLinkElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
|
||||
// that content states have changed will call IntrinsicState, which will try
|
||||
// to get updated information about the visitedness from Link.
|
||||
if (aName == nsGkAtoms::href && kNameSpaceID_None == aNameSpaceID) {
|
||||
Link::ResetLinkState(!!aNotify);
|
||||
Link::ResetLinkState(!!aNotify, true);
|
||||
}
|
||||
|
||||
if (NS_SUCCEEDED(rv) && aNameSpaceID == kNameSpaceID_None &&
|
||||
@ -370,7 +370,7 @@ nsHTMLLinkElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute,
|
||||
// that content states have changed will call IntrinsicState, which will try
|
||||
// to get updated information about the visitedness from Link.
|
||||
if (aAttribute == nsGkAtoms::href && kNameSpaceID_None == aNameSpaceID) {
|
||||
Link::ResetLinkState(!!aNotify);
|
||||
Link::ResetLinkState(!!aNotify, false);
|
||||
}
|
||||
|
||||
return rv;
|
||||
|
@ -269,6 +269,7 @@ MOCHITEST_FILES = \
|
||||
test_bug780993.html \
|
||||
test_bug786564.html \
|
||||
test_bug797113.html \
|
||||
test_bug787134.html \
|
||||
test_iframe_sandbox_inheritance.html \
|
||||
file_iframe_sandbox_a_if1.html \
|
||||
file_iframe_sandbox_a_if2.html \
|
||||
|
29
content/html/content/test/test_bug787134.html
Normal file
29
content/html/content/test/test_bug787134.html
Normal file
@ -0,0 +1,29 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=787134
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 787134</title>
|
||||
<script type="application/javascript" src="/MochiKit/packed.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script src="reflect.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=787134">Mozilla Bug 787134</a>
|
||||
<p id="display"></p>
|
||||
<p><a id="link-test1" href="example link">example link</a></p>
|
||||
<pre id="test">
|
||||
<script>
|
||||
var div = document.createElement('div');
|
||||
div.innerHTML = '<a href=#></a>';
|
||||
var a = div.firstChild;
|
||||
ok(a.mozMatchesSelector(':link'), "Should match a link not in a document");
|
||||
is(div.querySelector(':link'), a, "Should find a link not in a document");
|
||||
a = document.querySelector('#link-test1');
|
||||
ok(a.mozMatchesSelector(':link'), "Should match a link in a document with an invalid URL");
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -48,7 +48,7 @@ nsMathMLElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
||||
{
|
||||
static const char kMathMLStyleSheetURI[] = "resource://gre-resources/mathml.css";
|
||||
|
||||
Link::ResetLinkState(false);
|
||||
Link::ResetLinkState(false, Link::ElementHasHref());
|
||||
|
||||
nsresult rv = nsMathMLElementBase::BindToTree(aDocument, aParent,
|
||||
aBindingParent,
|
||||
@ -83,7 +83,7 @@ nsMathMLElement::UnbindFromTree(bool aDeep, bool aNullParent)
|
||||
{
|
||||
// If this link is ever reinserted into a document, it might
|
||||
// be under a different xml:base, so forget the cached state now.
|
||||
Link::ResetLinkState(false);
|
||||
Link::ResetLinkState(false, Link::ElementHasHref());
|
||||
|
||||
nsIDocument* doc = GetCurrentDoc();
|
||||
if (doc) {
|
||||
@ -793,7 +793,7 @@ nsMathMLElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
|
||||
if (aName == nsGkAtoms::href &&
|
||||
(aNameSpaceID == kNameSpaceID_None ||
|
||||
aNameSpaceID == kNameSpaceID_XLink)) {
|
||||
Link::ResetLinkState(!!aNotify);
|
||||
Link::ResetLinkState(!!aNotify, true);
|
||||
}
|
||||
|
||||
return rv;
|
||||
@ -813,7 +813,9 @@ nsMathMLElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttr,
|
||||
if (aAttr == nsGkAtoms::href &&
|
||||
(aNameSpaceID == kNameSpaceID_None ||
|
||||
aNameSpaceID == kNameSpaceID_XLink)) {
|
||||
Link::ResetLinkState(!!aNotify);
|
||||
// Note: just because we removed a single href attr doesn't mean there's no href,
|
||||
// since there are 2 possible namespaces.
|
||||
Link::ResetLinkState(!!aNotify, Link::ElementHasHref());
|
||||
}
|
||||
|
||||
return rv;
|
||||
|
@ -110,7 +110,7 @@ nsSVGAElement::BindToTree(nsIDocument *aDocument, nsIContent *aParent,
|
||||
nsIContent *aBindingParent,
|
||||
bool aCompileEventHandlers)
|
||||
{
|
||||
Link::ResetLinkState(false);
|
||||
Link::ResetLinkState(false, Link::ElementHasHref());
|
||||
|
||||
nsresult rv = nsSVGAElementBase::BindToTree(aDocument, aParent,
|
||||
aBindingParent,
|
||||
@ -129,7 +129,7 @@ nsSVGAElement::UnbindFromTree(bool aDeep, bool aNullParent)
|
||||
{
|
||||
// If this link is ever reinserted into a document, it might
|
||||
// be under a different xml:base, so forget the cached state now.
|
||||
Link::ResetLinkState(false);
|
||||
Link::ResetLinkState(false, Link::ElementHasHref());
|
||||
|
||||
nsIDocument* doc = GetCurrentDoc();
|
||||
if (doc) {
|
||||
@ -282,7 +282,7 @@ nsSVGAElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
|
||||
// that content states have changed will call IntrinsicState, which will try
|
||||
// to get updated information about the visitedness from Link.
|
||||
if (aName == nsGkAtoms::href && aNameSpaceID == kNameSpaceID_XLink) {
|
||||
Link::ResetLinkState(!!aNotify);
|
||||
Link::ResetLinkState(!!aNotify, true);
|
||||
}
|
||||
|
||||
return rv;
|
||||
@ -300,7 +300,7 @@ nsSVGAElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttr,
|
||||
// that content states have changed will call IntrinsicState, which will try
|
||||
// to get updated information about the visitedness from Link.
|
||||
if (aAttr == nsGkAtoms::href && aNameSpaceID == kNameSpaceID_XLink) {
|
||||
Link::ResetLinkState(!!aNotify);
|
||||
Link::ResetLinkState(!!aNotify, false);
|
||||
}
|
||||
|
||||
return rv;
|
||||
|
@ -72,7 +72,7 @@ namespace dom {
|
||||
|
||||
Link::Link(Element* aElement)
|
||||
: mElement(aElement)
|
||||
, mLinkState(mozilla::dom::Link::defaultState)
|
||||
, mLinkState(eLinkState_NotLink)
|
||||
, mRegistered(false)
|
||||
{
|
||||
}
|
||||
@ -81,11 +81,18 @@ Link::~Link()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
Link::ElementHasHref() const
|
||||
{
|
||||
NS_NOTREACHED("Unexpected call to Link::ElementHasHref");
|
||||
return false; // suppress compiler warning
|
||||
}
|
||||
|
||||
nsLinkState
|
||||
Link::GetLinkState() const
|
||||
{
|
||||
NS_NOTREACHED("Unexpected call to Link::GetLinkState");
|
||||
return eLinkState_Unknown; // suppress compiler warning
|
||||
return eLinkState_NotLink; // suppress compiler warning
|
||||
}
|
||||
|
||||
void
|
||||
@ -95,7 +102,7 @@ Link::SetLinkState(nsLinkState aState)
|
||||
}
|
||||
|
||||
void
|
||||
Link::ResetLinkState(bool aNotify)
|
||||
Link::ResetLinkState(bool aNotify, bool aHasHref)
|
||||
{
|
||||
NS_NOTREACHED("Unexpected call to Link::ResetLinkState");
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user