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