qmlengine.cpp 44 KB
Newer Older
1 2 3 4 5 6
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
7
** Contact: Nokia Corporation (qt-info@nokia.com)
8 9 10 11
**
**
** GNU Lesser General Public License Usage
**
hjk's avatar
hjk committed
12 13 14 15 16 17
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18
**
con's avatar
con committed
19
** In addition, as a special exception, Nokia gives you certain additional
hjk's avatar
hjk committed
20
** rights. These rights are described in the Nokia Qt LGPL Exception
con's avatar
con committed
21 22
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
23 24 25 26 27
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
con's avatar
con committed
28
** If you have questions regarding the use of this file, please contact
29
** Nokia at qt-info@nokia.com.
30 31 32 33
**
**************************************************************************/

#include "qmlengine.h"
Lasse Holmstedt's avatar
Lasse Holmstedt committed
34
#include "qmladapter.h"
35
#include "interactiveinterpreter.h"
36

Friedemann Kleint's avatar
Friedemann Kleint committed
37
#include "debuggerstartparameters.h"
38
#include "debuggeractions.h"
39
#include "debuggerconstants.h"
hjk's avatar
hjk committed
40
#include "debuggercore.h"
41
#include "debuggerdialogs.h"
42
#include "debuggerinternalconstants.h"
43
#include "debuggermainwindow.h"
hjk's avatar
hjk committed
44
#include "debuggerrunner.h"
45
#include "debuggerstringutils.h"
46
#include "debuggertooltipmanager.h"
47

48 49 50 51 52
#include "breakhandler.h"
#include "moduleshandler.h"
#include "registerhandler.h"
#include "stackhandler.h"
#include "watchhandler.h"
53
#include "sourcefileshandler.h"
54
#include "watchutils.h"
55
#include "qtmessageloghandler.h"
56

Lasse Holmstedt's avatar
Lasse Holmstedt committed
57
#include <extensionsystem/pluginmanager.h>
58
#include <projectexplorer/applicationlauncher.h>
59
#include <qmljsdebugclient/qdeclarativeoutputparser.h>
60
#include <qmljsdebugclient/qmlenginedebugclient.h>
61
#include <qmljseditor/qmljseditorconstants.h>
62 63
#include <qmljs/parser/qmljsast_p.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
64

65
#include <utils/environment.h>
66 67
#include <utils/qtcassert.h>

68 69
#include <coreplugin/coreconstants.h>
#include <coreplugin/editormanager/editormanager.h>
70
#include <coreplugin/helpmanager.h>
71 72 73
#include <coreplugin/icore.h>

#include <texteditor/itexteditor.h>
74

75 76 77 78 79
#include <QDateTime>
#include <QDebug>
#include <QDir>
#include <QFileInfo>
#include <QTimer>
80

81 82 83 84 85 86 87
#include <QAction>
#include <QApplication>
#include <QMainWindow>
#include <QMessageBox>
#include <QPlainTextEdit>
#include <QToolTip>
#include <QTextDocument>
88

89 90
#include <QTcpSocket>
#include <QHostAddress>
91

92 93 94 95 96 97 98 99
#define DEBUG_QML 1
#if DEBUG_QML
#   define SDEBUG(s) qDebug() << s
#else
#   define SDEBUG(s)
#endif
# define XSDEBUG(s) qDebug() << s

hjk's avatar
hjk committed
100
using namespace ProjectExplorer;
101 102
using namespace QmlJS;
using namespace AST;
hjk's avatar
hjk committed
103

104 105 106
namespace Debugger {
namespace Internal {

107 108
class QmlEnginePrivate
{
109
public:
110 111
    explicit QmlEnginePrivate(QmlEngine *q);

112
private:
113
    friend class QmlEngine;
hjk's avatar
hjk committed
114 115
    QmlAdapter m_adapter;
    ApplicationLauncher m_applicationLauncher;
116
    QTimer m_noDebugOutputTimer;
117
    QmlJsDebugClient::QDeclarativeOutputParser m_outputParser;
118 119
    QHash<QString, QTextDocument*> m_sourceDocuments;
    QHash<QString, QWeakPointer<TextEditor::ITextEditor> > m_sourceEditors;
120 121
    InteractiveInterpreter m_interpreter;
    bool m_validContext;
122
    QHash<QString,BreakpointModelId> pendingBreakpoints;
123
    bool m_retryOnConnectFail;
124
    QList<quint32> queryIds;
125 126
};

127
QmlEnginePrivate::QmlEnginePrivate(QmlEngine *q)
128
    : m_adapter(q),
129 130
      m_validContext(false),
      m_retryOnConnectFail(false)
131 132
{}

133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
class ASTWalker: public Visitor
{
public:
    void operator()(Node *ast, quint32 *l, quint32 *c)
    {
        done = false;
        line = l;
        column = c;
        Node::accept(ast, this);
    }

    bool preVisit(Node *ast)
    {
        return ast->lastSourceLocation().startLine >= *line && !done;
    }

    //Case 1: Breakpoint is between sourceStart(exclusive) and
    //        sourceEnd(inclusive) --> End tree walk.
    //Case 2: Breakpoint is on sourceStart --> Check for the start
    //        of the first executable code. Set the line number and
    //        column number. End tree walk.
    //Case 3: Breakpoint is on "unbreakable" code --> Find the next "breakable"
    //        code and check for Case 2. End tree walk.

    //Add more types when suitable.

    bool visit(UiScriptBinding *ast)
    {
Aurindam Jana's avatar
Aurindam Jana committed
161 162 163
        if (!ast->statement)
            return true;

164 165 166 167 168 169 170 171 172 173 174
        quint32 sourceStartLine = ast->firstSourceLocation().startLine;
        quint32 statementStartLine;
        quint32 statementColumn;

        if (ast->statement->kind == Node::Kind_ExpressionStatement) {
            statementStartLine = ast->statement->firstSourceLocation().
                    startLine;
            statementColumn = ast->statement->firstSourceLocation().startColumn;

        } else if (ast->statement->kind == Node::Kind_Block) {
            Block *block = static_cast<Block *>(ast->statement);
Aurindam Jana's avatar
Aurindam Jana committed
175 176
            if (!block || !block->statements)
                return true;
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
            statementStartLine = block->statements->firstSourceLocation().
                    startLine;
            statementColumn = block->statements->firstSourceLocation().
                    startColumn;

        } else {
            return true;
        }


        //Case 1
        //Check for possible relocation within the binding statement

        //Rewritten to (function <token>() { { }})
        //The offset 16 is position of inner lbrace without token length.
        const int offset = 16;

        //Case 2
        if (statementStartLine == *line) {
            if (sourceStartLine == *line)
                *column = offset + ast->qualifiedId->identifierToken.length;
            done = true;
        }

        //Case 3
        if (statementStartLine > *line) {
            *line = statementStartLine;
            if (sourceStartLine == *line)
                *column = offset + ast->qualifiedId->identifierToken.length;
            else
                *column = statementColumn;
            done = true;
        }
        return true;
    }

    bool visit(FunctionDeclaration *ast) {
        quint32 sourceStartLine = ast->firstSourceLocation().startLine;
        quint32 sourceStartColumn = ast->firstSourceLocation().startColumn;
        quint32 statementStartLine = ast->body->firstSourceLocation().startLine;
        quint32 statementColumn = ast->body->firstSourceLocation().startColumn;

        //Case 1
        //Check for possible relocation within the function declaration

        //Case 2
        if (statementStartLine == *line) {
            if (sourceStartLine == *line)
                *column = statementColumn - sourceStartColumn + 1;
            done = true;
        }

        //Case 3
        if (statementStartLine > *line) {
            *line = statementStartLine;
            if (sourceStartLine == *line)
                *column = statementColumn - sourceStartColumn + 1;
            else
                *column = statementColumn;
            done = true;
        }
        return true;
    }

    bool visit(EmptyStatement *ast)
    {
        *line = ast->lastSourceLocation().startLine + 1;
        return true;
    }

    bool visit(VariableStatement *ast) { test(ast); return true; }
    bool visit(VariableDeclarationList *ast) { test(ast); return true; }
    bool visit(VariableDeclaration *ast) { test(ast); return true; }
    bool visit(ExpressionStatement *ast) { test(ast); return true; }
    bool visit(IfStatement *ast) { test(ast); return true; }
    bool visit(DoWhileStatement *ast) { test(ast); return true; }
    bool visit(WhileStatement *ast) { test(ast); return true; }
    bool visit(ForStatement *ast) { test(ast); return true; }
    bool visit(LocalForStatement *ast) { test(ast); return true; }
    bool visit(ForEachStatement *ast) { test(ast); return true; }
    bool visit(LocalForEachStatement *ast) { test(ast); return true; }
    bool visit(ContinueStatement *ast) { test(ast); return true; }
    bool visit(BreakStatement *ast) { test(ast); return true; }
    bool visit(ReturnStatement *ast) { test(ast); return true; }
    bool visit(WithStatement *ast) { test(ast); return true; }
    bool visit(SwitchStatement *ast) { test(ast); return true; }
    bool visit(CaseBlock *ast) { test(ast); return true; }
    bool visit(CaseClauses *ast) { test(ast); return true; }
    bool visit(CaseClause *ast) { test(ast); return true; }
    bool visit(DefaultClause *ast) { test(ast); return true; }
    bool visit(LabelledStatement *ast) { test(ast); return true; }
    bool visit(ThrowStatement *ast) { test(ast); return true; }
    bool visit(TryStatement *ast) { test(ast); return true; }
    bool visit(Catch *ast) { test(ast); return true; }
    bool visit(Finally *ast) { test(ast); return true; }
    bool visit(FunctionExpression *ast) { test(ast); return true; }
    bool visit(DebuggerStatement *ast) { test(ast); return true; }

    void test(Node *ast)
    {
        quint32 statementStartLine = ast->firstSourceLocation().startLine;
        //Case 1/2
        if (statementStartLine <= *line &&
                *line <= ast->lastSourceLocation().startLine)
            done = true;

        //Case 3
        if (statementStartLine > *line) {
            *line = statementStartLine;
            *column = ast->firstSourceLocation().startColumn;
            done = true;
        }
    }

    bool done;
    quint32 *line;
    quint32 *column;
};
295

296 297 298 299 300 301
///////////////////////////////////////////////////////////////////////
//
// QmlEngine
//
///////////////////////////////////////////////////////////////////////

302 303
QmlEngine::QmlEngine(const DebuggerStartParameters &startParameters,
        DebuggerEngine *masterEngine)
304
  : DebuggerEngine(startParameters, QmlLanguage, masterEngine),
305
    d(new QmlEnginePrivate(this))
306
{
Friedemann Kleint's avatar
Friedemann Kleint committed
307
    setObjectName(QLatin1String("QmlEngine"));
308 309 310 311 312 313 314 315 316 317 318 319 320 321

    ExtensionSystem::PluginManager *pluginManager =
        ExtensionSystem::PluginManager::instance();
    pluginManager->addObject(this);

    connect(&d->m_adapter, SIGNAL(connectionError(QAbstractSocket::SocketError)),
        SLOT(connectionError(QAbstractSocket::SocketError)));
    connect(&d->m_adapter, SIGNAL(serviceConnectionError(QString)),
        SLOT(serviceConnectionError(QString)));
    connect(&d->m_adapter, SIGNAL(connected()),
        SLOT(connectionEstablished()));
    connect(&d->m_adapter, SIGNAL(connectionStartupFailed()),
        SLOT(connectionStartupFailed()));

322 323 324 325 326 327
    connect(this, SIGNAL(stateChanged(Debugger::DebuggerState)),
            SLOT(updateCurrentContext()));
    connect(this->stackHandler(), SIGNAL(currentIndexChanged()),
            SLOT(updateCurrentContext()));
    connect(&d->m_adapter, SIGNAL(selectionChanged()),
            SLOT(updateCurrentContext()));
328 329 330 331 332
    connect(d->m_adapter.messageClient(),
            SIGNAL(message(QtMsgType,QString,
                           QmlJsDebugClient::QDebugContextInfo)),
            SLOT(appendDebugOutput(QtMsgType,QString,
                                   QmlJsDebugClient::QDebugContextInfo)));
333

334 335 336 337
    connect(&d->m_applicationLauncher,
        SIGNAL(processExited(int)),
        SLOT(disconnected()));
    connect(&d->m_applicationLauncher,
Robert Loehning's avatar
Robert Loehning committed
338 339
        SIGNAL(appendMessage(QString,Utils::OutputFormat)),
        SLOT(appendMessage(QString,Utils::OutputFormat)));
340 341 342 343
    connect(&d->m_applicationLauncher,
            SIGNAL(processStarted()),
            &d->m_noDebugOutputTimer,
            SLOT(start()));
344

345
    d->m_outputParser.setNoOutputText(ApplicationLauncher::msgWinCannotRetrieveDebuggingOutput());
346 347 348
    connect(&d->m_outputParser, SIGNAL(waitingForConnectionOnPort(quint16)),
            this, SLOT(beginConnection(quint16)));
    connect(&d->m_outputParser, SIGNAL(waitingForConnectionViaOst()),
349 350
            this, SLOT(beginConnection()));
    connect(&d->m_outputParser, SIGNAL(noOutputMessage()),
351
            this, SLOT(tryToConnect()));
352
    connect(&d->m_outputParser, SIGNAL(errorMessage(QString)),
353
            this, SLOT(appStartupFailed(QString)));
354

355
    // Only wait 8 seconds for the 'Waiting for connection' on application ouput, then just try to connect
356 357 358
    // (application output might be redirected / blocked)
    d->m_noDebugOutputTimer.setSingleShot(true);
    d->m_noDebugOutputTimer.setInterval(8000);
359
    connect(&d->m_noDebugOutputTimer, SIGNAL(timeout()), this, SLOT(tryToConnect()));
360 361

    qtMessageLogHandler()->setHasEditableRow(true);
362 363 364 365 366

    connect(ModelManagerInterface::instance(),
            SIGNAL(documentUpdated(QmlJS::Document::Ptr)),
            this,
            SLOT(documentUpdated(QmlJS::Document::Ptr)));
367 368

    // we won't get any debug output
369 370 371 372
    if (startParameters.useTerminal) {
        d->m_noDebugOutputTimer.setInterval(0);
        d->m_retryOnConnectFail = true;
    }
373 374 375
}

QmlEngine::~QmlEngine()
hjk's avatar
hjk committed
376
{
377 378 379 380 381 382
    ExtensionSystem::PluginManager *pluginManager =
        ExtensionSystem::PluginManager::instance();

    if (pluginManager->allObjects().contains(this)) {
        pluginManager->removeObject(this);
    }
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
383

384 385 386 387 388 389 390 391 392 393
    QList<Core::IEditor *> editorsToClose;

    QHash<QString, QWeakPointer<TextEditor::ITextEditor> >::iterator iter;
    for (iter = d->m_sourceEditors.begin(); iter != d->m_sourceEditors.end(); ++iter) {
        QWeakPointer<TextEditor::ITextEditor> textEditPtr = iter.value();
        if (textEditPtr)
            editorsToClose << textEditPtr.data();
    }
    Core::EditorManager::instance()->closeEditors(editorsToClose);

hjk's avatar
hjk committed
394 395
    delete d;
}
396

hjk's avatar
hjk committed
397
void QmlEngine::setupInferior()
398
{
hjk's avatar
hjk committed
399
    QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state());
Lasse Holmstedt's avatar
Lasse Holmstedt committed
400

401
    notifyInferiorSetupOk();
hjk's avatar
hjk committed
402
}
hjk's avatar
hjk committed
403

con's avatar
con committed
404
void QmlEngine::appendMessage(const QString &msg, Utils::OutputFormat /* format */)
hjk's avatar
hjk committed
405
{
406
    showMessage(msg, AppOutput); // FIXME: Redirect to RunControl
hjk's avatar
hjk committed
407 408
}

Lasse Holmstedt's avatar
Lasse Holmstedt committed
409 410 411 412
void QmlEngine::connectionEstablished()
{
    attemptBreakpointSynchronization();

413 414 415 416
    if (!watchHandler()->watcherNames().isEmpty()) {
        synchronizeWatchers();
    }
    connect(watchersModel(),SIGNAL(layoutChanged()),this,SLOT(synchronizeWatchers()));
Christiaan Janssen's avatar
Christiaan Janssen committed
417

418 419
    if (state() == EngineRunRequested)
        notifyEngineRunAndInferiorRunOk();
Lasse Holmstedt's avatar
Lasse Holmstedt committed
420
}
421

422 423
void QmlEngine::tryToConnect(quint16 port)
{
424
    showMessage(QLatin1String("QML Debugger: No application output received in time, trying to connect ..."), LogStatus);
425 426 427 428
    d->m_retryOnConnectFail = true;
    beginConnection(port);
}

429
void QmlEngine::beginConnection(quint16 port)
430 431
{
    d->m_noDebugOutputTimer.stop();
432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451
    if (port > 0) {
        QTC_CHECK(startParameters().communicationChannel
                  == DebuggerStartParameters::CommunicationChannelTcpIp);
        QTC_ASSERT(startParameters().connParams.port == 0
                   || startParameters().connParams.port == port,
                   qWarning() << "Port " << port << "from application output does not match"
                   << startParameters().connParams.port << "from start parameters.")
        d->m_adapter.beginConnectionTcp(startParameters().qmlServerAddress, port);
        return;
    }
    if (startParameters().communicationChannel
           == DebuggerStartParameters::CommunicationChannelTcpIp) {
        // no port from application output, use the one from start parameters ...
        d->m_adapter.beginConnectionTcp(startParameters().qmlServerAddress,
                                        startParameters().qmlServerPort);
    } else {
        QTC_CHECK(startParameters().communicationChannel
                  == DebuggerStartParameters::CommunicationChannelUsb);
        d->m_adapter.beginConnectionOst(startParameters().remoteChannel);
    }
Lasse Holmstedt's avatar
Lasse Holmstedt committed
452 453
}

454
void QmlEngine::connectionStartupFailed()
Lasse Holmstedt's avatar
Lasse Holmstedt committed
455
{
456
    if (d->m_retryOnConnectFail) {
457 458
        // retry after 3 seconds ...
        QTimer::singleShot(3000, this, SLOT(beginConnection()));
459 460
        return;
    }
461

hjk's avatar
hjk committed
462
    QMessageBox *infoBox = new QMessageBox(Core::ICore::mainWindow());
463 464
    infoBox->setIcon(QMessageBox::Critical);
    infoBox->setWindowTitle(tr("Qt Creator"));
465 466 467 468 469
    infoBox->setText(tr("Could not connect to the in-process QML debugger."
                        "\nDo you want to retry?"));
    infoBox->setStandardButtons(QMessageBox::Retry | QMessageBox::Cancel |
                                QMessageBox::Help);
    infoBox->setDefaultButton(QMessageBox::Retry);
470 471 472
    infoBox->setModal(true);

    connect(infoBox, SIGNAL(finished(int)),
473
            this, SLOT(errorMessageBoxFinished(int)));
474 475 476 477

    infoBox->show();
}

478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493
void QmlEngine::appStartupFailed(const QString &errorMessage)
{
    QMessageBox *infoBox = new QMessageBox(Core::ICore::mainWindow());
    infoBox->setIcon(QMessageBox::Critical);
    infoBox->setWindowTitle(tr("Qt Creator"));
    infoBox->setText(tr("Could not connect to the in-process QML debugger."
                        "\n%1").arg(errorMessage));
    infoBox->setStandardButtons(QMessageBox::Ok | QMessageBox::Help);
    infoBox->setDefaultButton(QMessageBox::Ok);
    connect(infoBox, SIGNAL(finished(int)),
            this, SLOT(errorMessageBoxFinished(int)));
    infoBox->show();

    notifyEngineRunFailed();
}

494
void QmlEngine::errorMessageBoxFinished(int result)
495 496
{
    switch (result) {
497
    case QMessageBox::Retry: {
498
        beginConnection();
499 500 501 502
        break;
    }
    case QMessageBox::Help: {
        Core::HelpManager *helpManager = Core::HelpManager::instance();
503
        helpManager->handleHelpRequest(QLatin1String("qthelp://com.nokia.qtcreator/doc/creator-debugging-qml.html"));
504
        // fall through
505 506
    }
    default:
507 508 509
        if (state() == InferiorRunOk) {
            notifyInferiorSpontaneousStop();
            notifyInferiorIll();
510
        } else if (state() == EngineRunRequested) {
511
            notifyEngineRunFailed();
512
        }
Kai Koehne's avatar
Kai Koehne committed
513
        break;
514
    }
Lasse Holmstedt's avatar
Lasse Holmstedt committed
515 516
}

517
void QmlEngine::connectionError(QAbstractSocket::SocketError socketError)
Lasse Holmstedt's avatar
Lasse Holmstedt committed
518
{
hjk's avatar
hjk committed
519
    if (socketError == QAbstractSocket::RemoteHostClosedError)
520
        showMessage(tr("QML Debugger: Remote host closed connection."), StatusBar);
521

522 523 524 525
    if (!isSlaveEngine()) { // normal flow for slave engine when gdb exits
        notifyInferiorSpontaneousStop();
        notifyInferiorIll();
    }
Lasse Holmstedt's avatar
Lasse Holmstedt committed
526 527
}

528 529
void QmlEngine::serviceConnectionError(const QString &serviceName)
{
530 531
    showMessage(tr("QML Debugger: Could not connect to service '%1'.")
        .arg(serviceName), StatusBar);
532 533
}

534 535 536 537 538
bool QmlEngine::canDisplayTooltip() const
{
    return state() == InferiorRunOk || state() == InferiorStopOk;
}

539
void QmlEngine::filterApplicationMessage(const QString &output, int /*channel*/)
540
{
541
    d->m_outputParser.processOutput(output);
542 543 544 545 546 547 548 549 550 551
}

void QmlEngine::showMessage(const QString &msg, int channel, int timeout) const
{
    if (channel == AppOutput || channel == AppError) {
        const_cast<QmlEngine*>(this)->filterApplicationMessage(msg, channel);
    }
    DebuggerEngine::showMessage(msg, channel, timeout);
}

552 553 554
void QmlEngine::gotoLocation(const Location &location)
{
    const QString fileName = location.fileName();
Daniel Molkentin's avatar
Daniel Molkentin committed
555 556
    // TODO: QUrl::isLocalFile() once we depend on Qt 4.8
    if (QUrl(fileName).scheme().compare(QLatin1String("file"), Qt::CaseInsensitive) == 0) {
557 558 559 560 561
        // internal file from source files -> show generated .js
        QTC_ASSERT(d->m_sourceDocuments.contains(fileName), return);
        Core::IEditor *editor = 0;

        Core::EditorManager *editorManager = Core::EditorManager::instance();
Aurindam Jana's avatar
Aurindam Jana committed
562 563 564 565 566 567 568 569 570 571
        QString titlePattern = tr("JS Source for %1").arg(fileName);
        //Check if there are open editors with the same title
        QList<Core::IEditor *> editors = editorManager->openedEditors();
        foreach (Core::IEditor *ed, editors) {
            if (ed->displayName() == titlePattern) {
                editor = ed;
                break;
            }
        }
        if (!editor) {
572 573 574 575 576 577
            editor = editorManager->openEditorWithContents(QmlJSEditor::Constants::C_QMLJSEDITOR_ID,
                                                           &titlePattern);
            if (editor) {
                editor->setProperty(Constants::OPENED_BY_DEBUGGER, true);
            }

578 579
            updateEditor(editor, d->m_sourceDocuments.value(fileName));
        }
580
        editorManager->activateEditor(editor);
581

582 583 584 585 586
    } else {
        DebuggerEngine::gotoLocation(location);
    }
}

587 588
void QmlEngine::closeConnection()
{
589
    disconnect(watchersModel(),SIGNAL(layoutChanged()),this,SLOT(synchronizeWatchers()));
590
    d->m_adapter.closeConnection();
591 592
}

hjk's avatar
hjk committed
593 594 595
void QmlEngine::runEngine()
{
    QTC_ASSERT(state() == EngineRunRequested, qDebug() << state());
Lasse Holmstedt's avatar
Lasse Holmstedt committed
596

597
    if (!isSlaveEngine()) {
598 599 600
        if (startParameters().startMode == AttachToRemoteServer)
            beginConnection();
        else
601
            startApplicationLauncher();
602 603
    } else {
        d->m_noDebugOutputTimer.start();
604
    }
605 606 607 608 609
}

void QmlEngine::startApplicationLauncher()
{
    if (!d->m_applicationLauncher.isRunning()) {
610 611 612 613
        appendMessage(tr("Starting %1 %2").arg(
                          QDir::toNativeSeparators(startParameters().executable),
                          startParameters().processArgs)
                      + QLatin1Char('\n')
con's avatar
con committed
614
                     , Utils::NormalMessageFormat);
hjk's avatar
hjk committed
615
        d->m_applicationLauncher.start(ApplicationLauncher::Gui,
616 617 618
                                    startParameters().executable,
                                    startParameters().processArgs);
    }
619
}
Lasse Holmstedt's avatar
Lasse Holmstedt committed
620

621 622 623 624 625 626
void QmlEngine::stopApplicationLauncher()
{
    if (d->m_applicationLauncher.isRunning()) {
        disconnect(&d->m_applicationLauncher, SIGNAL(processExited(int)), this, SLOT(disconnected()));
        d->m_applicationLauncher.stop();
    }
627 628
}

Christian Kandeler's avatar
Christian Kandeler committed
629
void QmlEngine::handleRemoteSetupDone(int gdbServerPort, int qmlPort)
630
{
Christian Kandeler's avatar
Christian Kandeler committed
631
    Q_UNUSED(gdbServerPort);
632

Christian Kandeler's avatar
Christian Kandeler committed
633 634
    if (qmlPort != -1)
        startParameters().qmlServerPort = qmlPort;
635 636

    notifyEngineRemoteSetupDone();
637
    notifyEngineSetupOk();
638 639 640 641
}

void QmlEngine::handleRemoteSetupFailed(const QString &message)
{
642 643 644
    if (isMasterEngine())
        QMessageBox::critical(0,tr("Failed to start application"),
            tr("Application startup failed: %1").arg(message));
645 646

    notifyEngineRemoteSetupFailed();
647
    notifyEngineSetupFailed();
648 649
}

hjk's avatar
hjk committed
650
void QmlEngine::shutdownInferior()
651
{
652 653 654 655
    d->m_noDebugOutputTimer.stop();

    if (d->m_adapter.activeDebuggerClient())
        d->m_adapter.activeDebuggerClient()->endSession();
656

657 658
    if (isSlaveEngine()) {
        resetLocation();
Lasse Holmstedt's avatar
Lasse Holmstedt committed
659
    }
660
    stopApplicationLauncher();
661
    closeConnection();
662

hjk's avatar
hjk committed
663 664 665 666 667
    notifyInferiorShutdownOk();
}

void QmlEngine::shutdownEngine()
{
668 669
    // double check (ill engine?):
    stopApplicationLauncher();
Lasse Holmstedt's avatar
Lasse Holmstedt committed
670

671
    notifyEngineShutdownOk();
672 673
    if (!isSlaveEngine())
        showMessage(QString(), StatusBar);
674 675
}

hjk's avatar
hjk committed
676
void QmlEngine::setupEngine()
677
{
678
    if (startParameters().requestRemoteSetup) {
679
        // we need to get the port first
680
        notifyEngineRequestRemoteSetup();
681 682 683
    } else {
        d->m_applicationLauncher.setEnvironment(startParameters().environment);
        d->m_applicationLauncher.setWorkingDirectory(startParameters().workingDirectory);
Lasse Holmstedt's avatar
Lasse Holmstedt committed
684

685 686 687 688 689 690 691
        // We can't do this in the constructore because runControl() isn't yet defined
        connect(&d->m_applicationLauncher, SIGNAL(bringToForegroundRequested(qint64)),
                runControl(), SLOT(bringApplicationToForeground(qint64)),
                Qt::UniqueConnection);

        notifyEngineSetupOk();
    }
692 693
}

694 695
void QmlEngine::continueInferior()
{
hjk's avatar
hjk committed
696
    QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
697 698 699
    if (d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->continueInferior();
    }
700
    resetLocation();
hjk's avatar
hjk committed
701 702
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
703 704 705 706
}

void QmlEngine::interruptInferior()
{
707 708 709
    if (d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->interruptInferior();
    }
710
    notifyInferiorStopOk();
711 712 713 714
}

void QmlEngine::executeStep()
{
715 716 717
    if (d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->executeStep();
    }
hjk's avatar
hjk committed
718 719
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
720 721 722 723
}

void QmlEngine::executeStepI()
{
724 725 726
    if (d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->executeStepI();
    }
hjk's avatar
hjk committed
727 728
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
729 730 731 732
}

void QmlEngine::executeStepOut()
{
733 734 735
    if (d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->executeStepOut();
    }
hjk's avatar
hjk committed
736 737
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
738 739 740 741
}

void QmlEngine::executeNext()
{
742 743 744
    if (d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->executeNext();
    }
hjk's avatar
hjk committed
745 746
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
747 748 749 750
}

void QmlEngine::executeNextI()
{
751
    executeNext();
752 753
}

754
void QmlEngine::executeRunToLine(const ContextData &data)
755
{
756 757 758
    QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
    showStatusMessage(tr("Run to line  %1 (%2) requested...").arg(data.lineNumber).arg(data.fileName), 5000);
    resetLocation();
759 760 761 762 763 764
    ContextData modifiedData = data;
    quint32 line = data.lineNumber;
    quint32 column;
    bool valid;
    if (adjustBreakpointLineAndColumn(data.fileName, &line, &column, &valid))
        modifiedData.lineNumber = line;
765
    if (d->m_adapter.activeDebuggerClient())
766
        d->m_adapter.activeDebuggerClient()->executeRunToLine(modifiedData);
767 768
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
769 770 771 772 773 774 775 776
}

void QmlEngine::executeRunToFunction(const QString &functionName)
{
    Q_UNUSED(functionName)
    XSDEBUG("FIXME:  QmlEngine::executeRunToFunction()");
}

777
void QmlEngine::executeJumpToLine(const ContextData &data)
778
{
779
    Q_UNUSED(data)
780 781 782 783 784
    XSDEBUG("FIXME:  QmlEngine::executeJumpToLine()");
}

void QmlEngine::activateFrame(int index)
{
785 786 787
    if (state() != InferiorStopOk && state() != InferiorUnrunnable)
        return;

788 789 790
    if (d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->activateFrame(index);
    }
791
    gotoLocation(stackHandler()->frames().value(index));
792 793 794 795 796 797 798
}

void QmlEngine::selectThread(int index)
{
    Q_UNUSED(index)
}

799 800 801 802 803 804 805
void QmlEngine::insertBreakpoint(BreakpointModelId id)
{
    BreakHandler *handler = breakHandler();
    BreakpointState state = handler->state(id);
    QTC_ASSERT(state == BreakpointInsertRequested, qDebug() << id << this << state);
    handler->notifyBreakpointInsertProceeding(id);

806 807 808 809 810 811 812 813 814 815 816 817 818 819
    const BreakpointParameters &params = handler->breakpointData(id);
    quint32 line = params.lineNumber;
    quint32 column = 0;
    if (params.type == BreakpointByFileAndLine) {
        bool valid = false;
        if (!adjustBreakpointLineAndColumn(params.fileName, &line, &column,
                                           &valid)) {
            d->pendingBreakpoints.insertMulti(params.fileName, id);
            return;
        }
        if (!valid)
            return;
    }

820
    if (d->m_adapter.activeDebuggerClient()) {
821
        d->m_adapter.activeDebuggerClient()->insertBreakpoint(id, line, column);
822 823
    } else {
        foreach (QmlDebuggerClient *client, d->m_adapter.debuggerClients()) {
824
            client->insertBreakpoint(id, line, column);
825 826 827 828 829 830 831
        }
    }
}

void QmlEngine::removeBreakpoint(BreakpointModelId id)
{
    BreakHandler *handler = breakHandler();
832 833 834 835 836 837 838 839 840 841 842 843 844 845 846

    const BreakpointParameters &params = handler->breakpointData(id);
    if (params.type == BreakpointByFileAndLine &&
            d->pendingBreakpoints.contains(params.fileName)) {
        QHash<QString, BreakpointModelId>::iterator i =
                d->pendingBreakpoints.find(params.fileName);
        while (i != d->pendingBreakpoints.end() && i.key() == params.fileName) {
            if (i.value() == id) {
                d->pendingBreakpoints.erase(i);
                return;
            }
            ++i;
        }
    }

847 848 849 850 851
    BreakpointState state = handler->state(id);
    QTC_ASSERT(state == BreakpointRemoveRequested, qDebug() << id << this << state);
    handler->notifyBreakpointRemoveProceeding(id);

    if (d->m_adapter.activeDebuggerClient()) {
852
        d->m_adapter.activeDebuggerClient()->removeBreakpoint(id);
853 854
    } else {
        foreach (QmlDebuggerClient *client, d->m_adapter.debuggerClients()) {
855
            client->removeBreakpoint(id);
856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871
        }
    }

    if (handler->state(id) == BreakpointRemoveProceeding) {
        handler->notifyBreakpointRemoveOk(id);
    }
}

void QmlEngine::changeBreakpoint(BreakpointModelId id)
{
    BreakHandler *handler = breakHandler();
    BreakpointState state = handler->state(id);
    QTC_ASSERT(state == BreakpointChangeRequested, qDebug() << id << this << state);
    handler->notifyBreakpointChangeProceeding(id);

    if (d->m_adapter.activeDebuggerClient()) {
872
        d->m_adapter.activeDebuggerClient()->changeBreakpoint(id);
873 874
    } else {
        foreach (QmlDebuggerClient *client, d->m_adapter.debuggerClients()) {
875
            client->changeBreakpoint(id);
876 877 878 879 880 881 882 883
        }
    }

    if (handler->state(id) == BreakpointChangeProceeding) {
        handler->notifyBreakpointChangeOk(id);
    }
}

884 885
void QmlEngine::attemptBreakpointSynchronization()
{
886 887 888 889 890
    if (!stateAcceptsBreakpointChanges()) {
        showMessage(_("BREAKPOINT SYNCHRONIZATION NOT POSSIBLE IN CURRENT STATE"));
        return;
    }

hjk's avatar
hjk committed
891
    BreakHandler *handler = breakHandler();
892

893
    DebuggerEngine *bpOwner = isSlaveEngine() ? masterEngine() : this;
894
    foreach (BreakpointModelId id, handler->unclaimedBreakpointIds()) {
895 896
        // Take ownership of the breakpoint. Requests insertion.
        if (acceptsBreakpoint(id))
897
            handler->setEngine(id, bpOwner);
898 899
    }

900
    foreach (BreakpointModelId id, handler->engineBreakpointIds(bpOwner)) {
901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920
        switch (handler->state(id)) {
        case BreakpointNew:
            // Should not happen once claimed.
            QTC_CHECK(false);
            continue;
        case BreakpointInsertRequested:
            insertBreakpoint(id);
            continue;
        case BreakpointChangeRequested:
            changeBreakpoint(id);
            continue;
        case BreakpointRemoveRequested:
            removeBreakpoint(id);
            continue;
        case BreakpointChangeProceeding:
        case BreakpointInsertProceeding:
        case BreakpointRemoveProceeding:
        case BreakpointInserted:
        case BreakpointDead:
            continue;
921
        }
922
        QTC_ASSERT(false, qDebug() << "UNKNOWN STATE"  << id << state());
923 924
    }

925
    DebuggerEngine::attemptBreakpointSynchronization();
926

927
    if (d->m_adapter.activeDebuggerClient()) {
928
        d->m_adapter.activeDebuggerClient()->synchronizeBreakpoints();
929 930
    } else {
        foreach (QmlDebuggerClient *client, d->m_adapter.debuggerClients()) {
931
            client->synchronizeBreakpoints();
932
        }
933
    }
934 935
}

936
bool QmlEngine::acceptsBreakpoint(BreakpointModelId id) const
937
{
938
    if (!breakHandler()->breakpointData(id).isCppBreakpoint())
939 940 941 942 943
            return true;

    //If it is a Cpp Breakpoint query if the type can be also handled by the debugger client
    //TODO: enable setting of breakpoints before start of debug session
    //For now, the event breakpoint can be set after the activeDebuggerClient is known
944
    //This is because the older client does not support BreakpointOnQmlSignalHandler
945 946 947 948 949
    bool acceptBreakpoint = false;
    if (d->m_adapter.activeDebuggerClient()) {
        acceptBreakpoint = d->m_adapter.activeDebuggerClient()->acceptsBreakpoint(id);
    }
    return acceptBreakpoint;
950 951
}

952 953 954 955 956 957 958 959 960 961 962 963 964
void QmlEngine::loadSymbols(const QString &moduleName)
{
    Q_UNUSED(moduleName)
}

void QmlEngine::loadAllSymbols()
{
}

void QmlEngine::reloadModules()
{
}

965 966 967 968 969 970 971
void QmlEngine::reloadSourceFiles()
{
    if (d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->getSourceFiles();
    }
}

972 973 974 975 976 977 978 979 980 981 982
void QmlEngine::requestModuleSymbols(const QString &moduleName)
{
    Q_UNUSED(moduleName)
}

//////////////////////////////////////////////////////////////////////
//
// Tooltip specific stuff
//
//////////////////////////////////////////////////////////////////////

983
bool QmlEngine::setToolTipExpression(const QPoint &mousePos,
984
    TextEditor::ITextEditor *editor, const DebuggerToolTipContext &ctx)
985
{
986
    // This is processed by QML inspector, which has dependencies to
hjk's avatar
hjk committed
987
    // the qml js editor. Makes life easier.
988
    emit tooltipRequested(mousePos, editor, ctx.position);
989
    return true;
990 991 992 993 994 995 996 997
}

//////////////////////////////////////////////////////////////////////
//
// Watch specific stuff
//
//////////////////////////////////////////////////////////////////////

998
void QmlEngine::assignValueInDebugger(const WatchData *data,
hjk's avatar
hjk committed
999
    const QString &expression, const QVariant &valueV)
1000
{
1001 1002 1003 1004
    if (!expression.isEmpty() && d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->assignValueInDebugger(data,
                                                                   expression,
                                                                   valueV);
1005
    }
1006 1007
}

hjk's avatar
hjk committed
1008 1009
void QmlEngine::updateWatchData(const WatchData &data,
    const WatchUpdateFlags &)
1010
{
Olivier Goffart's avatar
Olivier Goffart committed
1011
//    qDebug() << "UPDATE WATCH DATA" << data.toString();
1012
    //watchHandler()->rebuildModel();
1013
    showStatusMessage(tr("Stopped."), 5000);
1014