qmlengine.cpp 43.8 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"
34
#include "baseqmldebuggerclient.h"
35
#include "qmlinspectoragent.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 <qmldebug/baseenginedebugclient.h>
59
#include <qmljseditor/qmljseditorconstants.h>
60 61
#include <qmljs/parser/qmljsast_p.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
62

63
#include <utils/environment.h>
64 65
#include <utils/qtcassert.h>

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

#include <texteditor/itexteditor.h>
72

73 74 75 76 77
#include <QDateTime>
#include <QDebug>
#include <QDir>
#include <QFileInfo>
#include <QTimer>
78

79 80 81 82 83 84
#include <QAction>
#include <QApplication>
#include <QMainWindow>
#include <QMessageBox>
#include <QPlainTextEdit>
#include <QToolTip>
85

86 87
#include <QTcpSocket>
#include <QHostAddress>
88

89 90 91 92 93 94 95 96
#define DEBUG_QML 1
#if DEBUG_QML
#   define SDEBUG(s) qDebug() << s
#else
#   define SDEBUG(s)
#endif
# define XSDEBUG(s) qDebug() << s

97 98
using namespace QmlJS;
using namespace AST;
hjk's avatar
hjk committed
99

100 101 102
namespace Debugger {
namespace Internal {

Kai Koehne's avatar
Kai Koehne committed
103
class ASTWalker : public Visitor
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
{
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
131 132 133
        if (!ast->statement)
            return true;

134 135 136 137 138 139 140 141 142 143 144
        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
145 146
            if (!block || !block->statements)
                return true;
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 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
            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;
};
265

266 267 268 269 270 271
///////////////////////////////////////////////////////////////////////
//
// QmlEngine
//
///////////////////////////////////////////////////////////////////////

272 273
QmlEngine::QmlEngine(const DebuggerStartParameters &startParameters,
        DebuggerEngine *masterEngine)
Kai Koehne's avatar
Kai Koehne committed
274 275 276 277 278
  : DebuggerEngine(startParameters, QmlLanguage, masterEngine)
  , m_adapter(this)
  , m_inspectorAdapter(&m_adapter, this)
  , m_retryOnConnectFail(false)
  , m_automaticConnect(false)
279
{
Friedemann Kleint's avatar
Friedemann Kleint committed
280
    setObjectName(QLatin1String("QmlEngine"));
281 282 283 284 285

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

Kai Koehne's avatar
Kai Koehne committed
286
    connect(&m_adapter, SIGNAL(connectionError(QAbstractSocket::SocketError)),
287
        SLOT(connectionError(QAbstractSocket::SocketError)));
Kai Koehne's avatar
Kai Koehne committed
288
    connect(&m_adapter, SIGNAL(serviceConnectionError(QString)),
289
        SLOT(serviceConnectionError(QString)));
Kai Koehne's avatar
Kai Koehne committed
290
    connect(&m_adapter, SIGNAL(connected()),
291
        SLOT(connectionEstablished()));
Kai Koehne's avatar
Kai Koehne committed
292
    connect(&m_adapter, SIGNAL(connectionStartupFailed()),
293 294
        SLOT(connectionStartupFailed()));

295 296 297 298
    connect(this, SIGNAL(stateChanged(Debugger::DebuggerState)),
            SLOT(updateCurrentContext()));
    connect(this->stackHandler(), SIGNAL(currentIndexChanged()),
            SLOT(updateCurrentContext()));
Kai Koehne's avatar
Kai Koehne committed
299
    connect(&m_inspectorAdapter, SIGNAL(selectionChanged()),
300
            SLOT(updateCurrentContext()));
Kai Koehne's avatar
Kai Koehne committed
301
    connect(m_inspectorAdapter.agent(), SIGNAL(
302 303
                expressionResult(quint32,QVariant)),
            SLOT(expressionEvaluated(quint32,QVariant)));
Kai Koehne's avatar
Kai Koehne committed
304
    connect(m_adapter.messageClient(),
305
            SIGNAL(message(QtMsgType,QString,
306
                           QmlDebug::QDebugContextInfo)),
307
            SLOT(appendDebugOutput(QtMsgType,QString,
308
                                   QmlDebug::QDebugContextInfo)));
309

310

Kai Koehne's avatar
Kai Koehne committed
311
    connect(&m_applicationLauncher,
312 313
        SIGNAL(processExited(int)),
        SLOT(disconnected()));
Kai Koehne's avatar
Kai Koehne committed
314
    connect(&m_applicationLauncher,
Robert Loehning's avatar
Robert Loehning committed
315 316
        SIGNAL(appendMessage(QString,Utils::OutputFormat)),
        SLOT(appendMessage(QString,Utils::OutputFormat)));
Kai Koehne's avatar
Kai Koehne committed
317
    connect(&m_applicationLauncher,
318
            SIGNAL(processStarted()),
Kai Koehne's avatar
Kai Koehne committed
319
            &m_noDebugOutputTimer,
320
            SLOT(start()));
321

Kai Koehne's avatar
Kai Koehne committed
322 323 324
    m_outputParser.setNoOutputText(ProjectExplorer::ApplicationLauncher
                                   ::msgWinCannotRetrieveDebuggingOutput());
    connect(&m_outputParser, SIGNAL(waitingForConnectionOnPort(quint16)),
325
            this, SLOT(beginConnection(quint16)));
Kai Koehne's avatar
Kai Koehne committed
326
    connect(&m_outputParser, SIGNAL(waitingForConnectionViaOst()),
327
            this, SLOT(beginConnection()));
Kai Koehne's avatar
Kai Koehne committed
328
    connect(&m_outputParser, SIGNAL(noOutputMessage()),
329
            this, SLOT(tryToConnect()));
Kai Koehne's avatar
Kai Koehne committed
330
    connect(&m_outputParser, SIGNAL(errorMessage(QString)),
331
            this, SLOT(appStartupFailed(QString)));
332

Kai Koehne's avatar
Kai Koehne committed
333 334 335 336 337
    // Only wait 8 seconds for the 'Waiting for connection' on application output,
    // then just try to connect (application output might be redirected / blocked)
    m_noDebugOutputTimer.setSingleShot(true);
    m_noDebugOutputTimer.setInterval(8000);
    connect(&m_noDebugOutputTimer, SIGNAL(timeout()), this, SLOT(tryToConnect()));
338 339

    qtMessageLogHandler()->setHasEditableRow(true);
340 341 342 343 344

    connect(ModelManagerInterface::instance(),
            SIGNAL(documentUpdated(QmlJS::Document::Ptr)),
            this,
            SLOT(documentUpdated(QmlJS::Document::Ptr)));
345 346

    // we won't get any debug output
347
    if (startParameters.useTerminal) {
Kai Koehne's avatar
Kai Koehne committed
348 349 350
        m_noDebugOutputTimer.setInterval(0);
        m_retryOnConnectFail = true;
        m_automaticConnect = true;
351
    }
352 353 354
}

QmlEngine::~QmlEngine()
hjk's avatar
hjk committed
355
{
356 357 358 359 360 361
    ExtensionSystem::PluginManager *pluginManager =
        ExtensionSystem::PluginManager::instance();

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

363 364 365
    QList<Core::IEditor *> editorsToClose;

    QHash<QString, QWeakPointer<TextEditor::ITextEditor> >::iterator iter;
Kai Koehne's avatar
Kai Koehne committed
366
    for (iter = m_sourceEditors.begin(); iter != m_sourceEditors.end(); ++iter) {
367 368 369 370 371
        QWeakPointer<TextEditor::ITextEditor> textEditPtr = iter.value();
        if (textEditPtr)
            editorsToClose << textEditPtr.data();
    }
    Core::EditorManager::instance()->closeEditors(editorsToClose);
hjk's avatar
hjk committed
372
}
373

hjk's avatar
hjk committed
374
void QmlEngine::setupInferior()
375
{
hjk's avatar
hjk committed
376
    QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state());
Lasse Holmstedt's avatar
Lasse Holmstedt committed
377

378
    notifyInferiorSetupOk();
379

Kai Koehne's avatar
Kai Koehne committed
380
    if (m_automaticConnect)
381
        beginConnection();
hjk's avatar
hjk committed
382
}
hjk's avatar
hjk committed
383

con's avatar
con committed
384
void QmlEngine::appendMessage(const QString &msg, Utils::OutputFormat /* format */)
hjk's avatar
hjk committed
385
{
386
    showMessage(msg, AppOutput); // FIXME: Redirect to RunControl
hjk's avatar
hjk committed
387 388
}

Lasse Holmstedt's avatar
Lasse Holmstedt committed
389 390 391 392
void QmlEngine::connectionEstablished()
{
    attemptBreakpointSynchronization();

393 394 395 396
    if (!watchHandler()->watcherNames().isEmpty()) {
        synchronizeWatchers();
    }
    connect(watchersModel(),SIGNAL(layoutChanged()),this,SLOT(synchronizeWatchers()));
Christiaan Janssen's avatar
Christiaan Janssen committed
397

398 399
    if (state() == EngineRunRequested)
        notifyEngineRunAndInferiorRunOk();
Lasse Holmstedt's avatar
Lasse Holmstedt committed
400
}
401

402 403
void QmlEngine::tryToConnect(quint16 port)
{
404
    showMessage(QLatin1String("QML Debugger: No application output received in time, trying to connect ..."), LogStatus);
Kai Koehne's avatar
Kai Koehne committed
405
    m_retryOnConnectFail = true;
406 407 408 409 410 411 412 413 414 415 416
    if (state() == EngineRunRequested) {
        if (isSlaveEngine()) {
            // Probably cpp is being debugged and hence we did not get the output yet.
            if (!masterEngine()->isDying())
                m_noDebugOutputTimer.start();
            else
                appStartupFailed(tr("No application output received in time"));
        } else {
            beginConnection(port);
        }
    } else {
Kai Koehne's avatar
Kai Koehne committed
417
        m_automaticConnect = true;
418
    }
419 420
}

421
void QmlEngine::beginConnection(quint16 port)
422
{
Kai Koehne's avatar
Kai Koehne committed
423
    m_noDebugOutputTimer.stop();
424

Kai Koehne's avatar
Kai Koehne committed
425
    if (state() != EngineRunRequested && m_retryOnConnectFail)
426 427
        return;

428
    QTC_ASSERT(state() == EngineRunRequested, return);
429

430 431 432 433 434 435
    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"
436
                   << startParameters().connParams.port << "from start parameters.");
Kai Koehne's avatar
Kai Koehne committed
437
        m_adapter.beginConnectionTcp(startParameters().qmlServerAddress, port);
438 439 440 441 442
        return;
    }
    if (startParameters().communicationChannel
           == DebuggerStartParameters::CommunicationChannelTcpIp) {
        // no port from application output, use the one from start parameters ...
Kai Koehne's avatar
Kai Koehne committed
443
        m_adapter.beginConnectionTcp(startParameters().qmlServerAddress,
444 445 446 447
                                        startParameters().qmlServerPort);
    } else {
        QTC_CHECK(startParameters().communicationChannel
                  == DebuggerStartParameters::CommunicationChannelUsb);
Kai Koehne's avatar
Kai Koehne committed
448
        m_adapter.beginConnectionOst(startParameters().remoteChannel);
449
    }
Lasse Holmstedt's avatar
Lasse Holmstedt committed
450 451
}

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

hjk's avatar
hjk committed
460
    QMessageBox *infoBox = new QMessageBox(Core::ICore::mainWindow());
461 462
    infoBox->setIcon(QMessageBox::Critical);
    infoBox->setWindowTitle(tr("Qt Creator"));
463 464 465 466 467
    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);
468 469 470
    infoBox->setModal(true);

    connect(infoBox, SIGNAL(finished(int)),
471
            this, SLOT(errorMessageBoxFinished(int)));
472 473 474 475

    infoBox->show();
}

476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491
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();
}

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

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

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

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

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

537
void QmlEngine::filterApplicationMessage(const QString &output, int /*channel*/)
538
{
Kai Koehne's avatar
Kai Koehne committed
539
    m_outputParser.processOutput(output);
540 541 542 543 544 545 546 547 548 549
}

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);
}

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

        Core::EditorManager *editorManager = Core::EditorManager::instance();
Aurindam Jana's avatar
Aurindam Jana committed
560 561 562 563 564 565 566 567 568 569
        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) {
hjk's avatar
hjk committed
570
            editor = Core::EditorManager::openEditorWithContents(QmlJSEditor::Constants::C_QMLJSEDITOR_ID,
571 572 573 574 575
                                                           &titlePattern);
            if (editor) {
                editor->setProperty(Constants::OPENED_BY_DEBUGGER, true);
            }

Kai Koehne's avatar
Kai Koehne committed
576
            updateEditor(editor, m_sourceDocuments.value(fileName));
577
        }
hjk's avatar
hjk committed
578
        Core::EditorManager::activateEditor(editor);
579

580 581 582 583 584
    } else {
        DebuggerEngine::gotoLocation(location);
    }
}

585 586
void QmlEngine::closeConnection()
{
587
    disconnect(watchersModel(),SIGNAL(layoutChanged()),this,SLOT(synchronizeWatchers()));
Kai Koehne's avatar
Kai Koehne committed
588
    m_adapter.closeConnection();
589 590
}

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

595
    if (!isSlaveEngine()) {
596 597 598
        if (startParameters().startMode == AttachToRemoteServer)
            beginConnection();
        else
599
            startApplicationLauncher();
600
    } else {
Kai Koehne's avatar
Kai Koehne committed
601
        m_noDebugOutputTimer.start();
602
    }
603 604 605 606
}

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

619 620
void QmlEngine::stopApplicationLauncher()
{
Kai Koehne's avatar
Kai Koehne committed
621 622 623 624
    if (m_applicationLauncher.isRunning()) {
        disconnect(&m_applicationLauncher, SIGNAL(processExited(int)),
                   this, SLOT(disconnected()));
        m_applicationLauncher.stop();
625
    }
626 627
}

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

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

    notifyEngineRemoteSetupDone();
636
    notifyEngineSetupOk();
637 638 639 640 641 642

    // The remote setup can take while especialy with mixed debugging.
    // Just waiting for 8 seconds is not enough. Increase the timeout
    // to 60 s
    // In case we get an output the m_outputParser will start the connection.
    m_noDebugOutputTimer.setInterval(60000);
643 644 645 646
}

void QmlEngine::handleRemoteSetupFailed(const QString &message)
{
647 648 649
    if (isMasterEngine())
        QMessageBox::critical(0,tr("Failed to start application"),
            tr("Application startup failed: %1").arg(message));
650 651

    notifyEngineRemoteSetupFailed();
652
    notifyEngineSetupFailed();
653 654
}

hjk's avatar
hjk committed
655
void QmlEngine::shutdownInferior()
656
{
Kai Koehne's avatar
Kai Koehne committed
657 658
    if (m_adapter.activeDebuggerClient())
        m_adapter.activeDebuggerClient()->endSession();
659

660 661
    if (isSlaveEngine()) {
        resetLocation();
Lasse Holmstedt's avatar
Lasse Holmstedt committed
662
    }
663
    stopApplicationLauncher();
664
    closeConnection();
665

hjk's avatar
hjk committed
666 667 668 669 670
    notifyInferiorShutdownOk();
}

void QmlEngine::shutdownEngine()
{
671 672 673
    m_noDebugOutputTimer.stop();

   // double check (ill engine?):
674
    stopApplicationLauncher();
Lasse Holmstedt's avatar
Lasse Holmstedt committed
675

676
    notifyEngineShutdownOk();
677 678
    if (!isSlaveEngine())
        showMessage(QString(), StatusBar);
679 680
}

hjk's avatar
hjk committed
681
void QmlEngine::setupEngine()
682
{
683
    if (startParameters().requestRemoteSetup) {
684
        // we need to get the port first
685
        notifyEngineRequestRemoteSetup();
686
    } else {
Kai Koehne's avatar
Kai Koehne committed
687 688
        m_applicationLauncher.setEnvironment(startParameters().environment);
        m_applicationLauncher.setWorkingDirectory(startParameters().workingDirectory);
Lasse Holmstedt's avatar
Lasse Holmstedt committed
689

690
        // We can't do this in the constructore because runControl() isn't yet defined
Kai Koehne's avatar
Kai Koehne committed
691
        connect(&m_applicationLauncher, SIGNAL(bringToForegroundRequested(qint64)),
692 693 694 695 696
                runControl(), SLOT(bringApplicationToForeground(qint64)),
                Qt::UniqueConnection);

        notifyEngineSetupOk();
    }
697 698
}

699 700
void QmlEngine::continueInferior()
{
hjk's avatar
hjk committed
701
    QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
Kai Koehne's avatar
Kai Koehne committed
702 703
    if (m_adapter.activeDebuggerClient()) {
        m_adapter.activeDebuggerClient()->continueInferior();
704
    }
705
    resetLocation();
hjk's avatar
hjk committed
706 707
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
708 709 710 711
}

void QmlEngine::interruptInferior()
{
Kai Koehne's avatar
Kai Koehne committed
712 713
    if (m_adapter.activeDebuggerClient()) {
        m_adapter.activeDebuggerClient()->interruptInferior();
714
    }
715
    notifyInferiorStopOk();
716 717 718 719
}

void QmlEngine::executeStep()
{
Kai Koehne's avatar
Kai Koehne committed
720 721
    if (m_adapter.activeDebuggerClient()) {
        m_adapter.activeDebuggerClient()->executeStep();
722
    }
hjk's avatar
hjk committed
723 724
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
725 726 727 728
}

void QmlEngine::executeStepI()
{
Kai Koehne's avatar
Kai Koehne committed
729 730
    if (m_adapter.activeDebuggerClient()) {
        m_adapter.activeDebuggerClient()->executeStepI();
731
    }
hjk's avatar
hjk committed
732 733
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
734 735 736 737
}

void QmlEngine::executeStepOut()
{
Kai Koehne's avatar
Kai Koehne committed
738 739
    if (m_adapter.activeDebuggerClient()) {
        m_adapter.activeDebuggerClient()->executeStepOut();
740
    }
hjk's avatar
hjk committed
741 742
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
743 744 745 746
}

void QmlEngine::executeNext()
{
Kai Koehne's avatar
Kai Koehne committed
747 748
    if (m_adapter.activeDebuggerClient()) {
        m_adapter.activeDebuggerClient()->executeNext();
749
    }
hjk's avatar
hjk committed
750 751
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
752 753 754 755
}

void QmlEngine::executeNextI()
{
756
    executeNext();
757 758
}

759
void QmlEngine::executeRunToLine(const ContextData &data)
760
{
761 762 763
    QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
    showStatusMessage(tr("Run to line  %1 (%2) requested...").arg(data.lineNumber).arg(data.fileName), 5000);
    resetLocation();
764 765 766 767 768 769
    ContextData modifiedData = data;
    quint32 line = data.lineNumber;
    quint32 column;
    bool valid;
    if (adjustBreakpointLineAndColumn(data.fileName, &line, &column, &valid))
        modifiedData.lineNumber = line;
Kai Koehne's avatar
Kai Koehne committed
770 771
    if (m_adapter.activeDebuggerClient())
        m_adapter.activeDebuggerClient()->executeRunToLine(modifiedData);
772 773
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
774 775 776 777 778 779 780 781
}

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

782
void QmlEngine::executeJumpToLine(const ContextData &data)
783
{
784
    Q_UNUSED(data)
785 786 787 788 789
    XSDEBUG("FIXME:  QmlEngine::executeJumpToLine()");
}

void QmlEngine::activateFrame(int index)
{
790 791 792
    if (state() != InferiorStopOk && state() != InferiorUnrunnable)
        return;

Kai Koehne's avatar
Kai Koehne committed
793 794
    if (m_adapter.activeDebuggerClient()) {
        m_adapter.activeDebuggerClient()->activateFrame(index);
795
    }
796
    gotoLocation(stackHandler()->frames().value(index));
797 798 799 800 801 802 803
}

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

804 805 806 807 808 809 810
void QmlEngine::insertBreakpoint(BreakpointModelId id)
{
    BreakHandler *handler = breakHandler();
    BreakpointState state = handler->state(id);
    QTC_ASSERT(state == BreakpointInsertRequested, qDebug() << id << this << state);
    handler->notifyBreakpointInsertProceeding(id);

811 812 813 814 815 816 817
    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)) {
Kai Koehne's avatar
Kai Koehne committed
818
            pendingBreakpoints.insertMulti(params.fileName, id);
819 820 821 822 823 824
            return;
        }
        if (!valid)
            return;
    }

Kai Koehne's avatar
Kai Koehne committed
825 826
    if (m_adapter.activeDebuggerClient()) {
        m_adapter.activeDebuggerClient()->insertBreakpoint(id, line, column);
827
    } else {
Kai Koehne's avatar
Kai Koehne committed
828
        foreach (BaseQmlDebuggerClient *client, m_adapter.debuggerClients()) {
829
            client->insertBreakpoint(id, line, column);
830 831 832 833 834 835 836
        }
    }
}

void QmlEngine::removeBreakpoint(BreakpointModelId id)
{
    BreakHandler *handler = breakHandler();
837 838 839

    const BreakpointParameters &params = handler->breakpointData(id);
    if (params.type == BreakpointByFileAndLine &&
Kai Koehne's avatar
Kai Koehne committed
840
            pendingBreakpoints.contains(params.fileName)) {
841
        QHash<QString, BreakpointModelId>::iterator i =
Kai Koehne's avatar
Kai Koehne committed
842 843
                pendingBreakpoints.find(params.fileName);
        while (i != pendingBreakpoints.end() && i.key() == params.fileName) {
844
            if (i.value() == id) {
Kai Koehne's avatar
Kai Koehne committed
845
                pendingBreakpoints.erase(i);
846 847 848 849 850 851
                return;
            }
            ++i;
        }
    }

852 853 854 855
    BreakpointState state = handler->state(id);
    QTC_ASSERT(state == BreakpointRemoveRequested, qDebug() << id << this << state);
    handler->notifyBreakpointRemoveProceeding(id);

Kai Koehne's avatar
Kai Koehne committed
856 857
    if (m_adapter.activeDebuggerClient()) {
        m_adapter.activeDebuggerClient()->removeBreakpoint(id);
858
    } else {
Kai Koehne's avatar
Kai Koehne committed
859
        foreach (BaseQmlDebuggerClient *client, m_adapter.debuggerClients()) {
860
            client->removeBreakpoint(id);
861 862 863 864 865 866 867 868 869 870 871 872 873 874 875
        }
    }

    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);

Kai Koehne's avatar
Kai Koehne committed
876 877
    if (m_adapter.activeDebuggerClient()) {
        m_adapter.activeDebuggerClient()->changeBreakpoint(id);
878
    } else {
Kai Koehne's avatar
Kai Koehne committed
879
        foreach (BaseQmlDebuggerClient *client, m_adapter.debuggerClients()) {
880
            client->changeBreakpoint(id);
881 882 883 884 885 886 887 888
        }
    }

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

889 890
void QmlEngine::attemptBreakpointSynchronization()
{
891 892 893 894 895
    if (!stateAcceptsBreakpointChanges()) {
        showMessage(_("BREAKPOINT SYNCHRONIZATION NOT POSSIBLE IN CURRENT STATE"));
        return;
    }

hjk's avatar
hjk committed
896
    BreakHandler *handler = breakHandler();
897

898
    DebuggerEngine *bpOwner = isSlaveEngine() ? masterEngine() : this;
899
    foreach (BreakpointModelId id, handler->unclaimedBreakpointIds()) {
900 901
        // Take ownership of the breakpoint. Requests insertion.
        if (acceptsBreakpoint(id))
902
            handler->setEngine(id, bpOwner);
903 904
    }

905
    foreach (BreakpointModelId id, handler->engineBreakpointIds(bpOwner)) {
906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925
        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;
926
        }
927
        QTC_ASSERT(false, qDebug() << "UNKNOWN STATE"  << id << state());
928 929
    }

930
    DebuggerEngine::attemptBreakpointSynchronization();
931

Kai Koehne's avatar
Kai Koehne committed
932 933
    if (m_adapter.activeDebuggerClient()) {
        m_adapter.activeDebuggerClient()->synchronizeBreakpoints();
934
    } else {
Kai Koehne's avatar
Kai Koehne committed
935
        foreach (BaseQmlDebuggerClient *client, m_adapter.debuggerClients()) {
936
            client->synchronizeBreakpoints();
937
        }
938
    }
939 940
}