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 "PageGroup.h"
37#include "VisitedLinkStore.h"
38#include "XLinkNames.h"
39
40namespace WebCore {
41
42using namespace HTMLNames;
43
44inline static const AtomicString* linkAttribute(Element& element)
45{
46    if (!element.isLink())
47        return 0;
48    if (element.isHTMLElement())
49        return &element.fastGetAttribute(HTMLNames::hrefAttr);
50    if (element.isSVGElement())
51        return &element.getAttribute(XLinkNames::hrefAttr);
52    return 0;
53}
54
55VisitedLinkState::VisitedLinkState(Document& document)
56    : m_document(document)
57{
58}
59
60void VisitedLinkState::invalidateStyleForAllLinks()
61{
62    if (m_linksCheckedForVisitedState.isEmpty())
63        return;
64    for (auto& element : descendantsOfType<Element>(m_document)) {
65        if (element.isLink())
66            element.setNeedsStyleRecalc();
67    }
68}
69
70inline static LinkHash linkHashForElement(Document& document, Element& element)
71{
72    if (isHTMLAnchorElement(element))
73        return toHTMLAnchorElement(element).visitedLinkHash();
74    if (const AtomicString* attribute = linkAttribute(element))
75        return WebCore::visitedLinkHash(document.baseURL(), *attribute);
76    return 0;
77}
78
79void VisitedLinkState::invalidateStyleForLink(LinkHash linkHash)
80{
81    if (!m_linksCheckedForVisitedState.contains(linkHash))
82        return;
83    for (auto& element : descendantsOfType<Element>(m_document)) {
84        if (linkHashForElement(m_document, element) == linkHash)
85            element.setNeedsStyleRecalc();
86    }
87}
88
89EInsideLink VisitedLinkState::determineLinkStateSlowCase(Element& element)
90{
91    ASSERT(element.isLink());
92
93    const AtomicString* attribute = linkAttribute(element);
94    if (!attribute || attribute->isNull())
95        return NotInsideLink;
96
97    // An empty href refers to the document itself which is always visited. It is useful to check this explicitly so
98    // that visited links can be tested in platform independent manner, without explicit support in the test harness.
99    if (attribute->isEmpty())
100        return InsideVisitedLink;
101
102    LinkHash hash;
103    if (isHTMLAnchorElement(element))
104        hash = toHTMLAnchorElement(element).visitedLinkHash();
105    else
106        hash = WebCore::visitedLinkHash(element.document().baseURL(), *attribute);
107
108    if (!hash)
109        return InsideUnvisitedLink;
110
111    Frame* frame = element.document().frame();
112    if (!frame)
113        return InsideUnvisitedLink;
114
115    Page* page = frame->page();
116    if (!page)
117        return InsideUnvisitedLink;
118
119    m_linksCheckedForVisitedState.add(hash);
120
121    if (!page->visitedLinkStore().isLinkVisited(*page, hash, element.document().baseURL(), *attribute))
122        return InsideUnvisitedLink;
123
124    return InsideVisitedLink;
125}
126
127} // namespace WebCore
128