Bug 1936278 - Prevent search mode chiclet from being dismissed when clicking in page...
[gecko.git] / dom / svg / SVGAnimatedViewBox.cpp
blobf2058bf99a684aa3e93fd0afdb8226872b62af94
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "SVGAnimatedViewBox.h"
9 #include "mozAutoDocUpdate.h"
10 #include "mozilla/Maybe.h"
11 #include <utility>
13 #include "SVGViewBoxSMILType.h"
14 #include "mozilla/SMILValue.h"
15 #include "mozilla/SVGContentUtils.h"
16 #include "mozilla/dom/SVGRect.h"
17 #include "nsCharSeparatedTokenizer.h"
18 #include "nsTextFormatter.h"
20 using namespace mozilla::dom;
22 namespace mozilla {
24 #define NUM_VIEWBOX_COMPONENTS 4
26 /* Implementation of SVGViewBox methods */
28 bool SVGViewBox::operator==(const SVGViewBox& aOther) const {
29 if (&aOther == this) return true;
31 return (none && aOther.none) ||
32 (!none && !aOther.none && x == aOther.x && y == aOther.y &&
33 width == aOther.width && height == aOther.height);
36 /* static */
37 nsresult SVGViewBox::FromString(const nsAString& aStr, SVGViewBox* aViewBox) {
38 if (aStr.EqualsLiteral("none")) {
39 aViewBox->none = true;
40 return NS_OK;
43 nsCharSeparatedTokenizerTemplate<nsContentUtils::IsHTMLWhitespace,
44 nsTokenizerFlags::SeparatorOptional>
45 tokenizer(aStr, ',');
46 float vals[NUM_VIEWBOX_COMPONENTS];
47 uint32_t i;
48 for (i = 0; i < NUM_VIEWBOX_COMPONENTS && tokenizer.hasMoreTokens(); ++i) {
49 if (!SVGContentUtils::ParseNumber(tokenizer.nextToken(), vals[i])) {
50 return NS_ERROR_DOM_SYNTAX_ERR;
54 if (i != NUM_VIEWBOX_COMPONENTS || // Too few values.
55 tokenizer.hasMoreTokens() || // Too many values.
56 tokenizer.separatorAfterCurrentToken()) { // Trailing comma.
57 return NS_ERROR_DOM_SYNTAX_ERR;
60 aViewBox->x = vals[0];
61 aViewBox->y = vals[1];
62 aViewBox->width = vals[2];
63 aViewBox->height = vals[3];
64 aViewBox->none = false;
66 return NS_OK;
69 MOZ_CONSTINIT static SVGAttrTearoffTable<SVGAnimatedViewBox, SVGRect>
70 sBaseSVGViewBoxTearoffTable;
71 MOZ_CONSTINIT static SVGAttrTearoffTable<SVGAnimatedViewBox, SVGRect>
72 sAnimSVGViewBoxTearoffTable;
73 MOZ_CONSTINIT SVGAttrTearoffTable<SVGAnimatedViewBox, SVGAnimatedRect>
74 SVGAnimatedViewBox::sSVGAnimatedRectTearoffTable;
76 //----------------------------------------------------------------------
77 // Helper class: AutoChangeViewBoxNotifier
78 // Stack-based helper class to pair calls to WillChangeViewBox and
79 // DidChangeViewBox.
80 class MOZ_RAII AutoChangeViewBoxNotifier {
81 public:
82 AutoChangeViewBoxNotifier(SVGAnimatedViewBox* aViewBox,
83 SVGElement* aSVGElement, bool aDoSetAttr = true)
84 : mViewBox(aViewBox), mSVGElement(aSVGElement), mDoSetAttr(aDoSetAttr) {
85 MOZ_ASSERT(mViewBox, "Expecting non-null viewBox");
86 MOZ_ASSERT(mSVGElement, "Expecting non-null element");
88 if (mDoSetAttr) {
89 mUpdateBatch.emplace(aSVGElement->GetComposedDoc(), true);
90 mEmptyOrOldValue = mSVGElement->WillChangeViewBox(mUpdateBatch.ref());
94 ~AutoChangeViewBoxNotifier() {
95 if (mDoSetAttr) {
96 mSVGElement->DidChangeViewBox(mEmptyOrOldValue, mUpdateBatch.ref());
98 if (mViewBox->mAnimVal) {
99 mSVGElement->AnimationNeedsResample();
103 private:
104 SVGAnimatedViewBox* const mViewBox;
105 SVGElement* const mSVGElement;
106 Maybe<mozAutoDocUpdate> mUpdateBatch;
107 nsAttrValue mEmptyOrOldValue;
108 bool mDoSetAttr;
111 /* Implementation of SVGAnimatedViewBox methods */
113 void SVGAnimatedViewBox::Init() {
114 mHasBaseVal = false;
115 // We shouldn't use mBaseVal for rendering (its usages should be guarded with
116 // "mHasBaseVal" checks), but just in case we do by accident, this will
117 // ensure that we treat it as "none" and ignore its numeric values:
118 mBaseVal.none = true;
120 mAnimVal = nullptr;
123 bool SVGAnimatedViewBox::HasRect() const {
124 // Check mAnimVal if we have one; otherwise, check mBaseVal if we have one;
125 // otherwise, just return false (we clearly do not have a rect).
126 const SVGViewBox* rect = mAnimVal.get();
127 if (!rect) {
128 if (!mHasBaseVal) {
129 // no anim val, no base val --> no viewbox rect
130 return false;
132 rect = &mBaseVal;
135 return !rect->none && rect->width >= 0 && rect->height >= 0;
138 void SVGAnimatedViewBox::SetAnimValue(const SVGViewBox& aRect,
139 SVGElement* aSVGElement) {
140 if (!mAnimVal) {
141 // it's okay if allocation fails - and no point in reporting that
142 mAnimVal = MakeUnique<SVGViewBox>(aRect);
143 } else {
144 if (aRect == *mAnimVal) {
145 return;
147 *mAnimVal = aRect;
149 aSVGElement->DidAnimateViewBox();
152 void SVGAnimatedViewBox::SetBaseValue(const SVGViewBox& aRect,
153 SVGElement* aSVGElement) {
154 if (!mHasBaseVal || mBaseVal == aRect) {
155 // This method is used to set a single x, y, width
156 // or height value. It can't create a base value
157 // as the other components may be undefined. We record
158 // the new value though, so as not to lose data.
159 mBaseVal = aRect;
160 return;
163 AutoChangeViewBoxNotifier notifier(this, aSVGElement);
165 mBaseVal = aRect;
166 mHasBaseVal = true;
169 nsresult SVGAnimatedViewBox::SetBaseValueString(const nsAString& aValue,
170 SVGElement* aSVGElement,
171 bool aDoSetAttr) {
172 SVGViewBox viewBox;
174 nsresult rv = SVGViewBox::FromString(aValue, &viewBox);
175 if (NS_FAILED(rv)) {
176 return rv;
178 // Comparison against mBaseVal is only valid if we currently have a base val.
179 if (mHasBaseVal && viewBox == mBaseVal) {
180 return NS_OK;
183 AutoChangeViewBoxNotifier notifier(this, aSVGElement, aDoSetAttr);
184 mHasBaseVal = true;
185 mBaseVal = viewBox;
187 return NS_OK;
190 void SVGAnimatedViewBox::GetBaseValueString(nsAString& aValue) const {
191 if (mBaseVal.none) {
192 aValue.AssignLiteral("none");
193 return;
195 nsTextFormatter::ssprintf(aValue, u"%g %g %g %g", (double)mBaseVal.x,
196 (double)mBaseVal.y, (double)mBaseVal.width,
197 (double)mBaseVal.height);
200 already_AddRefed<SVGAnimatedRect> SVGAnimatedViewBox::ToSVGAnimatedRect(
201 SVGElement* aSVGElement) {
202 RefPtr<SVGAnimatedRect> domAnimatedRect =
203 sSVGAnimatedRectTearoffTable.GetTearoff(this);
204 if (!domAnimatedRect) {
205 domAnimatedRect = new SVGAnimatedRect(this, aSVGElement);
206 sSVGAnimatedRectTearoffTable.AddTearoff(this, domAnimatedRect);
209 return domAnimatedRect.forget();
212 already_AddRefed<SVGRect> SVGAnimatedViewBox::ToDOMBaseVal(
213 SVGElement* aSVGElement) {
214 if (!mHasBaseVal || mBaseVal.none) {
215 return nullptr;
218 RefPtr<SVGRect> domBaseVal = sBaseSVGViewBoxTearoffTable.GetTearoff(this);
219 if (!domBaseVal) {
220 domBaseVal = new SVGRect(this, aSVGElement, SVGRect::RectType::BaseValue);
221 sBaseSVGViewBoxTearoffTable.AddTearoff(this, domBaseVal);
224 return domBaseVal.forget();
227 SVGRect::~SVGRect() {
228 switch (mType) {
229 case RectType::BaseValue:
230 sBaseSVGViewBoxTearoffTable.RemoveTearoff(mVal);
231 break;
232 case RectType::AnimValue:
233 sAnimSVGViewBoxTearoffTable.RemoveTearoff(mVal);
234 break;
235 default:
236 break;
240 already_AddRefed<SVGRect> SVGAnimatedViewBox::ToDOMAnimVal(
241 SVGElement* aSVGElement) {
242 if ((mAnimVal && mAnimVal->none) ||
243 (!mAnimVal && (!mHasBaseVal || mBaseVal.none))) {
244 return nullptr;
247 RefPtr<SVGRect> domAnimVal = sAnimSVGViewBoxTearoffTable.GetTearoff(this);
248 if (!domAnimVal) {
249 domAnimVal = new SVGRect(this, aSVGElement, SVGRect::RectType::AnimValue);
250 sAnimSVGViewBoxTearoffTable.AddTearoff(this, domAnimVal);
253 return domAnimVal.forget();
256 UniquePtr<SMILAttr> SVGAnimatedViewBox::ToSMILAttr(SVGElement* aSVGElement) {
257 return MakeUnique<SMILViewBox>(this, aSVGElement);
260 nsresult SVGAnimatedViewBox::SMILViewBox ::ValueFromString(
261 const nsAString& aStr, const SVGAnimationElement* /*aSrcElement*/,
262 SMILValue& aValue, bool& aPreventCachingOfSandwich) const {
263 SVGViewBox viewBox;
264 nsresult res = SVGViewBox::FromString(aStr, &viewBox);
265 if (NS_FAILED(res)) {
266 return res;
268 SMILValue val(&SVGViewBoxSMILType::sSingleton);
269 *static_cast<SVGViewBox*>(val.mU.mPtr) = viewBox;
270 aValue = std::move(val);
272 return NS_OK;
275 SMILValue SVGAnimatedViewBox::SMILViewBox::GetBaseValue() const {
276 SMILValue val(&SVGViewBoxSMILType::sSingleton);
277 *static_cast<SVGViewBox*>(val.mU.mPtr) = mVal->mBaseVal;
278 return val;
281 void SVGAnimatedViewBox::SMILViewBox::ClearAnimValue() {
282 if (mVal->mAnimVal) {
283 mVal->mAnimVal = nullptr;
284 mSVGElement->DidAnimateViewBox();
288 nsresult SVGAnimatedViewBox::SMILViewBox::SetAnimValue(
289 const SMILValue& aValue) {
290 NS_ASSERTION(aValue.mType == &SVGViewBoxSMILType::sSingleton,
291 "Unexpected type to assign animated value");
292 if (aValue.mType == &SVGViewBoxSMILType::sSingleton) {
293 SVGViewBox& vb = *static_cast<SVGViewBox*>(aValue.mU.mPtr);
294 mVal->SetAnimValue(vb, mSVGElement);
296 return NS_OK;
299 } // namespace mozilla