qmlengine.cpp 43.5 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
2
**
hjk's avatar
hjk committed
3 4
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
7
**
hjk's avatar
hjk committed
8 9 10 11 12 13 14
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
15 16
**
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17 18 19 20 21 22 23 24 25
** Alternatively, 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.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
con's avatar
con committed
26 27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
28
****************************************************************************/
29 30

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

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

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

53
#include <qmldebug/baseenginedebugclient.h>
54
#include <qmljseditor/qmljseditorconstants.h>
55 56
#include <qmljs/parser/qmljsast_p.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
57 58
#include <qmljs/consolemanagerinterface.h>
#include <qmljs/consoleitem.h>
59

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

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

#include <texteditor/itexteditor.h>
69

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

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

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

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

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

96 97 98
namespace Debugger {
namespace Internal {

Kai Koehne's avatar
Kai Koehne committed
99
class ASTWalker : public Visitor
100 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
{
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
127 128 129
        if (!ast->statement)
            return true;

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

262 263 264 265 266
QmlJS::ConsoleManagerInterface *qmlConsoleManager()
{
    return QmlJS::ConsoleManagerInterface::instance();
}

267 268 269 270 271 272
///////////////////////////////////////////////////////////////////////
//
// QmlEngine
//
///////////////////////////////////////////////////////////////////////

273 274
QmlEngine::QmlEngine(const DebuggerStartParameters &startParameters)
  : DebuggerEngine(startParameters)
Kai Koehne's avatar
Kai Koehne committed
275 276 277 278
  , 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

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

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

306

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

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

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

335 336 337 338 339
    ModelManagerInterface *mmIface = ModelManagerInterface::instance();
    if (mmIface) {
        connect(ModelManagerInterface::instance(), SIGNAL(documentUpdated(QmlJS::Document::Ptr)),
                this, SLOT(documentUpdated(QmlJS::Document::Ptr)));
    }
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
    if (qmlConsoleManager())
        qmlConsoleManager()->setScriptEvaluator(this);
348 349 350
}

QmlEngine::~QmlEngine()
hjk's avatar
hjk committed
351
{
352 353 354
    QList<Core::IEditor *> editorsToClose;

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

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

367
    notifyInferiorSetupOk();
368

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

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

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

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

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

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

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

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

417
    QTC_ASSERT(state() == EngineRunRequested, return);
418

419 420 421 422
    if (port > 0) {
        QTC_ASSERT(startParameters().connParams.port == 0
                   || startParameters().connParams.port == port,
                   qWarning() << "Port " << port << "from application output does not match"
423
                   << startParameters().connParams.port << "from start parameters.");
Kai Koehne's avatar
Kai Koehne committed
424
        m_adapter.beginConnectionTcp(startParameters().qmlServerAddress, port);
425 426
        return;
    }
427 428 429
    // 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
430 431
}

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

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

    connect(infoBox, SIGNAL(finished(int)),
451
            this, SLOT(errorMessageBoxFinished(int)));
452 453 454 455

    infoBox->show();
}

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

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

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

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

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

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

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

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

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

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

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

560 561 562 563 564
    } else {
        DebuggerEngine::gotoLocation(location);
    }
}

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

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

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

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

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

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

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

    // 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);
623 624
}

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

632
    notifyEngineSetupFailed();
633 634
}

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

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

hjk's avatar
hjk committed
646 647 648 649 650
    notifyInferiorShutdownOk();
}

void QmlEngine::shutdownEngine()
{
651 652
    if (qmlConsoleManager())
        qmlConsoleManager()->setScriptEvaluator(0);
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
}

hjk's avatar
hjk committed
781
void QmlEngine::selectThread(ThreadId threadId)
782
{
hjk's avatar
hjk committed
783
    Q_UNUSED(threadId)
784 785
}

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
bool QmlEngine::acceptsBreakpoint(BreakpointModelId id) const
924
{
925
    if (!breakHandler()->breakpointData(id).isCppBreakpoint())
926 927 928 929 930
            return true;

    //If it is a Cpp Breakpoint query if the type can be also handled by the debugger client
    //TODO: enable setting of breakpoints before start of debug session
    //For now, the event breakpoint can be set after the activeDebuggerClient is known
931
    //This is because the older client does not support BreakpointOnQmlSignalHandler
932
    bool acceptBreakpoint = false;
Kai Koehne's avatar
Kai Koehne committed
933 934
    if (m_adapter.activeDebuggerClient()) {
        acceptBreakpoint = m_adapter.activeDebuggerClient()->acceptsBreakpoint(id);
935 936
    }
    return acceptBreakpoint;
937 938
}

939 940 941 942 943 944 945 946 947 948 949 950 951
void QmlEngine::loadSymbols(const QString &moduleName)
{
    Q_UNUSED(moduleName)
}

void QmlEngine::loadAllSymbols()
{
}

void QmlEngine::reloadModules()
{
}

952 953
void QmlEngine::reloadSourceFiles()
{
Kai Koehne's avatar
Kai Koehne committed
954 955
    if (m_adapter.activeDebuggerClient()) {
        m_adapter.activeDebuggerClient()->getSourceFiles();
956 957 958
    }
}

</