[IntersectionObserver] Validate threshold values
[WebKit-https.git] / Source / WebCore / page / IntersectionObserver.cpp
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
28 #if ENABLE(INTERSECTION_OBSERVER)
29 #include "IntersectionObserver.h"
30
31 #include "CSSParserTokenRange.h"
32 #include "CSSPropertyParserHelpers.h"
33 #include "CSSTokenizer.h"
34 #include "Element.h"
35 #include "IntersectionObserverCallback.h"
36 #include "IntersectionObserverEntry.h"
37 #include <wtf/Vector.h>
38
39 namespace WebCore {
40
41 static ExceptionOr<LengthBox> parseRootMargin(String& rootMargin)
42 {
43     CSSTokenizer tokenizer(rootMargin);
44     auto tokenRange = tokenizer.tokenRange();
45     Vector<Length, 4> margins;
46     while (!tokenRange.atEnd()) {
47         if (margins.size() == 4)
48             return Exception { SyntaxError, "Failed to construct 'IntersectionObserver': Extra text found at the end of rootMargin." };
49         RefPtr<CSSPrimitiveValue> parsedValue = CSSPropertyParserHelpers::consumeLengthOrPercent(tokenRange, HTMLStandardMode, ValueRangeAll);
50         if (!parsedValue || parsedValue->isCalculated())
51             return Exception { SyntaxError, "Failed to construct 'IntersectionObserver': rootMargin must be specified in pixels or percent." };
52         if (parsedValue->isPercentage())
53             margins.append(Length(parsedValue->doubleValue(), Percent));
54         else if (parsedValue->isPx())
55             margins.append(Length(parsedValue->intValue(), Fixed));
56         else
57             return Exception { SyntaxError, "Failed to construct 'IntersectionObserver': rootMargin must be specified in pixels or percent." };
58     }
59     switch (margins.size()) {
60     case 0:
61         for (unsigned i = 0; i < 4; ++i)
62             margins.append(Length());
63         break;
64     case 1:
65         for (unsigned i = 0; i < 3; ++i)
66             margins.append(margins[0]);
67         break;
68     case 2:
69         margins.append(margins[0]);
70         margins.append(margins[1]);
71         break;
72     case 3:
73         margins.append(margins[1]);
74         break;
75     case 4:
76         break;
77     default:
78         ASSERT_NOT_REACHED();
79     }
80
81     return LengthBox(WTFMove(margins[0]), WTFMove(margins[1]), WTFMove(margins[2]), WTFMove(margins[3]));
82 }
83
84 ExceptionOr<Ref<IntersectionObserver>> IntersectionObserver::create(Ref<IntersectionObserverCallback>&& callback, IntersectionObserver::Init&& init)
85 {
86     auto rootMarginOrException = parseRootMargin(init.rootMargin);
87     if (rootMarginOrException.hasException())
88         return rootMarginOrException.releaseException();
89
90     Vector<double> thresholds;
91     WTF::switchOn(init.threshold, [&thresholds] (double initThreshold) {
92         thresholds.reserveInitialCapacity(1);
93         thresholds.uncheckedAppend(initThreshold);
94     }, [&thresholds] (Vector<double>& initThresholds) {
95         thresholds = WTFMove(initThresholds);
96     });
97
98     for (auto threshold : thresholds) {
99         if (!(threshold >= 0 && threshold <= 1))
100             return Exception { RangeError, "Failed to construct 'IntersectionObserver': all thresholds must lie in the range [0.0, 1.0]." };
101     }
102
103     return adoptRef(*new IntersectionObserver(WTFMove(callback), WTFMove(init.root), rootMarginOrException.releaseReturnValue(), WTFMove(thresholds)));
104 }
105
106 IntersectionObserver::IntersectionObserver(Ref<IntersectionObserverCallback>&& callback, RefPtr<Element>&& root, LengthBox&& parsedRootMargin, Vector<double>&& thresholds)
107     : m_root(WTFMove(root))
108     , m_rootMargin(WTFMove(parsedRootMargin))
109     , m_thresholds(WTFMove(thresholds))
110     , m_callback(WTFMove(callback))
111 {
112 }
113
114 String IntersectionObserver::rootMargin() const
115 {
116     StringBuilder stringBuilder;
117     PhysicalBoxSide sides[4] = { PhysicalBoxSide::Top, PhysicalBoxSide::Right, PhysicalBoxSide::Bottom, PhysicalBoxSide::Left };
118     for (auto side : sides) {
119         auto& length = m_rootMargin.at(side);
120         stringBuilder.appendNumber(length.intValue());
121         if (length.type() == Percent)
122             stringBuilder.append('%');
123         else
124             stringBuilder.append("px", 2);
125         if (side != PhysicalBoxSide::Left)
126             stringBuilder.append(' ');
127     }
128     return stringBuilder.toString();
129 }
130
131 void IntersectionObserver::observe(Element&)
132 {
133 }
134
135 void IntersectionObserver::unobserve(Element&)
136 {
137 }
138
139 void IntersectionObserver::disconnect()
140 {
141 }
142
143 Vector<RefPtr<IntersectionObserverEntry>> IntersectionObserver::takeRecords()
144 {
145     return { };
146 }
147
148
149 } // namespace WebCore
150
151 #endif // ENABLE(INTERSECTION_OBSERVER)