summaryrefslogtreecommitdiff
path: root/src/webchannel
diff options
context:
space:
mode:
Diffstat (limited to 'src/webchannel')
-rw-r--r--src/webchannel/doc/src/index.qdoc40
-rw-r--r--src/webchannel/doc/src/javascript.qdoc17
-rw-r--r--src/webchannel/qmetaobjectpublisher.cpp66
-rw-r--r--src/webchannel/qmetaobjectpublisher_p.h9
-rw-r--r--src/webchannel/qqmlwebchannel.cpp21
-rw-r--r--src/webchannel/qwebchannel.cpp10
-rw-r--r--src/webchannel/qwebchannel.js18
-rw-r--r--src/webchannel/qwebchannelabstracttransport.cpp7
8 files changed, 134 insertions, 54 deletions
diff --git a/src/webchannel/doc/src/index.qdoc b/src/webchannel/doc/src/index.qdoc
index 5f354bd..43f4e6f 100644
--- a/src/webchannel/doc/src/index.qdoc
+++ b/src/webchannel/doc/src/index.qdoc
@@ -31,26 +31,50 @@
\title Qt WebChannel
\brief Bridges the gap between Qt applications and HTML/JavaScript.
- Qt WebChannel enables peer-to-peer communication between the host
- (QML/C++ application) and the client (HTML/JavaScript application). The
- transport mechanism is supported out of the box by the two popular web
- engines, Qt WebKit 2 and Qt WebEngine (experimental). It works on all
- browsers that support Qt WebSockets, enabling Qt WebChannel applications
+ Qt WebChannel enables peer-to-peer communication between the server
+ (QML/C++ application) and the client (HTML/JavaScript or QML application). The
+ transport mechanism is supported out of the box by \l{Qt WebEngine}. It works on all
+ browsers that support \l{Qt WebSockets}, enabling Qt WebChannel applications
to run in any JavaScript runtime. Additionally, a custom transport
mechanism can also be implemented using Qt WebSockets to support Qt
WebChannel-based communication.
The module provides a JavaScript library for seamless integration of C++
- and QML applications with HTML/JavaScript clients. The client must use the
+ and QML applications with HTML/JavaScript and QML clients. The clients must use the
JavaScript library to access the serialized QObjects published by the host
applications.
- \section1 Related Information
+ \section1 Getting Started
+
+ To use these classes in your application, use the following include
+ statement:
+
+ \code
+ #include <QtWebChannel/QtWebChannel>
+ \endcode
+
+ To link against the module, add this line to your \l qmake \c .pro file:
+
+ \code
+ QT += webchannel
+ \endcode
+
+ The QML types are accessed by using:
+ \badcode
+ import QtWebChannel 1.0
+ \endcode
+
+ \section1 API Reference
\list
\li \l{Qt WebChannel JavaScript API}{JavaScript API}
\li \l{Qt WebChannel C++ Classes}{C++ API}
\li \l{Qt WebChannel QML Types}{QML API}
- \li \l{Qt WebChannel Examples}{Examples} - show how use the API in practice
+ \endlist
+
+ \section1 Examples
+
+ \list
+ \li \l{Qt WebChannel Examples}{Examples}
\endlist
*/
diff --git a/src/webchannel/doc/src/javascript.qdoc b/src/webchannel/doc/src/javascript.qdoc
index cfb5b21..a508ff9 100644
--- a/src/webchannel/doc/src/javascript.qdoc
+++ b/src/webchannel/doc/src/javascript.qdoc
@@ -31,21 +31,20 @@
\brief This page explains how to use the JavaScript QWebChannel API in HTML clients.
- \section1 Setup
+ \section1 Setting up the JavaScript API
- To communicate with a QWebChannel or WebChannel, any HTML client must use and setup the
- JavaScript API provided by \c qwebchannel.js. For HTML clients run inside Qt WebKit, you
- can load the file via \c qrc:///qtwebchannel/qwebchannel.js. For external clients you will
- need to copy the file to your webserver. Then instantiate a QWebChannel object and pass
+ To communicate with a QWebChannel or \l [QML] WebChannel, a client must use and set up the
+ JavaScript API provided by \c qwebchannel.js. For clients run inside \l{Qt WebEngine}, you
+ can load the file via \c qrc:///qtwebchannel/qwebchannel.js. For external clients, you
+ need to copy the file to your web server. Then instantiate a QWebChannel object and pass
it a transport object and a callback function, which will be invoked once the
- initialization of the channel finished and published objects become available.
+ initialization of the channel finishes and the published objects become available.
The transport object implements a minimal message passing interface. It should be an object
with a \c send() function, which takes a stringified JSON message and transmits it to the
server-side QWebChannelAbstractTransport object. Furthermore, its \c onmessage property should
- be called when a message from the server was received. This interface is implemented internally
- by the Qt WebKit navigator.qtWebChannelTransport object. Alternatively, you can also use a
- WebSocket, which also implements this interface.
+ be called when a message from the server was received. Alternatively, you can use a
+ \l{Qt WebSockets}{WebSocket} to implement the interface.
Note that the JavaScript QWebChannel object should be constructed once the transport object is
fully operational. In case of a WebSocket, that means you should create the QWebChannel in the
diff --git a/src/webchannel/qmetaobjectpublisher.cpp b/src/webchannel/qmetaobjectpublisher.cpp
index a0c3af7..e9d93ea 100644
--- a/src/webchannel/qmetaobjectpublisher.cpp
+++ b/src/webchannel/qmetaobjectpublisher.cpp
@@ -372,11 +372,7 @@ QVariant QMetaObjectPublisher::invokeMethod(QObject *const object, const int met
// construct converter objects of QVariant to QGenericArgument
VariantArgument arguments[10];
for (int i = 0; i < qMin(args.size(), method.parameterCount()); ++i) {
- QVariant arg = args.at(i).toVariant();
- if (method.parameterType(i) != QMetaType::QVariant && !arg.convert(method.parameterType(i))) {
- qWarning() << "Could not convert argument" << args.at(i) << "to target type" << method.parameterTypes().at(i) << '.';
- }
- arguments[i].value = arg;
+ arguments[i].value = toVariant(args.at(i), method.parameterType(i));
}
// construct QGenericReturnArgument
@@ -397,6 +393,16 @@ QVariant QMetaObjectPublisher::invokeMethod(QObject *const object, const int met
return returnValue;
}
+void QMetaObjectPublisher::setProperty(QObject *object, const int propertyIndex, const QJsonValue &value)
+{
+ QMetaProperty property = object->metaObject()->property(propertyIndex);
+ if (!property.isValid()) {
+ qWarning() << "Cannot set unknown property" << propertyIndex << "of object" << object;
+ } else if (!property.write(object, toVariant(value, property.userType()))) {
+ qWarning() << "Could not write value " << value << "to property" << property.name() << "of object" << object;
+ }
+}
+
void QMetaObjectPublisher::signalEmitted(const QObject *object, const int signalIndex, const QVariantList &arguments)
{
if (!webChannel || webChannel->d_func()->transports.isEmpty()) {
@@ -449,6 +455,46 @@ void QMetaObjectPublisher::objectDestroyed(const QObject *object)
pendingPropertyUpdates.remove(object);
}
+QObject *QMetaObjectPublisher::unwrapObject(const QString &objectId) const
+{
+ if (!objectId.isEmpty()) {
+ ObjectInfo objectInfo = wrappedObjects.value(objectId);
+ if (objectInfo.object && !objectInfo.classinfo.isEmpty())
+ return objectInfo.object;
+ }
+
+ qWarning() << "No wrapped object" << objectId;
+ return Q_NULLPTR;
+}
+
+QVariant QMetaObjectPublisher::toVariant(const QJsonValue &value, int targetType) const
+{
+ if (targetType == QMetaType::QJsonValue) {
+ return QVariant::fromValue(value);
+ } else if (targetType == QMetaType::QJsonArray) {
+ if (!value.isArray())
+ qWarning() << "Cannot not convert non-array argument" << value << "to QJsonArray.";
+ return QVariant::fromValue(value.toArray());
+ } else if (targetType == QMetaType::QJsonObject) {
+ if (!value.isObject())
+ qWarning() << "Cannot not convert non-object argument" << value << "to QJsonObject.";
+ return QVariant::fromValue(value.toObject());
+ } else if (QMetaType::typeFlags(targetType) & QMetaType::PointerToQObject) {
+ QObject *unwrappedObject = unwrapObject(value.toObject()[KEY_ID].toString());
+ if (unwrappedObject == Q_NULLPTR)
+ qWarning() << "Cannot not convert non-object argument" << value << "to QObject*.";
+ return QVariant::fromValue(unwrappedObject);
+ }
+
+ // this converts QJsonObjects to QVariantMaps, which is not desired when
+ // we want to get a QJsonObject or QJsonValue (see above)
+ QVariant variant = value.toVariant();
+ if (targetType != QMetaType::QVariant && !variant.convert(targetType)) {
+ qWarning() << "Could not convert argument" << value << "to target type" << QVariant::typeToName(targetType) << '.';
+ }
+ return variant;
+}
+
// NOTE: transport can be a nullptr
// in such a case, we need to ensure that the property is registered to
// the target transports of the parentObjectId
@@ -594,14 +640,8 @@ void QMetaObjectPublisher::handleMessage(const QJsonObject &message, QWebChannel
} else if (type == TypeDisconnectFromSignal) {
signalHandler.disconnectFrom(object, message.value(KEY_SIGNAL).toInt(-1));
} else if (type == TypeSetProperty) {
- const int propertyIdx = message.value(KEY_PROPERTY).toInt(-1);
- QMetaProperty property = object->metaObject()->property(propertyIdx);
- if (!property.isValid()) {
- qWarning() << "Cannot set unknown property" << message.value(KEY_PROPERTY) << "of object" << objectName;
- } else if (!object->metaObject()->property(propertyIdx).write(object, message.value(KEY_VALUE).toVariant())) {
- qWarning() << "Could not write value " << message.value(KEY_VALUE)
- << "to property" << property.name() << "of object" << objectName;
- }
+ setProperty(object, message.value(KEY_PROPERTY).toInt(-1),
+ message.value(KEY_VALUE));
}
}
}
diff --git a/src/webchannel/qmetaobjectpublisher_p.h b/src/webchannel/qmetaobjectpublisher_p.h
index f5109b6..29956e0 100644
--- a/src/webchannel/qmetaobjectpublisher_p.h
+++ b/src/webchannel/qmetaobjectpublisher_p.h
@@ -156,6 +156,11 @@ public:
QVariant invokeMethod(QObject *const object, const int methodIndex, const QJsonArray &args);
/**
+ * Set the value of property @p propertyIndex on @p object to @p value.
+ */
+ void setProperty(QObject *object, const int propertyIndex, const QJsonValue &value);
+
+ /**
* Callback of the signalHandler which forwards the signal invocation to the webchannel clients.
*/
void signalEmitted(const QObject *object, const int signalIndex, const QVariantList &arguments);
@@ -167,6 +172,10 @@ public:
*/
void objectDestroyed(const QObject *object);
+ QObject *unwrapObject(const QString &objectId) const;
+
+ QVariant toVariant(const QJsonValue &value, int targetType) const;
+
/**
* Given a QVariant containing a QObject*, wrap the object and register for property updates
* return the objects class information.
diff --git a/src/webchannel/qqmlwebchannel.cpp b/src/webchannel/qqmlwebchannel.cpp
index 264bc54..d23ebef 100644
--- a/src/webchannel/qqmlwebchannel.cpp
+++ b/src/webchannel/qqmlwebchannel.cpp
@@ -69,7 +69,7 @@ QT_BEGIN_NAMESPACE
A list of transport objects, which implement QWebChannelAbstractTransport. The transports
are used to talk to the remote clients.
- \sa WebChannel::connectTo(), WebChannel::disconnectFrom()
+ \sa connectTo(), disconnectFrom()
*/
/*!
@@ -77,7 +77,7 @@ QT_BEGIN_NAMESPACE
\brief A list of objects which should be accessible to remote clients.
- The objects must have the attached WebChannel::id property set to an identifier, under which the
+ The objects must have the attached \l id property set to an identifier, under which the
object is then known on the HTML side.
Once registered, all signals and property changes are automatically propagated to the clients.
@@ -86,7 +86,7 @@ QT_BEGIN_NAMESPACE
If one needs to register objects which are not available when the component is created, use the
imperative registerObjects method.
- \sa WebChannel::registerObjects(), WebChannel::id
+ \sa registerObjects(), id
*/
class QQmlWebChannelPrivate : public QWebChannelPrivate
@@ -134,8 +134,8 @@ QQmlWebChannel::~QQmlWebChannel()
/*!
\qmlmethod void WebChannel::registerObjects(QVariantMap objects)
- Register objects to make them accessible to HTML clients. The key of the map is used as an identifier
- for the object on the client side.
+ Registers objects to make them accessible to HTML clients. The key of the
+ map is used as an identifier for the object on the client side.
Once registered, all signals and property changes are automatically propagated to the clients.
Public invokable methods, including slots, are also accessible to the clients.
@@ -143,7 +143,7 @@ QQmlWebChannel::~QQmlWebChannel()
This imperative API can be used to register objects on the fly. For static objects, the declarative
registeredObjects property should be preferred.
- \sa WebChannel::registeredObjects
+ \sa registeredObjects
*/
void QQmlWebChannel::registerObjects(const QVariantMap &objects)
{
@@ -167,11 +167,12 @@ QQmlWebChannelAttached *QQmlWebChannel::qmlAttachedProperties(QObject *obj)
/*!
\qmlmethod void WebChannel::connectTo(QWebChannelAbstractTransport transport)
- \brief Connectect to the \a transport, which represents a communication channel to a single client.
+ \brief Connects to the \a transport, which represents a communication
+ channel to a single client.
The transport object must be an implementation of QWebChannelAbstractTransport.
- \sa WebChannel::transports, WebChannel::disconnectFrom()
+ \sa transports, disconnectFrom()
*/
void QQmlWebChannel::connectTo(QObject *transport)
{
@@ -185,12 +186,12 @@ void QQmlWebChannel::connectTo(QObject *transport)
/*!
\qmlmethod void WebChannel::disconnectFrom(QWebChannelAbstractTransport transport)
- \brief Disconnect the \a transport from this WebChannel.
+ \brief Disconnects the \a transport from this WebChannel.
The client will not be able to communicate with the WebChannel anymore, nor will it receive any
signals or property updates.
- \sa WebChannel::connectTo()
+ \sa connectTo()
*/
void QQmlWebChannel::disconnectFrom(QObject *transport)
{
diff --git a/src/webchannel/qwebchannel.cpp b/src/webchannel/qwebchannel.cpp
index b7c62e9..c2e0d19 100644
--- a/src/webchannel/qwebchannel.cpp
+++ b/src/webchannel/qwebchannel.cpp
@@ -52,7 +52,7 @@ QT_BEGIN_NAMESPACE
\class QWebChannel
\inmodule QtWebChannel
- \brief Expose QObjects to remote HTML clients.
+ \brief Exposes QObjects to remote HTML clients.
\since 5.4
The QWebChannel fills the gap between C++ applications and HTML/JavaScript
@@ -69,7 +69,7 @@ QT_BEGIN_NAMESPACE
features used by \c{qwebchannel.js}. As such, one can interact
with basically any modern HTML browser or standalone JavaScript runtime, such as node.js.
- There also exists a declarative WebChannel API.
+ There also exists a declarative \l{Qt WebChannel QML Types}{WebChannel API}.
\sa {Qt WebChannel Standalone Example}, {Qt WebChannel JavaScript API}{JavaScript API}
*/
@@ -136,7 +136,7 @@ QWebChannel::~QWebChannel()
}
/*!
- Register a group of objects to the QWebChannel.
+ Registers a group of objects to the QWebChannel.
The properties, signals and public invokable methods of the objects are published to the remote clients.
There, an object with the identifier used as key in the \a objects map is then constructed.
@@ -166,7 +166,7 @@ QHash<QString, QObject *> QWebChannel::registeredObjects() const
}
/*!
- Register a single object to the QWebChannel.
+ Registers a single object to the QWebChannel.
The properties, signals and public methods of the \a object are published to the remote clients.
There, an object with the identifier \a id is then constructed.
@@ -182,7 +182,7 @@ void QWebChannel::registerObject(const QString &id, QObject *object)
}
/*!
- Deregister the given \a object from the QWebChannel.
+ Deregisters the given \a object from the QWebChannel.
Remote clients will receive a \c destroyed signal for the given object.
diff --git a/src/webchannel/qwebchannel.js b/src/webchannel/qwebchannel.js
index d39e301..5b047c2 100644
--- a/src/webchannel/qwebchannel.js
+++ b/src/webchannel/qwebchannel.js
@@ -319,7 +319,7 @@ function QObject(name, data, webChannel)
this.signalEmitted = function(signalName, signalArgs)
{
- invokeSignalCallbacks(signalName, signalArgs);
+ invokeSignalCallbacks(signalName, this.unwrapQObject(signalArgs));
}
function addMethod(methodData)
@@ -330,10 +330,15 @@ function QObject(name, data, webChannel)
var args = [];
var callback;
for (var i = 0; i < arguments.length; ++i) {
- if (typeof arguments[i] === "function")
- callback = arguments[i];
+ var argument = arguments[i];
+ if (typeof argument === "function")
+ callback = argument;
+ else if (argument instanceof QObject && webChannel.objects[argument.__id__] !== undefined)
+ args.push({
+ "id": argument.__id__
+ });
else
- args.push(arguments[i]);
+ args.push(argument);
}
webChannel.exec({
@@ -387,11 +392,14 @@ function QObject(name, data, webChannel)
return;
}
object.__propertyCache__[propertyIndex] = value;
+ var valueToSend = value;
+ if (valueToSend instanceof QObject && webChannel.objects[valueToSend.__id__] !== undefined)
+ valueToSend = { "id": valueToSend.__id__ };
webChannel.exec({
"type": QWebChannelMessageTypes.setProperty,
"object": object.__id__,
"property": propertyIndex,
- "value": value
+ "value": valueToSend
});
}
});
diff --git a/src/webchannel/qwebchannelabstracttransport.cpp b/src/webchannel/qwebchannelabstracttransport.cpp
index 414c9fe..86a9c18 100644
--- a/src/webchannel/qwebchannelabstracttransport.cpp
+++ b/src/webchannel/qwebchannelabstracttransport.cpp
@@ -50,9 +50,8 @@ QT_BEGIN_NAMESPACE
Users of the QWebChannel must implement this interface and connect instances of it
to the QWebChannel server for every client that should be connected to the QWebChannel.
- The {Qt WebChannel Standalone Example}{Standalone Example} shows how this can be done
- using Qt WebSockets. Qt WebKit implements this interface internally and uses the native
- WebKit IPC mechanism to transmit messages to HTML clients.
+ The \l{Qt WebChannel Standalone Example} shows how this can be done
+ using \l{Qt WebSockets}.
\note The JSON message protocol is considered internal and might change over time.
@@ -69,7 +68,7 @@ QT_BEGIN_NAMESPACE
/*!
\fn QWebChannelAbstractTransport::sendMessage(const QJsonObject &message)
- Send a JSON \a message to the remote client. An implementation would serialize the message and
+ Sends a JSON \a message to the remote client. An implementation would serialize the message and
transmit it to the remote JavaScript client.
*/