|
1 /* |
|
2 * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. |
|
3 * Copyright (C) 2010 Google Inc. All rights reserved. |
|
4 * |
|
5 * Redistribution and use in source and binary forms, with or without |
|
6 * modification, are permitted provided that the following conditions |
|
7 * are met: |
|
8 * |
|
9 * 1. Redistributions of source code must retain the above copyright |
|
10 * notice, this list of conditions and the following disclaimer. |
|
11 * 2. Redistributions in binary form must reproduce the above copyright |
|
12 * notice, this list of conditions and the following disclaimer in the |
|
13 * documentation and/or other materials provided with the distribution. |
|
14 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of |
|
15 * its contributors may be used to endorse or promote products derived |
|
16 * from this software without specific prior written permission. |
|
17 * |
|
18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY |
|
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
|
21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
|
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
|
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
|
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
|
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
|
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
28 */ |
|
29 |
|
30 #include "config.h" |
|
31 #include "ScriptDebugServer.h" |
|
32 |
|
33 #if ENABLE(JAVASCRIPT_DEBUGGER) |
|
34 |
|
35 #include "DOMWindow.h" |
|
36 #include "EventLoop.h" |
|
37 #include "Frame.h" |
|
38 #include "FrameTree.h" |
|
39 #include "FrameView.h" |
|
40 #include "JSDOMWindowCustom.h" |
|
41 #include "JavaScriptCallFrame.h" |
|
42 #include "Page.h" |
|
43 #include "PageGroup.h" |
|
44 #include "PluginView.h" |
|
45 #include "ScriptBreakpoint.h" |
|
46 #include "ScriptController.h" |
|
47 #include "ScriptDebugListener.h" |
|
48 #include "ScrollView.h" |
|
49 #include "Widget.h" |
|
50 #include <debugger/DebuggerCallFrame.h> |
|
51 #include <parser/SourceCode.h> |
|
52 #include <runtime/JSLock.h> |
|
53 #include <wtf/MainThread.h> |
|
54 #include <wtf/StdLibExtras.h> |
|
55 #include <wtf/UnusedParam.h> |
|
56 |
|
57 using namespace JSC; |
|
58 |
|
59 namespace WebCore { |
|
60 |
|
61 ScriptDebugServer& ScriptDebugServer::shared() |
|
62 { |
|
63 DEFINE_STATIC_LOCAL(ScriptDebugServer, server, ()); |
|
64 return server; |
|
65 } |
|
66 |
|
67 ScriptDebugServer::ScriptDebugServer() |
|
68 : m_callingListeners(false) |
|
69 , m_pauseOnExceptionsState(DontPauseOnExceptions) |
|
70 , m_pauseOnNextStatement(false) |
|
71 , m_paused(false) |
|
72 , m_pausedPage(0) |
|
73 , m_doneProcessingDebuggerEvents(true) |
|
74 , m_breakpointsActivated(true) |
|
75 , m_pauseOnCallFrame(0) |
|
76 , m_recompileTimer(this, &ScriptDebugServer::recompileAllJSFunctions) |
|
77 { |
|
78 } |
|
79 |
|
80 ScriptDebugServer::~ScriptDebugServer() |
|
81 { |
|
82 deleteAllValues(m_pageListenersMap); |
|
83 } |
|
84 |
|
85 void ScriptDebugServer::addListener(ScriptDebugListener* listener, Page* page) |
|
86 { |
|
87 ASSERT_ARG(listener, listener); |
|
88 ASSERT_ARG(page, page); |
|
89 |
|
90 pair<PageListenersMap::iterator, bool> result = m_pageListenersMap.add(page, 0); |
|
91 if (result.second) |
|
92 result.first->second = new ListenerSet; |
|
93 |
|
94 ListenerSet* listeners = result.first->second; |
|
95 listeners->add(listener); |
|
96 |
|
97 didAddListener(page); |
|
98 } |
|
99 |
|
100 void ScriptDebugServer::removeListener(ScriptDebugListener* listener, Page* page) |
|
101 { |
|
102 ASSERT_ARG(listener, listener); |
|
103 ASSERT_ARG(page, page); |
|
104 |
|
105 PageListenersMap::iterator it = m_pageListenersMap.find(page); |
|
106 if (it == m_pageListenersMap.end()) |
|
107 return; |
|
108 |
|
109 ListenerSet* listeners = it->second; |
|
110 listeners->remove(listener); |
|
111 if (listeners->isEmpty()) { |
|
112 m_pageListenersMap.remove(it); |
|
113 delete listeners; |
|
114 } |
|
115 |
|
116 didRemoveListener(page); |
|
117 } |
|
118 |
|
119 void ScriptDebugServer::pageCreated(Page* page) |
|
120 { |
|
121 ASSERT_ARG(page, page); |
|
122 |
|
123 if (!hasListenersInterestedInPage(page)) |
|
124 return; |
|
125 page->setDebugger(this); |
|
126 } |
|
127 |
|
128 bool ScriptDebugServer::isDebuggerAlwaysEnabled() |
|
129 { |
|
130 return false; |
|
131 } |
|
132 |
|
133 bool ScriptDebugServer::hasListenersInterestedInPage(Page* page) |
|
134 { |
|
135 ASSERT_ARG(page, page); |
|
136 |
|
137 return m_pageListenersMap.contains(page); |
|
138 } |
|
139 |
|
140 bool ScriptDebugServer::setBreakpoint(const String& sourceID, ScriptBreakpoint breakpoint, unsigned lineNumber, unsigned* actualLineNumber) |
|
141 { |
|
142 intptr_t sourceIDValue = sourceID.toIntPtr(); |
|
143 if (!sourceIDValue) |
|
144 return false; |
|
145 BreakpointsMap::iterator it = m_breakpoints.find(sourceIDValue); |
|
146 if (it == m_breakpoints.end()) |
|
147 it = m_breakpoints.set(sourceIDValue, SourceBreakpoints()).first; |
|
148 it->second.set(lineNumber, breakpoint); |
|
149 *actualLineNumber = lineNumber; |
|
150 return true; |
|
151 } |
|
152 |
|
153 void ScriptDebugServer::removeBreakpoint(const String& sourceID, unsigned lineNumber) |
|
154 { |
|
155 intptr_t sourceIDValue = sourceID.toIntPtr(); |
|
156 if (!sourceIDValue) |
|
157 return; |
|
158 BreakpointsMap::iterator it = m_breakpoints.find(sourceIDValue); |
|
159 if (it != m_breakpoints.end()) |
|
160 it->second.remove(lineNumber); |
|
161 } |
|
162 |
|
163 bool ScriptDebugServer::hasBreakpoint(intptr_t sourceID, unsigned lineNumber) const |
|
164 { |
|
165 if (!m_breakpointsActivated) |
|
166 return false; |
|
167 |
|
168 BreakpointsMap::const_iterator it = m_breakpoints.find(sourceID); |
|
169 if (it == m_breakpoints.end()) |
|
170 return false; |
|
171 SourceBreakpoints::const_iterator breakIt = it->second.find(lineNumber); |
|
172 if (breakIt == it->second.end() || !breakIt->second.enabled) |
|
173 return false; |
|
174 |
|
175 // An empty condition counts as no condition which is equivalent to "true". |
|
176 if (breakIt->second.condition.isEmpty()) |
|
177 return true; |
|
178 |
|
179 JSValue exception; |
|
180 JSValue result = m_currentCallFrame->evaluate(stringToUString(breakIt->second.condition), exception); |
|
181 if (exception) { |
|
182 // An erroneous condition counts as "false". |
|
183 return false; |
|
184 } |
|
185 return result.toBoolean(m_currentCallFrame->scopeChain()->globalObject->globalExec()); |
|
186 } |
|
187 |
|
188 void ScriptDebugServer::clearBreakpoints() |
|
189 { |
|
190 m_breakpoints.clear(); |
|
191 } |
|
192 |
|
193 void ScriptDebugServer::setBreakpointsActivated(bool activated) |
|
194 { |
|
195 m_breakpointsActivated = activated; |
|
196 } |
|
197 |
|
198 void ScriptDebugServer::setPauseOnExceptionsState(PauseOnExceptionsState pause) |
|
199 { |
|
200 m_pauseOnExceptionsState = pause; |
|
201 } |
|
202 |
|
203 void ScriptDebugServer::pause() |
|
204 { |
|
205 m_pauseOnNextStatement = true; |
|
206 } |
|
207 |
|
208 void ScriptDebugServer::continueProgram() |
|
209 { |
|
210 if (!m_paused) |
|
211 return; |
|
212 |
|
213 m_pauseOnNextStatement = false; |
|
214 m_doneProcessingDebuggerEvents = true; |
|
215 } |
|
216 |
|
217 void ScriptDebugServer::stepIntoStatement() |
|
218 { |
|
219 if (!m_paused) |
|
220 return; |
|
221 |
|
222 m_pauseOnNextStatement = true; |
|
223 m_doneProcessingDebuggerEvents = true; |
|
224 } |
|
225 |
|
226 void ScriptDebugServer::stepOverStatement() |
|
227 { |
|
228 if (!m_paused) |
|
229 return; |
|
230 |
|
231 m_pauseOnCallFrame = m_currentCallFrame.get(); |
|
232 m_doneProcessingDebuggerEvents = true; |
|
233 } |
|
234 |
|
235 void ScriptDebugServer::stepOutOfFunction() |
|
236 { |
|
237 if (!m_paused) |
|
238 return; |
|
239 |
|
240 m_pauseOnCallFrame = m_currentCallFrame ? m_currentCallFrame->caller() : 0; |
|
241 m_doneProcessingDebuggerEvents = true; |
|
242 } |
|
243 |
|
244 bool ScriptDebugServer::editScriptSource(const String&, const String&, String&) |
|
245 { |
|
246 // FIXME(40300): implement this. |
|
247 return false; |
|
248 } |
|
249 |
|
250 JavaScriptCallFrame* ScriptDebugServer::currentCallFrame() |
|
251 { |
|
252 if (!m_paused) |
|
253 return 0; |
|
254 return m_currentCallFrame.get(); |
|
255 } |
|
256 |
|
257 void ScriptDebugServer::dispatchDidPause(ScriptDebugListener* listener) |
|
258 { |
|
259 ASSERT(m_paused); |
|
260 ScriptState* state = m_currentCallFrame->scopeChain()->globalObject->globalExec(); |
|
261 listener->didPause(state); |
|
262 } |
|
263 |
|
264 void ScriptDebugServer::dispatchDidContinue(ScriptDebugListener* listener) |
|
265 { |
|
266 listener->didContinue(); |
|
267 } |
|
268 |
|
269 void ScriptDebugServer::dispatchDidParseSource(const ListenerSet& listeners, const JSC::SourceCode& source, ScriptWorldType worldType) |
|
270 { |
|
271 String sourceID = ustringToString(JSC::UString::from(source.provider()->asID())); |
|
272 String url = ustringToString(source.provider()->url()); |
|
273 String data = ustringToString(JSC::UString(source.data(), source.length())); |
|
274 int firstLine = source.firstLine(); |
|
275 |
|
276 Vector<ScriptDebugListener*> copy; |
|
277 copyToVector(listeners, copy); |
|
278 for (size_t i = 0; i < copy.size(); ++i) |
|
279 copy[i]->didParseSource(sourceID, url, data, firstLine, worldType); |
|
280 } |
|
281 |
|
282 void ScriptDebugServer::dispatchFailedToParseSource(const ListenerSet& listeners, const SourceCode& source, int errorLine, const String& errorMessage) |
|
283 { |
|
284 String url = ustringToString(source.provider()->url()); |
|
285 String data = ustringToString(JSC::UString(source.data(), source.length())); |
|
286 int firstLine = source.firstLine(); |
|
287 |
|
288 Vector<ScriptDebugListener*> copy; |
|
289 copyToVector(listeners, copy); |
|
290 for (size_t i = 0; i < copy.size(); ++i) |
|
291 copy[i]->failedToParseSource(url, data, firstLine, errorLine, errorMessage); |
|
292 } |
|
293 |
|
294 static Page* toPage(JSGlobalObject* globalObject) |
|
295 { |
|
296 ASSERT_ARG(globalObject, globalObject); |
|
297 |
|
298 JSDOMWindow* window = asJSDOMWindow(globalObject); |
|
299 Frame* frame = window->impl()->frame(); |
|
300 return frame ? frame->page() : 0; |
|
301 } |
|
302 |
|
303 static ScriptWorldType currentWorldType(ExecState* exec) |
|
304 { |
|
305 if (currentWorld(exec) == mainThreadNormalWorld()) |
|
306 return MAIN_WORLD; |
|
307 return EXTENSIONS_WORLD; |
|
308 } |
|
309 |
|
310 void ScriptDebugServer::detach(JSGlobalObject* globalObject) |
|
311 { |
|
312 // If we're detaching from the currently executing global object, manually tear down our |
|
313 // stack, since we won't get further debugger callbacks to do so. Also, resume execution, |
|
314 // since there's no point in staying paused once a window closes. |
|
315 if (m_currentCallFrame && m_currentCallFrame->dynamicGlobalObject() == globalObject) { |
|
316 m_currentCallFrame = 0; |
|
317 m_pauseOnCallFrame = 0; |
|
318 continueProgram(); |
|
319 } |
|
320 Debugger::detach(globalObject); |
|
321 } |
|
322 |
|
323 void ScriptDebugServer::sourceParsed(ExecState* exec, const SourceCode& source, int errorLine, const UString& errorMessage) |
|
324 { |
|
325 if (m_callingListeners) |
|
326 return; |
|
327 |
|
328 Page* page = toPage(exec->lexicalGlobalObject()); |
|
329 if (!page) |
|
330 return; |
|
331 |
|
332 ScriptWorldType worldType = currentWorldType(exec); |
|
333 |
|
334 m_callingListeners = true; |
|
335 |
|
336 bool isError = errorLine != -1; |
|
337 |
|
338 if (ListenerSet* pageListeners = m_pageListenersMap.get(page)) { |
|
339 ASSERT(!pageListeners->isEmpty()); |
|
340 if (isError) |
|
341 dispatchFailedToParseSource(*pageListeners, source, errorLine, ustringToString(errorMessage)); |
|
342 else |
|
343 dispatchDidParseSource(*pageListeners, source, worldType); |
|
344 } |
|
345 |
|
346 m_callingListeners = false; |
|
347 } |
|
348 |
|
349 void ScriptDebugServer::dispatchFunctionToListeners(const ListenerSet& listeners, JavaScriptExecutionCallback callback) |
|
350 { |
|
351 Vector<ScriptDebugListener*> copy; |
|
352 copyToVector(listeners, copy); |
|
353 for (size_t i = 0; i < copy.size(); ++i) |
|
354 (this->*callback)(copy[i]); |
|
355 } |
|
356 |
|
357 void ScriptDebugServer::dispatchFunctionToListeners(JavaScriptExecutionCallback callback, Page* page) |
|
358 { |
|
359 if (m_callingListeners) |
|
360 return; |
|
361 |
|
362 m_callingListeners = true; |
|
363 |
|
364 if (ListenerSet* pageListeners = m_pageListenersMap.get(page)) { |
|
365 ASSERT(!pageListeners->isEmpty()); |
|
366 dispatchFunctionToListeners(*pageListeners, callback); |
|
367 } |
|
368 |
|
369 m_callingListeners = false; |
|
370 } |
|
371 |
|
372 void ScriptDebugServer::setJavaScriptPaused(const PageGroup& pageGroup, bool paused) |
|
373 { |
|
374 setMainThreadCallbacksPaused(paused); |
|
375 |
|
376 const HashSet<Page*>& pages = pageGroup.pages(); |
|
377 |
|
378 HashSet<Page*>::const_iterator end = pages.end(); |
|
379 for (HashSet<Page*>::const_iterator it = pages.begin(); it != end; ++it) |
|
380 setJavaScriptPaused(*it, paused); |
|
381 } |
|
382 |
|
383 void ScriptDebugServer::setJavaScriptPaused(Page* page, bool paused) |
|
384 { |
|
385 ASSERT_ARG(page, page); |
|
386 |
|
387 page->setDefersLoading(paused); |
|
388 |
|
389 for (Frame* frame = page->mainFrame(); frame; frame = frame->tree()->traverseNext()) |
|
390 setJavaScriptPaused(frame, paused); |
|
391 } |
|
392 |
|
393 void ScriptDebugServer::setJavaScriptPaused(Frame* frame, bool paused) |
|
394 { |
|
395 ASSERT_ARG(frame, frame); |
|
396 |
|
397 if (!frame->script()->canExecuteScripts(NotAboutToExecuteScript)) |
|
398 return; |
|
399 |
|
400 frame->script()->setPaused(paused); |
|
401 |
|
402 Document* document = frame->document(); |
|
403 if (paused) |
|
404 document->suspendActiveDOMObjects(); |
|
405 else |
|
406 document->resumeActiveDOMObjects(); |
|
407 |
|
408 setJavaScriptPaused(frame->view(), paused); |
|
409 } |
|
410 |
|
411 void ScriptDebugServer::setJavaScriptPaused(FrameView* view, bool paused) |
|
412 { |
|
413 if (!view) |
|
414 return; |
|
415 |
|
416 const HashSet<RefPtr<Widget> >* children = view->children(); |
|
417 ASSERT(children); |
|
418 |
|
419 HashSet<RefPtr<Widget> >::const_iterator end = children->end(); |
|
420 for (HashSet<RefPtr<Widget> >::const_iterator it = children->begin(); it != end; ++it) { |
|
421 Widget* widget = (*it).get(); |
|
422 if (!widget->isPluginView()) |
|
423 continue; |
|
424 static_cast<PluginView*>(widget)->setJavaScriptPaused(paused); |
|
425 } |
|
426 } |
|
427 |
|
428 void ScriptDebugServer::pauseIfNeeded(Page* page) |
|
429 { |
|
430 if (m_paused) |
|
431 return; |
|
432 |
|
433 if (!page || !hasListenersInterestedInPage(page)) |
|
434 return; |
|
435 |
|
436 bool pauseNow = m_pauseOnNextStatement; |
|
437 pauseNow |= (m_pauseOnCallFrame == m_currentCallFrame); |
|
438 pauseNow |= (m_currentCallFrame->line() > 0 && hasBreakpoint(m_currentCallFrame->sourceID(), m_currentCallFrame->line())); |
|
439 if (!pauseNow) |
|
440 return; |
|
441 |
|
442 m_pauseOnCallFrame = 0; |
|
443 m_pauseOnNextStatement = false; |
|
444 m_paused = true; |
|
445 m_pausedPage = page; |
|
446 |
|
447 dispatchFunctionToListeners(&ScriptDebugServer::dispatchDidPause, page); |
|
448 |
|
449 setJavaScriptPaused(page->group(), true); |
|
450 |
|
451 TimerBase::fireTimersInNestedEventLoop(); |
|
452 |
|
453 EventLoop loop; |
|
454 m_doneProcessingDebuggerEvents = false; |
|
455 while (!m_doneProcessingDebuggerEvents && !loop.ended()) |
|
456 loop.cycle(); |
|
457 |
|
458 setJavaScriptPaused(page->group(), false); |
|
459 |
|
460 m_paused = false; |
|
461 m_pausedPage = 0; |
|
462 |
|
463 dispatchFunctionToListeners(&ScriptDebugServer::dispatchDidContinue, page); |
|
464 } |
|
465 |
|
466 void ScriptDebugServer::callEvent(const DebuggerCallFrame& debuggerCallFrame, intptr_t sourceID, int lineNumber) |
|
467 { |
|
468 if (m_paused) |
|
469 return; |
|
470 |
|
471 m_currentCallFrame = JavaScriptCallFrame::create(debuggerCallFrame, m_currentCallFrame, sourceID, lineNumber); |
|
472 pauseIfNeeded(toPage(debuggerCallFrame.dynamicGlobalObject())); |
|
473 } |
|
474 |
|
475 void ScriptDebugServer::atStatement(const DebuggerCallFrame& debuggerCallFrame, intptr_t sourceID, int lineNumber) |
|
476 { |
|
477 if (m_paused) |
|
478 return; |
|
479 |
|
480 ASSERT(m_currentCallFrame); |
|
481 if (!m_currentCallFrame) |
|
482 return; |
|
483 |
|
484 m_currentCallFrame->update(debuggerCallFrame, sourceID, lineNumber); |
|
485 pauseIfNeeded(toPage(debuggerCallFrame.dynamicGlobalObject())); |
|
486 } |
|
487 |
|
488 void ScriptDebugServer::returnEvent(const DebuggerCallFrame& debuggerCallFrame, intptr_t sourceID, int lineNumber) |
|
489 { |
|
490 if (m_paused) |
|
491 return; |
|
492 |
|
493 ASSERT(m_currentCallFrame); |
|
494 if (!m_currentCallFrame) |
|
495 return; |
|
496 |
|
497 m_currentCallFrame->update(debuggerCallFrame, sourceID, lineNumber); |
|
498 pauseIfNeeded(toPage(debuggerCallFrame.dynamicGlobalObject())); |
|
499 |
|
500 // Treat stepping over a return statement like stepping out. |
|
501 if (m_currentCallFrame == m_pauseOnCallFrame) |
|
502 m_pauseOnCallFrame = m_currentCallFrame->caller(); |
|
503 m_currentCallFrame = m_currentCallFrame->caller(); |
|
504 } |
|
505 |
|
506 void ScriptDebugServer::exception(const DebuggerCallFrame& debuggerCallFrame, intptr_t sourceID, int lineNumber, bool hasHandler) |
|
507 { |
|
508 if (m_paused) |
|
509 return; |
|
510 |
|
511 ASSERT(m_currentCallFrame); |
|
512 if (!m_currentCallFrame) |
|
513 return; |
|
514 |
|
515 if (m_pauseOnExceptionsState == PauseOnAllExceptions || (m_pauseOnExceptionsState == PauseOnUncaughtExceptions && !hasHandler)) |
|
516 m_pauseOnNextStatement = true; |
|
517 |
|
518 m_currentCallFrame->update(debuggerCallFrame, sourceID, lineNumber); |
|
519 pauseIfNeeded(toPage(debuggerCallFrame.dynamicGlobalObject())); |
|
520 } |
|
521 |
|
522 void ScriptDebugServer::willExecuteProgram(const DebuggerCallFrame& debuggerCallFrame, intptr_t sourceID, int lineNumber) |
|
523 { |
|
524 if (m_paused) |
|
525 return; |
|
526 |
|
527 m_currentCallFrame = JavaScriptCallFrame::create(debuggerCallFrame, m_currentCallFrame, sourceID, lineNumber); |
|
528 pauseIfNeeded(toPage(debuggerCallFrame.dynamicGlobalObject())); |
|
529 } |
|
530 |
|
531 void ScriptDebugServer::didExecuteProgram(const DebuggerCallFrame& debuggerCallFrame, intptr_t sourceID, int lineNumber) |
|
532 { |
|
533 if (m_paused) |
|
534 return; |
|
535 |
|
536 ASSERT(m_currentCallFrame); |
|
537 if (!m_currentCallFrame) |
|
538 return; |
|
539 |
|
540 m_currentCallFrame->update(debuggerCallFrame, sourceID, lineNumber); |
|
541 pauseIfNeeded(toPage(debuggerCallFrame.dynamicGlobalObject())); |
|
542 |
|
543 // Treat stepping over the end of a program like stepping out. |
|
544 if (m_currentCallFrame == m_pauseOnCallFrame) |
|
545 m_pauseOnCallFrame = m_currentCallFrame->caller(); |
|
546 m_currentCallFrame = m_currentCallFrame->caller(); |
|
547 } |
|
548 |
|
549 void ScriptDebugServer::didReachBreakpoint(const DebuggerCallFrame& debuggerCallFrame, intptr_t sourceID, int lineNumber) |
|
550 { |
|
551 if (m_paused) |
|
552 return; |
|
553 |
|
554 ASSERT(m_currentCallFrame); |
|
555 if (!m_currentCallFrame) |
|
556 return; |
|
557 |
|
558 m_pauseOnNextStatement = true; |
|
559 m_currentCallFrame->update(debuggerCallFrame, sourceID, lineNumber); |
|
560 pauseIfNeeded(toPage(debuggerCallFrame.dynamicGlobalObject())); |
|
561 } |
|
562 |
|
563 void ScriptDebugServer::recompileAllJSFunctionsSoon() |
|
564 { |
|
565 m_recompileTimer.startOneShot(0); |
|
566 } |
|
567 |
|
568 void ScriptDebugServer::recompileAllJSFunctions(Timer<ScriptDebugServer>*) |
|
569 { |
|
570 JSLock lock(SilenceAssertionsOnly); |
|
571 // If JavaScript stack is not empty postpone recompilation. |
|
572 if (JSDOMWindow::commonJSGlobalData()->dynamicGlobalObject) |
|
573 recompileAllJSFunctionsSoon(); |
|
574 else |
|
575 Debugger::recompileAllJSFunctions(JSDOMWindow::commonJSGlobalData()); |
|
576 } |
|
577 |
|
578 void ScriptDebugServer::didAddListener(Page* page) |
|
579 { |
|
580 recompileAllJSFunctionsSoon(); |
|
581 page->setDebugger(this); |
|
582 } |
|
583 |
|
584 void ScriptDebugServer::didRemoveListener(Page* page) |
|
585 { |
|
586 if (hasListenersInterestedInPage(page)) |
|
587 return; |
|
588 |
|
589 if (m_pausedPage == page) |
|
590 m_doneProcessingDebuggerEvents = true; |
|
591 |
|
592 recompileAllJSFunctionsSoon(); |
|
593 page->setDebugger(0); |
|
594 } |
|
595 |
|
596 } // namespace WebCore |
|
597 |
|
598 #endif // ENABLE(JAVASCRIPT_DEBUGGER) |