 Chromium Code Reviews
 Chromium Code Reviews Issue 1449623002:
  IntersectionObserver: second cut.  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src@master
    
  
    Issue 1449623002:
  IntersectionObserver: second cut.  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src@master| OLD | NEW | 
|---|---|
| (Empty) | |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "config.h" | |
| 6 #include "core/dom/IntersectionObserver.h" | |
| 7 | |
| 8 #include "bindings/core/v8/ExceptionState.h" | |
| 9 #include "core/css/parser/CSSParserTokenRange.h" | |
| 10 #include "core/css/parser/CSSTokenizer.h" | |
| 11 #include "core/dom/ExceptionCode.h" | |
| 12 #include "core/dom/ExecutionContext.h" | |
| 13 #include "core/dom/IntersectionObserverCallback.h" | |
| 14 #include "core/dom/IntersectionObserverEntry.h" | |
| 15 #include "core/dom/IntersectionObserverInit.h" | |
| 16 #include "core/html/HTMLFrameOwnerElement.h" | |
| 17 #include "platform/Timer.h" | |
| 18 #include "wtf/MainThread.h" | |
| 19 #include <algorithm> | |
| 20 | |
| 21 namespace blink { | |
| 22 | |
| 23 PassRefPtrWillBeRawPtr<IntersectionObserver> IntersectionObserver::create(const IntersectionObserverInit& observerInit, PassOwnPtrWillBeRawPtr<IntersectionObser verCallback> callback, ExceptionState& exceptionState) | |
| 24 { | |
| 25 ASSERT(isMainThread()); | |
| 26 | |
| 27 int rootMargin = 0; | |
| 28 if (observerInit.hasRootMargin()) { | |
| 29 CSSParserTokenRange tokenRange = CSSTokenizer::Scope(observerInit.rootMa rgin()).tokenRange(); | |
| 30 const CSSParserToken& token = tokenRange.consumeIncludingWhitespace(); | |
| 31 switch (token.type()) { | |
| 32 case EOFToken: | |
| 33 break; | |
| 34 case NumberToken: | |
| 35 rootMargin = static_cast<int>(token.numericValue()); | |
| 36 break; | |
| 37 case DimensionToken: | |
| 38 switch (token.unitType()) { | |
| 39 case CSSPrimitiveValue::UnitType::Number: | |
| 40 case CSSPrimitiveValue::UnitType::Pixels: | |
| 41 rootMargin = static_cast<int>(token.numericValue()); | |
| 
eae
2015/11/16 23:34:44
Is flooring the number the desired result? If so c
 
szager1
2015/11/19 19:15:38
Done in next patch.
 | |
| 42 break; | |
| 43 default: | |
| 44 exceptionState.throwTypeError("rootMargin must be specified in p ixels."); | |
| 45 break; | |
| 46 } | |
| 47 break; | |
| 48 default: | |
| 49 exceptionState.throwTypeError("rootMargin must be specified in pixel s."); | |
| 50 } | |
| 51 } | |
| 52 if (exceptionState.hadException()) | |
| 53 return PassRefPtrWillBeRawPtr<IntersectionObserver>(); | |
| 54 | |
| 55 Vector<float> thresholds; | |
| 56 if (observerInit.hasThreshold()) { | |
| 57 const DoubleOrDoubleArray& thresholdParam = observerInit.threshold(); | |
| 58 if (thresholdParam.isDouble()) { | |
| 59 thresholds.append(static_cast<float>(thresholdParam.getAsDouble())); | |
| 60 } else { | |
| 61 for (auto thresholdValue: thresholdParam.getAsDoubleArray()) | |
| 62 thresholds.append(static_cast<float>(thresholdValue)); | |
| 63 } | |
| 64 } else { | |
| 65 thresholds.append(0); | |
| 66 } | |
| 67 for (auto thresholdValue: thresholds) { | |
| 68 if (thresholdValue < 0.0 || thresholdValue > 1.0) { | |
| 69 exceptionState.throwTypeError("Threshold values must be between 0 an d 1"); | |
| 70 break; | |
| 71 } | |
| 72 } | |
| 73 std::sort(thresholds.begin(), thresholds.end()); | |
| 74 if (exceptionState.hadException()) | |
| 75 return PassRefPtrWillBeRawPtr<IntersectionObserver>(); | |
| 76 | |
| 77 return adoptRefWillBeNoop(new IntersectionObserver(callback, observerInit.ro ot(), rootMargin, thresholds)); | |
| 78 } | |
| 79 | |
| 80 IntersectionObserver::IntersectionObserver(PassOwnPtrWillBeRawPtr<IntersectionOb serverCallback> callback, RefPtrWillBeRawPtr<Element> root, int rootMargin, cons t Vector<float>& thresholds) | |
| 81 : m_callback(callback) | |
| 82 , m_thresholds(thresholds) | |
| 83 , m_rootMargin(rootMargin) | |
| 84 , m_hasRoot(root) | |
| 85 { | |
| 86 if (root) { | |
| 87 // This is a phony observation, created just to ensure the root element keeps | |
| 88 // a reference to the observer. That reference will be used to disconne ct | |
| 89 // the observer when the root element is destroyed. | |
| 90 m_root = root->createWeakPtr(); | |
| 91 m_trackingDocument = root->document().createWeakPtr(); | |
| 92 createObservation(root.get()); | |
| 93 } | |
| 94 } | |
| 95 | |
| 96 IntersectionObserver::~IntersectionObserver() | |
| 97 { | |
| 98 ASSERT(!m_observations.size()); | |
| 99 ASSERT(!m_entries.size()); | |
| 100 } | |
| 101 | |
| 102 bool IntersectionObserver::checkTargetHierarchy(Element* target) | |
| 
eae
2015/11/16 23:34:45
hasSameRoot or sharesAncestor might be better name
 
szager1
2015/11/19 19:15:38
Maybe isValidTarget?  Or isDescendantOfRoot?  I'll
 | |
| 103 { | |
| 104 if (!target->inDocument()) | |
| 105 return false; | |
| 106 if (!hasRoot()) | |
| 107 return true; | |
| 108 Element* rootElement = root(); | |
| 109 if (!rootElement || !rootElement->inDocument()) | |
| 110 return false; | |
| 111 LocalFrame* rootFrame = rootElement->document().frame(); | |
| 112 LocalFrame* targetFrame = target->document().frame(); | |
| 113 if (!rootFrame || !targetFrame || !targetFrame->tree().isDescendantOf(rootFr ame)) | |
| 114 return false; | |
| 115 while (targetFrame != rootFrame) { | |
| 116 FrameOwner* targetOwner = targetFrame->owner(); | |
| 117 if (!targetOwner || !targetOwner->isLocal()) | |
| 118 return false; | |
| 119 target = toHTMLFrameOwnerElement(targetOwner); | |
| 120 ASSERT(target); | |
| 121 targetFrame = target->document().frame(); | |
| 122 if (!targetFrame) | |
| 123 return false; | |
| 124 } | |
| 125 return target->isDescendantOf(rootElement); | |
| 126 } | |
| 127 | |
| 128 Document& IntersectionObserver::getTrackingDocumentForTarget(Element* target) | |
| 129 { | |
| 130 if (root()) | |
| 131 return root()->document(); | |
| 132 ASSERT(target); | |
| 133 Frame* frame = target->document().frame(); | |
| 134 ASSERT(frame); | |
| 135 frame = frame->tree().top(); | |
| 136 ASSERT(frame && frame->isLocalFrame() && toLocalFrame(frame)->document()); | |
| 137 return *toLocalFrame(frame)->document(); | |
| 138 } | |
| 139 | |
| 140 void IntersectionObserver::observe(Element* target, ExceptionState& exceptionSta te) | |
| 141 { | |
| 142 if (!target) | |
| 143 exceptionState.throwTypeError("Observation target must be an element."); | |
| 144 else if ((m_hasRoot && !root()) || (m_observations.size() && !trackingDocume nt())) | |
| 145 exceptionState.throwDOMException(HierarchyRequestError, "Invalid observe r: root element or containing document has been deleted."); | |
| 146 else if (root() == target) | |
| 147 exceptionState.throwDOMException(HierarchyRequestError, "Cannot use the same element for root and target."); | |
| 148 if (exceptionState.hadException()) | |
| 149 return; | |
| 150 | |
| 151 if (IntersectionObservation::contains(m_observations, this, target)) | |
| 152 return; | |
| 153 | |
| 154 // If there's no root and this is the first target, then we didn't know unti l | |
| 155 // now which document will track the observation and generate notifications. | |
| 156 if (!trackingDocument()) { | |
| 157 ASSERT(!m_root && !m_observations.size()); | |
| 158 m_trackingDocument = getTrackingDocumentForTarget(target).createWeakPtr( ); | |
| 159 } else if (m_trackingDocument.get() != &getTrackingDocumentForTarget(target) ) { | |
| 160 exceptionState.throwDOMException(HierarchyRequestError, "All target elem ents must be in the same frame tree."); | |
| 161 return; | |
| 162 } | |
| 163 | |
| 164 createObservation(target); | |
| 165 } | |
| 166 | |
| 167 void IntersectionObserver::unobserve(Element* target, ExceptionState&) | |
| 168 { | |
| 169 if (!target) | |
| 170 return; | |
| 171 IntersectionObservation::HashSet::iterator observationIterator = Intersectio nObservation::find(m_observations, this, target); | |
| 172 if (observationIterator == m_observations.end()) | |
| 173 return; | |
| 174 (*observationIterator)->disconnect(); | |
| 175 } | |
| 176 | |
| 177 void IntersectionObserver::disconnect(IntersectionObservation& observation) | |
| 178 { | |
| 179 // This will get called during Element destruction, so we must be careful | |
| 180 // to check the weak pointers (root, target, trackingDocument). | |
| 181 m_observations.remove(adoptRef(&observation)); | |
| 182 if (observation.target()) | |
| 183 observation.target()->removeIntersectionObservation(observation); | |
| 184 if (root()) | |
| 185 root()->removeIntersectionObservation(observation); | |
| 186 // TODO: This will probably break things if there are unsent observations. | |
| 187 if (trackingDocument()) | |
| 188 trackingDocument()->intersectionObservationRegistry()->removeObservation (observation); | |
| 189 } | |
| 190 | |
| 191 void IntersectionObserver::disconnect() | |
| 192 { | |
| 193 WillBeHeapVector<RefPtr<IntersectionObservation>> toDisconnect; | |
| 194 for (auto& observation: m_observations) | |
| 195 toDisconnect.append(observation); | |
| 196 for (auto& observation: toDisconnect) | |
| 197 observation->disconnect(); | |
| 198 ASSERT(!m_observations.size()); | |
| 199 } | |
| 200 | |
| 201 IntersectionObserverEntryVector IntersectionObserver::takeRecords() | |
| 202 { | |
| 203 IntersectionObserverEntryVector entries; | |
| 204 entries.swap(m_entries); | |
| 205 return entries; | |
| 206 } | |
| 207 | |
| 208 void IntersectionObserver::enqueueIntersectionObserverEntry(PassRefPtrWillBeRawP tr<IntersectionObserverEntry> entry) | |
| 209 { | |
| 210 ASSERT(isMainThread()); | |
| 211 m_entries.append(entry); | |
| 212 m_trackingDocument->intersectionObservationRegistry()->activateIntersectionO bserver(*this); | |
| 213 } | |
| 214 | |
| 215 size_t IntersectionObserver::firstThresholdGreaterThan(float ratio) const | |
| 216 { | |
| 217 size_t result = 0; | |
| 218 size_t max = m_thresholds.size(); | |
| 219 while (result < max && m_thresholds[result] <= ratio) | |
| 220 ++result; | |
| 221 return result; | |
| 222 } | |
| 223 | |
| 224 bool IntersectionObserver::shouldBeSuspended() const | |
| 225 { | |
| 226 return m_callback->executionContext() && m_callback->executionContext()->act iveDOMObjectsAreSuspended(); | |
| 227 } | |
| 228 | |
| 229 void IntersectionObserver::deliver() | |
| 230 { | |
| 231 ASSERT(!shouldBeSuspended()); | |
| 232 | |
| 233 if (m_entries.isEmpty()) | |
| 234 return; | |
| 235 | |
| 236 IntersectionObserverEntryVector entries; | |
| 237 entries.swap(m_entries); | |
| 238 m_callback->handleEvent(entries, this); | |
| 239 } | |
| 240 | |
| 241 PassRefPtr<IntersectionObservation> IntersectionObserver::createObservation(Elem ent* target) | |
| 242 { | |
| 243 RefPtrWillBeRawPtr<IntersectionObservation> observation = IntersectionObserv ation::create(*this, target->createWeakPtr(), false); | |
| 244 m_observations.add(observation); | |
| 245 m_trackingDocument->intersectionObservationRegistry()->addObservation(*obser vation); | |
| 246 target->addIntersectionObservation(*observation); | |
| 247 return observation; | |
| 248 } | |
| 249 | |
| 250 DEFINE_TRACE(IntersectionObserver) | |
| 251 { | |
| 252 visitor->trace(m_callback); | |
| 253 visitor->trace(m_root); | |
| 254 // TODO: Uncommenting the next line causes a compilation failure. Fix it. | |
| 255 // visitor->trace(m_observations); | |
| 
haraken
2015/11/16 00:41:34
You need to use:
#if ENABLE(OILPAN)
  visitor->tr
 
szager1
2015/11/19 19:15:38
Working on it.
 | |
| 256 visitor->trace(m_entries); | |
| 257 } | |
| 258 | |
| 259 } // namespace blink | |
| OLD | NEW |