qmlengine.cpp 44.3 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"
Lasse Holmstedt's avatar
Lasse Holmstedt committed
34
#include "qmladapter.h"
35
#include "qmlinspectoradapter.h"
36
#include "interactiveinterpreter.h"
37
#include "baseqmldebuggerclient.h"
38
#include "qmlinspectoragent.h"
39

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

51 52 53 54 55
#include "breakhandler.h"
#include "moduleshandler.h"
#include "registerhandler.h"
#include "stackhandler.h"
#include "watchhandler.h"
56
#include "sourcefileshandler.h"
57
#include "watchutils.h"
58
#include "qtmessageloghandler.h"
59

Lasse Holmstedt's avatar
Lasse Holmstedt committed
60
#include <extensionsystem/pluginmanager.h>
61
#include <projectexplorer/applicationlauncher.h>
62 63
#include <qmldebug/qmloutputparser.h>
#include <qmldebug/baseenginedebugclient.h>
64
#include <qmljseditor/qmljseditorconstants.h>
65 66
#include <qmljs/parser/qmljsast_p.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
67

68
#include <utils/environment.h>
69 70
#include <utils/qtcassert.h>

71 72
#include <coreplugin/coreconstants.h>
#include <coreplugin/editormanager/editormanager.h>
73
#include <coreplugin/helpmanager.h>
74 75 76
#include <coreplugin/icore.h>

#include <texteditor/itexteditor.h>
77

78 79 80 81 82
#include <QDateTime>
#include <QDebug>
#include <QDir>
#include <QFileInfo>
#include <QTimer>
83

84 85 86 87 88 89 90
#include <QAction>
#include <QApplication>
#include <QMainWindow>
#include <QMessageBox>
#include <QPlainTextEdit>
#include <QToolTip>
#include <QTextDocument>
91

92 93
#include <QTcpSocket>
#include <QHostAddress>
94

95 96 97 98 99 100 101 102
#define DEBUG_QML 1
#if DEBUG_QML
#   define SDEBUG(s) qDebug() << s
#else
#   define SDEBUG(s)
#endif
# define XSDEBUG(s) qDebug() << s

hjk's avatar
hjk committed
103
using namespace ProjectExplorer;
104 105
using namespace QmlJS;
using namespace AST;
hjk's avatar
hjk committed
106

107 108 109
namespace Debugger {
namespace Internal {

110 111
class QmlEnginePrivate
{
112
public:
113 114
    explicit QmlEnginePrivate(QmlEngine *q);

115
private:
116
    friend class QmlEngine;
hjk's avatar
hjk committed
117
    QmlAdapter m_adapter;
118
    QmlInspectorAdapter m_inspectorAdapter;
hjk's avatar
hjk committed
119
    ApplicationLauncher m_applicationLauncher;
120
    QTimer m_noDebugOutputTimer;
121
    QmlOutputParser m_outputParser;
122 123
    QHash<QString, QTextDocument*> m_sourceDocuments;
    QHash<QString, QWeakPointer<TextEditor::ITextEditor> > m_sourceEditors;
124
    InteractiveInterpreter m_interpreter;
125
    QHash<QString,BreakpointModelId> pendingBreakpoints;
126
    QList<quint32> queryIds;
127 128
    bool m_retryOnConnectFail;
    bool m_automaticConnect;
129 130
};

131
QmlEnginePrivate::QmlEnginePrivate(QmlEngine *q)
132
    : m_adapter(q),
133
      m_inspectorAdapter(&m_adapter, q),
134 135
      m_retryOnConnectFail(false),
      m_automaticConnect(false)
136 137
{
}
138

139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
class ASTWalker: public Visitor
{
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
167 168 169
        if (!ast->statement)
            return true;

170 171 172 173 174 175 176 177 178 179 180
        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
181 182
            if (!block || !block->statements)
                return true;
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 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
            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;
};
301

302 303 304 305 306 307
///////////////////////////////////////////////////////////////////////
//
// QmlEngine
//
///////////////////////////////////////////////////////////////////////

308 309
QmlEngine::QmlEngine(const DebuggerStartParameters &startParameters,
        DebuggerEngine *masterEngine)
310
  : DebuggerEngine(startParameters, QmlLanguage, masterEngine),
311
    d(new QmlEnginePrivate(this))
312
{
Friedemann Kleint's avatar
Friedemann Kleint committed
313
    setObjectName(QLatin1String("QmlEngine"));
314 315 316 317 318 319 320 321 322 323 324 325 326 327

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

    connect(&d->m_adapter, SIGNAL(connectionError(QAbstractSocket::SocketError)),
        SLOT(connectionError(QAbstractSocket::SocketError)));
    connect(&d->m_adapter, SIGNAL(serviceConnectionError(QString)),
        SLOT(serviceConnectionError(QString)));
    connect(&d->m_adapter, SIGNAL(connected()),
        SLOT(connectionEstablished()));
    connect(&d->m_adapter, SIGNAL(connectionStartupFailed()),
        SLOT(connectionStartupFailed()));

328 329 330 331
    connect(this, SIGNAL(stateChanged(Debugger::DebuggerState)),
            SLOT(updateCurrentContext()));
    connect(this->stackHandler(), SIGNAL(currentIndexChanged()),
            SLOT(updateCurrentContext()));
332
    connect(&d->m_inspectorAdapter, SIGNAL(selectionChanged()),
333
            SLOT(updateCurrentContext()));
334 335 336
    connect(d->m_inspectorAdapter.agent(), SIGNAL(
                expressionResult(quint32,QVariant)),
            SLOT(expressionEvaluated(quint32,QVariant)));
337 338
    connect(d->m_adapter.messageClient(),
            SIGNAL(message(QtMsgType,QString,
339
                           QmlDebug::QDebugContextInfo)),
340
            SLOT(appendDebugOutput(QtMsgType,QString,
341
                                   QmlDebug::QDebugContextInfo)));
342

343

344 345 346 347
    connect(&d->m_applicationLauncher,
        SIGNAL(processExited(int)),
        SLOT(disconnected()));
    connect(&d->m_applicationLauncher,
Robert Loehning's avatar
Robert Loehning committed
348 349
        SIGNAL(appendMessage(QString,Utils::OutputFormat)),
        SLOT(appendMessage(QString,Utils::OutputFormat)));
350 351 352 353
    connect(&d->m_applicationLauncher,
            SIGNAL(processStarted()),
            &d->m_noDebugOutputTimer,
            SLOT(start()));
354

355
    d->m_outputParser.setNoOutputText(ApplicationLauncher::msgWinCannotRetrieveDebuggingOutput());
356 357 358
    connect(&d->m_outputParser, SIGNAL(waitingForConnectionOnPort(quint16)),
            this, SLOT(beginConnection(quint16)));
    connect(&d->m_outputParser, SIGNAL(waitingForConnectionViaOst()),
359 360
            this, SLOT(beginConnection()));
    connect(&d->m_outputParser, SIGNAL(noOutputMessage()),
361
            this, SLOT(tryToConnect()));
362
    connect(&d->m_outputParser, SIGNAL(errorMessage(QString)),
363
            this, SLOT(appStartupFailed(QString)));
364

365
    // Only wait 8 seconds for the 'Waiting for connection' on application ouput, then just try to connect
366 367 368
    // (application output might be redirected / blocked)
    d->m_noDebugOutputTimer.setSingleShot(true);
    d->m_noDebugOutputTimer.setInterval(8000);
369
    connect(&d->m_noDebugOutputTimer, SIGNAL(timeout()), this, SLOT(tryToConnect()));
370 371

    qtMessageLogHandler()->setHasEditableRow(true);
372 373 374 375 376

    connect(ModelManagerInterface::instance(),
            SIGNAL(documentUpdated(QmlJS::Document::Ptr)),
            this,
            SLOT(documentUpdated(QmlJS::Document::Ptr)));
377 378

    // we won't get any debug output
379 380 381
    if (startParameters.useTerminal) {
        d->m_noDebugOutputTimer.setInterval(0);
        d->m_retryOnConnectFail = true;
382
        d->m_automaticConnect = true;
383
    }
384 385 386
}

QmlEngine::~QmlEngine()
hjk's avatar
hjk committed
387
{
388 389 390 391 392 393
    ExtensionSystem::PluginManager *pluginManager =
        ExtensionSystem::PluginManager::instance();

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

395 396 397 398 399 400 401 402 403 404
    QList<Core::IEditor *> editorsToClose;

    QHash<QString, QWeakPointer<TextEditor::ITextEditor> >::iterator iter;
    for (iter = d->m_sourceEditors.begin(); iter != d->m_sourceEditors.end(); ++iter) {
        QWeakPointer<TextEditor::ITextEditor> textEditPtr = iter.value();
        if (textEditPtr)
            editorsToClose << textEditPtr.data();
    }
    Core::EditorManager::instance()->closeEditors(editorsToClose);

hjk's avatar
hjk committed
405 406
    delete d;
}
407

hjk's avatar
hjk committed
408
void QmlEngine::setupInferior()
409
{
hjk's avatar
hjk committed
410
    QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state());
Lasse Holmstedt's avatar
Lasse Holmstedt committed
411

412
    notifyInferiorSetupOk();
413 414 415

    if (d->m_automaticConnect)
        beginConnection();
hjk's avatar
hjk committed
416
}
hjk's avatar
hjk committed
417

con's avatar
con committed
418
void QmlEngine::appendMessage(const QString &msg, Utils::OutputFormat /* format */)
hjk's avatar
hjk committed
419
{
420
    showMessage(msg, AppOutput); // FIXME: Redirect to RunControl
hjk's avatar
hjk committed
421 422
}

Lasse Holmstedt's avatar
Lasse Holmstedt committed
423 424 425 426
void QmlEngine::connectionEstablished()
{
    attemptBreakpointSynchronization();

427 428 429 430
    if (!watchHandler()->watcherNames().isEmpty()) {
        synchronizeWatchers();
    }
    connect(watchersModel(),SIGNAL(layoutChanged()),this,SLOT(synchronizeWatchers()));
Christiaan Janssen's avatar
Christiaan Janssen committed
431

432 433
    if (state() == EngineRunRequested)
        notifyEngineRunAndInferiorRunOk();
Lasse Holmstedt's avatar
Lasse Holmstedt committed
434
}
435

436 437
void QmlEngine::tryToConnect(quint16 port)
{
438
    showMessage(QLatin1String("QML Debugger: No application output received in time, trying to connect ..."), LogStatus);
439
    d->m_retryOnConnectFail = true;
440 441 442 443 444
    if (state() == EngineRunRequested
            && !d->m_automaticConnect)
        beginConnection(port);
    else
        d->m_automaticConnect = true;
445 446
}

447
void QmlEngine::beginConnection(quint16 port)
448 449
{
    d->m_noDebugOutputTimer.stop();
450 451 452 453

    if (state() != EngineRunRequested && d->m_retryOnConnectFail)
        return;

454
    QTC_ASSERT(state() == EngineRunRequested, return);
455

456 457 458 459 460 461
    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"
462
                   << startParameters().connParams.port << "from start parameters.");
463 464 465 466 467 468 469 470 471 472 473 474 475
        d->m_adapter.beginConnectionTcp(startParameters().qmlServerAddress, port);
        return;
    }
    if (startParameters().communicationChannel
           == DebuggerStartParameters::CommunicationChannelTcpIp) {
        // no port from application output, use the one from start parameters ...
        d->m_adapter.beginConnectionTcp(startParameters().qmlServerAddress,
                                        startParameters().qmlServerPort);
    } else {
        QTC_CHECK(startParameters().communicationChannel
                  == DebuggerStartParameters::CommunicationChannelUsb);
        d->m_adapter.beginConnectionOst(startParameters().remoteChannel);
    }
Lasse Holmstedt's avatar
Lasse Holmstedt committed
476 477
}

478
void QmlEngine::connectionStartupFailed()
Lasse Holmstedt's avatar
Lasse Holmstedt committed
479
{
480
    if (d->m_retryOnConnectFail) {
481 482
        // retry after 3 seconds ...
        QTimer::singleShot(3000, this, SLOT(beginConnection()));
483 484
        return;
    }
485

hjk's avatar
hjk committed
486
    QMessageBox *infoBox = new QMessageBox(Core::ICore::mainWindow());
487 488
    infoBox->setIcon(QMessageBox::Critical);
    infoBox->setWindowTitle(tr("Qt Creator"));
489 490 491 492 493
    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);
494 495 496
    infoBox->setModal(true);

    connect(infoBox, SIGNAL(finished(int)),
497
            this, SLOT(errorMessageBoxFinished(int)));
498 499 500 501

    infoBox->show();
}

502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517
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();
}

518
void QmlEngine::errorMessageBoxFinished(int result)
519 520
{
    switch (result) {
521
    case QMessageBox::Retry: {
522
        beginConnection();
523 524 525 526
        break;
    }
    case QMessageBox::Help: {
        Core::HelpManager *helpManager = Core::HelpManager::instance();
527
        helpManager->handleHelpRequest(QLatin1String("qthelp://com.nokia.qtcreator/doc/creator-debugging-qml.html"));
528
        // fall through
529 530
    }
    default:
531 532 533
        if (state() == InferiorRunOk) {
            notifyInferiorSpontaneousStop();
            notifyInferiorIll();
534
        } else if (state() == EngineRunRequested) {
535
            notifyEngineRunFailed();
536
        }
Kai Koehne's avatar
Kai Koehne committed
537
        break;
538
    }
Lasse Holmstedt's avatar
Lasse Holmstedt committed
539 540
}

541
void QmlEngine::connectionError(QAbstractSocket::SocketError socketError)
Lasse Holmstedt's avatar
Lasse Holmstedt committed
542
{
hjk's avatar
hjk committed
543
    if (socketError == QAbstractSocket::RemoteHostClosedError)
544
        showMessage(tr("QML Debugger: Remote host closed connection."), StatusBar);
545

546 547 548 549
    if (!isSlaveEngine()) { // normal flow for slave engine when gdb exits
        notifyInferiorSpontaneousStop();
        notifyInferiorIll();
    }
Lasse Holmstedt's avatar
Lasse Holmstedt committed
550 551
}

552 553
void QmlEngine::serviceConnectionError(const QString &serviceName)
{
554 555
    showMessage(tr("QML Debugger: Could not connect to service '%1'.")
        .arg(serviceName), StatusBar);
556 557
}

558 559 560 561 562
bool QmlEngine::canDisplayTooltip() const
{
    return state() == InferiorRunOk || state() == InferiorStopOk;
}

563
void QmlEngine::filterApplicationMessage(const QString &output, int /*channel*/)
564
{
565
    d->m_outputParser.processOutput(output);
566 567 568 569 570 571 572 573 574 575
}

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

576 577 578
void QmlEngine::gotoLocation(const Location &location)
{
    const QString fileName = location.fileName();
Daniel Molkentin's avatar
Daniel Molkentin committed
579 580
    // TODO: QUrl::isLocalFile() once we depend on Qt 4.8
    if (QUrl(fileName).scheme().compare(QLatin1String("file"), Qt::CaseInsensitive) == 0) {
581 582 583 584 585
        // internal file from source files -> show generated .js
        QTC_ASSERT(d->m_sourceDocuments.contains(fileName), return);
        Core::IEditor *editor = 0;

        Core::EditorManager *editorManager = Core::EditorManager::instance();
Aurindam Jana's avatar
Aurindam Jana committed
586 587 588 589 590 591 592 593 594 595
        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) {
596 597 598 599 600 601
            editor = editorManager->openEditorWithContents(QmlJSEditor::Constants::C_QMLJSEDITOR_ID,
                                                           &titlePattern);
            if (editor) {
                editor->setProperty(Constants::OPENED_BY_DEBUGGER, true);
            }

602 603
            updateEditor(editor, d->m_sourceDocuments.value(fileName));
        }
604
        editorManager->activateEditor(editor);
605

606 607 608 609 610
    } else {
        DebuggerEngine::gotoLocation(location);
    }
}

611 612
void QmlEngine::closeConnection()
{
613
    disconnect(watchersModel(),SIGNAL(layoutChanged()),this,SLOT(synchronizeWatchers()));
614
    d->m_adapter.closeConnection();
615 616
}

hjk's avatar
hjk committed
617 618 619
void QmlEngine::runEngine()
{
    QTC_ASSERT(state() == EngineRunRequested, qDebug() << state());
Lasse Holmstedt's avatar
Lasse Holmstedt committed
620

621
    if (!isSlaveEngine()) {
622 623 624
        if (startParameters().startMode == AttachToRemoteServer)
            beginConnection();
        else
625
            startApplicationLauncher();
626 627
    } else {
        d->m_noDebugOutputTimer.start();
628
    }
629 630 631 632 633
}

void QmlEngine::startApplicationLauncher()
{
    if (!d->m_applicationLauncher.isRunning()) {
634 635 636 637
        appendMessage(tr("Starting %1 %2").arg(
                          QDir::toNativeSeparators(startParameters().executable),
                          startParameters().processArgs)
                      + QLatin1Char('\n')
con's avatar
con committed
638
                     , Utils::NormalMessageFormat);
hjk's avatar
hjk committed
639
        d->m_applicationLauncher.start(ApplicationLauncher::Gui,
640 641 642
                                    startParameters().executable,
                                    startParameters().processArgs);
    }
643
}
Lasse Holmstedt's avatar
Lasse Holmstedt committed
644

645 646 647 648 649 650
void QmlEngine::stopApplicationLauncher()
{
    if (d->m_applicationLauncher.isRunning()) {
        disconnect(&d->m_applicationLauncher, SIGNAL(processExited(int)), this, SLOT(disconnected()));
        d->m_applicationLauncher.stop();
    }
651 652
}

Christian Kandeler's avatar
Christian Kandeler committed
653
void QmlEngine::handleRemoteSetupDone(int gdbServerPort, int qmlPort)
654
{
Christian Kandeler's avatar
Christian Kandeler committed
655
    Q_UNUSED(gdbServerPort);
656

Christian Kandeler's avatar
Christian Kandeler committed
657 658
    if (qmlPort != -1)
        startParameters().qmlServerPort = qmlPort;
659 660

    notifyEngineRemoteSetupDone();
661
    notifyEngineSetupOk();
662 663 664 665
}

void QmlEngine::handleRemoteSetupFailed(const QString &message)
{
666 667 668
    if (isMasterEngine())
        QMessageBox::critical(0,tr("Failed to start application"),
            tr("Application startup failed: %1").arg(message));
669 670

    notifyEngineRemoteSetupFailed();
671
    notifyEngineSetupFailed();
672 673
}

hjk's avatar
hjk committed
674
void QmlEngine::shutdownInferior()
675
{
676 677 678 679
    d->m_noDebugOutputTimer.stop();

    if (d->m_adapter.activeDebuggerClient())
        d->m_adapter.activeDebuggerClient()->endSession();
680

681 682
    if (isSlaveEngine()) {
        resetLocation();
Lasse Holmstedt's avatar
Lasse Holmstedt committed
683
    }
684
    stopApplicationLauncher();
685
    closeConnection();
686

hjk's avatar
hjk committed
687 688 689 690 691
    notifyInferiorShutdownOk();
}

void QmlEngine::shutdownEngine()
{
692 693
    // double check (ill engine?):
    stopApplicationLauncher();
Lasse Holmstedt's avatar
Lasse Holmstedt committed
694

695
    notifyEngineShutdownOk();
696 697
    if (!isSlaveEngine())
        showMessage(QString(), StatusBar);
698 699
}

hjk's avatar
hjk committed
700
void QmlEngine::setupEngine()
701
{
702
    if (startParameters().requestRemoteSetup) {
703
        // we need to get the port first
704
        notifyEngineRequestRemoteSetup();
705 706 707
    } else {
        d->m_applicationLauncher.setEnvironment(startParameters().environment);
        d->m_applicationLauncher.setWorkingDirectory(startParameters().workingDirectory);
Lasse Holmstedt's avatar
Lasse Holmstedt committed
708

709 710 711 712 713 714 715
        // We can't do this in the constructore because runControl() isn't yet defined
        connect(&d->m_applicationLauncher, SIGNAL(bringToForegroundRequested(qint64)),
                runControl(), SLOT(bringApplicationToForeground(qint64)),
                Qt::UniqueConnection);

        notifyEngineSetupOk();
    }
716 717
}

718 719
void QmlEngine::continueInferior()
{
hjk's avatar
hjk committed
720
    QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
721 722 723
    if (d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->continueInferior();
    }
724
    resetLocation();
hjk's avatar
hjk committed
725 726
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
727 728 729 730
}

void QmlEngine::interruptInferior()
{
731 732 733
    if (d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->interruptInferior();
    }
734
    notifyInferiorStopOk();
735 736 737 738
}

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

void QmlEngine::executeStepI()
{
748 749 750
    if (d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->executeStepI();
    }
hjk's avatar
hjk committed
751 752
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
753 754 755 756
}

void QmlEngine::executeStepOut()
{
757 758 759
    if (d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->executeStepOut();
    }
hjk's avatar
hjk committed
760 761
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
762 763 764 765
}

void QmlEngine::executeNext()
{
766 767 768
    if (d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->executeNext();
    }
hjk's avatar
hjk committed
769 770
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
771 772 773 774
}

void QmlEngine::executeNextI()
{
775
    executeNext();
776 777
}

778
void QmlEngine::executeRunToLine(const ContextData &data)
779
{
780 781 782
    QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
    showStatusMessage(tr("Run to line  %1 (%2) requested...").arg(data.lineNumber).arg(data.fileName), 5000);
    resetLocation();
783 784 785 786 787 788
    ContextData modifiedData = data;
    quint32 line = data.lineNumber;
    quint32 column;
    bool valid;
    if (adjustBreakpointLineAndColumn(data.fileName, &line, &column, &valid))
        modifiedData.lineNumber = line;
789
    if (d->m_adapter.activeDebuggerClient())
790
        d->m_adapter.activeDebuggerClient()->executeRunToLine(modifiedData);
791 792
    notifyInferiorRunRequested();
    notifyInferiorRunOk();
793 794 795 796 797 798 799 800
}

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

801
void QmlEngine::executeJumpToLine(const ContextData &data)
802
{
803
    Q_UNUSED(data)
804 805 806 807 808
    XSDEBUG("FIXME:  QmlEngine::executeJumpToLine()");
}

void QmlEngine::activateFrame(int index)
{
809 810 811
    if (state() != InferiorStopOk && state() != InferiorUnrunnable)
        return;

812 813 814
    if (d->m_adapter.activeDebuggerClient()) {
        d->m_adapter.activeDebuggerClient()->activateFrame(index);
    }
815
    gotoLocation(stackHandler()->frames().value(index));
816 817 818 819 820 821 822
}

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

823 824 825 826 827 828 829
void QmlEngine::insertBreakpoint(BreakpointModelId id)
{
    BreakHandler *handler = breakHandler();
    BreakpointState state = handler->state(id);
    QTC_ASSERT(state == BreakpointInsertRequested, qDebug() << id << this << state);
    handler->notifyBreakpointInsertProceeding(id);

830 831 832 833 834 835 836 837 838 839 840 841 842 843
    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)) {
            d->pendingBreakpoints.insertMulti(params.fileName, id);
            return;
        }
        if (!valid)
            return;
    }

844
    if (d->m_adapter.activeDebuggerClient()) {
845
        d->m_adapter.activeDebuggerClient()->insertBreakpoint(id, line, column);
846
    } else {
847
        foreach (BaseQmlDebuggerClient *client, d->m_adapter.debuggerClients()) {
848
            client->insertBreakpoint(id, line, column);
849 850 851 852 853 854 855
        }
    }
}

void QmlEngine::removeBreakpoint(BreakpointModelId id)
{
    BreakHandler *handler = breakHandler();
856 857 858 859 860 861 862 863 864 865 866 867 868 869 870

    const BreakpointParameters &params = handler->breakpointData(id);
    if (params.type == BreakpointByFileAndLine &&
            d->pendingBreakpoints.contains(params.fileName)) {
        QHash<QString, BreakpointModelId>::iterator i =
                d->pendingBreakpoints.find(params.fileName);
        while (i != d->pendingBreakpoints.end() && i.key() == params.fileName) {
            if (i.value() == id) {
                d->pendingBreakpoints.erase(i);
                return;
            }
            ++i;
        }
    }

871 872 873 874 875
    BreakpointState state = handler->state(id);
    QTC_ASSERT(state == BreakpointRemoveRequested, qDebug() << id << this << state);
    handler->notifyBreakpointRemoveProceeding(id);

    if (d->m_adapter.activeDebuggerClient()) {
876
        d->m_adapter.activeDebuggerClient()->removeBreakpoint(id);
877
    } else {
878
        foreach (BaseQmlDebuggerClient *client, d->m_adapter.debuggerClients()) {
879
            client->removeBreakpoint(id);
880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895
        }
    }

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

    if (d->m_adapter.activeDebuggerClient()) {
896
        d->m_adapter.activeDebuggerClient()->changeBreakpoint(id);
897
    } else {
898
        foreach (BaseQmlDebuggerClient *client, d->m_adapter.debuggerClients()) {
899
            client->changeBreakpoint(id);
900 901 902 903 904 905 906 907
        }
    }

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

908 909
void QmlEngine::attemptBreakpointSynchronization()
{
910 911 912 913 914
    if (!stateAcceptsBreakpointChanges()) {
        showMessage(_("BREAKPOINT SYNCHRONIZATION NOT POSSIBLE IN CURRENT STATE"));
        return;
    }

hjk's avatar
hjk committed
915
    BreakHandler *handler = breakHandler();
916

917
    DebuggerEngine *bpOwner = isSlaveEngine() ? masterEngine() : this;
918
    foreach (BreakpointModelId id, handler->unclaimedBreakpointIds()) {
919 920
        // Take ownership of the breakpoint. Requests insertion.
        if (acceptsBreakpoint(id))
921
            handler->setEngine(id, bpOwner);
922 923
    }

924
    foreach (BreakpointModelId id, handler->engineBreakpointIds(bpOwner)) {
925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944
        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;
945
        }
946
        QTC_ASSERT(false, qDebug() << "UNKNOWN STATE"  << id << state());
947 948
    }

949
    DebuggerEngine::attemptBreakpointSynchronization();
950

951
    if (d->m_adapter.activeDebuggerClient()) {
952
        d->m_adapter.activeDebuggerClient()->synchronizeBreakpoints();
953
    } else {
954
        foreach (BaseQmlDebuggerClient *client, d->m_adapter.debuggerClients()) {
955
            client->synchronizeBreakpoints();
956
        }
957
    }
958 959
}

960
bool QmlEngine::acceptsBreakpoint(BreakpointModelId id) const
961
{
962
    if (!breakHandler()->breakpointData(id).isCppBreakpoint())
963 964 965 966 967
            return true;

    //If it is a Cpp Breakpoint query if the type can be also handled by the debugger client
    //TODO: enable setting of breakpoints before start of debug session
    //For now, the event breakpoint can be set after the activeDebuggerClient is known
968
    //This is because the older client does not support BreakpointOnQmlSignalHandler
969 970 971 972 973
    bool acceptBreakpoint = false;
    if (d->m_adapter.activeDebuggerClient()) {
        acceptBreakpoint = d->m_adapter.activeDebuggerClient()->acceptsBreakpoint(id);
    }
    return acceptBreakpoint;
974 975
}

976 977 978 979 980 981 982 983 984 985 986 987 988
void QmlEngine::loadSymbols(const QString &moduleName)
{
    Q_UNUSED(moduleName)
}

void QmlEngine::loadAllSymbols()
{
}

void QmlEngine::reloadModules()
{
}

989 990 991 992 993 994