qmlengine.cpp 43.6 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 425 426
    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"
427
                   << startParameters().connParams.port << "from start parameters.");
Kai Koehne's avatar
Kai Koehne committed
428
        m_adapter.beginConnectionTcp(startParameters().qmlServerAddress, port);
429 430 431 432 433
        return;
    }
    if (startParameters().communicationChannel
           == DebuggerStartParameters::CommunicationChannelTcpIp) {
        // no port from application output, use the one from start parameters ...
Kai Koehne's avatar
Kai Koehne committed
434
        m_adapter.beginConnectionTcp(startParameters().qmlServerAddress,
435 436 437 438
                                        startParameters().qmlServerPort);
    } else {
        QTC_CHECK(startParameters().communicationChannel
                  == DebuggerStartParameters::CommunicationChannelUsb);
Kai Koehne's avatar
Kai Koehne committed
439
        m_adapter.beginConnectionOst(startParameters().remoteChannel);
440
    }
Lasse Holmstedt's avatar
Lasse Holmstedt committed
441 442
}

443
void QmlEngine::connectionStartupFailed()
Lasse Holmstedt's avatar
Lasse Holmstedt committed
444
{
Kai Koehne's avatar
Kai Koehne committed
445
    if (m_retryOnConnectFail) {
446 447
        // retry after 3 seconds ...
        QTimer::singleShot(3000, this, SLOT(beginConnection()));
448 449
        return;
    }
450

hjk's avatar
hjk committed
451
    QMessageBox *infoBox = new QMessageBox(Core::ICore::mainWindow());
452 453
    infoBox->setIcon(QMessageBox::Critical);
    infoBox->setWindowTitle(tr("Qt Creator"));
454 455 456 457 458
    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);
459 460 461
    infoBox->setModal(true);

    connect(infoBox, SIGNAL(finished(int)),
462
            this, SLOT(errorMessageBoxFinished(int)));
463 464 465 466

    infoBox->show();
}

467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482
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();
}

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

506
void QmlEngine::connectionError(QAbstractSocket::SocketError socketError)
Lasse Holmstedt's avatar
Lasse Holmstedt committed
507
{
hjk's avatar
hjk committed
508
    if (socketError == QAbstractSocket::RemoteHostClosedError)
509
        showMessage(tr("QML Debugger: Remote host closed connection."), StatusBar);
510

511 512 513 514
    if (!isSlaveEngine()) { // normal flow for slave engine when gdb exits
        notifyInferiorSpontaneousStop();
        notifyInferiorIll();
    }
Lasse Holmstedt's avatar
Lasse Holmstedt committed
515 516
}

517 518
void QmlEngine::serviceConnectionError(const QString &serviceName)
{
519 520
    showMessage(tr("QML Debugger: Could not connect to service '%1'.")
        .arg(serviceName), StatusBar);
521 522
}

523 524 525 526 527
bool QmlEngine::canDisplayTooltip() const
{
    return state() == InferiorRunOk || state() == InferiorStopOk;
}

528
void QmlEngine::filterApplicationMessage(const QString &output, int /*channel*/)
529
{
Kai Koehne's avatar
Kai Koehne committed
530
    m_outputParser.processOutput(output);
531 532 533 534 535 536 537 538 539 540
}

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

541 542 543
void QmlEngine::gotoLocation(const Location &location)
{
    const QString fileName = location.fileName();
Daniel Molkentin's avatar
Daniel Molkentin committed
544 545
    // TODO: QUrl::isLocalFile() once we depend on Qt 4.8
    if (QUrl(fileName).scheme().compare(QLatin1String("file"), Qt::CaseInsensitive) == 0) {
546
        // internal file from source files -> show generated .js
Kai Koehne's avatar
Kai Koehne committed
547
        QTC_ASSERT(m_sourceDocuments.contains(fileName), return);
548 549 550
        Core::IEditor *editor = 0;

        Core::EditorManager *editorManager = Core::EditorManager::instance();
Aurindam Jana's avatar
Aurindam Jana committed
551 552 553 554 555 556 557 558 559 560
        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
561
            editor = Core::EditorManager::openEditorWithContents(QmlJSEditor::Constants::C_QMLJSEDITOR_ID,
562 563 564 565 566
                                                           &titlePattern);
            if (editor) {
                editor->setProperty(Constants::OPENED_BY_DEBUGGER, true);
            }

Kai Koehne's avatar
Kai Koehne committed
567
            updateEditor(editor, m_sourceDocuments.value(fileName));
568
        }
hjk's avatar
hjk committed
569
        Core::EditorManager::activateEditor(editor);
570

571 572 573 574 575
    } else {
        DebuggerEngine::gotoLocation(location);
    }
}

576 577
void QmlEngine::closeConnection()
{
578
    disconnect(watchersModel(),SIGNAL(layoutChanged()),this,SLOT(synchronizeWatchers()));
Kai Koehne's avatar
Kai Koehne committed
579
    m_adapter.closeConnection();
580 581
}

hjk's avatar
hjk committed
582 583 584
void QmlEngine::runEngine()
{
    QTC_ASSERT(state() == EngineRunRequested, qDebug() << state());
Lasse Holmstedt's avatar
Lasse Holmstedt committed
585

586
    if (!isSlaveEngine()) {
587
        if (startParameters().startMode == AttachToRemoteServer)
588 589
            m_noDebugOutputTimer.start();
        else if (startParameters().startMode == AttachToRemoteProcess)
590 591
            beginConnection();
        else
592
            startApplicationLauncher();
593
    } else {
Kai Koehne's avatar
Kai Koehne committed
594
        m_noDebugOutputTimer.start();
595
    }
596 597 598 599
}

void QmlEngine::startApplicationLauncher()
{
Kai Koehne's avatar
Kai Koehne committed
600
    if (!m_applicationLauncher.isRunning()) {
601 602 603 604
        appendMessage(tr("Starting %1 %2").arg(
                          QDir::toNativeSeparators(startParameters().executable),
                          startParameters().processArgs)
                      + QLatin1Char('\n')
con's avatar
con committed
605
                     , Utils::NormalMessageFormat);
Kai Koehne's avatar
Kai Koehne committed
606
        m_applicationLauncher.start(ProjectExplorer::ApplicationLauncher::Gui,
607 608 609
                                    startParameters().executable,
                                    startParameters().processArgs);
    }
610
}
Lasse Holmstedt's avatar
Lasse Holmstedt committed
611

612 613
void QmlEngine::stopApplicationLauncher()
{
Kai Koehne's avatar
Kai Koehne committed
614 615 616 617
    if (m_applicationLauncher.isRunning()) {
        disconnect(&m_applicationLauncher, SIGNAL(processExited(int)),
                   this, SLOT(disconnected()));
        m_applicationLauncher.stop();
618
    }
619 620
}

621
void QmlEngine::notifyEngineRemoteSetupDone(int gdbServerPort, int qmlPort)
622
{
Christian Kandeler's avatar
Christian Kandeler committed
623 624
    if (qmlPort != -1)
        startParameters().qmlServerPort = qmlPort;
625

626
    DebuggerEngine::notifyEngineRemoteSetupDone(gdbServerPort, qmlPort);
627
    notifyEngineSetupOk();
628 629 630 631 632 633

    // 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);
634 635
}

636
void QmlEngine::notifyEngineRemoteSetupFailed(const QString &message)
637
{
638
    DebuggerEngine::notifyEngineRemoteSetupFailed(message);
639 640 641
    if (isMasterEngine())
        QMessageBox::critical(0,tr("Failed to start application"),
            tr("Application startup failed: %1").arg(message));
642

643
    notifyEngineSetupFailed();
644 645
}

hjk's avatar
hjk committed
646
void QmlEngine::shutdownInferior()
647
{
Kai Koehne's avatar
Kai Koehne committed
648 649
    if (m_adapter.activeDebuggerClient())
        m_adapter.activeDebuggerClient()->endSession();
650

651 652
    if (isSlaveEngine()) {
        resetLocation();
Lasse Holmstedt's avatar
Lasse Holmstedt committed
653
    }
654
    stopApplicationLauncher();
655
    closeConnection();
656

hjk's avatar
hjk committed
657 658 659 660 661
    notifyInferiorShutdownOk();
}

void QmlEngine::shutdownEngine()
{
662 663 664
    m_noDebugOutputTimer.stop();

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

667
    notifyEngineShutdownOk();
668 669
    if (!isSlaveEngine())
        showMessage(QString(), StatusBar);
670 671
}

hjk's avatar
hjk committed
672
void QmlEngine::setupEngine()
673
{
674
    if (startParameters().remoteSetupNeeded) {
675
        // we need to get the port first
676
        notifyEngineRequestRemoteSetup();
677
    } else {
Kai Koehne's avatar
Kai Koehne committed
678 679
        m_applicationLauncher.setEnvironment(startParameters().environment);
        m_applicationLauncher.setWorkingDirectory(startParameters().workingDirectory);
Lasse Holmstedt's avatar
Lasse Holmstedt committed
680

681
        // We can't do this in the constructore because runControl() isn't yet defined
Kai Koehne's avatar
Kai Koehne committed
682
        connect(&m_applicationLauncher, SIGNAL(bringToForegroundRequested(qint64)),
683 684 685 686 687
                runControl(), SLOT(bringApplicationToForeground(qint64)),
                Qt::UniqueConnection);

        notifyEngineSetupOk();
    }
688 689
}

690 691
void QmlEngine::continueInferior()
{
hjk's avatar
hjk committed
692
    QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
Kai Koehne's avatar
Kai Koehne committed
693 694
    if (m_adapter.activeDebuggerClient()) {
        m_adapter.activeDebuggerClient()->continueInferior();
695
    }
696
    resetLocation();
hjk's avatar
hjk committed
697 698
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
699 700 701 702
}

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

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

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

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

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

void QmlEngine::executeNextI()
{
747
    executeNext();
748 749
}

750
void QmlEngine::executeRunToLine(const ContextData &data)
751
{
752 753 754
    QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
    showStatusMessage(tr("Run to line  %1 (%2) requested...").arg(data.lineNumber).arg(data.fileName), 5000);
    resetLocation();
755 756 757 758 759 760
    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
761 762
    if (m_adapter.activeDebuggerClient())
        m_adapter.activeDebuggerClient()->executeRunToLine(modifiedData);
763 764
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
765 766 767 768 769 770 771 772
}

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

773
void QmlEngine::executeJumpToLine(const ContextData &data)
774
{
775
    Q_UNUSED(data)
776 777 778 779 780
    XSDEBUG("FIXME:  QmlEngine::executeJumpToLine()");
}

void QmlEngine::activateFrame(int index)
{
781 782 783
    if (state() != InferiorStopOk && state() != InferiorUnrunnable)
        return;

Kai Koehne's avatar
Kai Koehne committed
784 785
    if (m_adapter.activeDebuggerClient()) {
        m_adapter.activeDebuggerClient()->activateFrame(index);
786
    }
787
    gotoLocation(stackHandler()->frames().value(index));
788 789 790 791 792 793 794
}

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

795 796 797 798 799 800 801
void QmlEngine::insertBreakpoint(BreakpointModelId id)
{
    BreakHandler *handler = breakHandler();
    BreakpointState state = handler->state(id);
    QTC_ASSERT(state == BreakpointInsertRequested, qDebug() << id << this << state);
    handler->notifyBreakpointInsertProceeding(id);

802 803 804 805 806 807 808
    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
809
            pendingBreakpoints.insertMulti(params.fileName, id);
810 811 812 813 814 815
            return;
        }
        if (!valid)
            return;
    }

Kai Koehne's avatar
Kai Koehne committed
816 817
    if (m_adapter.activeDebuggerClient()) {
        m_adapter.activeDebuggerClient()->insertBreakpoint(id, line, column);
818
    } else {
Kai Koehne's avatar
Kai Koehne committed
819
        foreach (BaseQmlDebuggerClient *client, m_adapter.debuggerClients()) {
820
            client->insertBreakpoint(id, line, column);
821 822 823 824 825 826 827
        }
    }
}

void QmlEngine::removeBreakpoint(BreakpointModelId id)
{
    BreakHandler *handler = breakHandler();
828 829 830

    const BreakpointParameters &params = handler->breakpointData(id);
    if (params.type == BreakpointByFileAndLine &&
Kai Koehne's avatar
Kai Koehne committed
831
            pendingBreakpoints.contains(params.fileName)) {
832
        QHash<QString, BreakpointModelId>::iterator i =
Kai Koehne's avatar
Kai Koehne committed
833 834
                pendingBreakpoints.find(params.fileName);
        while (i != pendingBreakpoints.end() && i.key() == params.fileName) {
835
            if (i.value() == id) {
Kai Koehne's avatar
Kai Koehne committed
836
                pendingBreakpoints.erase(i);
837 838 839 840 841 842
                return;
            }
            ++i;
        }
    }

843 844 845 846
    BreakpointState state = handler->state(id);
    QTC_ASSERT(state == BreakpointRemoveRequested, qDebug() << id << this << state);
    handler->notifyBreakpointRemoveProceeding(id);

Kai Koehne's avatar
Kai Koehne committed
847 848
    if (m_adapter.activeDebuggerClient()) {
        m_adapter.activeDebuggerClient()->removeBreakpoint(id);
849
    } else {
Kai Koehne's avatar
Kai Koehne committed
850
        foreach (BaseQmlDebuggerClient *client, m_adapter.debuggerClients()) {
851
            client->removeBreakpoint(id);
852 853 854 855 856 857 858 859 860 861 862 863 864 865 866
        }
    }

    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
867 868
    if (m_adapter.activeDebuggerClient()) {
        m_adapter.activeDebuggerClient()->changeBreakpoint(id);
869
    } else {
Kai Koehne's avatar
Kai Koehne committed
870
        foreach (BaseQmlDebuggerClient *client, m_adapter.debuggerClients()) {
871
            client->changeBreakpoint(id);
872 873 874 875 876 877 878 879
        }
    }

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

880 881
void QmlEngine::attemptBreakpointSynchronization()
{
882 883 884 885 886
    if (!stateAcceptsBreakpointChanges()) {
        showMessage(_("BREAKPOINT SYNCHRONIZATION NOT POSSIBLE IN CURRENT STATE"));
        return;
    }

hjk's avatar
hjk committed
887
    BreakHandler *handler = breakHandler();
888

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

896
    foreach (BreakpointModelId id, handler->engineBreakpointIds(bpOwner)) {
897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916
        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;
917
        }
918
        QTC_ASSERT(false, qDebug() << "UNKNOWN STATE"  << id << state());
919 920
    }

921
    DebuggerEngine::attemptBreakpointSynchronization();
922

Kai Koehne's avatar
Kai Koehne committed
923 924
    if (m_adapter.activeDebuggerClient()) {
        m_adapter.activeDebuggerClient()->synchronizeBreakpoints();
925
    } else {
Kai Koehne's avatar
Kai Koehne committed
926
        foreach (BaseQmlDebuggerClient *client, m_adapter.debuggerClients()) {
927
            client->synchronizeBreakpoints();
928
        }