1/*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com)
4 * Copyright (C) 2006, 2007 Nicholas Shanks (webkit@nickshanks.com)
5 * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
6 * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org>
7 * Copyright (C) 2007, 2008 Eric Seidel <eric@webkit.org>
8 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
9 * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
10 * Copyright (C) Research In Motion Limited 2011. All rights reserved.
11 * Copyright (C) 2012 Google Inc. All rights reserved.
12 *
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Library General Public
15 * License as published by the Free Software Foundation; either
16 * version 2 of the License, or (at your option) any later version.
17 *
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Library General Public License for more details.
22 *
23 * You should have received a copy of the GNU Library General Public License
24 * along with this library; see the file COPYING.LIB. If not, write to
25 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
26 * Boston, MA 02110-1301, USA.
27 */
28
29#include "config.h"
30#include "VisitedLinkState.h"
31
32#include "ElementIterator.h"
33#include "Frame.h"
34#include "HTMLAnchorElement.h"
35#include "Page.h"
36#include "SVGAElement.h"
37#include "SVGNames.h"
38#include "VisitedLinkStore.h"
39#include "XLinkNames.h"
40
41namespace WebCore {
42
43using namespace HTMLNames;
44
45inline static const AtomString* linkAttribute(const Element& element)
46{
47 if (!element.isLink())
48 return nullptr;
49 if (element.isHTMLElement())
50 return &element.attributeWithoutSynchronization(HTMLNames::hrefAttr);
51 if (element.isSVGElement())
52 return &element.getAttribute(SVGNames::hrefAttr, XLinkNames::hrefAttr);
53 return nullptr;
54}
55
56VisitedLinkState::VisitedLinkState(Document& document)
57 : m_document(document)
58{
59}
60
61void VisitedLinkState::invalidateStyleForAllLinks()
62{
63 if (m_linksCheckedForVisitedState.isEmpty())
64 return;
65 for (auto& element : descendantsOfType<Element>(m_document)) {
66 if (element.isLink())
67 element.invalidateStyleForSubtree();
68 }
69}
70
71inline static Optional<SharedStringHash> linkHashForElement(const Element& element)
72{
73 if (is<HTMLAnchorElement>(element))
74 return downcast<HTMLAnchorElement>(element).visitedLinkHash();
75 if (is<SVGAElement>(element))
76 return downcast<SVGAElement>(element).visitedLinkHash();
77 return WTF::nullopt;
78}
79
80void VisitedLinkState::invalidateStyleForLink(SharedStringHash linkHash)
81{
82 if (!m_linksCheckedForVisitedState.contains(linkHash))
83 return;
84 for (auto& element : descendantsOfType<Element>(m_document)) {
85 if (element.isLink() && linkHashForElement(element) == linkHash)
86 element.invalidateStyleForSubtree();
87 }
88}
89
90InsideLink VisitedLinkState::determineLinkStateSlowCase(const Element& element)
91{
92 ASSERT(element.isLink());
93
94 const AtomString* attribute = linkAttribute(element);
95 if (!attribute || attribute->isNull())
96 return InsideLink::NotInside;
97
98 auto hashIfFound = linkHashForElement(element);
99
100 if (!hashIfFound)
101 return attribute->isEmpty() ? InsideLink::InsideVisited : InsideLink::InsideUnvisited;
102
103 auto hash = *hashIfFound;
104
105 // An empty href (hash==0) refers to the document itself which is always visited. It is useful to check this explicitly so
106 // that visited links can be tested in platform independent manner, without explicit support in the test harness.
107 if (!hash)
108 return InsideLink::InsideVisited;
109
110 Frame* frame = element.document().frame();
111 if (!frame)
112 return InsideLink::InsideUnvisited;
113
114 Page* page = frame->page();
115 if (!page)
116 return InsideLink::InsideUnvisited;
117
118 m_linksCheckedForVisitedState.add(hash);
119
120 if (!page->visitedLinkStore().isLinkVisited(*page, hash, element.document().baseURL(), *attribute))
121 return InsideLink::InsideUnvisited;
122
123 return InsideLink::InsideVisited;
124}
125
126} // namespace WebCore
127