1/*
2 * Copyright (C) 2016 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "ClassChangeInvalidation.h"
28
29#include "ElementChildIterator.h"
30#include "SpaceSplitString.h"
31#include "StyleInvalidationFunctions.h"
32#include "StyleInvalidator.h"
33#include <wtf/BitVector.h>
34
35namespace WebCore {
36namespace Style {
37
38using ClassChangeVector = Vector<AtomicStringImpl*, 4>;
39
40static ClassChangeVector collectClasses(const SpaceSplitString& classes)
41{
42 ClassChangeVector result;
43 result.reserveCapacity(classes.size());
44 for (unsigned i = 0; i < classes.size(); ++i)
45 result.uncheckedAppend(classes[i].impl());
46 return result;
47}
48
49static ClassChangeVector computeClassChange(const SpaceSplitString& oldClasses, const SpaceSplitString& newClasses)
50{
51 unsigned oldSize = oldClasses.size();
52 unsigned newSize = newClasses.size();
53
54 if (!oldSize)
55 return collectClasses(newClasses);
56 if (!newSize)
57 return collectClasses(oldClasses);
58
59 ClassChangeVector changedClasses;
60
61 BitVector remainingClassBits;
62 remainingClassBits.ensureSize(oldSize);
63 // Class vectors tend to be very short. This is faster than using a hash table.
64 for (unsigned i = 0; i < newSize; ++i) {
65 bool foundFromBoth = false;
66 for (unsigned j = 0; j < oldSize; ++j) {
67 if (newClasses[i] == oldClasses[j]) {
68 remainingClassBits.quickSet(j);
69 foundFromBoth = true;
70 }
71 }
72 if (foundFromBoth)
73 continue;
74 changedClasses.append(newClasses[i].impl());
75 }
76 for (unsigned i = 0; i < oldSize; ++i) {
77 // If the bit is not set the corresponding class has been removed.
78 if (remainingClassBits.quickGet(i))
79 continue;
80 changedClasses.append(oldClasses[i].impl());
81 }
82
83 return changedClasses;
84}
85
86void ClassChangeInvalidation::computeInvalidation(const SpaceSplitString& oldClasses, const SpaceSplitString& newClasses)
87{
88 auto changedClasses = computeClassChange(oldClasses, newClasses);
89
90 bool shouldInvalidateCurrent = false;
91 bool mayAffectStyleInShadowTree = false;
92
93 traverseRuleFeatures(m_element, [&] (const RuleFeatureSet& features, bool mayAffectShadowTree) {
94 for (auto* changedClass : changedClasses) {
95 if (mayAffectShadowTree && features.classRules.contains(changedClass))
96 mayAffectStyleInShadowTree = true;
97 if (features.classesAffectingHost.contains(changedClass))
98 shouldInvalidateCurrent = true;
99 }
100 });
101
102 if (mayAffectStyleInShadowTree) {
103 // FIXME: We should do fine-grained invalidation for shadow tree.
104 m_element.invalidateStyleForSubtree();
105 }
106
107 if (shouldInvalidateCurrent)
108 m_element.invalidateStyle();
109
110 auto& ruleSets = m_element.styleResolver().ruleSets();
111
112 for (auto* changedClass : changedClasses) {
113 if (auto* invalidationRuleSets = ruleSets.classInvalidationRuleSets(changedClass)) {
114 for (auto& invalidationRuleSet : *invalidationRuleSets)
115 m_invalidationRuleSets.append(&invalidationRuleSet);
116 }
117 }
118}
119
120void ClassChangeInvalidation::invalidateStyleWithRuleSets()
121{
122 for (auto* invalidationRuleSet : m_invalidationRuleSets) {
123 Invalidator invalidator(*invalidationRuleSet->ruleSet);
124 invalidator.invalidateStyleWithMatchElement(m_element, invalidationRuleSet->matchElement);
125 }
126}
127
128}
129}
130