qmlengine.cpp 43.7 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 *masterEngine)
271
  : DebuggerEngine(startParameters, masterEngine)
Kai Koehne's avatar
Kai Koehne committed
272 273 274 275
  , m_adapter(this)
  , m_inspectorAdapter(&m_adapter, this)
  , m_retryOnConnectFail(false)
  , m_automaticConnect(false)
276
{
Friedemann Kleint's avatar
Friedemann Kleint committed
277
    setObjectName(QLatin1String("QmlEngine"));
278

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

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

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

305

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

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

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

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

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

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

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

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

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

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

370
    notifyInferiorSetupOk();
371

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

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

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

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

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

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

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

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

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

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

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

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

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

    infoBox->show();
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

644
    notifyEngineSetupFailed();
645 646
}

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

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

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

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

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

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

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

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

        notifyEngineSetupOk();
    }
689 690
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

922
    DebuggerEngine::attemptBreakpointSynchronization();
923

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