1/*
2 * Copyright (C) 2007, 2008, 2012, 2013, 2014 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. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "CSSKeyframesRule.h"
28
29#include "CSSDeferredParser.h"
30#include "CSSKeyframeRule.h"
31#include "CSSParser.h"
32#include "CSSRuleList.h"
33#include "CSSStyleSheet.h"
34#include "Document.h"
35#include <wtf/text/StringBuilder.h>
36
37namespace WebCore {
38
39StyleRuleKeyframes::StyleRuleKeyframes(const AtomicString& name)
40 : StyleRuleBase(Keyframes)
41 , m_name(name)
42{
43}
44
45StyleRuleKeyframes::StyleRuleKeyframes(const AtomicString& name, std::unique_ptr<DeferredStyleGroupRuleList>&& deferredRules)
46 : StyleRuleBase(Keyframes)
47 , m_name(name)
48 , m_deferredRules(WTFMove(deferredRules))
49{
50
51}
52
53StyleRuleKeyframes::StyleRuleKeyframes(const StyleRuleKeyframes& o)
54 : StyleRuleBase(o)
55 , m_name(o.m_name)
56{
57 m_keyframes.reserveInitialCapacity(o.keyframes().size());
58 for (auto& keyframe : o.keyframes())
59 m_keyframes.uncheckedAppend(keyframe.copyRef());
60}
61
62StyleRuleKeyframes::~StyleRuleKeyframes() = default;
63
64void StyleRuleKeyframes::parseDeferredRulesIfNeeded() const
65{
66 if (!m_deferredRules)
67 return;
68
69 m_deferredRules->parseDeferredKeyframes(const_cast<StyleRuleKeyframes&>(*this));
70 m_deferredRules = nullptr;
71}
72
73const Vector<Ref<StyleRuleKeyframe>>& StyleRuleKeyframes::keyframes() const
74{
75 parseDeferredRulesIfNeeded();
76 return m_keyframes;
77}
78
79void StyleRuleKeyframes::parserAppendKeyframe(RefPtr<StyleRuleKeyframe>&& keyframe)
80{
81 if (!keyframe)
82 return;
83 m_keyframes.append(keyframe.releaseNonNull());
84}
85
86void StyleRuleKeyframes::wrapperAppendKeyframe(Ref<StyleRuleKeyframe>&& keyframe)
87{
88 parseDeferredRulesIfNeeded();
89 m_keyframes.append(WTFMove(keyframe));
90}
91
92void StyleRuleKeyframes::wrapperRemoveKeyframe(unsigned index)
93{
94 parseDeferredRulesIfNeeded();
95 m_keyframes.remove(index);
96}
97
98size_t StyleRuleKeyframes::findKeyframeIndex(const String& key) const
99{
100 parseDeferredRulesIfNeeded();
101
102 auto keys = CSSParser::parseKeyframeKeyList(key);
103
104 if (!keys)
105 return notFound;
106
107 for (size_t i = m_keyframes.size(); i--; ) {
108 if (m_keyframes[i]->keys() == *keys)
109 return i;
110 }
111
112 return notFound;
113}
114
115CSSKeyframesRule::CSSKeyframesRule(StyleRuleKeyframes& keyframesRule, CSSStyleSheet* parent)
116 : CSSRule(parent)
117 , m_keyframesRule(keyframesRule)
118 , m_childRuleCSSOMWrappers(keyframesRule.keyframes().size())
119{
120}
121
122CSSKeyframesRule::~CSSKeyframesRule()
123{
124 ASSERT(m_childRuleCSSOMWrappers.size() == m_keyframesRule->keyframes().size());
125
126 for (unsigned i = 0; i < m_childRuleCSSOMWrappers.size(); ++i) {
127 if (m_childRuleCSSOMWrappers[i])
128 m_childRuleCSSOMWrappers[i]->setParentRule(0);
129 }
130}
131
132void CSSKeyframesRule::setName(const String& name)
133{
134 CSSStyleSheet::RuleMutationScope mutationScope(this);
135
136 m_keyframesRule->setName(name);
137}
138
139void CSSKeyframesRule::appendRule(const String& ruleText)
140{
141 ASSERT(m_childRuleCSSOMWrappers.size() == m_keyframesRule->keyframes().size());
142
143 CSSParser parser(parserContext());
144 RefPtr<StyleRuleKeyframe> keyframe = parser.parseKeyframeRule(ruleText);
145 if (!keyframe)
146 return;
147
148 CSSStyleSheet::RuleMutationScope mutationScope(this);
149
150 m_keyframesRule->wrapperAppendKeyframe(keyframe.releaseNonNull());
151
152 m_childRuleCSSOMWrappers.grow(length());
153}
154
155void CSSKeyframesRule::insertRule(const String& ruleText)
156{
157 if (CSSStyleSheet* parent = parentStyleSheet()) {
158 if (Document* ownerDocument = parent->ownerDocument())
159 ownerDocument->addConsoleMessage(MessageSource::JS, MessageLevel::Warning, "CSSKeyframesRule 'insertRule' function is deprecated. Use 'appendRule' instead."_s);
160 }
161 appendRule(ruleText);
162}
163
164void CSSKeyframesRule::deleteRule(const String& s)
165{
166 ASSERT(m_childRuleCSSOMWrappers.size() == m_keyframesRule->keyframes().size());
167
168 size_t i = m_keyframesRule->findKeyframeIndex(s);
169 if (i == notFound)
170 return;
171
172 CSSStyleSheet::RuleMutationScope mutationScope(this);
173
174 m_keyframesRule->wrapperRemoveKeyframe(i);
175
176 if (m_childRuleCSSOMWrappers[i])
177 m_childRuleCSSOMWrappers[i]->setParentRule(0);
178 m_childRuleCSSOMWrappers.remove(i);
179}
180
181CSSKeyframeRule* CSSKeyframesRule::findRule(const String& s)
182{
183 size_t i = m_keyframesRule->findKeyframeIndex(s);
184 return i != notFound ? item(i) : nullptr;
185}
186
187String CSSKeyframesRule::cssText() const
188{
189 StringBuilder result;
190 result.appendLiteral("@-webkit-keyframes ");
191 result.append(name());
192 result.appendLiteral(" { \n");
193
194 unsigned size = length();
195 for (unsigned i = 0; i < size; ++i) {
196 result.appendLiteral(" ");
197 result.append(m_keyframesRule->keyframes()[i]->cssText());
198 result.append('\n');
199 }
200 result.append('}');
201 return result.toString();
202}
203
204unsigned CSSKeyframesRule::length() const
205{
206 return m_keyframesRule->keyframes().size();
207}
208
209CSSKeyframeRule* CSSKeyframesRule::item(unsigned index) const
210{
211 if (index >= length())
212 return nullptr;
213 ASSERT(m_childRuleCSSOMWrappers.size() == m_keyframesRule->keyframes().size());
214 auto& rule = m_childRuleCSSOMWrappers[index];
215 if (!rule)
216 rule = adoptRef(*new CSSKeyframeRule(m_keyframesRule->keyframes()[index], const_cast<CSSKeyframesRule*>(this)));
217 return rule.get();
218}
219
220CSSRuleList& CSSKeyframesRule::cssRules()
221{
222 if (!m_ruleListCSSOMWrapper)
223 m_ruleListCSSOMWrapper = std::make_unique<LiveCSSRuleList<CSSKeyframesRule>>(*this);
224 return *m_ruleListCSSOMWrapper;
225}
226
227void CSSKeyframesRule::reattach(StyleRuleBase& rule)
228{
229 ASSERT_WITH_SECURITY_IMPLICATION(rule.isKeyframesRule());
230 m_keyframesRule = static_cast<StyleRuleKeyframes&>(rule);
231}
232
233} // namespace WebCore
234