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