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