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 #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);
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 = ::qt_cast<KHTMLPart*>(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(¶, &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
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
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
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
00439 KPopupMenu *debugMenu = new KPopupMenu(this);
00440 menuBar()->insertItem("&Debug",debugMenu);
00441
00442 m_actionCollection = new KActionCollection(this);
00443 m_actionCollection->setInstance(this);
00444
00445
00446 KShortcut scNext = KShortcut(KKeySequence(KKey(Qt::Key_F12)));
00447 scNext.append(KKeySequence(KKey(Qt::Key_F10)));
00448 m_nextAction = new KAction(i18n("Next breakpoint","&Next"),"dbgnext",scNext,this,SLOT(slotNext()),
00449 m_actionCollection,"next");
00450 m_stepAction = new KAction(i18n("&Step"),"dbgstep",KShortcut(Qt::Key_F11),this,SLOT(slotStep()),
00451 m_actionCollection,"step");
00452
00453 KShortcut scCont = KShortcut(KKeySequence(KKey(Qt::Key_F5)));
00454 scCont.append(KKeySequence(KKey(Qt::Key_F9)));
00455 m_continueAction = new KAction(i18n("&Continue"),"dbgrun",scCont,this,SLOT(slotContinue()),
00456 m_actionCollection,"cont");
00457 m_stopAction = new KAction(i18n("St&op"),"stop",KShortcut(Qt::Key_F4),this,SLOT(slotStop()),
00458 m_actionCollection,"stop");
00459 m_breakAction = new KAction(i18n("&Break at Next Statement"),"dbgrunto",KShortcut(Qt::Key_F8),this,SLOT(slotBreakNext()),
00460 m_actionCollection,"breaknext");
00461
00462
00463 m_nextAction->setToolTip(i18n("Next breakpoint","Next"));
00464 m_stepAction->setToolTip(i18n("Step"));
00465 m_continueAction->setToolTip(i18n("Continue"));
00466 m_stopAction->setToolTip(i18n("Stop"));
00467 m_breakAction->setToolTip("Break at next Statement");
00468
00469 m_nextAction->setEnabled(false);
00470 m_stepAction->setEnabled(false);
00471 m_continueAction->setEnabled(false);
00472 m_stopAction->setEnabled(false);
00473 m_breakAction->setEnabled(true);
00474
00475 m_nextAction->plug(debugMenu);
00476 m_stepAction->plug(debugMenu);
00477 m_continueAction->plug(debugMenu);
00478
00479 m_breakAction->plug(debugMenu);
00480
00481 m_nextAction->plug(toolBar());
00482 m_stepAction->plug(toolBar());
00483 m_continueAction->plug(toolBar());
00484
00485 m_breakAction->plug(toolBar());
00486
00487 toolBar()->insertWidget(1,300,m_sourceSel);
00488 toolBar()->setItemAutoSized(1);
00489
00490 updateContextList();
00491 setMinimumSize(300,200);
00492 resize(600,450);
00493
00494 }
00495
00496 KJSDebugWin::~KJSDebugWin()
00497 {
00498 free(m_breakpoints);
00499 free(m_execs);
00500 }
00501
00502 KJSDebugWin *KJSDebugWin::createInstance()
00503 {
00504 assert(!kjs_html_debugger);
00505 kjs_html_debugger = new KJSDebugWin();
00506 return kjs_html_debugger;
00507 }
00508
00509 void KJSDebugWin::destroyInstance()
00510 {
00511 assert(kjs_html_debugger);
00512 kjs_html_debugger->hide();
00513 delete kjs_html_debugger;
00514 }
00515
00516 void KJSDebugWin::slotNext()
00517 {
00518 m_mode = Next;
00519 leaveSession();
00520 }
00521
00522 void KJSDebugWin::slotStep()
00523 {
00524 m_mode = Step;
00525 leaveSession();
00526 }
00527
00528 void KJSDebugWin::slotContinue()
00529 {
00530 m_mode = Continue;
00531 leaveSession();
00532 }
00533
00534 void KJSDebugWin::slotStop()
00535 {
00536 m_mode = Stop;
00537 while (!m_execStates.isEmpty())
00538 leaveSession();
00539 }
00540
00541 void KJSDebugWin::slotBreakNext()
00542 {
00543 m_mode = Step;
00544 }
00545
00546 void KJSDebugWin::slotToggleBreakpoint(int lineno)
00547 {
00548 if (m_sourceSel->currentItem() < 0)
00549 return;
00550
00551 SourceFile *sourceFile = m_sourceSelFiles.at(m_sourceSel->currentItem());
00552
00553
00554 int sourceId = -1;
00555 int highestBaseLine = -1;
00556 QMap<int,SourceFragment*>::Iterator it;
00557
00558 for (it = m_sourceFragments.begin(); it != m_sourceFragments.end(); ++it) {
00559 SourceFragment *sourceFragment = it.data();
00560 if (sourceFragment &&
00561 sourceFragment->sourceFile == sourceFile &&
00562 sourceFragment->baseLine <= lineno &&
00563 sourceFragment->baseLine > highestBaseLine) {
00564
00565 sourceId = sourceFragment->sourceId;
00566 highestBaseLine = sourceFragment->baseLine;
00567 }
00568 }
00569
00570 if (sourceId < 0)
00571 return;
00572
00573
00574 int fragmentLineno = lineno-highestBaseLine+1;
00575 if (!setBreakpoint(sourceId,fragmentLineno))
00576 deleteBreakpoint(sourceId,fragmentLineno);
00577
00578 m_sourceDisplay->updateContents();
00579 }
00580
00581 void KJSDebugWin::slotShowFrame(int frameno)
00582 {
00583 if (frameno < 0 || frameno >= m_execsCount)
00584 return;
00585
00586 Context ctx = m_execs[frameno]->context();
00587 setSourceLine(ctx.sourceId(),ctx.curStmtFirstLine());
00588 }
00589
00590 void KJSDebugWin::slotSourceSelected(int sourceSelIndex)
00591 {
00592
00593 if (sourceSelIndex < 0 || sourceSelIndex >= (int)m_sourceSel->count())
00594 return;
00595 SourceFile *sourceFile = m_sourceSelFiles.at(sourceSelIndex);
00596 displaySourceFile(sourceFile,true);
00597
00598
00599
00600 if (m_contextList->currentItem() >= 0) {
00601 Context ctx = m_execs[m_contextList->currentItem()]->context();
00602 if (m_sourceFragments[ctx.sourceId()]->sourceFile == m_sourceSelFiles.at(sourceSelIndex))
00603 setSourceLine(ctx.sourceId(),ctx.curStmtFirstLine());
00604 }
00605 }
00606
00607 void KJSDebugWin::slotEval()
00608 {
00609
00610
00611
00612 ExecState *exec;
00613 Object thisobj;
00614 if (m_execStates.isEmpty()) {
00615 if (m_sourceSel->currentItem() < 0)
00616 return;
00617 SourceFile *sourceFile = m_sourceSelFiles.at(m_sourceSel->currentItem());
00618 if (!sourceFile->interpreter)
00619 return;
00620 exec = sourceFile->interpreter->globalExec();
00621 thisobj = exec->interpreter()->globalObject();
00622 }
00623 else {
00624 exec = m_execStates.top();
00625 thisobj = exec->context().thisValue();
00626 }
00627
00628
00629 UString code(m_evalEdit->code());
00630 QString msg;
00631
00632 KJSCPUGuard guard;
00633 guard.start();
00634
00635 Interpreter *interp = exec->interpreter();
00636
00637 Object obj = Object::dynamicCast(interp->globalObject().get(exec, "eval"));
00638 List args;
00639 args.append(String(code));
00640
00641 m_evalDepth++;
00642 Value retval = obj.call(exec, thisobj, args);
00643 m_evalDepth--;
00644 guard.stop();
00645
00646
00647 if (exec->hadException()) {
00648 Value exc = exec->exception();
00649 exec->clearException();
00650 msg = "Exception: " + exc.toString(interp->globalExec()).qstring();
00651 }
00652 else {
00653 msg = retval.toString(interp->globalExec()).qstring();
00654 }
00655
00656 m_evalEdit->insert(msg+"\n");
00657 updateContextList();
00658 }
00659
00660 void KJSDebugWin::closeEvent(QCloseEvent *e)
00661 {
00662 while (!m_execStates.isEmpty())
00663 leaveSession();
00664 return QWidget::closeEvent(e);
00665 }
00666
00667 bool KJSDebugWin::eventFilter(QObject *o, QEvent *e)
00668 {
00669 switch (e->type()) {
00670 case QEvent::MouseButtonPress:
00671 case QEvent::MouseButtonRelease:
00672 case QEvent::MouseButtonDblClick:
00673 case QEvent::MouseMove:
00674 case QEvent::KeyPress:
00675 case QEvent::KeyRelease:
00676 case QEvent::Destroy:
00677 case QEvent::Close:
00678 case QEvent::Quit:
00679 while (o->parent())
00680 o = o->parent();
00681 if (o == this)
00682 return QWidget::eventFilter(o,e);
00683 else
00684 return true;
00685 break;
00686 default:
00687 return QWidget::eventFilter(o,e);
00688 }
00689 }
00690
00691 void KJSDebugWin::disableOtherWindows()
00692 {
00693 QWidgetList *widgets = QApplication::allWidgets();
00694 QWidgetListIt it(*widgets);
00695 for (; it.current(); ++it)
00696 it.current()->installEventFilter(this);
00697 }
00698
00699 void KJSDebugWin::enableOtherWindows()
00700 {
00701 QWidgetList *widgets = QApplication::allWidgets();
00702 QWidgetListIt it(*widgets);
00703 for (; it.current(); ++it)
00704 it.current()->removeEventFilter(this);
00705 }
00706
00707 bool KJSDebugWin::sourceParsed(KJS::ExecState *exec, int sourceId,
00708 const KJS::UString &source, int errorLine)
00709 {
00710
00711 SourceFile *sourceFile = 0;
00712 if (!m_nextSourceUrl.isEmpty())
00713 sourceFile = getSourceFile(exec->interpreter(),m_nextSourceUrl);
00714
00715 int index;
00716 if (!sourceFile) {
00717 index = m_sourceSel->count();
00718 if (!m_nextSourceUrl.isEmpty()) {
00719
00720 QString code = source.qstring();
00721 KParts::ReadOnlyPart *part = static_cast<ScriptInterpreter*>(exec->interpreter())->part();
00722 if (m_nextSourceUrl == part->url().url()) {
00723
00724
00725 code = QString::null;
00726 }
00727
00728 sourceFile = new SourceFile(m_nextSourceUrl,code,exec->interpreter());
00729 setSourceFile(exec->interpreter(),m_nextSourceUrl,sourceFile);
00730 m_sourceSelFiles.append(sourceFile);
00731 m_sourceSel->insertItem(m_nextSourceUrl);
00732 }
00733 else {
00734
00735
00736 sourceFile = new SourceFile("(unknown)",source.qstring(),exec->interpreter());
00737 m_sourceSelFiles.append(sourceFile);
00738 m_sourceSel->insertItem(QString::number(index) += "-???");
00739 }
00740 }
00741 else {
00742
00743
00744 if (!sourceFile->interpreter)
00745 sourceFile->interpreter = exec->interpreter();
00746 for (index = 0; index < m_sourceSel->count(); index++) {
00747 if (m_sourceSelFiles.at(index) == sourceFile)
00748 break;
00749 }
00750 assert(index < m_sourceSel->count());
00751 }
00752
00753 SourceFragment *sf = new SourceFragment(sourceId,m_nextSourceBaseLine,errorLine,sourceFile);
00754 m_sourceFragments[sourceId] = sf;
00755
00756 if (m_sourceSel->currentItem() < 0)
00757 m_sourceSel->setCurrentItem(index);
00758
00759 if (m_sourceSel->currentItem() == index) {
00760 displaySourceFile(sourceFile,true);
00761 }
00762
00763 m_nextSourceBaseLine = 1;
00764 m_nextSourceUrl = "";
00765
00766 return (m_mode != Stop);
00767 }
00768
00769 bool KJSDebugWin::sourceUnused(KJS::ExecState *exec, int sourceId)
00770 {
00771
00772
00773
00774 for (int e = 0; e < m_execsCount; e++)
00775 assert(m_execs[e]->context().sourceId() != sourceId);
00776
00777
00778 SourceFragment *fragment = m_sourceFragments[sourceId];
00779 if (fragment) {
00780 m_sourceFragments.erase(sourceId);
00781
00782 SourceFile *sourceFile = fragment->sourceFile;
00783 if (sourceFile->hasOneRef()) {
00784 for (int i = 0; i < m_sourceSel->count(); i++) {
00785 if (m_sourceSelFiles.at(i) == sourceFile) {
00786 m_sourceSel->removeItem(i);
00787 m_sourceSelFiles.remove(i);
00788 break;
00789 }
00790 }
00791 removeSourceFile(exec->interpreter(),sourceFile->url);
00792 }
00793 delete fragment;
00794 }
00795
00796 return (m_mode != Stop);
00797 }
00798
00799 bool KJSDebugWin::exception(ExecState *exec, const Value &value, bool inTryCatch)
00800 {
00801 assert(value.isValid());
00802
00803
00804 if (inTryCatch)
00805 return true;
00806
00807 KParts::ReadOnlyPart *part = static_cast<ScriptInterpreter*>(exec->interpreter())->part();
00808 KHTMLPart *khtmlpart = ::qt_cast<KHTMLPart*>(part);
00809 if (khtmlpart && !khtmlpart->settings()->isJavaScriptErrorReportingEnabled())
00810 return true;
00811
00812 QWidget *dlgParent = (m_evalDepth == 0) ? (QWidget*)part->widget() : (QWidget*)this;
00813
00814 QString exceptionMsg = value.toString(exec).qstring();
00815
00816
00817
00818
00819 Object valueObj = Object::dynamicCast(value);
00820 Object syntaxError = exec->interpreter()->builtinSyntaxError();
00821 if (valueObj.isValid() && valueObj.get(exec,"constructor").imp() == syntaxError.imp()) {
00822 Value sidValue = valueObj.get(exec,"sid");
00823 if (sidValue.isA(NumberType)) {
00824 int sourceId = (int)sidValue.toNumber(exec);
00825 assert(m_sourceFragments[sourceId]);
00826 exceptionMsg = i18n("Parse error at %1 line %2")
00827 .arg(m_sourceFragments[sourceId]->sourceFile->url)
00828 .arg(m_sourceFragments[sourceId]->baseLine+m_sourceFragments[sourceId]->errorLine-1);
00829 }
00830 }
00831
00832 bool dontShowAgain = false;
00833 if (m_execsCount == 0) {
00834
00835
00836
00837 QString msg = i18n("An error occurred while attempting to run a script on this page.\n\n%1")
00838 .arg(exceptionMsg);
00839 KJSErrorDialog dlg(dlgParent,msg,false);
00840 dlg.exec();
00841 dontShowAgain = dlg.dontShowAgain();
00842 }
00843 else {
00844 Context ctx = m_execs[m_execsCount-1]->context();
00845 SourceFragment *sourceFragment = m_sourceFragments[ctx.sourceId()];
00846 QString msg = i18n("An error occurred while attempting to run a script on this page.\n\n%1 line %2:\n%3")
00847 .arg(KStringHandler::rsqueeze( sourceFragment->sourceFile->url,80),
00848 QString::number( sourceFragment->baseLine+ctx.curStmtFirstLine()-1),
00849 exceptionMsg);
00850
00851 KJSErrorDialog dlg(dlgParent,msg,true);
00852 dlg.exec();
00853 dontShowAgain = dlg.dontShowAgain();
00854
00855 if (dlg.debugSelected()) {
00856 m_mode = Next;
00857 m_steppingDepth = m_execsCount-1;
00858 enterSession(exec);
00859 }
00860 }
00861
00862 if (dontShowAgain) {
00863 KConfig *config = kapp->config();
00864 KConfigGroupSaver saver(config,QString::fromLatin1("Java/JavaScript Settings"));
00865 config->writeEntry("ReportJavaScriptErrors",QVariant(false,0));
00866 config->sync();
00867 QByteArray data;
00868 kapp->dcopClient()->send( "konqueror*", "KonquerorIface", "reparseConfiguration()", data );
00869 }
00870
00871 return (m_mode != Stop);
00872 }
00873
00874 bool KJSDebugWin::atStatement(KJS::ExecState *exec)
00875 {
00876 assert(m_execsCount > 0);
00877 assert(m_execs[m_execsCount-1] == exec);
00878 checkBreak(exec);
00879 return (m_mode != Stop);
00880 }
00881
00882 bool KJSDebugWin::enterContext(ExecState *exec)
00883 {
00884 if (m_execsCount >= m_execsAlloc) {
00885 m_execsAlloc += 10;
00886 m_execs = (ExecState**)realloc(m_execs,m_execsAlloc*sizeof(ExecState*));
00887 }
00888 m_execs[m_execsCount++] = exec;
00889
00890 if (m_mode == Step)
00891 m_steppingDepth = m_execsCount-1;
00892
00893 checkBreak(exec);
00894 return (m_mode != Stop);
00895 }
00896
00897 bool KJSDebugWin::exitContext(ExecState *exec, const Completion &)
00898 {
00899 assert(m_execsCount > 0);
00900 assert(m_execs[m_execsCount-1] == exec);
00901
00902 checkBreak(exec);
00903
00904 m_execsCount--;
00905 if (m_steppingDepth > m_execsCount-1)
00906 m_steppingDepth = m_execsCount-1;
00907 if (m_execsCount == 0)
00908 updateContextList();
00909
00910 return (m_mode != Stop);
00911 }
00912
00913 void KJSDebugWin::displaySourceFile(SourceFile *sourceFile, bool forceRefresh)
00914 {
00915 if (m_curSourceFile == sourceFile && !forceRefresh)
00916 return;
00917 sourceFile->ref();
00918 m_sourceDisplay->setSource(sourceFile);
00919 if (m_curSourceFile)
00920 m_curSourceFile->deref();
00921 m_curSourceFile = sourceFile;
00922 }
00923
00924 void KJSDebugWin::setSourceLine(int sourceId, int lineno)
00925 {
00926 SourceFragment *source = m_sourceFragments[sourceId];
00927 if (!source)
00928 return;
00929
00930 SourceFile *sourceFile = source->sourceFile;
00931 if (m_curSourceFile != source->sourceFile) {
00932 for (int i = 0; i < m_sourceSel->count(); i++)
00933 if (m_sourceSelFiles.at(i) == sourceFile)
00934 m_sourceSel->setCurrentItem(i);
00935 displaySourceFile(sourceFile,false);
00936 }
00937 m_sourceDisplay->setCurrentLine(source->baseLine+lineno-2);
00938 }
00939
00940 void KJSDebugWin::setNextSourceInfo(QString url, int baseLine)
00941 {
00942 m_nextSourceUrl = url;
00943 m_nextSourceBaseLine = baseLine;
00944 }
00945
00946 void KJSDebugWin::sourceChanged(Interpreter *interpreter, QString url)
00947 {
00948 SourceFile *sourceFile = getSourceFile(interpreter,url);
00949 if (sourceFile && m_curSourceFile == sourceFile)
00950 displaySourceFile(sourceFile,true);
00951 }
00952
00953 void KJSDebugWin::clearInterpreter(Interpreter *interpreter)
00954 {
00955 QMap<int,SourceFragment*>::Iterator it;
00956
00957 for (it = m_sourceFragments.begin(); it != m_sourceFragments.end(); ++it)
00958 if (it.data() && it.data()->sourceFile->interpreter == interpreter)
00959 it.data()->sourceFile->interpreter = 0;
00960 }
00961
00962 SourceFile *KJSDebugWin::getSourceFile(Interpreter *interpreter, QString url)
00963 {
00964 QString key = QString("%1|%2").arg((long)interpreter).arg(url);
00965 return m_sourceFiles[key];
00966 }
00967
00968 void KJSDebugWin::setSourceFile(Interpreter *interpreter, QString url, SourceFile *sourceFile)
00969 {
00970 QString key = QString("%1|%2").arg((long)interpreter).arg(url);
00971 sourceFile->ref();
00972 if (SourceFile* oldFile = m_sourceFiles[key])
00973 oldFile->deref();
00974 m_sourceFiles[key] = sourceFile;
00975 }
00976
00977 void KJSDebugWin::removeSourceFile(Interpreter *interpreter, QString url)
00978 {
00979 QString key = QString("%1|%2").arg((long)interpreter).arg(url);
00980 if (SourceFile* oldFile = m_sourceFiles[key])
00981 oldFile->deref();
00982 m_sourceFiles.remove(key);
00983 }
00984
00985 void KJSDebugWin::checkBreak(ExecState *exec)
00986 {
00987 if (m_breakpointCount > 0) {
00988 Context ctx = m_execs[m_execsCount-1]->context();
00989 if (haveBreakpoint(ctx.sourceId(),ctx.curStmtFirstLine(),ctx.curStmtLastLine())) {
00990 m_mode = Next;
00991 m_steppingDepth = m_execsCount-1;
00992 }
00993 }
00994
00995 if ((m_mode == Step || m_mode == Next) && m_steppingDepth == m_execsCount-1)
00996 enterSession(exec);
00997 }
00998
00999 void KJSDebugWin::enterSession(ExecState *exec)
01000 {
01001
01002
01003
01004
01005
01006 if (!isVisible())
01007 show();
01008
01009 m_mode = Continue;
01010
01011 if (m_execStates.isEmpty()) {
01012 disableOtherWindows();
01013 m_nextAction->setEnabled(true);
01014 m_stepAction->setEnabled(true);
01015 m_continueAction->setEnabled(true);
01016 m_stopAction->setEnabled(true);
01017 m_breakAction->setEnabled(false);
01018 }
01019 m_execStates.push(exec);
01020
01021 updateContextList();
01022
01023 qApp->enter_loop();
01024 }
01025
01026 void KJSDebugWin::leaveSession()
01027 {
01028
01029
01030
01031
01032 assert(!m_execStates.isEmpty());
01033
01034 m_execStates.pop();
01035
01036 if (m_execStates.isEmpty()) {
01037 m_nextAction->setEnabled(false);
01038 m_stepAction->setEnabled(false);
01039 m_continueAction->setEnabled(false);
01040 m_stopAction->setEnabled(false);
01041 m_breakAction->setEnabled(true);
01042 m_sourceDisplay->setCurrentLine(-1);
01043 enableOtherWindows();
01044 }
01045
01046 qApp->exit_loop();
01047 }
01048
01049 void KJSDebugWin::updateContextList()
01050 {
01051 disconnect(m_contextList,SIGNAL(highlighted(int)),this,SLOT(slotShowFrame(int)));
01052
01053 m_contextList->clear();
01054 for (int i = 0; i < m_execsCount; i++)
01055 m_contextList->insertItem(contextStr(m_execs[i]->context()));
01056
01057 if (m_execsCount > 0) {
01058 m_contextList->setSelected(m_execsCount-1, true);
01059 Context ctx = m_execs[m_execsCount-1]->context();
01060 setSourceLine(ctx.sourceId(),ctx.curStmtFirstLine());
01061 }
01062
01063 connect(m_contextList,SIGNAL(highlighted(int)),this,SLOT(slotShowFrame(int)));
01064 }
01065
01066 QString KJSDebugWin::contextStr(const Context &ctx)
01067 {
01068 QString str = "";
01069 SourceFragment *sourceFragment = m_sourceFragments[ctx.sourceId()];
01070 QString url = sourceFragment->sourceFile->url;
01071 int fileLineno = sourceFragment->baseLine+ctx.curStmtFirstLine()-1;
01072
01073 switch (ctx.codeType()) {
01074 case GlobalCode:
01075 str = QString("Global code at %1:%2").arg(url).arg(fileLineno);
01076 break;
01077 case EvalCode:
01078 str = QString("Eval code at %1:%2").arg(url).arg(fileLineno);
01079 break;
01080 case FunctionCode:
01081 if (!ctx.functionName().isNull())
01082 str = QString("%1() at %2:%3").arg(ctx.functionName().qstring()).arg(url).arg(fileLineno);
01083 else
01084 str = QString("Anonymous function at %1:%2").arg(url).arg(fileLineno);
01085 break;
01086 }
01087
01088 return str;
01089 }
01090
01091 bool KJSDebugWin::setBreakpoint(int sourceId, int lineno)
01092 {
01093 if (haveBreakpoint(sourceId,lineno,lineno))
01094 return false;
01095
01096 m_breakpointCount++;
01097 m_breakpoints = static_cast<Breakpoint*>(realloc(m_breakpoints,
01098 m_breakpointCount*sizeof(Breakpoint)));
01099 m_breakpoints[m_breakpointCount-1].sourceId = sourceId;
01100 m_breakpoints[m_breakpointCount-1].lineno = lineno;
01101
01102 return true;
01103 }
01104
01105 bool KJSDebugWin::deleteBreakpoint(int sourceId, int lineno)
01106 {
01107 for (int i = 0; i < m_breakpointCount; i++) {
01108 if (m_breakpoints[i].sourceId == sourceId && m_breakpoints[i].lineno == lineno) {
01109
01110 memmove(m_breakpoints+i,m_breakpoints+i+1,(m_breakpointCount-i-1)*sizeof(Breakpoint));
01111 m_breakpointCount--;
01112 m_breakpoints = static_cast<Breakpoint*>(realloc(m_breakpoints,
01113 m_breakpointCount*sizeof(Breakpoint)));
01114 return true;
01115 }
01116 }
01117
01118 return false;
01119 }
01120
01121 bool KJSDebugWin::haveBreakpoint(SourceFile *sourceFile, int line0, int line1)
01122 {
01123 for (int i = 0; i < m_breakpointCount; i++) {
01124 int sourceId = m_breakpoints[i].sourceId;
01125 int lineno = m_breakpoints[i].lineno;
01126 if (m_sourceFragments.contains(sourceId) &&
01127 m_sourceFragments[sourceId]->sourceFile == sourceFile) {
01128 int absLineno = m_sourceFragments[sourceId]->baseLine+lineno-1;
01129 if (absLineno >= line0 && absLineno <= line1)
01130 return true;
01131 }
01132 }
01133
01134 return false;
01135 }
01136
01137 #include "kjs_debugwin.moc"
01138
01139 #endif // KJS_DEBUGGER