Implements more debugger APIs on JavaScriptDebugServer and reduces
authortimothy@apple.com <timothy@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 13 May 2008 22:29:19 +0000 (22:29 +0000)
committertimothy@apple.com <timothy@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 13 May 2008 22:29:19 +0000 (22:29 +0000)
the number of callbacks to JavaScriptDebugListeners. These changes
will better facilitate debugger optimizations when SquirrelFish merges.

Reviewed by Kevin McCullough.

* page/InspectorController.cpp:
(WebCore::InspectorController::didParseSource): Removed the ExecState.
(WebCore::InspectorController::failedToParseSource): Ditto.
(WebCore::InspectorController::didPause): Added.
* page/InspectorController.h: Changed the JavaScriptDebugListener functions.
* page/JavaScriptDebugListener.h: Removed some callbacks and added didPause.
* page/JavaScriptDebugServer.cpp:
(WebCore::JavaScriptDebugServer::JavaScriptDebugServer): Initialize new data members.
(WebCore::JavaScriptDebugServer::~JavaScriptDebugServer): Delete all values of m_breakpoints.
(WebCore::JavaScriptDebugServer::removeListener): Call resume if the last listener was removed.
(WebCore::JavaScriptDebugServer::hasListenersInterestedInPage): Returns true if there are any
global listeners or a listener for the page.
(WebCore::JavaScriptDebugServer::addBreakpoint): Adds and entry to m_breakpoints.
(WebCore::JavaScriptDebugServer::removeBreakpoint): Removes a entry in m_breakpoints.
(WebCore::JavaScriptDebugServer::hasBreakpoint):  Checks if there is a breakpoint for the
sourceID and line.
(WebCore::JavaScriptDebugServer::clearBreakpoints): Removed all breakpoints.
(WebCore::JavaScriptDebugServer::pauseOnNextStatement): Sets m_pauseOnNextStatement to true.
(WebCore::JavaScriptDebugServer::resume): Sets m_paused to false.
(WebCore::JavaScriptDebugServer::stepIntoStatement): Calls resume and sets
m_pauseOnNextStatement to true.
(WebCore::JavaScriptDebugServer::stepOverStatement): Calls resume and sets m_pauseOnExecState to
the current call frame's ExecState.
(WebCore::JavaScriptDebugServer::stepOutOfFunction): Calls resume and sets m_pauseOnExecState to
the current call frame's caller ExecState.
(WebCore::JavaScriptDebugServer::currentCallFrame): Returns m_currentCallFrame if paused.
(WebCore::dispatchDidParseSource): Removed the ExecState argument.
(WebCore::dispatchFailedToParseSource): Ditto.
(WebCore::JavaScriptDebugServer::sourceParsed): Doesn't pass the ExecState to dispatchDidParseSource
or dispatchFailedToParseSource.
(WebCore::dispatchFunctionToListeners):
(WebCore::JavaScriptDebugServer::dispatchFunctionToListeners): Removes all the arguments passed
to the callback.
(WebCore::JavaScriptDebugServer::setJavaScriptPaused): Various overloaded functions
to pause parts of WebCore to prevent JavaScript execution while paused.
(WebCore::JavaScriptDebugServer::pauseIfNeeded): Decides if the debugger should pause based
on the passed in ExecState, source ID and line number. This checks for breakpoints, stepping, etc.
Calls didPause on all the listeners and spins a EventLoop until resume is called.
(WebCore::JavaScriptDebugServer::callEvent): Call pauseIfNeeded.
(WebCore::JavaScriptDebugServer::atStatement): Ditto.
(WebCore::JavaScriptDebugServer::returnEvent): Ditto.
(WebCore::JavaScriptDebugServer::exception): Ditto.
* page/JavaScriptDebugServer.h: Added new functions.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@33404 268f45cc-cd09-0410-ab3c-d52691b4dbfc

JavaScriptCore/JavaScriptCore.exp
WebCore/ChangeLog
WebCore/page/InspectorController.cpp
WebCore/page/InspectorController.h
WebCore/page/JavaScriptDebugListener.h
WebCore/page/JavaScriptDebugServer.cpp
WebCore/page/JavaScriptDebugServer.h

index 7ac3f9f..6037574 100644 (file)
@@ -221,6 +221,7 @@ __ZN3WTF16callOnMainThreadEPFvPvES0_
 __ZN3WTF16fastZeroedMallocEm
 __ZN3WTF19initializeThreadingEv
 __ZN3WTF23waitForThreadCompletionEjPPv
+__ZN3WTF28setMainThreadCallbacksPausedEb
 __ZN3WTF32atomicallyInitializedStaticMutexE
 __ZN3WTF5Mutex4lockEv
 __ZN3WTF5Mutex6unlockEv
index c125b78..7404193 100644 (file)
@@ -1,5 +1,57 @@
 2008-05-13  Timothy Hatcher  <timothy@apple.com>
 
+        Implements more debugger APIs on JavaScriptDebugServer and reduces
+        the number of callbacks to JavaScriptDebugListeners. These changes
+        will better facilitate debugger optimizations when SquirrelFish merges.
+
+        Reviewed by Kevin McCullough.
+
+        * page/InspectorController.cpp:
+        (WebCore::InspectorController::didParseSource): Removed the ExecState.
+        (WebCore::InspectorController::failedToParseSource): Ditto.
+        (WebCore::InspectorController::didPause): Added.
+        * page/InspectorController.h: Changed the JavaScriptDebugListener functions.
+        * page/JavaScriptDebugListener.h: Removed some callbacks and added didPause.
+        * page/JavaScriptDebugServer.cpp:
+        (WebCore::JavaScriptDebugServer::JavaScriptDebugServer): Initialize new data members.
+        (WebCore::JavaScriptDebugServer::~JavaScriptDebugServer): Delete all values of m_breakpoints.
+        (WebCore::JavaScriptDebugServer::removeListener): Call resume if the last listener was removed.
+        (WebCore::JavaScriptDebugServer::hasListenersInterestedInPage): Returns true if there are any
+        global listeners or a listener for the page.
+        (WebCore::JavaScriptDebugServer::addBreakpoint): Adds and entry to m_breakpoints.
+        (WebCore::JavaScriptDebugServer::removeBreakpoint): Removes a entry in m_breakpoints.
+        (WebCore::JavaScriptDebugServer::hasBreakpoint):  Checks if there is a breakpoint for the
+        sourceID and line.
+        (WebCore::JavaScriptDebugServer::clearBreakpoints): Removed all breakpoints.
+        (WebCore::JavaScriptDebugServer::pauseOnNextStatement): Sets m_pauseOnNextStatement to true.
+        (WebCore::JavaScriptDebugServer::resume): Sets m_paused to false.
+        (WebCore::JavaScriptDebugServer::stepIntoStatement): Calls resume and sets
+        m_pauseOnNextStatement to true.
+        (WebCore::JavaScriptDebugServer::stepOverStatement): Calls resume and sets m_pauseOnExecState to
+        the current call frame's ExecState.
+        (WebCore::JavaScriptDebugServer::stepOutOfFunction): Calls resume and sets m_pauseOnExecState to
+        the current call frame's caller ExecState.
+        (WebCore::JavaScriptDebugServer::currentCallFrame): Returns m_currentCallFrame if paused.
+        (WebCore::dispatchDidParseSource): Removed the ExecState argument.
+        (WebCore::dispatchFailedToParseSource): Ditto.
+        (WebCore::JavaScriptDebugServer::sourceParsed): Doesn't pass the ExecState to dispatchDidParseSource
+        or dispatchFailedToParseSource.
+        (WebCore::dispatchFunctionToListeners):
+        (WebCore::JavaScriptDebugServer::dispatchFunctionToListeners): Removes all the arguments passed
+        to the callback.
+        (WebCore::JavaScriptDebugServer::setJavaScriptPaused): Various overloaded functions
+        to pause parts of WebCore to prevent JavaScript execution while paused.
+        (WebCore::JavaScriptDebugServer::pauseIfNeeded): Decides if the debugger should pause based
+        on the passed in ExecState, source ID and line number. This checks for breakpoints, stepping, etc.
+        Calls didPause on all the listeners and spins a EventLoop until resume is called.
+        (WebCore::JavaScriptDebugServer::callEvent): Call pauseIfNeeded.
+        (WebCore::JavaScriptDebugServer::atStatement): Ditto.
+        (WebCore::JavaScriptDebugServer::returnEvent): Ditto.
+        (WebCore::JavaScriptDebugServer::exception): Ditto.
+        * page/JavaScriptDebugServer.h: Added new functions.
+
+2008-05-13  Timothy Hatcher  <timothy@apple.com>
+
         Change View.show to call detach if the parentNode dosen't
         match before appending to the passed in parent element.
         This also prevents appending to the same parent element
index a7239e2..3640ec3 100644 (file)
@@ -2002,27 +2002,15 @@ bool InspectorController::handleException(JSContextRef context, JSValueRef excep
 #pragma mark -
 #pragma mark JavaScriptDebugListener functions
 
-void InspectorController::didParseSource(ExecState*, const UString& /*source*/, int /*startingLineNumber*/, const UString& /*sourceURL*/, int /*sourceID*/)
+void InspectorController::didParseSource(const UString& /*source*/, int /*startingLineNumber*/, const UString& /*sourceURL*/, int /*sourceID*/)
 {
 }
 
-void InspectorController::failedToParseSource(ExecState*, const UString& /*source*/, int /*startingLineNumber*/, const UString& /*sourceURL*/, int /*errorLine*/, const UString& /*errorMessage*/)
+void InspectorController::failedToParseSource(const UString& /*source*/, int /*startingLineNumber*/, const UString& /*sourceURL*/, int /*errorLine*/, const UString& /*errorMessage*/)
 {
 }
 
-void InspectorController::didEnterCallFrame(ExecState*, int /*sourceID*/, int /*lineNumber*/)
-{
-}
-
-void InspectorController::willExecuteStatement(ExecState*, int /*sourceID*/, int /*lineNumber*/)
-{
-}
-
-void InspectorController::willLeaveCallFrame(ExecState*, int /*sourceID*/, int /*lineNumber*/)
-{
-}
-
-void InspectorController::exceptionWasRaised(ExecState*, int /*sourceID*/, int /*lineNumber*/)
+void InspectorController::didPause()
 {
 }
 
index be1f3de..c19cc95 100644 (file)
@@ -171,12 +171,9 @@ private:
     void showWindow();
     void closeWindow();
 
-    virtual void didParseSource(KJS::ExecState*, const KJS::UString& source, int startingLineNumber, const KJS::UString& sourceURL, int sourceID);
-    virtual void failedToParseSource(KJS::ExecState*, const KJS::UString& source, int startingLineNumber, const KJS::UString& sourceURL, int errorLine, const KJS::UString& errorMessage);
-    virtual void didEnterCallFrame(KJS::ExecState*, int sourceID, int lineNumber);
-    virtual void willExecuteStatement(KJS::ExecState*, int sourceID, int lineNumber);
-    virtual void willLeaveCallFrame(KJS::ExecState*, int sourceID, int lineNumber);
-    virtual void exceptionWasRaised(KJS::ExecState*, int sourceID, int lineNumber);
+    virtual void didParseSource(const KJS::UString& source, int startingLineNumber, const KJS::UString& sourceURL, int sourceID);
+    virtual void failedToParseSource(const KJS::UString& source, int startingLineNumber, const KJS::UString& sourceURL, int errorLine, const KJS::UString& errorMessage);
+    virtual void didPause();
 
     Page* m_inspectedPage;
     InspectorClient* m_client;
index 1b801cc..167c951 100644 (file)
@@ -43,12 +43,9 @@ namespace WebCore {
     public:
         virtual ~JavaScriptDebugListener() { }
 
-        virtual void didParseSource(KJS::ExecState*, const KJS::UString& source, int startingLineNumber, const KJS::UString& sourceURL, int sourceID) = 0;
-        virtual void failedToParseSource(KJS::ExecState*, const KJS::UString& source, int startingLineNumber, const KJS::UString& sourceURL, int errorLine, const KJS::UString& errorMessage) = 0;
-        virtual void didEnterCallFrame(KJS::ExecState*, int sourceID, int lineNumber) = 0;
-        virtual void willExecuteStatement(KJS::ExecState*, int sourceID, int lineNumber) = 0;
-        virtual void willLeaveCallFrame(KJS::ExecState*, int sourceID, int lineNumber) = 0;
-        virtual void exceptionWasRaised(KJS::ExecState*, int sourceID, int lineNumber) = 0;
+        virtual void didParseSource(const KJS::UString& source, int startingLineNumber, const KJS::UString& sourceURL, int sourceID) = 0;
+        virtual void failedToParseSource(const KJS::UString& source, int startingLineNumber, const KJS::UString& sourceURL, int errorLine, const KJS::UString& errorMessage) = 0;
+        virtual void didPause() = 0;
     };
 
 } // namespace WebCore
index beb387c..4d6d01d 100644 (file)
 #include "JavaScriptDebugServer.h"
 
 #include "DOMWindow.h"
+#include "EventLoop.h"
 #include "Frame.h"
+#include "FrameTree.h"
+#include "FrameView.h"
 #include "JSDOMWindow.h"
+#include "JavaScriptCallFrame.h"
 #include "JavaScriptDebugListener.h"
+#include "kjs_proxy.h"
 #include "Page.h"
+#include "PageGroup.h"
+#include "PluginView.h"
+#include "ScrollView.h"
+#include "Widget.h"
+#include <wtf/MainThread.h>
 
 using namespace KJS;
 
@@ -49,12 +59,16 @@ JavaScriptDebugServer& JavaScriptDebugServer::shared()
 
 JavaScriptDebugServer::JavaScriptDebugServer()
     : m_callingListeners(false)
+    , m_pauseOnNextStatement(false)
+    , m_paused(false)
+    , m_pauseOnExecState(0)
 {
 }
 
 JavaScriptDebugServer::~JavaScriptDebugServer()
 {
     deleteAllValues(m_pageListenersMap);
+    deleteAllValues(m_breakpoints);
 }
 
 void JavaScriptDebugServer::addListener(JavaScriptDebugListener* listener)
@@ -68,8 +82,10 @@ void JavaScriptDebugServer::addListener(JavaScriptDebugListener* listener)
 void JavaScriptDebugServer::removeListener(JavaScriptDebugListener* listener)
 {
     m_listeners.remove(listener);
-    if (!hasListeners())
+    if (!hasListeners()) {
         Page::setDebuggerForAllPages(0);
+        resume();
+    }
 }
 
 void JavaScriptDebugServer::addListener(JavaScriptDebugListener* listener, Page* page)
@@ -104,8 +120,10 @@ void JavaScriptDebugServer::removeListener(JavaScriptDebugListener* listener, Pa
         delete listeners;
     }
 
-    if (!hasListeners())
+    if (!hasListeners()) {
         Page::setDebuggerForAllPages(0);
+        resume();
+    }
 }
 
 void JavaScriptDebugServer::pageCreated(Page* page)
@@ -116,20 +134,123 @@ void JavaScriptDebugServer::pageCreated(Page* page)
     page->setDebugger(this);
 }
 
-static void dispatchDidParseSource(const ListenerSet& listeners, ExecState* exec, const UString& source, int startingLineNumber, const UString& sourceURL, int sourceID)
+bool JavaScriptDebugServer::hasListenersInterestedInPage(Page* page)
+{
+    ASSERT_ARG(page, page);
+
+    if (!m_listeners.isEmpty())
+        return true;
+
+    return m_pageListenersMap.contains(page);
+}
+
+void JavaScriptDebugServer::addBreakpoint(int sourceID, unsigned lineNumber)
+{
+    HashSet<unsigned>* lines = m_breakpoints.get(sourceID);
+    if (!lines) {
+        lines = new HashSet<unsigned>;
+        m_breakpoints.set(sourceID, lines);
+    }
+
+    lines->add(lineNumber);
+}
+
+void JavaScriptDebugServer::removeBreakpoint(int sourceID, unsigned lineNumber)
+{
+    HashSet<unsigned>* lines = m_breakpoints.get(sourceID);
+    if (!lines)
+        return;
+
+    lines->remove(lineNumber);
+
+    if (!lines->isEmpty())
+        return;
+
+    m_breakpoints.remove(sourceID);
+    delete lines;
+}
+
+bool JavaScriptDebugServer::hasBreakpoint(int sourceID, unsigned lineNumber) const
+{
+    HashSet<unsigned>* lines = m_breakpoints.get(sourceID);
+    if (!lines)
+        return false;
+    return lines->contains(lineNumber);
+}
+
+void JavaScriptDebugServer::clearBreakpoints()
+{
+    deleteAllValues(m_breakpoints);
+    m_breakpoints.clear();
+}
+
+void JavaScriptDebugServer::pauseOnNextStatement()
+{
+    m_pauseOnNextStatement = true;
+}
+
+void JavaScriptDebugServer::resume()
+{
+    m_paused = false;
+}
+
+void JavaScriptDebugServer::stepIntoStatement()
+{
+    if (!m_paused)
+        return;
+
+    resume();
+
+    m_pauseOnNextStatement = true;
+}
+
+void JavaScriptDebugServer::stepOverStatement()
+{
+    if (!m_paused)
+        return;
+
+    resume();
+
+    if (m_currentCallFrame)
+        m_pauseOnExecState = m_currentCallFrame->execState();
+    else
+        m_pauseOnExecState = 0;
+}
+
+void JavaScriptDebugServer::stepOutOfFunction()
+{
+    if (!m_paused)
+        return;
+
+    resume();
+
+    if (m_currentCallFrame && m_currentCallFrame->caller())
+        m_pauseOnExecState = m_currentCallFrame->caller()->execState();
+    else
+        m_pauseOnExecState = 0;
+}
+
+JavaScriptCallFrame* JavaScriptDebugServer::currentCallFrame()
+{
+    if (!m_paused)
+        return 0;
+    return m_currentCallFrame.get();
+}
+
+static void dispatchDidParseSource(const ListenerSet& listeners, const UString& source, int startingLineNumber, const UString& sourceURL, int sourceID)
 {
     Vector<JavaScriptDebugListener*> copy;
     copyToVector(listeners, copy);
     for (size_t i = 0; i < copy.size(); ++i)
-        copy[i]->didParseSource(exec, source, startingLineNumber, sourceURL, sourceID);
+        copy[i]->didParseSource(source, startingLineNumber, sourceURL, sourceID);
 }
 
-static void dispatchFailedToParseSource(const ListenerSet& listeners, ExecState* exec, const UString& source, int startingLineNumber, const UString& sourceURL, int errorLine, const UString& errorMessage)
+static void dispatchFailedToParseSource(const ListenerSet& listeners, const UString& source, int startingLineNumber, const UString& sourceURL, int errorLine, const UString& errorMessage)
 {
     Vector<JavaScriptDebugListener*> copy;
     copyToVector(listeners, copy);
     for (size_t i = 0; i < copy.size(); ++i)
-        copy[i]->failedToParseSource(exec, source, startingLineNumber, sourceURL, errorLine, errorMessage);
+        copy[i]->failedToParseSource(source, startingLineNumber, sourceURL, errorLine, errorMessage);
 }
 
 static Page* toPage(ExecState* exec)
@@ -159,32 +280,32 @@ bool JavaScriptDebugServer::sourceParsed(ExecState* exec, int sourceID, const US
 
     if (!m_listeners.isEmpty()) {
         if (isError)
-            dispatchFailedToParseSource(m_listeners, exec, source, startingLineNumber, sourceURL, errorLine, errorMessage);
+            dispatchFailedToParseSource(m_listeners, source, startingLineNumber, sourceURL, errorLine, errorMessage);
         else
-            dispatchDidParseSource(m_listeners, exec, source, startingLineNumber, sourceURL, sourceID);
+            dispatchDidParseSource(m_listeners, source, startingLineNumber, sourceURL, sourceID);
     }
 
     if (ListenerSet* pageListeners = m_pageListenersMap.get(page)) {
         ASSERT(!pageListeners->isEmpty());
         if (isError)
-            dispatchFailedToParseSource(*pageListeners, exec, source, startingLineNumber, sourceURL, errorLine, errorMessage);
+            dispatchFailedToParseSource(*pageListeners, source, startingLineNumber, sourceURL, errorLine, errorMessage);
         else
-            dispatchDidParseSource(*pageListeners, exec, source, startingLineNumber, sourceURL, sourceID);
+            dispatchDidParseSource(*pageListeners, source, startingLineNumber, sourceURL, sourceID);
     }
 
     m_callingListeners = false;
     return true;
 }
 
-static void dispatchFunctionToListeners(const ListenerSet& listeners, JavaScriptDebugServer::JavaScriptExecutionCallback callback, ExecState* exec, int sourceID, int lineNumber)
+static void dispatchFunctionToListeners(const ListenerSet& listeners, JavaScriptDebugServer::JavaScriptExecutionCallback callback)
 {
     Vector<JavaScriptDebugListener*> copy;
     copyToVector(listeners, copy);
     for (size_t i = 0; i < copy.size(); ++i)
-        (copy[i]->*callback)(exec, sourceID, lineNumber);
+        (copy[i]->*callback)();
 }
 
-void JavaScriptDebugServer::dispatchFunctionToListeners(JavaScriptExecutionCallback callback, ExecState* exec, int sourceID, int lineNumber)
+void JavaScriptDebugServer::dispatchFunctionToListeners(JavaScriptExecutionCallback callback, ExecState* exec)
 {
     if (m_callingListeners)
         return;
@@ -197,36 +318,156 @@ void JavaScriptDebugServer::dispatchFunctionToListeners(JavaScriptExecutionCallb
 
     ASSERT(hasListeners());
 
-    WebCore::dispatchFunctionToListeners(m_listeners, callback, exec, sourceID, lineNumber);
+    WebCore::dispatchFunctionToListeners(m_listeners, callback);
     if (ListenerSet* pageListeners = m_pageListenersMap.get(page)) {
         ASSERT(!pageListeners->isEmpty());
-        WebCore::dispatchFunctionToListeners(*pageListeners, callback, exec, sourceID, lineNumber);
+        WebCore::dispatchFunctionToListeners(*pageListeners, callback);
     }
 
     m_callingListeners = false;
 }
 
+void JavaScriptDebugServer::setJavaScriptPaused(const PageGroup& pageGroup, bool paused)
+{
+    setMainThreadCallbacksPaused(paused);
+
+    const HashSet<Page*>& pages = pageGroup.pages();
+
+    HashSet<Page*>::const_iterator end = pages.end();
+    for (HashSet<Page*>::const_iterator it = pages.begin(); it != end; ++it)
+        setJavaScriptPaused(*it, false);
+}
+
+void JavaScriptDebugServer::setJavaScriptPaused(Page* page, bool paused)
+{
+    ASSERT_ARG(page, page);
+
+    page->setDefersLoading(paused);
+
+    for (Frame* frame = page->mainFrame(); frame; frame = frame->tree()->traverseNext())
+        setJavaScriptPaused(frame, paused);
+}
+
+void JavaScriptDebugServer::setJavaScriptPaused(Frame* frame, bool paused)
+{
+    ASSERT_ARG(frame, frame);
+
+    if (!frame->scriptProxy()->isEnabled())
+        return;
+
+    frame->scriptProxy()->setPaused(paused);
+
+    if (JSDOMWindow* window = toJSDOMWindow(frame)) {
+        if (paused)
+            m_pausedTimeouts.set(frame, window->pauseTimeouts());
+        else
+            window->resumeTimeouts(m_pausedTimeouts.take(frame));
+    }
+
+    setJavaScriptPaused(frame->view(), paused);
+}
+
+void JavaScriptDebugServer::setJavaScriptPaused(FrameView* view, bool paused)
+{
+#if !PLATFORM(MAC)
+    if (!view)
+        return;
+
+    HashSet<Widget*>* children = static_cast<ScrollView*>(view)->children();
+    ASSERT(children);
+
+    HashSet<Widget*>::iterator end = children->end();
+    for (HashSet<Widget*>::iterator it = children->begin(); it != end; ++it) {
+        Widget* widget = *it;
+        if (!widget->isPluginView())
+            continue;
+        static_cast<PluginView*>(widget)->setJavaScriptPaused(paused);
+    }
+#endif
+}
+
+void JavaScriptDebugServer::pauseIfNeeded(ExecState* exec, int sourceID, int lineNumber)
+{
+    if (m_paused)
+        return;
+
+    Page* page = toPage(exec);
+    if (!page || !hasListenersInterestedInPage(page))
+        return;
+
+    bool pauseNow = m_pauseOnNextStatement;
+    if (!pauseNow && m_pauseOnExecState)
+        pauseNow = (m_pauseOnExecState == exec);
+    if (!pauseNow && lineNumber > 0)
+        pauseNow = hasBreakpoint(sourceID, lineNumber);
+    if (!pauseNow)
+        return;
+
+    m_pauseOnExecState = 0;
+    m_pauseOnNextStatement = false;
+    m_paused = true;
+
+    dispatchFunctionToListeners(&JavaScriptDebugListener::didPause, exec);
+
+    setJavaScriptPaused(page->group(), true);
+
+    EventLoop loop;
+    while (m_paused && !loop.ended())
+        loop.cycle();
+
+    setJavaScriptPaused(page->group(), false);
+
+    m_paused = false;
+}
+
 bool JavaScriptDebugServer::callEvent(ExecState* exec, int sourceID, int lineNumber, JSObject*, const List&)
 {
-    dispatchFunctionToListeners(&JavaScriptDebugListener::didEnterCallFrame, exec, sourceID, lineNumber);
+    if (m_paused)
+        return true;
+
+    if (m_currentCallFrame && m_currentCallFrame->execState() != exec->callingExecState()) {
+        m_currentCallFrame->invalidate();
+        m_currentCallFrame = 0;
+    }
+
+    m_currentCallFrame = JavaScriptCallFrame::create(exec, m_currentCallFrame, sourceID, lineNumber);
+    pauseIfNeeded(exec, sourceID, lineNumber);
     return true;
 }
 
 bool JavaScriptDebugServer::atStatement(ExecState* exec, int sourceID, int firstLine, int)
 {
-    dispatchFunctionToListeners(&JavaScriptDebugListener::willExecuteStatement, exec, sourceID, firstLine);
+    if (m_paused)
+        return true;
+    if (m_currentCallFrame)
+        m_currentCallFrame->setLine(firstLine);
+    else
+        m_currentCallFrame = JavaScriptCallFrame::create(exec, 0, sourceID, firstLine);
+    pauseIfNeeded(exec, sourceID, firstLine);
     return true;
 }
 
 bool JavaScriptDebugServer::returnEvent(ExecState* exec, int sourceID, int lineNumber, JSObject*)
 {
-    dispatchFunctionToListeners(&JavaScriptDebugListener::willLeaveCallFrame, exec, sourceID, lineNumber);
+    if (m_paused)
+        return true;
+    m_currentCallFrame->invalidate();
+    m_currentCallFrame = m_currentCallFrame->caller();
+    pauseIfNeeded(exec, sourceID, lineNumber);
     return true;
 }
 
 bool JavaScriptDebugServer::exception(ExecState* exec, int sourceID, int lineNumber, JSValue*)
 {
-    dispatchFunctionToListeners(&JavaScriptDebugListener::exceptionWasRaised, exec, sourceID, lineNumber);
+    if (m_paused)
+        return true;
+    if (m_currentCallFrame)
+        m_currentCallFrame->setLine(lineNumber);
+    else
+        m_currentCallFrame = JavaScriptCallFrame::create(exec, 0, sourceID, lineNumber);
+    // FIXME: ideally this should only pause if a "pause on exception" flag is set,
+    // not m_pauseOnNextStatement, etc.
+    pauseIfNeeded(exec, sourceID, lineNumber);
     return true;
 }
 
index 2b57f26..edeb2e3 100644 (file)
 
 #include <wtf/HashMap.h>
 #include <wtf/HashSet.h>
+#include <wtf/RefPtr.h>
 
 namespace WebCore {
 
     class Frame;
+    class FrameView;
     class Page;
+    class PageGroup;
+    class PausedTimeouts;
+    class JavaScriptCallFrame;
     class JavaScriptDebugListener;
 
     class JavaScriptDebugServer : KJS::Debugger {
@@ -50,18 +55,39 @@ namespace WebCore {
         void addListener(JavaScriptDebugListener*, Page*);
         void removeListener(JavaScriptDebugListener*, Page*);
 
+        void addBreakpoint(int sourceID, unsigned lineNumber);
+        void removeBreakpoint(int sourceID, unsigned lineNumber);
+        bool hasBreakpoint(int sourceID, unsigned lineNumber) const;
+        void clearBreakpoints();
+
+        void pauseOnNextStatement();
+        void resume();
+
+        void stepIntoStatement();
+        void stepOverStatement();
+        void stepOutOfFunction();
+
+        JavaScriptCallFrame* currentCallFrame();
+
         void pageCreated(Page*);
 
         typedef HashSet<JavaScriptDebugListener*> ListenerSet;
-        typedef void (JavaScriptDebugListener::*JavaScriptExecutionCallback)(KJS::ExecState*, int sourceID, int lineNumber);
+        typedef void (JavaScriptDebugListener::*JavaScriptExecutionCallback)();
 
     private:
         JavaScriptDebugServer();
         ~JavaScriptDebugServer();
 
         bool hasListeners() const { return !m_listeners.isEmpty() || !m_pageListenersMap.isEmpty(); }
+        bool hasListenersInterestedInPage(Page*);
+
+        void setJavaScriptPaused(const PageGroup&, bool paused);
+        void setJavaScriptPaused(Page*, bool paused);
+        void setJavaScriptPaused(Frame*, bool paused);
+        void setJavaScriptPaused(FrameView*, bool paused);
 
-        void dispatchFunctionToListeners(JavaScriptExecutionCallback, KJS::ExecState*, int sourceID, int lineNumber);
+        void dispatchFunctionToListeners(JavaScriptExecutionCallback, KJS::ExecState*);
+        void pauseIfNeeded(KJS::ExecState* exec, int sourceID, int lineNumber);
 
         virtual bool sourceParsed(KJS::ExecState*, int sourceID, const KJS::UString& sourceURL, const KJS::UString& source, int startingLineNumber, int errorLine, const KJS::UString& errorMsg);
         virtual bool callEvent(KJS::ExecState*, int sourceID, int lineNumber, KJS::JSObject* function, const KJS::List& args);
@@ -73,6 +99,12 @@ namespace WebCore {
         PageListenersMap m_pageListenersMap;
         ListenerSet m_listeners;
         bool m_callingListeners;
+        bool m_pauseOnNextStatement;
+        bool m_paused;
+        KJS::ExecState* m_pauseOnExecState;
+        RefPtr<JavaScriptCallFrame> m_currentCallFrame;
+        HashMap<RefPtr<Frame>, PausedTimeouts*> m_pausedTimeouts;
+        HashMap<int, HashSet<unsigned>*> m_breakpoints;
     };
 
 } // namespace WebCore