[WPE] Add Qt extension
authorphiln@webkit.org <philn@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 18 Jan 2019 13:07:38 +0000 (13:07 +0000)
committerphiln@webkit.org <philn@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 18 Jan 2019 13:07:38 +0000 (13:07 +0000)
https://bugs.webkit.org/show_bug.cgi?id=191464

Reviewed by Carlos Garcia Campos.

.:

* Source/cmake/OptionsWPE.cmake: Add ENABLE_WPE_QT_API CMake option. Disabled by default.

Source/WebKit:

This new extension is a QML plugin embedding a WPE ViewBackend
implementation. It provides a public API very similar to Qt's
WebView module. It comes with a simple mini-browser implemented in
QML.

QtWPE is known to work with the Wayland-EGL (in GNOME and Weston
compositors) and EGLFS QPAs.

* PlatformWPE.cmake:
* UIProcess/API/wpe/qt/WPEQmlExtensionPlugin.cpp: Added.
(WPEQmlExtensionPlugin::registerTypes):
* UIProcess/API/wpe/qt/WPEQmlExtensionPlugin.h: Added.
* UIProcess/API/wpe/qt/WPEQtView.cpp: Added.
(WPEQtView::WPEQtView):
(WPEQtView::~WPEQtView):
(WPEQtView::geometryChanged):
(WPEQtView::configureWindow):
(WPEQtView::createOffscreen):
(WPEQtView::backendCreated):
(WPEQtView::notifyUrlChangedCallback):
(WPEQtView::notifyTitleChangedCallback):
(WPEQtView::notifyLoadProgressCallback):
(WPEQtView::notifyLoadChangedCallback):
(WPEQtView::notifyLoadFailedCallback):
(WPEQtView::updatePaintNode):
(WPEQtView::url const):
(WPEQtView::setUrl):
(WPEQtView::loadProgress const):
(WPEQtView::title const):
(WPEQtView::canGoBack const):
(WPEQtView::isLoading const):
(WPEQtView::canGoForward const):
(WPEQtView::goBack):
(WPEQtView::goForward):
(WPEQtView::reload):
(WPEQtView::stop):
(WPEQtView::loadHtml):
(WPEQtView::jsAsyncReadyCallback):
(WPEQtView::handleJsResult):
(WPEQtView::runJavaScript):
(WPEQtView::mousePressEvent):
(WPEQtView::mouseReleaseEvent):
(WPEQtView::hoverEnterEvent):
(WPEQtView::hoverLeaveEvent):
(WPEQtView::hoverMoveEvent):
(WPEQtView::wheelEvent):
(WPEQtView::keyPressEvent):
(WPEQtView::keyReleaseEvent):
(WPEQtView::touchEvent):
* UIProcess/API/wpe/qt/WPEQtView.h: Added.
* UIProcess/API/wpe/qt/WPEQtViewBackend.cpp: Added.
(configureCallback):
(WPEQtViewBackend::WPEQtViewBackend):
(WPEQtViewBackend::~WPEQtViewBackend):
(WPEQtViewBackend::configureGlibEglDisplay):
(WPEQtViewBackend::configureEglDisplay):
(WPEQtViewBackend::initialize):
(WPEQtViewBackend::backend const):
(WPEQtViewBackend::resize):
(WPEQtViewBackend::getTexture):
(WPEQtViewBackend::initSurface):
(WPEQtViewBackend::displayImage):
(WPEQtViewBackend::modifiers const):
(WPEQtViewBackend::dispatchHoverEnterEvent):
(WPEQtViewBackend::dispatchHoverLeaveEvent):
(WPEQtViewBackend::dispatchHoverMoveEvent):
(WPEQtViewBackend::dispatchMousePressEvent):
(WPEQtViewBackend::dispatchMouseReleaseEvent):
(WPEQtViewBackend::dispatchWheelEvent):
(WPEQtViewBackend::dispatchKeyEvent):
(WPEQtViewBackend::dispatchTouchEvent):
* UIProcess/API/wpe/qt/WPEQtViewBackend.h: Added.
* UIProcess/API/wpe/qt/WPEQtViewLoadRequest.cpp: Added.
(WPEQtViewLoadRequest::WPEQtViewLoadRequest):
(WPEQtViewLoadRequest::~WPEQtViewLoadRequest):
(WPEQtViewLoadRequest::url const):
(WPEQtViewLoadRequest::status const):
(WPEQtViewLoadRequest::errorString const):
* UIProcess/API/wpe/qt/WPEQtViewLoadRequest.h: Added.
* UIProcess/API/wpe/qt/WPEQtViewLoadRequestPrivate.h: Added.
(WPEQtViewLoadRequestPrivate::WPEQtViewLoadRequestPrivate):
(WPEQtViewLoadRequestPrivate::~WPEQtViewLoadRequestPrivate):
* UIProcess/API/wpe/qt/qmldir: Added.

Tools:

MiniBrowser and API tests for the WPE Qt API. To run the
MiniBrowser a new script is introduced. Example invocation:

$ run-qt-wpe-minibrowser -platform wayland https://webkit.org

Also note-worthy is the introduction of the python2-subprocess32
dependency to run the GLib API tests.

* MiniBrowser/wpe/CMakeLists.txt:
* MiniBrowser/wpe/qt/CMakeLists.txt: Added.
* MiniBrowser/wpe/qt/main.cpp: Added.
(main):
* MiniBrowser/wpe/qt/main.qml: Added.
* MiniBrowser/wpe/qt/qml.qrc: Added.
* Scripts/run-gtk-tests:
(GtkTestRunner.is_google_test):
(GtkTestRunner):
(GtkTestRunner.is_qt_test):
* Scripts/run-qt-wpe-minibrowser: Added.
* Scripts/run-wpe-tests:
(WPETestRunner):
(WPETestRunner.is_google_test):
(WPETestRunner.is_qt_test):
* TestWebKitAPI/Tests/WPEQt/TestLoad.cpp: Added.
(TestLoad::main):
* TestWebKitAPI/Tests/WPEQt/TestLoadHtml.cpp: Added.
(TestLoadHtml::main):
* TestWebKitAPI/Tests/WPEQt/TestLoadRequest.cpp: Added.
(TestLoadRequest::main):
* TestWebKitAPI/Tests/WPEQt/TestRunJavaScript.cpp: Added.
(TestRunJavaScript::main):
* TestWebKitAPI/Tests/WPEQt/WPEQtTest.cpp: Added.
(waitForSignal):
* TestWebKitAPI/Tests/WPEQt/WPEQtTest.h: Added.
(LoadSpy::LoadSpy):
(LoadSpy::~LoadSpy):
(LoadSpy::onLoadingChanged):
(LoadStartedCatcher::LoadStartedCatcher):
(LoadStartedCatcher::~LoadStartedCatcher):
(LoadStartedCatcher::onLoadingChanged):
(waitForLoadSucceeded):
(waitForLoadFailed):
* TestWebKitAPI/glib/CMakeLists.txt:
* flatpak/flatpakutils.py:
(WebkitFlatpak.load_from_args):
(WebkitFlatpak.__init__):
(WebkitFlatpak.clean_args):
(WebkitFlatpak.run_in_sandbox):
* flatpak/org.webkit.CommonModules.yaml:
* flatpak/org.webkit.WPE.yaml:
* flatpak/org.webkit.WPEModules.yaml:
* flatpak/org.webkit.WPEQT.yaml: Copied from Tools/flatpak/org.webkit.WebKit.yaml.
* flatpak/org.webkit.WebKit.yaml:
* glib/api_test_runner.py:
(TestRunner._run_test_qt):
(TestRunner.is_qt_test):
(TestRunner._run_test):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@240141 268f45cc-cd09-0410-ab3c-d52691b4dbfc

37 files changed:
ChangeLog
Source/WebKit/ChangeLog
Source/WebKit/PlatformWPE.cmake
Source/WebKit/UIProcess/API/wpe/qt/WPEQmlExtensionPlugin.cpp [new file with mode: 0644]
Source/WebKit/UIProcess/API/wpe/qt/WPEQmlExtensionPlugin.h [new file with mode: 0644]
Source/WebKit/UIProcess/API/wpe/qt/WPEQtView.cpp [new file with mode: 0644]
Source/WebKit/UIProcess/API/wpe/qt/WPEQtView.h [new file with mode: 0644]
Source/WebKit/UIProcess/API/wpe/qt/WPEQtViewBackend.cpp [new file with mode: 0644]
Source/WebKit/UIProcess/API/wpe/qt/WPEQtViewBackend.h [new file with mode: 0644]
Source/WebKit/UIProcess/API/wpe/qt/WPEQtViewLoadRequest.cpp [new file with mode: 0644]
Source/WebKit/UIProcess/API/wpe/qt/WPEQtViewLoadRequest.h [new file with mode: 0644]
Source/WebKit/UIProcess/API/wpe/qt/WPEQtViewLoadRequestPrivate.h [new file with mode: 0644]
Source/WebKit/UIProcess/API/wpe/qt/qmldir [new file with mode: 0644]
Source/cmake/OptionsWPE.cmake
Tools/ChangeLog
Tools/MiniBrowser/wpe/CMakeLists.txt
Tools/MiniBrowser/wpe/qt/CMakeLists.txt [new file with mode: 0644]
Tools/MiniBrowser/wpe/qt/main.cpp [new file with mode: 0644]
Tools/MiniBrowser/wpe/qt/main.qml [new file with mode: 0644]
Tools/MiniBrowser/wpe/qt/qml.qrc [new file with mode: 0644]
Tools/Scripts/run-gtk-tests
Tools/Scripts/run-qt-wpe-minibrowser [new file with mode: 0755]
Tools/Scripts/run-wpe-tests
Tools/TestWebKitAPI/Tests/WPEQt/TestLoad.cpp [new file with mode: 0644]
Tools/TestWebKitAPI/Tests/WPEQt/TestLoadHtml.cpp [new file with mode: 0644]
Tools/TestWebKitAPI/Tests/WPEQt/TestLoadRequest.cpp [new file with mode: 0644]
Tools/TestWebKitAPI/Tests/WPEQt/TestRunJavaScript.cpp [new file with mode: 0644]
Tools/TestWebKitAPI/Tests/WPEQt/WPEQtTest.cpp [new file with mode: 0644]
Tools/TestWebKitAPI/Tests/WPEQt/WPEQtTest.h [new file with mode: 0644]
Tools/TestWebKitAPI/glib/CMakeLists.txt
Tools/flatpak/flatpakutils.py
Tools/flatpak/org.webkit.CommonModules.yaml
Tools/flatpak/org.webkit.WPE.yaml
Tools/flatpak/org.webkit.WPEModules.yaml
Tools/flatpak/org.webkit.WPEQT.yaml [new file with mode: 0644]
Tools/flatpak/org.webkit.WebKit.yaml
Tools/glib/api_test_runner.py

index 1d4ce89..9af449d 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2019-01-18  Philippe Normand  <pnormand@igalia.com>
+
+        [WPE] Add Qt extension
+        https://bugs.webkit.org/show_bug.cgi?id=191464
+
+        Reviewed by Carlos Garcia Campos.
+
+        * Source/cmake/OptionsWPE.cmake: Add ENABLE_WPE_QT_API CMake option. Disabled by default.
+
 2019-01-17  Truitt Savell  <tsavell@apple.com>
 
         Unreviewed, rolling out r240124.
index 1f3e471..927032f 100644 (file)
@@ -1,3 +1,94 @@
+2019-01-18  Philippe Normand  <pnormand@igalia.com>
+
+        [WPE] Add Qt extension
+        https://bugs.webkit.org/show_bug.cgi?id=191464
+
+        Reviewed by Carlos Garcia Campos.
+
+        This new extension is a QML plugin embedding a WPE ViewBackend
+        implementation. It provides a public API very similar to Qt's
+        WebView module. It comes with a simple mini-browser implemented in
+        QML.
+
+        QtWPE is known to work with the Wayland-EGL (in GNOME and Weston
+        compositors) and EGLFS QPAs.
+
+        * PlatformWPE.cmake:
+        * UIProcess/API/wpe/qt/WPEQmlExtensionPlugin.cpp: Added.
+        (WPEQmlExtensionPlugin::registerTypes):
+        * UIProcess/API/wpe/qt/WPEQmlExtensionPlugin.h: Added.
+        * UIProcess/API/wpe/qt/WPEQtView.cpp: Added.
+        (WPEQtView::WPEQtView):
+        (WPEQtView::~WPEQtView):
+        (WPEQtView::geometryChanged):
+        (WPEQtView::configureWindow):
+        (WPEQtView::createOffscreen):
+        (WPEQtView::backendCreated):
+        (WPEQtView::notifyUrlChangedCallback):
+        (WPEQtView::notifyTitleChangedCallback):
+        (WPEQtView::notifyLoadProgressCallback):
+        (WPEQtView::notifyLoadChangedCallback):
+        (WPEQtView::notifyLoadFailedCallback):
+        (WPEQtView::updatePaintNode):
+        (WPEQtView::url const):
+        (WPEQtView::setUrl):
+        (WPEQtView::loadProgress const):
+        (WPEQtView::title const):
+        (WPEQtView::canGoBack const):
+        (WPEQtView::isLoading const):
+        (WPEQtView::canGoForward const):
+        (WPEQtView::goBack):
+        (WPEQtView::goForward):
+        (WPEQtView::reload):
+        (WPEQtView::stop):
+        (WPEQtView::loadHtml):
+        (WPEQtView::jsAsyncReadyCallback):
+        (WPEQtView::handleJsResult):
+        (WPEQtView::runJavaScript):
+        (WPEQtView::mousePressEvent):
+        (WPEQtView::mouseReleaseEvent):
+        (WPEQtView::hoverEnterEvent):
+        (WPEQtView::hoverLeaveEvent):
+        (WPEQtView::hoverMoveEvent):
+        (WPEQtView::wheelEvent):
+        (WPEQtView::keyPressEvent):
+        (WPEQtView::keyReleaseEvent):
+        (WPEQtView::touchEvent):
+        * UIProcess/API/wpe/qt/WPEQtView.h: Added.
+        * UIProcess/API/wpe/qt/WPEQtViewBackend.cpp: Added.
+        (configureCallback):
+        (WPEQtViewBackend::WPEQtViewBackend):
+        (WPEQtViewBackend::~WPEQtViewBackend):
+        (WPEQtViewBackend::configureGlibEglDisplay):
+        (WPEQtViewBackend::configureEglDisplay):
+        (WPEQtViewBackend::initialize):
+        (WPEQtViewBackend::backend const):
+        (WPEQtViewBackend::resize):
+        (WPEQtViewBackend::getTexture):
+        (WPEQtViewBackend::initSurface):
+        (WPEQtViewBackend::displayImage):
+        (WPEQtViewBackend::modifiers const):
+        (WPEQtViewBackend::dispatchHoverEnterEvent):
+        (WPEQtViewBackend::dispatchHoverLeaveEvent):
+        (WPEQtViewBackend::dispatchHoverMoveEvent):
+        (WPEQtViewBackend::dispatchMousePressEvent):
+        (WPEQtViewBackend::dispatchMouseReleaseEvent):
+        (WPEQtViewBackend::dispatchWheelEvent):
+        (WPEQtViewBackend::dispatchKeyEvent):
+        (WPEQtViewBackend::dispatchTouchEvent):
+        * UIProcess/API/wpe/qt/WPEQtViewBackend.h: Added.
+        * UIProcess/API/wpe/qt/WPEQtViewLoadRequest.cpp: Added.
+        (WPEQtViewLoadRequest::WPEQtViewLoadRequest):
+        (WPEQtViewLoadRequest::~WPEQtViewLoadRequest):
+        (WPEQtViewLoadRequest::url const):
+        (WPEQtViewLoadRequest::status const):
+        (WPEQtViewLoadRequest::errorString const):
+        * UIProcess/API/wpe/qt/WPEQtViewLoadRequest.h: Added.
+        * UIProcess/API/wpe/qt/WPEQtViewLoadRequestPrivate.h: Added.
+        (WPEQtViewLoadRequestPrivate::WPEQtViewLoadRequestPrivate):
+        (WPEQtViewLoadRequestPrivate::~WPEQtViewLoadRequestPrivate):
+        * UIProcess/API/wpe/qt/qmldir: Added.
+
 2019-01-17  Wenson Hsieh  <wenson_hsieh@apple.com>
 
         [iOS] Content offset jumps erratically when autoscrolling near scroll view content inset areas
index 1f675c4..3c511ed 100644 (file)
@@ -1,4 +1,5 @@
 include(InspectorGResources.cmake)
+include(GNUInstallDirs)
 
 set(WebKit_OUTPUT_NAME WPEWebKit-${WPE_API_VERSION})
 set(WebKit_WebProcess_OUTPUT_NAME WPEWebProcess)
@@ -316,22 +317,6 @@ target_link_libraries(WPEInjectedBundle WebKit)
 target_include_directories(WPEInjectedBundle PRIVATE ${WebKit_INCLUDE_DIRECTORIES})
 target_include_directories(WPEInjectedBundle SYSTEM PRIVATE ${WebKit_SYSTEM_INCLUDE_DIRECTORIES})
 
-install(TARGETS WPEInjectedBundle
-        DESTINATION "${LIB_INSTALL_DIR}/wpe-webkit-${WPE_API_VERSION}/injected-bundle"
-)
-
-install(FILES "${CMAKE_BINARY_DIR}/wpe-webkit-${WPE_API_VERSION}.pc"
-              "${CMAKE_BINARY_DIR}/wpe-web-extension-${WPE_API_VERSION}.pc"
-        DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig"
-        COMPONENT "Development"
-)
-
-install(FILES ${WPE_API_INSTALLED_HEADERS}
-              ${WPE_WEB_EXTENSION_API_INSTALLED_HEADERS}
-        DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/wpe-webkit-${WPE_API_VERSION}/wpe"
-        COMPONENT "Development"
-)
-
 file(WRITE ${CMAKE_BINARY_DIR}/gtkdoc-wpe.cfg
     "[wpe-${WPE_API_VERSION}]\n"
     "pkgconfig_file=${WPE_PKGCONFIG_FILE}\n"
@@ -370,3 +355,67 @@ file(WRITE ${CMAKE_BINARY_DIR}/gtkdoc-webextensions.cfg
     "headers=${WPE_WEB_EXTENSION_API_INSTALLED_HEADERS}\n"
     "main_sgml_file=wpe-webextensions-docs.sgml\n"
 )
+
+if (ENABLE_WPE_QT_API)
+    set(qtwpe_SOURCES
+        ${WEBKIT_DIR}/UIProcess/API/wpe/qt/WPEQtViewBackend.cpp
+        ${WEBKIT_DIR}/UIProcess/API/wpe/qt/WPEQmlExtensionPlugin.cpp
+        ${WEBKIT_DIR}/UIProcess/API/wpe/qt/WPEQtView.cpp
+        ${WEBKIT_DIR}/UIProcess/API/wpe/qt/WPEQtViewLoadRequest.cpp
+    )
+
+    set(qtwpe_LIBRARIES
+        Qt5::Core Qt5::Quick
+        WebKit
+        ${LIBEPOXY_LIBRARIES}
+        ${WPE_BACKEND_FDO_LIBRARIES}
+    )
+
+    set(qtwpe_INCLUDE_DIRECTORIES
+        ${Qt5_INCLUDE_DIRS}
+        ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
+        ${LIBEPOXY_INCLUDE_DIRS}
+        ${WPE_BACKEND_FDO_INCLUDE_DIRS}
+    )
+
+    list(APPEND WPE_API_INSTALLED_HEADERS
+        ${WEBKIT_DIR}/UIProcess/API/wpe/qt/WPEQtView.h
+        ${WEBKIT_DIR}/UIProcess/API/wpe/qt/WPEQtViewLoadRequest.h
+    )
+
+    add_library(qtwpe SHARED ${qtwpe_SOURCES})
+    set_target_properties(qtwpe PROPERTIES
+        OUTPUT_NAME qtwpe
+        AUTOMOC ON
+        CXX_STANDARD 14
+    )
+    target_compile_definitions(qtwpe PUBLIC QT_NO_KEYWORDS=1)
+    target_link_libraries(qtwpe ${qtwpe_LIBRARIES})
+    target_include_directories(qtwpe SYSTEM PRIVATE ${qtwpe_INCLUDE_DIRECTORIES})
+    install(TARGETS qtwpe DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}/qml/org/wpewebkit/qtwpe/")
+    install(FILES ${WEBKIT_DIR}/UIProcess/API/wpe/qt/qmldir DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}/qml/org/wpewebkit/qtwpe/")
+
+    file(MAKE_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/qml/org/wpewebkit/qtwpe)
+    add_custom_command(TARGET qtwpe POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy
+        ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libqtwpe.so
+        ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/qml/org/wpewebkit/qtwpe)
+    add_custom_command(TARGET qtwpe POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy
+        ${WEBKIT_DIR}/UIProcess/API/wpe/qt/qmldir
+        ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/qml/org/wpewebkit/qtwpe)
+endif ()
+
+install(TARGETS WPEInjectedBundle
+        DESTINATION "${LIB_INSTALL_DIR}/wpe-webkit-${WPE_API_VERSION}/injected-bundle"
+)
+
+install(FILES "${CMAKE_BINARY_DIR}/wpe-webkit-${WPE_API_VERSION}.pc"
+              "${CMAKE_BINARY_DIR}/wpe-web-extension-${WPE_API_VERSION}.pc"
+        DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig"
+        COMPONENT "Development"
+)
+
+install(FILES ${WPE_API_INSTALLED_HEADERS}
+              ${WPE_WEB_EXTENSION_API_INSTALLED_HEADERS}
+        DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/wpe-webkit-${WPE_API_VERSION}/wpe"
+        COMPONENT "Development"
+)
diff --git a/Source/WebKit/UIProcess/API/wpe/qt/WPEQmlExtensionPlugin.cpp b/Source/WebKit/UIProcess/API/wpe/qt/WPEQmlExtensionPlugin.cpp
new file mode 100644 (file)
index 0000000..79dbfa5
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2018, 2019 Igalia S.L
+ * Copyright (C) 2018, 2019 Zodiac Inflight Innovations
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "WPEQmlExtensionPlugin.h"
+
+#include "WPEQtView.h"
+#include "WPEQtViewLoadRequest.h"
+#include <qqml.h>
+
+void WPEQmlExtensionPlugin::registerTypes(const char* uri)
+{
+    // @uri org.wpewebkit.qtwpe
+    qmlRegisterType<WPEQtView>(uri, 1, 0, "WPEView");
+
+    const QString& msg = QObject::tr("Cannot create separate instance of WPEQtViewLoadRequest");
+    qmlRegisterUncreatableType<WPEQtViewLoadRequest>(uri, 1, 0, "WPEViewLoadRequest", msg);
+}
diff --git a/Source/WebKit/UIProcess/API/wpe/qt/WPEQmlExtensionPlugin.h b/Source/WebKit/UIProcess/API/wpe/qt/WPEQmlExtensionPlugin.h
new file mode 100644 (file)
index 0000000..5fd6fd6
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2018, 2019 Igalia S.L
+ * Copyright (C) 2018, 2019 Zodiac Inflight Innovations
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#include <QQmlExtensionPlugin>
+
+class WPEQmlExtensionPlugin : public QQmlExtensionPlugin {
+    Q_OBJECT
+    Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
+
+public:
+    void registerTypes(const char* uri);
+};
diff --git a/Source/WebKit/UIProcess/API/wpe/qt/WPEQtView.cpp b/Source/WebKit/UIProcess/API/wpe/qt/WPEQtView.cpp
new file mode 100644 (file)
index 0000000..e1dc857
--- /dev/null
@@ -0,0 +1,499 @@
+/*
+ * Copyright (C) 2018, 2019 Igalia S.L
+ * Copyright (C) 2018, 2019 Zodiac Inflight Innovations
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "WPEQtView.h"
+
+#include "WPEQtViewLoadRequest.h"
+#include "WPEQtViewLoadRequestPrivate.h"
+#include <QGuiApplication>
+#include <QQuickWindow>
+#include <QSGSimpleTextureNode>
+#include <QScreen>
+#include <QtPlatformHeaders/QEGLNativeContext>
+#include <qpa/qplatformnativeinterface.h>
+#include <wtf/glib/GUniquePtr.h>
+
+/*!
+  \qmltype WPEView
+  \inqmlmodule org.wpewebkit.qtwpe
+  \brief A component for displaying web content.
+
+  WPEView is a component for displaying web content which is implemented using native
+  APIs on the platforms where this is available, thus it does not necessarily require
+  including a full web browser stack as part of the application.
+
+  WPEView provides an API compatible with Qt's QtWebView component. However
+  WPEView is limited to Linux platforms supporting EGL KHR extensions. WPEView
+  was successfully tested with the EGLFS and Wayland-EGL QPAs.
+*/
+WPEQtView::WPEQtView(QQuickItem* parent)
+    : QQuickItem(parent)
+{
+    connect(this, &QQuickItem::windowChanged, this, &WPEQtView::configureWindow);
+    setFlag(ItemHasContents, true);
+    setAcceptedMouseButtons(Qt::AllButtons);
+    setAcceptHoverEvents(true);
+    setAcceptTouchEvents(true);
+}
+
+WPEQtView::~WPEQtView()
+{
+    g_signal_handlers_disconnect_by_func(m_webView.get(), reinterpret_cast<gpointer>(notifyUrlChangedCallback), this);
+    g_signal_handlers_disconnect_by_func(m_webView.get(), reinterpret_cast<gpointer>(notifyTitleChangedCallback), this);
+    g_signal_handlers_disconnect_by_func(m_webView.get(), reinterpret_cast<gpointer>(notifyLoadChangedCallback), this);
+    g_signal_handlers_disconnect_by_func(m_webView.get(), reinterpret_cast<gpointer>(notifyLoadFailedCallback), this);
+    g_signal_handlers_disconnect_by_func(m_webView.get(), reinterpret_cast<gpointer>(notifyLoadProgressCallback), this);
+}
+
+void WPEQtView::geometryChanged(const QRectF& newGeometry, const QRectF&)
+{
+    m_size = newGeometry.size();
+    if (m_backend)
+        m_backend->resize(newGeometry.size());
+}
+
+void WPEQtView::configureWindow()
+{
+    auto* win = window();
+    if (!win)
+        return;
+
+    win->setSurfaceType(QWindow::OpenGLSurface);
+    connect(win, &QQuickWindow::sceneGraphInitialized, this, &WPEQtView::createWebView);
+}
+
+void WPEQtView::createWebView()
+{
+    if (m_backend)
+        return;
+
+    auto display = static_cast<EGLDisplay>(QGuiApplication::platformNativeInterface()->nativeResourceForIntegration("egldisplay"));
+    auto* context = window()->openglContext();
+    std::unique_ptr<WPEQtViewBackend> backend = WPEQtViewBackend::create(m_size, context, display, QPointer<WPEQtView>(this));
+    RELEASE_ASSERT_WITH_MESSAGE(backend, "EGL initialization failed");
+    if (!backend)
+        return;
+
+    m_backend = backend.get();
+    auto settings = adoptGRef(webkit_settings_new_with_settings("enable-developer-extras", TRUE,
+        "enable-webgl", TRUE, "enable-mediasource", TRUE, nullptr));
+    m_webView = adoptGRef(WEBKIT_WEB_VIEW(g_object_new(WEBKIT_TYPE_WEB_VIEW,
+        "backend", webkit_web_view_backend_new(m_backend->backend(), [](gpointer data) {
+            delete static_cast<WPEQtViewBackend*>(data);
+        }, backend.release()),
+        "settings", settings.get(), nullptr)));
+
+    g_signal_connect_swapped(m_webView.get(), "notify::uri", G_CALLBACK(notifyUrlChangedCallback), this);
+    g_signal_connect_swapped(m_webView.get(), "notify::title", G_CALLBACK(notifyTitleChangedCallback), this);
+    g_signal_connect_swapped(m_webView.get(), "notify::estimated-load-progress", G_CALLBACK(notifyLoadProgressCallback), this);
+    g_signal_connect(m_webView.get(), "load-changed", G_CALLBACK(notifyLoadChangedCallback), this);
+    g_signal_connect(m_webView.get(), "load-failed", G_CALLBACK(notifyLoadFailedCallback), this);
+
+    if (!m_url.isEmpty())
+        webkit_web_view_load_uri(m_webView.get(), m_url.toString().toUtf8().constData());
+    else if (!m_html.isEmpty())
+        webkit_web_view_load_html(m_webView.get(), m_html.toUtf8().constData(), m_baseUrl.toString().toUtf8().constData());
+
+    Q_EMIT webViewCreated();
+}
+
+void WPEQtView::notifyUrlChangedCallback(WPEQtView* view)
+{
+    Q_EMIT view->urlChanged();
+}
+
+void WPEQtView::notifyTitleChangedCallback(WPEQtView* view)
+{
+    Q_EMIT view->titleChanged();
+}
+
+void WPEQtView::notifyLoadProgressCallback(WPEQtView* view)
+{
+    Q_EMIT view->loadProgressChanged();
+}
+
+void WPEQtView::notifyLoadChangedCallback(WebKitWebView*, WebKitLoadEvent event, WPEQtView* view)
+{
+    bool statusSet = false;
+    WPEQtView::LoadStatus loadStatus;
+    switch (event) {
+    case WEBKIT_LOAD_STARTED:
+        loadStatus = WPEQtView::LoadStatus::LoadStartedStatus;
+        statusSet = true;
+        break;
+    case WEBKIT_LOAD_FINISHED:
+        loadStatus = WPEQtView::LoadStatus::LoadSucceededStatus;
+        statusSet = !view->errorOccured();
+        view->setErrorOccured(false);
+        break;
+    default:
+        break;
+    }
+
+    if (statusSet) {
+        WPEQtViewLoadRequestPrivate loadRequestPrivate(view->url(), loadStatus, "");
+        std::unique_ptr<WPEQtViewLoadRequest> loadRequest = std::make_unique<WPEQtViewLoadRequest>(loadRequestPrivate);
+        Q_EMIT view->loadingChanged(loadRequest.get());
+    }
+}
+
+void WPEQtView::notifyLoadFailedCallback(WebKitWebView*, WebKitLoadEvent, const gchar* failingURI, GError* error, WPEQtView* view)
+{
+    view->setErrorOccured(true);
+
+    WPEQtView::LoadStatus loadStatus;
+    if (g_error_matches(error, WEBKIT_NETWORK_ERROR, WEBKIT_NETWORK_ERROR_CANCELLED))
+        loadStatus = WPEQtView::LoadStatus::LoadStoppedStatus;
+    else
+        loadStatus = WPEQtView::LoadStatus::LoadFailedStatus;
+
+    WPEQtViewLoadRequestPrivate loadRequestPrivate(QUrl(QString(failingURI)), loadStatus, error->message);
+    std::unique_ptr<WPEQtViewLoadRequest> loadRequest = std::make_unique<WPEQtViewLoadRequest>(loadRequestPrivate);
+    Q_EMIT view->loadingChanged(loadRequest.get());
+}
+
+QSGNode* WPEQtView::updatePaintNode(QSGNode* node, UpdatePaintNodeData*)
+{
+    if (!m_webView || !m_backend)
+        return node;
+
+    auto* textureNode = static_cast<QSGSimpleTextureNode*>(node);
+    if (!textureNode)
+        textureNode = new QSGSimpleTextureNode();
+
+    GLuint textureId = m_backend->texture(window()->openglContext());
+    if (!textureId)
+        return node;
+
+    textureNode->setTexture(window()->createTextureFromId(textureId, m_size.toSize(), QQuickWindow::TextureHasAlphaChannel));
+    textureNode->setRect(boundingRect());
+    return textureNode;
+}
+
+QUrl WPEQtView::url() const
+{
+    if (!m_webView)
+        return m_url;
+
+    const gchar* uri = webkit_web_view_get_uri(m_webView.get());
+    return uri ? QUrl(QString(uri)) : m_url;
+}
+
+/*!
+  \qmlproperty url WPEView::url
+
+  The URL of currently loaded web page. Changing this will trigger
+  loading new content.
+
+  The URL is used as-is. URLs that originate from user input should
+  be parsed with QUrl::fromUserInput().
+
+  \note The WPEView does not support loading content through the Qt Resource system.
+*/
+void WPEQtView::setUrl(const QUrl& url)
+{
+    if (url == m_url)
+        return;
+
+    m_errorOccured = false;
+    m_url = url;
+    if (m_webView)
+        webkit_web_view_load_uri(m_webView.get(), m_url.toString().toUtf8().constData());
+}
+
+/*!
+  \qmlproperty int WPEView::loadProgress
+  \readonly
+
+  The current load progress of the web content, represented as
+  an integer between 0 and 100.
+*/
+int WPEQtView::loadProgress() const
+{
+    if (!m_webView)
+        return 0;
+
+    return webkit_web_view_get_estimated_load_progress(m_webView.get()) * 100;
+}
+
+/*!
+  \qmlproperty string WPEView::title
+  \readonly
+
+  The title of the currently loaded web page.
+*/
+QString WPEQtView::title() const
+{
+    if (!m_webView)
+        return "";
+
+    return webkit_web_view_get_title(m_webView.get());
+}
+
+/*!
+  \qmlproperty bool WPEView::canGoBack
+  \readonly
+
+  Holds \c true if it's currently possible to navigate back in the web history.
+*/
+bool WPEQtView::canGoBack() const
+{
+    if (!m_webView)
+        return false;
+
+    return webkit_web_view_can_go_back(m_webView.get());
+}
+
+/*!
+  \qmlproperty bool WPEView::loading
+  \readonly
+
+  Holds \c true if the WPEView is currently in the process of loading
+  new content, \c false otherwise.
+
+  \sa loadingChanged()
+*/
+
+/*!
+  \qmlsignal WPEView::loadingChanged(WPEViewLoadRequest loadRequest)
+
+  This signal is emitted when the state of loading the web content changes.
+  By handling this signal it's possible, for example, to react to page load
+  errors.
+
+  The \a loadRequest parameter holds the \e url and \e status of the request,
+  as well as an \e errorString containing an error message for a failed
+  request.
+
+  \sa WPEViewLoadRequest
+*/
+bool WPEQtView::isLoading() const
+{
+    if (!m_webView)
+        return false;
+
+    return webkit_web_view_is_loading(m_webView.get());
+}
+
+/*!
+  \qmlproperty bool WPEView::canGoForward
+  \readonly
+
+  Holds \c true if it's currently possible to navigate forward in the web history.
+*/
+bool WPEQtView::canGoForward() const
+{
+    if (!m_webView)
+        return false;
+
+    return webkit_web_view_can_go_forward(m_webView.get());
+}
+
+/*!
+  \qmlmethod void WPEView::goBack()
+
+  Navigates back in the web history.
+*/
+void WPEQtView::goBack()
+{
+    if (m_webView)
+        webkit_web_view_go_back(m_webView.get());
+}
+
+/*!
+  \qmlmethod void WPEView::goForward()
+
+  Navigates forward in the web history.
+*/
+void WPEQtView::goForward()
+{
+    if (m_webView)
+        webkit_web_view_go_forward(m_webView.get());
+}
+
+/*!
+  \qmlmethod void WPEView::reload()
+
+  Reloads the current \l url.
+*/
+void WPEQtView::reload()
+{
+    if (m_webView)
+        webkit_web_view_reload(m_webView.get());
+}
+
+/*!
+  \qmlmethod void WPEView::stop()
+
+  Stops loading the current \l url.
+*/
+void WPEQtView::stop()
+{
+    if (m_webView)
+        webkit_web_view_stop_loading(m_webView.get());
+}
+
+/*!
+  \qmlmethod void WPEView::loadHtml(string html, url baseUrl)
+
+  Loads the specified \a html content to the web view.
+
+  This method offers a lower-level alternative to the \l url property,
+  which references HTML pages via URL.
+
+  External objects such as stylesheets or images referenced in the HTML
+  document should be located relative to \a baseUrl. For example, if \a html
+  is retrieved from \c http://www.example.com/documents/overview.html, which
+  is the base URL, then an image referenced with the relative url, \c diagram.png,
+  should be at \c{http://www.example.com/documents/diagram.png}.
+
+  \note The WPEView does not support loading content through the Qt Resource system.
+
+  \sa url
+*/
+void WPEQtView::loadHtml(const QString& html, const QUrl& baseUrl)
+{
+    m_html = html;
+    m_baseUrl = baseUrl;
+    m_errorOccured = false;
+
+    if (m_webView)
+        webkit_web_view_load_html(m_webView.get(), html.toUtf8().constData(), baseUrl.toString().toUtf8().constData());
+}
+
+struct JavascriptCallbackData {
+    JavascriptCallbackData(QJSValue cb, QPointer<WPEQtView> obj)
+        : callback(cb)
+        , object(obj) { }
+
+    QJSValue callback;
+    QPointer<WPEQtView> object;
+};
+
+static void jsAsyncReadyCallback(GObject* object, GAsyncResult* result, gpointer userData)
+{
+    GUniqueOutPtr<GError> error;
+    std::unique_ptr<JavascriptCallbackData> data(reinterpret_cast<JavascriptCallbackData*>(userData));
+    WebKitJavascriptResult* jsResult = webkit_web_view_run_javascript_finish(WEBKIT_WEB_VIEW(object), result, &error.outPtr());
+    if (!jsResult) {
+        qWarning("Error running javascript: %s", error->message);
+        return;
+    }
+
+    if (data->object.data()) {
+        QQmlEngine* engine = qmlEngine(data->object.data());
+        if (!engine) {
+            qWarning("No JavaScript engine, unable to handle JavaScript callback!");
+            webkit_javascript_result_unref(jsResult);
+            return;
+        }
+
+        QJSValueList args;
+        JSCValue* value = webkit_javascript_result_get_js_value(jsResult);
+        QVariant variant;
+        // FIXME: Handle more value types?
+        if (jsc_value_is_string(value)) {
+            GUniquePtr<gchar> strValue(jsc_value_to_string(value));
+            JSCContext* context = jsc_value_get_context(value);
+            JSCException* exception = jsc_context_get_exception(context);
+            if (exception) {
+                qWarning("Error running javascript: %s", jsc_exception_get_message(exception));
+                jsc_context_clear_exception(context);
+            } else
+                variant.setValue(QString(g_strdup(strValue.get())));
+        }
+        args.append(engine->toScriptValue(variant));
+        data->callback.call(args);
+    }
+    webkit_javascript_result_unref(jsResult);
+}
+
+/*!
+  \qmlmethod void WPEView::runJavaScript(string script, variant callback)
+
+  Runs the specified JavaScript.
+  In case a \a callback function is provided, it will be invoked after the \a script
+  finished running.
+
+  \badcode
+  runJavaScript("document.title", function(result) { console.log(result); });
+  \endcode
+*/
+void WPEQtView::runJavaScript(const QString& script, const QJSValue& callback)
+{
+    std::unique_ptr<JavascriptCallbackData> data = std::make_unique<JavascriptCallbackData>(callback, QPointer<WPEQtView>(this));
+    webkit_web_view_run_javascript(m_webView.get(), script.toUtf8().constData(), nullptr, jsAsyncReadyCallback, data.release());
+}
+
+void WPEQtView::mousePressEvent(QMouseEvent* event)
+{
+    forceActiveFocus();
+    if (m_backend)
+        m_backend->dispatchMousePressEvent(event);
+}
+
+void WPEQtView::mouseReleaseEvent(QMouseEvent* event)
+{
+    if (m_backend)
+        m_backend->dispatchMouseReleaseEvent(event);
+}
+
+void WPEQtView::hoverEnterEvent(QHoverEvent* event)
+{
+    if (m_backend)
+        m_backend->dispatchHoverEnterEvent(event);
+}
+
+void WPEQtView::hoverLeaveEvent(QHoverEvent* event)
+{
+    if (m_backend)
+        m_backend->dispatchHoverLeaveEvent(event);
+}
+
+void WPEQtView::hoverMoveEvent(QHoverEvent* event)
+{
+    if (m_backend)
+        m_backend->dispatchHoverMoveEvent(event);
+}
+
+void WPEQtView::wheelEvent(QWheelEvent* event)
+{
+    if (m_backend)
+        m_backend->dispatchWheelEvent(event);
+}
+
+void WPEQtView::keyPressEvent(QKeyEvent* event)
+{
+    if (m_backend)
+        m_backend->dispatchKeyEvent(event, true);
+}
+
+void WPEQtView::keyReleaseEvent(QKeyEvent* event)
+{
+    if (m_backend)
+        m_backend->dispatchKeyEvent(event, false);
+}
+
+void WPEQtView::touchEvent(QTouchEvent* event)
+{
+    if (m_backend)
+        m_backend->dispatchTouchEvent(event);
+}
diff --git a/Source/WebKit/UIProcess/API/wpe/qt/WPEQtView.h b/Source/WebKit/UIProcess/API/wpe/qt/WPEQtView.h
new file mode 100644 (file)
index 0000000..2c08e07
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2018, 2019 Igalia S.L
+ * Copyright (C) 2018, 2019 Zodiac Inflight Innovations
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#include "config.h"
+
+#include "WPEQtViewBackend.h"
+#include <QQmlEngine>
+#include <QQuickItem>
+#include <QUrl>
+#include <memory>
+#include <wpe/webkit.h>
+#include <wtf/glib/GRefPtr.h>
+
+class WPEQtViewLoadRequest;
+
+class WPEQtView : public QQuickItem {
+    Q_OBJECT
+    Q_DISABLE_COPY(WPEQtView)
+    Q_PROPERTY(QUrl url READ url WRITE setUrl NOTIFY urlChanged)
+    Q_PROPERTY(bool loading READ isLoading NOTIFY loadingChanged)
+    Q_PROPERTY(int loadProgress READ loadProgress NOTIFY loadProgressChanged)
+    Q_PROPERTY(QString title READ title NOTIFY titleChanged)
+    Q_PROPERTY(bool canGoBack READ canGoBack NOTIFY loadingChanged)
+    Q_PROPERTY(bool canGoForward READ canGoForward NOTIFY loadingChanged)
+    Q_ENUMS(LoadStatus)
+
+public:
+    enum LoadStatus {
+        LoadStartedStatus,
+        LoadStoppedStatus,
+        LoadSucceededStatus,
+        LoadFailedStatus
+    };
+
+    WPEQtView(QQuickItem* parent = nullptr);
+    ~WPEQtView();
+    QSGNode* updatePaintNode(QSGNode*, UpdatePaintNodeData*) final;
+
+    void triggerUpdate() { QMetaObject::invokeMethod(this, "update"); };
+
+    QUrl url() const;
+    void setUrl(const QUrl&);
+    int loadProgress() const;
+    QString title() const;
+    bool canGoBack() const;
+    bool isLoading() const;
+    bool canGoForward() const;
+
+public Q_SLOTS:
+    void goBack();
+    void goForward();
+    void reload();
+    void stop();
+    void loadHtml(const QString& html, const QUrl& baseUrl = QUrl());
+    void runJavaScript(const QString& script, const QJSValue& callback = QJSValue());
+
+Q_SIGNALS:
+    void webViewCreated();
+    void urlChanged();
+    void titleChanged();
+    void loadingChanged(WPEQtViewLoadRequest*);
+    void loadProgressChanged();
+
+protected:
+    bool errorOccured() const { return m_errorOccured; };
+    void setErrorOccured(bool errorOccured) { m_errorOccured = errorOccured; };
+
+    void geometryChanged(const QRectF& newGeometry, const QRectF& oldGeometry) override;
+
+    void hoverEnterEvent(QHoverEvent*) override;
+    void hoverLeaveEvent(QHoverEvent*) override;
+    void hoverMoveEvent(QHoverEvent*) override;
+
+    void mousePressEvent(QMouseEvent*) override;
+    void mouseReleaseEvent(QMouseEvent*) override;
+    void wheelEvent(QWheelEvent*) override;
+
+    void keyPressEvent(QKeyEvent*) override;
+    void keyReleaseEvent(QKeyEvent*) override;
+
+    void touchEvent(QTouchEvent*) override;
+
+private Q_SLOTS:
+    void configureWindow();
+    void createWebView();
+
+private:
+    static void notifyUrlChangedCallback(WPEQtView*);
+    static void notifyTitleChangedCallback(WPEQtView*);
+    static void notifyLoadProgressCallback(WPEQtView*);
+    static void notifyLoadChangedCallback(WebKitWebView*, WebKitLoadEvent, WPEQtView*);
+    static void notifyLoadFailedCallback(WebKitWebView*, WebKitLoadEvent, const gchar* failingURI, GError*, WPEQtView*);
+
+    GRefPtr<WebKitWebView> m_webView;
+    QUrl m_url;
+    QString m_html;
+    QUrl m_baseUrl;
+    QSizeF m_size;
+    WPEQtViewBackend* m_backend;
+    bool m_errorOccured { false };
+};
diff --git a/Source/WebKit/UIProcess/API/wpe/qt/WPEQtViewBackend.cpp b/Source/WebKit/UIProcess/API/wpe/qt/WPEQtViewBackend.cpp
new file mode 100644 (file)
index 0000000..51430aa
--- /dev/null
@@ -0,0 +1,375 @@
+/*
+ * Copyright (C) 2018, 2019 Igalia S.L
+ * Copyright (C) 2018, 2019 Zodiac Inflight Innovations
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "WPEQtViewBackend.h"
+
+#include "WPEQtView.h"
+#include <QGuiApplication>
+#include <QOpenGLFunctions>
+
+static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC imageTargetTexture2DOES;
+
+std::unique_ptr<WPEQtViewBackend> WPEQtViewBackend::create(const QSizeF& size, QPointer<QOpenGLContext> context, EGLDisplay eglDisplay, QPointer<WPEQtView> view)
+{
+    if (!context || !view)
+        return nullptr;
+
+    if (eglDisplay == EGL_NO_DISPLAY)
+        return nullptr;
+
+    eglInitialize(eglDisplay, nullptr, nullptr);
+
+    if (!eglBindAPI(EGL_OPENGL_ES_API))
+        return nullptr;
+
+    static const EGLint configAttributes[13] = {
+        EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+        EGL_RED_SIZE, 1,
+        EGL_GREEN_SIZE, 1,
+        EGL_BLUE_SIZE, 1,
+        EGL_ALPHA_SIZE, 1,
+        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+        EGL_NONE
+    };
+
+    EGLint count = 0;
+    if (!eglGetConfigs(eglDisplay, nullptr, 0, &count) || count < 1)
+        return nullptr;
+
+    EGLConfig eglConfig;
+    EGLint matched = 0;
+    EGLContext eglContext = nullptr;
+    if (eglChooseConfig(eglDisplay, configAttributes, &eglConfig, 1, &matched) && !!matched) {
+        static const EGLint contextAttributes[3] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
+        eglContext = eglCreateContext(eglDisplay, eglConfig, nullptr, contextAttributes);
+    }
+
+    if (!eglContext)
+        return nullptr;
+
+    return std::make_unique<WPEQtViewBackend>(size, eglDisplay, eglContext, context, view);
+}
+
+WPEQtViewBackend::WPEQtViewBackend(const QSizeF& size, EGLDisplay display, EGLContext eglContext, QPointer<QOpenGLContext> context, QPointer<WPEQtView> view)
+    : m_eglDisplay(display)
+    , m_eglContext(eglContext)
+    , m_view(view)
+    , m_size(size)
+{
+#if defined(WPE_BACKEND_CHECK_VERSION) && WPE_BACKEND_CHECK_VERSION(0, 2, 0)
+    wpe_loader_init("libWPEBackend-fdo-0.1.so");
+#endif
+
+    wpe_fdo_initialize_for_egl_display(m_eglDisplay);
+
+    imageTargetTexture2DOES = reinterpret_cast<PFNGLEGLIMAGETARGETTEXTURE2DOESPROC>(eglGetProcAddress("glEGLImageTargetTexture2DOES"));
+
+    static const char* vertexShaderSource =
+        "attribute vec2 pos;\n"
+        "attribute vec2 texture;\n"
+        "varying vec2 v_texture;\n"
+        "void main() {\n"
+        "  v_texture = texture;\n"
+        "  gl_Position = vec4(pos, 0, 1);\n"
+        "}\n";
+    static const char* fragmentShaderSource =
+        "precision mediump float;\n"
+        "uniform sampler2D u_texture;\n"
+        "varying vec2 v_texture;\n"
+        "void main() {\n"
+        "  gl_FragColor = texture2D(u_texture, v_texture);\n"
+        "}\n";
+
+    QOpenGLFunctions* glFunctions = context->functions();
+    GLuint vertexShader = glFunctions->glCreateShader(GL_VERTEX_SHADER);
+    glFunctions->glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr);
+    glFunctions->glCompileShader(vertexShader);
+
+    GLuint fragmentShader = glFunctions->glCreateShader(GL_FRAGMENT_SHADER);
+    glFunctions->glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr);
+    glFunctions->glCompileShader(fragmentShader);
+
+    m_program = glFunctions->glCreateProgram();
+    glFunctions->glAttachShader(m_program, vertexShader);
+    glFunctions->glAttachShader(m_program, fragmentShader);
+    glFunctions->glLinkProgram(m_program);
+
+    glFunctions->glBindAttribLocation(m_program, 0, "pos");
+    glFunctions->glBindAttribLocation(m_program, 1, "texture");
+    m_textureUniform = glFunctions->glGetUniformLocation(m_program, "u_texture");
+
+    static struct wpe_view_backend_exportable_fdo_egl_client exportableClient = {
+        // export_buffer_resource
+        [](void* data, EGLImageKHR image)
+        {
+            static_cast<WPEQtViewBackend*>(data)->displayImage(image);
+        },
+        // padding
+        nullptr, nullptr, nullptr, nullptr
+    };
+
+    m_exportable = wpe_view_backend_exportable_fdo_egl_create(&exportableClient, this, m_size.width(), m_size.height());
+
+#if defined(WPE_BACKEND_CHECK_VERSION) && WPE_BACKEND_CHECK_VERSION(1, 1, 0)
+    wpe_view_backend_add_activity_state(backend(), wpe_view_activity_state_visible | wpe_view_activity_state_focused | wpe_view_activity_state_in_window);
+#endif
+
+    m_surface.setFormat(context->format());
+    m_surface.create();
+}
+
+WPEQtViewBackend::~WPEQtViewBackend()
+{
+    wpe_view_backend_exportable_fdo_destroy(m_exportable);
+    eglDestroyContext(m_eglDisplay, m_eglContext);
+}
+
+void WPEQtViewBackend::resize(const QSizeF& newSize)
+{
+    if (!newSize.isValid())
+        return;
+
+    m_size = newSize;
+    wpe_view_backend_dispatch_set_size(backend(), m_size.width(), m_size.height());
+}
+
+GLuint WPEQtViewBackend::texture(QOpenGLContext* context)
+{
+    if (!m_lockedImage || !hasValidSurface())
+        return 0;
+
+    context->makeCurrent(&m_surface);
+
+    QOpenGLFunctions* glFunctions = context->functions();
+    if (!m_textureId) {
+        glFunctions->glGenTextures(1, &m_textureId);
+        glFunctions->glBindTexture(GL_TEXTURE_2D, m_textureId);
+        glFunctions->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+        glFunctions->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+        glFunctions->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+        glFunctions->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+        glFunctions->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_size.width(), m_size.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+        glFunctions->glBindTexture(GL_TEXTURE_2D, 0);
+    }
+
+    glFunctions->glClearColor(1, 0, 0, 1);
+    glFunctions->glClear(GL_COLOR_BUFFER_BIT);
+
+    glFunctions->glUseProgram(m_program);
+
+    glFunctions->glActiveTexture(GL_TEXTURE0);
+    glFunctions->glBindTexture(GL_TEXTURE_2D, m_textureId);
+    imageTargetTexture2DOES(GL_TEXTURE_2D, m_lockedImage);
+    glFunctions->glUniform1i(m_textureUniform, 0);
+
+    static const GLfloat vertices[4][2] = {
+        { -1.0, 1.0  },
+        {  1.0, 1.0  },
+        { -1.0, -1.0 },
+        {  1.0, -1.0 },
+    };
+
+    static const GLfloat texturePos[4][2] = {
+        { 0, 0 },
+        { 1, 0 },
+        { 0, 1 },
+        { 1, 1 },
+    };
+
+    glFunctions->glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, vertices);
+    glFunctions->glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, texturePos);
+
+    glFunctions->glEnableVertexAttribArray(0);
+    glFunctions->glEnableVertexAttribArray(1);
+
+    glFunctions->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+    glFunctions->glDisableVertexAttribArray(0);
+    glFunctions->glDisableVertexAttribArray(1);
+
+    wpe_view_backend_exportable_fdo_dispatch_frame_complete(m_exportable);
+    wpe_view_backend_exportable_fdo_egl_dispatch_release_image(m_exportable, m_lockedImage);
+    m_lockedImage = EGL_NO_IMAGE_KHR;
+
+    return m_textureId;
+}
+
+void WPEQtViewBackend::displayImage(EGLImageKHR image)
+{
+    RELEASE_ASSERT(m_lockedImage == EGL_NO_IMAGE_KHR);
+    m_lockedImage = image;
+    if (m_view)
+        m_view->triggerUpdate();
+}
+
+uint32_t WPEQtViewBackend::modifiers() const
+{
+    uint32_t mask = m_keyboardModifiers;
+    if (m_mouseModifiers)
+        mask |= m_mouseModifiers;
+    return mask;
+}
+
+void WPEQtViewBackend::dispatchHoverEnterEvent(QHoverEvent*)
+{
+    m_hovering = true;
+    m_mouseModifiers = 0;
+}
+
+void WPEQtViewBackend::dispatchHoverLeaveEvent(QHoverEvent*)
+{
+    m_hovering = false;
+}
+
+void WPEQtViewBackend::dispatchHoverMoveEvent(QHoverEvent* event)
+{
+    if (!m_hovering)
+        return;
+
+    uint32_t state = !!m_mousePressedButton;
+    struct wpe_input_pointer_event wpeEvent = { wpe_input_pointer_event_type_motion,
+        static_cast<uint32_t>(event->timestamp()),
+        event->pos().x(), event->pos().y(),
+        m_mousePressedButton, state, modifiers() };
+    wpe_view_backend_dispatch_pointer_event(backend(), &wpeEvent);
+}
+
+void WPEQtViewBackend::dispatchMousePressEvent(QMouseEvent* event)
+{
+    uint32_t button = 0;
+    uint32_t modifier = 0;
+    switch (event->button()) {
+    case Qt::LeftButton:
+        button = 1;
+        modifier = wpe_input_pointer_modifier_button1;
+        break;
+    case Qt::RightButton:
+        button = 2;
+        modifier = wpe_input_pointer_modifier_button2;
+        break;
+    default:
+        break;
+    }
+    m_mousePressedButton = button;
+    uint32_t state = 1;
+    m_mouseModifiers |= modifier;
+    struct wpe_input_pointer_event wpeEvent = { wpe_input_pointer_event_type_button,
+        static_cast<uint32_t>(event->timestamp()),
+        event->x(), event->y(), button, state, modifiers() };
+    wpe_view_backend_dispatch_pointer_event(backend(), &wpeEvent);
+}
+
+void WPEQtViewBackend::dispatchMouseReleaseEvent(QMouseEvent* event)
+{
+    uint32_t button = 0;
+    uint32_t modifier = 0;
+    switch (event->button()) {
+    case Qt::LeftButton:
+        button = 1;
+        modifier = wpe_input_pointer_modifier_button1;
+        break;
+    case Qt::RightButton:
+        button = 2;
+        modifier = wpe_input_pointer_modifier_button2;
+        break;
+    default:
+        break;
+    }
+    m_mousePressedButton = 0;
+    uint32_t state = 0;
+    m_mouseModifiers &= ~modifier;
+    struct wpe_input_pointer_event wpeEvent = { wpe_input_pointer_event_type_button,
+        static_cast<uint32_t>(event->timestamp()),
+        event->x(), event->y(), button, state, modifiers() };
+    wpe_view_backend_dispatch_pointer_event(backend(), &wpeEvent);
+}
+
+void WPEQtViewBackend::dispatchWheelEvent(QWheelEvent* event)
+{
+    QPoint delta = event->angleDelta();
+    uint32_t axis = delta.y() == event->y();
+    QPoint numDegrees = delta / 8;
+    QPoint numSteps = numDegrees / 15;
+    int32_t length = numSteps.y() ? numSteps.y() : numSteps.x();
+    struct wpe_input_axis_event wpeEvent = { wpe_input_axis_event_type_motion,
+        static_cast<uint32_t>(event->timestamp()),
+        event->x(), event->y(), axis, length, modifiers() };
+    wpe_view_backend_dispatch_axis_event(backend(), &wpeEvent);
+}
+
+void WPEQtViewBackend::dispatchKeyEvent(QKeyEvent* event, bool state)
+{
+    uint32_t keysym = event->nativeVirtualKey();
+    if (!keysym)
+        keysym = wpe_input_xkb_context_get_key_code(wpe_input_xkb_context_get_default(), event->key(), state);
+
+    uint32_t modifiers = 0;
+    Qt::KeyboardModifiers qtModifiers = event->modifiers();
+    if (!qtModifiers)
+        qtModifiers = QGuiApplication::keyboardModifiers();
+
+    if (qtModifiers & Qt::ShiftModifier)
+        modifiers |= wpe_input_keyboard_modifier_shift;
+
+    if (qtModifiers & Qt::ControlModifier)
+        modifiers |= wpe_input_keyboard_modifier_control;
+    if (qtModifiers & Qt::MetaModifier)
+        modifiers |= wpe_input_keyboard_modifier_meta;
+    if (qtModifiers & Qt::AltModifier)
+        modifiers |= wpe_input_keyboard_modifier_alt;
+
+    struct wpe_input_keyboard_event wpeEvent = { static_cast<uint32_t>(event->timestamp()),
+        keysym, event->nativeScanCode(), state, modifiers };
+    wpe_view_backend_dispatch_keyboard_event(backend(), &wpeEvent);
+}
+
+void WPEQtViewBackend::dispatchTouchEvent(QTouchEvent* event)
+{
+    wpe_input_touch_event_type eventType;
+    switch (event->type()) {
+    case QEvent::TouchBegin:
+        eventType = wpe_input_touch_event_type_down;
+        break;
+    case QEvent::TouchUpdate:
+        eventType = wpe_input_touch_event_type_motion;
+        break;
+    case QEvent::TouchEnd:
+        eventType = wpe_input_touch_event_type_up;
+        break;
+    default:
+        eventType = wpe_input_touch_event_type_null;
+        break;
+    }
+
+    int i = 0;
+    struct wpe_input_touch_event_raw* rawEvents = g_new0(wpe_input_touch_event_raw, event->touchPoints().length());
+    for (auto& point : event->touchPoints()) {
+        rawEvents[i] = { eventType, static_cast<uint32_t>(event->timestamp()),
+            point.id(), static_cast<int32_t>(point.pos().x()), static_cast<int32_t>(point.pos().y()) };
+        i++;
+    }
+
+    struct wpe_input_touch_event wpeEvent = { rawEvents, static_cast<uint64_t>(i), eventType,
+        static_cast<int32_t>(rawEvents[0].id),
+        static_cast<uint32_t>(event->timestamp()), modifiers() };
+    wpe_view_backend_dispatch_touch_event(backend(), &wpeEvent);
+    g_free(rawEvents);
+}
diff --git a/Source/WebKit/UIProcess/API/wpe/qt/WPEQtViewBackend.h b/Source/WebKit/UIProcess/API/wpe/qt/WPEQtViewBackend.h
new file mode 100644 (file)
index 0000000..f0456d6
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2018, 2019 Igalia S.L
+ * Copyright (C) 2018, 2019 Zodiac Inflight Innovations
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+// This include order is necessary to enforce the GBM EGL platform.
+#include <gbm.h>
+#include <epoxy/egl.h>
+
+#include <QHoverEvent>
+#include <QKeyEvent>
+#include <QMouseEvent>
+#include <QOffscreenSurface>
+#include <QOpenGLContext>
+#include <QPointer>
+#include <QWheelEvent>
+#include <wpe/fdo-egl.h>
+#include <wpe/fdo.h>
+
+class WPEQtView;
+
+class Q_DECL_EXPORT WPEQtViewBackend {
+public:
+    static std::unique_ptr<WPEQtViewBackend> create(const QSizeF&, QPointer<QOpenGLContext>, EGLDisplay, QPointer<WPEQtView>);
+    WPEQtViewBackend(const QSizeF&, EGLDisplay, EGLContext, QPointer<QOpenGLContext>, QPointer<WPEQtView>);
+    virtual ~WPEQtViewBackend();
+
+    void resize(const QSizeF&);
+    GLuint texture(QOpenGLContext*);
+    bool hasValidSurface() const { return m_surface.isValid(); };
+
+    void dispatchHoverEnterEvent(QHoverEvent*);
+    void dispatchHoverLeaveEvent(QHoverEvent*);
+    void dispatchHoverMoveEvent(QHoverEvent*);
+
+    void dispatchMousePressEvent(QMouseEvent*);
+    void dispatchMouseReleaseEvent(QMouseEvent*);
+    void dispatchWheelEvent(QWheelEvent*);
+
+    void dispatchKeyEvent(QKeyEvent*, bool state);
+
+    void dispatchTouchEvent(QTouchEvent*);
+
+    struct wpe_view_backend* backend() const { return wpe_view_backend_exportable_fdo_get_view_backend(m_exportable); };
+
+private:
+    void displayImage(EGLImageKHR);
+    uint32_t modifiers() const;
+
+    EGLDisplay m_eglDisplay { nullptr };
+    EGLContext m_eglContext { nullptr };
+    struct wpe_view_backend_exportable_fdo* m_exportable { nullptr };
+
+    EGLImageKHR m_lockedImage { EGL_NO_IMAGE_KHR };
+
+    QPointer<WPEQtView> m_view;
+    QOffscreenSurface m_surface;
+    QSizeF m_size;
+    GLuint m_textureId { 0 };
+    unsigned m_program { 0 };
+    unsigned m_textureUniform { 0 };
+
+    bool m_hovering { false };
+    uint32_t m_mouseModifiers { 0 };
+    uint32_t m_keyboardModifiers { 0 };
+    uint32_t m_mousePressedButton { 0 };
+};
diff --git a/Source/WebKit/UIProcess/API/wpe/qt/WPEQtViewLoadRequest.cpp b/Source/WebKit/UIProcess/API/wpe/qt/WPEQtViewLoadRequest.cpp
new file mode 100644 (file)
index 0000000..8f7713d
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2018, 2019 Igalia S.L
+ * Copyright (C) 2018, 2019 Zodiac Inflight Innovations
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "WPEQtViewLoadRequest.h"
+
+#include "WPEQtView.h"
+#include "WPEQtViewLoadRequestPrivate.h"
+
+/*!
+  \qmltype WPEViewLoadRequest
+  \instantiates WPEQtViewLoadRequest
+  \inqmlmodule org.wpewebkit.qtwpe
+
+  \brief A utility type for \l {WPEView}'s \l {WPEView::}{loadingChanged()} signal.
+
+  The WPEViewLoadRequest type contains load status information for the requested URL.
+
+  \sa {WPEView::loadingChanged()}{WPEView.loadingChanged()}
+*/
+WPEQtViewLoadRequest::WPEQtViewLoadRequest(const WPEQtViewLoadRequestPrivate& d)
+    : d_ptr(new WPEQtViewLoadRequestPrivate(d))
+{
+
+}
+
+WPEQtViewLoadRequest::~WPEQtViewLoadRequest()
+{
+
+}
+
+/*!
+  \qmlproperty url WPEView::WPEViewLoadRequest::url
+  \readonly
+
+  The URL of the load request.
+*/
+QUrl WPEQtViewLoadRequest::url() const
+{
+    Q_D(const WPEQtViewLoadRequest);
+    return d->m_url;
+}
+
+/*!
+  \qmlproperty enumeration WPEViewLoadRequest::status
+  \readonly
+
+  This enumeration represents the load status of a web page load request.
+
+  \value WPEView.LoadStartedStatus The page is currently loading.
+  \value WPEView.LoadStoppedStatus The page loading was interrupted.
+  \value WPEView.LoadSucceededStatus The page was loaded successfully.
+  \value WPEView.LoadFailedStatus The page could not be loaded.
+
+  \sa {WPEView::loadingChanged()}{WPEView.loadingChanged}
+*/
+WPEQtView::LoadStatus WPEQtViewLoadRequest::status() const
+{
+    Q_D(const WPEQtViewLoadRequest);
+    return d->m_status;
+}
+
+/*!
+  \qmlproperty string WPEView::WPEViewLoadRequest::errorString
+  \readonly
+
+  Holds the error message if the load request failed.
+*/
+QString WPEQtViewLoadRequest::errorString() const
+{
+    Q_D(const WPEQtViewLoadRequest);
+    return d->m_errorString;
+}
diff --git a/Source/WebKit/UIProcess/API/wpe/qt/WPEQtViewLoadRequest.h b/Source/WebKit/UIProcess/API/wpe/qt/WPEQtViewLoadRequest.h
new file mode 100644 (file)
index 0000000..aad0e06
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2018, 2019 Igalia S.L
+ * Copyright (C) 2018, 2019 Zodiac Inflight Innovations
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#include "WPEQtView.h"
+
+#include <QObject>
+
+class WPEQtViewLoadRequestPrivate;
+
+class WPEQtViewLoadRequest : public QObject {
+    Q_OBJECT
+    Q_PROPERTY(QUrl url READ url)
+    Q_PROPERTY(WPEQtView::LoadStatus status READ status)
+    Q_PROPERTY(QString errorString READ errorString)
+
+public:
+    ~WPEQtViewLoadRequest();
+
+    QUrl url() const;
+    WPEQtView::LoadStatus status() const;
+    QString errorString() const;
+
+    explicit WPEQtViewLoadRequest(const WPEQtViewLoadRequestPrivate&);
+
+private:
+    friend class WPEQtView;
+
+    Q_DECLARE_PRIVATE(WPEQtViewLoadRequest)
+    QScopedPointer<WPEQtViewLoadRequestPrivate> d_ptr;
+};
+
diff --git a/Source/WebKit/UIProcess/API/wpe/qt/WPEQtViewLoadRequestPrivate.h b/Source/WebKit/UIProcess/API/wpe/qt/WPEQtViewLoadRequestPrivate.h
new file mode 100644 (file)
index 0000000..e876771
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018, 2019 Igalia S.L
+ * Copyright (C) 2018, 2019 Zodiac Inflight Innovations
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#include "WPEQtView.h"
+#include <QtCore/qstring.h>
+#include <QtCore/qurl.h>
+
+class WPEQtViewLoadRequestPrivate {
+public:
+    WPEQtViewLoadRequestPrivate() { }
+    WPEQtViewLoadRequestPrivate(const QUrl& url, WPEQtView::LoadStatus status, const QString& errorString)
+        : m_url(url)
+        , m_status(status)
+        , m_errorString(errorString)
+    { }
+    ~WPEQtViewLoadRequestPrivate() { }
+
+    QUrl m_url;
+    WPEQtView::LoadStatus m_status;
+    QString m_errorString;
+};
+
+Q_DECLARE_METATYPE(WPEQtViewLoadRequestPrivate)
diff --git a/Source/WebKit/UIProcess/API/wpe/qt/qmldir b/Source/WebKit/UIProcess/API/wpe/qt/qmldir
new file mode 100644 (file)
index 0000000..0d9300a
--- /dev/null
@@ -0,0 +1,2 @@
+module org.wpewebkit.qtwpe
+plugin qtwpe
index c7a259b..5f311b8 100644 (file)
@@ -45,6 +45,7 @@ WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_WEB_RTC PRIVATE ${ENABLE_EXPERIMENTAL_FE
 # and the option is not relevant to any other WebKit ports.
 WEBKIT_OPTION_DEFINE(ENABLE_GTKDOC "Whether or not to use generate gtkdoc." PUBLIC OFF)
 WEBKIT_OPTION_DEFINE(USE_WOFF2 "Whether to enable support for WOFF2 Web Fonts." PUBLIC ON)
+WEBKIT_OPTION_DEFINE(ENABLE_WPE_QT_API "Whether to enable support for the Qt5/QML plugin" PUBLIC OFF)
 
 # Private options specific to the WPE port.
 WEBKIT_OPTION_DEFINE(USE_OPENVR "Whether to use OpenVR as WebVR backend." PRIVATE ${ENABLE_EXPERIMENTAL_FEATURES})
@@ -110,6 +111,13 @@ if (ENABLE_XSLT)
     find_package(LibXslt 1.1.7 REQUIRED)
 endif ()
 
+if (ENABLE_WPE_QT_API)
+    find_package(Qt5 REQUIRED COMPONENTS Core Quick Gui)
+    find_package(Qt5Test REQUIRED)
+    find_package(PkgConfig)
+    pkg_check_modules(WPE_BACKEND_FDO REQUIRED wpebackend-fdo-0.1)
+endif ()
+
 add_definitions(-DBUILDING_WPE__=1)
 add_definitions(-DGETTEXT_PACKAGE="WPE")
 add_definitions(-DJSC_GLIB_API_ENABLED)
index 6bb4100..2bcfefb 100644 (file)
@@ -1,3 +1,68 @@
+2019-01-18  Philippe Normand  <pnormand@igalia.com>
+
+        [WPE] Add Qt extension
+        https://bugs.webkit.org/show_bug.cgi?id=191464
+
+        Reviewed by Carlos Garcia Campos.
+
+        MiniBrowser and API tests for the WPE Qt API. To run the
+        MiniBrowser a new script is introduced. Example invocation:
+
+        $ run-qt-wpe-minibrowser -platform wayland https://webkit.org
+
+        Also note-worthy is the introduction of the python2-subprocess32
+        dependency to run the GLib API tests.
+
+        * MiniBrowser/wpe/CMakeLists.txt:
+        * MiniBrowser/wpe/qt/CMakeLists.txt: Added.
+        * MiniBrowser/wpe/qt/main.cpp: Added.
+        (main):
+        * MiniBrowser/wpe/qt/main.qml: Added.
+        * MiniBrowser/wpe/qt/qml.qrc: Added.
+        * Scripts/run-gtk-tests:
+        (GtkTestRunner.is_google_test):
+        (GtkTestRunner):
+        (GtkTestRunner.is_qt_test):
+        * Scripts/run-qt-wpe-minibrowser: Added.
+        * Scripts/run-wpe-tests:
+        (WPETestRunner):
+        (WPETestRunner.is_google_test):
+        (WPETestRunner.is_qt_test):
+        * TestWebKitAPI/Tests/WPEQt/TestLoad.cpp: Added.
+        (TestLoad::main):
+        * TestWebKitAPI/Tests/WPEQt/TestLoadHtml.cpp: Added.
+        (TestLoadHtml::main):
+        * TestWebKitAPI/Tests/WPEQt/TestLoadRequest.cpp: Added.
+        (TestLoadRequest::main):
+        * TestWebKitAPI/Tests/WPEQt/TestRunJavaScript.cpp: Added.
+        (TestRunJavaScript::main):
+        * TestWebKitAPI/Tests/WPEQt/WPEQtTest.cpp: Added.
+        (waitForSignal):
+        * TestWebKitAPI/Tests/WPEQt/WPEQtTest.h: Added.
+        (LoadSpy::LoadSpy):
+        (LoadSpy::~LoadSpy):
+        (LoadSpy::onLoadingChanged):
+        (LoadStartedCatcher::LoadStartedCatcher):
+        (LoadStartedCatcher::~LoadStartedCatcher):
+        (LoadStartedCatcher::onLoadingChanged):
+        (waitForLoadSucceeded):
+        (waitForLoadFailed):
+        * TestWebKitAPI/glib/CMakeLists.txt:
+        * flatpak/flatpakutils.py:
+        (WebkitFlatpak.load_from_args):
+        (WebkitFlatpak.__init__):
+        (WebkitFlatpak.clean_args):
+        (WebkitFlatpak.run_in_sandbox):
+        * flatpak/org.webkit.CommonModules.yaml:
+        * flatpak/org.webkit.WPE.yaml:
+        * flatpak/org.webkit.WPEModules.yaml:
+        * flatpak/org.webkit.WPEQT.yaml: Copied from Tools/flatpak/org.webkit.WebKit.yaml.
+        * flatpak/org.webkit.WebKit.yaml:
+        * glib/api_test_runner.py:
+        (TestRunner._run_test_qt):
+        (TestRunner.is_qt_test):
+        (TestRunner._run_test):
+
 2019-01-17  Wenson Hsieh  <wenson_hsieh@apple.com>
 
         [iOS] Content offset jumps erratically when autoscrolling near scroll view content inset areas
index f261379..74591ff 100644 (file)
@@ -34,3 +34,7 @@ add_executable(MiniBrowser ${MiniBrowser_SOURCES})
 target_link_libraries(MiniBrowser ${MiniBrowser_LIBRARIES})
 
 install(TARGETS MiniBrowser DESTINATION "${LIBEXEC_INSTALL_DIR}")
+
+if (ENABLE_WPE_QT_API)
+    add_subdirectory(qt)
+endif ()
diff --git a/Tools/MiniBrowser/wpe/qt/CMakeLists.txt b/Tools/MiniBrowser/wpe/qt/CMakeLists.txt
new file mode 100644 (file)
index 0000000..5186843
--- /dev/null
@@ -0,0 +1,13 @@
+
+if (ENABLE_WPE_QT_API)
+    set(CMAKE_AUTOMOC ON)
+    set(CMAKE_AUTORCC ON)
+
+    if (DEVELOPER_MODE)
+        add_definitions(-DWEBKIT_INJECTED_BUNDLE_PATH="${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
+    endif ()
+
+    add_executable(qt-wpe-mini-browser "main.cpp" "qml.qrc")
+    target_link_libraries(qt-wpe-mini-browser Qt5::Core Qt5::Quick)
+    install(TARGETS qt-wpe-mini-browser DESTINATION "${LIBEXEC_INSTALL_DIR}")
+endif ()
diff --git a/Tools/MiniBrowser/wpe/qt/main.cpp b/Tools/MiniBrowser/wpe/qt/main.cpp
new file mode 100644 (file)
index 0000000..050aede
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2018, 2019 Igalia S.L
+ * Copyright (C) 2018, 2019 Zodiac Inflight Innovations
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <QCommandLineOption>
+#include <QCommandLineParser>
+#include <QGuiApplication>
+#include <QProcessEnvironment>
+#include <QQmlApplicationEngine>
+#include <QQmlContext>
+#include <QUrl>
+
+int main(int argc, char* argv[])
+{
+#if defined(WEBKIT_INJECTED_BUNDLE_PATH)
+    setenv("WEBKIT_INJECTED_BUNDLE_PATH", WEBKIT_INJECTED_BUNDLE_PATH, 0);
+#endif
+    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
+
+    QGuiApplication app(argc, argv);
+
+    QCommandLineParser parser;
+    QCoreApplication::setApplicationVersion("0.1");
+    parser.setApplicationDescription(QGuiApplication::applicationDisplayName());
+    parser.addHelpOption();
+    parser.addVersionOption();
+    parser.addPositionalArgument("initialUrl", "The URL to open.");
+    QStringList arguments = app.arguments();
+    parser.process(arguments);
+    const QString initialUrl = parser.positionalArguments().isEmpty() ?
+        QStringLiteral("https://wpewebkit.org") : parser.positionalArguments().first();
+
+    QQmlApplicationEngine engine;
+    QQmlContext* context = engine.rootContext();
+    context->setContextProperty(QStringLiteral("initialUrl"), QUrl(initialUrl));
+
+    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
+    if (engine.rootObjects().isEmpty())
+        return -1;
+
+    return app.exec();
+}
diff --git a/Tools/MiniBrowser/wpe/qt/main.qml b/Tools/MiniBrowser/wpe/qt/main.qml
new file mode 100644 (file)
index 0000000..a405d76
--- /dev/null
@@ -0,0 +1,20 @@
+import QtQuick 2.11
+import QtQuick.Window 2.11
+import org.wpewebkit.qtwpe 1.0
+
+Window {
+    id: main_window
+    visible: true
+    width: 1280
+    height: 720
+    title: qsTr("Hello WPE!")
+
+    WPEView {
+        url: initialUrl
+        focus: true
+        anchors.fill: parent
+        onTitleChanged: {
+            main_window.title = title;
+        }
+    }
+}
diff --git a/Tools/MiniBrowser/wpe/qt/qml.qrc b/Tools/MiniBrowser/wpe/qt/qml.qrc
new file mode 100644 (file)
index 0000000..5f6483a
--- /dev/null
@@ -0,0 +1,5 @@
+<RCC>
+    <qresource prefix="/">
+        <file>main.qml</file>
+    </qresource>
+</RCC>
index a9bcc18..0b6741c 100755 (executable)
@@ -125,6 +125,9 @@ class GtkTestRunner(TestRunner):
     def is_google_test(self, test_program):
         return os.path.basename(os.path.dirname(test_program)) in ["WebKit", "WTF", "WebCore",  "WebCoreGtk"]
 
+    def is_qt_test(self, test_program):
+        return False
+
 if __name__ == "__main__":
     flatpakutils.run_in_sandbox_if_available(sys.argv)
     if not flatpakutils.is_sandboxed() and not jhbuildutils.enter_jhbuild_environment_if_available("gtk"):
diff --git a/Tools/Scripts/run-qt-wpe-minibrowser b/Tools/Scripts/run-qt-wpe-minibrowser
new file mode 100755 (executable)
index 0000000..28e6c7f
--- /dev/null
@@ -0,0 +1,60 @@
+#!/usr/bin/env perl
+# Copyright (C) 2018, 2019 Igalia S.L.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+# Boston, MA 02110-1301, USA.
+
+# Simplified "run" script for launching the QtWPE MiniBrowser.
+
+use strict;
+use warnings;
+use File::Spec::Functions qw/catdir/;
+use FindBin;
+use lib $FindBin::Bin;
+use webkitdirs;
+
+my $launcherName;
+my $libPath;
+my $launcherPath;
+my @jhbuildWrapper;
+
+prohibitUnknownPort();
+setConfiguration();
+
+if (isWPE()) {
+    my $configuration = passedConfiguration();
+    my $productDir = productDir();
+    my $libPath;
+    $libPath = "/app/webkit/WebKitBuild/$configuration/lib/qml" if $configuration;
+    $ENV{"QML2_IMPORT_PATH"} = "$libPath" if $libPath;
+    runInFlatpakIfAvailable("/app/webkit/Tools/Scripts/run-qt-wpe-minibrowser");
+
+    # Check to see that all the frameworks are built.
+    checkFrameworks();
+
+    if (!inFlatpakSandbox()) {
+       $libPath = "$productDir/lib/qml" if $productDir;
+       $ENV{"QML2_IMPORT_PATH"} = "$libPath" if $libPath;
+    }
+
+    $launcherPath = catdir($productDir, "bin", "qt-wpe-mini-browser");
+    die "Can't find $launcherPath" unless -x $launcherPath;
+    @jhbuildWrapper = wrapperPrefixIfNeeded();
+
+    print "Starting MiniBrowser.\n";
+    exec @jhbuildWrapper, $launcherPath, @ARGV or die;
+} else {
+    die "Unsupported platform."
+}
index e77ebe9..cb7d704 100755 (executable)
@@ -26,12 +26,13 @@ top_level_directory = os.path.normpath(os.path.join(os.path.dirname(__file__), "
 sys.path.insert(0, os.path.join(top_level_directory, "Tools", "jhbuild"))
 sys.path.insert(0, os.path.join(top_level_directory, "Tools", "flatpak"))
 sys.path.insert(0, os.path.join(top_level_directory, "Tools", "glib"))
+import common
 import jhbuildutils
 import flatpakutils
 from api_test_runner import TestRunner, add_options
 
 class WPETestRunner(TestRunner):
-    TestRunner.TEST_DIRS = [ "WPE", "WebKit", "JavaScriptCore", "WTF", "WebCore" ]
+    TestRunner.TEST_DIRS = [ "WPE", "WPEQt", "WebKit", "JavaScriptCore", "WTF", "WebCore" ]
 
     def __init__(self, options, tests=[]):
         super(WPETestRunner, self).__init__("wpe", options, tests)
@@ -42,6 +43,8 @@ class WPETestRunner(TestRunner):
     def is_google_test(self, test_program):
         return os.path.basename(os.path.dirname(test_program)) in ["WebKit", "WTF", "WebCore"]
 
+    def is_qt_test(self, test_program):
+        return os.path.basename(os.path.dirname(test_program)) == "WPEQt"
 
 if __name__ == "__main__":
     flatpakutils.run_in_sandbox_if_available([sys.argv[0], "--wpe"] + sys.argv[1:])
diff --git a/Tools/TestWebKitAPI/Tests/WPEQt/TestLoad.cpp b/Tools/TestWebKitAPI/Tests/WPEQt/TestLoad.cpp
new file mode 100644 (file)
index 0000000..2c97093
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2018, 2019 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2,1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include "WPEQtTest.h"
+
+#include <QtCore/QTemporaryFile>
+#include <QtCore/qdir.h>
+#include <QtCore/qfileinfo.h>
+#include <QtCore/qstandardpaths.h>
+#include <QtCore/qtemporarydir.h>
+
+class TestLoad : public WPEQtTest {
+Q_OBJECT
+private:
+    void main() override;
+};
+
+void TestLoad::main()
+{
+    QString cacheLocation = QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
+    static const char* testHTML = "<html><head><title>FooBar</title></head><body><p>This is a test</p></body></html>";
+
+    QTemporaryFile file(cacheLocation + QStringLiteral("/XXXXXXfile.html"));
+    QVERIFY2(file.open(), qPrintable(QStringLiteral("Cannot create temporary file:") + file.errorString()));
+
+    file.write(testHTML);
+    const QString fileName(file.fileName());
+    file.close();
+
+    const QUrl url(QUrl::fromLocalFile(fileName));
+    m_view->setUrl(url);
+
+    waitForLoadSucceeded(m_view);
+    QTRY_COMPARE(m_view->loadProgress(), 100);
+    QTRY_VERIFY(!m_view->isLoading());
+    QCOMPARE(m_view->title(), QStringLiteral("FooBar"));
+    QVERIFY(!m_view->canGoBack());
+    QVERIFY(!m_view->canGoForward());
+    QCOMPARE(m_view->url(), url);
+}
+
+QTEST_APPLESS_MAIN(TestLoad)
+#include "TestLoad.moc"
diff --git a/Tools/TestWebKitAPI/Tests/WPEQt/TestLoadHtml.cpp b/Tools/TestWebKitAPI/Tests/WPEQt/TestLoadHtml.cpp
new file mode 100644 (file)
index 0000000..7bc05fb
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2018, 2019 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2,1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include "WPEQtTest.h"
+
+class TestLoadHtml : public WPEQtTest {
+Q_OBJECT
+private:
+    void main() override;
+};
+
+void TestLoadHtml::main()
+{
+    m_view->loadHtml(QString("<html><head><title>WebViewTitle</title></head><body />"));
+    waitForLoadSucceeded(m_view);
+    QTRY_COMPARE(m_view->loadProgress(), 100);
+    QTRY_VERIFY(!m_view->isLoading());
+    QCOMPARE(m_view->title(), QStringLiteral("WebViewTitle"));
+}
+
+QTEST_APPLESS_MAIN(TestLoadHtml)
+#include "TestLoadHtml.moc"
diff --git a/Tools/TestWebKitAPI/Tests/WPEQt/TestLoadRequest.cpp b/Tools/TestWebKitAPI/Tests/WPEQt/TestLoadRequest.cpp
new file mode 100644 (file)
index 0000000..23e4f84
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2018, 2019 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2,1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include "WPEQtTest.h"
+
+#include <QtCore/QTemporaryFile>
+#include <QtCore/qdir.h>
+#include <QtCore/qfileinfo.h>
+#include <QtCore/qstandardpaths.h>
+#include <QtCore/qtemporarydir.h>
+
+class TestLoadRequest : public WPEQtTest {
+Q_OBJECT
+private:
+    void main() override;
+};
+
+void TestLoadRequest::main()
+{
+    {
+        QString cacheLocation = QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
+        QTemporaryFile file(cacheLocation + QStringLiteral("/XXXXXXfile.html"));
+        QVERIFY2(file.open(), qPrintable(QStringLiteral("Cannot create temporary file:") + file.errorString()));
+
+        file.write("<html><head><title>FooBar</title></head><body />");
+        const QString fileName = file.fileName();
+        file.close();
+        const QUrl url = QUrl::fromLocalFile(fileName);
+        QSignalSpy loadChangedSignalSpy(m_view, SIGNAL(loadingChanged(WPEQtViewLoadRequest*)));
+        m_view->setUrl(url);
+        waitForLoadSucceeded(m_view);
+        QVERIFY(!m_view->isLoading());
+        QCOMPARE(m_view->loadProgress(), 100);
+        QCOMPARE(m_view->title(), QStringLiteral("FooBar"));
+        QCOMPARE(m_view->url(), url);
+        QCOMPARE(loadChangedSignalSpy.count(), 2);
+    }
+
+    {
+        QSignalSpy loadChangedSignalSpy(m_view, SIGNAL(loadingChanged(WPEQtViewLoadRequest*)));
+        m_view->setUrl(QUrl("file://IDONTEXIST.html"));
+        waitForLoadFailed(m_view);
+        QCOMPARE(loadChangedSignalSpy.count(), 2);
+    }
+}
+
+QTEST_APPLESS_MAIN(TestLoadRequest)
+#include "TestLoadRequest.moc"
diff --git a/Tools/TestWebKitAPI/Tests/WPEQt/TestRunJavaScript.cpp b/Tools/TestWebKitAPI/Tests/WPEQt/TestRunJavaScript.cpp
new file mode 100644 (file)
index 0000000..ec24dd6
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2018, 2019 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2,1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include "WPEQtTest.h"
+
+class TestRunJavaScript : public WPEQtTest {
+    Q_OBJECT
+private:
+    void main() override;
+};
+
+void TestRunJavaScript::main()
+{
+    const QString title = QString(QLatin1String("WebViewTitle"));
+    m_view->loadHtml(QString("<html><head><title>%1</title></head><body /></html>").arg(title));
+
+    waitForLoadSucceeded(m_view);
+    QCOMPARE(m_view->loadProgress(), 100);
+    QVERIFY(!m_view->isLoading());
+    QCOMPARE(m_view->title(), title);
+    const QString tstProperty = QString(QLatin1String("Qt.tst_data"));
+    QJSValue callback = m_engine.evaluate(QString("function(result) { %1 = result; }").arg(tstProperty));
+    QVERIFY2(!callback.isError(), qPrintable(callback.toString()));
+    QVERIFY(!callback.isUndefined());
+    QVERIFY(callback.isCallable());
+    m_view->runJavaScript(QString(QLatin1String("document.title")), callback);
+    QTRY_COMPARE(m_engine.evaluate(tstProperty).toString(), title);
+}
+
+QTEST_APPLESS_MAIN(TestRunJavaScript)
+#include "TestRunJavaScript.moc"
diff --git a/Tools/TestWebKitAPI/Tests/WPEQt/WPEQtTest.cpp b/Tools/TestWebKitAPI/Tests/WPEQt/WPEQtTest.cpp
new file mode 100644 (file)
index 0000000..92d61d2
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2018, 2019 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2,1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "WPEQtTest.h"
+
+bool waitForSignal(QObject* obj, const char* signal, int timeout)
+{
+    QEventLoop loop;
+    QObject::connect(obj, signal, &loop, SLOT(quit()));
+    QTimer timer;
+    QSignalSpy timeoutSpy(&timer, SIGNAL(timeout()));
+    if (timeout > 0) {
+        QObject::connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
+        timer.setSingleShot(true);
+        timer.start(timeout);
+    }
+    loop.exec();
+    return timeoutSpy.isEmpty();
+}
+
+#include "WPEQtTest.moc"
diff --git a/Tools/TestWebKitAPI/Tests/WPEQt/WPEQtTest.h b/Tools/TestWebKitAPI/Tests/WPEQt/WPEQtTest.h
new file mode 100644 (file)
index 0000000..0f51a74
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2018, 2019 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2,1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+// WPEQt has to be included before the remaining Qt headers, because of epoxy.
+#include <wpe/qt/WPEQtView.h>
+#include <wpe/qt/WPEQtViewLoadRequest.h>
+
+#include <QEventLoop>
+#include <QQmlApplicationEngine>
+#include <QQuickWindow>
+#include <QSignalSpy>
+#include <QTimer>
+#include <QtTest/QTest>
+
+class LoadSpy : public QEventLoop {
+    Q_OBJECT
+
+public:
+    LoadSpy(WPEQtView *webView)
+    {
+        connect(webView, SIGNAL(loadingChanged(WPEQtViewLoadRequest*)), SLOT(onLoadingChanged(WPEQtViewLoadRequest*)));
+    }
+
+    ~LoadSpy() { }
+
+Q_SIGNALS:
+    void loadSucceeded();
+    void loadFailed();
+
+private Q_SLOTS:
+    void onLoadingChanged(WPEQtViewLoadRequest *loadRequest)
+    {
+        if (loadRequest->status() == WPEQtView::LoadSucceededStatus)
+            Q_EMIT loadSucceeded();
+        else if (loadRequest->status() == WPEQtView::LoadFailedStatus)
+            Q_EMIT loadFailed();
+    }
+};
+
+class LoadStartedCatcher : public QObject {
+    Q_OBJECT
+
+public:
+    LoadStartedCatcher(WPEQtView *webView)
+        : m_webView(webView)
+    {
+        connect(m_webView, SIGNAL(loadingChanged(WPEQtViewLoadRequest*)), this, SLOT(onLoadingChanged(WPEQtViewLoadRequest*)));
+    }
+
+    virtual ~LoadStartedCatcher() { }
+
+public Q_SLOTS:
+    void onLoadingChanged(WPEQtViewLoadRequest *loadRequest)
+    {
+        if (loadRequest->status() == WPEQtView::LoadStartedStatus)
+            QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
+    }
+
+Q_SIGNALS:
+    void finished();
+
+private:
+    WPEQtView* m_webView;
+};
+
+bool waitForSignal(QObject*, const char* signal, int timeout = 10000);
+
+inline bool waitForLoadSucceeded(WPEQtView* webView, int timeout = 10000)
+{
+    LoadSpy loadSpy(webView);
+    return waitForSignal(&loadSpy, SIGNAL(loadSucceeded()), timeout);
+}
+
+inline bool waitForLoadFailed(WPEQtView* webView, int timeout = 10000)
+{
+    LoadSpy loadSpy(webView);
+    return waitForSignal(&loadSpy, SIGNAL(loadFailed()), timeout);
+}
+
+class WPEQtTest: public QObject {
+    Q_OBJECT
+public:
+    WPEQtTest()
+        : m_argc(0)
+        , m_app(m_argc, nullptr)
+        , m_engine()
+    {
+        g_setenv("WEBKIT_EXEC_PATH", WEBKIT_EXEC_PATH, FALSE);
+        g_setenv("WEBKIT_INJECTED_BUNDLE_PATH", WEBKIT_INJECTED_BUNDLE_PATH, FALSE);
+
+        QQmlComponent component(&m_engine);
+        component.setData("import QtQuick 2.11\n"
+            "import QtQuick.Window 2.11\n"
+            "import org.wpewebkit.qtwpe 1.0\n"
+            "Window {\n"
+            "    id: main_window\n"
+            "    visible: true\n"
+            "    width: 1280\n"
+            "    height: 720\n"
+            "    WPEView {\n"
+            "        objectName: \"wpeview\"\n"
+            "        focus: true\n"
+            "        anchors.fill: parent\n"
+            "    }\n"
+            "}", QUrl());
+
+        QObject* object = component.create();
+        m_view = object->findChild<WPEQtView*>("wpeview");
+
+        QTRY_COMPARE(m_view->loadProgress(), 0);
+    }
+
+    virtual ~WPEQtTest()
+    {
+        delete m_view;
+    }
+
+private Q_SLOTS:
+    void run()
+    {
+        QObject::connect(m_view, &WPEQtView::webViewCreated, this, [this] {
+            main();
+            m_app.quit();
+        });
+        m_app.exec();
+    }
+
+private:
+    virtual void main() = 0;
+
+    int m_argc;
+    QGuiApplication m_app;
+
+protected:
+    WPEQtView* m_view { nullptr };
+    QQmlEngine m_engine;
+};
index f4c1e69..edb8a13 100644 (file)
@@ -21,9 +21,13 @@ set(WebKitGLibAPITests_SYSTEM_INCLUDE_DIRECTORIES
     ${LIBSOUP_INCLUDE_DIRS}
 )
 
-set(WebKitGLibAPITest_LIBRARIES
+set(WebKitAPITest_LIBRARIES
     JavaScriptCore
     WebKit
+)
+
+set(WebKitGLibAPITest_LIBRARIES
+    ${WebKitAPITest_LIBRARIES}
     WebKitGLibAPITestsCore
     ${GLIB_LIBRARIES}
     ${LIBSOUP_LIBRARIES}
@@ -145,3 +149,32 @@ ADD_WK2_TEST(TestDOMElement ${TOOLS_DIR}/TestWebKitAPI/Tests/WebKitGLib/TestDOME
 if (PORT STREQUAL "GTK")
     ADD_WK2_TEST(TestCookieManager ${TOOLS_DIR}/TestWebKitAPI/Tests/WebKitGLib/TestCookieManager.cpp)
 endif ()
+
+macro(ADD_WPE_QT_TEST test_name)
+    add_executable(${test_name} ${ARGN} ${TOOLS_DIR}/TestWebKitAPI/Tests/WPEQt/WPEQtTest.cpp)
+    set_target_properties(${test_name} PROPERTIES
+        RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/TestWebKitAPI/WPEQt
+        AUTOMOC ON
+        CXX_STANDARD 14
+    )
+    target_compile_definitions(${test_name} PUBLIC QT_NO_KEYWORDS=1)
+    target_link_libraries(${test_name} ${WPEQtAPITest_LIBRARIES})
+    target_include_directories(${test_name} SYSTEM PRIVATE ${WPEQtAPITests_INCLUDE_DIRECTORIES})
+    target_include_directories(${test_name} PRIVATE
+      ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_SOURCE_DIR}/Source/WebKit/UIProcess/API/wpe/qt)
+endmacro()
+
+if (PORT STREQUAL "WPE" AND ENABLE_WPE_QT_API)
+    set(WPEQtAPITests_INCLUDE_DIRECTORIES
+        ${Qt5_INCLUDE_DIRS}
+        ${Qt5Test_INCLUDE_DIRS}
+    )
+    set(WPEQtAPITest_LIBRARIES
+         ${WebKitAPITest_LIBRARIES}
+         Qt5::Test qtwpe
+    )
+    ADD_WPE_QT_TEST(TestLoad ${TOOLS_DIR}/TestWebKitAPI/Tests/WPEQt/TestLoad.cpp)
+    ADD_WPE_QT_TEST(TestLoadHtml ${TOOLS_DIR}/TestWebKitAPI/Tests/WPEQt/TestLoadHtml.cpp)
+    ADD_WPE_QT_TEST(TestLoadRequest ${TOOLS_DIR}/TestWebKitAPI/Tests/WPEQt/TestLoadRequest.cpp)
+    ADD_WPE_QT_TEST(TestRunJavaScript ${TOOLS_DIR}/TestWebKitAPI/Tests/WPEQt/TestRunJavaScript.cpp)
+endif ()
index 41440ed..35d35d4 100644 (file)
@@ -52,6 +52,10 @@ FLATPAK_REQ = [
     ("flatpak-builder", "0.10.0"),
 ]
 
+WPE_MANIFEST_MAP = {
+    "qt": "org.webkit.WPEQT.yaml",
+}
+
 scriptdir = os.path.abspath(os.path.dirname(__file__))
 _log = logging.getLogger(__name__)
 
@@ -498,6 +502,7 @@ class WebkitFlatpak:
                             dest="user_command")
         general.add_argument('--available', action='store_true', dest="check_available", help='Check if required dependencies are available.'),
         general.add_argument("--use-icecream", help="Use the distributed icecream (icecc) compiler.", action="store_true")
+        general.add_argument("--wpe-extension", action="store", dest="wpe_extension", help="WPE Extension to enable")
 
         debugoptions = parser.add_argument_group("Debugging")
         debugoptions.add_argument("--gdb", nargs="?", help="Activate gdb, passing extra args to it if wanted.")
@@ -553,6 +558,7 @@ class WebkitFlatpak:
         self.app_module = None
         self.flatpak_default_args = []
         self.check_available = False
+        self.wpe_extension = None
 
         # Default application to run in the sandbox
         self.command = None
@@ -596,7 +602,13 @@ class WebkitFlatpak:
             " --debug" if self.debug else " --release")
 
         self.name = "org.webkit.%s" % self.platform
-        self.manifest_path = os.path.abspath(os.path.join(scriptdir, '../flatpak/org.webkit.WebKit.yaml'))
+
+        if self.wpe_extension:
+            manifest_filename = WPE_MANIFEST_MAP[self.wpe_extension]
+        else:
+            manifest_filename = "org.webkit.WebKit.yaml"
+        self.manifest_path = os.path.abspath(os.path.join(scriptdir, '../flatpak/') + manifest_filename)
+
         self.build_name = self.name + "-generated"
 
         build_root = os.path.join(self.source_root, 'WebKitBuild')
@@ -679,6 +691,9 @@ class WebkitFlatpak:
                 "--bind-mount=/etc/perl=%s" % os.path.join(self.flatpak_build_path, "files/lib/perl"),
                 "--bind-mount=/run/host/%s=%s" % (tempfile.gettempdir(), tempfile.gettempdir()),
                 "--bind-mount=%s=%s" % (self.sandbox_source_root, self.source_root),
+                "--talk-name=org.a11y.Bus",
+                "--talk-name=org.gtk.vfs",
+                "--talk-name=org.gtk.vfs.*",
                 # We mount WebKitBuild/PORTNAME/BuildType to /app/webkit/WebKitBuild/BuildType
                 # so we can build WPE and GTK in a same source tree.
                 "--bind-mount=%s=%s" % (sandbox_build_path, self.build_path)]
@@ -708,6 +723,7 @@ class WebkitFlatpak:
                 "LANG",
                 "NUMBER_OF_PROCESSORS",
                 "CCACHE_PREFIX",
+                "QML2_IMPORT_PATH",
             ]
 
             if self.use_icecream:
index 247596e..bb603a9 100644 (file)
@@ -15,7 +15,7 @@
   sources:
     - type: git
       url: https://github.com/apache/httpd.git
-      branch: 2.4.33
+      branch: 2.4.37
     - type : file
       path : files/httpd-autogen.sh
       dest-filename : autogen.sh
     url: https://files.pythonhosted.org/packages/9e/17/1d4ed6e1a4c0918a0357dfa2fdbe26bf63f6e616013c04a14bce9fd33e40/pyaml-17.12.1.tar.gz
     sha256: 66623c52f34d83a2c0fc963e08e8b9d0c13d88404e3b43b1852ef71eda19afa3
 
+- name: python2-subprocess32
+  buildsystem: simple
+  build-commands:
+  - pip2 install --target=/app/lib/python2.7/site-packages/ .
+  sources:
+  - type: archive
+    url: https://files.pythonhosted.org/packages/be/2b/beeba583e9877e64db10b52a96915afc0feabf7144dcbf2a0d0ea68bf73d/subprocess32-3.5.3.tar.gz
+    sha256: 6bc82992316eef3ccff319b5033809801c0c3372709c5f6985299c88ac7225c3
+
 # GStreamer modules
 - name: libvpx
   no-autogen: true
@@ -90,7 +99,7 @@
       branch: v1.7.0
   config-opts:
     - --enable-pic
-    - --as=yasm
+    - --as=nasm
     - --disable-unit-tests
     - --size-limit=16384x16384
     - --enable-postproc
     - "--enable-static"
     - "--enable-pic"
     - "--disable-lavf"
+    - "--disable-asm"
   sources:
     - type: archive
       url: http://download.videolan.org/pub/x264/snapshots/x264-snapshot-20140212-2245-stable.tar.bz2
       path: ../gstreamer/patches/gst-plugins-bad-0002-aomenc-Handle-8-bit_depth-images-with-AOM_IMG_FMT_HI.patch
   config-opts:
     - -Ddisable_gtkdoc=true
+- name: ffmpeg
+  sources:
+    - type: archive
+      url: https://ffmpeg.org/releases/ffmpeg-3.4.5.tar.gz
+      sha256: 18f80cc9ca322134ed40d25d7489af954fa519b4e7e6289b7084f1b0a1cdf472
+  config-opts:
+    - --enable-static
+    - --enable-pic
+    - --disable-avdevice
+    - --disable-postproc
+    - --disable-swscale
+    - --disable-programs
+    - --disable-ffplay
+    - --disable-ffprobe
+    - --disable-ffmpeg
+    - --disable-encoder=flac
+    - --disable-protocols
+    - --disable-devices
+    - --disable-network
+    - --disable-hwaccels
+    - --disable-dxva2
+    - --disable-vdpau
+    - --disable-filters
+    - --enable-filter=yadif
+    - --disable-doc
+    - --disable-d3d11va
+    - --disable-dxva2
+    - --disable-audiotoolbox
+    - --disable-videotoolbox
+    - --disable-vaapi
+    - --disable-crystalhd
+    - --disable-mediacodec
+    - --disable-nvenc
+    - --disable-mmal
+    - --disable-omx
+    - --disable-omx-rpi
+    - --disable-cuda
+    - --disable-cuvid
+    - --disable-libmfx
+    - --disable-libnpp
+    - --disable-iconv
+    - --disable-jni
+    - --disable-v4l2_m2m
+    - --enable-optimizations
+- name: gst-libav
+  buildsystem: meson
+  builddir: true
+  sources:
+    - type: archive
+      url: https://gstreamer.freedesktop.org/src/gst-libav/gst-libav-1.14.4.tar.xz
+      sha256: dfd78591901df7853eab7e56a86c34a1b03635da0d3d56b89aa577f1897865da
+  config-opts:
+    - -Ddisable_gtkdoc=true
 
-- name: libgcrypt # Speedup libgrcypt
+- name: libgcrypt # Speedup libgcrypt
   sources:
     - type: git
       url: https://dev.gnupg.org/source/libgcrypt.git
index 3fa1c6b..4cbabba 100644 (file)
@@ -1,15 +1,4 @@
-- name: libwpe
-  buildsystem: cmake-ninja
-  sources:
-    - type: archive
-      url: https://wpewebkit.org/releases/libwpe-1.0.0.tar.xz
-      sha256: aff11612123f9ab85a8b9a4bcdfb3a7503eba0a0d2d96f2cdecd30e911091719
-- name: wpebackend-fdo
-  buildsystem: cmake-ninja
-  sources:
-    - type: archive
-      url: https://wpewebkit.org/releases/wpebackend-fdo-1.0.0.tar.xz
-      sha256: 7a747f87a1ae46d30144369050e3ce348b58986d04e1a139ba75c198fa636729
+- org.webkit.WPEModules.yaml
 - name: webkitgtk-test-fonts
   no-autogen: true
   sources:
index 3a4af78..962c967 100644 (file)
@@ -2,11 +2,11 @@
   buildsystem: cmake-ninja
   sources:
     - type: archive
-      url: https://wpewebkit.org/releases/libwpe-1.0.0.tar.xz
-      sha256: aff11612123f9ab85a8b9a4bcdfb3a7503eba0a0d2d96f2cdecd30e911091719
+      url: https://wpewebkit.org/releases/libwpe-1.1.0.tar.xz
+      sha256: 72e34ad754be11abd1a438cfe195d8d644c52105ab2b1c3b39dec6228bc776ce
 - name: wpebackend-fdo
   buildsystem: cmake-ninja
   sources:
     - type: archive
-      url: https://wpewebkit.org/releases/wpebackend-fdo-1.0.0.tar.xz
-      sha256: 7a747f87a1ae46d30144369050e3ce348b58986d04e1a139ba75c198fa636729
+      url: https://wpewebkit.org/releases/wpebackend-fdo-1.1.0.tar.xz
+      sha256: f6c72130d16e50860cb83eb0f6e109c76f1826d2c6bee39025fb3651941761e7
diff --git a/Tools/flatpak/org.webkit.WPEQT.yaml b/Tools/flatpak/org.webkit.WPEQT.yaml
new file mode 100644 (file)
index 0000000..8f15b95
--- /dev/null
@@ -0,0 +1,39 @@
+app-id: org.webkit.WPEQT
+runtime: org.kde.Platform
+runtime-version: "5.11"
+# Control the exact version of the Sdk/Runtime that is being used.
+sdk-hash: dd9e5d3b3134c24fc191226f058fc78c6bdf1c25fd7be38bea977fcb15307e95
+runtime-hash: 02ede84d3591a5ea8028204d86059bffaccb778159ec53c859bbc60d9e7025e8
+sdk: org.kde.Sdk
+command: %(COMMAND)s
+finish-args:
+  # Basically no sandboxing, the goal here is to make it flexible
+  # for developers, not really to isolate (openning all devices
+  # to allow acces video cameras until we have a portal at least).
+  - --share=ipc
+  - --socket=x11
+  - --socket=wayland
+  - --device=all
+  - --share=network
+  - --socket=pulseaudio
+  - --system-talk-name=org.freedesktop.GeoClue2
+  - --system-talk-name=org.a11y.Bus
+  - --filesystem=host
+  - --socket=system-bus
+  - --talk-name=org.freedesktop.Flatpak
+  - --env=GST_PRESET_PATH=/app/share/gstreamer-1.0/presets/
+build-options:
+  cflags: -O2 -g
+  cxxflags: -O2 -g
+  strip: false
+  no-debuginfo: true
+modules:
+  - org.webkit.CommonModules.yaml
+  - org.webkit.WPEModules.yaml
+
+  # This module is not actually built.
+  - name: org.webkit.WPEQT
+    buildsystem: cmake
+    sources:
+    - type: dir
+      path: /app/webkit/Source/WebKit/UIProcess/API/wpe/qt
index 145ca02..fe5b008 100644 (file)
@@ -17,6 +17,7 @@ finish-args:
   - --share=network
   - --socket=pulseaudio
   - --system-talk-name=org.freedesktop.GeoClue2
+  - --system-talk-name=org.a11y.Bus
   - --filesystem=host
   - --socket=system-bus
   - --talk-name=org.freedesktop.Flatpak
index 4da894a..7afe544 100755 (executable)
@@ -17,7 +17,6 @@
 # the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 # Boston, MA 02110-1301, USA.
 
-import subprocess
 import os
 import errno
 import sys
@@ -32,6 +31,10 @@ from webkitpy.common.host import Host
 from webkitpy.common.test_expectations import TestExpectations
 from webkitpy.common.timeout_context import Timeout
 
+if os.name == 'posix' and sys.version_info[0] < 3:
+    import subprocess32 as subprocess
+else:
+    import subprocess
 
 class TestRunner(object):
     TEST_DIRS = []
@@ -157,6 +160,29 @@ class TestRunner(object):
 
         return GLibTestRunner(test_program, timeout, is_slow_test, timeout * 10).run(skipped=self._test_cases_to_skip(test_program), env=self._test_env)
 
+    def _run_test_qt(self, test_program):
+        env = self._test_env
+        env['XDG_SESSION_TYPE'] = 'wayland'
+        env['QML2_IMPORT_PATH'] = common.library_build_path('qml')
+
+        name = os.path.basename(test_program)
+        try:
+            output = subprocess.check_output([test_program, ], stderr=subprocess.STDOUT,
+                                             env=env, timeout=self._options.timeout)
+        except subprocess.CalledProcessError, exc:
+            print(exc.output)
+            if exc.returncode > 0:
+                result = "FAIL"
+            elif exc.returncode < 0:
+                result = "CRASH"
+        except subprocess.TimeoutExpired, exp:
+            result = "TIMEOUT"
+            print(exp.output)
+        else:
+            result = "PASS"
+            print("**PASS** %s" % name)
+        return {name: result}
+
     def _get_tests_from_google_test_suite(self, test_program):
         try:
             output = subprocess.check_output([test_program, '--gtest_list_tests'], env=self._test_env)
@@ -222,6 +248,9 @@ class TestRunner(object):
     def is_google_test(self, test_program):
         raise NotImplementedError
 
+    def is_qt_test(self, test_program):
+        raise NotImplementedError
+
     def _run_test(self, test_program):
         if self.is_glib_test(test_program):
             return self._run_test_glib(test_program)
@@ -229,6 +258,9 @@ class TestRunner(object):
         if self.is_google_test(test_program):
             return self._run_google_test_suite(test_program)
 
+        if self.is_qt_test(test_program):
+            return self._run_test_qt(test_program)
+
         return {}
 
     def run_tests(self):