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.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Sep 29 09:42:28 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003