khtml Library API Documentation

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