qmlengine.cpp 43.1 KB
Newer Older
1 2 3 4 5 6
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
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
Kai Koehne's avatar
Kai Koehne committed
407
            && !m_automaticConnect)
408 409
        beginConnection(port);
    else
Kai Koehne's avatar
Kai Koehne committed
410
        m_automaticConnect = true;
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) {
562 563 564 565 566 567
            editor = editorManager->openEditorWithContents(QmlJSEditor::Constants::C_QMLJSEDITOR_ID,
                                                           &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
        }
570
        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 589 590
        if (startParameters().startMode == AttachToRemoteServer)
            beginConnection();
        else
591
            startApplicationLauncher();
592
    } else {
Kai Koehne's avatar
Kai Koehne committed
593
        m_noDebugOutputTimer.start();
594
    }
595 596 597 598
}

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

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

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

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

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

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

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

hjk's avatar
hjk committed
641
void QmlEngine::shutdownInferior()
642
{
Kai Koehne's avatar
Kai Koehne committed
643
    m_noDebugOutputTimer.stop();
644

Kai Koehne's avatar
Kai Koehne committed
645 646
    if (m_adapter.activeDebuggerClient())
        m_adapter.activeDebuggerClient()->endSession();
647

648 649
    if (isSlaveEngine()) {
        resetLocation();
Lasse Holmstedt's avatar
Lasse Holmstedt committed
650
    }
651
    stopApplicationLauncher();
652
    closeConnection();
653

hjk's avatar
hjk committed
654 655 656 657 658
    notifyInferiorShutdownOk();
}

void QmlEngine::shutdownEngine()
{
659 660
    // double check (ill engine?):
    stopApplicationLauncher();
Lasse Holmstedt's avatar
Lasse Holmstedt committed
661

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

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

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

        notifyEngineSetupOk();
    }
683 684
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

916
    DebuggerEngine::attemptBreakpointSynchronization();
917

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

927
bool QmlEngine::acceptsBreakpoint(BreakpointModelId id) const
928
{
929
    if (!breakHandler()->breakpointData(id).isCppBreakpoint())
Aurindam Jana's avatar