File indexing completed on 2024-12-01 12:33:25

0001 /*
0002  *  This file is part of the KDE libraries
0003  *  Copyright (C) 2006 Matt Broadstone (mbroadst@gmail.com)
0004  *
0005  *  This library is free software; you can redistribute it and/or
0006  *  modify it under the terms of the GNU Library General Public
0007  *  License as published by the Free Software Foundation; either
0008  *  version 2 of the License, or (at your option) any later version.
0009  *
0010  *  This library is distributed in the hope that it will be useful,
0011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
0012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013  *  Library General Public License for more details.
0014  *
0015  *  You should have received a copy of the GNU Library General Public
0016  *  License along with this library; if not, write to the Free Software
0017  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
0018  */
0019 
0020 #ifndef DEBUGWINDOW_H
0021 #define DEBUGWINDOW_H
0022 
0023 #include <kcomponentdata.h>
0024 #include <kxmlguiwindow.h>
0025 
0026 #include <kjs/debugger.h>
0027 #include <kjs/completion.h>
0028 #include <kjs/interpreter.h>
0029 #include <kjs/value.h>
0030 #include <kjs_binding.h>
0031 
0032 #include <ktexteditor/document.h>
0033 #include <ktexteditor/view.h>
0034 #include <ktexteditor/editor.h>
0035 #include <ktexteditor/markinterface.h>
0036 
0037 #include "khtml_pagecache.h"
0038 #include "khtml_part.h"
0039 #include "dom/dom_misc.h"
0040 #include "misc/shared.h"
0041 
0042 #include <QStack>
0043 #include <QVector>
0044 
0045 #include "interpreter_ctx.h"
0046 #include "debugdocument.h"
0047 
0048 class QAction;
0049 class KToggleAction;
0050 class QTabWidget;
0051 class QFrame;
0052 class QEventLoop;
0053 
0054 namespace KJSDebugger
0055 {
0056 
0057 class CallStackDock;
0058 class WatchesDock;
0059 class LocalVariablesDock;
0060 class ScriptsDock;
0061 class BreakpointsDock;
0062 class ConsoleDock;
0063 
0064 /**
0065 * DebuggerWindow
0066 *
0067 * KJSDebugWin represents the debugger window that is visible to the user. It contains
0068 * a stack frame list, a code viewer and a source fragment selector, plus buttons
0069 * to control execution including next, step and continue.
0070 *
0071 * There is only one debug window per program. This can be obtained by calling #instance
0072 */
0073 class DebugWindow : public KXmlGuiWindow, public KJS::Debugger, public KComponentData,
0074     public khtml::Shared<DebugWindow>
0075 {
0076     Q_OBJECT
0077 
0078 public:
0079     DebugWindow(QWidget *parent = 0);
0080     virtual ~DebugWindow();
0081 
0082     static DebugWindow *window();
0083 
0084     // Returns true if the debugger is active, and has blocked the execution
0085     // for some reason.
0086     // ### seems like some of what we (mis-)use inSession() for should use this
0087     static bool isBlocked();
0088 
0089     // Returns if we blocked execution; KHTML will attempt to use it
0090     // to prevent some kinds of accidental recursion. Should go
0091     // if proper modal dialog manager shows up
0092     bool inSession() const
0093     {
0094         return !m_activeSessionCtxs.isEmpty();
0095     }
0096 
0097 public:
0098 
0099     // All of the below are overridden from KJS::Debugger
0100     bool sourceParsed(KJS::ExecState *exec, int sourceId, const KJS::UString &sourceURL,
0101                       const KJS::UString &source, int startingLineNumber, int errorLine, const KJS::UString &errorMsg);
0102     bool exception(KJS::ExecState *exec, int sourceId, int lineno, KJS::JSValue *exceptionObj);
0103     bool atStatement(KJS::ExecState *exec, int sourceId, int firstLine, int lastLine);
0104     bool enterContext(KJS::ExecState *exec, int sourceId, int lineno, KJS::JSObject *function, const KJS::List &args);
0105     bool exitContext(KJS::ExecState *exec, int sourceId, int lineno, KJS::JSObject *function);
0106     void attach(KJS::Interpreter *interp);
0107     void detach(KJS::Interpreter *interp);
0108 
0109     bool shouldReindentSources() const;
0110 
0111     // Called by KJSProxy when we navigate away from a page
0112     void clearInterpreter(KJS::Interpreter *interp);
0113 
0114     // Hook for activating the debugger from gdb or such
0115     static void forceStopAtNext();
0116 
0117 public Q_SLOTS:
0118     void stopAtNext();
0119     void continueExecution();
0120     void stepInto();
0121     void stepOut();
0122     void stepOver();
0123 
0124     void markSet(KTextEditor::Document *document, KTextEditor::Mark mark,
0125                  KTextEditor::MarkInterface::MarkChangeAction action);
0126 
0127 protected:
0128     virtual void closeEvent(QCloseEvent *event);
0129 
0130     bool eventFilter(QObject *object, QEvent *event);
0131     void disableOtherWindows();
0132     void enableOtherWindows();
0133 
0134 private Q_SLOTS:
0135     void settingsChanged();
0136 
0137     void displayScript(KJSDebugger::DebugDocument *document);
0138     void displayScript(KJSDebugger::DebugDocument *document, int line); // -1 denotes not focusing on the line
0139     void updateVarView();
0140     void closeTab();
0141     void documentDestroyed(KJSDebugger::DebugDocument *doc);
0142 
0143     void doEval(const QString &code);
0144 private:
0145     void createActions();
0146     void createMenus();
0147     void createToolBars();
0148     void createStatusBar();
0149     void createTabWidget();
0150 
0151     void enterDebugSession(KJS::ExecState *exec, DebugDocument *document, int line);
0152     void leaveDebugSession();
0153 
0154     void enterModality();
0155     void leaveModality();
0156 
0157     void enterLoop();
0158     void exitLoop();
0159 
0160     enum RunMode { Running, Stopped };
0161 
0162     void setUIMode(RunMode mode);
0163     void updateStoppedMark(RunMode mode);
0164 private:
0165     void cleanupDocument(DebugDocument::Ptr document);
0166 
0167     // Checks to see whether we should stop at the given location, based on the current
0168     // mode and breakpoints. Returns false if we should abort
0169     bool checkSourceLocation(KJS::ExecState *exec, int sourceId, int firstLine, int lastLine);
0170 
0171     // Standard actions
0172     QAction *m_exitAct;
0173 
0174     // Flow control actions
0175     QAction *m_continueAct;
0176     QAction *m_stopAct;
0177     QAction *m_stepIntoAct;
0178     QAction *m_stepOutAct;
0179     QAction *m_stepOverAct;
0180 
0181     KToggleAction *m_catchExceptionsAction;
0182     KToggleAction *m_reindentAction;
0183 
0184 //     WatchesDock *m_watches;
0185     LocalVariablesDock *m_localVariables;
0186     ScriptsDock *m_scripts;
0187     CallStackDock *m_callStack;
0188     BreakpointsDock *m_breakpoints;
0189     ConsoleDock *m_console;
0190 
0191     QTabWidget *m_tabWidget;
0192 
0193     // e.g. not aborted
0194     bool shouldContinue(InterpreterContext *ic);
0195 
0196     // This keeps track of modal dialogs we've put up
0197     // that may disable the CPU guard.
0198     int m_modalLevel;
0199 
0200     // This is all the nested event loops that are active
0201     QStack<QEventLoop *> m_activeEventLoops;
0202 
0203     void resetTimeoutsIfNeeded();
0204 
0205     bool m_reindentSources;
0206     bool m_catchExceptions;
0207     void syncFromConfig();
0208     void syncToConfig();
0209 
0210     // The handling of debugger modes is a bit funny.
0211     // essentially, we want normal step/stepOver/stepOut
0212     // to work per (dynamic) interpreter, but "break at next"
0213     // should work globally.
0214     bool m_breakAtNext;
0215 
0216     InterpreterContext *ctx()
0217     {
0218         return m_activeSessionCtxs.isEmpty() ? 0 : m_activeSessionCtxs.top();
0219     }
0220 
0221     QHash<int,     DebugDocument::Ptr> m_docForSid;
0222 
0223     // For each interpreter, we keep track of what documents belong to it
0224     // so we can discard them when needed, as well as flush for reload
0225     QHash<KJS::Interpreter *, QList<DebugDocument::Ptr> > m_docsForIntrp;
0226 
0227     // Some of the state we want to keep track of while debugging, such as backtraces,
0228     // is per-interpreter, and this lets us look uit up.
0229     QHash<KJS::Interpreter *, InterpreterContext *> m_contexts;
0230 
0231     // This keeps track of the contexts for the various debuggers
0232     // we may be in session for. It's needed because the same window is
0233     // used for all, so we may occassionally be a few levels of recursion in,
0234     // so we need to know exactly how to unwind, etc.
0235     QStack<InterpreterContext *> m_activeSessionCtxs;
0236 
0237     // This denotes the session we were in once we entered the running UI
0238     // mode. May be null
0239     InterpreterContext *m_runningSessionCtx;
0240 
0241     // The documents that are currently open for viewing.
0242     // The index matches that of the tab widget;
0243     QList<DebugDocument *> m_openDocuments;
0244 
0245     static DebugWindow *s_debugger;
0246 };
0247 
0248 } // namespace
0249 
0250 #endif // DEBUGWINDOW_H