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