1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "config.h"
6#include "core/html/canvas/HitRegion.h"
7
8#include "core/accessibility/AXObjectCache.h"
9#include "core/rendering/RenderBoxModelObject.h"
10
11namespace blink {
12
13HitRegion::HitRegion(const HitRegionOptionsInternal& options)
14    : m_id(options.id)
15    , m_control(options.control)
16    , m_path(options.path)
17    , m_fillRule(options.fillRule)
18{
19}
20
21void HitRegion::updateAccessibility(Element* canvas)
22{
23    if (!m_control || !canvas || !canvas->renderer() || !m_control->isDescendantOf(canvas))
24        return;
25
26    AXObjectCache* axObjectCache = m_control->document().existingAXObjectCache();
27    if (!axObjectCache)
28        return;
29
30    FloatRect boundingRect = m_path.boundingRect();
31
32    // Offset by the canvas rect, taking border and padding into account.
33    RenderBoxModelObject* rbmo = canvas->renderBoxModelObject();
34    IntRect canvasRect = canvas->renderer()->absoluteBoundingBoxRect();
35    canvasRect.move(rbmo->borderLeft() + rbmo->paddingLeft(),
36        rbmo->borderTop() + rbmo->paddingTop());
37    LayoutRect elementRect = enclosingLayoutRect(boundingRect);
38    elementRect.moveBy(canvasRect.location());
39
40    axObjectCache->setCanvasObjectBounds(m_control.get(), elementRect);
41}
42
43bool HitRegion::contains(const LayoutPoint& point) const
44{
45    return m_path.contains(point, m_fillRule);
46}
47
48void HitRegion::removePixels(const Path& clearArea)
49{
50    m_path.subtractPath(clearArea);
51}
52
53void HitRegion::trace(Visitor* visitor)
54{
55    visitor->trace(m_control);
56}
57
58void HitRegionManager::addHitRegion(PassRefPtrWillBeRawPtr<HitRegion> passHitRegion)
59{
60    RefPtrWillBeRawPtr<HitRegion> hitRegion = passHitRegion;
61
62    m_hitRegionList.add(hitRegion);
63
64    if (!hitRegion->id().isEmpty())
65        m_hitRegionIdMap.set(hitRegion->id(), hitRegion);
66
67    if (hitRegion->control())
68        m_hitRegionControlMap.set(hitRegion->control(), hitRegion);
69}
70
71void HitRegionManager::removeHitRegion(HitRegion* hitRegion)
72{
73    if (!hitRegion)
74        return;
75
76    if (!hitRegion->id().isEmpty())
77        m_hitRegionIdMap.remove(hitRegion->id());
78
79    if (hitRegion->control())
80        m_hitRegionControlMap.remove(hitRegion->control());
81
82    m_hitRegionList.remove(hitRegion);
83}
84
85void HitRegionManager::removeHitRegionById(const String& id)
86{
87    if (!id.isEmpty())
88        removeHitRegion(getHitRegionById(id));
89}
90
91void HitRegionManager::removeHitRegionByControl(Element* control)
92{
93    removeHitRegion(getHitRegionByControl(control));
94}
95
96void HitRegionManager::removeHitRegionsInRect(const FloatRect& rect, const AffineTransform& ctm)
97{
98    Path clearArea;
99    clearArea.addRect(rect);
100    clearArea.transform(ctm);
101
102    HitRegionIterator itEnd = m_hitRegionList.rend();
103    HitRegionList toBeRemoved;
104
105    for (HitRegionIterator it = m_hitRegionList.rbegin(); it != itEnd; ++it) {
106        RefPtrWillBeRawPtr<HitRegion> hitRegion = *it;
107        hitRegion->removePixels(clearArea);
108        if (hitRegion->path().isEmpty())
109            toBeRemoved.add(hitRegion);
110    }
111
112    itEnd = toBeRemoved.rend();
113    for (HitRegionIterator it = toBeRemoved.rbegin(); it != itEnd; ++it)
114        removeHitRegion(it->get());
115}
116
117void HitRegionManager::removeAllHitRegions()
118{
119    m_hitRegionList.clear();
120    m_hitRegionIdMap.clear();
121    m_hitRegionControlMap.clear();
122}
123
124HitRegion* HitRegionManager::getHitRegionById(const String& id) const
125{
126    return m_hitRegionIdMap.get(id);
127}
128
129HitRegion* HitRegionManager::getHitRegionByControl(Element* control) const
130{
131    if (control)
132        return m_hitRegionControlMap.get(control);
133
134    return 0;
135}
136
137HitRegion* HitRegionManager::getHitRegionAtPoint(const LayoutPoint& point) const
138{
139    HitRegionIterator itEnd = m_hitRegionList.rend();
140
141    for (HitRegionIterator it = m_hitRegionList.rbegin(); it != itEnd; ++it) {
142        RefPtrWillBeRawPtr<HitRegion> hitRegion = *it;
143        if (hitRegion->contains(point))
144            return hitRegion.get();
145    }
146
147    return 0;
148}
149
150unsigned HitRegionManager::getHitRegionsCount() const
151{
152    return m_hitRegionList.size();
153}
154
155void HitRegionManager::trace(Visitor* visitor)
156{
157#if ENABLE(OILPAN)
158    visitor->trace(m_hitRegionList);
159    visitor->trace(m_hitRegionIdMap);
160    visitor->trace(m_hitRegionControlMap);
161#endif
162}
163
164DEFINE_EMPTY_DESTRUCTOR_WILL_BE_REMOVED(HitRegionManager)
165
166} // namespace blink
167