qmlengine.cpp 43 KB
Newer Older
1 2 3 4 5 6
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
7
** Contact: Nokia Corporation (qt-info@nokia.com)
8 9 10 11
**
**
** GNU Lesser General Public License Usage
**
hjk's avatar
hjk committed
12 13 14 15 16 17
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18
**
con's avatar
con committed
19
** In addition, as a special exception, Nokia gives you certain additional
hjk's avatar
hjk committed
20
** rights. These rights are described in the Nokia Qt LGPL Exception
con's avatar
con committed
21 22
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
23 24 25 26 27
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
con's avatar
con committed
28
** If you have questions regarding the use of this file, please contact
29
** Nokia at qt-info@nokia.com.
30 31 32 33
**
**************************************************************************/

#include "qmlengine.h"
34
#include "baseqmldebuggerclient.h"
35
#include "qmlinspectoragent.h"
36

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

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

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

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

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

#include <texteditor/itexteditor.h>
72

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

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

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

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

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

100 101 102
namespace Debugger {
namespace Internal {

Kai Koehne's avatar
Kai Koehne committed
103
class ASTWalker : public Visitor
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
{
public:
    void operator()(Node *ast, quint32 *l, quint32 *c)
    {
        done = false;
        line = l;
        column = c;
        Node::accept(ast, this);
    }

    bool preVisit(Node *ast)
    {
        return ast->lastSourceLocation().startLine >= *line && !done;
    }

    //Case 1: Breakpoint is between sourceStart(exclusive) and
    //        sourceEnd(inclusive) --> End tree walk.
    //Case 2: Breakpoint is on sourceStart --> Check for the start
    //        of the first executable code. Set the line number and
    //        column number. End tree walk.
    //Case 3: Breakpoint is on "unbreakable" code --> Find the next "breakable"
    //        code and check for Case 2. End tree walk.

    //Add more types when suitable.

    bool visit(UiScriptBinding *ast)
    {
Aurindam Jana's avatar
Aurindam Jana committed
131 132 133
        if (!ast->statement)
            return true;

134 135 136 137 138 139 140 141 142 143 144
        quint32 sourceStartLine = ast->firstSourceLocation().startLine;
        quint32 statementStartLine;
        quint32 statementColumn;

        if (ast->statement->kind == Node::Kind_ExpressionStatement) {
            statementStartLine = ast->statement->firstSourceLocation().
                    startLine;
            statementColumn = ast->statement->firstSourceLocation().startColumn;

        } else if (ast->statement->kind == Node::Kind_Block) {
            Block *block = static_cast<Block *>(ast->statement);
Aurindam Jana's avatar
Aurindam Jana committed
145 146
            if (!block || !block->statements)
                return true;
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
            statementStartLine = block->statements->firstSourceLocation().
                    startLine;
            statementColumn = block->statements->firstSourceLocation().
                    startColumn;

        } else {
            return true;
        }


        //Case 1
        //Check for possible relocation within the binding statement

        //Rewritten to (function <token>() { { }})
        //The offset 16 is position of inner lbrace without token length.
        const int offset = 16;

        //Case 2
        if (statementStartLine == *line) {
            if (sourceStartLine == *line)
                *column = offset + ast->qualifiedId->identifierToken.length;
            done = true;
        }

        //Case 3
        if (statementStartLine > *line) {
            *line = statementStartLine;
            if (sourceStartLine == *line)
                *column = offset + ast->qualifiedId->identifierToken.length;
            else
                *column = statementColumn;
            done = true;
        }
        return true;
    }

    bool visit(FunctionDeclaration *ast) {
        quint32 sourceStartLine = ast->firstSourceLocation().startLine;
        quint32 sourceStartColumn = ast->firstSourceLocation().startColumn;
        quint32 statementStartLine = ast->body->firstSourceLocation().startLine;
        quint32 statementColumn = ast->body->firstSourceLocation().startColumn;

        //Case 1
        //Check for possible relocation within the function declaration

        //Case 2
        if (statementStartLine == *line) {
            if (sourceStartLine == *line)
                *column = statementColumn - sourceStartColumn + 1;
            done = true;
        }

        //Case 3
        if (statementStartLine > *line) {
            *line = statementStartLine;
            if (sourceStartLine == *line)
                *column = statementColumn - sourceStartColumn + 1;
            else
                *column = statementColumn;
            done = true;
        }
        return true;
    }

    bool visit(EmptyStatement *ast)
    {
        *line = ast->lastSourceLocation().startLine + 1;
        return true;
    }

    bool visit(VariableStatement *ast) { test(ast); return true; }
    bool visit(VariableDeclarationList *ast) { test(ast); return true; }
    bool visit(VariableDeclaration *ast) { test(ast); return true; }
    bool visit(ExpressionStatement *ast) { test(ast); return true; }
    bool visit(IfStatement *ast) { test(ast); return true; }
    bool visit(DoWhileStatement *ast) { test(ast); return true; }
    bool visit(WhileStatement *ast) { test(ast); return true; }
    bool visit(ForStatement *ast) { test(ast); return true; }
    bool visit(LocalForStatement *ast) { test(ast); return true; }
    bool visit(ForEachStatement *ast) { test(ast); return true; }
    bool visit(LocalForEachStatement *ast) { test(ast); return true; }
    bool visit(ContinueStatement *ast) { test(ast); return true; }
    bool visit(BreakStatement *ast) { test(ast); return true; }
    bool visit(ReturnStatement *ast) { test(ast); return true; }
    bool visit(WithStatement *ast) { test(ast); return true; }
    bool visit(SwitchStatement *ast) { test(ast); return true; }
    bool visit(CaseBlock *ast) { test(ast); return true; }
    bool visit(CaseClauses *ast) { test(ast); return true; }
    bool visit(CaseClause *ast) { test(ast); return true; }
    bool visit(DefaultClause *ast) { test(ast); return true; }
    bool visit(LabelledStatement *ast) { test(ast); return true; }
    bool visit(ThrowStatement *ast) { test(ast); return true; }
    bool visit(TryStatement *ast) { test(ast); return true; }
    bool visit(Catch *ast) { test(ast); return true; }
    bool visit(Finally *ast) { test(ast); return true; }
    bool visit(FunctionExpression *ast) { test(ast); return true; }
    bool visit(DebuggerStatement *ast) { test(ast); return true; }

    void test(Node *ast)
    {
        quint32 statementStartLine = ast->firstSourceLocation().startLine;
        //Case 1/2
        if (statementStartLine <= *line &&
                *line <= ast->lastSourceLocation().startLine)
            done = true;

        //Case 3
        if (statementStartLine > *line) {
            *line = statementStartLine;
            *column = ast->firstSourceLocation().startColumn;
            done = true;
        }
    }

    bool done;
    quint32 *line;
    quint32 *column;
};
265

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

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

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

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

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

310

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

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

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

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

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

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

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

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

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

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

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

378
    notifyInferiorSetupOk();
379

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

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

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

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

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

402 403
void QmlEngine::tryToConnect(quint16 port)
{
404
    showMessage(QLatin1String("QML Debugger: No application output received in time, trying to connect ..."), LogStatus);
Kai Koehne's avatar
Kai Koehne committed
405
    m_retryOnConnectFail = true;
406
    if (state() == EngineRunRequested)
407 408
        beginConnection(port);
    else
Kai Koehne's avatar
Kai Koehne committed
409
        m_automaticConnect = true;
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 588 589
        if (startParameters().startMode == AttachToRemoteServer)
            beginConnection();
        else
590
            startApplicationLauncher();
591
    } else {
Kai Koehne's avatar
Kai Koehne committed
592
        m_noDebugOutputTimer.start();
593
    }
594 595 596 597
}

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

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

Christian Kandeler's avatar
Christian Kandeler committed
619
void QmlEngine::handleRemoteSetupDone(int gdbServerPort, int qmlPort)
620
{
Christian Kandeler's avatar
Christian Kandeler committed
621
    Q_UNUSED(gdbServerPort);
622

Christian Kandeler's avatar
Christian Kandeler committed
623 624
    if (qmlPort != -1)
        startParameters().qmlServerPort = qmlPort;
625 626

    notifyEngineRemoteSetupDone();
627
    notifyEngineSetupOk();
628 629 630 631
}

void QmlEngine::handleRemoteSetupFailed(const QString &message)
{
632 633 634
    if (isMasterEngine())
        QMessageBox::critical(0,tr("Failed to start application"),
            tr("Application startup failed: %1").arg(message));
635 636

    notifyEngineRemoteSetupFailed();
637
    notifyEngineSetupFailed();
638 639
}

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

645 646
    if (isSlaveEngine()) {
        resetLocation();
Lasse Holmstedt's avatar
Lasse Holmstedt committed
647
    }
648
    stopApplicationLauncher();
649
    closeConnection();
650

hjk's avatar
hjk committed
651 652 653 654 655
    notifyInferiorShutdownOk();
}

void QmlEngine::shutdownEngine()
{
656 657 658
    m_noDebugOutputTimer.stop();

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

661
    notifyEngineShutdownOk();
662 663
    if (!isSlaveEngine())
        showMessage(QString(), StatusBar);
664 665
}

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

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

        notifyEngineSetupOk();
    }
682 683
}

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

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

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

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

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

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

void QmlEngine::executeNextI()
{
741
    executeNext();
742 743
}

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

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

767
void QmlEngine::executeJumpToLine(const ContextData &data)
768
{
769
    Q_UNUSED(data)
770 771 772 773 774
    XSDEBUG("FIXME:  QmlEngine::executeJumpToLine()");
}

void QmlEngine::activateFrame(int index)
{
775 776 777
    if (state() != InferiorStopOk && state() != InferiorUnrunnable)
        return;

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

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

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

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

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

void QmlEngine::removeBreakpoint(BreakpointModelId id)
{
    BreakHandler *handler = breakHandler();
822 823 824

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

837 838 839 840
    BreakpointState state = handler->state(id);
    QTC_ASSERT(state == BreakpointRemoveRequested, qDebug() << id << this << state);
    handler->notifyBreakpointRemoveProceeding(id);

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

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

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

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

hjk's avatar
hjk committed
881
    BreakHandler *handler = breakHandler();
882

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

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

915
    DebuggerEngine::attemptBreakpointSynchronization();
916

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

926
bool QmlEngine::acceptsBreakpoint(BreakpointModelId id) const
927
{
928
    if (!breakHandler()->breakpointData(id).isCppBreakpoint())
929 930 931 <