OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #ifndef CSSAnimationUpdate_h | 5 #ifndef CSSAnimationUpdate_h |
6 #define CSSAnimationUpdate_h | 6 #define CSSAnimationUpdate_h |
7 | 7 |
8 #include "core/animation/AnimationStack.h" | 8 #include "core/animation/AnimationStack.h" |
9 #include "core/animation/Interpolation.h" | 9 #include "core/animation/Interpolation.h" |
10 #include "core/animation/KeyframeEffectModel.h" | 10 #include "core/animation/KeyframeEffectModel.h" |
11 #include "core/animation/css/CSSAnimatableValueFactory.h" | 11 #include "core/animation/css/CSSAnimatableValueFactory.h" |
12 #include "core/animation/css/CSSPropertyEquality.h" | 12 #include "core/animation/css/CSSPropertyEquality.h" |
13 #include "core/css/CSSKeyframesRule.h" | 13 #include "core/css/CSSKeyframesRule.h" |
14 #include "core/layout/LayoutObject.h" | 14 #include "core/layout/LayoutObject.h" |
15 #include "wtf/HashMap.h" | 15 #include "wtf/HashMap.h" |
16 #include "wtf/Vector.h" | 16 #include "wtf/Vector.h" |
17 #include "wtf/text/AtomicString.h" | 17 #include "wtf/text/AtomicString.h" |
18 | 18 |
19 namespace blink { | 19 namespace blink { |
20 | 20 |
21 class Animation; | 21 class Animation; |
22 class InertEffect; | 22 class InertEffect; |
23 | 23 |
24 // This class stores the CSS Animations/Transitions information we use during a
style recalc. | 24 // This class stores the CSS Animations/Transitions information we use during a
style recalc. |
25 // This includes updates to animations/transitions as well as the Interpolations
to be applied. | 25 // This includes updates to animations/transitions as well as the Interpolations
to be applied. |
26 class CSSAnimationUpdate final : public NoBaseWillBeGarbageCollectedFinalized<CS
SAnimationUpdate> { | 26 class CSSAnimationUpdate final : public GarbageCollectedFinalized<CSSAnimationUp
date> { |
27 public: | 27 public: |
28 class NewAnimation { | 28 class NewAnimation { |
29 ALLOW_ONLY_INLINE_ALLOCATION(); | 29 ALLOW_ONLY_INLINE_ALLOCATION(); |
30 public: | 30 public: |
31 NewAnimation() | 31 NewAnimation() |
32 : styleRuleVersion(0) | 32 : styleRuleVersion(0) |
33 { | 33 { |
34 } | 34 } |
35 | 35 |
36 NewAnimation(AtomicString name, PassRefPtrWillBeRawPtr<InertEffect> effe
ct, Timing timing, PassRefPtrWillBeRawPtr<StyleRuleKeyframes> styleRule) | 36 NewAnimation(AtomicString name, InertEffect* effect, Timing timing, Pass
RefPtrWillBeRawPtr<StyleRuleKeyframes> styleRule) |
37 : name(name) | 37 : name(name) |
38 , effect(effect) | 38 , effect(effect) |
39 , timing(timing) | 39 , timing(timing) |
40 , styleRule(styleRule) | 40 , styleRule(styleRule) |
41 , styleRuleVersion(this->styleRule->version()) | 41 , styleRuleVersion(this->styleRule->version()) |
42 { | 42 { |
43 } | 43 } |
44 | 44 |
45 DEFINE_INLINE_TRACE() | 45 DEFINE_INLINE_TRACE() |
46 { | 46 { |
47 visitor->trace(effect); | 47 visitor->trace(effect); |
48 visitor->trace(styleRule); | 48 visitor->trace(styleRule); |
49 } | 49 } |
50 | 50 |
51 AtomicString name; | 51 AtomicString name; |
52 RefPtrWillBeMember<InertEffect> effect; | 52 Member<InertEffect> effect; |
53 Timing timing; | 53 Timing timing; |
54 RefPtrWillBeMember<StyleRuleKeyframes> styleRule; | 54 RefPtrWillBeMember<StyleRuleKeyframes> styleRule; |
55 unsigned styleRuleVersion; | 55 unsigned styleRuleVersion; |
56 }; | 56 }; |
57 | 57 |
58 class UpdatedAnimation { | 58 class UpdatedAnimation { |
59 ALLOW_ONLY_INLINE_ALLOCATION(); | 59 ALLOW_ONLY_INLINE_ALLOCATION(); |
60 public: | 60 public: |
61 UpdatedAnimation() | 61 UpdatedAnimation() |
62 : styleRuleVersion(0) | 62 : styleRuleVersion(0) |
63 { | 63 { |
64 } | 64 } |
65 | 65 |
66 UpdatedAnimation(AtomicString name, Animation* animation, PassRefPtrWill
BeRawPtr<InertEffect> effect, Timing specifiedTiming, PassRefPtrWillBeRawPtr<Sty
leRuleKeyframes> styleRule) | 66 UpdatedAnimation(AtomicString name, Animation* animation, InertEffect* e
ffect, Timing specifiedTiming, PassRefPtrWillBeRawPtr<StyleRuleKeyframes> styleR
ule) |
67 : name(name) | 67 : name(name) |
68 , animation(animation) | 68 , animation(animation) |
69 , effect(effect) | 69 , effect(effect) |
70 , specifiedTiming(specifiedTiming) | 70 , specifiedTiming(specifiedTiming) |
71 , styleRule(styleRule) | 71 , styleRule(styleRule) |
72 , styleRuleVersion(this->styleRule->version()) | 72 , styleRuleVersion(this->styleRule->version()) |
73 { | 73 { |
74 } | 74 } |
75 | 75 |
76 DEFINE_INLINE_TRACE() | 76 DEFINE_INLINE_TRACE() |
77 { | 77 { |
78 visitor->trace(animation); | 78 visitor->trace(animation); |
79 visitor->trace(effect); | 79 visitor->trace(effect); |
80 visitor->trace(styleRule); | 80 visitor->trace(styleRule); |
81 } | 81 } |
82 | 82 |
83 AtomicString name; | 83 AtomicString name; |
84 RawPtrWillBeMember<Animation> animation; | 84 Member<Animation> animation; |
85 RefPtrWillBeMember<InertEffect> effect; | 85 Member<InertEffect> effect; |
86 Timing specifiedTiming; | 86 Timing specifiedTiming; |
87 RefPtrWillBeMember<StyleRuleKeyframes> styleRule; | 87 RefPtrWillBeMember<StyleRuleKeyframes> styleRule; |
88 unsigned styleRuleVersion; | 88 unsigned styleRuleVersion; |
89 }; | 89 }; |
90 | 90 |
91 class UpdatedAnimationStyle { | 91 class UpdatedAnimationStyle { |
92 ALLOW_ONLY_INLINE_ALLOCATION(); | 92 ALLOW_ONLY_INLINE_ALLOCATION(); |
93 public: | 93 public: |
94 struct CompositableStyleSnapshot { | 94 struct CompositableStyleSnapshot { |
95 DISALLOW_ALLOCATION(); | 95 DISALLOW_ALLOCATION(); |
96 | 96 |
97 public: | 97 public: |
98 RefPtrWillBeMember<AnimatableValue> opacity; | 98 Member<AnimatableValue> opacity; |
99 RefPtrWillBeMember<AnimatableValue> transform; | 99 Member<AnimatableValue> transform; |
100 RefPtrWillBeMember<AnimatableValue> webkitFilter; | 100 Member<AnimatableValue> webkitFilter; |
101 | 101 |
102 DEFINE_INLINE_TRACE() | 102 DEFINE_INLINE_TRACE() |
103 { | 103 { |
104 visitor->trace(opacity); | 104 visitor->trace(opacity); |
105 visitor->trace(transform); | 105 visitor->trace(transform); |
106 visitor->trace(webkitFilter); | 106 visitor->trace(webkitFilter); |
107 } | 107 } |
108 }; | 108 }; |
109 | 109 |
110 UpdatedAnimationStyle() | 110 UpdatedAnimationStyle() |
111 { | 111 { |
112 } | 112 } |
113 | 113 |
114 UpdatedAnimationStyle(Animation* animation, KeyframeEffectModelBase* mod
el, const UpdatedAnimationStyle::CompositableStyleSnapshot& snapshot) | 114 UpdatedAnimationStyle(Animation* animation, KeyframeEffectModelBase* mod
el, const UpdatedAnimationStyle::CompositableStyleSnapshot& snapshot) |
115 : animation(animation) | 115 : animation(animation) |
116 , model(model) | 116 , model(model) |
117 , snapshot(snapshot) | 117 , snapshot(snapshot) |
118 { | 118 { |
119 } | 119 } |
120 | 120 |
121 DEFINE_INLINE_TRACE() | 121 DEFINE_INLINE_TRACE() |
122 { | 122 { |
123 visitor->trace(animation); | 123 visitor->trace(animation); |
124 visitor->trace(model); | 124 visitor->trace(model); |
125 visitor->trace(snapshot); | 125 visitor->trace(snapshot); |
126 } | 126 } |
127 | 127 |
128 RawPtrWillBeMember<Animation> animation; | 128 Member<Animation> animation; |
129 RawPtrWillBeMember<KeyframeEffectModelBase> model; | 129 Member<KeyframeEffectModelBase> model; |
130 CompositableStyleSnapshot snapshot; | 130 CompositableStyleSnapshot snapshot; |
131 }; | 131 }; |
132 | 132 |
133 void startAnimation(const AtomicString& animationName, PassRefPtrWillBeRawPt
r<InertEffect> effect, const Timing& timing, PassRefPtrWillBeRawPtr<StyleRuleKey
frames> styleRule) | 133 void startAnimation(const AtomicString& animationName, InertEffect* effect,
const Timing& timing, PassRefPtrWillBeRawPtr<StyleRuleKeyframes> styleRule) |
134 { | 134 { |
135 effect->setName(animationName); | 135 effect->setName(animationName); |
136 m_newAnimations.append(NewAnimation(animationName, effect, timing, style
Rule)); | 136 m_newAnimations.append(NewAnimation(animationName, effect, timing, style
Rule)); |
137 } | 137 } |
138 // Returns whether animation has been suppressed and should be filtered duri
ng style application. | 138 // Returns whether animation has been suppressed and should be filtered duri
ng style application. |
139 bool isSuppressedAnimation(const Animation* animation) const { return m_supp
ressedAnimations.contains(animation); } | 139 bool isSuppressedAnimation(const Animation* animation) const { return m_supp
ressedAnimations.contains(animation); } |
140 void cancelAnimation(const AtomicString& name, Animation& animation) | 140 void cancelAnimation(const AtomicString& name, Animation& animation) |
141 { | 141 { |
142 m_cancelledAnimationNames.append(name); | 142 m_cancelledAnimationNames.append(name); |
143 m_suppressedAnimations.add(&animation); | 143 m_suppressedAnimations.add(&animation); |
144 } | 144 } |
145 void toggleAnimationPaused(const AtomicString& name) | 145 void toggleAnimationPaused(const AtomicString& name) |
146 { | 146 { |
147 m_animationsWithPauseToggled.append(name); | 147 m_animationsWithPauseToggled.append(name); |
148 } | 148 } |
149 void updateAnimation(const AtomicString& name, Animation* animation, PassRef
PtrWillBeRawPtr<InertEffect> effect, const Timing& specifiedTiming, | 149 void updateAnimation(const AtomicString& name, Animation* animation, InertEf
fect* effect, const Timing& specifiedTiming, PassRefPtrWillBeRawPtr<StyleRuleKey
frames> styleRule) |
150 PassRefPtrWillBeRawPtr<StyleRuleKeyframes> styleRule) | |
151 { | 150 { |
152 m_animationsWithUpdates.append(UpdatedAnimation(name, animation, effect,
specifiedTiming, styleRule)); | 151 m_animationsWithUpdates.append(UpdatedAnimation(name, animation, effect,
specifiedTiming, styleRule)); |
153 m_suppressedAnimations.add(animation); | 152 m_suppressedAnimations.add(animation); |
154 } | 153 } |
155 void updateAnimationStyle(Animation* animation, KeyframeEffectModelBase* mod
el, LayoutObject* layoutObject, const ComputedStyle& newStyle) | 154 void updateAnimationStyle(Animation* animation, KeyframeEffectModelBase* mod
el, LayoutObject* layoutObject, const ComputedStyle& newStyle) |
156 { | 155 { |
157 UpdatedAnimationStyle::CompositableStyleSnapshot snapshot; | 156 UpdatedAnimationStyle::CompositableStyleSnapshot snapshot; |
158 if (layoutObject) { | 157 if (layoutObject) { |
159 const ComputedStyle& oldStyle = layoutObject->styleRef(); | 158 const ComputedStyle& oldStyle = layoutObject->styleRef(); |
160 if (!CSSPropertyEquality::propertiesEqual(CSSPropertyOpacity, oldSty
le, newStyle) && model->affects(PropertyHandle(CSSPropertyOpacity))) | 159 if (!CSSPropertyEquality::propertiesEqual(CSSPropertyOpacity, oldSty
le, newStyle) && model->affects(PropertyHandle(CSSPropertyOpacity))) |
161 snapshot.opacity = CSSAnimatableValueFactory::create(CSSProperty
Opacity, newStyle); | 160 snapshot.opacity = CSSAnimatableValueFactory::create(CSSProperty
Opacity, newStyle); |
162 if (!CSSPropertyEquality::propertiesEqual(CSSPropertyTransform, oldS
tyle, newStyle) && model->affects(PropertyHandle(CSSPropertyTransform))) | 161 if (!CSSPropertyEquality::propertiesEqual(CSSPropertyTransform, oldS
tyle, newStyle) && model->affects(PropertyHandle(CSSPropertyTransform))) |
163 snapshot.transform = CSSAnimatableValueFactory::create(CSSProper
tyTransform, newStyle); | 162 snapshot.transform = CSSAnimatableValueFactory::create(CSSProper
tyTransform, newStyle); |
164 if (!CSSPropertyEquality::propertiesEqual(CSSPropertyWebkitFilter, o
ldStyle, newStyle) && model->affects(PropertyHandle(CSSPropertyWebkitFilter))) | 163 if (!CSSPropertyEquality::propertiesEqual(CSSPropertyWebkitFilter, o
ldStyle, newStyle) && model->affects(PropertyHandle(CSSPropertyWebkitFilter))) |
165 snapshot.webkitFilter = CSSAnimatableValueFactory::create(CSSPro
pertyWebkitFilter, newStyle); | 164 snapshot.webkitFilter = CSSAnimatableValueFactory::create(CSSPro
pertyWebkitFilter, newStyle); |
166 } | 165 } |
167 | 166 |
168 m_animationsWithStyleUpdates.append(UpdatedAnimationStyle(animation, mod
el, snapshot)); | 167 m_animationsWithStyleUpdates.append(UpdatedAnimationStyle(animation, mod
el, snapshot)); |
169 } | 168 } |
170 | 169 |
171 void startTransition(CSSPropertyID id, const AnimatableValue* from, const An
imatableValue* to, PassRefPtrWillBeRawPtr<InertEffect> effect) | 170 void startTransition(CSSPropertyID id, const AnimatableValue* from, const An
imatableValue* to, InertEffect* effect) |
172 { | 171 { |
173 effect->setName(getPropertyName(id)); | 172 effect->setName(getPropertyName(id)); |
174 NewTransition newTransition; | 173 NewTransition newTransition; |
175 newTransition.id = id; | 174 newTransition.id = id; |
176 newTransition.from = from; | 175 newTransition.from = from; |
177 newTransition.to = to; | 176 newTransition.to = to; |
178 newTransition.effect = effect; | 177 newTransition.effect = effect; |
179 m_newTransitions.set(id, newTransition); | 178 m_newTransitions.set(id, newTransition); |
180 } | 179 } |
181 bool isCancelledTransition(CSSPropertyID id) const { return m_cancelledTrans
itions.contains(id); } | 180 bool isCancelledTransition(CSSPropertyID id) const { return m_cancelledTrans
itions.contains(id); } |
182 void cancelTransition(CSSPropertyID id) { m_cancelledTransitions.add(id); } | 181 void cancelTransition(CSSPropertyID id) { m_cancelledTransitions.add(id); } |
183 void finishTransition(CSSPropertyID id) { m_finishedTransitions.add(id); } | 182 void finishTransition(CSSPropertyID id) { m_finishedTransitions.add(id); } |
184 | 183 |
185 const WillBeHeapVector<NewAnimation>& newAnimations() const { return m_newAn
imations; } | 184 const HeapVector<NewAnimation>& newAnimations() const { return m_newAnimatio
ns; } |
186 const Vector<AtomicString>& cancelledAnimationNames() const { return m_cance
lledAnimationNames; } | 185 const Vector<AtomicString>& cancelledAnimationNames() const { return m_cance
lledAnimationNames; } |
187 const WillBeHeapHashSet<RawPtrWillBeMember<const Animation>>& suppressedAnim
ations() const { return m_suppressedAnimations; } | 186 const HeapHashSet<Member<const Animation>>& suppressedAnimations() const { r
eturn m_suppressedAnimations; } |
188 const Vector<AtomicString>& animationsWithPauseToggled() const { return m_an
imationsWithPauseToggled; } | 187 const Vector<AtomicString>& animationsWithPauseToggled() const { return m_an
imationsWithPauseToggled; } |
189 const WillBeHeapVector<UpdatedAnimation>& animationsWithUpdates() const { re
turn m_animationsWithUpdates; } | 188 const HeapVector<UpdatedAnimation>& animationsWithUpdates() const { return m
_animationsWithUpdates; } |
190 const WillBeHeapVector<UpdatedAnimationStyle>& animationsWithStyleUpdates()
const { return m_animationsWithStyleUpdates; } | 189 const HeapVector<UpdatedAnimationStyle>& animationsWithStyleUpdates() const
{ return m_animationsWithStyleUpdates; } |
191 | 190 |
192 struct NewTransition { | 191 struct NewTransition { |
193 ALLOW_ONLY_INLINE_ALLOCATION(); | 192 ALLOW_ONLY_INLINE_ALLOCATION(); |
194 public: | 193 public: |
195 DEFINE_INLINE_TRACE() | 194 DEFINE_INLINE_TRACE() |
196 { | 195 { |
197 visitor->trace(from); | 196 visitor->trace(from); |
198 visitor->trace(to); | 197 visitor->trace(to); |
199 visitor->trace(effect); | 198 visitor->trace(effect); |
200 } | 199 } |
201 | 200 |
202 CSSPropertyID id; | 201 CSSPropertyID id; |
203 RawPtrWillBeMember<const AnimatableValue> from; | 202 Member<const AnimatableValue> from; |
204 RawPtrWillBeMember<const AnimatableValue> to; | 203 Member<const AnimatableValue> to; |
205 RefPtrWillBeMember<InertEffect> effect; | 204 Member<InertEffect> effect; |
206 }; | 205 }; |
207 using NewTransitionMap = WillBeHeapHashMap<CSSPropertyID, NewTransition>; | 206 using NewTransitionMap = HeapHashMap<CSSPropertyID, NewTransition>; |
208 const NewTransitionMap& newTransitions() const { return m_newTransitions; } | 207 const NewTransitionMap& newTransitions() const { return m_newTransitions; } |
209 const HashSet<CSSPropertyID>& cancelledTransitions() const { return m_cancel
ledTransitions; } | 208 const HashSet<CSSPropertyID>& cancelledTransitions() const { return m_cancel
ledTransitions; } |
210 const HashSet<CSSPropertyID>& finishedTransitions() const { return m_finishe
dTransitions; } | 209 const HashSet<CSSPropertyID>& finishedTransitions() const { return m_finishe
dTransitions; } |
211 | 210 |
212 void adoptActiveInterpolationsForAnimations(ActiveInterpolationMap& newMap)
{ newMap.swap(m_activeInterpolationsForAnimations); } | 211 void adoptActiveInterpolationsForAnimations(ActiveInterpolationMap& newMap)
{ newMap.swap(m_activeInterpolationsForAnimations); } |
213 void adoptActiveInterpolationsForTransitions(ActiveInterpolationMap& newMap)
{ newMap.swap(m_activeInterpolationsForTransitions); } | 212 void adoptActiveInterpolationsForTransitions(ActiveInterpolationMap& newMap)
{ newMap.swap(m_activeInterpolationsForTransitions); } |
214 const ActiveInterpolationMap& activeInterpolationsForAnimations() const { re
turn m_activeInterpolationsForAnimations; } | 213 const ActiveInterpolationMap& activeInterpolationsForAnimations() const { re
turn m_activeInterpolationsForAnimations; } |
215 const ActiveInterpolationMap& activeInterpolationsForTransitions() const { r
eturn m_activeInterpolationsForTransitions; } | 214 const ActiveInterpolationMap& activeInterpolationsForTransitions() const { r
eturn m_activeInterpolationsForTransitions; } |
216 ActiveInterpolationMap& activeInterpolationsForAnimations() { return m_activ
eInterpolationsForAnimations; } | 215 ActiveInterpolationMap& activeInterpolationsForAnimations() { return m_activ
eInterpolationsForAnimations; } |
217 | 216 |
(...skipping 12 matching lines...) Expand all Loading... |
230 && m_activeInterpolationsForTransitions.isEmpty(); | 229 && m_activeInterpolationsForTransitions.isEmpty(); |
231 } | 230 } |
232 | 231 |
233 DECLARE_TRACE(); | 232 DECLARE_TRACE(); |
234 | 233 |
235 private: | 234 private: |
236 // Order is significant since it defines the order in which new animations | 235 // Order is significant since it defines the order in which new animations |
237 // will be started. Note that there may be multiple animations present | 236 // will be started. Note that there may be multiple animations present |
238 // with the same name, due to the way in which we split up animations with | 237 // with the same name, due to the way in which we split up animations with |
239 // incomplete keyframes. | 238 // incomplete keyframes. |
240 WillBeHeapVector<NewAnimation> m_newAnimations; | 239 HeapVector<NewAnimation> m_newAnimations; |
241 Vector<AtomicString> m_cancelledAnimationNames; | 240 Vector<AtomicString> m_cancelledAnimationNames; |
242 WillBeHeapHashSet<RawPtrWillBeMember<const Animation>> m_suppressedAnimation
s; | 241 HeapHashSet<Member<const Animation>> m_suppressedAnimations; |
243 Vector<AtomicString> m_animationsWithPauseToggled; | 242 Vector<AtomicString> m_animationsWithPauseToggled; |
244 WillBeHeapVector<UpdatedAnimation> m_animationsWithUpdates; | 243 HeapVector<UpdatedAnimation> m_animationsWithUpdates; |
245 WillBeHeapVector<UpdatedAnimationStyle> m_animationsWithStyleUpdates; | 244 HeapVector<UpdatedAnimationStyle> m_animationsWithStyleUpdates; |
246 | 245 |
247 NewTransitionMap m_newTransitions; | 246 NewTransitionMap m_newTransitions; |
248 HashSet<CSSPropertyID> m_cancelledTransitions; | 247 HashSet<CSSPropertyID> m_cancelledTransitions; |
249 HashSet<CSSPropertyID> m_finishedTransitions; | 248 HashSet<CSSPropertyID> m_finishedTransitions; |
250 | 249 |
251 ActiveInterpolationMap m_activeInterpolationsForAnimations; | 250 ActiveInterpolationMap m_activeInterpolationsForAnimations; |
252 ActiveInterpolationMap m_activeInterpolationsForTransitions; | 251 ActiveInterpolationMap m_activeInterpolationsForTransitions; |
253 }; | 252 }; |
254 | 253 |
255 } // namespace blink | 254 } // namespace blink |
256 | 255 |
257 WTF_ALLOW_INIT_WITH_MEM_FUNCTIONS(blink::CSSAnimationUpdate::NewAnimation); | 256 WTF_ALLOW_INIT_WITH_MEM_FUNCTIONS(blink::CSSAnimationUpdate::NewAnimation); |
258 WTF_ALLOW_INIT_WITH_MEM_FUNCTIONS(blink::CSSAnimationUpdate::UpdatedAnimation); | 257 WTF_ALLOW_INIT_WITH_MEM_FUNCTIONS(blink::CSSAnimationUpdate::UpdatedAnimation); |
259 WTF_ALLOW_INIT_WITH_MEM_FUNCTIONS(blink::CSSAnimationUpdate::UpdatedAnimationSty
le); | 258 WTF_ALLOW_INIT_WITH_MEM_FUNCTIONS(blink::CSSAnimationUpdate::UpdatedAnimationSty
le); |
260 | 259 |
261 #endif | 260 #endif |
OLD | NEW |