Diff
Modified: trunk/LayoutTests/ChangeLog (244641 => 244642)
--- trunk/LayoutTests/ChangeLog 2019-04-25 07:52:36 UTC (rev 244641)
+++ trunk/LayoutTests/ChangeLog 2019-04-25 09:55:38 UTC (rev 244642)
@@ -1,3 +1,14 @@
+2019-04-25 Antti Koivisto <[email protected]>
+
+ Visited link hash should be computed only once
+ https://bugs.webkit.org/show_bug.cgi?id=197229
+ <rdar://problem/48438924>
+
+ Reviewed by Alex Christensen.
+
+ * fast/history/visited-href-mutation-expected.html: Added.
+ * fast/history/visited-href-mutation.html: Added.
+
2019-04-25 Philippe Normand <[email protected]>
[GStreamer] gst_element_get_state: assertion 'GST_IS_ELEMENT (element)' failed in WebCore::MediaPlayerPrivateGStreamer::paused
Added: trunk/LayoutTests/fast/history/visited-href-mutation-expected.html (0 => 244642)
--- trunk/LayoutTests/fast/history/visited-href-mutation-expected.html (rev 0)
+++ trunk/LayoutTests/fast/history/visited-href-mutation-expected.html 2019-04-25 09:55:38 UTC (rev 244642)
@@ -0,0 +1,15 @@
+<style>
+.expected-link { color:blue }
+.expected-visited { color:green }
+</style>
+<a href="" class="expected-visited">Case 1</a>
+<a href="" class="expected-visited">Case 2</a>
+<a href="" class="expected-link">Case 3</a>
+<a href="" class="expected-link">Case 4</a>
+<a href="" class="expected-visited">Case 5</a>
+<a href="" class="expected-visited">Case 6</a>
+<a href="" class="expected-link">Case 7</a>
+<a href="" class="expected-link">Case 8</a>
+<a class="">Case 9</a>
+<a href="" class="expected-visited">Case 10</a>
+<a href="" class="expected-link">Case 11</a>
Added: trunk/LayoutTests/fast/history/visited-href-mutation.html (0 => 244642)
--- trunk/LayoutTests/fast/history/visited-href-mutation.html (rev 0)
+++ trunk/LayoutTests/fast/history/visited-href-mutation.html 2019-04-25 09:55:38 UTC (rev 244642)
@@ -0,0 +1,49 @@
+<html>
+<head>
+<script>
+if (window.testRunner) {
+ testRunner.waitUntilDone();
+ testRunner.keepWebHistory();
+}
+
+function setHref(testcase, url)
+{
+ testcase.style.display = "none";
+ testcase.href = ""
+ testcase.style.display = "inline";
+}
+
+function test()
+{
+ setHref(case2, "resources/not-visited.html");
+ setHref(case4, "resources/dummy.html");
+ setHref(case6, "resources/not-visited.html");
+ setHref(case8, "");
+ setHref(case10, "resources/dummy.html");
+ setHref(case11, "resources/not-visited.html");
+
+ if (window.testRunner)
+ testRunner.notifyDone();
+}
+</script>
+<style>
+a:link { color: blue; }
+a:visited { color: green; }
+</style>
+</head>
+<body _onload_="test()">
+<div>
+<a id="case1" href="" 1</a>
+<a id="case2" href="" 2</a>
+<a id="case3" href="" 3</a>
+<a id="case4" href="" 4</a>
+<a id="case5" href="" 5</a>
+<a id="case6" href="" 6</a>
+<a id="case7" href="" 7</a>
+<a id="case8" href="" 8</a>
+<a id="case9">Case 9</a>
+<a id="case10">Case 10</a>
+<a id="case11">Case 11</a>
+</div>
+<iframe src="" style="visibility:hidden"></iframe>
+</body>
Modified: trunk/Source/WebCore/ChangeLog (244641 => 244642)
--- trunk/Source/WebCore/ChangeLog 2019-04-25 07:52:36 UTC (rev 244641)
+++ trunk/Source/WebCore/ChangeLog 2019-04-25 09:55:38 UTC (rev 244642)
@@ -1,3 +1,35 @@
+2019-04-25 Antti Koivisto <[email protected]>
+
+ Visited link hash should be computed only once
+ https://bugs.webkit.org/show_bug.cgi?id=197229
+ <rdar://problem/48438924>
+
+ Reviewed by Alex Christensen.
+
+ Test: fast/history/visited-href-mutation.html
+
+ Visited link style is now based on the first target URL of the link element. Further href mutations don't affect styling.
+
+ * dom/Document.cpp:
+ (WebCore::Document::updateBaseURL):
+ * dom/VisitedLinkState.cpp:
+ (WebCore::linkAttribute):
+ (WebCore::linkHashForElement):
+
+ Visited link support is now limited to HTML and SVG <a> elements.
+
+ (WebCore::VisitedLinkState::invalidateStyleForLink):
+ (WebCore::VisitedLinkState::determineLinkStateSlowCase):
+ * html/HTMLAnchorElement.cpp:
+ (WebCore::HTMLAnchorElement::HTMLAnchorElement):
+ (WebCore::HTMLAnchorElement::parseAttribute):
+ * html/HTMLAnchorElement.h:
+ (WebCore::HTMLAnchorElement::visitedLinkHash const):
+ (WebCore::HTMLAnchorElement::invalidateCachedVisitedLinkHash): Deleted.
+ * svg/SVGAElement.cpp:
+ (WebCore::SVGAElement::visitedLinkHash const):
+ * svg/SVGAElement.h:
+
2019-04-25 Philippe Normand <[email protected]>
[GStreamer] gst_element_get_state: assertion 'GST_IS_ELEMENT (element)' failed in WebCore::MediaPlayerPrivateGStreamer::paused
Modified: trunk/Source/WebCore/dom/Document.cpp (244641 => 244642)
--- trunk/Source/WebCore/dom/Document.cpp 2019-04-25 07:52:36 UTC (rev 244641)
+++ trunk/Source/WebCore/dom/Document.cpp 2019-04-25 09:55:38 UTC (rev 244642)
@@ -3184,13 +3184,6 @@
if (!m_baseURL.isValid())
m_baseURL = URL();
-
- if (!equalIgnoringFragmentIdentifier(oldBaseURL, m_baseURL)) {
- // Base URL change changes any relative visited links.
- // FIXME: There are other URLs in the tree that would need to be re-evaluated on dynamic base URL change. Style should be invalidated too.
- for (auto& anchor : descendantsOfType<HTMLAnchorElement>(*this))
- anchor.invalidateCachedVisitedLinkHash();
- }
}
void Document::setBaseURLOverride(const URL& url)
Modified: trunk/Source/WebCore/dom/VisitedLinkState.cpp (244641 => 244642)
--- trunk/Source/WebCore/dom/VisitedLinkState.cpp 2019-04-25 07:52:36 UTC (rev 244641)
+++ trunk/Source/WebCore/dom/VisitedLinkState.cpp 2019-04-25 09:55:38 UTC (rev 244642)
@@ -33,6 +33,7 @@
#include "Frame.h"
#include "HTMLAnchorElement.h"
#include "Page.h"
+#include "SVGAElement.h"
#include "SVGNames.h"
#include "VisitedLinkStore.h"
#include "XLinkNames.h"
@@ -44,12 +45,12 @@
inline static const AtomicString* linkAttribute(const Element& element)
{
if (!element.isLink())
- return 0;
+ return nullptr;
if (element.isHTMLElement())
return &element.attributeWithoutSynchronization(HTMLNames::hrefAttr);
if (element.isSVGElement())
return &element.getAttribute(SVGNames::hrefAttr, XLinkNames::hrefAttr);
- return 0;
+ return nullptr;
}
VisitedLinkState::VisitedLinkState(Document& document)
@@ -67,13 +68,13 @@
}
}
-inline static SharedStringHash linkHashForElement(Document& document, const Element& element)
+inline static Optional<SharedStringHash> linkHashForElement(const Element& element)
{
if (is<HTMLAnchorElement>(element))
return downcast<HTMLAnchorElement>(element).visitedLinkHash();
- if (const AtomicString* attribute = linkAttribute(element))
- return computeVisitedLinkHash(document.baseURL(), *attribute);
- return 0;
+ if (is<SVGAElement>(element))
+ return downcast<SVGAElement>(element).visitedLinkHash();
+ return WTF::nullopt;
}
void VisitedLinkState::invalidateStyleForLink(SharedStringHash linkHash)
@@ -81,7 +82,7 @@
if (!m_linksCheckedForVisitedState.contains(linkHash))
return;
for (auto& element : descendantsOfType<Element>(m_document)) {
- if (linkHashForElement(m_document, element) == linkHash)
+ if (element.isLink() && linkHashForElement(element) == linkHash)
element.invalidateStyleForSubtree();
}
}
@@ -94,19 +95,17 @@
if (!attribute || attribute->isNull())
return InsideLink::NotInside;
- // An empty href refers to the document itself which is always visited. It is useful to check this explicitly so
- // that visited links can be tested in platform independent manner, without explicit support in the test harness.
- if (attribute->isEmpty())
- return InsideLink::InsideVisited;
+ auto hashIfFound = linkHashForElement(element);
- SharedStringHash hash;
- if (is<HTMLAnchorElement>(element))
- hash = downcast<HTMLAnchorElement>(element).visitedLinkHash();
- else
- hash = computeVisitedLinkHash(element.document().baseURL(), *attribute);
+ if (!hashIfFound)
+ return attribute->isEmpty() ? InsideLink::InsideVisited : InsideLink::InsideUnvisited;
+ auto hash = *hashIfFound;
+
+ // An empty href (hash==0) refers to the document itself which is always visited. It is useful to check this explicitly so
+ // that visited links can be tested in platform independent manner, without explicit support in the test harness.
if (!hash)
- return InsideLink::InsideUnvisited;
+ return InsideLink::InsideVisited;
Frame* frame = element.document().frame();
if (!frame)
Modified: trunk/Source/WebCore/html/HTMLAnchorElement.cpp (244641 => 244642)
--- trunk/Source/WebCore/html/HTMLAnchorElement.cpp 2019-04-25 07:52:36 UTC (rev 244641)
+++ trunk/Source/WebCore/html/HTMLAnchorElement.cpp 2019-04-25 09:55:38 UTC (rev 244642)
@@ -68,7 +68,6 @@
: HTMLElement(tagName, document)
, m_hasRootEditableElementForSelectionOnMouseDown(false)
, m_wasShiftKeyDownOnMouseDown(false)
- , m_cachedVisitedLinkHash(0)
{
}
@@ -248,7 +247,6 @@
document().frame()->loader().client().prefetchDNS(document().completeURL(parsedURL).host().toString());
}
}
- invalidateCachedVisitedLinkHash();
} else if (name == nameAttr || name == titleAttr) {
// Do nothing.
} else if (name == relAttr) {
Modified: trunk/Source/WebCore/html/HTMLAnchorElement.h (244641 => 244642)
--- trunk/Source/WebCore/html/HTMLAnchorElement.h 2019-04-25 07:52:36 UTC (rev 244641)
+++ trunk/Source/WebCore/html/HTMLAnchorElement.h 2019-04-25 09:55:38 UTC (rev 244642)
@@ -66,7 +66,6 @@
bool hasRel(Relation) const;
SharedStringHash visitedLinkHash() const;
- void invalidateCachedVisitedLinkHash() { m_cachedVisitedLinkHash = 0; }
WEBCORE_EXPORT DOMTokenList& relList() const;
@@ -115,16 +114,19 @@
bool m_hasRootEditableElementForSelectionOnMouseDown;
bool m_wasShiftKeyDownOnMouseDown;
OptionSet<Relation> m_linkRelations;
- mutable SharedStringHash m_cachedVisitedLinkHash;
+ // This is computed only once and must not be affected by subsequent URL changes.
+ mutable Optional<SharedStringHash> m_storedVisitedLinkHash;
+
mutable std::unique_ptr<DOMTokenList> m_relList;
};
inline SharedStringHash HTMLAnchorElement::visitedLinkHash() const
{
- if (!m_cachedVisitedLinkHash)
- m_cachedVisitedLinkHash = computeVisitedLinkHash(document().baseURL(), attributeWithoutSynchronization(HTMLNames::hrefAttr));
- return m_cachedVisitedLinkHash;
+ ASSERT(isLink());
+ if (!m_storedVisitedLinkHash)
+ m_storedVisitedLinkHash = computeVisitedLinkHash(document().baseURL(), attributeWithoutSynchronization(HTMLNames::hrefAttr));
+ return *m_storedVisitedLinkHash;
}
// Functions shared with the other anchor elements (i.e., SVG).
Modified: trunk/Source/WebCore/svg/SVGAElement.cpp (244641 => 244642)
--- trunk/Source/WebCore/svg/SVGAElement.cpp 2019-04-25 07:52:36 UTC (rev 244641)
+++ trunk/Source/WebCore/svg/SVGAElement.cpp 2019-04-25 09:55:38 UTC (rev 244642)
@@ -217,4 +217,12 @@
return isLink() || SVGGraphicsElement::willRespondToMouseClickEvents();
}
+SharedStringHash SVGAElement::visitedLinkHash() const
+{
+ ASSERT(isLink());
+ if (!m_storedVisitedLinkHash)
+ m_storedVisitedLinkHash = computeVisitedLinkHash(document().baseURL(), getAttribute(SVGNames::hrefAttr, XLinkNames::hrefAttr));
+ return *m_storedVisitedLinkHash;
+}
+
} // namespace WebCore
Modified: trunk/Source/WebCore/svg/SVGAElement.h (244641 => 244642)
--- trunk/Source/WebCore/svg/SVGAElement.h 2019-04-25 07:52:36 UTC (rev 244641)
+++ trunk/Source/WebCore/svg/SVGAElement.h 2019-04-25 09:55:38 UTC (rev 244642)
@@ -25,6 +25,7 @@
#include "SVGExternalResourcesRequired.h"
#include "SVGGraphicsElement.h"
#include "SVGURIReference.h"
+#include "SharedStringHash.h"
namespace WebCore {
@@ -36,6 +37,8 @@
String target() const final { return m_target->currentValue(); }
Ref<SVGAnimatedString>& targetAnimated() { return m_target; }
+ SharedStringHash visitedLinkHash() const;
+
private:
SVGAElement(const QualifiedName&, Document&);
@@ -63,6 +66,9 @@
PropertyRegistry m_propertyRegistry { *this };
Ref<SVGAnimatedString> m_target { SVGAnimatedString::create(this) };
+
+ // This is computed only once and must not be affected by subsequent URL changes.
+ mutable Optional<SharedStringHash> m_storedVisitedLinkHash;
};
} // namespace WebCore