Title: [244642] trunk
Revision
244642
Author
[email protected]
Date
2019-04-25 02:55:38 -0700 (Thu, 25 Apr 2019)

Log Message

Visited link hash should be computed only once
https://bugs.webkit.org/show_bug.cgi?id=197229
<rdar://problem/48438924>

Reviewed by Alex Christensen.

Source/WebCore:

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:

LayoutTests:

* fast/history/visited-href-mutation-expected.html: Added.
* fast/history/visited-href-mutation.html: Added.

Modified Paths

Added Paths

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
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to