qmlengine.cpp 43.1 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).
**
Eike Ziller's avatar
Eike Ziller committed
7
** Contact: http://www.qt-project.org/
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.
**
28 29 30 31
**
**************************************************************************/

#include "qmlengine.h"
32
#include "baseqmldebuggerclient.h"
33
#include "qmlinspectoragent.h"
34

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

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

Lasse Holmstedt's avatar
Lasse Holmstedt committed
55
#include <extensionsystem/pluginmanager.h>
56
#include <qmldebug/baseenginedebugclient.h>
57
#include <qmljseditor/qmljseditorconstants.h>
58 59
#include <qmljs/parser/qmljsast_p.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
60

61
#include <utils/environment.h>
62 63
#include <utils/qtcassert.h>

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

#include <texteditor/itexteditor.h>
70

71 72 73 74 75
#include <QDateTime>
#include <QDebug>
#include <QDir>
#include <QFileInfo>
#include <QTimer>
76

77 78 79 80 81
#include <QAction>
#include <QApplication>
#include <QMessageBox>
#include <QPlainTextEdit>
#include <QToolTip>
82

83 84
#include <QTcpSocket>
#include <QHostAddress>
85

86 87 88 89 90 91 92 93
#define DEBUG_QML 1
#if DEBUG_QML
#   define SDEBUG(s) qDebug() << s
#else
#   define SDEBUG(s)
#endif
# define XSDEBUG(s) qDebug() << s

94 95
using namespace QmlJS;
using namespace AST;
hjk's avatar
hjk committed
96

97 98 99
namespace Debugger {
namespace Internal {

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

131 132 133 134 135 136 137 138 139 140 141
        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
142 143
            if (!block || !block->statements)
                return true;
144 145 146 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
            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;
};
262

263 264 265 266 267 268
///////////////////////////////////////////////////////////////////////
//
// QmlEngine
//
///////////////////////////////////////////////////////////////////////

269 270
QmlEngine::QmlEngine(const DebuggerStartParameters &startParameters)
  : DebuggerEngine(startParameters)
Kai Koehne's avatar
Kai Koehne committed
271 272 273 274
  , m_adapter(this)
  , m_inspectorAdapter(&m_adapter, this)
  , m_retryOnConnectFail(false)
  , m_automaticConnect(false)
275
{
Friedemann Kleint's avatar
Friedemann Kleint committed
276
    setObjectName(QLatin1String("QmlEngine"));
277

278
    ExtensionSystem::PluginManager::addObject(this);
279

Kai Koehne's avatar
Kai Koehne committed
280
    connect(&m_adapter, SIGNAL(connectionError(QAbstractSocket::SocketError)),
281
        SLOT(connectionError(QAbstractSocket::SocketError)));
Kai Koehne's avatar
Kai Koehne committed
282
    connect(&m_adapter, SIGNAL(serviceConnectionError(QString)),
283
        SLOT(serviceConnectionError(QString)));
Kai Koehne's avatar
Kai Koehne committed
284
    connect(&m_adapter, SIGNAL(connected()),
285
        SLOT(connectionEstablished()));
Kai Koehne's avatar
Kai Koehne committed
286
    connect(&m_adapter, SIGNAL(connectionStartupFailed()),
287 288
        SLOT(connectionStartupFailed()));

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

304

Kai Koehne's avatar
Kai Koehne committed
305
    connect(&m_applicationLauncher,
306 307
        SIGNAL(processExited(int)),
        SLOT(disconnected()));
Kai Koehne's avatar
Kai Koehne committed
308
    connect(&m_applicationLauncher,
Robert Loehning's avatar
Robert Loehning committed
309 310
        SIGNAL(appendMessage(QString,Utils::OutputFormat)),
        SLOT(appendMessage(QString,Utils::OutputFormat)));
Kai Koehne's avatar
Kai Koehne committed
311
    connect(&m_applicationLauncher,
312
            SIGNAL(processStarted()),
Kai Koehne's avatar
Kai Koehne committed
313
            &m_noDebugOutputTimer,
314
            SLOT(start()));
315

Kai Koehne's avatar
Kai Koehne committed
316 317 318
    m_outputParser.setNoOutputText(ProjectExplorer::ApplicationLauncher
                                   ::msgWinCannotRetrieveDebuggingOutput());
    connect(&m_outputParser, SIGNAL(waitingForConnectionOnPort(quint16)),
319
            this, SLOT(beginConnection(quint16)));
Kai Koehne's avatar
Kai Koehne committed
320
    connect(&m_outputParser, SIGNAL(waitingForConnectionViaOst()),
321
            this, SLOT(beginConnection()));
Kai Koehne's avatar
Kai Koehne committed
322
    connect(&m_outputParser, SIGNAL(noOutputMessage()),
323
            this, SLOT(tryToConnect()));
Kai Koehne's avatar
Kai Koehne committed
324
    connect(&m_outputParser, SIGNAL(errorMessage(QString)),
325
            this, SLOT(appStartupFailed(QString)));
326

Kai Koehne's avatar
Kai Koehne committed
327 328 329 330 331
    // 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()));
332 333

    qtMessageLogHandler()->setHasEditableRow(true);
334 335 336 337 338

    connect(ModelManagerInterface::instance(),
            SIGNAL(documentUpdated(QmlJS::Document::Ptr)),
            this,
            SLOT(documentUpdated(QmlJS::Document::Ptr)));
339 340

    // we won't get any debug output
341
    if (startParameters.useTerminal) {
Kai Koehne's avatar
Kai Koehne committed
342 343 344
        m_noDebugOutputTimer.setInterval(0);
        m_retryOnConnectFail = true;
        m_automaticConnect = true;
345
    }
346 347 348
}

QmlEngine::~QmlEngine()
hjk's avatar
hjk committed
349
{
350 351
    if (ExtensionSystem::PluginManager::allObjects().contains(this)) {
        ExtensionSystem::PluginManager::removeObject(this);
352
    }
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
353

354 355 356
    QList<Core::IEditor *> editorsToClose;

    QHash<QString, QWeakPointer<TextEditor::ITextEditor> >::iterator iter;
Kai Koehne's avatar
Kai Koehne committed
357
    for (iter = m_sourceEditors.begin(); iter != m_sourceEditors.end(); ++iter) {
358 359 360 361 362
        QWeakPointer<TextEditor::ITextEditor> textEditPtr = iter.value();
        if (textEditPtr)
            editorsToClose << textEditPtr.data();
    }
    Core::EditorManager::instance()->closeEditors(editorsToClose);
hjk's avatar
hjk committed
363
}
364

hjk's avatar
hjk committed
365
void QmlEngine::setupInferior()
366
{
hjk's avatar
hjk committed
367
    QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state());
Lasse Holmstedt's avatar
Lasse Holmstedt committed
368

369
    notifyInferiorSetupOk();
370

Kai Koehne's avatar
Kai Koehne committed
371
    if (m_automaticConnect)
372
        beginConnection();
hjk's avatar
hjk committed
373
}
hjk's avatar
hjk committed
374

con's avatar
con committed
375
void QmlEngine::appendMessage(const QString &msg, Utils::OutputFormat /* format */)
hjk's avatar
hjk committed
376
{
377
    showMessage(msg, AppOutput); // FIXME: Redirect to RunControl
hjk's avatar
hjk committed
378 379
}

Lasse Holmstedt's avatar
Lasse Holmstedt committed
380 381 382 383
void QmlEngine::connectionEstablished()
{
    attemptBreakpointSynchronization();

384 385 386 387
    if (!watchHandler()->watcherNames().isEmpty()) {
        synchronizeWatchers();
    }
    connect(watchersModel(),SIGNAL(layoutChanged()),this,SLOT(synchronizeWatchers()));
Christiaan Janssen's avatar
Christiaan Janssen committed
388

389 390
    if (state() == EngineRunRequested)
        notifyEngineRunAndInferiorRunOk();
Lasse Holmstedt's avatar
Lasse Holmstedt committed
391
}
392

393 394
void QmlEngine::tryToConnect(quint16 port)
{
395
    showMessage(QLatin1String("QML Debugger: No application output received in time, trying to connect ..."), LogStatus);
Kai Koehne's avatar
Kai Koehne committed
396
    m_retryOnConnectFail = true;
397 398 399 400 401 402 403 404 405 406 407
    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
408
        m_automaticConnect = true;
409
    }
410 411
}

412
void QmlEngine::beginConnection(quint16 port)
413
{
Kai Koehne's avatar
Kai Koehne committed
414
    m_noDebugOutputTimer.stop();
415

Kai Koehne's avatar
Kai Koehne committed
416
    if (state() != EngineRunRequested && m_retryOnConnectFail)
417 418
        return;

419
    QTC_ASSERT(state() == EngineRunRequested, return);
420

421 422 423 424
    if (port > 0) {
        QTC_ASSERT(startParameters().connParams.port == 0
                   || startParameters().connParams.port == port,
                   qWarning() << "Port " << port << "from application output does not match"
425
                   << startParameters().connParams.port << "from start parameters.");
Kai Koehne's avatar
Kai Koehne committed
426
        m_adapter.beginConnectionTcp(startParameters().qmlServerAddress, port);
427 428
        return;
    }
429 430 431
    // no port from application output, use the one from start parameters ...
    m_adapter.beginConnectionTcp(startParameters().qmlServerAddress,
                                 startParameters().qmlServerPort);
Lasse Holmstedt's avatar
Lasse Holmstedt committed
432 433
}

434
void QmlEngine::connectionStartupFailed()
Lasse Holmstedt's avatar
Lasse Holmstedt committed
435
{
Kai Koehne's avatar
Kai Koehne committed
436
    if (m_retryOnConnectFail) {
437 438
        // retry after 3 seconds ...
        QTimer::singleShot(3000, this, SLOT(beginConnection()));
439 440
        return;
    }
441

hjk's avatar
hjk committed
442
    QMessageBox *infoBox = new QMessageBox(Core::ICore::mainWindow());
443 444
    infoBox->setIcon(QMessageBox::Critical);
    infoBox->setWindowTitle(tr("Qt Creator"));
445 446 447 448 449
    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);
450 451 452
    infoBox->setModal(true);

    connect(infoBox, SIGNAL(finished(int)),
453
            this, SLOT(errorMessageBoxFinished(int)));
454 455 456 457

    infoBox->show();
}

458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473
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();
}

474
void QmlEngine::errorMessageBoxFinished(int result)
475 476
{
    switch (result) {
477
    case QMessageBox::Retry: {
478
        beginConnection();
479 480 481 482
        break;
    }
    case QMessageBox::Help: {
        Core::HelpManager *helpManager = Core::HelpManager::instance();
483
        helpManager->handleHelpRequest(QLatin1String("qthelp://com.nokia.qtcreator/doc/creator-debugging-qml.html"));
484
        // fall through
485 486
    }
    default:
487 488 489
        if (state() == InferiorRunOk) {
            notifyInferiorSpontaneousStop();
            notifyInferiorIll();
490
        } else if (state() == EngineRunRequested) {
491
            notifyEngineRunFailed();
492
        }
Kai Koehne's avatar
Kai Koehne committed
493
        break;
494
    }
Lasse Holmstedt's avatar
Lasse Holmstedt committed
495 496
}

497
void QmlEngine::connectionError(QAbstractSocket::SocketError socketError)
Lasse Holmstedt's avatar
Lasse Holmstedt committed
498
{
hjk's avatar
hjk committed
499
    if (socketError == QAbstractSocket::RemoteHostClosedError)
500
        showMessage(tr("QML Debugger: Remote host closed connection."), StatusBar);
501

502 503 504 505
    if (!isSlaveEngine()) { // normal flow for slave engine when gdb exits
        notifyInferiorSpontaneousStop();
        notifyInferiorIll();
    }
Lasse Holmstedt's avatar
Lasse Holmstedt committed
506 507
}

508 509
void QmlEngine::serviceConnectionError(const QString &serviceName)
{
510 511
    showMessage(tr("QML Debugger: Could not connect to service '%1'.")
        .arg(serviceName), StatusBar);
512 513
}

514 515 516 517 518
bool QmlEngine::canDisplayTooltip() const
{
    return state() == InferiorRunOk || state() == InferiorStopOk;
}

519
void QmlEngine::filterApplicationMessage(const QString &output, int /*channel*/)
520
{
Kai Koehne's avatar
Kai Koehne committed
521
    m_outputParser.processOutput(output);
522 523 524 525 526 527 528 529 530 531
}

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

532 533 534
void QmlEngine::gotoLocation(const Location &location)
{
    const QString fileName = location.fileName();
Daniel Molkentin's avatar
Daniel Molkentin committed
535 536
    // TODO: QUrl::isLocalFile() once we depend on Qt 4.8
    if (QUrl(fileName).scheme().compare(QLatin1String("file"), Qt::CaseInsensitive) == 0) {
537
        // internal file from source files -> show generated .js
Kai Koehne's avatar
Kai Koehne committed
538
        QTC_ASSERT(m_sourceDocuments.contains(fileName), return);
539 540 541
        Core::IEditor *editor = 0;

        Core::EditorManager *editorManager = Core::EditorManager::instance();
Aurindam Jana's avatar
Aurindam Jana committed
542 543 544 545 546 547 548 549 550 551
        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
552
            editor = Core::EditorManager::openEditorWithContents(QmlJSEditor::Constants::C_QMLJSEDITOR_ID,
553 554 555 556 557
                                                           &titlePattern);
            if (editor) {
                editor->setProperty(Constants::OPENED_BY_DEBUGGER, true);
            }

Kai Koehne's avatar
Kai Koehne committed
558
            updateEditor(editor, m_sourceDocuments.value(fileName));
559
        }
hjk's avatar
hjk committed
560
        Core::EditorManager::activateEditor(editor);
561

562 563 564 565 566
    } else {
        DebuggerEngine::gotoLocation(location);
    }
}

567 568
void QmlEngine::closeConnection()
{
569
    disconnect(watchersModel(),SIGNAL(layoutChanged()),this,SLOT(synchronizeWatchers()));
Kai Koehne's avatar
Kai Koehne committed
570
    m_adapter.closeConnection();
571 572
}

hjk's avatar
hjk committed
573 574 575
void QmlEngine::runEngine()
{
    QTC_ASSERT(state() == EngineRunRequested, qDebug() << state());
Lasse Holmstedt's avatar
Lasse Holmstedt committed
576

577
    if (!isSlaveEngine()) {
578
        if (startParameters().startMode == AttachToRemoteServer)
579 580
            m_noDebugOutputTimer.start();
        else if (startParameters().startMode == AttachToRemoteProcess)
581 582
            beginConnection();
        else
583
            startApplicationLauncher();
584
    } else {
Kai Koehne's avatar
Kai Koehne committed
585
        m_noDebugOutputTimer.start();
586
    }
587 588 589 590
}

void QmlEngine::startApplicationLauncher()
{
Kai Koehne's avatar
Kai Koehne committed
591
    if (!m_applicationLauncher.isRunning()) {
592 593 594 595
        appendMessage(tr("Starting %1 %2").arg(
                          QDir::toNativeSeparators(startParameters().executable),
                          startParameters().processArgs)
                      + QLatin1Char('\n')
con's avatar
con committed
596
                     , Utils::NormalMessageFormat);
Kai Koehne's avatar
Kai Koehne committed
597
        m_applicationLauncher.start(ProjectExplorer::ApplicationLauncher::Gui,
598 599 600
                                    startParameters().executable,
                                    startParameters().processArgs);
    }
601
}
Lasse Holmstedt's avatar
Lasse Holmstedt committed
602

603 604
void QmlEngine::stopApplicationLauncher()
{
Kai Koehne's avatar
Kai Koehne committed
605 606 607 608
    if (m_applicationLauncher.isRunning()) {
        disconnect(&m_applicationLauncher, SIGNAL(processExited(int)),
                   this, SLOT(disconnected()));
        m_applicationLauncher.stop();
609
    }
610 611
}

612
void QmlEngine::notifyEngineRemoteSetupDone(int gdbServerPort, int qmlPort)
613
{
Christian Kandeler's avatar
Christian Kandeler committed
614 615
    if (qmlPort != -1)
        startParameters().qmlServerPort = qmlPort;
616

617
    DebuggerEngine::notifyEngineRemoteSetupDone(gdbServerPort, qmlPort);
618
    notifyEngineSetupOk();
619 620 621 622 623 624

    // 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);
625 626
}

627
void QmlEngine::notifyEngineRemoteSetupFailed(const QString &message)
628
{
629
    DebuggerEngine::notifyEngineRemoteSetupFailed(message);
630 631 632
    if (isMasterEngine())
        QMessageBox::critical(0,tr("Failed to start application"),
            tr("Application startup failed: %1").arg(message));
633

634
    notifyEngineSetupFailed();
635 636
}

hjk's avatar
hjk committed
637
void QmlEngine::shutdownInferior()
638
{
Kai Koehne's avatar
Kai Koehne committed
639 640
    if (m_adapter.activeDebuggerClient())
        m_adapter.activeDebuggerClient()->endSession();
641

642 643
    if (isSlaveEngine()) {
        resetLocation();
Lasse Holmstedt's avatar
Lasse Holmstedt committed
644
    }
645
    stopApplicationLauncher();
646
    closeConnection();
647

hjk's avatar
hjk committed
648 649 650 651 652
    notifyInferiorShutdownOk();
}

void QmlEngine::shutdownEngine()
{
653 654 655
    m_noDebugOutputTimer.stop();

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

658
    notifyEngineShutdownOk();
659 660
    if (!isSlaveEngine())
        showMessage(QString(), StatusBar);
661 662
}

hjk's avatar
hjk committed
663
void QmlEngine::setupEngine()
664
{
665
    if (startParameters().remoteSetupNeeded) {
666
        // we need to get the port first
667
        notifyEngineRequestRemoteSetup();
668
    } else {
Kai Koehne's avatar
Kai Koehne committed
669 670
        m_applicationLauncher.setEnvironment(startParameters().environment);
        m_applicationLauncher.setWorkingDirectory(startParameters().workingDirectory);
Lasse Holmstedt's avatar
Lasse Holmstedt committed
671

672
        // We can't do this in the constructore because runControl() isn't yet defined
Kai Koehne's avatar
Kai Koehne committed
673
        connect(&m_applicationLauncher, SIGNAL(bringToForegroundRequested(qint64)),
674 675 676 677 678
                runControl(), SLOT(bringApplicationToForeground(qint64)),
                Qt::UniqueConnection);

        notifyEngineSetupOk();
    }
679 680
}

681 682
void QmlEngine::continueInferior()
{
hjk's avatar
hjk committed
683
    QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
Kai Koehne's avatar
Kai Koehne committed
684 685
    if (m_adapter.activeDebuggerClient()) {
        m_adapter.activeDebuggerClient()->continueInferior();
686
    }
687
    resetLocation();
hjk's avatar
hjk committed
688 689
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
690 691 692 693
}

void QmlEngine::interruptInferior()
{
Kai Koehne's avatar
Kai Koehne committed
694 695
    if (m_adapter.activeDebuggerClient()) {
        m_adapter.activeDebuggerClient()->interruptInferior();
696
    }
697
    notifyInferiorStopOk();
698 699 700 701
}

void QmlEngine::executeStep()
{
Kai Koehne's avatar
Kai Koehne committed
702 703
    if (m_adapter.activeDebuggerClient()) {
        m_adapter.activeDebuggerClient()->executeStep();
704
    }
hjk's avatar
hjk committed
705 706
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
707 708 709 710
}

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

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

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

void QmlEngine::executeNextI()
{
738
    executeNext();
739 740
}

741
void QmlEngine::executeRunToLine(const ContextData &data)
742
{
743
    QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
Jarek Kobus's avatar
Jarek Kobus committed
744
    showStatusMessage(tr("Run to line %1 (%2) requested...").arg(data.lineNumber).arg(data.fileName), 5000);
745
    resetLocation();
746 747 748 749 750 751
    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
752 753
    if (m_adapter.activeDebuggerClient())
        m_adapter.activeDebuggerClient()->executeRunToLine(modifiedData);
754 755
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
756 757 758 759 760 761 762 763
}

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

764
void QmlEngine::executeJumpToLine(const ContextData &data)
765
{
766
    Q_UNUSED(data)
767 768 769 770 771
    XSDEBUG("FIXME:  QmlEngine::executeJumpToLine()");
}

void QmlEngine::activateFrame(int index)
{
772 773 774
    if (state() != InferiorStopOk && state() != InferiorUnrunnable)
        return;

Kai Koehne's avatar
Kai Koehne committed
775 776
    if (m_adapter.activeDebuggerClient()) {
        m_adapter.activeDebuggerClient()->activateFrame(index);
777
    }
778
    gotoLocation(stackHandler()->frames().value(index));
779 780 781 782 783 784 785
}

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

786 787 788 789 790 791 792
void QmlEngine::insertBreakpoint(BreakpointModelId id)
{
    BreakHandler *handler = breakHandler();
    BreakpointState state = handler->state(id);
    QTC_ASSERT(state == BreakpointInsertRequested, qDebug() << id << this << state);
    handler->notifyBreakpointInsertProceeding(id);

793 794 795 796 797 798 799
    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
800
            pendingBreakpoints.insertMulti(params.fileName, id);
801 802 803 804 805 806
            return;
        }
        if (!valid)
            return;
    }

Kai Koehne's avatar
Kai Koehne committed
807 808
    if (m_adapter.activeDebuggerClient()) {
        m_adapter.activeDebuggerClient()->insertBreakpoint(id, line, column);
809
    } else {
Kai Koehne's avatar
Kai Koehne committed
810
        foreach (BaseQmlDebuggerClient *client, m_adapter.debuggerClients()) {
811
            client->insertBreakpoint(id, line, column);
812 813 814 815 816 817 818
        }
    }
}

void QmlEngine::removeBreakpoint(BreakpointModelId id)
{
    BreakHandler *handler = breakHandler();
819 820 821

    const BreakpointParameters &params = handler->breakpointData(id);
    if (params.type == BreakpointByFileAndLine &&
Kai Koehne's avatar
Kai Koehne committed
822
            pendingBreakpoints.contains(params.fileName)) {
823
        QHash<QString, BreakpointModelId>::iterator i =
Kai Koehne's avatar
Kai Koehne committed
824 825
                pendingBreakpoints.find(params.fileName);
        while (i != pendingBreakpoints.end() && i.key() == params.fileName) {
826
            if (i.value() == id) {
Kai Koehne's avatar
Kai Koehne committed
827
                pendingBreakpoints.erase(i);
828 829 830 831 832 833
                return;
            }
            ++i;
        }
    }

834 835 836 837
    BreakpointState state = handler->state(id);
    QTC_ASSERT(state == BreakpointRemoveRequested, qDebug() << id << this << state);
    handler->notifyBreakpointRemoveProceeding(id);

Kai Koehne's avatar
Kai Koehne committed
838 839
    if (m_adapter.activeDebuggerClient()) {
        m_adapter.activeDebuggerClient()->removeBreakpoint(id);
840
    } else {
Kai Koehne's avatar
Kai Koehne committed
841
        foreach (BaseQmlDebuggerClient *client, m_adapter.debuggerClients()) {
842
            client->removeBreakpoint(id);
843 844 845 846 847 848 849 850 851 852 853 854 855 856 857
        }
    }

    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
858 859
    if (m_adapter.activeDebuggerClient()) {
        m_adapter.activeDebuggerClient()->changeBreakpoint(id);
860
    } else {
Kai Koehne's avatar
Kai Koehne committed
861
        foreach (BaseQmlDebuggerClient *client, m_adapter.debuggerClients()) {
862
            client->changeBreakpoint(id);
863 864 865 866 867 868 869 870
        }
    }

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

871 872
void QmlEngine::attemptBreakpointSynchronization()
{
873 874 875 876 877
    if (!stateAcceptsBreakpointChanges()) {
        showMessage(_("BREAKPOINT SYNCHRONIZATION NOT POSSIBLE IN CURRENT STATE"));
        return;
    }

hjk's avatar
hjk committed
878
    BreakHandler *handler = breakHandler();
879

880
    DebuggerEngine *bpOwner = isSlaveEngine() ? masterEngine() : this;
881
    foreach (BreakpointModelId id, handler->unclaimedBreakpointIds()) {
882 883
        // Take ownership of the breakpoint. Requests insertion.
        if (acceptsBreakpoint(id))
884
            handler->setEngine(id, bpOwner);
885 886
    }

887
    foreach (BreakpointModelId id, handler->engineBreakpointIds(bpOwner)) {
888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907
        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;
908
        }
909
        QTC_ASSERT(false, qDebug() << "UNKNOWN STATE"  << id << state());
910 911
    }

912
    DebuggerEngine::attemptBreakpointSynchronization();
913

Kai Koehne's avatar
Kai Koehne committed
914 915
    if (m_adapter.activeDebuggerClient()) {
        m_adapter.activeDebuggerClient()->synchronizeBreakpoints();
916
    } else {
Kai Koehne's avatar
Kai Koehne committed
917
        foreach (BaseQmlDebuggerClient *client, m_adapter.debuggerClients()) {
918
            client->synchronizeBreakpoints();
919
        }
920
    }
921 922
}

923