LEFT | RIGHT |
1 /* | 1 /* |
2 * Copyright (C) 2016 Apple Inc. All rights reserved. | 2 * Copyright (C) 2016 Apple Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions | 5 * modification, are permitted provided that the following conditions |
6 * are met: | 6 * are met: |
7 * 1. Redistributions of source code must retain the above copyright | 7 * 1. Redistributions of source code must retain the above copyright |
8 * notice, this list of conditions and the following disclaimer. | 8 * notice, this list of conditions and the following disclaimer. |
9 * 2. Redistributions in binary form must reproduce the above copyright | 9 * 2. Redistributions in binary form must reproduce the above copyright |
10 * notice, this list of conditions and the following disclaimer in the | 10 * notice, this list of conditions and the following disclaimer in the |
(...skipping 10 matching lines...) Expand all Loading... |
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | 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 | 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
23 * THE POSSIBILITY OF SUCH DAMAGE. | 23 * THE POSSIBILITY OF SUCH DAMAGE. |
24 */ | 24 */ |
25 | 25 |
26 #include "config.h" | 26 #include "config.h" |
27 | 27 |
28 #if ENABLE(INTERSECTION_OBSERVER) | 28 #if ENABLE(INTERSECTION_OBSERVER) |
29 #include "IntersectionObserver.h" | 29 #include "IntersectionObserver.h" |
30 | 30 |
31 #include "CSSParser.h" | 31 #include "CSSParserTokenRange.h" |
32 #include "CSSPropertyParser.h" | |
33 #include "CSSPropertyParserHelpers.h" | 32 #include "CSSPropertyParserHelpers.h" |
34 #include "CSSTokenizer.h" | 33 #include "CSSTokenizer.h" |
35 #include "DOMWindow.h" | 34 #include "DOMWindow.h" |
36 #include "Element.h" | 35 #include "Element.h" |
37 #include "ExceptionCode.h" | 36 #include "ExceptionCode.h" |
38 #include "IntersectionObserverCallback.h" | 37 #include "IntersectionObserverCallback.h" |
39 #include "IntersectionObserverEntry.h" | 38 #include "IntersectionObserverEntry.h" |
40 #include "Logging.h" | 39 #include "Logging.h" |
41 #include "Performance.h" | 40 #include "Performance.h" |
42 #include "StyleResolver.h" | 41 #include "StyleResolver.h" |
(...skipping 20 matching lines...) Expand all Loading... |
63 RefPtr<CSSPrimitiveValue> parsedValue = CSSPropertyParserHelpers::consum
eLengthOrPercent(tokenRange, HTMLStandardMode, ValueRangeAll); | 62 RefPtr<CSSPrimitiveValue> parsedValue = CSSPropertyParserHelpers::consum
eLengthOrPercent(tokenRange, HTMLStandardMode, ValueRangeAll); |
64 if (!parsedValue || parsedValue->isCalculated()) | 63 if (!parsedValue || parsedValue->isCalculated()) |
65 return Exception { SyntaxError, "Failed to construct 'IntersectionOb
server': rootMargin must be specified in pixels or percent." }; | 64 return Exception { SyntaxError, "Failed to construct 'IntersectionOb
server': rootMargin must be specified in pixels or percent." }; |
66 if (parsedValue->isPercentage()) | 65 if (parsedValue->isPercentage()) |
67 margins.append(Length(parsedValue->doubleValue(), Percent)); | 66 margins.append(Length(parsedValue->doubleValue(), Percent)); |
68 else if (parsedValue->isPx()) | 67 else if (parsedValue->isPx()) |
69 margins.append(Length(parsedValue->intValue(), Fixed)); | 68 margins.append(Length(parsedValue->intValue(), Fixed)); |
70 else | 69 else |
71 return Exception { SyntaxError, "Failed to construct 'IntersectionOb
server': rootMargin must be specified in pixels or percent." }; | 70 return Exception { SyntaxError, "Failed to construct 'IntersectionOb
server': rootMargin must be specified in pixels or percent." }; |
72 } | 71 } |
73 | |
74 Length top, right, bottom, left; | |
75 switch (margins.size()) { | 72 switch (margins.size()) { |
76 case 0: | 73 case 0: |
77 for (int i = 0; i < 4; ++i) | 74 for (unsigned i = 0; i < 4; ++i) |
78 margins.append(Length()); | 75 margins.append(Length()); |
79 break; | 76 break; |
80 case 1: | 77 case 1: |
81 for (int i = 0; i < 3; ++i) | 78 for (unsigned i = 0; i < 3; ++i) |
82 margins.append(margins[0]); | 79 margins.append(margins[0]); |
83 break; | 80 break; |
84 case 2: | 81 case 2: |
85 margins.append(margins[0]); | 82 margins.append(margins[0]); |
86 margins.append(margins[1]); | 83 margins.append(margins[1]); |
87 break; | 84 break; |
88 case 3: | 85 case 3: |
89 margins.append(margins[1]); | 86 margins.append(margins[1]); |
90 break; | 87 break; |
91 case 4: | 88 case 4: |
92 break; | 89 break; |
93 default: | 90 default: |
94 ASSERT_NOT_REACHED(); | 91 ASSERT_NOT_REACHED(); |
95 } | 92 } |
96 | |
97 StringBuilder stringBuilder; | |
98 for (size_t i = 0; i < 4; ++i) { | |
99 stringBuilder.appendNumber(margins[i].intValue()); | |
100 if (margins[i].type() == Percent) | |
101 stringBuilder.append('%'); | |
102 else | |
103 stringBuilder.append("px", 2); | |
104 if (i < 3) | |
105 stringBuilder.append(' '); | |
106 } | |
107 rootMargin = stringBuilder.toString(); | |
108 | 93 |
109 return LengthBox(WTFMove(margins[0]), WTFMove(margins[1]), WTFMove(margins[2
]), WTFMove(margins[3])); | 94 return LengthBox(WTFMove(margins[0]), WTFMove(margins[1]), WTFMove(margins[2
]), WTFMove(margins[3])); |
110 } | 95 } |
111 | 96 |
112 ExceptionOr<Ref<IntersectionObserver>> IntersectionObserver::create(Document& do
cument, Ref<IntersectionObserverCallback>&& callback, IntersectionObserver::Init
&& init) | 97 ExceptionOr<Ref<IntersectionObserver>> IntersectionObserver::create(Document& do
cument, Ref<IntersectionObserverCallback>&& callback, IntersectionObserver::Init
&& init) |
113 { | 98 { |
114 auto rootMarginOrException = parseRootMargin(init.rootMargin); | 99 auto rootMarginOrException = parseRootMargin(init.rootMargin); |
115 if (rootMarginOrException.hasException()) | 100 if (rootMarginOrException.hasException()) |
116 return rootMarginOrException.releaseException(); | 101 return rootMarginOrException.releaseException(); |
117 | 102 |
118 bool foundOutOfRangeThreshold = false; | 103 bool foundOutOfRangeThreshold = false; |
119 if (WTF::holds_alternative<double>(init.threshold)) | 104 if (WTF::holds_alternative<double>(init.threshold)) |
120 foundOutOfRangeThreshold = !isThresholdWithinRange(WTF::get<double>(init
.threshold)); | 105 foundOutOfRangeThreshold = !isThresholdWithinRange(WTF::get<double>(init
.threshold)); |
121 else { | 106 else { |
122 for (auto& threshold : WTF::get<Vector<double>>(init.threshold)) | 107 for (auto& threshold : WTF::get<Vector<double>>(init.threshold)) |
123 foundOutOfRangeThreshold |= !isThresholdWithinRange(threshold); | 108 foundOutOfRangeThreshold |= !isThresholdWithinRange(threshold); |
124 } | 109 } |
125 if (foundOutOfRangeThreshold) | 110 if (foundOutOfRangeThreshold) |
126 return Exception { RangeError, "Failed to construct 'IntersectionObserve
r': all thresholds must lie in the range [0.0, 1.0]." }; | 111 return Exception { RangeError, "Failed to construct 'IntersectionObserve
r': all thresholds must lie in the range [0.0, 1.0]." }; |
127 | 112 |
128 return adoptRef(*new IntersectionObserver(document, WTFMove(callback), WTFMo
ve(init), rootMarginOrException.releaseReturnValue())); | 113 return adoptRef(*new IntersectionObserver(document, WTFMove(callback), WTFMo
ve(init), rootMarginOrException.releaseReturnValue())); |
129 } | 114 } |
130 | 115 |
131 // FIXME: need to stop observing if the root is deleted. | 116 IntersectionObserver::IntersectionObserver(Document& document, Ref<IntersectionO
bserverCallback>&& callback, Init&& init, LengthBox&& parsedRootMargin) |
132 IntersectionObserver::IntersectionObserver(Document& document, Ref<IntersectionO
bserverCallback>&& callback, Init&& init, LengthBox&& marginBox) | |
133 : m_implicitRootDocument(nullptr) | 117 : m_implicitRootDocument(nullptr) |
134 , m_root(init.root) | 118 , m_root(init.root) |
135 , m_rootMargin(WTFMove(init.rootMargin)) | 119 , m_rootMargin(WTFMove(parsedRootMargin)) |
136 , m_callback(WTFMove(callback)) | 120 , m_callback(WTFMove(callback)) |
137 , m_marginBox(marginBox) | |
138 { | 121 { |
139 if (WTF::holds_alternative<double>(init.threshold)) | 122 if (WTF::holds_alternative<double>(init.threshold)) |
140 m_thresholds.append(WTF::get<double>(init.threshold)); | 123 m_thresholds.append(WTF::get<double>(init.threshold)); |
141 else | 124 else |
142 m_thresholds = WTF::get<Vector<double>>(WTFMove(init.threshold)); | 125 m_thresholds = WTF::get<Vector<double>>(WTFMove(init.threshold)); |
143 std::sort(m_thresholds.begin(), m_thresholds.end(), std::less<double>()); | 126 std::sort(m_thresholds.begin(), m_thresholds.end(), std::less<double>()); |
144 | 127 |
145 if (!m_root) { | 128 if (m_root) { |
146 if (auto* frame = document.frame()) { | 129 auto& observerData = m_root->ensureIntersectionObserverData(); |
147 m_implicitRootDocument = frame->mainFrame().document(); | 130 observerData.observers.append(this); |
148 m_implicitRootDocument->addViewportIntersectionObserver(*this); | 131 } else if (auto* frame = document.frame()) { |
149 } | 132 m_implicitRootDocument = frame->mainFrame().document(); |
150 } | 133 m_implicitRootDocument->addViewportIntersectionObserver(*this); |
| 134 } |
| 135 } |
| 136 |
| 137 String IntersectionObserver::rootMargin() const |
| 138 { |
| 139 StringBuilder stringBuilder; |
| 140 PhysicalBoxSide sides[4] = { PhysicalBoxSide::Top, PhysicalBoxSide::Right, P
hysicalBoxSide::Bottom, PhysicalBoxSide::Left }; |
| 141 for (auto side : sides) { |
| 142 auto& length = m_rootMargin.at(side); |
| 143 stringBuilder.appendNumber(length.intValue()); |
| 144 if (length.type() == Percent) |
| 145 stringBuilder.append('%'); |
| 146 else |
| 147 stringBuilder.append("px", 2); |
| 148 if (side != PhysicalBoxSide::Left) |
| 149 stringBuilder.append(' '); |
| 150 } |
| 151 return stringBuilder.toString(); |
151 } | 152 } |
152 | 153 |
153 IntersectionObserver::~IntersectionObserver() | 154 IntersectionObserver::~IntersectionObserver() |
154 { | 155 { |
155 disconnect(); | 156 disconnect(); |
156 if (m_implicitRootDocument) | 157 if (m_implicitRootDocument) |
157 m_implicitRootDocument->removeViewportIntersectionObserver(*this); | 158 m_implicitRootDocument->removeViewportIntersectionObserver(*this); |
158 // FIXME: For explicit root case, need to remove this from the list in the r
oot's rare data. | 159 else if (m_root) { |
| 160 auto& elementObservers = m_root->intersectionObserverData()->observers; |
| 161 for (size_t i = 0; i < elementObservers.size(); ++i) { |
| 162 if (elementObservers.at(i) == this) { |
| 163 elementObservers.remove(i); |
| 164 break; |
| 165 } |
| 166 } |
| 167 } |
159 } | 168 } |
160 | 169 |
161 void IntersectionObserver::observe(Element& element) | 170 void IntersectionObserver::observe(Element& element) |
162 { | 171 { |
163 if (!trackingDocument()) | 172 if (!trackingDocument()) |
164 return; | 173 return; |
165 element.startObservingIntersections(*this); | 174 element.startObservingIntersections(*this); |
166 } | 175 } |
167 | 176 |
168 void IntersectionObserver::unobserve(Element& element) | 177 void IntersectionObserver::unobserve(Element& element) |
(...skipping 30 matching lines...) Expand all Loading... |
199 if (auto* document = trackingDocument()) | 208 if (auto* document = trackingDocument()) |
200 document->deactivateIntersectionObserver(*this); | 209 document->deactivateIntersectionObserver(*this); |
201 } | 210 } |
202 } | 211 } |
203 | 212 |
204 bool IntersectionObserver::hasObservationTarget(Element& element) const | 213 bool IntersectionObserver::hasObservationTarget(Element& element) const |
205 { | 214 { |
206 return m_observationTargets.contains(&element); | 215 return m_observationTargets.contains(&element); |
207 } | 216 } |
208 | 217 |
209 bool IntersectionObserver::createTimestamp(DOMHighResTimeStamp& timestamp) const
{ | 218 bool IntersectionObserver::createTimestamp(DOMHighResTimeStamp& timestamp) const |
| 219 { |
210 auto* context = m_callback->scriptExecutionContext(); | 220 auto* context = m_callback->scriptExecutionContext(); |
211 if (!context) | 221 if (!context) |
212 return false; | 222 return false; |
213 ASSERT(context->isDocument()); | 223 ASSERT(context->isDocument()); |
214 auto& document = downcast<Document>(*context); | 224 auto& document = downcast<Document>(*context); |
215 if (auto* window = document.domWindow()) { | 225 if (auto* window = document.domWindow()) { |
216 if (auto* performance = window->performance()) { | 226 if (auto* performance = window->performance()) { |
217 timestamp = performance->now(); | 227 timestamp = performance->now(); |
218 return true; | 228 return true; |
219 } | 229 } |
220 } | 230 } |
221 return false; | 231 return false; |
222 } | 232 } |
223 | 233 |
| 234 void IntersectionObserver::rootDestroyed() |
| 235 { |
| 236 ASSERT(m_root); |
| 237 disconnect(); |
| 238 m_root = nullptr; |
| 239 } |
| 240 |
224 void IntersectionObserver::implicitRootDocumentDestroyed() | 241 void IntersectionObserver::implicitRootDocumentDestroyed() |
225 { | 242 { |
226 ASSERT(m_implicitRootDocument); | 243 ASSERT(m_implicitRootDocument); |
| 244 disconnect(); |
227 m_implicitRootDocument = nullptr; | 245 m_implicitRootDocument = nullptr; |
228 } | 246 } |
229 | 247 |
230 void IntersectionObserver::appendQueuedEntry(Ref<IntersectionObserverEntry>&& en
try) | 248 void IntersectionObserver::appendQueuedEntry(Ref<IntersectionObserverEntry>&& en
try) |
231 { | 249 { |
232 LOG_WITH_STREAM(IntersectionObserver, stream << " adding queued entry " << e
ntry.get()); | 250 LOG_WITH_STREAM(IntersectionObserver, stream << " adding queued entry " << e
ntry.get()); |
233 | 251 |
234 m_queuedEntries.append(WTFMove(entry)); | 252 m_queuedEntries.append(WTFMove(entry)); |
235 } | 253 } |
236 | 254 |
237 void IntersectionObserver::notify() | 255 void IntersectionObserver::notify() |
238 { | 256 { |
239 if (m_queuedEntries.isEmpty() || !m_callback->canInvokeCallback()) | 257 if (m_queuedEntries.isEmpty() || !m_callback->canInvokeCallback()) |
240 return; | 258 return; |
241 | 259 |
242 LOG(IntersectionObserver, "IntersectionObserver %p callback(): %lu records",
this, m_queuedEntries.size()); | 260 LOG(IntersectionObserver, "IntersectionObserver %p callback(): %lu records",
this, m_queuedEntries.size()); |
243 | 261 |
244 m_callback->handleEvent(takeRecords(), *this); | 262 m_callback->handleEvent(takeRecords(), *this); |
245 } | 263 } |
246 | 264 |
247 } // namespace WebCore | 265 } // namespace WebCore |
248 | 266 |
249 #endif // ENABLE(INTERSECTION_OBSERVER) | 267 #endif // ENABLE(INTERSECTION_OBSERVER) |
LEFT | RIGHT |