OSDN Git Service

Update license.
[qt-creator-jp/qt-creator-jp.git] / src / plugins / qmljsinspector / qmljsclientproxy.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
8 **
9 **
10 ** GNU Lesser General Public License Usage
11 **
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 **
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 **
23 ** Other Usage
24 **
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
27 **
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 "qmljsclientproxy.h"
34 #include "qmljsprivateapi.h"
35 #include "qmljsobserverclient.h"
36 #include "qmljsinspector.h"
37
38 #include <debugger/debuggerplugin.h>
39 #include <debugger/debuggerrunner.h>
40 #include <debugger/qml/qmlengine.h>
41 #include <debugger/qml/qmladapter.h>
42 #include <extensionsystem/pluginmanager.h>
43 #include <utils/qtcassert.h>
44 #include <projectexplorer/project.h>
45
46 #include <QtCore/QUrl>
47 #include <QtNetwork/QAbstractSocket>
48
49 using namespace QmlJSInspector::Internal;
50
51 ClientProxy::ClientProxy(Debugger::QmlAdapter *adapter, QObject *parent)
52     : QObject(parent)
53     , m_adapter(adapter)
54     , m_engineClient(0)
55     , m_observerClient(0)
56     , m_engineQuery(0)
57     , m_contextQuery(0)
58     , m_isConnected(false)
59 {
60     m_requestObjectsTimer.setSingleShot(true);
61     m_requestObjectsTimer.setInterval(3000);
62     connect(&m_requestObjectsTimer, SIGNAL(timeout()), this, SLOT(refreshObjectTree()));
63     connectToServer();
64 }
65
66 void ClientProxy::connectToServer()
67 {
68     m_engineClient = new QDeclarativeEngineDebug(m_adapter->connection(), this);
69
70     connect(m_engineClient, SIGNAL(newObjects()), this, SLOT(newObjects()));
71
72     m_observerClient = new QmlJSObserverClient(m_adapter->connection(), this);
73
74     connect(m_observerClient, SIGNAL(connectedStatusChanged(QDeclarativeDebugClient::Status)),
75              this, SLOT(clientStatusChanged(QDeclarativeDebugClient::Status)));
76     connect(m_observerClient, SIGNAL(currentObjectsChanged(QList<int>)),
77         SLOT(onCurrentObjectsChanged(QList<int>)));
78     connect(m_observerClient, SIGNAL(colorPickerActivated()),
79         SIGNAL(colorPickerActivated()));
80     connect(m_observerClient, SIGNAL(zoomToolActivated()),
81         SIGNAL(zoomToolActivated()));
82     connect(m_observerClient, SIGNAL(selectToolActivated()),
83         SIGNAL(selectToolActivated()));
84     connect(m_observerClient, SIGNAL(selectMarqueeToolActivated()),
85         SIGNAL(selectMarqueeToolActivated()));
86     connect(m_observerClient, SIGNAL(animationSpeedChanged(qreal)),
87         SIGNAL(animationSpeedChanged(qreal)));
88     connect(m_observerClient, SIGNAL(animationPausedChanged(bool)),
89         SIGNAL(animationPausedChanged(bool)));
90     connect(m_observerClient, SIGNAL(designModeBehaviorChanged(bool)),
91         SIGNAL(designModeBehaviorChanged(bool)));
92     connect(m_observerClient, SIGNAL(showAppOnTopChanged(bool)),
93         SIGNAL(showAppOnTopChanged(bool)));
94     connect(m_observerClient, SIGNAL(reloaded()), this,
95         SIGNAL(serverReloaded()));
96     connect(m_observerClient, SIGNAL(selectedColorChanged(QColor)),
97         SIGNAL(selectedColorChanged(QColor)));
98     connect(m_observerClient, SIGNAL(contextPathUpdated(QStringList)),
99         SIGNAL(contextPathUpdated(QStringList)));
100     connect(m_observerClient, SIGNAL(logActivity(QString,QString)),
101             m_adapter, SLOT(logServiceActivity(QString,QString)));
102
103     updateConnected();
104 }
105
106 void ClientProxy::clientStatusChanged(QDeclarativeDebugClient::Status status)
107 {
108     QString serviceName;
109     if (QDeclarativeDebugClient *client = qobject_cast<QDeclarativeDebugClient*>(sender())) {
110         serviceName = client->name();
111     }
112
113     m_adapter->logServiceStatusChange(serviceName, status);
114
115     updateConnected();
116 }
117
118 void ClientProxy::refreshObjectTree()
119 {
120     if (!m_contextQuery) {
121         m_requestObjectsTimer.stop();
122         qDeleteAll(m_objectTreeQuery);
123         m_objectTreeQuery.clear();
124         queryEngineContext(m_engines.value(0).debugId());
125     }
126 }
127
128 void ClientProxy::onCurrentObjectsChanged(const QList<int> &debugIds, bool requestIfNeeded)
129 {
130     QList<QDeclarativeDebugObjectReference> selectedItems;
131
132     foreach (int debugId, debugIds) {
133         QDeclarativeDebugObjectReference ref = objectReferenceForId(debugId);
134         if (ref.debugId() != -1) {
135             selectedItems << ref;
136         } else if (requestIfNeeded) {
137             // ### FIXME right now, there's no way in the protocol to
138             // a) get some item and know its parent (although that's possible
139             //    by adding it to a separate plugin)
140             // b) add children to part of an existing tree.
141             // So the only choice that remains is to update the complete
142             // tree when we have an unknown debug id.
143             // break;
144         }
145     }
146
147     emit selectedItemsChanged(selectedItems);
148 }
149
150 void ClientProxy::setSelectedItemsByObjectId(const QList<QDeclarativeDebugObjectReference> &objectRefs)
151 {
152     if (isConnected()) {
153         QList<int> debugIds;
154
155         foreach (const QDeclarativeDebugObjectReference &ref, objectRefs) {
156             debugIds << ref.debugId();
157         }
158
159         m_observerClient->setCurrentObjects(debugIds);
160     }
161 }
162
163 QDeclarativeDebugObjectReference ClientProxy::objectReferenceForId(int debugId) const
164 {
165     foreach (const QDeclarativeDebugObjectReference& it, m_rootObjects) {
166         QDeclarativeDebugObjectReference result = objectReferenceForId(debugId, it);
167         if (result.debugId() == debugId)
168             return result;
169     }
170     return QDeclarativeDebugObjectReference();
171 }
172
173 void ClientProxy::log(LogDirection direction, const QString &message)
174 {
175     QString msg;
176     if (direction == LogSend) {
177         msg += " sending ";
178     } else {
179         msg += " receiving ";
180     }
181     msg += message;
182
183     m_adapter->logServiceActivity("QDeclarativeDebug", msg);
184 }
185
186 QList<QDeclarativeDebugObjectReference> QmlJSInspector::Internal::ClientProxy::rootObjectReference() const
187 {
188     return m_rootObjects;
189 }
190
191 QDeclarativeDebugObjectReference ClientProxy::objectReferenceForId(int debugId,
192                                                                    const QDeclarativeDebugObjectReference &objectRef) const
193 {
194     if (objectRef.debugId() == debugId)
195         return objectRef;
196
197     foreach (const QDeclarativeDebugObjectReference &child, objectRef.children()) {
198         QDeclarativeDebugObjectReference result = objectReferenceForId(debugId, child);
199         if (result.debugId() == debugId)
200             return result;
201     }
202
203     return QDeclarativeDebugObjectReference();
204 }
205
206 QDeclarativeDebugObjectReference ClientProxy::objectReferenceForId(const QString &objectId) const
207 {
208     if (!objectId.isEmpty() && objectId[0].isLower()) {
209         const QList<QDeclarativeDebugObjectReference> refs = objectReferences();
210         foreach (const QDeclarativeDebugObjectReference &ref, refs) {
211             if (ref.idString() == objectId)
212                 return ref;
213         }
214     }
215     return QDeclarativeDebugObjectReference();
216 }
217
218 QDeclarativeDebugObjectReference ClientProxy::objectReferenceForLocation(const int line, const int column) const
219 {
220     const QList<QDeclarativeDebugObjectReference> refs = objectReferences();
221     foreach (const QDeclarativeDebugObjectReference &ref, refs) {
222         if (ref.source().lineNumber() == line && ref.source().columnNumber() == column)
223             return ref;
224     }
225
226     return QDeclarativeDebugObjectReference();
227 }
228
229 QList<QDeclarativeDebugObjectReference> ClientProxy::objectReferences() const
230 {
231     QList<QDeclarativeDebugObjectReference> result;
232     foreach (const QDeclarativeDebugObjectReference &it, m_rootObjects) {
233         result.append(objectReferences(it));
234     }
235     return result;
236 }
237
238 QList<QDeclarativeDebugObjectReference> ClientProxy::objectReferences(const QDeclarativeDebugObjectReference &objectRef) const
239 {
240     QList<QDeclarativeDebugObjectReference> result;
241     result.append(objectRef);
242
243     foreach (const QDeclarativeDebugObjectReference &child, objectRef.children()) {
244         result.append(objectReferences(child));
245     }
246
247     return result;
248 }
249
250 bool ClientProxy::setBindingForObject(int objectDebugId,
251                                       const QString &propertyName,
252                                       const QVariant &value,
253                                       bool isLiteralValue)
254 {
255     if (objectDebugId == -1)
256         return false;
257
258     if (propertyName == QLatin1String("id"))
259         return false; // Crashes the QMLViewer.
260
261     if (!isConnected())
262         return false;
263
264     log(LogSend, QString("SET_BINDING %1 %2 %3 %4").arg(QString::number(objectDebugId), propertyName, value.toString(), QString(isLiteralValue ? "true" : "false")));
265
266     bool result = m_engineClient->setBindingForObject(objectDebugId, propertyName, value.toString(), isLiteralValue);
267
268     if (!result)
269         log(LogSend, QString("failed!"));
270
271     return result;
272 }
273
274 bool ClientProxy::setMethodBodyForObject(int objectDebugId, const QString &methodName, const QString &methodBody)
275 {
276     if (objectDebugId == -1)
277         return false;
278
279     if (!isConnected())
280         return false;
281
282     log(LogSend, QString("SET_METHOD_BODY %1 %2 %3").arg(QString::number(objectDebugId), methodName, methodBody));
283
284     bool result = m_engineClient->setMethodBody(objectDebugId, methodName, methodBody);
285
286     if (!result)
287         log(LogSend, QString("failed!"));
288
289     return result;
290 }
291
292 bool ClientProxy::resetBindingForObject(int objectDebugId, const QString& propertyName)
293 {
294     if (objectDebugId == -1)
295         return false;
296
297     if (!isConnected())
298         return false;
299
300     log(LogSend, QString("RESET_BINDING %1 %2").arg(QString::number(objectDebugId), propertyName));
301
302     bool result = m_engineClient->resetBindingForObject(objectDebugId, propertyName);
303
304     if (!result)
305         log(LogSend, QString("failed!"));
306
307     return result;
308 }
309
310 QDeclarativeDebugExpressionQuery *ClientProxy::queryExpressionResult(int objectDebugId, const QString &expr)
311 {
312     if (objectDebugId == -1)
313         return 0;
314
315     if (!isConnected())
316         return 0;
317
318     bool block = m_adapter->disableJsDebugging(true);
319
320     log(LogSend, QString("EVAL_EXPRESSION %1 %2").arg(QString::number(objectDebugId), expr));
321     QDeclarativeDebugExpressionQuery *query
322             = m_engineClient->queryExpressionResult(objectDebugId, expr, m_engineClient);
323
324     m_adapter->disableJsDebugging(block);
325     return query;
326 }
327
328 void ClientProxy::clearComponentCache()
329 {
330     if (isConnected())
331         m_observerClient->clearComponentCache();
332 }
333
334 bool ClientProxy::addObjectWatch(int objectDebugId)
335 {
336     if (objectDebugId == -1)
337         return false;
338
339     if (!isConnected())
340         return false;
341
342     // already set
343     if (m_objectWatches.keys().contains(objectDebugId))
344         return true;
345
346     QDeclarativeDebugObjectReference ref = objectReferenceForId(objectDebugId);
347     if (ref.debugId() != objectDebugId)
348         return false;
349
350     // is flooding the debugging output log!
351     // log(LogSend, QString("WATCH_PROPERTY %1").arg(objectDebugId));
352
353     QDeclarativeDebugWatch *watch = m_engineClient->addWatch(ref, m_engineClient);
354     m_objectWatches.insert(objectDebugId, watch);
355
356     connect(watch,SIGNAL(valueChanged(QByteArray,QVariant)),this,SLOT(objectWatchTriggered(QByteArray,QVariant)));
357
358     return false;
359 }
360
361 void ClientProxy::objectWatchTriggered(const QByteArray &propertyName, const QVariant &propertyValue)
362 {
363     // is flooding the debugging output log!
364     // log(LogReceive, QString("UPDATE_WATCH %1 %2").arg(QString::fromAscii(propertyName), propertyValue.toString()));
365
366     QDeclarativeDebugWatch *watch = dynamic_cast<QDeclarativeDebugWatch *>(QObject::sender());
367     if (watch)
368         emit propertyChanged(watch->objectDebugId(),propertyName, propertyValue);
369 }
370
371 bool ClientProxy::removeObjectWatch(int objectDebugId)
372 {
373     if (objectDebugId == -1)
374         return false;
375
376     if (!m_objectWatches.keys().contains(objectDebugId))
377         return false;
378
379     if (!isConnected())
380         return false;
381
382     QDeclarativeDebugWatch *watch = m_objectWatches.value(objectDebugId);
383     disconnect(watch,SIGNAL(valueChanged(QByteArray,QVariant)), this, SLOT(objectWatchTriggered(QByteArray,QVariant)));
384
385     // is flooding the debugging output log!
386     // log(LogSend, QString("NO_WATCH %1").arg(QString::number(objectDebugId)));
387
388     m_engineClient->removeWatch(watch);
389     delete watch;
390     m_objectWatches.remove(objectDebugId);
391
392
393     return true;
394 }
395
396 void ClientProxy::removeAllObjectWatches()
397 {
398     foreach (int watchedObject, m_objectWatches.keys())
399         removeObjectWatch(watchedObject);
400 }
401
402 void ClientProxy::queryEngineContext(int id)
403 {
404     if (id < 0)
405         return;
406
407     if (!isConnected())
408         return;
409
410     if (m_contextQuery) {
411         delete m_contextQuery;
412         m_contextQuery = 0;
413     }
414
415     log(LogSend, QString("LIST_OBJECTS %1").arg(QString::number(id)));
416
417     m_contextQuery = m_engineClient->queryRootContexts(QDeclarativeDebugEngineReference(id),
418                                                        m_engineClient);
419     if (!m_contextQuery->isWaiting())
420         contextChanged();
421     else
422         connect(m_contextQuery, SIGNAL(stateChanged(QDeclarativeDebugQuery::State)),
423                 this, SLOT(contextChanged()));
424 }
425
426 void ClientProxy::contextChanged()
427 {
428     log(LogReceive, QString("LIST_OBJECTS_R"));
429     if (m_contextQuery) {
430         m_rootObjects.clear();
431         QDeclarativeDebugContextReference rootContext = m_contextQuery->rootContext();
432         delete m_contextQuery;
433         m_contextQuery = 0;
434
435         qDeleteAll(m_objectTreeQuery);
436         m_objectTreeQuery.clear();
437         m_requestObjectsTimer.stop();
438
439         fetchContextObjectRecursive(rootContext);
440     }
441 }
442
443 void ClientProxy::fetchContextObjectRecursive(const QDeclarativeDebugContextReference& context)
444 {
445     if (!isConnected())
446         return;
447
448     foreach (const QDeclarativeDebugObjectReference & obj, context.objects()) {
449
450         log(LogSend, QString("FETCH_OBJECT %1").arg(obj.idString()));
451
452         QDeclarativeDebugObjectQuery* query
453                 = m_engineClient->queryObjectRecursive(obj, m_engineClient);
454         if (!query->isWaiting()) {
455             query->deleteLater(); //ignore errors;
456         } else {
457             m_objectTreeQuery << query;
458             connect(query,
459                     SIGNAL(stateChanged(QDeclarativeDebugQuery::State)),
460                     SLOT(objectTreeFetched(QDeclarativeDebugQuery::State)));
461         }
462     }
463     foreach (const QDeclarativeDebugContextReference& child, context.contexts()) {
464         fetchContextObjectRecursive(child);
465     }
466 }
467
468
469 void ClientProxy::objectTreeFetched(QDeclarativeDebugQuery::State state)
470 {
471     QDeclarativeDebugObjectQuery *query = qobject_cast<QDeclarativeDebugObjectQuery *>(sender());
472     if (!query || state == QDeclarativeDebugQuery::Error) {
473         delete query;
474         return;
475     }
476
477     log(LogReceive, QString("FETCH_OBJECT_R %1").arg(query->object().idString()));
478
479     m_rootObjects.append(query->object());
480
481     int removed = m_objectTreeQuery.removeAll(query);
482     Q_ASSERT(removed == 1);
483     Q_UNUSED(removed);
484     delete query;
485
486     if (m_objectTreeQuery.isEmpty()) {
487         int old_count = m_debugIdHash.count();
488         m_debugIdHash.clear();
489         m_debugIdHash.reserve(old_count + 1);
490         foreach (const QDeclarativeDebugObjectReference &it, m_rootObjects)
491             buildDebugIdHashRecursive(it);
492         emit objectTreeUpdated();
493
494         if (isConnected()) {
495             if (!m_observerClient->currentObjects().isEmpty())
496                 onCurrentObjectsChanged(m_observerClient->currentObjects(), false);
497
498             m_observerClient->setObjectIdList(m_rootObjects);
499         }
500     }
501 }
502
503 void ClientProxy::buildDebugIdHashRecursive(const QDeclarativeDebugObjectReference& ref)
504 {
505     QString filename = ref.source().url().toLocalFile();
506     int lineNum = ref.source().lineNumber();
507     int colNum = ref.source().columnNumber();
508     int rev = 0;
509
510     // handle the case where the url contains the revision number encoded. (for object created by the debugger)
511     static QRegExp rx("(.*)_(\\d+):(\\d+)$");
512     if (rx.exactMatch(filename)) {
513         filename = rx.cap(1);
514         rev = rx.cap(2).toInt();
515         lineNum += rx.cap(3).toInt() - 1;
516     }
517
518     filename = InspectorUi::instance()->findFileInProject(filename);
519
520     // append the debug ids in the hash
521     m_debugIdHash[qMakePair<QString, int>(filename, rev)][qMakePair<int, int>(lineNum, colNum)].append(ref.debugId());
522
523     foreach (const QDeclarativeDebugObjectReference &it, ref.children())
524         buildDebugIdHashRecursive(it);
525 }
526
527
528 void ClientProxy::reloadQmlViewer()
529 {
530     if (isConnected())
531         m_observerClient->reloadViewer();
532 }
533
534 void ClientProxy::setDesignModeBehavior(bool inDesignMode)
535 {
536     if (isConnected())
537         m_observerClient->setDesignModeBehavior(inDesignMode);
538 }
539
540 void ClientProxy::setAnimationSpeed(qreal slowDownFactor)
541 {
542     if (isConnected())
543         m_observerClient->setAnimationSpeed(slowDownFactor);
544 }
545
546 void ClientProxy::setAnimationPaused(bool paused)
547 {
548     if (isConnected())
549         m_observerClient->setAnimationPaused(paused);
550 }
551
552 void ClientProxy::changeToColorPickerTool()
553 {
554     if (isConnected())
555         m_observerClient->changeToColorPickerTool();
556 }
557
558 void ClientProxy::changeToZoomTool()
559 {
560     if (isConnected())
561         m_observerClient->changeToZoomTool();
562 }
563 void ClientProxy::changeToSelectTool()
564 {
565     if (isConnected())
566         m_observerClient->changeToSelectTool();
567 }
568
569 void ClientProxy::changeToSelectMarqueeTool()
570 {
571     if (isConnected())
572         m_observerClient->changeToSelectMarqueeTool();
573 }
574
575 void ClientProxy::showAppOnTop(bool showOnTop)
576 {
577     if (isConnected())
578         m_observerClient->showAppOnTop(showOnTop);
579 }
580
581 void ClientProxy::createQmlObject(const QString &qmlText, int parentDebugId,
582                                   const QStringList &imports, const QString &filename)
583 {
584     if (isConnected())
585         m_observerClient->createQmlObject(qmlText, parentDebugId, imports, filename);
586 }
587
588 void ClientProxy::destroyQmlObject(int debugId)
589 {
590     if (isConnected())
591         m_observerClient->destroyQmlObject(debugId);
592 }
593
594 void ClientProxy::reparentQmlObject(int debugId, int newParent)
595 {
596     if (isConnected())
597         m_observerClient->reparentQmlObject(debugId, newParent);
598 }
599
600 void ClientProxy::setContextPathIndex(int contextIndex)
601 {
602     if (isConnected())
603         m_observerClient->setContextPathIndex(contextIndex);
604 }
605
606 void ClientProxy::updateConnected()
607 {
608     bool isConnected = m_observerClient && m_observerClient->status() == QDeclarativeDebugClient::Enabled
609             && m_engineClient && m_engineClient->status() == QDeclarativeEngineDebug::Enabled;
610
611     if (isConnected != m_isConnected) {
612         m_isConnected = isConnected;
613         if (isConnected) {
614             emit connected();
615             reloadEngines();
616         } else {
617             emit disconnected();
618         }
619     }
620 }
621
622 void ClientProxy::reloadEngines()
623 {
624     if (m_engineQuery) {
625         emit connectionStatusMessage("[Inspector] Waiting for response to previous engine query");
626         return;
627     }
628
629     if (!isConnected())
630         return;
631
632     emit aboutToReloadEngines();
633
634     log(LogSend, QString("LIST_ENGINES"));
635
636     m_engineQuery = m_engineClient->queryAvailableEngines(m_engineClient);
637     if (!m_engineQuery->isWaiting()) {
638         updateEngineList();
639     } else {
640         connect(m_engineQuery, SIGNAL(stateChanged(QDeclarativeDebugQuery::State)),
641                          this, SLOT(updateEngineList()));
642     }
643 }
644
645 QList<QDeclarativeDebugEngineReference> ClientProxy::engines() const
646 {
647     return m_engines;
648 }
649
650 void ClientProxy::updateEngineList()
651 {
652     log(LogReceive, QString("LIST_ENGINES_R"));
653
654     m_engines = m_engineQuery->engines();
655     delete m_engineQuery;
656     m_engineQuery = 0;
657
658     emit enginesChanged();
659 }
660
661 Debugger::QmlAdapter *ClientProxy::qmlAdapter() const
662 {
663     return m_adapter;
664 }
665
666 bool ClientProxy::isConnected() const
667 {
668     return m_isConnected;
669 }
670
671 void ClientProxy::newObjects()
672 {
673     log(LogReceive, QString("OBJECT_CREATED"));
674     if (!m_requestObjectsTimer.isActive())
675         m_requestObjectsTimer.start();
676 }