• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 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)
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 <runtime/CollectorHeapIterator.h>
49 #include <debugger/DebuggerCallFrame.h>
50 #include <runtime/JSLock.h>
51 #include <parser/Parser.h>
52 #include <wtf/MainThread.h>
53 #include <wtf/StdLibExtras.h>
54 #include <wtf/UnusedParam.h>
55 
56 using namespace JSC;
57 
58 namespace WebCore {
59 
60 typedef JavaScriptDebugServer::ListenerSet ListenerSet;
61 
shared()62 JavaScriptDebugServer& JavaScriptDebugServer::shared()
63 {
64     DEFINE_STATIC_LOCAL(JavaScriptDebugServer, server, ());
65     return server;
66 }
67 
JavaScriptDebugServer()68 JavaScriptDebugServer::JavaScriptDebugServer()
69     : m_callingListeners(false)
70     , m_pauseOnExceptions(false)
71     , m_pauseOnNextStatement(false)
72     , m_paused(false)
73     , m_doneProcessingDebuggerEvents(true)
74     , m_pauseOnCallFrame(0)
75     , m_recompileTimer(this, &JavaScriptDebugServer::recompileAllJSFunctions)
76 {
77 }
78 
~JavaScriptDebugServer()79 JavaScriptDebugServer::~JavaScriptDebugServer()
80 {
81     deleteAllValues(m_pageListenersMap);
82     deleteAllValues(m_breakpoints);
83 }
84 
addListener(JavaScriptDebugListener * listener)85 void JavaScriptDebugServer::addListener(JavaScriptDebugListener* listener)
86 {
87     ASSERT_ARG(listener, listener);
88 
89     m_listeners.add(listener);
90 
91     didAddListener(0);
92 }
93 
removeListener(JavaScriptDebugListener * listener)94 void JavaScriptDebugServer::removeListener(JavaScriptDebugListener* listener)
95 {
96     ASSERT_ARG(listener, listener);
97 
98     m_listeners.remove(listener);
99 
100     didRemoveListener(0);
101     if (!hasListeners())
102         didRemoveLastListener();
103 }
104 
addListener(JavaScriptDebugListener * listener,Page * page)105 void JavaScriptDebugServer::addListener(JavaScriptDebugListener* listener, Page* page)
106 {
107     ASSERT_ARG(listener, listener);
108     ASSERT_ARG(page, page);
109 
110     pair<PageListenersMap::iterator, bool> result = m_pageListenersMap.add(page, 0);
111     if (result.second)
112         result.first->second = new ListenerSet;
113 
114     ListenerSet* listeners = result.first->second;
115     listeners->add(listener);
116 
117     didAddListener(page);
118 }
119 
removeListener(JavaScriptDebugListener * listener,Page * page)120 void JavaScriptDebugServer::removeListener(JavaScriptDebugListener* listener, Page* page)
121 {
122     ASSERT_ARG(listener, listener);
123     ASSERT_ARG(page, page);
124 
125     PageListenersMap::iterator it = m_pageListenersMap.find(page);
126     if (it == m_pageListenersMap.end())
127         return;
128 
129     ListenerSet* listeners = it->second;
130     listeners->remove(listener);
131     if (listeners->isEmpty()) {
132         m_pageListenersMap.remove(it);
133         delete listeners;
134     }
135 
136     didRemoveListener(page);
137     if (!hasListeners())
138         didRemoveLastListener();
139 }
140 
pageCreated(Page * page)141 void JavaScriptDebugServer::pageCreated(Page* page)
142 {
143     ASSERT_ARG(page, page);
144 
145     if (!hasListenersInterestedInPage(page))
146         return;
147     page->setDebugger(this);
148 }
149 
hasListenersInterestedInPage(Page * page)150 bool JavaScriptDebugServer::hasListenersInterestedInPage(Page* page)
151 {
152     ASSERT_ARG(page, page);
153 
154     if (hasGlobalListeners())
155         return true;
156 
157     return m_pageListenersMap.contains(page);
158 }
159 
addBreakpoint(intptr_t sourceID,unsigned lineNumber)160 void JavaScriptDebugServer::addBreakpoint(intptr_t sourceID, unsigned lineNumber)
161 {
162     HashSet<unsigned>* lines = m_breakpoints.get(sourceID);
163     if (!lines) {
164         lines = new HashSet<unsigned>;
165         m_breakpoints.set(sourceID, lines);
166     }
167 
168     lines->add(lineNumber);
169 }
170 
removeBreakpoint(intptr_t sourceID,unsigned lineNumber)171 void JavaScriptDebugServer::removeBreakpoint(intptr_t sourceID, unsigned lineNumber)
172 {
173     HashSet<unsigned>* lines = m_breakpoints.get(sourceID);
174     if (!lines)
175         return;
176 
177     lines->remove(lineNumber);
178 
179     if (!lines->isEmpty())
180         return;
181 
182     m_breakpoints.remove(sourceID);
183     delete lines;
184 }
185 
hasBreakpoint(intptr_t sourceID,unsigned lineNumber) const186 bool JavaScriptDebugServer::hasBreakpoint(intptr_t sourceID, unsigned lineNumber) const
187 {
188     HashSet<unsigned>* lines = m_breakpoints.get(sourceID);
189     if (!lines)
190         return false;
191     return lines->contains(lineNumber);
192 }
193 
clearBreakpoints()194 void JavaScriptDebugServer::clearBreakpoints()
195 {
196     deleteAllValues(m_breakpoints);
197     m_breakpoints.clear();
198 }
199 
setPauseOnExceptions(bool pause)200 void JavaScriptDebugServer::setPauseOnExceptions(bool pause)
201 {
202     m_pauseOnExceptions = pause;
203 }
204 
pauseProgram()205 void JavaScriptDebugServer::pauseProgram()
206 {
207     m_pauseOnNextStatement = true;
208 }
209 
continueProgram()210 void JavaScriptDebugServer::continueProgram()
211 {
212     if (!m_paused)
213         return;
214 
215     m_pauseOnNextStatement = false;
216     m_doneProcessingDebuggerEvents = true;
217 }
218 
stepIntoStatement()219 void JavaScriptDebugServer::stepIntoStatement()
220 {
221     if (!m_paused)
222         return;
223 
224     m_pauseOnNextStatement = true;
225     m_doneProcessingDebuggerEvents = true;
226 }
227 
stepOverStatement()228 void JavaScriptDebugServer::stepOverStatement()
229 {
230     if (!m_paused)
231         return;
232 
233     m_pauseOnCallFrame = m_currentCallFrame.get();
234     m_doneProcessingDebuggerEvents = true;
235 }
236 
stepOutOfFunction()237 void JavaScriptDebugServer::stepOutOfFunction()
238 {
239     if (!m_paused)
240         return;
241 
242     m_pauseOnCallFrame = m_currentCallFrame ? m_currentCallFrame->caller() : 0;
243     m_doneProcessingDebuggerEvents = true;
244 }
245 
currentCallFrame()246 JavaScriptCallFrame* JavaScriptDebugServer::currentCallFrame()
247 {
248     if (!m_paused)
249         return 0;
250     return m_currentCallFrame.get();
251 }
252 
dispatchDidParseSource(const ListenerSet & listeners,ExecState * exec,const JSC::SourceCode & source)253 static void dispatchDidParseSource(const ListenerSet& listeners, ExecState* exec, const JSC::SourceCode& source)
254 {
255     Vector<JavaScriptDebugListener*> copy;
256     copyToVector(listeners, copy);
257     for (size_t i = 0; i < copy.size(); ++i)
258         copy[i]->didParseSource(exec, source);
259 }
260 
dispatchFailedToParseSource(const ListenerSet & listeners,ExecState * exec,const SourceCode & source,int errorLine,const String & errorMessage)261 static void dispatchFailedToParseSource(const ListenerSet& listeners, ExecState* exec, const SourceCode& source, int errorLine, const String& errorMessage)
262 {
263     Vector<JavaScriptDebugListener*> copy;
264     copyToVector(listeners, copy);
265     for (size_t i = 0; i < copy.size(); ++i)
266         copy[i]->failedToParseSource(exec, source, errorLine, errorMessage);
267 }
268 
toPage(JSGlobalObject * globalObject)269 static Page* toPage(JSGlobalObject* globalObject)
270 {
271     ASSERT_ARG(globalObject, globalObject);
272 
273     JSDOMWindow* window = asJSDOMWindow(globalObject);
274     Frame* frame = window->impl()->frame();
275     return frame ? frame->page() : 0;
276 }
277 
detach(JSGlobalObject * globalObject)278 void JavaScriptDebugServer::detach(JSGlobalObject* globalObject)
279 {
280     // If we're detaching from the currently executing global object, manually tear down our
281     // stack, since we won't get further debugger callbacks to do so. Also, resume execution,
282     // since there's no point in staying paused once a window closes.
283     if (m_currentCallFrame && m_currentCallFrame->dynamicGlobalObject() == globalObject) {
284         m_currentCallFrame = 0;
285         m_pauseOnCallFrame = 0;
286         continueProgram();
287     }
288     Debugger::detach(globalObject);
289 }
290 
sourceParsed(ExecState * exec,const SourceCode & source,int errorLine,const UString & errorMessage)291 void JavaScriptDebugServer::sourceParsed(ExecState* exec, const SourceCode& source, int errorLine, const UString& errorMessage)
292 {
293     if (m_callingListeners)
294         return;
295 
296     Page* page = toPage(exec->dynamicGlobalObject());
297     if (!page)
298         return;
299 
300     m_callingListeners = true;
301 
302     ASSERT(hasListeners());
303 
304     bool isError = errorLine != -1;
305 
306     if (hasGlobalListeners()) {
307         if (isError)
308             dispatchFailedToParseSource(m_listeners, exec, source, errorLine, errorMessage);
309         else
310             dispatchDidParseSource(m_listeners, exec, source);
311     }
312 
313     if (ListenerSet* pageListeners = m_pageListenersMap.get(page)) {
314         ASSERT(!pageListeners->isEmpty());
315         if (isError)
316             dispatchFailedToParseSource(*pageListeners, exec, source, errorLine, errorMessage);
317         else
318             dispatchDidParseSource(*pageListeners, exec, source);
319     }
320 
321     m_callingListeners = false;
322 }
323 
dispatchFunctionToListeners(const ListenerSet & listeners,JavaScriptDebugServer::JavaScriptExecutionCallback callback)324 static void dispatchFunctionToListeners(const ListenerSet& listeners, JavaScriptDebugServer::JavaScriptExecutionCallback callback)
325 {
326     Vector<JavaScriptDebugListener*> copy;
327     copyToVector(listeners, copy);
328     for (size_t i = 0; i < copy.size(); ++i)
329         (copy[i]->*callback)();
330 }
331 
dispatchFunctionToListeners(JavaScriptExecutionCallback callback,Page * page)332 void JavaScriptDebugServer::dispatchFunctionToListeners(JavaScriptExecutionCallback callback, Page* page)
333 {
334     if (m_callingListeners)
335         return;
336 
337     m_callingListeners = true;
338 
339     ASSERT(hasListeners());
340 
341     WebCore::dispatchFunctionToListeners(m_listeners, callback);
342 
343     if (ListenerSet* pageListeners = m_pageListenersMap.get(page)) {
344         ASSERT(!pageListeners->isEmpty());
345         WebCore::dispatchFunctionToListeners(*pageListeners, callback);
346     }
347 
348     m_callingListeners = false;
349 }
350 
setJavaScriptPaused(const PageGroup & pageGroup,bool paused)351 void JavaScriptDebugServer::setJavaScriptPaused(const PageGroup& pageGroup, bool paused)
352 {
353     setMainThreadCallbacksPaused(paused);
354 
355     const HashSet<Page*>& pages = pageGroup.pages();
356 
357     HashSet<Page*>::const_iterator end = pages.end();
358     for (HashSet<Page*>::const_iterator it = pages.begin(); it != end; ++it)
359         setJavaScriptPaused(*it, paused);
360 }
361 
setJavaScriptPaused(Page * page,bool paused)362 void JavaScriptDebugServer::setJavaScriptPaused(Page* page, bool paused)
363 {
364     ASSERT_ARG(page, page);
365 
366     page->setDefersLoading(paused);
367 
368     for (Frame* frame = page->mainFrame(); frame; frame = frame->tree()->traverseNext())
369         setJavaScriptPaused(frame, paused);
370 }
371 
setJavaScriptPaused(Frame * frame,bool paused)372 void JavaScriptDebugServer::setJavaScriptPaused(Frame* frame, bool paused)
373 {
374     ASSERT_ARG(frame, frame);
375 
376     if (!frame->script()->isEnabled())
377         return;
378 
379     frame->script()->setPaused(paused);
380 
381     Document* document = frame->document();
382     if (paused)
383         document->suspendActiveDOMObjects();
384     else
385         document->resumeActiveDOMObjects();
386 
387     setJavaScriptPaused(frame->view(), paused);
388 }
389 
390 #if PLATFORM(MAC)
391 
setJavaScriptPaused(FrameView *,bool)392 void JavaScriptDebugServer::setJavaScriptPaused(FrameView*, bool)
393 {
394 }
395 
396 #else
397 
setJavaScriptPaused(FrameView * view,bool paused)398 void JavaScriptDebugServer::setJavaScriptPaused(FrameView* view, bool paused)
399 {
400     if (!view)
401         return;
402 
403     const HashSet<RefPtr<Widget> >* children = view->children();
404     ASSERT(children);
405 
406     HashSet<RefPtr<Widget> >::const_iterator end = children->end();
407     for (HashSet<RefPtr<Widget> >::const_iterator it = children->begin(); it != end; ++it) {
408         Widget* widget = (*it).get();
409         if (!widget->isPluginView())
410             continue;
411         static_cast<PluginView*>(widget)->setJavaScriptPaused(paused);
412     }
413 }
414 
415 #endif
416 
pauseIfNeeded(Page * page)417 void JavaScriptDebugServer::pauseIfNeeded(Page* page)
418 {
419     if (m_paused)
420         return;
421 
422     if (!page || !hasListenersInterestedInPage(page))
423         return;
424 
425     bool pauseNow = m_pauseOnNextStatement;
426     pauseNow |= (m_pauseOnCallFrame == m_currentCallFrame);
427     pauseNow |= (m_currentCallFrame->line() > 0 && hasBreakpoint(m_currentCallFrame->sourceID(), m_currentCallFrame->line()));
428     if (!pauseNow)
429         return;
430 
431     m_pauseOnCallFrame = 0;
432     m_pauseOnNextStatement = false;
433     m_paused = true;
434 
435     dispatchFunctionToListeners(&JavaScriptDebugListener::didPause, page);
436 
437     setJavaScriptPaused(page->group(), true);
438 
439     TimerBase::fireTimersInNestedEventLoop();
440 
441     EventLoop loop;
442     m_doneProcessingDebuggerEvents = false;
443     while (!m_doneProcessingDebuggerEvents && !loop.ended())
444         loop.cycle();
445 
446     setJavaScriptPaused(page->group(), false);
447 
448     m_paused = false;
449 
450     dispatchFunctionToListeners(&JavaScriptDebugListener::didContinue, page);
451 }
452 
callEvent(const DebuggerCallFrame & debuggerCallFrame,intptr_t sourceID,int lineNumber)453 void JavaScriptDebugServer::callEvent(const DebuggerCallFrame& debuggerCallFrame, intptr_t sourceID, int lineNumber)
454 {
455     if (m_paused)
456         return;
457 
458     m_currentCallFrame = JavaScriptCallFrame::create(debuggerCallFrame, m_currentCallFrame, sourceID, lineNumber);
459     pauseIfNeeded(toPage(debuggerCallFrame.dynamicGlobalObject()));
460 }
461 
atStatement(const DebuggerCallFrame & debuggerCallFrame,intptr_t sourceID,int lineNumber)462 void JavaScriptDebugServer::atStatement(const DebuggerCallFrame& debuggerCallFrame, intptr_t sourceID, int lineNumber)
463 {
464     if (m_paused)
465         return;
466 
467     ASSERT(m_currentCallFrame);
468     if (!m_currentCallFrame)
469         return;
470 
471     m_currentCallFrame->update(debuggerCallFrame, sourceID, lineNumber);
472     pauseIfNeeded(toPage(debuggerCallFrame.dynamicGlobalObject()));
473 }
474 
returnEvent(const DebuggerCallFrame & debuggerCallFrame,intptr_t sourceID,int lineNumber)475 void JavaScriptDebugServer::returnEvent(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     // Treat stepping over a return statement like stepping out.
488     if (m_currentCallFrame == m_pauseOnCallFrame)
489         m_pauseOnCallFrame = m_currentCallFrame->caller();
490     m_currentCallFrame = m_currentCallFrame->caller();
491 }
492 
exception(const DebuggerCallFrame & debuggerCallFrame,intptr_t sourceID,int lineNumber)493 void JavaScriptDebugServer::exception(const DebuggerCallFrame& debuggerCallFrame, intptr_t sourceID, int lineNumber)
494 {
495     if (m_paused)
496         return;
497 
498     ASSERT(m_currentCallFrame);
499     if (!m_currentCallFrame)
500         return;
501 
502     if (m_pauseOnExceptions)
503         m_pauseOnNextStatement = true;
504 
505     m_currentCallFrame->update(debuggerCallFrame, sourceID, lineNumber);
506     pauseIfNeeded(toPage(debuggerCallFrame.dynamicGlobalObject()));
507 }
508 
willExecuteProgram(const DebuggerCallFrame & debuggerCallFrame,intptr_t sourceID,int lineNumber)509 void JavaScriptDebugServer::willExecuteProgram(const DebuggerCallFrame& debuggerCallFrame, intptr_t sourceID, int lineNumber)
510 {
511     if (m_paused)
512         return;
513 
514     m_currentCallFrame = JavaScriptCallFrame::create(debuggerCallFrame, m_currentCallFrame, sourceID, lineNumber);
515     pauseIfNeeded(toPage(debuggerCallFrame.dynamicGlobalObject()));
516 }
517 
didExecuteProgram(const DebuggerCallFrame & debuggerCallFrame,intptr_t sourceID,int lineNumber)518 void JavaScriptDebugServer::didExecuteProgram(const DebuggerCallFrame& debuggerCallFrame, intptr_t sourceID, int lineNumber)
519 {
520     if (m_paused)
521         return;
522 
523     ASSERT(m_currentCallFrame);
524     if (!m_currentCallFrame)
525         return;
526 
527     m_currentCallFrame->update(debuggerCallFrame, sourceID, lineNumber);
528     pauseIfNeeded(toPage(debuggerCallFrame.dynamicGlobalObject()));
529 
530     // Treat stepping over the end of a program like stepping out.
531     if (m_currentCallFrame == m_pauseOnCallFrame)
532         m_pauseOnCallFrame = m_currentCallFrame->caller();
533     m_currentCallFrame = m_currentCallFrame->caller();
534 }
535 
didReachBreakpoint(const DebuggerCallFrame & debuggerCallFrame,intptr_t sourceID,int lineNumber)536 void JavaScriptDebugServer::didReachBreakpoint(const DebuggerCallFrame& debuggerCallFrame, intptr_t sourceID, int lineNumber)
537 {
538     if (m_paused)
539         return;
540 
541     ASSERT(m_currentCallFrame);
542     if (!m_currentCallFrame)
543         return;
544 
545     m_pauseOnNextStatement = true;
546     m_currentCallFrame->update(debuggerCallFrame, sourceID, lineNumber);
547     pauseIfNeeded(toPage(debuggerCallFrame.dynamicGlobalObject()));
548 }
549 
recompileAllJSFunctionsSoon()550 void JavaScriptDebugServer::recompileAllJSFunctionsSoon()
551 {
552     m_recompileTimer.startOneShot(0);
553 }
554 
recompileAllJSFunctions(Timer<JavaScriptDebugServer> *)555 void JavaScriptDebugServer::recompileAllJSFunctions(Timer<JavaScriptDebugServer>*)
556 {
557     JSLock lock(SilenceAssertionsOnly);
558     JSGlobalData* globalData = JSDOMWindow::commonJSGlobalData();
559 
560     // If JavaScript is running, it's not safe to recompile, since we'll end
561     // up throwing away code that is live on the stack.
562     ASSERT(!globalData->dynamicGlobalObject);
563     if (globalData->dynamicGlobalObject)
564         return;
565 
566     Vector<ProtectedPtr<JSFunction> > functions;
567     Heap::iterator heapEnd = globalData->heap.primaryHeapEnd();
568     for (Heap::iterator it = globalData->heap.primaryHeapBegin(); it != heapEnd; ++it) {
569         if ((*it)->isObject(&JSFunction::info)) {
570             JSFunction* function = static_cast<JSFunction*>(*it);
571             if (!function->isHostFunction())
572                 functions.append(function);
573         }
574     }
575 
576     typedef HashMap<RefPtr<FunctionBodyNode>, RefPtr<FunctionBodyNode> > FunctionBodyMap;
577     typedef HashMap<SourceProvider*, ExecState*> SourceProviderMap;
578 
579     FunctionBodyMap functionBodies;
580     SourceProviderMap sourceProviders;
581 
582     size_t size = functions.size();
583     for (size_t i = 0; i < size; ++i) {
584         JSFunction* function = functions[i];
585 
586         FunctionBodyNode* oldBody = function->body();
587         pair<FunctionBodyMap::iterator, bool> result = functionBodies.add(oldBody, 0);
588         if (!result.second) {
589             function->setBody(result.first->second.get());
590             continue;
591         }
592 
593         ExecState* exec = function->scope().globalObject()->JSGlobalObject::globalExec();
594         const SourceCode& sourceCode = oldBody->source();
595 
596         RefPtr<FunctionBodyNode> newBody = globalData->parser->parse<FunctionBodyNode>(exec, 0, sourceCode);
597         ASSERT(newBody);
598         newBody->finishParsing(oldBody->copyParameters(), oldBody->parameterCount());
599 
600         result.first->second = newBody;
601         function->setBody(newBody.release());
602 
603         if (hasListeners() && function->scope().globalObject()->debugger() == this)
604             sourceProviders.add(sourceCode.provider(), exec);
605     }
606 
607     // Call sourceParsed() after reparsing all functions because it will execute
608     // JavaScript in the inspector.
609     SourceProviderMap::const_iterator end = sourceProviders.end();
610     for (SourceProviderMap::const_iterator iter = sourceProviders.begin(); iter != end; ++iter)
611         sourceParsed((*iter).second, SourceCode((*iter).first), -1, 0);
612 }
613 
didAddListener(Page * page)614 void JavaScriptDebugServer::didAddListener(Page* page)
615 {
616     recompileAllJSFunctionsSoon();
617 
618     if (page)
619         page->setDebugger(this);
620     else
621         Page::setDebuggerForAllPages(this);
622 }
623 
didRemoveListener(Page * page)624 void JavaScriptDebugServer::didRemoveListener(Page* page)
625 {
626     if (hasGlobalListeners() || (page && hasListenersInterestedInPage(page)))
627         return;
628 
629     recompileAllJSFunctionsSoon();
630 
631     if (page)
632         page->setDebugger(0);
633     else
634         Page::setDebuggerForAllPages(0);
635 }
636 
didRemoveLastListener()637 void JavaScriptDebugServer::didRemoveLastListener()
638 {
639     m_doneProcessingDebuggerEvents = true;
640 }
641 
642 } // namespace WebCore
643 
644 #endif // ENABLE(JAVASCRIPT_DEBUGGER)
645