From eee482929a81ae9d685a0ee140733227ceac6543 Mon Sep 17 00:00:00 2001 From: Pierre Rossi Date: Thu, 14 Aug 2014 02:21:56 +0800 Subject: Integrate with WebChannel Provide a transport mechanism for WebChannel over chromium IPC and expose WebChannel in our experimental QML API. Co-authored by Milian Wolff. Change-Id: Ia24b1d4ebc8515de677d4849ec33cb55c963918e Reviewed-by: Andras Becsi --- src/core/common/qt_messages.h | 4 + src/core/core_common.pri | 8 + src/core/core_gyp_generator.pro | 11 +- src/core/core_module.pro | 6 +- src/core/renderer/content_renderer_client_qt.cpp | 8 +- src/core/renderer/web_channel_ipc_transport.cpp | 175 +++++++++++++++++++++ src/core/renderer/web_channel_ipc_transport.h | 59 +++++++ src/core/web_channel_ipc_transport_host.cpp | 80 ++++++++++ src/core/web_channel_ipc_transport_host.h | 65 ++++++++ src/core/web_contents_adapter.cpp | 28 ++++ src/core/web_contents_adapter.h | 8 +- src/core/web_contents_adapter_p.h | 4 + src/webengine/api/qquickwebengineview.cpp | 18 +++ src/webengine/api/qquickwebengineview_p_p.h | 5 + sync.profile | 1 + tests/auto/quick/qmltests/data/tst_webchannel.qml | 114 ++++++++++++++ .../auto/quick/qmltests/data/webchannel-test.html | 21 +++ 17 files changed, 601 insertions(+), 14 deletions(-) create mode 100644 src/core/core_common.pri create mode 100644 src/core/renderer/web_channel_ipc_transport.cpp create mode 100644 src/core/renderer/web_channel_ipc_transport.h create mode 100644 src/core/web_channel_ipc_transport_host.cpp create mode 100644 src/core/web_channel_ipc_transport_host.h create mode 100644 tests/auto/quick/qmltests/data/tst_webchannel.qml create mode 100644 tests/auto/quick/qmltests/data/webchannel-test.html diff --git a/src/core/common/qt_messages.h b/src/core/common/qt_messages.h index 315987cd3..5ae5fef7d 100644 --- a/src/core/common/qt_messages.h +++ b/src/core/common/qt_messages.h @@ -29,6 +29,8 @@ IPC_MESSAGE_ROUTED1(QtRenderViewObserver_FetchDocumentMarkup, IPC_MESSAGE_ROUTED1(QtRenderViewObserver_FetchDocumentInnerText, uint64 /* requestId */) +IPC_MESSAGE_ROUTED1(WebChannelIPCTransport_Message, std::vector /*binaryJSON*/) + //----------------------------------------------------------------------------- // WebContents messages // These are messages sent from the renderer back to the browser process. @@ -42,3 +44,5 @@ IPC_MESSAGE_ROUTED2(QtRenderViewObserverHost_DidFetchDocumentInnerText, base::string16 /* innerText */) IPC_MESSAGE_ROUTED0(QtRenderViewObserverHost_DidFirstVisuallyNonEmptyLayout) + +IPC_MESSAGE_ROUTED1(WebChannelIPCTransportHost_SendMessage, std::vector /*binaryJSON*/) diff --git a/src/core/core_common.pri b/src/core/core_common.pri new file mode 100644 index 000000000..2e9ee4198 --- /dev/null +++ b/src/core/core_common.pri @@ -0,0 +1,8 @@ +# NOTE: The TARGET, QT, QT_PRIVATE variables are used in both core_module.pro and core_gyp_generator.pro +# gyp/ninja will take care of the compilation, qmake/make will finish with linking and install. + +TARGET = QtWebEngineCore +QT += qml quick webchannel +QT_PRIVATE += quick-private gui-private core-private + +qtHaveModule(positioning):QT += positioning diff --git a/src/core/core_gyp_generator.pro b/src/core/core_gyp_generator.pro index 2ec2816c1..3980dcc5a 100644 --- a/src/core/core_gyp_generator.pro +++ b/src/core/core_gyp_generator.pro @@ -7,11 +7,7 @@ GYPINCLUDES += qtwebengine.gypi TEMPLATE = lib -# NOTE: The TARGET, QT, QT_PRIVATE variables must match those in core_module.pro. -# gyp/ninja will take care of the compilation, qmake/make will finish with linking and install. -TARGET = QtWebEngineCore -QT += qml quick -QT_PRIVATE += quick-private gui-private core-private +include(core_common.pri) # Defining keywords such as 'signal' clashes with the chromium code base. DEFINES += QT_NO_KEYWORDS \ @@ -63,6 +59,7 @@ SOURCES = \ render_widget_host_view_qt.cpp \ renderer/content_renderer_client_qt.cpp \ renderer/qt_render_view_observer.cpp \ + renderer/web_channel_ipc_transport.cpp \ resource_bundle_qt.cpp \ resource_context_qt.cpp \ resource_dispatcher_host_delegate_qt.cpp \ @@ -70,6 +67,7 @@ SOURCES = \ surface_factory_qt.cpp \ url_request_context_getter_qt.cpp \ url_request_qrc_job_qt.cpp \ + web_channel_ipc_transport_host.cpp \ web_contents_adapter.cpp \ web_contents_delegate_qt.cpp \ web_contents_view_qt.cpp \ @@ -116,6 +114,7 @@ HEADERS = \ render_widget_host_view_qt_delegate.h \ renderer/content_renderer_client_qt.h \ renderer/qt_render_view_observer.h \ + renderer/web_channel_ipc_transport.h \ resource_context_qt.h \ resource_dispatcher_host_delegate_qt.h \ stream_video_node.h \ @@ -123,6 +122,7 @@ HEADERS = \ type_conversion.h \ url_request_context_getter_qt.h \ url_request_qrc_job_qt.h \ + web_channel_ipc_transport_host.h \ web_contents_adapter.h \ web_contents_adapter_client.h \ web_contents_adapter_p.h \ @@ -140,5 +140,4 @@ qtHaveModule(positioning) { SOURCES += location_provider_qt.cpp HEADERS += location_provider_qt.h DEFINES += QT_USE_POSITIONING=1 - QT += positioning } diff --git a/src/core/core_module.pro b/src/core/core_module.pro index e6933cc4b..afa11d31f 100644 --- a/src/core/core_module.pro +++ b/src/core/core_module.pro @@ -1,10 +1,6 @@ MODULE = webenginecore -TARGET = QtWebEngineCore - -qtHaveModule(positioning):QT += positioning -QT += qml quick -QT_PRIVATE += quick-private gui-private core-private +include(core_common.pri) # Needed to set a CFBundleIdentifier QMAKE_INFO_PLIST = Info_mac.plist diff --git a/src/core/renderer/content_renderer_client_qt.cpp b/src/core/renderer/content_renderer_client_qt.cpp index a13a7999c..f8970e7a0 100644 --- a/src/core/renderer/content_renderer_client_qt.cpp +++ b/src/core/renderer/content_renderer_client_qt.cpp @@ -50,6 +50,7 @@ #include "ui/base/webui/jstemplate_builder.h" #include "content/public/common/web_preferences.h" +#include "renderer/web_channel_ipc_transport.h" #include "renderer/qt_render_view_observer.h" #include "grit/renderer_resources.h" @@ -66,14 +67,17 @@ ContentRendererClientQt::~ContentRendererClientQt() void ContentRendererClientQt::RenderThreadStarted() { + content::RenderThread *renderThread = content::RenderThread::Get(); + renderThread->RegisterExtension(WebChannelIPCTransport::getV8Extension()); m_visitedLinkSlave.reset(new visitedlink::VisitedLinkSlave); - content::RenderThread::Get()->AddObserver(m_visitedLinkSlave.data()); + renderThread->AddObserver(m_visitedLinkSlave.data()); } void ContentRendererClientQt::RenderViewCreated(content::RenderView* render_view) { - // RenderViewObserver destroys itself with its RenderView. + // RenderViewObservers destroy themselves with their RenderView. new QtRenderViewObserver(render_view); + new WebChannelIPCTransport(render_view); } bool ContentRendererClientQt::HasErrorPage(int httpStatusCode, std::string *errorDomain) diff --git a/src/core/renderer/web_channel_ipc_transport.cpp b/src/core/renderer/web_channel_ipc_transport.cpp new file mode 100644 index 000000000..0491a6103 --- /dev/null +++ b/src/core/renderer/web_channel_ipc_transport.cpp @@ -0,0 +1,175 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "renderer/web_channel_ipc_transport.h" + +#include "common/qt_messages.h" + +#include "content/public/renderer/render_view.h" +#include "third_party/WebKit/public/web/WebLocalFrame.h" +#include "third_party/WebKit/public/web/WebView.h" +#include "v8/include/v8.h" + +#include + +static const char kWebChannelTransportExtensionName[] = "v8/WebChannelTransport"; + +static const char kWebChannelTransportApi[] = + "if (typeof(navigator) === 'undefined')" \ + " navigator = {};" \ + "if (typeof(navigator.qtWebChannelTransport) === 'undefined')" \ + " navigator.qtWebChannelTransport = {};" \ + "navigator.qtWebChannelTransport.send = function(message) {" \ + " native function NativeQtSendMessage();" \ + " NativeQtSendMessage(message);" \ + "};"; + +class WebChannelTransportExtension : public v8::Extension { +public: + static content::RenderView *GetRenderView(); + + WebChannelTransportExtension() : v8::Extension(kWebChannelTransportExtensionName, kWebChannelTransportApi) + { + } + + virtual v8::Handle GetNativeFunctionTemplate(v8::Isolate* isolate, v8::Handle name) Q_DECL_OVERRIDE; + + static void NativeQtSendMessage(const v8::FunctionCallbackInfo& args) + { + content::RenderView *renderView = GetRenderView(); + if (!renderView || args.Length() != 1) + return; + v8::Handle val = args[0]; + if (!val->IsString() && !val->IsStringObject()) + return; + v8::String::Utf8Value utf8(val->ToString()); + + QByteArray valueData(*utf8, utf8.length()); + QJsonParseError error; + QJsonDocument doc = QJsonDocument::fromJson(valueData, &error); + if (error.error != QJsonParseError::NoError) + qWarning("%s %d: Parsing error: %s",__FILE__, __LINE__, qPrintable(error.errorString())); + int size = 0; + const char *rawData = doc.rawData(&size); + renderView->Send(new WebChannelIPCTransportHost_SendMessage(renderView->GetRoutingID(), std::vector(rawData, rawData + size))); + } +}; + +content::RenderView *WebChannelTransportExtension::GetRenderView() +{ + blink::WebLocalFrame *webframe = blink::WebLocalFrame::frameForCurrentContext(); + DCHECK(webframe) << "There should be an active frame since we just got a native function called."; + if (!webframe) + return 0; + + blink::WebView *webview = webframe->view(); + if (!webview) + return 0; // can happen during closing + + return content::RenderView::FromWebView(webview); +} + +v8::Handle WebChannelTransportExtension::GetNativeFunctionTemplate(v8::Isolate *isolate, v8::Handle name) +{ + if (name->Equals(v8::String::NewFromUtf8(isolate, "NativeQtSendMessage"))) + return v8::FunctionTemplate::New(isolate, NativeQtSendMessage); + + return v8::Handle(); +} + +WebChannelIPCTransport::WebChannelIPCTransport(content::RenderView *renderView) + : content::RenderViewObserver(renderView) +{ +} + +void WebChannelIPCTransport::dispatchWebChannelMessage(const std::vector &binaryJSON) +{ + blink::WebView *webView = render_view()->GetWebView(); + if (!webView) + return; + + QJsonDocument doc = QJsonDocument::fromRawData(binaryJSON.data(), binaryJSON.size(), QJsonDocument::BypassValidation); + Q_ASSERT(doc.isObject()); + QByteArray json = doc.toJson(QJsonDocument::Compact); + + v8::Isolate *isolate = v8::Isolate::GetCurrent(); + v8::HandleScope handleScope(isolate); + blink::WebFrame *frame = webView->mainFrame(); + v8::Handle context = frame->mainWorldScriptContext(); + v8::Context::Scope contextScope(context); + + v8::Handle global(context->Global()); + v8::Handle navigatorValue(global->Get(v8::String::NewFromUtf8(isolate, "navigator"))); + if (!navigatorValue->IsObject()) + return; + v8::Handle navigatorQtValue(navigatorValue->ToObject()->Get(v8::String::NewFromUtf8(isolate, "qtWebChannelTransport"))); + if (!navigatorQtValue->IsObject()) + return; + v8::Handle onmessageCallbackValue(navigatorQtValue->ToObject()->Get(v8::String::NewFromUtf8(isolate, "onmessage"))); + if (!onmessageCallbackValue->IsFunction()) { + qWarning("onmessage is not a callable property of navigator.qtWebChannelTransport. Some things might not work as expected."); + return; + } + + v8::Handle messageObject(v8::Object::New(isolate)); + messageObject->ForceSet(v8::String::NewFromUtf8(isolate, "data") + , v8::String::NewFromUtf8(isolate, json.constData(), v8::String::kNormalString, json.size()) + , v8::PropertyAttribute(v8::ReadOnly | v8::DontDelete)); + + v8::Handle callback = v8::Handle::Cast(onmessageCallbackValue); + const int argc = 1; + v8::Handle argv[argc]; + argv[0] = messageObject; + frame->callFunctionEvenIfScriptDisabled(callback, navigatorQtValue->ToObject(), argc, argv); +} + +v8::Extension *WebChannelIPCTransport::getV8Extension() +{ + return new WebChannelTransportExtension; +} + +bool WebChannelIPCTransport::OnMessageReceived(const IPC::Message &message) +{ + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(WebChannelIPCTransport, message) + IPC_MESSAGE_HANDLER(WebChannelIPCTransport_Message, dispatchWebChannelMessage) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} diff --git a/src/core/renderer/web_channel_ipc_transport.h b/src/core/renderer/web_channel_ipc_transport.h new file mode 100644 index 000000000..29e819e95 --- /dev/null +++ b/src/core/renderer/web_channel_ipc_transport.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef NAVIGATOR_QT_EXTENSION_H +#define NAVIGATOR_QT_EXTENSION_H + +#include "base/values.h" +#include "content/public/renderer/render_view_observer.h" +#include + +namespace v8 { +class Extension; +} + +class WebChannelIPCTransport : public content::RenderViewObserver { +public: + static v8::Extension* getV8Extension(); + + WebChannelIPCTransport(content::RenderView *); + +private: + void dispatchWebChannelMessage(const std::vector &binaryJSON); + virtual bool OnMessageReceived(const IPC::Message &message) Q_DECL_OVERRIDE; +}; + +#endif // NAVIGATOR_QT_EXTENSION_H diff --git a/src/core/web_channel_ipc_transport_host.cpp b/src/core/web_channel_ipc_transport_host.cpp new file mode 100644 index 000000000..d940aeab7 --- /dev/null +++ b/src/core/web_channel_ipc_transport_host.cpp @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "web_channel_ipc_transport_host.h" + +#include "base/strings/string16.h" + +#include "common/qt_messages.h" +#include "type_conversion.h" + +#include +#include + +WebChannelIPCTransportHost::WebChannelIPCTransportHost(content::WebContents *contents, QObject *parent) + : QWebChannelAbstractTransport(parent) + , content::WebContentsObserver(contents) +{ +} + +WebChannelIPCTransportHost::~WebChannelIPCTransportHost() +{ +} + +void WebChannelIPCTransportHost::sendMessage(const QJsonObject &message) +{ + QJsonDocument doc(message); + int size = 0; + const char *rawData = doc.rawData(&size); + Send(new WebChannelIPCTransport_Message(routing_id(), std::vector(rawData, rawData + size))); +} + +void WebChannelIPCTransportHost::onWebChannelMessage(const std::vector &message) +{ + QJsonDocument doc = QJsonDocument::fromRawData(message.data(), message.size(), QJsonDocument::BypassValidation); + Q_ASSERT(doc.isObject()); + Q_EMIT messageReceived(doc.object(), this); +} + +bool WebChannelIPCTransportHost::OnMessageReceived(const IPC::Message &message) +{ + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(WebChannelIPCTransportHost, message) + IPC_MESSAGE_HANDLER(WebChannelIPCTransportHost_SendMessage, onWebChannelMessage) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} diff --git a/src/core/web_channel_ipc_transport_host.h b/src/core/web_channel_ipc_transport_host.h new file mode 100644 index 000000000..d51ebba24 --- /dev/null +++ b/src/core/web_channel_ipc_transport_host.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef WEB_CHANNEL_IPC_TRANSPORT_H +#define WEB_CHANNEL_IPC_TRANSPORT_H + + +#include +#include "content/public/browser/web_contents_observer.h" + +#include "qtwebenginecoreglobal.h" +#include + + +QT_FORWARD_DECLARE_CLASS(QString) + +class WebChannelIPCTransportHost : public QWebChannelAbstractTransport + , public content::WebContentsObserver +{ +public: + WebChannelIPCTransportHost(content::WebContents *, QObject *parent = 0); + virtual ~WebChannelIPCTransportHost(); + + // QWebChannelAbstractTransport + virtual void sendMessage(const QJsonObject &message) Q_DECL_OVERRIDE; + +private: + bool OnMessageReceived(const IPC::Message& message) Q_DECL_OVERRIDE; + void onWebChannelMessage(const std::vector &message); +}; + +#endif // WEB_CHANNEL_IPC_TRANSPORT_H diff --git a/src/core/web_contents_adapter.cpp b/src/core/web_contents_adapter.cpp index 249f9204c..306de1a45 100644 --- a/src/core/web_contents_adapter.cpp +++ b/src/core/web_contents_adapter.cpp @@ -49,6 +49,7 @@ #include "media_capture_devices_dispatcher.h" #include "qt_render_view_observer_host.h" #include "type_conversion.h" +#include "web_channel_ipc_transport_host.h" #include "web_contents_adapter_client.h" #include "web_contents_view_qt.h" #include "web_engine_context.h" @@ -77,6 +78,7 @@ #include #include #include +#include static const int kTestWindowWidth = 800; static const int kTestWindowHeight = 600; @@ -320,6 +322,8 @@ class LoadRecursionGuard { WebContentsAdapterPrivate::WebContentsAdapterPrivate() // This has to be the first thing we create, and the last we destroy. : engineContext(WebEngineContext::current()) + , webChannel(0) + , adapterClient(0) , nextRequestId(1) , lastFindRequestId(0) { @@ -830,3 +834,27 @@ content::WebContents *WebContentsAdapter::webContents() const Q_D(const WebContentsAdapter); return d->webContents.get(); } + +QWebChannel *WebContentsAdapter::webChannel() const +{ + Q_D(const WebContentsAdapter); + return d->webChannel; +} + +void WebContentsAdapter::setWebChannel(QWebChannel *channel) +{ + Q_D(WebContentsAdapter); + if (d->webChannel == channel) + return; + if (!d->webChannelTransport.get()) + d->webChannelTransport.reset(new WebChannelIPCTransportHost(d->webContents.get())); + else + d->webChannel->disconnectFrom(d->webChannelTransport.get()); + + d->webChannel = channel; + if (!channel) { + d->webChannelTransport.reset(); + return; + } + channel->connectTo(d->webChannelTransport.get()); +} diff --git a/src/core/web_contents_adapter.h b/src/core/web_contents_adapter.h index 3c4ad9970..6ba7c4b0a 100644 --- a/src/core/web_contents_adapter.h +++ b/src/core/web_contents_adapter.h @@ -50,9 +50,13 @@ class WebContents; struct WebPreferences; } class BrowserContextQt; +class MessagePassingInterface; class WebContentsAdapterPrivate; -QT_FORWARD_DECLARE_CLASS(QAccessibleInterface); +QT_BEGIN_NAMESPACE +class QAccessibleInterface; +class QWebChannel; +QT_END_NAMESPACE class QWEBENGINE_EXPORT WebContentsAdapter : public QSharedData { public: @@ -115,6 +119,8 @@ public: QAccessibleInterface *browserAccessible(); BrowserContextQt* browserContext(); BrowserContextAdapter* browserContextAdapter(); + QWebChannel *webChannel() const; + void setWebChannel(QWebChannel *); // meant to be used within WebEngineCore only content::WebContents *webContents() const; diff --git a/src/core/web_contents_adapter_p.h b/src/core/web_contents_adapter_p.h index f47c05de0..dcdf97f2e 100644 --- a/src/core/web_contents_adapter_p.h +++ b/src/core/web_contents_adapter_p.h @@ -46,9 +46,11 @@ class BrowserContextAdapter; class QtRenderViewObserverHost; +class WebChannelIPCTransportHost; class WebContentsAdapterClient; class WebContentsDelegateQt; class WebEngineContext; +QT_FORWARD_DECLARE_CLASS(QWebChannel) class WebContentsAdapterPrivate { public: @@ -59,6 +61,8 @@ public: scoped_ptr webContents; scoped_ptr webContentsDelegate; scoped_ptr renderViewObserverHost; + scoped_ptr webChannelTransport; + QWebChannel *webChannel; WebContentsAdapterClient *adapterClient; quint64 nextRequestId; int lastFindRequestId; diff --git a/src/webengine/api/qquickwebengineview.cpp b/src/webengine/api/qquickwebengineview.cpp index c6c67ff01..a47defb7c 100644 --- a/src/webengine/api/qquickwebengineview.cpp +++ b/src/webengine/api/qquickwebengineview.cpp @@ -61,6 +61,7 @@ #include #include #include +#include #include #include #include @@ -811,6 +812,23 @@ QQuickWebEngineHistory *QQuickWebEngineViewExperimental::navigationHistory() con return d_ptr->m_history.data(); } +QQmlWebChannel *QQuickWebEngineViewExperimental::webChannel() const +{ + d_ptr->ensureContentsAdapter(); + QQmlWebChannel *qmlWebChannel = qobject_cast(d_ptr->adapter->webChannel()); + Q_ASSERT(!d_ptr->adapter->webChannel() || qmlWebChannel); + if (!qmlWebChannel) { + qmlWebChannel = new QQmlWebChannel; + d_ptr->adapter->setWebChannel(qmlWebChannel); + } + return qmlWebChannel; +} + +void QQuickWebEngineViewExperimental::setWebChannel(QQmlWebChannel *webChannel) +{ + d_ptr->adapter->setWebChannel(webChannel); +} + void QQuickWebEngineViewExperimental::grantFeaturePermission(const QUrl &securityOrigin, QQuickWebEngineViewExperimental::Feature feature, bool granted) { if (!d_ptr->adapter) diff --git a/src/webengine/api/qquickwebengineview_p_p.h b/src/webengine/api/qquickwebengineview_p_p.h index 48eb25cb3..d7785092f 100644 --- a/src/webengine/api/qquickwebengineview_p_p.h +++ b/src/webengine/api/qquickwebengineview_p_p.h @@ -55,6 +55,7 @@ class QQuickWebEngineView; class QQmlComponent; class QQmlContext; class QQuickWebEngineSettings; +class QQmlWebChannel; class Q_WEBENGINE_PRIVATE_EXPORT QQuickWebEngineViewport : public QObject { Q_OBJECT @@ -80,6 +81,7 @@ class Q_WEBENGINE_PRIVATE_EXPORT QQuickWebEngineViewExperimental : public QObjec Q_PROPERTY(QQmlComponent *extraContextMenuEntriesComponent READ extraContextMenuEntriesComponent WRITE setExtraContextMenuEntriesComponent NOTIFY extraContextMenuEntriesComponentChanged) Q_PROPERTY(bool isFullScreen READ isFullScreen WRITE setIsFullScreen NOTIFY isFullScreenChanged) Q_PROPERTY(QQuickWebEngineHistory *navigationHistory READ navigationHistory CONSTANT FINAL) + Q_PROPERTY(QQmlWebChannel *webChannel READ webChannel WRITE setWebChannel) Q_ENUMS(Feature) Q_FLAGS(FindFlags) @@ -103,6 +105,9 @@ public: void setExtraContextMenuEntriesComponent(QQmlComponent *); QQmlComponent *extraContextMenuEntriesComponent() const; QQuickWebEngineHistory *navigationHistory() const; + QQuickWebEngineSettings *settings() const; + QQmlWebChannel *webChannel() const; + void setWebChannel(QQmlWebChannel *); public Q_SLOTS: void goBackTo(int index); diff --git a/sync.profile b/sync.profile index 1e9519fe3..774941d56 100644 --- a/sync.profile +++ b/sync.profile @@ -21,4 +21,5 @@ "qtxmlpatterns" => "", # FIXME: take examples out into their own module to avoid a potential circular dependency later ? "qtquickcontrols" => "", + "qtwebchannel" => "", ); diff --git a/tests/auto/quick/qmltests/data/tst_webchannel.qml b/tests/auto/quick/qmltests/data/tst_webchannel.qml new file mode 100644 index 000000000..1abb191e1 --- /dev/null +++ b/tests/auto/quick/qmltests/data/tst_webchannel.qml @@ -0,0 +1,114 @@ +/********************************************************************* +** Copyright (C) 2014 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtWebChannel module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 +import QtWebEngine 1.0 +import QtWebEngine.experimental 1.0 + +import QtWebChannel 1.0 + +Item { + id: test + signal barCalled(var arg) + signal clientInitializedCalled(var arg) + + QtObject { + id: testObject + WebChannel.id: "testObject" + + property var foo: 42 + + function clientInitialized(arg) + { + clientInitializedCalled(arg); + } + + function bar(arg) { + barCalled(arg); + } + + signal runTest(var foo) + } + + TestWebEngineView { + id: webView + experimental.webChannel.registeredObjects: [testObject] + } + + SignalSpy { + id: initializedSpy + target: test + signalName: "clientInitializedCalled" + } + + SignalSpy { + id: barSpy + target: test + signalName: "barCalled" + } + + TestCase { + name: "WebViewWebChannel" + property url testUrl: Qt.resolvedUrl("./webchannel-test.html") + + function init() { + initializedSpy.clear(); + barSpy.clear(); + } + + function test_basic() { + webView.url = testUrl; + verify(webView.waitForLoadSucceeded()); + + initializedSpy.wait(); + compare(initializedSpy.signalArguments.length, 1); + compare(initializedSpy.signalArguments[0][0], 42); + + var newValue = "roundtrip"; + testObject.runTest(newValue); + barSpy.wait(); + compare(barSpy.signalArguments.length, 1); + compare(barSpy.signalArguments[0][0], newValue); + + compare(testObject.foo, newValue); + } + } +} diff --git a/tests/auto/quick/qmltests/data/webchannel-test.html b/tests/auto/quick/qmltests/data/webchannel-test.html new file mode 100644 index 000000000..940821209 --- /dev/null +++ b/tests/auto/quick/qmltests/data/webchannel-test.html @@ -0,0 +1,21 @@ + + + + + + + + + + -- cgit v1.2.3