23 #include "debugwindow.h" 25 #include <QSharedData> 26 #include "khtml_debug.h" 27 #include <QtAlgorithms> 31 #include "khtml_debug.h" 32 #include <kactioncollection.h> 33 #include <ktoggleaction.h> 35 #include <kstringhandler.h> 36 #include <kxmlguifactory.h> 38 #include <ktexteditor/configinterface.h> 39 #include <ktexteditor/sessionconfiginterface.h> 40 #include <ktexteditor/modificationinterface.h> 41 #include <ktexteditor/editorchooser.h> 42 #include <ktexteditor/cursor.h> 44 #include "kjs_proxy.h" 46 #include "kjs_binding.h" 47 #include "khtmlview.h" 48 #include "khtml_settings.h" 49 #include "khtml_factory.h" 50 #include <kjs/ustring.h> 51 #include <kjs/object.h> 52 #include <kjs/function.h> 53 #include <kjs/context.h> 54 #include <ecma/kjs_window.h> 58 #include <QVBoxLayout> 62 #include <QToolButton> 64 #include "breakpointsdock.h" 65 #include "consoledock.h" 66 #include "localvariabledock.h" 67 #include "watchesdock.h" 68 #include "callstackdock.h" 69 #include "scriptsdock.h" 71 #include "value2string.h" 90 DebugWindow::DebugWindow(
QWidget *parent)
96 setCaption(
i18n(
"JavaScript Debugger"));
99 m_localVariables =
new LocalVariablesDock;
100 m_scripts =
new ScriptsDock;
101 m_callStack =
new CallStackDock;
103 m_console =
new ConsoleDock;
104 connect(m_console, SIGNAL(requestEval(
QString)),
120 setCentralWidget(splitter);
130 connect(m_scripts, SIGNAL(displayScript(KJSDebugger::DebugDocument*)),
131 this, SLOT(displayScript(KJSDebugger::DebugDocument*)));
132 connect(m_callStack, SIGNAL(displayScript(KJSDebugger::DebugDocument*,
int)),
133 this, SLOT(displayScript(KJSDebugger::DebugDocument*,
int)));
134 connect(m_callStack, SIGNAL(displayScript(KJSDebugger::DebugDocument*,
int)),
135 this, SLOT(updateVarView()));
137 m_breakAtNext =
false;
139 m_runningSessionCtx = 0;
142 void DebugWindow::syncFromConfig()
145 m_reindentSources =
config.readEntry<
bool>(
"ReindentSources",
true);
146 m_catchExceptions =
config.readEntry<
bool>(
"CatchExceptions",
true);
151 void DebugWindow::syncToConfig()
154 config.writeEntry(
"ReindentSources", m_reindentSources);
155 config.writeEntry(
"CatchExceptions", m_catchExceptions);
158 bool DebugWindow::shouldReindentSources()
const 160 return m_reindentSources;
163 void DebugWindow::settingsChanged()
165 m_catchExceptions = m_catchExceptionsAction->isChecked();
166 m_reindentSources = m_reindentAction->isChecked();
170 void DebugWindow::createActions()
174 m_stopAct->setIconText(
i18n(
"Break at Next"));
175 actionCollection()->addAction(
"stop", m_stopAct);
176 m_stopAct->setEnabled(
true);
177 connect(m_stopAct, SIGNAL(triggered(
bool)),
this, SLOT(stopAtNext()));
180 actionCollection()->addAction(
"continue", m_continueAct);
181 actionCollection()->setDefaultShortcut(m_continueAct,
Qt::Key_F9);
182 m_continueAct->setEnabled(
false);
183 connect(m_continueAct, SIGNAL(triggered(
bool)),
this, SLOT(continueExecution()));
186 actionCollection()->addAction(
"stepOver", m_stepOverAct);
187 actionCollection()->setDefaultShortcut(m_stepOverAct,
Qt::Key_F10);
188 m_stepOverAct->setEnabled(
false);
189 connect(m_stepOverAct, SIGNAL(triggered(
bool)),
this, SLOT(stepOver()));
192 actionCollection()->addAction(
"stepInto", m_stepIntoAct);
193 actionCollection()->setDefaultShortcut(m_stepIntoAct,
Qt::Key_F11);
194 m_stepIntoAct->setEnabled(
false);
196 connect(m_stepIntoAct, SIGNAL(triggered(
bool)),
this, SLOT(stepInto()));
199 actionCollection()->addAction(
"stepOut", m_stepOutAct);
200 actionCollection()->setDefaultShortcut(m_stepOutAct,
Qt::Key_F12);
201 m_stepOutAct->setEnabled(
false);
202 connect(m_stepOutAct, SIGNAL(triggered(
bool)),
this, SLOT(stepOut()));
205 actionCollection()->addAction(
"reindent", m_reindentAction);
206 m_reindentAction->setChecked(m_reindentSources);
207 connect(m_reindentAction, SIGNAL(toggled(
bool)),
this, SLOT(settingsChanged()));
210 actionCollection()->addAction(
"except", m_catchExceptionsAction);
211 m_catchExceptionsAction->setChecked(m_catchExceptions);
212 connect(m_catchExceptionsAction, SIGNAL(toggled(
bool)),
this, SLOT(settingsChanged()));
215 void DebugWindow::createMenus()
223 menuBar()->addMenu(debugMenu);
226 settingsMenu->
addAction(m_catchExceptionsAction);
227 settingsMenu->
addAction(m_reindentAction);
228 menuBar()->addMenu(settingsMenu);
231 void DebugWindow::createToolBars()
233 toolBar()->addAction(m_stopAct);
234 toolBar()->addSeparator();
235 toolBar()->addAction(m_continueAct);
236 toolBar()->addAction(m_stepOverAct);
237 toolBar()->addAction(m_stepIntoAct);
238 toolBar()->addAction(m_stepOutAct);
241 void DebugWindow::createTabWidget()
250 connect(closeTabButton, SIGNAL(clicked()),
this, SLOT(closeTab()));
255 void DebugWindow::createStatusBar()
257 statusBar()->showMessage(
i18n(
"Ready"));
260 void DebugWindow::updateStoppedMark(RunMode mode)
266 DebugDocument::Ptr doc = ctx()->activeDocument();
267 assert(!doc.isNull());
270 if (mode == Running) {
273 imark->
removeMark(ctx()->activeLine() - doc->baseLine(),
274 KTextEditor::MarkInterface::Execution);
276 displayScript(doc.get(), ctx()->activeLine());
278 imark->addMark(ctx()->activeLine() - doc->baseLine(),
279 KTextEditor::MarkInterface::Execution);
283 void DebugWindow::setUIMode(RunMode mode)
287 updateStoppedMark(mode);
289 if (mode == Running) {
291 m_continueAct->setEnabled(
false);
292 m_stepIntoAct->setEnabled(
false);
293 m_stepOutAct->setEnabled(
false);
294 m_stepOverAct->setEnabled(
false);
295 m_runningSessionCtx = ctx();
298 m_localVariables->updateDisplay(ctx()->execContexts.top());
299 m_callStack->displayStack(ctx());
302 m_continueAct->setEnabled(
true);
303 m_stepIntoAct->setEnabled(
true);
304 m_stepOutAct->setEnabled(
true);
305 m_stepOverAct->setEnabled(
true);
306 m_runningSessionCtx = 0;
312 bool DebugWindow::isBlocked()
318 return self->inSession() ||
self->m_modalLevel;
321 void DebugWindow::resetTimeoutsIfNeeded()
326 intp->restartTimeoutCheck();
327 intp = intp->nextInterpreter();
328 }
while (intp != KJS::Interpreter::firstInterpreter());
332 void DebugWindow::forceStopAtNext()
335 self->m_breakAtNext =
true;
338 void DebugWindow::stopAtNext()
340 m_breakAtNext = m_stopAct->isChecked();
343 bool DebugWindow::shouldContinue(InterpreterContext *ic)
345 return !ic || ic->mode !=
Abort;
348 void DebugWindow::leaveDebugSession()
352 if (ctx()->mode != Step) {
355 updateStoppedMark(Running);
358 m_activeSessionCtxs.pop();
359 resetTimeoutsIfNeeded();
363 void DebugWindow::continueExecution()
371 void DebugWindow::stepInto()
380 void DebugWindow::stepOut()
385 ctx()->mode = StepOut;
386 ctx()->depthAtSkip = ctx()->execContexts.size();
390 void DebugWindow::stepOver()
395 ctx()->mode = StepOver;
396 ctx()->depthAtSkip = ctx()->execContexts.size();
400 DebugWindow::~DebugWindow()
402 assert(m_docsForIntrp.isEmpty());
403 assert(m_docForSid.isEmpty());
404 assert(m_activeSessionCtxs.isEmpty());
411 event->setAccepted(
false);
422 if (!m_contexts[interp]) {
423 m_contexts[interp] =
new InterpreterContext;
428 void DebugWindow::cleanupDocument(DebugDocument::Ptr doc)
430 m_docForSid.remove(doc->sid());
431 m_scripts->documentDestroyed(doc.get());
434 static void fatalAssert(
bool shouldBeTrue,
const char *error)
446 InterpreterContext *ctx = m_contexts[interp];
447 assert(!m_activeSessionCtxs.contains(ctx));
452 foreach (DebugDocument::Ptr doc, docs) {
453 cleanupDocument(doc);
456 m_docsForIntrp.remove(interp);
458 delete m_contexts.take(interp);
459 resetTimeoutsIfNeeded();
469 InterpreterContext *ctx = m_contexts.value(interp);
474 fatalAssert(!m_activeSessionCtxs.contains(ctx),
"Interpreter clear on active session");
479 while (i.hasNext()) {
480 DebugDocument::Ptr doc = i.next();
481 if (m_openDocuments.contains(doc.get())) {
487 cleanupDocument(doc);
491 bool DebugWindow::sourceParsed(
ExecState *exec,
int sourceId,
const UString &jsSourceURL,
492 const UString &source,
int startingLineNumber,
int errorLine,
const UString &)
497 <<
"sourceURL: " << jsSourceURL.
qstring()
498 <<
"startingLineNumber: " << startingLineNumber
499 <<
"errorLine: " << errorLine;
505 DebugDocument::Ptr document;
508 foreach (DebugDocument::Ptr cand, m_openDocuments) {
509 if (cand->isMarkedReload() && cand->url() == sourceURL && cand->baseLine() == startingLineNumber) {
522 foreach (InterpreterContext *ic, m_contexts) {
523 if (!ic->execContexts.isEmpty() && ic->execContexts.top() == exec) {
524 uiURL = ic->callStack.top().doc->url();
532 sourceId, startingLineNumber, qsource);
534 connect(document.get(), SIGNAL(documentDestroyed(KJSDebugger::DebugDocument*)),
535 this, SLOT(documentDestroyed(KJSDebugger::DebugDocument*)));
538 document->reloaded(sourceId, qsource);
544 m_scripts->addDocument(document.get());
547 m_docForSid[sourceId] = document;
550 document->setHasFunctions();
556 bool DebugWindow::exception(
ExecState *exec,
int sourceId,
int lineNo,
JSValue *exceptionObj)
564 if ((khtmlpart && !khtmlpart->
settings()->isJavaScriptErrorReportingEnabled()) || !m_catchExceptions) {
565 return shouldContinue(ic);
568 QString exceptionMsg = exceptionToString(exec, exceptionObj);
571 DebugDocument::Ptr doc = m_docForSid[sourceId];
575 if (exec->context()->codeType() == EvalCode) {
578 if (!doc->url().isEmpty()) {
582 QString msg =
i18n(
"An error occurred while attempting to run a script on this page.\n\n%1 line %2:\n%3",
585 KJSErrorDialog dlg(
this , msg,
true);
590 resetTimeoutsIfNeeded();
592 if (dlg.dontShowAgain()) {
593 m_catchExceptions =
false;
594 m_catchExceptionsAction->setChecked(
false);
597 if (dlg.debugSelected()) {
600 if (ic->hasActiveDocument()) {
601 enterDebugSession(exec, doc.get(), lineNo);
603 displayScript(doc.get(), lineNo);
607 return shouldContinue(ic);
610 bool DebugWindow::atStatement(
ExecState *exec,
int sourceId,
int firstLine,
int lastLine)
613 ctx->updateCall(firstLine);
614 return checkSourceLocation(exec, sourceId, firstLine, lastLine);
617 bool DebugWindow::checkSourceLocation(
KJS::ExecState *exec,
int sourceId,
int firstLine,
int lastLine)
623 if (!shouldContinue(candidateCtx)) {
627 bool enterDebugMode =
false;
631 enterDebugMode =
true;
634 if (candidateCtx->mode == Step) {
635 enterDebugMode =
true;
636 }
else if (candidateCtx->mode == StepOver) {
637 if (candidateCtx->execContexts.size() <= candidateCtx->depthAtSkip) {
638 enterDebugMode =
true;
642 DebugDocument::Ptr document = m_docForSid[sourceId];
643 assert(!document.isNull());
646 if (document->hasBreakpoint(firstLine)) {
647 enterDebugMode =
true;
651 if (enterDebugMode) {
652 enterDebugSession(exec, document.get(), firstLine);
656 return shouldContinue(candidateCtx);
659 bool DebugWindow::enterContext(
ExecState *exec,
int sourceId,
int lineno, JSObject *
function,
const List &args)
665 DebugDocument::Ptr document = m_docForSid[sourceId];
666 QString stackEntry = document->name();
667 if (
function && function->inherits(&InternalFunctionImp::info)) {
668 KJS::InternalFunctionImp *func =
static_cast<InternalFunctionImp *
>(
function);
669 QString functionName = func->functionName().qstring();
671 stackEntry = functionName;
675 if (exec->context()->codeType() == EvalCode) {
679 ctx->addCall(document, stackEntry, lineno);
680 ctx->execContexts.push(exec);
682 return shouldContinue(ctx);
685 bool DebugWindow::exitContext(
ExecState *exec,
int sourceId,
int lineno, JSObject *
function)
691 if (m_localVariables->currentlyDisplaying() == exec) {
694 m_localVariables->updateDisplay(0);
695 m_callStack->clearDisplay();
699 assert(ic->execContexts.top() == exec);
700 ic->execContexts.pop();
708 if (ic->mode == StepOut) {
709 if (ic->execContexts.size() < ic->depthAtSkip) {
721 if (m_activeSessionCtxs.isEmpty() &&
722 ic->execContexts.isEmpty() && ic->mode == Step) {
729 if (!m_activeSessionCtxs.isEmpty() && m_runningSessionCtx == ic) {
730 if (ic->execContexts.isEmpty()) {
733 fatalAssert(exec->context()->callingContext(),
"Apparent event re-entry");
744 if (exec->context()->codeType() == EvalCode) {
745 DebugDocument::Ptr doc = m_docForSid[sourceId];
746 if (!m_openDocuments.contains(doc.get()) && !doc->hasFunctions()) {
747 cleanupDocument(doc);
752 return shouldContinue(ic);
757 void DebugWindow::doEval(
const QString &qcode)
766 exec = m_callStack->selectedFrameContext();
768 exec = m_activeSessionCtxs.top()->execContexts.top();
772 int idx = m_tabWidget->currentIndex();
774 m_console->reportResult(qcode,
775 i18n(
"Do not know where to evaluate the expression. Please pause a script or open a source file."));
778 DebugDocument *document = m_openDocuments[idx];
779 exec = document->interpreter()->globalExec();
780 thisObj = document->interpreter()->globalObject();
795 JSObject *obj = interp->
globalObject()->get(exec,
"eval")->getObject();
797 args.
append(jsString(code));
802 JSValue *retVal = obj->call(exec, thisObj, args);
809 msg =
i18n(
"Evaluation threw an exception %1", exceptionToString(exec, exc));
811 msg = valueToString(retVal);
820 m_console->reportResult(qcode, msg);
829 void DebugWindow::updateVarView()
831 m_localVariables->updateDisplay(m_callStack->selectedFrameContext());
834 void DebugWindow::displayScript(DebugDocument *document)
836 displayScript(document, -1);
839 void DebugWindow::displayScript(DebugDocument *document,
int line)
845 if (m_tabWidget->isHidden()) {
851 if (!m_openDocuments.contains(document)) {
852 m_openDocuments.append(document);
853 m_tabWidget->addTab(view, document->name());
857 int idx = m_openDocuments.indexOf(document);
858 m_tabWidget->setCurrentIndex(idx);
866 void DebugWindow::documentDestroyed(KJSDebugger::DebugDocument *doc)
870 int idx = m_openDocuments.indexOf(doc);
875 m_tabWidget->removeTab(idx);
876 m_openDocuments.removeAt(idx);
877 if (m_openDocuments.isEmpty()) {
882 void DebugWindow::closeTab()
884 int idx = m_tabWidget->currentIndex();
885 m_tabWidget->removeTab(idx);
886 m_openDocuments.removeAt(idx);
887 if (m_openDocuments.isEmpty()) {
895 if (mark.type != KTextEditor::MarkInterface::BreakpointActive) {
900 DebugDocument *debugDocument = qobject_cast<DebugDocument *>(document->
parent());
901 assert(debugDocument);
903 int lineNumber = mark.line + debugDocument->baseLine();
907 debugDocument->setBreakpoint(lineNumber);
910 debugDocument->removeBreakpoint(lineNumber);
916 <<
"line: " << lineNumber;
919 void DebugWindow::enterDebugSession(
KJS::ExecState *exec, DebugDocument *document,
int line)
936 m_breakAtNext =
false;
937 m_stopAct->setChecked(
false);
945 bool DebugWindow::eventFilter(
QObject *
object,
QEvent *event)
947 switch (event->
type()) {
959 while (object->
parent()) {
960 object =
object->parent();
962 if (
object ==
this) {
966 event->setAccepted(
false);
978 void DebugWindow::enterLoop()
981 m_activeEventLoops.push(&eventLoop);
983 if (m_activeSessionCtxs.size() == 1) {
989 m_activeEventLoops.pop();
992 void DebugWindow::exitLoop()
994 if (m_activeSessionCtxs.isEmpty()) {
997 m_activeEventLoops.top()->quit();
1000 void DebugWindow::enterModality()
1003 foreach (
QWidget *widget, widgets) {
1008 void DebugWindow::leaveModality()
1011 foreach (
QWidget *widget, widgets) {
virtual void removeMark(int line, uint markType)=0
QString & append(QChar ch)
QEvent::Type type() const const
virtual bool setCursorPosition(Cursor position)=0
Interpreter * dynamicInterpreter() const
KCOREADDONS_EXPORT QString rsqueeze(const QString &str, int maxlen=40)
KJS_EXTERNAL_EXPORT QString qstring() const
void setStretchFactor(int index, int stretch)
void append(JSValue *val)
This class is khtml's main class.
QTextStream & endl(QTextStream &stream)
KToggleAction * pause(const QObject *recvr, const char *slot, QObject *parent)
KSharedConfigPtr config()
JSObject * thisValue() const
void setException(JSValue *e)
We inherit from Interpreter, to save a pointer to the HTML part that the interpreter runs for...
void installEventFilter(QObject *filterObj)
int exec(QEventLoop::ProcessEventsFlags flags)
void closeEvent(QCloseEvent *) override
virtual void attach(Interpreter *interp)
bool isEmpty() const const
JSValue * exception() const
virtual bool eventFilter(QObject *watched, QEvent *event)
bool hadException() const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
virtual QString documentName() const =0
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
QString i18n(const char *text, const TYPE &arg...)
Helper for pausing/resuming timers.
QIcon fromTheme(const QString &name)
virtual void detach(Interpreter *interp)
QObject * parent() const const
const KHTMLSettings * settings() const
void removeEventFilter(QObject *obj)
JSGlobalObject * globalObject() const