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.3.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Oct 8 11:16:11 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003