| # khtml Library API Documentation
Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Namespaces | Source Files

# kjs_debugwin.cpp

    00001 /*
    00002  *  This file is part of the KDE libraries
    00003  *  Copyright (C) 2000-2001 Harri Porten (porten@kde.org)
    00004  *  Copyright (C) 2001,2003 Peter Kelly (pmk@post.com)
    00005  *
    00006  *  This library is free software; you can redistribute it and/or
    00007  *  modify it under the terms of the GNU Library General Public
    00008  *  License as published by the Free Software Foundation; either
    00009  *  version 2 of the License, or (at your option) any later version.
    00010  *
    00011  *  This library is distributed in the hope that it will be useful,
    00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
    00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    00014  *  Library General Public License for more details.
    00015  *
    00016  *  You should have received a copy of the GNU Library General Public
    00017  *  License along with this library; if not, write to the Free Software
    00018  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    00019  */
    00020
    00021 #include "kjs_debugwin.h"
    00022 #include "kjs_proxy.h"
    00023
    00024 #ifdef KJS_DEBUGGER
    00025
    00026 #include <assert.h>
    00027 #include <stdlib.h>
    00028 #include <qlayout.h>
    00029 #include <qpushbutton.h>
    00030 #include <qtextedit.h>
    00031 #include <qlistbox.h>
    00032 #include <qmultilineedit.h>
    00033 #include <qapplication.h>
    00034 #include <qsplitter.h>
    00035 #include <qcombobox.h>
    00036 #include <qbitmap.h>
    00037 #include <qwidgetlist.h>
    00038 #include <qlabel.h>
    00039 #include <qdatastream.h>
    00040 #include <qcstring.h>
    00041 #include <qpainter.h>
    00042 #include <qscrollbar.h>
    00043
    00044 #include <klocale.h>
    00045 #include <kdebug.h>
    00046 #include <kiconloader.h>
    00047 #include <kglobal.h>
    00048 #include <kmessagebox.h>
    00049 #include <kguiitem.h>
    00050 #include <kpopupmenu.h>
    00051 #include <kmenubar.h>
    00052 #include <kaction.h>
    00053 #include <kactioncollection.h>
    00054 #include <kglobalsettings.h>
    00055 #include <kshortcut.h>
    00056 #include <kconfig.h>
    00057 #include <kconfigbase.h>
    00058 #include <kapplication.h>
    00059 #include <dcop/dcopclient.h>
    00060 #include <kstringhandler.h>
    00061
    00062 #include "kjs_dom.h"
    00063 #include "kjs_binding.h"
    00064 #include "khtml_part.h"
    00065 #include "khtmlview.h"
    00066 #include "khtml_pagecache.h"
    00067 #include "khtml_settings.h"
    00068 #include "khtml_factory.h"
    00069 #include "misc/decoder.h"
    00070 #include <kjs/ustring.h>
    00071 #include <kjs/object.h>
    00072 #include <kjs/function.h>
    00073 #include <kjs/interpreter.h>
    00074
    00075 using namespace KJS;
    00076 using namespace khtml;
    00077
    00078 SourceDisplay::SourceDisplay(KJSDebugWin *debugWin, QWidget *parent, const char *name)
    00079   : QScrollView(parent,name), m_currentLine(-1), m_sourceFile(0), m_debugWin(debugWin),
    00080     m_font(KGlobalSettings::fixedFont())
    00081 {
    00082   verticalScrollBar()->setLineStep(QFontMetrics(m_font).height());
    00083   viewport()->setBackgroundMode(Qt::NoBackground);
    00084   m_breakpointIcon = KGlobal::iconLoader()->loadIcon("stop",KIcon::Small);
    00085 }
    00086
    00087 SourceDisplay::~SourceDisplay()
    00088 {
    00089   if (m_sourceFile) {
    00090     m_sourceFile->deref();
    00091     m_sourceFile = 0L;
    00092   }
    00093 }
    00094
    00095 void SourceDisplay::setSource(SourceFile *sourceFile)
    00096 {
    00097   if ( sourceFile )
    00098       sourceFile->ref();
    00099   if (m_sourceFile)
    00100       m_sourceFile->deref();
    00101   m_sourceFile = sourceFile;
    00102   if ( m_sourceFile )
    00103       m_sourceFile->ref();
    00104
    00105   if (!m_sourceFile || !m_debugWin->isVisible()) {
    00106     return;
    00107   }
    00108
    00109   QString code = sourceFile->getCode();
    00110   const QChar *chars = code.unicode();
    00111   uint len = code.length();
    00112   QChar newLine('\n');
    00113   QChar cr('\r');
    00114   QChar tab('\t');
    00115   QString tabstr("        ");
    00116   QString line;
    00117   m_lines.clear();
    00118   int width = 0;
    00119   QFontMetrics metrics(m_font);
    00120
    00121   for (uint pos = 0; pos < len; pos++) {
    00122     QChar c = chars[pos];
    00123     if (c == cr) {
    00124       if (pos < len-1 && chars[pos+1] == newLine)
    00125     continue;
    00126       else
    00127     c = newLine;
    00128     }
    00129     if (c == newLine) {
    00130       m_lines.append(line);
    00131       int lineWidth = metrics.width(line);
    00132       if (lineWidth > width)
    00133     width = lineWidth;
    00134       line = "";
    00135     }
    00136     else if (c == tab) {
    00137       line += tabstr;
    00138     }
    00139     else {
    00140       line += c;
    00141     }
    00142   }
    00143   if (line.length()) {
    00144     m_lines.append(line);
    00145     int lineWidth = metrics.width(line);
    00146     if (lineWidth > width)
    00147       width = lineWidth;
    00148   }
    00149
    00150   int linenoDisplayWidth = metrics.width("888888");
    00151   resizeContents(linenoDisplayWidth+4+width,metrics.height()*m_lines.count());
    00152   update();
    00153   sourceFile->deref();
    00154 }
    00155
    00156 void SourceDisplay::setCurrentLine(int lineno, bool doCenter)
    00157 {
    00158   m_currentLine = lineno;
    00159
    00160   if (doCenter && m_currentLine >= 0) {
    00161     QFontMetrics metrics(m_font);
    00162     int height = metrics.height();
    00163     center(0,height*m_currentLine+height/2);
    00164   }
    00165
    00166   updateContents();
    00167 }
    00168
    00169 void SourceDisplay::contentsMousePressEvent(QMouseEvent *e)
    00170 {
    00171   QScrollView::mouseDoubleClickEvent(e);
    00172   QFontMetrics metrics(m_font);
    00173   int lineno = e->y()/metrics.height();
    00174   emit lineDoubleClicked(lineno+1); // line numbers start from 1
    00175 }
    00176
    00177 void SourceDisplay::showEvent(QShowEvent *)
    00178 {
    00179     setSource(m_sourceFile);
    00180 }
    00181
    00182 void SourceDisplay::drawContents(QPainter *p, int clipx, int clipy, int clipw, int cliph)
    00183 {
    00184   if (!m_sourceFile) {
    00185     p->fillRect(clipx,clipy,clipw,cliph,palette().active().base());
    00186     return;
    00187   }
    00188
    00189   QFontMetrics metrics(m_font);
    00190   int height = metrics.height();
    00191
    00192   int bottom = clipy + cliph;
    00193   int right = clipx + clipw;
    00194
    00195   int firstLine = clipy/height-1;
    00196   if (firstLine < 0)
    00197     firstLine = 0;
    00198   int lastLine = bottom/height+2;
    00199   if (lastLine > (int)m_lines.count())
    00200     lastLine = m_lines.count();
    00201
    00202   p->setFont(m_font);
    00203
    00204   int linenoWidth = metrics.width("888888");
    00205
    00206   for (int lineno = firstLine; lineno <= lastLine; lineno++) {
    00207     QString linenoStr = QString().sprintf("%d",lineno+1);
    00208
    00209
    00210     p->fillRect(0,height*lineno,linenoWidth,height,palette().active().mid());
    00211
    00212     p->setPen(palette().active().text());
    00213     p->drawText(0,height*lineno,linenoWidth,height,Qt::AlignRight,linenoStr);
    00214
    00215     QColor bgColor;
    00216     QColor textColor;
    00217
    00218     if (lineno == m_currentLine) {
    00219       bgColor = palette().active().highlight();
    00220       textColor = palette().active().highlightedText();
    00221     }
    00222     else if (m_debugWin->haveBreakpoint(m_sourceFile,lineno+1,lineno+1)) {
    00223       bgColor = palette().active().text();
    00224       textColor = palette().active().base();
    00225       p->drawPixmap(2,height*lineno+height/2-m_breakpointIcon.height()/2,m_breakpointIcon);
    00226     }
    00227     else {
    00228       bgColor = palette().active().base();
    00229       textColor = palette().active().text();
    00230     }
    00231
    00232     p->fillRect(linenoWidth,height*lineno,right-linenoWidth,height,bgColor);
    00233     p->setPen(textColor);
    00234     p->drawText(linenoWidth+4,height*lineno,contentsWidth()-linenoWidth-4,height,
    00235         Qt::AlignLeft,m_lines[lineno]);
    00236   }
    00237
    00238   int remainingTop = height*(lastLine+1);
    00239   p->fillRect(0,remainingTop,linenoWidth,bottom-remainingTop,palette().active().mid());
    00240
    00241   p->fillRect(linenoWidth,remainingTop,
    00242           right-linenoWidth,bottom-remainingTop,palette().active().base());
    00243 }
    00244
    00245 //-------------------------------------------------------------------------
    00246
    00247 KJSDebugWin * KJSDebugWin::kjs_html_debugger = 0;
    00248
    00249 QString SourceFile::getCode()
    00250 {
    00251   if (interpreter) {
    00252     KHTMLPart *part = ::qt_cast<KHTMLPart*>(static_cast<ScriptInterpreter*>(interpreter)->part());
    00253     if (part && url == part->url().url() && KHTMLPageCache::self()->isValid(part->cacheId())) {
    00254       Decoder *decoder = part->createDecoder();
    00255       QByteArray data;
    00256       QDataStream stream(data,IO_WriteOnly);
    00257       KHTMLPageCache::self()->saveData(part->cacheId(),&stream);
    00258       QString str;
    00259       if (data.size() == 0)
    00260     str = "";
    00261       else
    00262     str = decoder->decode(data.data(),data.size()) + decoder->flush();
    00263       delete decoder;
    00264       return str;
    00265     }
    00266   }
    00267
    00268   return code;
    00269 }
    00270
    00271 //-------------------------------------------------------------------------
    00272
    00273 SourceFragment::SourceFragment(int sid, int bl, int el, SourceFile *sf)
    00274 {
    00275   sourceId = sid;
    00276   baseLine = bl;
    00277   errorLine = el;
    00278   sourceFile = sf;
    00279   sourceFile->ref();
    00280 }
    00281
    00282 SourceFragment::~SourceFragment()
    00283 {
    00284   sourceFile->deref();
    00285   sourceFile = 0L;
    00286 }
    00287
    00288 //-------------------------------------------------------------------------
    00289
    00290 KJSErrorDialog::KJSErrorDialog(QWidget *parent, const QString& errorMessage, bool showDebug)
    00291   : KDialogBase(parent,0,true,i18n("JavaScript Error"),
    00292         showDebug ? KDialogBase::Ok|KDialogBase::User1 : KDialogBase::Ok,
    00293         KDialogBase::Ok,false,KGuiItem("&Debug","gear"))
    00294 {
    00295   QWidget *page = new QWidget(this);
    00296   setMainWidget(page);
    00297
    00298   QLabel *iconLabel = new QLabel("",page);
    00299   iconLabel->setPixmap(KGlobal::iconLoader()->loadIcon("messagebox_critical",
    00300                                KIcon::NoGroup,KIcon::SizeMedium,
    00301                                KIcon::DefaultState,0,true));
    00302
    00303   QWidget *contents = new QWidget(page);
    00304   QLabel *label = new QLabel(errorMessage,contents);
    00305   m_dontShowAgainCb = new QCheckBox(i18n("&Do not show this message again"),contents);
    00306
    00307   QVBoxLayout *vl = new QVBoxLayout(contents,0,spacingHint());
    00308   vl->addWidget(label);
    00309   vl->addWidget(m_dontShowAgainCb);
    00310
    00311   QHBoxLayout *topLayout = new QHBoxLayout(page,0,spacingHint());
    00312   topLayout->addWidget(iconLabel);
    00313   topLayout->addWidget(contents);
    00314   topLayout->addStretch(10);
    00315
    00316   m_debugSelected = false;
    00317 }
    00318
    00319 KJSErrorDialog::~KJSErrorDialog()
    00320 {
    00321 }
    00322
    00323 void KJSErrorDialog::slotUser1()
    00324 {
    00325   m_debugSelected = true;
    00326   close();
    00327 }
    00328
    00329 //-------------------------------------------------------------------------
    00330 EvalMultiLineEdit::EvalMultiLineEdit(QWidget *parent)
    00331     : QMultiLineEdit(parent) {
    00332 }
    00333
    00334 void EvalMultiLineEdit::keyPressEvent(QKeyEvent * e)
    00335 {
    00336     if (e->key() == Qt::Key_Return) {
    00337         if (hasSelectedText()) {
    00338             m_code = selectedText();
    00339         } else {
    00340             int para, index;
    00341             getCursorPosition(&para, &index);
    00342             m_code = text(para);
    00343         }
    00344         end();
    00345     }
    00346     QMultiLineEdit::keyPressEvent(e);
    00347 }
    00348 //-------------------------------------------------------------------------
    00349 KJSDebugWin::KJSDebugWin(QWidget *parent, const char *name)
    00350   : KMainWindow(parent, name, WType_TopLevel), KInstance("kjs_debugger")
    00351 {
    00352   m_breakpoints = 0;
    00353   m_breakpointCount = 0;
    00354
    00355   m_curSourceFile = 0;
    00356   m_mode = Continue;
    00357   m_nextSourceUrl = "";
    00358   m_nextSourceBaseLine = 1;
    00359   m_execs = 0;
    00360   m_execsCount = 0;
    00361   m_execsAlloc = 0;
    00362   m_steppingDepth = 0;
    00363
    00364   m_stopIcon = KGlobal::iconLoader()->loadIcon("stop",KIcon::Small);
    00365   m_emptyIcon = QPixmap(m_stopIcon.width(),m_stopIcon.height());
    00366   QBitmap emptyMask(m_stopIcon.width(),m_stopIcon.height(),true);
    00367   m_emptyIcon.setMask(emptyMask);
    00368
    00369   setCaption(i18n("JavaScript Debugger"));
    00370
    00371   QWidget *mainWidget = new QWidget(this);
    00372   setCentralWidget(mainWidget);
    00373
    00374   QVBoxLayout *vl = new QVBoxLayout(mainWidget,5);
    00375
    00376   // frame list & code
    00377   QSplitter *hsplitter = new QSplitter(Qt::Vertical,mainWidget);
    00378   QSplitter *vsplitter = new QSplitter(hsplitter);
    00379   QFont font(KGlobalSettings::fixedFont());
    00380
    00381   QWidget *contextContainer = new QWidget(vsplitter);
    00382
    00383   QLabel *contextLabel = new QLabel(i18n("Call stack"),contextContainer);
    00384   QWidget *contextListContainer = new QWidget(contextContainer);
    00385   m_contextList = new QListBox(contextListContainer);
    00386   m_contextList->setMinimumSize(100,200);
    00387   connect(m_contextList,SIGNAL(highlighted(int)),this,SLOT(slotShowFrame(int)));
    00388
    00389   QHBoxLayout *clistLayout = new QHBoxLayout(contextListContainer);
    00390   clistLayout->addWidget(m_contextList);
    00391   clistLayout->addSpacing(KDialog::spacingHint());
    00392
    00393   QVBoxLayout *contextLayout = new QVBoxLayout(contextContainer);
    00394   contextLayout->addWidget(contextLabel);
    00395   contextLayout->addSpacing(KDialog::spacingHint());
    00396   contextLayout->addWidget(contextListContainer);
    00397
    00398   // source selection & display
    00399   QWidget *sourceSelDisplay = new QWidget(vsplitter);
    00400   QVBoxLayout *ssdvl = new QVBoxLayout(sourceSelDisplay);
    00401
    00402   m_sourceSel = new QComboBox(toolBar());
    00403   connect(m_sourceSel,SIGNAL(activated(int)),this,SLOT(slotSourceSelected(int)));
    00404
    00405   m_sourceDisplay = new SourceDisplay(this,sourceSelDisplay);
    00406   ssdvl->addWidget(m_sourceDisplay);
    00407   connect(m_sourceDisplay,SIGNAL(lineDoubleClicked(int)),SLOT(slotToggleBreakpoint(int)));
    00408
    00409   QValueList<int> vsplitSizes;
    00410   vsplitSizes.insert(vsplitSizes.end(),120);
    00411   vsplitSizes.insert(vsplitSizes.end(),480);
    00412   vsplitter->setSizes(vsplitSizes);
    00413
    00414   // evaluate
    00415
    00416   QWidget *evalContainer = new QWidget(hsplitter);
    00417
    00418   QLabel *evalLabel = new QLabel(i18n("JavaScript console"),evalContainer);
    00419   m_evalEdit = new EvalMultiLineEdit(evalContainer);
    00420   m_evalEdit->setWordWrap(QMultiLineEdit::NoWrap);
    00421   m_evalEdit->setFont(font);
    00422   connect(m_evalEdit,SIGNAL(returnPressed()),SLOT(slotEval()));
    00423   m_evalDepth = 0;
    00424
    00425   QVBoxLayout *evalLayout = new QVBoxLayout(evalContainer);
    00426   evalLayout->addSpacing(KDialog::spacingHint());
    00427   evalLayout->addWidget(evalLabel);
    00428   evalLayout->addSpacing(KDialog::spacingHint());
    00429   evalLayout->addWidget(m_evalEdit);
    00430
    00431   QValueList<int> hsplitSizes;
    00432   hsplitSizes.insert(hsplitSizes.end(),400);
    00433   hsplitSizes.insert(hsplitSizes.end(),200);
    00434   hsplitter->setSizes(hsplitSizes);
    00435
    00436   vl->addWidget(hsplitter);
    00437
    00438   // actions
    00439   KPopupMenu *debugMenu = new KPopupMenu(this);
    00440   menuBar()->insertItem("&Debug",debugMenu);
    00441
    00442   m_actionCollection = new KActionCollection(this);
    00443   m_actionCollection->setInstance(this);
    00444   m_nextAction       = new KAction(i18n("Next breakpoint","&Next"),"dbgnext",KShortcut(),this,SLOT(slotNext()),
    00445                    m_actionCollection,"next");
    00446   m_stepAction       = new KAction(i18n("&Step"),"dbgstep",KShortcut(),this,SLOT(slotStep()),
    00447                    m_actionCollection,"step");
    00448   m_continueAction   = new KAction(i18n("&Continue"),"dbgrun",KShortcut(),this,SLOT(slotContinue()),
    00449                    m_actionCollection,"cont");
    00450   m_stopAction       = new KAction(i18n("St&op"),"stop",KShortcut(),this,SLOT(slotStop()),
    00451                    m_actionCollection,"stop");
    00452   m_breakAction      = new KAction(i18n("&Break at Next Statement"),"dbgrunto",KShortcut(),this,SLOT(slotBreakNext()),
    00453                    m_actionCollection,"breaknext");
    00454
    00455   m_nextAction->setToolTip(i18n("Next breakpoint","Next"));
    00456   m_stepAction->setToolTip(i18n("Step"));
    00457   m_continueAction->setToolTip(i18n("Continue"));
    00458   m_stopAction->setToolTip(i18n("Stop"));
    00459   m_breakAction->setToolTip("Break at next Statement");
    00460
    00461   m_nextAction->setEnabled(false);
    00462   m_stepAction->setEnabled(false);
    00463   m_continueAction->setEnabled(false);
    00464   m_stopAction->setEnabled(false);
    00465   m_breakAction->setEnabled(true);
    00466
    00467   m_nextAction->plug(debugMenu);
    00468   m_stepAction->plug(debugMenu);
    00469   m_continueAction->plug(debugMenu);
    00470 //   m_stopAction->plug(debugMenu); ### disabled until DebuggerImp::stop() works reliably
    00471   m_breakAction->plug(debugMenu);
    00472
    00473   m_nextAction->plug(toolBar());
    00474   m_stepAction->plug(toolBar());
    00475   m_continueAction->plug(toolBar());
    00476 //   m_stopAction->plug(toolBar()); ###
    00477   m_breakAction->plug(toolBar());
    00478
    00479   toolBar()->insertWidget(1,300,m_sourceSel);
    00480   toolBar()->setItemAutoSized(1);
    00481
    00482   updateContextList();
    00483   setMinimumSize(300,200);
    00484   resize(600,450);
    00485
    00486 }
    00487
    00488 KJSDebugWin::~KJSDebugWin()
    00489 {
    00490   free(m_breakpoints);
    00491   free(m_execs);
    00492 }
    00493
    00494 KJSDebugWin *KJSDebugWin::createInstance()
    00495 {
    00496   assert(!kjs_html_debugger);
    00497   kjs_html_debugger = new KJSDebugWin();
    00498   return kjs_html_debugger;
    00499 }
    00500
    00501 void KJSDebugWin::destroyInstance()
    00502 {
    00503   assert(kjs_html_debugger);
    00504   kjs_html_debugger->hide();
    00505   delete kjs_html_debugger;
    00506 }
    00507
    00508 void KJSDebugWin::slotNext()
    00509 {
    00510   m_mode = Next;
    00511   leaveSession();
    00512 }
    00513
    00514 void KJSDebugWin::slotStep()
    00515 {
    00516   m_mode = Step;
    00517   leaveSession();
    00518 }
    00519
    00520 void KJSDebugWin::slotContinue()
    00521 {
    00522   m_mode = Continue;
    00523   leaveSession();
    00524 }
    00525
    00526 void KJSDebugWin::slotStop()
    00527 {
    00528   m_mode = Stop;
    00529   while (!m_execStates.isEmpty())
    00530     leaveSession();
    00531 }
    00532
    00533 void KJSDebugWin::slotBreakNext()
    00534 {
    00535   m_mode = Step;
    00536 }
    00537
    00538 void KJSDebugWin::slotToggleBreakpoint(int lineno)
    00539 {
    00540   if (m_sourceSel->currentItem() < 0)
    00541     return;
    00542
    00543   SourceFile *sourceFile = m_sourceSelFiles.at(m_sourceSel->currentItem());
    00544
    00545   // Find the source fragment containing the selected line (if any)
    00546   int sourceId = -1;
    00547   int highestBaseLine = -1;
    00548   QMap<int,SourceFragment*>::Iterator it;
    00549
    00550   for (it = m_sourceFragments.begin(); it != m_sourceFragments.end(); ++it) {
    00551     SourceFragment *sourceFragment = it.data();
    00552     if (sourceFragment &&
    00553     sourceFragment->sourceFile == sourceFile &&
    00554     sourceFragment->baseLine <= lineno &&
    00555     sourceFragment->baseLine > highestBaseLine) {
    00556
    00557     sourceId = sourceFragment->sourceId;
    00558     highestBaseLine = sourceFragment->baseLine;
    00559     }
    00560   }
    00561
    00562   if (sourceId < 0)
    00563     return;
    00564
    00565   // Update the source code display with the appropriate icon
    00566   int fragmentLineno = lineno-highestBaseLine+1;
    00567   if (!setBreakpoint(sourceId,fragmentLineno)) // was already set
    00568     deleteBreakpoint(sourceId,fragmentLineno);
    00569
    00570   m_sourceDisplay->updateContents();
    00571 }
    00572
    00573 void KJSDebugWin::slotShowFrame(int frameno)
    00574 {
    00575   if (frameno < 0 || frameno >= m_execsCount)
    00576     return;
    00577
    00578   Context ctx = m_execs[frameno]->context();
    00579   setSourceLine(ctx.sourceId(),ctx.curStmtFirstLine());
    00580 }
    00581
    00582 void KJSDebugWin::slotSourceSelected(int sourceSelIndex)
    00583 {
    00584   // A source file has been selected from the drop-down list - display the file
    00585   if (sourceSelIndex < 0 || sourceSelIndex >= (int)m_sourceSel->count())
    00586     return;
    00587   SourceFile *sourceFile = m_sourceSelFiles.at(sourceSelIndex);
    00588   displaySourceFile(sourceFile,true);
    00589
    00590   // If the currently selected context is in the current source file, then hilight
    00591   // the line it's on.
    00592   if (m_contextList->currentItem() >= 0) {
    00593     Context ctx = m_execs[m_contextList->currentItem()]->context();
    00594     if (m_sourceFragments[ctx.sourceId()]->sourceFile == m_sourceSelFiles.at(sourceSelIndex))
    00595       setSourceLine(ctx.sourceId(),ctx.curStmtFirstLine());
    00596   }
    00597 }
    00598
    00599 void KJSDebugWin::slotEval()
    00600 {
    00601   // Work out which execution state to use. If we're currently in a debugging session,
    00602   // use the current context - otherwise, use the global execution state from the interpreter
    00603   // corresponding to the currently displayed source file.
    00604   ExecState *exec;
    00605   Object thisobj;
    00606   if (m_execStates.isEmpty()) {
    00607     if (m_sourceSel->currentItem() < 0)
    00608       return;
    00609     SourceFile *sourceFile = m_sourceSelFiles.at(m_sourceSel->currentItem());
    00610     if (!sourceFile->interpreter)
    00611       return;
    00612     exec = sourceFile->interpreter->globalExec();
    00613     thisobj = exec->interpreter()->globalObject();
    00614   }
    00615   else {
    00616     exec = m_execStates.top();
    00617     thisobj = exec->context().thisValue();
    00618   }
    00619
    00620   // Evaluate the js code from m_evalEdit
    00621   UString code(m_evalEdit->code());
    00622   QString msg;
    00623
    00624   KJSCPUGuard guard;
    00625   guard.start();
    00626
    00627   Interpreter *interp = exec->interpreter();
    00628
    00629   Object obj = Object::dynamicCast(interp->globalObject().get(exec, "eval"));
    00630   List args;
    00631   args.append(String(code));
    00632
    00633   m_evalDepth++;
    00634   Value retval = obj.call(exec, thisobj, args);
    00635   m_evalDepth--;
    00636   guard.stop();
    00637
    00638   // Print the return value or exception message to the console
    00639   if (exec->hadException()) {
    00640     Value exc = exec->exception();
    00641     exec->clearException();
    00642     msg = "Exception: " + exc.toString(interp->globalExec()).qstring();
    00643   }
    00644   else {
    00645     msg = retval.toString(interp->globalExec()).qstring();
    00646   }
    00647
    00648   m_evalEdit->insert(msg+"\n");
    00649   updateContextList();
    00650 }
    00651
    00652 void KJSDebugWin::closeEvent(QCloseEvent *e)
    00653 {
    00654   while (!m_execStates.isEmpty()) // ### not sure if this will work
    00655     leaveSession();
    00656   return QWidget::closeEvent(e);
    00657 }
    00658
    00659 bool KJSDebugWin::eventFilter(QObject *o, QEvent *e)
    00660 {
    00661   switch (e->type()) {
    00662   case QEvent::MouseButtonPress:
    00663   case QEvent::MouseButtonRelease:
    00664   case QEvent::MouseButtonDblClick:
    00665   case QEvent::MouseMove:
    00666   case QEvent::KeyPress:
    00667   case QEvent::KeyRelease:
    00668   case QEvent::Destroy:
    00669   case QEvent::Close:
    00670   case QEvent::Quit:
    00671     while (o->parent())
    00672       o = o->parent();
    00673     if (o == this)
    00674       return QWidget::eventFilter(o,e);
    00675     else
    00676       return true;
    00677     break;
    00678   default:
    00679     return QWidget::eventFilter(o,e);
    00680   }
    00681 }
    00682
    00683 void KJSDebugWin::disableOtherWindows()
    00684 {
    00685   QWidgetList *widgets = QApplication::allWidgets();
    00686   QWidgetListIt it(*widgets);
    00687   for (; it.current(); ++it)
    00688     it.current()->installEventFilter(this);
    00689 }
    00690
    00691 void KJSDebugWin::enableOtherWindows()
    00692 {
    00693   QWidgetList *widgets = QApplication::allWidgets();
    00694   QWidgetListIt it(*widgets);
    00695   for (; it.current(); ++it)
    00696     it.current()->removeEventFilter(this);
    00697 }
    00698
    00699 bool KJSDebugWin::sourceParsed(KJS::ExecState *exec, int sourceId,
    00700                                const KJS::UString &source, int errorLine)
    00701 {
    00702   // Work out which source file this fragment is in
    00703   SourceFile *sourceFile = 0;
    00704   if (!m_nextSourceUrl.isEmpty())
    00705     sourceFile = getSourceFile(exec->interpreter(),m_nextSourceUrl);
    00706
    00707   int index;
    00708   if (!sourceFile) {
    00709     index = m_sourceSel->count();
    00710     if (!m_nextSourceUrl.isEmpty()) {
    00711
    00712       QString code = source.qstring();
    00713       KParts::ReadOnlyPart *part = static_cast<ScriptInterpreter*>(exec->interpreter())->part();
    00714       if (m_nextSourceUrl == part->url().url()) {
    00715     // Only store the code here if it's not from the part's html page... in that
    00716     // case we can get it from KHTMLPageCache
    00717     code = QString::null;
    00718       }
    00719
    00720       sourceFile = new SourceFile(m_nextSourceUrl,code,exec->interpreter());
    00721       setSourceFile(exec->interpreter(),m_nextSourceUrl,sourceFile);
    00722       m_sourceSelFiles.append(sourceFile);
    00723       m_sourceSel->insertItem(m_nextSourceUrl);
    00724     }
    00725     else {
    00726       // Sourced passed from somewhere else (possibly an eval call)... we don't know the url,
    00727       // but we still know the interpreter
    00728       sourceFile = new SourceFile("(unknown)",source.qstring(),exec->interpreter());
    00729       m_sourceSelFiles.append(sourceFile);
    00730       m_sourceSel->insertItem("???");
    00731     }
    00732   }
    00733   else {
    00734     for (index = 0; index < m_sourceSel->count(); index++) {
    00735       if (m_sourceSelFiles.at(index) == sourceFile)
    00736     break;
    00737     }
    00738     assert(index < m_sourceSel->count());
    00739   }
    00740
    00741   SourceFragment *sf = new SourceFragment(sourceId,m_nextSourceBaseLine,errorLine,sourceFile);
    00742   m_sourceFragments[sourceId] = sf;
    00743
    00744   if (m_sourceSel->currentItem() < 0)
    00745     m_sourceSel->setCurrentItem(index);
    00746
    00747   if (m_sourceSel->currentItem() == index) {
    00748     displaySourceFile(sourceFile,true);
    00749   }
    00750
    00751   m_nextSourceBaseLine = 1;
    00752   m_nextSourceUrl = "";
    00753
    00754   return (m_mode != Stop);
    00755 }
    00756
    00757 bool KJSDebugWin::sourceUnused(KJS::ExecState *exec, int sourceId)
    00758 {
    00759   // Verify that there aren't any contexts on the stack using the given sourceId
    00760   // This should never be the case because this function is only called when
    00761   // the interpreter has deleted all Node objects for the source.
    00762   for (int e = 0; e < m_execsCount; e++)
    00763     assert(m_execs[e]->context().sourceId() != sourceId);
    00764
    00765   // Now remove the fragment (and the SourceFile, if it was the last fragment in that file)
    00766   SourceFragment *fragment = m_sourceFragments[sourceId];
    00767   if (fragment) {
    00768     m_sourceFragments.erase(sourceId);
    00769
    00770     SourceFile *sourceFile = fragment->sourceFile;
    00771     if (sourceFile->hasOneRef()) {
    00772       for (int i = 0; i < m_sourceSel->count(); i++) {
    00773     if (m_sourceSelFiles.at(i) == sourceFile) {
    00774       m_sourceSel->removeItem(i);
    00775       m_sourceSelFiles.remove(i);
    00776       break;
    00777     }
    00778       }
    00779       removeSourceFile(exec->interpreter(),sourceFile->url);
    00780     }
    00781     delete fragment;
    00782   }
    00783
    00784   return (m_mode != Stop);
    00785 }
    00786
    00787 bool KJSDebugWin::exception(ExecState *exec, const Value &value, bool inTryCatch)
    00788 {
    00789   assert(value.isValid());
    00790
    00791   // Ignore exceptions that will be caught by the script
    00792   if (inTryCatch)
    00793     return true;
    00794
    00795   KParts::ReadOnlyPart *part = static_cast<ScriptInterpreter*>(exec->interpreter())->part();
    00796   KHTMLPart *khtmlpart = ::qt_cast<KHTMLPart*>(part);
    00797   if (khtmlpart && !khtmlpart->settings()->isJavaScriptErrorReportingEnabled())
    00798     return true;
    00799
    00800   QWidget *dlgParent = (m_evalDepth == 0) ? (QWidget*)part->widget() : (QWidget*)this;
    00801
    00802   QString exceptionMsg = value.toString(exec).qstring();
    00803
    00804   // Syntax errors are a special case. For these we want to display the url & lineno,
    00805   // which isn't included in the exception messeage. So we work it out from the values
    00806   // passed to sourceParsed()
    00807   Object valueObj = Object::dynamicCast(value);
    00808   Object syntaxError = exec->interpreter()->builtinSyntaxError();
    00809   if (valueObj.isValid() && valueObj.get(exec,"constructor").imp() == syntaxError.imp()) {
    00810     Value sidValue = valueObj.get(exec,"sid");
    00811     if (sidValue.isA(NumberType)) { // sid is not set for Function() constructor
    00812       int sourceId = (int)sidValue.toNumber(exec);
    00813       assert(m_sourceFragments[sourceId]);
    00814       exceptionMsg = i18n("Parse error at %1 line %2")
    00815              .arg(m_sourceFragments[sourceId]->sourceFile->url)
    00816              .arg(m_sourceFragments[sourceId]->baseLine+m_sourceFragments[sourceId]->errorLine-1);
    00817     }
    00818   }
    00819
    00820   bool dontShowAgain = false;
    00821   if (m_execsCount == 0) {
    00822     // An exception occurred and we're not currently executing any code... this can
    00823     // happen in some cases e.g. a parse error, or native code accessing funcitons like
    00824     // Object::put()
    00825     QString msg = i18n("An error occurred while attempting to run a script on this page.\n\n%1")
    00826           .arg(exceptionMsg);
    00827     KJSErrorDialog dlg(dlgParent,msg,false);
    00828     dlg.exec();
    00829     dontShowAgain = dlg.dontShowAgain();
    00830   }
    00831   else {
    00832     Context ctx = m_execs[m_execsCount-1]->context();
    00833     SourceFragment *sourceFragment = m_sourceFragments[ctx.sourceId()];
    00834     QString msg = i18n("An error occurred while attempting to run a script on this page.\n\n%1 line %2:\n%3")
    00835           .arg(KStringHandler::rsqueeze( sourceFragment->sourceFile->url,80),
    00836           QString::number( sourceFragment->baseLine+ctx.curStmtFirstLine()-1),
    00837           exceptionMsg);
    00838
    00839     KJSErrorDialog dlg(dlgParent,msg,true);
    00840     dlg.exec();
    00841     dontShowAgain = dlg.dontShowAgain();
    00842
    00843     if (dlg.debugSelected()) {
    00844       m_mode = Next;
    00845       m_steppingDepth = m_execsCount-1;
    00846       enterSession(exec);
    00847     }
    00848   }
    00849
    00850   if (dontShowAgain) {
    00851     KConfig *config = kapp->config();
    00852     KConfigGroupSaver saver(config,QString::fromLatin1("Java/JavaScript Settings"));
    00853     config->writeEntry("ReportJavaScriptErrors",QVariant(false,0));
    00854     config->sync();
    00855     QByteArray data;
    00856     kapp->dcopClient()->send( "konqueror*", "KonquerorIface", "reparseConfiguration()", data );
    00857   }
    00858
    00859   return (m_mode != Stop);
    00860 }
    00861
    00862 bool KJSDebugWin::atStatement(KJS::ExecState *exec)
    00863 {
    00864   assert(m_execsCount > 0);
    00865   assert(m_execs[m_execsCount-1] == exec);
    00866   checkBreak(exec);
    00867   return (m_mode != Stop);
    00868 }
    00869
    00870 bool KJSDebugWin::enterContext(ExecState *exec)
    00871 {
    00872   if (m_execsCount >= m_execsAlloc) {
    00873     m_execsAlloc += 10;
    00874     m_execs = (ExecState**)realloc(m_execs,m_execsAlloc*sizeof(ExecState*));
    00875   }
    00876   m_execs[m_execsCount++] = exec;
    00877
    00878   if (m_mode == Step)
    00879     m_steppingDepth = m_execsCount-1;
    00880
    00881   checkBreak(exec);
    00882   return (m_mode != Stop);
    00883 }
    00884
    00885 bool KJSDebugWin::exitContext(ExecState *exec, const Completion &/*completion*/)
    00886 {
    00887   assert(m_execsCount > 0);
    00888   assert(m_execs[m_execsCount-1] == exec);
    00889
    00890   checkBreak(exec);
    00891
    00892   m_execsCount--;
    00893   if (m_steppingDepth > m_execsCount-1)
    00894     m_steppingDepth = m_execsCount-1;
    00895   if (m_execsCount == 0)
    00896     updateContextList();
    00897
    00898   return (m_mode != Stop);
    00899 }
    00900
    00901 void KJSDebugWin::displaySourceFile(SourceFile *sourceFile, bool forceRefresh)
    00902 {
    00903   if (m_curSourceFile == sourceFile && !forceRefresh)
    00904     return;
    00905   sourceFile->ref();
    00906   m_sourceDisplay->setSource(sourceFile);
    00907   if (m_curSourceFile)
    00908      m_curSourceFile->deref();
    00909   m_curSourceFile = sourceFile;
    00910 }
    00911
    00912 void KJSDebugWin::setSourceLine(int sourceId, int lineno)
    00913 {
    00914   SourceFragment *source = m_sourceFragments[sourceId];
    00915   if (!source)
    00916     return;
    00917
    00918   SourceFile *sourceFile = source->sourceFile;
    00919   if (m_curSourceFile != source->sourceFile) {
    00920       for (int i = 0; i < m_sourceSel->count(); i++)
    00921     if (m_sourceSelFiles.at(i) == sourceFile)
    00922       m_sourceSel->setCurrentItem(i);
    00923       displaySourceFile(sourceFile,false);
    00924   }
    00925   m_sourceDisplay->setCurrentLine(source->baseLine+lineno-2);
    00926 }
    00927
    00928 void KJSDebugWin::setNextSourceInfo(QString url, int baseLine)
    00929 {
    00930   m_nextSourceUrl = url;
    00931   m_nextSourceBaseLine = baseLine;
    00932 }
    00933
    00934 void KJSDebugWin::sourceChanged(Interpreter *interpreter, QString url)
    00935 {
    00936   SourceFile *sourceFile = getSourceFile(interpreter,url);
    00937   if (sourceFile && m_curSourceFile == sourceFile)
    00938     displaySourceFile(sourceFile,true);
    00939 }
    00940
    00941 void KJSDebugWin::clearInterpreter(Interpreter *interpreter)
    00942 {
    00943   QMap<int,SourceFragment*>::Iterator it;
    00944
    00945   for (it = m_sourceFragments.begin(); it != m_sourceFragments.end(); ++it)
    00946     if (it.data() && it.data()->sourceFile->interpreter == interpreter)
    00947       it.data()->sourceFile->interpreter = 0;
    00948 }
    00949
    00950 SourceFile *KJSDebugWin::getSourceFile(Interpreter *interpreter, QString url)
    00951 {
    00952   QString key = QString("%1|%2").arg((long)interpreter).arg(url);
    00953   return m_sourceFiles[key];
    00954 }
    00955
    00956 void KJSDebugWin::setSourceFile(Interpreter *interpreter, QString url, SourceFile *sourceFile)
    00957 {
    00958   QString key = QString("%1|%2").arg((long)interpreter).arg(url);
    00959   m_sourceFiles[key] = sourceFile;
    00960 }
    00961
    00962 void KJSDebugWin::removeSourceFile(Interpreter *interpreter, QString url)
    00963 {
    00964   QString key = QString("%1|%2").arg((long)interpreter).arg(url);
    00965   m_sourceFiles.remove(key);
    00966 }
    00967
    00968 void KJSDebugWin::checkBreak(ExecState *exec)
    00969 {
    00970   if (m_breakpointCount > 0) {
    00971     Context ctx = m_execs[m_execsCount-1]->context();
    00972     if (haveBreakpoint(ctx.sourceId(),ctx.curStmtFirstLine(),ctx.curStmtLastLine())) {
    00973       m_mode = Next;
    00974       m_steppingDepth = m_execsCount-1;
    00975     }
    00976   }
    00977
    00978   if ((m_mode == Step || m_mode == Next) && m_steppingDepth == m_execsCount-1)
    00979     enterSession(exec);
    00980 }
    00981
    00982 void KJSDebugWin::enterSession(ExecState *exec)
    00983 {
    00984   // This "enters" a new debugging session, i.e. enables usage of the debugging window
    00985   // It re-enters the qt event loop here, allowing execution of other parts of the
    00986   // program to continue while the script is stopped. We have to be a bit careful here,
    00987   // i.e. make sure the user can't quit the app, and disable other event handlers which
    00988   // could interfere with the debugging session.
    00989   if (!isVisible())
    00990     show();
    00991
    00992   m_mode = Continue;
    00993
    00994   if (m_execStates.isEmpty()) {
    00995     disableOtherWindows();
    00996     m_nextAction->setEnabled(true);
    00997     m_stepAction->setEnabled(true);
    00998     m_continueAction->setEnabled(true);
    00999     m_stopAction->setEnabled(true);
    01000     m_breakAction->setEnabled(false);
    01001   }
    01002   m_execStates.push(exec);
    01003
    01004   updateContextList();
    01005
    01006   qApp->enter_loop(); // won't return until leaveSession() is called
    01007 }
    01008
    01009 void KJSDebugWin::leaveSession()
    01010 {
    01011   // Disables debugging for this window and returns to execute the rest of the script
    01012   // (or aborts execution, if the user pressed stop). When this returns, the program
    01013   // will exit the qt event loop, i.e. return to whatever processing was being done
    01014   // before the debugger was stopped.
    01015   assert(!m_execStates.isEmpty());
    01016
    01017   m_execStates.pop();
    01018
    01019   if (m_execStates.isEmpty()) {
    01020     m_nextAction->setEnabled(false);
    01021     m_stepAction->setEnabled(false);
    01022     m_continueAction->setEnabled(false);
    01023     m_stopAction->setEnabled(false);
    01024     m_breakAction->setEnabled(true);
    01025     m_sourceDisplay->setCurrentLine(-1);
    01026     enableOtherWindows();
    01027   }
    01028
    01029   qApp->exit_loop();
    01030 }
    01031
    01032 void KJSDebugWin::updateContextList()
    01033 {
    01034   disconnect(m_contextList,SIGNAL(highlighted(int)),this,SLOT(slotShowFrame(int)));
    01035
    01036   m_contextList->clear();
    01037   for (int i = 0; i < m_execsCount; i++)
    01038     m_contextList->insertItem(contextStr(m_execs[i]->context()));
    01039
    01040   if (m_execsCount > 0) {
    01041     m_contextList->setSelected(m_execsCount-1, true);
    01042     Context ctx = m_execs[m_execsCount-1]->context();
    01043     setSourceLine(ctx.sourceId(),ctx.curStmtFirstLine());
    01044   }
    01045
    01046   connect(m_contextList,SIGNAL(highlighted(int)),this,SLOT(slotShowFrame(int)));
    01047 }
    01048
    01049 QString KJSDebugWin::contextStr(const Context &ctx)
    01050 {
    01051   QString str = "";
    01052   SourceFragment *sourceFragment = m_sourceFragments[ctx.sourceId()];
    01053   QString url = sourceFragment->sourceFile->url;
    01054   int fileLineno = sourceFragment->baseLine+ctx.curStmtFirstLine()-1;
    01055
    01056   switch (ctx.codeType()) {
    01057   case GlobalCode:
    01058     str = QString("Global code at %1:%2").arg(url).arg(fileLineno);
    01059     break;
    01060   case EvalCode:
    01061     str = QString("Eval code at %1:%2").arg(url).arg(fileLineno);
    01062     break;
    01063   case FunctionCode:
    01064     if (!ctx.functionName().isNull())
    01065       str = QString("%1() at %2:%3").arg(ctx.functionName().qstring()).arg(url).arg(fileLineno);
    01066     else
    01067       str = QString("Anonymous function at %1:%2").arg(url).arg(fileLineno);
    01068     break;
    01069   }
    01070
    01071   return str;
    01072 }
    01073
    01074 bool KJSDebugWin::setBreakpoint(int sourceId, int lineno)
    01075 {
    01076   if (haveBreakpoint(sourceId,lineno,lineno))
    01077     return false;
    01078
    01079   m_breakpointCount++;
    01080   m_breakpoints = static_cast<Breakpoint*>(realloc(m_breakpoints,
    01081                            m_breakpointCount*sizeof(Breakpoint)));
    01082   m_breakpoints[m_breakpointCount-1].sourceId = sourceId;
    01083   m_breakpoints[m_breakpointCount-1].lineno = lineno;
    01084
    01085   return true;
    01086 }
    01087
    01088 bool KJSDebugWin::deleteBreakpoint(int sourceId, int lineno)
    01089 {
    01090   for (int i = 0; i < m_breakpointCount; i++) {
    01091     if (m_breakpoints[i].sourceId == sourceId && m_breakpoints[i].lineno == lineno) {
    01092
    01093       memmove(m_breakpoints+i,m_breakpoints+i+1,(m_breakpointCount-i-1)*sizeof(Breakpoint));
    01094       m_breakpointCount--;
    01095       m_breakpoints = static_cast<Breakpoint*>(realloc(m_breakpoints,
    01096                                m_breakpointCount*sizeof(Breakpoint)));
    01097       return true;
    01098     }
    01099   }
    01100
    01101   return false;
    01102 }
    01103
    01104 bool KJSDebugWin::haveBreakpoint(SourceFile *sourceFile, int line0, int line1)
    01105 {
    01106   for (int i = 0; i < m_breakpointCount; i++) {
    01107     int sourceId = m_breakpoints[i].sourceId;
    01108     int lineno = m_breakpoints[i].lineno;
    01109     if (m_sourceFragments.contains(sourceId) &&
    01110         m_sourceFragments[sourceId]->sourceFile == sourceFile) {
    01111       int absLineno = m_sourceFragments[sourceId]->baseLine+lineno-1;
    01112       if (absLineno >= line0 && absLineno <= line1)
    01113     return true;
    01114     }
    01115   }
    01116
    01117   return false;
    01118 }
    01119
    01120 #include "kjs_debugwin.moc"
    01121
    01122 #endif // KJS_DEBUGGER

This file is part of the documentation for khtml Library Version 3.4.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat Jun 11 20:33:17 2005 by doxygen 1.4.1 written by Dimitri van Heesch, © 1997-2003