| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2013 Google Inc. All rights reserved. | 2 * Copyright (C) 2013 Google 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 are | 5 * modification, are permitted provided that the following conditions are |
| 6 * met: | 6 * met: |
| 7 * | 7 * |
| 8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
| 9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
| 10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
| (...skipping 15 matching lines...) Expand all Loading... |
| 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 29 */ | 29 */ |
| 30 | 30 |
| 31 #include "config.h" | 31 #include "config.h" |
| 32 #include "core/inspector/AsyncCallStackTracker.h" | 32 #include "core/inspector/AsyncCallStackTracker.h" |
| 33 | 33 |
| 34 #include "core/dom/ContextLifecycleObserver.h" | 34 #include "core/dom/ContextLifecycleObserver.h" |
| 35 #include "core/dom/ExecutionContext.h" | 35 #include "core/dom/ExecutionContext.h" |
| 36 #include "core/events/EventTarget.h" |
| 37 #include "core/events/RegisteredEventListener.h" |
| 38 #include "wtf/text/StringBuilder.h" |
| 36 | 39 |
| 37 namespace WebCore { | 40 namespace WebCore { |
| 38 | 41 |
| 39 class AsyncCallStackTracker::ExecutionContextData : public ContextLifecycleObser
ver { | 42 class AsyncCallStackTracker::ExecutionContextData : public ContextLifecycleObser
ver { |
| 40 WTF_MAKE_FAST_ALLOCATED; | 43 WTF_MAKE_FAST_ALLOCATED; |
| 41 public: | 44 public: |
| 45 typedef std::pair<RegisteredEventListener, RefPtr<AsyncCallChain> > EventLis
tenerAsyncCallChain; |
| 46 typedef Vector<EventListenerAsyncCallChain, 1> EventListenerAsyncCallChainVe
ctor; |
| 47 typedef HashMap<AtomicString, EventListenerAsyncCallChainVector> EventListen
erAsyncCallChainVectorHashMap; |
| 48 |
| 42 ExecutionContextData(AsyncCallStackTracker* tracker, ExecutionContext* execu
tionContext) | 49 ExecutionContextData(AsyncCallStackTracker* tracker, ExecutionContext* execu
tionContext) |
| 43 : ContextLifecycleObserver(executionContext) | 50 : ContextLifecycleObserver(executionContext) |
| 44 , m_tracker(tracker) | 51 , m_tracker(tracker) |
| 45 { | 52 { |
| 46 } | 53 } |
| 47 | 54 |
| 48 virtual void contextDestroyed() OVERRIDE | 55 virtual void contextDestroyed() OVERRIDE |
| 49 { | 56 { |
| 50 ASSERT(executionContext()); | 57 ASSERT(executionContext()); |
| 51 ExecutionContextData* self = m_tracker->m_executionContextDataMap.take(e
xecutionContext()); | 58 ExecutionContextData* self = m_tracker->m_executionContextDataMap.take(e
xecutionContext()); |
| 52 ASSERT(self == this); | 59 ASSERT(self == this); |
| 53 ContextLifecycleObserver::contextDestroyed(); | 60 ContextLifecycleObserver::contextDestroyed(); |
| 54 delete self; | 61 delete self; |
| 55 } | 62 } |
| 56 | 63 |
| 57 private: | 64 void addEventListenerData(EventTarget* eventTarget, const AtomicString& even
tType, const EventListenerAsyncCallChain& item) |
| 58 friend class AsyncCallStackTracker; | 65 { |
| 66 HashMap<EventTarget*, EventListenerAsyncCallChainVectorHashMap>::iterato
r it = m_eventTargetCallChains.find(eventTarget); |
| 67 if (it == m_eventTargetCallChains.end()) |
| 68 it = m_eventTargetCallChains.set(eventTarget, EventListenerAsyncCall
ChainVectorHashMap()).iterator; |
| 69 EventListenerAsyncCallChainVectorHashMap& map = it->value; |
| 70 EventListenerAsyncCallChainVectorHashMap::iterator it2 = map.find(eventT
ype); |
| 71 if (it2 == map.end()) |
| 72 it2 = map.set(eventType, EventListenerAsyncCallChainVector()).iterat
or; |
| 73 it2->value.append(item); |
| 74 } |
| 75 |
| 76 void removeEventListenerData(EventTarget* eventTarget, const AtomicString& e
ventType, const RegisteredEventListener& item) |
| 77 { |
| 78 findEventListenerData(eventTarget, eventType, item, true); |
| 79 } |
| 80 |
| 81 PassRefPtr<AsyncCallChain> findEventListenerData(EventTarget* eventTarget, c
onst AtomicString& eventType, const RegisteredEventListener& item, bool remove =
false) |
| 82 { |
| 83 HashMap<EventTarget*, EventListenerAsyncCallChainVectorHashMap>::iterato
r it = m_eventTargetCallChains.find(eventTarget); |
| 84 if (it == m_eventTargetCallChains.end()) |
| 85 return 0; |
| 86 EventListenerAsyncCallChainVectorHashMap& map = it->value; |
| 87 EventListenerAsyncCallChainVectorHashMap::iterator it2 = map.find(eventT
ype); |
| 88 if (it2 == map.end()) |
| 89 return 0; |
| 90 RefPtr<AsyncCallChain> result; |
| 91 EventListenerAsyncCallChainVector& vector = it2->value; |
| 92 for (size_t i = 0; i < vector.size(); ++i) { |
| 93 if (vector[i].first == item) { |
| 94 result = vector[i].second; |
| 95 if (remove) { |
| 96 vector.remove(i); |
| 97 if (vector.isEmpty()) |
| 98 map.remove(it2); |
| 99 if (map.isEmpty()) |
| 100 m_eventTargetCallChains.remove(it); |
| 101 } |
| 102 break; |
| 103 } |
| 104 } |
| 105 return result.release(); |
| 106 } |
| 107 |
| 108 public: |
| 59 AsyncCallStackTracker* m_tracker; | 109 AsyncCallStackTracker* m_tracker; |
| 60 HashSet<int> m_intervalTimerIds; | 110 HashSet<int> m_intervalTimerIds; |
| 61 HashMap<int, RefPtr<AsyncCallChain> > m_timerCallChains; | 111 HashMap<int, RefPtr<AsyncCallChain> > m_timerCallChains; |
| 62 HashMap<int, RefPtr<AsyncCallChain> > m_animationFrameCallChains; | 112 HashMap<int, RefPtr<AsyncCallChain> > m_animationFrameCallChains; |
| 113 HashMap<EventTarget*, EventListenerAsyncCallChainVectorHashMap> m_eventTarge
tCallChains; |
| 63 }; | 114 }; |
| 64 | 115 |
| 65 AsyncCallStackTracker::AsyncCallStack::AsyncCallStack(const String& description,
const ScriptValue& callFrames) | 116 AsyncCallStackTracker::AsyncCallStack::AsyncCallStack(const String& description,
const ScriptValue& callFrames) |
| 66 : m_description(description) | 117 : m_description(description) |
| 67 , m_callFrames(callFrames) | 118 , m_callFrames(callFrames) |
| 68 { | 119 { |
| 69 } | 120 } |
| 70 | 121 |
| 71 AsyncCallStackTracker::AsyncCallStack::~AsyncCallStack() | 122 AsyncCallStackTracker::AsyncCallStack::~AsyncCallStack() |
| 72 { | 123 { |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 106 ASSERT(timerId > 0); | 157 ASSERT(timerId > 0); |
| 107 ExecutionContextData* data = createContextDataIfNeeded(context); | 158 ExecutionContextData* data = createContextDataIfNeeded(context); |
| 108 data->m_timerCallChains.set(timerId, createAsyncCallChain(singleShot ? setTi
meoutName : setIntervalName, callFrames)); | 159 data->m_timerCallChains.set(timerId, createAsyncCallChain(singleShot ? setTi
meoutName : setIntervalName, callFrames)); |
| 109 if (!singleShot) | 160 if (!singleShot) |
| 110 data->m_intervalTimerIds.add(timerId); | 161 data->m_intervalTimerIds.add(timerId); |
| 111 } | 162 } |
| 112 | 163 |
| 113 void AsyncCallStackTracker::didRemoveTimer(ExecutionContext* context, int timerI
d) | 164 void AsyncCallStackTracker::didRemoveTimer(ExecutionContext* context, int timerI
d) |
| 114 { | 165 { |
| 115 ASSERT(context); | 166 ASSERT(context); |
| 116 if (!isEnabled() || timerId <= 0) | 167 ASSERT(isEnabled()); |
| 168 if (timerId <= 0) |
| 117 return; | 169 return; |
| 118 ExecutionContextData* data = m_executionContextDataMap.get(context); | 170 ExecutionContextData* data = m_executionContextDataMap.get(context); |
| 119 if (!data) | 171 if (!data) |
| 120 return; | 172 return; |
| 121 data->m_intervalTimerIds.remove(timerId); | 173 data->m_intervalTimerIds.remove(timerId); |
| 122 data->m_timerCallChains.remove(timerId); | 174 data->m_timerCallChains.remove(timerId); |
| 123 } | 175 } |
| 124 | 176 |
| 125 void AsyncCallStackTracker::willFireTimer(ExecutionContext* context, int timerId
) | 177 void AsyncCallStackTracker::willFireTimer(ExecutionContext* context, int timerId
) |
| 126 { | 178 { |
| 127 ASSERT(context); | 179 ASSERT(context); |
| 128 if (!isEnabled()) | 180 ASSERT(isEnabled()); |
| 129 return; | |
| 130 ASSERT(timerId > 0); | 181 ASSERT(timerId > 0); |
| 131 ASSERT(!m_currentAsyncCallChain); | 182 ASSERT(!m_currentAsyncCallChain); |
| 132 ExecutionContextData* data = m_executionContextDataMap.get(context); | 183 ExecutionContextData* data = m_executionContextDataMap.get(context); |
| 133 if (!data) | 184 if (!data) |
| 134 return; | 185 return; |
| 135 if (data->m_intervalTimerIds.contains(timerId)) | 186 if (data->m_intervalTimerIds.contains(timerId)) |
| 136 m_currentAsyncCallChain = data->m_timerCallChains.get(timerId); | 187 setCurrentAsyncCallChain(data->m_timerCallChains.get(timerId)); |
| 137 else | 188 else |
| 138 m_currentAsyncCallChain = data->m_timerCallChains.take(timerId); | 189 setCurrentAsyncCallChain(data->m_timerCallChains.take(timerId)); |
| 139 } | 190 } |
| 140 | 191 |
| 141 void AsyncCallStackTracker::didRequestAnimationFrame(ExecutionContext* context,
int callbackId, const ScriptValue& callFrames) | 192 void AsyncCallStackTracker::didRequestAnimationFrame(ExecutionContext* context,
int callbackId, const ScriptValue& callFrames) |
| 142 { | 193 { |
| 143 DEFINE_STATIC_LOCAL(String, requestAnimationFrameName, ("requestAnimationFra
me")); | 194 DEFINE_STATIC_LOCAL(String, requestAnimationFrameName, ("requestAnimationFra
me")); |
| 144 | 195 |
| 145 ASSERT(context); | 196 ASSERT(context); |
| 146 ASSERT(isEnabled()); | 197 ASSERT(isEnabled()); |
| 147 if (!validateCallFrames(callFrames)) | 198 if (!validateCallFrames(callFrames)) |
| 148 return; | 199 return; |
| 149 ASSERT(callbackId > 0); | 200 ASSERT(callbackId > 0); |
| 150 ExecutionContextData* data = createContextDataIfNeeded(context); | 201 ExecutionContextData* data = createContextDataIfNeeded(context); |
| 151 data->m_animationFrameCallChains.set(callbackId, createAsyncCallChain(reques
tAnimationFrameName, callFrames)); | 202 data->m_animationFrameCallChains.set(callbackId, createAsyncCallChain(reques
tAnimationFrameName, callFrames)); |
| 152 } | 203 } |
| 153 | 204 |
| 154 void AsyncCallStackTracker::didCancelAnimationFrame(ExecutionContext* context, i
nt callbackId) | 205 void AsyncCallStackTracker::didCancelAnimationFrame(ExecutionContext* context, i
nt callbackId) |
| 155 { | 206 { |
| 156 ASSERT(context); | 207 ASSERT(context); |
| 157 if (!isEnabled() || callbackId <= 0) | 208 ASSERT(isEnabled()); |
| 209 if (callbackId <= 0) |
| 158 return; | 210 return; |
| 159 if (ExecutionContextData* data = m_executionContextDataMap.get(context)) | 211 if (ExecutionContextData* data = m_executionContextDataMap.get(context)) |
| 160 data->m_animationFrameCallChains.remove(callbackId); | 212 data->m_animationFrameCallChains.remove(callbackId); |
| 161 } | 213 } |
| 162 | 214 |
| 163 void AsyncCallStackTracker::willFireAnimationFrame(ExecutionContext* context, in
t callbackId) | 215 void AsyncCallStackTracker::willFireAnimationFrame(ExecutionContext* context, in
t callbackId) |
| 164 { | 216 { |
| 165 ASSERT(context); | 217 ASSERT(context); |
| 166 if (!isEnabled()) | 218 ASSERT(isEnabled()); |
| 167 return; | |
| 168 ASSERT(callbackId > 0); | 219 ASSERT(callbackId > 0); |
| 169 ASSERT(!m_currentAsyncCallChain); | 220 ASSERT(!m_currentAsyncCallChain); |
| 170 if (ExecutionContextData* data = m_executionContextDataMap.get(context)) | 221 if (ExecutionContextData* data = m_executionContextDataMap.get(context)) |
| 171 m_currentAsyncCallChain = data->m_animationFrameCallChains.take(callback
Id); | 222 setCurrentAsyncCallChain(data->m_animationFrameCallChains.take(callbackI
d)); |
| 223 } |
| 224 |
| 225 void AsyncCallStackTracker::didAddEventListener(EventTarget* eventTarget, const
AtomicString& eventType, EventListener* listener, bool useCapture, const ScriptV
alue& callFrames) |
| 226 { |
| 227 ASSERT(eventTarget->executionContext()); |
| 228 ASSERT(isEnabled()); |
| 229 if (!validateCallFrames(callFrames)) |
| 230 return; |
| 231 |
| 232 StringBuilder description; |
| 233 description.append(eventTarget->interfaceName()); |
| 234 if (!description.isEmpty()) |
| 235 description.append("."); |
| 236 if (listener->isAttribute()) { |
| 237 description.append("on"); |
| 238 description.append(eventType); |
| 239 } else { |
| 240 description.append("addEventListener(\""); |
| 241 description.append(eventType); |
| 242 description.append("\")"); |
| 243 } |
| 244 |
| 245 ExecutionContextData* data = createContextDataIfNeeded(eventTarget->executio
nContext()); |
| 246 data->addEventListenerData(eventTarget, eventType, std::make_pair(Registered
EventListener(listener, useCapture), createAsyncCallChain(description.toString()
, callFrames))); |
| 247 } |
| 248 |
| 249 void AsyncCallStackTracker::didRemoveEventListener(EventTarget* eventTarget, con
st AtomicString& eventType, EventListener* listener, bool useCapture) |
| 250 { |
| 251 ASSERT(eventTarget->executionContext()); |
| 252 ASSERT(isEnabled()); |
| 253 if (ExecutionContextData* data = m_executionContextDataMap.get(eventTarget->
executionContext())) |
| 254 data->removeEventListenerData(eventTarget, eventType, RegisteredEventLis
tener(listener, useCapture)); |
| 255 } |
| 256 |
| 257 void AsyncCallStackTracker::didRemoveAllEventListeners(EventTarget* eventTarget) |
| 258 { |
| 259 ASSERT(eventTarget->executionContext()); |
| 260 ASSERT(isEnabled()); |
| 261 if (ExecutionContextData* data = m_executionContextDataMap.get(eventTarget->
executionContext())) |
| 262 data->m_eventTargetCallChains.remove(eventTarget); |
| 263 } |
| 264 |
| 265 void AsyncCallStackTracker::willHandleEvent(EventTarget* eventTarget, const Atom
icString& eventType, EventListener* listener, bool useCapture) |
| 266 { |
| 267 ASSERT(eventTarget->executionContext()); |
| 268 ASSERT(isEnabled()); |
| 269 if (ExecutionContextData* data = m_executionContextDataMap.get(eventTarget->
executionContext())) |
| 270 setCurrentAsyncCallChain(data->findEventListenerData(eventTarget, eventT
ype, RegisteredEventListener(listener, useCapture))); |
| 172 } | 271 } |
| 173 | 272 |
| 174 void AsyncCallStackTracker::didFireAsyncCall() | 273 void AsyncCallStackTracker::didFireAsyncCall() |
| 175 { | 274 { |
| 176 m_currentAsyncCallChain = 0; | 275 setCurrentAsyncCallChain(0); |
| 177 } | 276 } |
| 178 | 277 |
| 179 PassRefPtr<AsyncCallStackTracker::AsyncCallChain> AsyncCallStackTracker::createA
syncCallChain(const String& description, const ScriptValue& callFrames) | 278 PassRefPtr<AsyncCallStackTracker::AsyncCallChain> AsyncCallStackTracker::createA
syncCallChain(const String& description, const ScriptValue& callFrames) |
| 180 { | 279 { |
| 181 ASSERT(isEnabled()); | |
| 182 RefPtr<AsyncCallChain> chain = adoptRef(m_currentAsyncCallChain ? new AsyncC
allStackTracker::AsyncCallChain(*m_currentAsyncCallChain) : new AsyncCallStackTr
acker::AsyncCallChain()); | 280 RefPtr<AsyncCallChain> chain = adoptRef(m_currentAsyncCallChain ? new AsyncC
allStackTracker::AsyncCallChain(*m_currentAsyncCallChain) : new AsyncCallStackTr
acker::AsyncCallChain()); |
| 183 ensureMaxAsyncCallChainDepth(chain.get(), m_maxAsyncCallStackDepth - 1); | 281 ensureMaxAsyncCallChainDepth(chain.get(), m_maxAsyncCallStackDepth - 1); |
| 184 chain->m_callStacks.prepend(adoptRef(new AsyncCallStackTracker::AsyncCallSta
ck(description, callFrames))); | 282 chain->m_callStacks.prepend(adoptRef(new AsyncCallStackTracker::AsyncCallSta
ck(description, callFrames))); |
| 185 return chain.release(); | 283 return chain.release(); |
| 186 } | 284 } |
| 187 | 285 |
| 286 void AsyncCallStackTracker::setCurrentAsyncCallChain(PassRefPtr<AsyncCallChain>
chain) |
| 287 { |
| 288 if (m_currentAsyncCallChain) { |
| 289 m_nestedAsyncCallCount += chain ? 1 : -1; |
| 290 if (!m_nestedAsyncCallCount) |
| 291 m_currentAsyncCallChain = 0; |
| 292 } else if (chain) { |
| 293 m_currentAsyncCallChain = chain; |
| 294 m_nestedAsyncCallCount = 1; |
| 295 } |
| 296 } |
| 297 |
| 188 void AsyncCallStackTracker::ensureMaxAsyncCallChainDepth(AsyncCallChain* chain,
unsigned maxDepth) | 298 void AsyncCallStackTracker::ensureMaxAsyncCallChainDepth(AsyncCallChain* chain,
unsigned maxDepth) |
| 189 { | 299 { |
| 190 while (chain->m_callStacks.size() > maxDepth) | 300 while (chain->m_callStacks.size() > maxDepth) |
| 191 chain->m_callStacks.removeLast(); | 301 chain->m_callStacks.removeLast(); |
| 192 } | 302 } |
| 193 | 303 |
| 194 bool AsyncCallStackTracker::validateCallFrames(const ScriptValue& callFrames) | 304 bool AsyncCallStackTracker::validateCallFrames(const ScriptValue& callFrames) |
| 195 { | 305 { |
| 196 return !callFrames.hasNoValue(); | 306 return !callFrames.hasNoValue(); |
| 197 } | 307 } |
| 198 | 308 |
| 199 AsyncCallStackTracker::ExecutionContextData* AsyncCallStackTracker::createContex
tDataIfNeeded(ExecutionContext* context) | 309 AsyncCallStackTracker::ExecutionContextData* AsyncCallStackTracker::createContex
tDataIfNeeded(ExecutionContext* context) |
| 200 { | 310 { |
| 201 ExecutionContextData* data = m_executionContextDataMap.get(context); | 311 ExecutionContextData* data = m_executionContextDataMap.get(context); |
| 202 if (!data) { | 312 if (!data) { |
| 203 data = new AsyncCallStackTracker::ExecutionContextData(this, context); | 313 data = new AsyncCallStackTracker::ExecutionContextData(this, context); |
| 204 m_executionContextDataMap.set(context, data); | 314 m_executionContextDataMap.set(context, data); |
| 205 } | 315 } |
| 206 return data; | 316 return data; |
| 207 } | 317 } |
| 208 | 318 |
| 209 void AsyncCallStackTracker::clear() | 319 void AsyncCallStackTracker::clear() |
| 210 { | 320 { |
| 211 m_currentAsyncCallChain = 0; | 321 m_currentAsyncCallChain = 0; |
| 322 m_nestedAsyncCallCount = 0; |
| 212 ExecutionContextDataMap copy; | 323 ExecutionContextDataMap copy; |
| 213 m_executionContextDataMap.swap(copy); | 324 m_executionContextDataMap.swap(copy); |
| 214 for (ExecutionContextDataMap::const_iterator it = copy.begin(); it != copy.e
nd(); ++it) | 325 for (ExecutionContextDataMap::const_iterator it = copy.begin(); it != copy.e
nd(); ++it) |
| 215 delete it->value; | 326 delete it->value; |
| 216 } | 327 } |
| 217 | 328 |
| 218 } // namespace WebCore | 329 } // namespace WebCore |
| OLD | NEW |