00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
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);
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(¶, &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
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
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
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
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
00470 m_breakAction->plug(debugMenu);
00471
00472 m_nextAction->plug(toolBar());
00473 m_stepAction->plug(toolBar());
00474 m_continueAction->plug(toolBar());
00475
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
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
00565 int fragmentLineno = lineno-highestBaseLine+1;
00566 if (!setBreakpoint(sourceId,fragmentLineno))
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
00584 if (sourceSelIndex < 0 || sourceSelIndex >= (int)m_sourceSel->count())
00585 return;
00586 SourceFile *sourceFile = m_sourceSelFiles.at(sourceSelIndex);
00587 displaySourceFile(sourceFile,true);
00588
00589
00590
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
00601
00602
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
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
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())
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
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
00715
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
00726
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
00759
00760
00761 for (int e = 0; e < m_execsCount; e++)
00762 assert(m_execs[e]->context().sourceId() != sourceId);
00763
00764
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
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
00803
00804
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)) {
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
00821
00822
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 &)
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
00983
00984
00985
00986
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();
01005 }
01006
01007 void KJSDebugWin::leaveSession()
01008 {
01009
01010
01011
01012
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