qmlengine.cpp 43.9 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
        if (startParameters().startMode == AttachToRemoteServer)
597 598
            m_noDebugOutputTimer.start();
        else if (startParameters().startMode == AttachToRemoteProcess)
599 600
            beginConnection();
        else
601
            startApplicationLauncher();
602
    } else {
Kai Koehne's avatar
Kai Koehne committed
603
        m_noDebugOutputTimer.start();
604
    }
605 606 607 608
}

void QmlEngine::startApplicationLauncher()
{
Kai Koehne's avatar
Kai Koehne committed
609
    if (!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);
Kai Koehne's avatar
Kai Koehne committed
615
        m_applicationLauncher.start(ProjectExplorer::ApplicationLauncher::Gui,
616 617 618
                                    startParameters().executable,
                                    startParameters().processArgs);
    }
619
}
Lasse Holmstedt's avatar
Lasse Holmstedt committed
620

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

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

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

    notifyEngineRemoteSetupDone();
638
    notifyEngineSetupOk();
639 640 641 642 643 644

    // 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);
645 646 647 648
}

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

    notifyEngineRemoteSetupFailed();
654
    notifyEngineSetupFailed();
655 656
}

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

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

hjk's avatar
hjk committed
668 669 670 671 672
    notifyInferiorShutdownOk();
}

void QmlEngine::shutdownEngine()
{
673 674 675
    m_noDebugOutputTimer.stop();

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

678
    notifyEngineShutdownOk();
679 680
    if (!isSlaveEngine())
        showMessage(QString(), StatusBar);
681 682
}

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

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

        notifyEngineSetupOk();
    }
699 700
}

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

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

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

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

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

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

void QmlEngine::executeNextI()
{
758
    executeNext();
759 760
}

761
void QmlEngine::executeRunToLine(const ContextData &data)
762
{
763 764 765
    QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
    showStatusMessage(tr("Run to line  %1 (%2) requested...").arg(data.lineNumber).arg(data.fileName), 5000);
    resetLocation();
766 767 768 769 770 771
    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
772 773
    if (m_adapter.activeDebuggerClient())
        m_adapter.activeDebuggerClient()->executeRunToLine(modifiedData);
774 775
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
776 777 778 779 780 781 782 783
}

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

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

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

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

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

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

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)) {
Kai Koehne's avatar
Kai Koehne committed
820
            pendingBreakpoints.insertMulti(params.fileName, id);
821 822 823 824 825 826
            return;
        }
        if (!valid)
            return;
    }

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

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

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

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

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

    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
878 879
    if (m_adapter.activeDebuggerClient()) {
        m_adapter.activeDebuggerClient()->changeBreakpoint(id);
880
    } else {
Kai Koehne's avatar
Kai Koehne committed
881
        foreach (BaseQmlDebuggerClient *client, m_adapter.debuggerClients()) {
882
            client->changeBreakpoint(id);
883 884 885 886 887 888 889 890
        }
    }

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

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

hjk's avatar
hjk committed
898
    BreakHandler *handler = breakHandler();
899

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

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

932
    DebuggerEngine::attemptBreakpointSynchronization();
933

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