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 #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 = 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"),"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"));
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       KHTMLPart *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   KHTMLPart *part = static_cast<ScriptInterpreter*>(exec->interpreter())->part();
00796   if (!part->settings()->isJavaScriptErrorReportingEnabled())
00797     return true;
00798 
00799   QWidget *dlgParent = (m_evalDepth == 0) ? (QWidget*)part->view() : (QWidget*)this;
00800 
00801   QString exceptionMsg = value.toString(exec).qstring();
00802 
00803   // Syntax errors are a special case. For these we want to display the url & lineno,
00804   // which isn't included in the exception messeage. So we work it out from the values
00805   // passed to sourceParsed()
00806   Object valueObj = Object::dynamicCast(value);
00807   Object syntaxError = exec->interpreter()->builtinSyntaxError();
00808   if (valueObj.isValid() && valueObj.get(exec,"constructor").imp() == syntaxError.imp()) {
00809     Value sidValue = valueObj.get(exec,"sid");
00810     if (sidValue.isA(NumberType)) { // sid is not set for Function() constructor
00811       int sourceId = (int)sidValue.toNumber(exec);
00812       assert(m_sourceFragments[sourceId]);
00813       exceptionMsg = i18n("Parse error at %1 line %2")
00814              .arg(m_sourceFragments[sourceId]->sourceFile->url)
00815              .arg(m_sourceFragments[sourceId]->baseLine+m_sourceFragments[sourceId]->errorLine-1);
00816     }
00817   }
00818 
00819   bool dontShowAgain = false;
00820   if (m_execsCount == 0) {
00821     // An exception occurred and we're not currently executing any code... this can
00822     // happen in some cases e.g. a parse error, or native code accessing funcitons like
00823     // Object::put()
00824     QString msg = i18n("An error occurred while attempting to run a script on this page.\n\n%1")
00825           .arg(exceptionMsg);
00826     KJSErrorDialog dlg(dlgParent,msg,false);
00827     dlg.exec();
00828     dontShowAgain = dlg.dontShowAgain();
00829   }
00830   else {
00831     Context ctx = m_execs[m_execsCount-1]->context();
00832     SourceFragment *sourceFragment = m_sourceFragments[ctx.sourceId()];
00833     QString msg = i18n("An error occurred while attempting to run a script on this page.\n\n%1 line %2:\n%3")
00834           .arg(KStringHandler::rsqueeze( sourceFragment->sourceFile->url,80),
00835           QString::number( sourceFragment->baseLine+ctx.curStmtFirstLine()-1),
00836           exceptionMsg);
00837 
00838     KJSErrorDialog dlg(dlgParent,msg,true);
00839     dlg.exec();
00840     dontShowAgain = dlg.dontShowAgain();
00841 
00842     if (dlg.debugSelected()) {
00843       m_mode = Next;
00844       m_steppingDepth = m_execsCount-1;
00845       enterSession(exec);
00846     }
00847   }
00848 
00849   if (dontShowAgain) {
00850     KConfig *config = kapp->config();
00851     KConfigGroupSaver saver(config,QString::fromLatin1("Java/JavaScript Settings"));
00852     config->writeEntry("ReportJavaScriptErrors",QVariant(false,0));
00853     config->sync();
00854     QByteArray data;
00855     kapp->dcopClient()->send( "konqueror*", "KonquerorIface", "reparseConfiguration()", data );
00856   }
00857 
00858   return (m_mode != Stop);
00859 }
00860 
00861 bool KJSDebugWin::atStatement(KJS::ExecState *exec)
00862 {
00863   assert(m_execsCount > 0);
00864   assert(m_execs[m_execsCount-1] == exec);
00865   checkBreak(exec);
00866   return (m_mode != Stop);
00867 }
00868 
00869 bool KJSDebugWin::enterContext(ExecState *exec)
00870 {
00871   if (m_execsCount >= m_execsAlloc) {
00872     m_execsAlloc += 10;
00873     m_execs = (ExecState**)realloc(m_execs,m_execsAlloc*sizeof(ExecState*));
00874   }
00875   m_execs[m_execsCount++] = exec;
00876 
00877   if (m_mode == Step)
00878     m_steppingDepth = m_execsCount-1;
00879 
00880   checkBreak(exec);
00881   return (m_mode != Stop);
00882 }
00883 
00884 bool KJSDebugWin::exitContext(ExecState *exec, const Completion &/*completion*/)
00885 {
00886   assert(m_execsCount > 0);
00887   assert(m_execs[m_execsCount-1] == exec);
00888 
00889   checkBreak(exec);
00890 
00891   m_execsCount--;
00892   if (m_steppingDepth > m_execsCount-1)
00893     m_steppingDepth = m_execsCount-1;
00894   if (m_execsCount == 0)
00895     updateContextList();
00896 
00897   return (m_mode != Stop);
00898 }
00899 
00900 void KJSDebugWin::displaySourceFile(SourceFile *sourceFile, bool forceRefresh)
00901 {
00902   if (m_curSourceFile == sourceFile && !forceRefresh)
00903     return;
00904   sourceFile->ref();
00905   m_sourceDisplay->setSource(sourceFile);
00906   if (m_curSourceFile)
00907      m_curSourceFile->deref();
00908   m_curSourceFile = sourceFile;
00909 }
00910 
00911 void KJSDebugWin::setSourceLine(int sourceId, int lineno)
00912 {
00913   SourceFragment *source = m_sourceFragments[sourceId];
00914   if (!source)
00915     return;
00916 
00917   SourceFile *sourceFile = source->sourceFile;
00918   if (m_curSourceFile != source->sourceFile) {
00919       for (int i = 0; i < m_sourceSel->count(); i++)
00920     if (m_sourceSelFiles.at(i) == sourceFile)
00921       m_sourceSel->setCurrentItem(i);
00922       displaySourceFile(sourceFile,false);
00923   }
00924   m_sourceDisplay->setCurrentLine(source->baseLine+lineno-2);
00925 }
00926 
00927 void KJSDebugWin::setNextSourceInfo(QString url, int baseLine)
00928 {
00929   m_nextSourceUrl = url;
00930   m_nextSourceBaseLine = baseLine;
00931 }
00932 
00933 void KJSDebugWin::sourceChanged(Interpreter *interpreter, QString url)
00934 {
00935   SourceFile *sourceFile = getSourceFile(interpreter,url);
00936   if (sourceFile && m_curSourceFile == sourceFile)
00937     displaySourceFile(sourceFile,true);
00938 }
00939 
00940 void KJSDebugWin::clearInterpreter(Interpreter *interpreter)
00941 {
00942   QMap<int,SourceFragment*>::Iterator it;
00943 
00944   for (it = m_sourceFragments.begin(); it != m_sourceFragments.end(); ++it)
00945     if (it.data() && it.data()->sourceFile->interpreter == interpreter)
00946       it.data()->sourceFile->interpreter = 0;
00947 }
00948 
00949 SourceFile *KJSDebugWin::getSourceFile(Interpreter *interpreter, QString url)
00950 {
00951   QString key = QString("%1|%2").arg((long)interpreter).arg(url);
00952   return m_sourceFiles[key];
00953 }
00954 
00955 void KJSDebugWin::setSourceFile(Interpreter *interpreter, QString url, SourceFile *sourceFile)
00956 {
00957   QString key = QString("%1|%2").arg((long)interpreter).arg(url);
00958   m_sourceFiles[key] = sourceFile;
00959 }
00960 
00961 void KJSDebugWin::removeSourceFile(Interpreter *interpreter, QString url)
00962 {
00963   QString key = QString("%1|%2").arg((long)interpreter).arg(url);
00964   m_sourceFiles.remove(key);
00965 }
00966 
00967 void KJSDebugWin::checkBreak(ExecState *exec)
00968 {
00969   if (m_breakpointCount > 0) {
00970     Context ctx = m_execs[m_execsCount-1]->context();
00971     if (haveBreakpoint(ctx.sourceId(),ctx.curStmtFirstLine(),ctx.curStmtLastLine())) {
00972       m_mode = Next;
00973       m_steppingDepth = m_execsCount-1;
00974     }
00975   }
00976 
00977   if ((m_mode == Step || m_mode == Next) && m_steppingDepth == m_execsCount-1)
00978     enterSession(exec);
00979 }
00980 
00981 void KJSDebugWin::enterSession(ExecState *exec)
00982 {
00983   // This "enters" a new debugging session, i.e. enables usage of the debugging window
00984   // It re-enters the qt event loop here, allowing execution of other parts of the
00985   // program to continue while the script is stopped. We have to be a bit careful here,
00986   // i.e. make sure the user can't quit the app, and disable other event handlers which
00987   // could interfere with the debugging session.
00988   if (!isVisible())
00989     show();
00990 
00991   m_mode = Continue;
00992 
00993   if (m_execStates.isEmpty()) {
00994     disableOtherWindows();
00995     m_nextAction->setEnabled(true);
00996     m_stepAction->setEnabled(true);
00997     m_continueAction->setEnabled(true);
00998     m_stopAction->setEnabled(true);
00999     m_breakAction->setEnabled(false);
01000   }
01001   m_execStates.push(exec);
01002 
01003   updateContextList();
01004 
01005   qApp->enter_loop(); // won't return until leaveSession() is called
01006 }
01007 
01008 void KJSDebugWin::leaveSession()
01009 {
01010   // Disables debugging for this window and returns to execute the rest of the script
01011   // (or aborts execution, if the user pressed stop). When this returns, the program
01012   // will exit the qt event loop, i.e. return to whatever processing was being done
01013   // before the debugger was stopped.
01014   assert(!m_execStates.isEmpty());
01015 
01016   m_execStates.pop();
01017 
01018   if (m_execStates.isEmpty()) {
01019     m_nextAction->setEnabled(false);
01020     m_stepAction->setEnabled(false);
01021     m_continueAction->setEnabled(false);
01022     m_stopAction->setEnabled(false);
01023     m_breakAction->setEnabled(true);
01024     m_sourceDisplay->setCurrentLine(-1);
01025     enableOtherWindows();
01026   }
01027 
01028   qApp->exit_loop();
01029 }
01030 
01031 void KJSDebugWin::updateContextList()
01032 {
01033   disconnect(m_contextList,SIGNAL(highlighted(int)),this,SLOT(slotShowFrame(int)));
01034 
01035   m_contextList->clear();
01036   for (int i = 0; i < m_execsCount; i++)
01037     m_contextList->insertItem(contextStr(m_execs[i]->context()));
01038 
01039   if (m_execsCount > 0) {
01040     m_contextList->setSelected(m_execsCount-1, true);
01041     Context ctx = m_execs[m_execsCount-1]->context();
01042     setSourceLine(ctx.sourceId(),ctx.curStmtFirstLine());
01043   }
01044 
01045   connect(m_contextList,SIGNAL(highlighted(int)),this,SLOT(slotShowFrame(int)));
01046 }
01047 
01048 QString KJSDebugWin::contextStr(const Context &ctx)
01049 {
01050   QString str = "";
01051   SourceFragment *sourceFragment = m_sourceFragments[ctx.sourceId()];
01052   QString url = sourceFragment->sourceFile->url;
01053   int fileLineno = sourceFragment->baseLine+ctx.curStmtFirstLine()-1;
01054 
01055   switch (ctx.codeType()) {
01056   case GlobalCode:
01057     str = QString("Global code at %1:%2").arg(url).arg(fileLineno);
01058     break;
01059   case EvalCode:
01060     str = QString("Eval code at %1:%2").arg(url).arg(fileLineno);
01061     break;
01062   case FunctionCode:
01063     if (!ctx.functionName().isNull())
01064       str = QString("%1() at %2:%3").arg(ctx.functionName().qstring()).arg(url).arg(fileLineno);
01065     else
01066       str = QString("Anonymous function at %1:%2").arg(url).arg(fileLineno);
01067     break;
01068   }
01069 
01070   return str;
01071 }
01072 
01073 bool KJSDebugWin::setBreakpoint(int sourceId, int lineno)
01074 {
01075   if (haveBreakpoint(sourceId,lineno,lineno))
01076     return false;
01077 
01078   m_breakpointCount++;
01079   m_breakpoints = static_cast<Breakpoint*>(realloc(m_breakpoints,
01080                            m_breakpointCount*sizeof(Breakpoint)));
01081   m_breakpoints[m_breakpointCount-1].sourceId = sourceId;
01082   m_breakpoints[m_breakpointCount-1].lineno = lineno;
01083 
01084   return true;
01085 }
01086 
01087 bool KJSDebugWin::deleteBreakpoint(int sourceId, int lineno)
01088 {
01089   for (int i = 0; i < m_breakpointCount; i++) {
01090     if (m_breakpoints[i].sourceId == sourceId && m_breakpoints[i].lineno == lineno) {
01091 
01092       memmove(m_breakpoints+i,m_breakpoints+i+1,(m_breakpointCount-i-1)*sizeof(Breakpoint));
01093       m_breakpointCount--;
01094       m_breakpoints = static_cast<Breakpoint*>(realloc(m_breakpoints,
01095                                m_breakpointCount*sizeof(Breakpoint)));
01096       return true;
01097     }
01098   }
01099 
01100   return false;
01101 }
01102 
01103 bool KJSDebugWin::haveBreakpoint(SourceFile *sourceFile, int line0, int line1)
01104 {
01105   for (int i = 0; i < m_breakpointCount; i++) {
01106     int sourceId = m_breakpoints[i].sourceId;
01107     int lineno = m_breakpoints[i].lineno;
01108     if (m_sourceFragments.contains(sourceId) &&
01109         m_sourceFragments[sourceId]->sourceFile == sourceFile) {
01110       int absLineno = m_sourceFragments[sourceId]->baseLine+lineno-1;
01111       if (absLineno >= line0 && absLineno <= line1)
01112     return true;
01113     }
01114   }
01115 
01116   return false;
01117 }
01118 
01119 #include "kjs_debugwin.moc"
01120 
01121 #endif // KJS_DEBUGGER
KDE Logo
This file is part of the documentation for khtml Library Version 3.3.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Feb 18 15:11:44 2005 by doxygen 1.3.9.1 written by Dimitri van Heesch, © 1997-2003