+ 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;
+}
+
+static inline void updateCurrentCallFrame(RefPtr<JavaScriptCallFrame>& currentCallFrame, ExecState* exec, int sourceID, int lineNumber, ExecState*& pauseExecState)
+{
+#ifdef DEBUG_DEBUGGER_CALLBACKS
+ const char* action = 0;
+#endif
+
+ if (currentCallFrame) {
+ if (currentCallFrame->execState() == exec) {
+ // Same call frame, just update the current line.
+ currentCallFrame->setLine(lineNumber);
+#ifdef DEBUG_DEBUGGER_CALLBACKS
+ action = " same";
+#endif
+ } else if (currentCallFrame->execState() == exec->callingExecState()) {
+ // Create a new call frame, and make the caller the previous call frame.
+ currentCallFrame = JavaScriptCallFrame::create(exec, currentCallFrame, sourceID, lineNumber);
+#ifdef DEBUG_DEBUGGER_CALLBACKS
+ action = " call";
+ ++s_callDepth;
+#endif
+ } else {
+#ifdef DEBUG_DEBUGGER_CALLBACKS
+ action = "return";
+#endif
+ // The current call frame isn't the same and it isn't the caller of a new call frame,
+ // so it might be a previous call frame (returning from a function). Or it is a stale call
+ // frame from the previous execution of global code. Walk up the caller chain until we find
+ // the current exec state. If the current exec state is found, the current call frame will be
+ // set to null (and a new one will be created below.)
+ while (currentCallFrame && currentCallFrame->execState() != exec) {
+ if (currentCallFrame->execState() == pauseExecState) {
+ // The current call frame matches the pause exec state (used for step over.)
+ // Since we are returning up the call stack, update the pause exec state to match.
+ // This makes stepping over a return statement act like a step out.
+ if (currentCallFrame->caller())
+ pauseExecState = currentCallFrame->caller()->execState();
+ else
+ pauseExecState = 0;
+ }
+
+ // Invalidate the call frame since it's ExecState is stale now.
+ currentCallFrame->invalidate();
+ currentCallFrame = currentCallFrame->caller();
+
+#ifdef DEBUG_DEBUGGER_CALLBACKS
+ if (s_callDepth)
+ --s_callDepth;
+#endif
+ }
+
+ if (currentCallFrame)
+ currentCallFrame->setLine(lineNumber);
+ }
+ }
+
+ if (!currentCallFrame) {
+ // Create a new call frame with no caller, this is likely global code.
+ currentCallFrame = JavaScriptCallFrame::create(exec, 0, sourceID, lineNumber);
+#ifdef DEBUG_DEBUGGER_CALLBACKS
+ action = " new";
+#endif
+ }
+
+#ifdef DEBUG_DEBUGGER_CALLBACKS
+ printf("%s: ", action);
+ for(unsigned i = 0; i < s_callDepth; ++i)
+ printf(" ");
+ printf("%d: at exec: %p (caller: %p, pause: %p) source: %d line: %d\n", s_callDepth, exec, exec->callingExecState(), pauseExecState, sourceID, lineNumber);
+#endif
+}
+
+bool JavaScriptDebugServer::callEvent(ExecState* exec, int sourceID, int lineNumber, JSObject*, const List&)
+{
+ if (m_paused)
+ return true;
+ updateCurrentCallFrame(m_currentCallFrame, exec, sourceID, lineNumber, m_pauseOnExecState);
+ pauseIfNeeded(exec, sourceID, lineNumber);