summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPiotr Wierciński <piotr.wiercinski@qt.io>2023-05-05 09:28:27 +0200
committerPiotr Wierciński <piotr.wiercinski@qt.io>2023-05-11 11:39:58 +0200
commitd30730e8aaf1b61272def0068cafb6a8992e92b7 (patch)
tree5eb3a2a051140e51adfbbbdd775997b02d7d8ed0
parentb489306ee5f21370f967b588125d45c05707b963 (diff)
wasm: Add QWebView support
Add QWebView support for WebAssembly platform. The underlying implementation is using <iframe> DOM element and is limited by it's API, hence not all features of QWebView can be implemented on wasm platform. The QWebView's behavior on WebAssembly platform may also depend on Cross-Origin policy settings. Fixes: QTBUG-75183 Change-Id: I41bd18d4d66c766858a8a7c71107a21c07b213db Reviewed-by: Mikołaj Boc <Mikolaj.Boc@qt.io>
-rw-r--r--src/plugins/CMakeLists.txt5
-rw-r--r--src/plugins/wasm/CMakeLists.txt19
-rw-r--r--src/plugins/wasm/qwasmwebview.cpp264
-rw-r--r--src/plugins/wasm/qwasmwebview_p.h100
-rw-r--r--src/plugins/wasm/qwasmwebviewplugin.cpp23
-rw-r--r--src/plugins/wasm/wasm.json3
6 files changed, 412 insertions, 2 deletions
diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt
index 1439748..510604a 100644
--- a/src/plugins/CMakeLists.txt
+++ b/src/plugins/CMakeLists.txt
@@ -3,9 +3,10 @@
if(ANDROID)
add_subdirectory(android)
-endif()
-if(NOT ANDROID AND (IOS OR MACOS))
+elseif(IOS OR MACOS)
add_subdirectory(darwin)
+elseif(WASM)
+ add_subdirectory(wasm)
endif()
if(WINRT)
add_subdirectory(winrt)
diff --git a/src/plugins/wasm/CMakeLists.txt b/src/plugins/wasm/CMakeLists.txt
new file mode 100644
index 0000000..27c4415
--- /dev/null
+++ b/src/plugins/wasm/CMakeLists.txt
@@ -0,0 +1,19 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## QWasmWebViewPlugin Plugin:
+#####################################################################
+
+qt_internal_add_plugin(QWasmWebViewPlugin
+ OUTPUT_NAME qtwebview_wasm
+ PLUGIN_TYPE webview
+ SOURCES
+ qwasmwebview.cpp qwasmwebview_p.h
+ qwasmwebviewplugin.cpp
+ LIBRARIES
+ Qt::CorePrivate
+ Qt::Core
+ Qt::Gui
+ Qt::WebViewPrivate
+)
diff --git a/src/plugins/wasm/qwasmwebview.cpp b/src/plugins/wasm/qwasmwebview.cpp
new file mode 100644
index 0000000..6325aca
--- /dev/null
+++ b/src/plugins/wasm/qwasmwebview.cpp
@@ -0,0 +1,264 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qwasmwebview_p.h"
+#include <private/qwebview_p.h>
+#include <private/qwebviewloadrequest_p.h>
+
+#include <QtCore/qmap.h>
+#include <QtGui/qguiapplication.h>
+#include <QtCore/qjsondocument.h>
+#include <QtCore/qjsonobject.h>
+#include <QtCore/qurl.h>
+#include <QtCore/qdebug.h>
+
+#include <QAbstractEventDispatcher>
+#include <QThread>
+
+#include <iostream>
+
+QT_BEGIN_NAMESPACE
+
+QWasmWebViewSettingsPrivate::QWasmWebViewSettingsPrivate(QObject *p) : QAbstractWebViewSettings(p)
+{
+}
+
+bool QWasmWebViewSettingsPrivate::localStorageEnabled() const
+{
+ qWarning("localStorageEnabled() not supported on this platform");
+ return false;
+}
+
+bool QWasmWebViewSettingsPrivate::javascriptEnabled() const
+{
+ qWarning("javascriptEnabled() not supported on this platform");
+ return false;
+}
+
+bool QWasmWebViewSettingsPrivate::localContentCanAccessFileUrls() const
+{
+ qWarning("localContentCanAccessFileUrls() not supported on this platform");
+ return false;
+}
+
+bool QWasmWebViewSettingsPrivate::allowFileAccess() const
+{
+ qWarning("allowFileAccess() not supported on this platform");
+ return false;
+}
+
+void QWasmWebViewSettingsPrivate::setLocalContentCanAccessFileUrls(bool enabled)
+{
+ Q_UNUSED(enabled);
+ qWarning("setLocalContentCanAccessFileUrls() not supported on this platform");
+}
+
+void QWasmWebViewSettingsPrivate::setJavascriptEnabled(bool enabled)
+{
+ Q_UNUSED(enabled);
+ qWarning("setJavascriptEnabled() not supported on this platform");
+}
+
+void QWasmWebViewSettingsPrivate::setLocalStorageEnabled(bool enabled)
+{
+ Q_UNUSED(enabled);
+ qWarning("setLocalStorageEnabled() not supported on this platform");
+}
+
+void QWasmWebViewSettingsPrivate::setAllowFileAccess(bool enabled)
+{
+ Q_UNUSED(enabled);
+ qWarning("setAllowFileAccess() not supported on this platform");
+}
+
+QWasmWebViewPrivate::QWasmWebViewPrivate(QObject *p) : QAbstractWebView(p), m_window(0)
+{
+ m_settings = new QWasmWebViewSettingsPrivate(this);
+}
+
+QWasmWebViewPrivate::~QWasmWebViewPrivate() { }
+
+QString QWasmWebViewPrivate::httpUserAgent() const
+{
+ if (m_iframe)
+ return QString::fromStdString(
+ (*m_iframe)["contentWindow"]["navigator"]["userAgent"].as<std::string>());
+ return QString();
+}
+
+void QWasmWebViewPrivate::setHttpUserAgent(const QString &userAgent)
+{
+ Q_UNUSED(userAgent);
+ qWarning("setHttpUserAgent() not supported on this platform");
+}
+
+QUrl QWasmWebViewPrivate::url() const
+{
+ if (m_iframe)
+ return QUrl(QString::fromStdString(
+ (*m_iframe)["contentWindow"]["location"]["href"].as<std::string>()));
+ return QUrl::fromUserInput("");
+}
+
+void QWasmWebViewPrivate::setUrl(const QUrl &url)
+{
+ m_currentUrl = url;
+ if (m_iframe) {
+ (*m_iframe).call<void>("removeAttribute", emscripten::val("srcdoc"));
+ (*m_iframe).set("src", m_currentUrl.toString().toStdString());
+ }
+}
+
+void QWasmWebViewPrivate::loadHtml(const QString &html, const QUrl &baseUrl)
+{
+ if (!baseUrl.isEmpty())
+ qWarning("Base URLs for loadHtml() are not supported on this platform.");
+
+ if (m_iframe)
+ (*m_iframe).set("srcdoc", html.toStdString());
+}
+
+bool QWasmWebViewPrivate::canGoBack() const
+{
+ qWarning("canGoBack() not supported on this platform");
+ return false;
+}
+
+void QWasmWebViewPrivate::goBack()
+{
+ qWarning("goBack() not supported on this platform");
+}
+
+bool QWasmWebViewPrivate::canGoForward() const
+{
+ qWarning("canGoForward() not supported on this platform");
+ return false;
+}
+
+void QWasmWebViewPrivate::goForward()
+{
+ qWarning("goForward() not supported on this platform");
+}
+
+void QWasmWebViewPrivate::reload()
+{
+ if (m_iframe) {
+ (*m_iframe)["contentWindow"]["location"].call<void>("reload");
+ setUrl(m_currentUrl);
+ // this order of operation is important, setting URL before reload()
+ // did not work, HTTP reload request was being cancelled by browser
+ }
+}
+
+void QWasmWebViewPrivate::stop()
+{
+ qWarning("stop() not supported on this platform");
+}
+
+QString QWasmWebViewPrivate::title() const
+{
+ if (m_iframe)
+ return QString::fromStdString(
+ (*m_iframe)["contentWindow"]["document"]["title"].as<std::string>());
+ return QString();
+}
+
+int QWasmWebViewPrivate::loadProgress() const
+{
+ qWarning("loadProgress() not supported on this platform");
+ return 100;
+}
+
+bool QWasmWebViewPrivate::isLoading() const
+{
+ qWarning("isLoading() not supported on this platform");
+ return false;
+}
+
+void QWasmWebViewPrivate::setParentView(QObject *view)
+{
+ m_window = qobject_cast<QWindow *>(view);
+}
+
+QObject *QWasmWebViewPrivate::parentView() const
+{
+ return m_window;
+}
+
+void QWasmWebViewPrivate::setGeometry(const QRect &geometry)
+{
+ m_geometry = geometry;
+ updateGeometry();
+}
+
+void QWasmWebViewPrivate::setVisibility(QWindow::Visibility visibility)
+{
+ setVisible(visibility != QWindow::Hidden ? true : false);
+}
+
+void QWasmWebViewPrivate::setVisible(bool visible)
+{
+ if (!m_iframe && m_window && m_window->handle())
+ initializeIFrame();
+
+ if (m_iframe)
+ (*m_iframe)["style"].set("display", visible ? "block" : "none");
+}
+
+void QWasmWebViewPrivate::setCookie(const QString &domain, const QString &name,
+ const QString &value)
+{
+ Q_UNUSED(domain);
+ Q_UNUSED(name);
+ Q_UNUSED(value);
+ qWarning("setCookie() not supported on this platform");
+}
+
+void QWasmWebViewPrivate::deleteCookie(const QString &domain, const QString &name)
+{
+ Q_UNUSED(domain);
+ Q_UNUSED(name);
+ qWarning("deleteCookie() not supported on this platform");
+}
+
+void QWasmWebViewPrivate::deleteAllCookies()
+{
+ qWarning("deleteAllCookies() not supported on this platform");
+}
+
+void QWasmWebViewPrivate::runJavaScriptPrivate(const QString &script, int callbackId)
+{
+ Q_UNUSED(script);
+ Q_UNUSED(callbackId);
+ qWarning("runJavaScriptPrivate() not supported on this platform");
+}
+
+QAbstractWebViewSettings *QWasmWebViewPrivate::getSettings() const
+{
+ return m_settings;
+}
+
+void QWasmWebViewPrivate::initializeIFrame()
+{
+ auto m_wasmWindow = dynamic_cast<QNativeInterface::Private::QWasmWindow *>(m_window->handle());
+ auto document = m_wasmWindow->document();
+ auto clientArea = m_wasmWindow->clientArea();
+
+ m_iframe = document.call<emscripten::val>("createElement", emscripten::val("iframe"));
+ clientArea.call<void>("appendChild", *m_iframe);
+ (*m_iframe)["style"].set("position", "absolute");
+ (*m_iframe)["style"].set("border", "none");
+ updateGeometry();
+}
+
+void QWasmWebViewPrivate::updateGeometry()
+{
+ if (m_iframe && m_geometry) {
+ (*m_iframe)["style"].set("width", std::to_string(m_geometry->width()) + "px");
+ (*m_iframe)["style"].set("height", std::to_string(m_geometry->height()) + "px");
+ (*m_iframe)["style"].set("top", std::to_string(m_geometry->top()) + "px");
+ (*m_iframe)["style"].set("left", std::to_string(m_geometry->left()) + "px");
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/wasm/qwasmwebview_p.h b/src/plugins/wasm/qwasmwebview_p.h
new file mode 100644
index 0000000..d71b73c
--- /dev/null
+++ b/src/plugins/wasm/qwasmwebview_p.h
@@ -0,0 +1,100 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QWASMWEBVIEW_P_H
+#define QWASMWEBVIEW_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qobject.h>
+#include <QtCore/qurl.h>
+#include <QtGui/qwindow.h>
+#include <QtGui/qpa/qplatformwindow.h>
+#include <QtGui/qpa/qplatformwindow_p.h>
+#include <emscripten/val.h>
+
+#include <private/qabstractwebview_p.h>
+
+#include <optional>
+
+QT_BEGIN_NAMESPACE
+
+class QWasmWebViewSettingsPrivate final : public QAbstractWebViewSettings
+{
+ Q_OBJECT
+public:
+ explicit QWasmWebViewSettingsPrivate(QObject *p = nullptr);
+
+ bool localStorageEnabled() const final;
+ bool javascriptEnabled() const final;
+ bool localContentCanAccessFileUrls() const final;
+ bool allowFileAccess() const final;
+
+public Q_SLOTS:
+ void setLocalContentCanAccessFileUrls(bool enabled) final;
+ void setJavascriptEnabled(bool enabled) final;
+ void setLocalStorageEnabled(bool enabled) final;
+ void setAllowFileAccess(bool enabled) final;
+};
+
+class QWasmWebViewPrivate final : public QAbstractWebView
+{
+ Q_OBJECT
+public:
+ explicit QWasmWebViewPrivate(QObject *p = nullptr);
+ ~QWasmWebViewPrivate() override;
+
+ QString httpUserAgent() const final;
+ void setHttpUserAgent(const QString &httpUserAgent) final;
+ QUrl url() const final;
+ void setUrl(const QUrl &url) final;
+ bool canGoBack() const final;
+ bool canGoForward() const final;
+ QString title() const final;
+ int loadProgress() const final;
+ bool isLoading() const final;
+
+ void setParentView(QObject *view) final;
+ QObject *parentView() const final;
+ void setGeometry(const QRect &geometry) final;
+ void setVisibility(QWindow::Visibility visibility) final;
+ void setVisible(bool visible) final;
+
+public Q_SLOTS:
+ void goBack() final;
+ void goForward() final;
+ void reload() final;
+ void stop() final;
+ void loadHtml(const QString &html, const QUrl &baseUrl = QUrl()) final;
+ void setCookie(const QString &domain, const QString &name, const QString &value) final;
+ void deleteCookie(const QString &domain, const QString &name) final;
+ void deleteAllCookies() final;
+
+protected:
+ void runJavaScriptPrivate(const QString& script,
+ int callbackId) final;
+ QAbstractWebViewSettings *getSettings() const final;
+
+private:
+ void initializeIFrame();
+ void updateGeometry();
+
+ QWasmWebViewSettingsPrivate *m_settings;
+ QWindow *m_window = nullptr;
+ std::optional<emscripten::val> m_iframe;
+ std::optional<QRect> m_geometry;
+ QUrl m_currentUrl;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWASMWEBVIEW_P_H
diff --git a/src/plugins/wasm/qwasmwebviewplugin.cpp b/src/plugins/wasm/qwasmwebviewplugin.cpp
new file mode 100644
index 0000000..0887099
--- /dev/null
+++ b/src/plugins/wasm/qwasmwebviewplugin.cpp
@@ -0,0 +1,23 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qwasmwebview_p.h"
+#include <private/qwebviewplugin_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWasmWebViewPlugin : public QWebViewPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QWebViewPluginInterface_iid FILE "wasm.json")
+
+public:
+ QAbstractWebView *create(const QString &key) const override
+ {
+ return key == QLatin1String("webview") ? new QWasmWebViewPrivate() : nullptr;
+ }
+};
+
+QT_END_NAMESPACE
+
+#include "qwasmwebviewplugin.moc"
diff --git a/src/plugins/wasm/wasm.json b/src/plugins/wasm/wasm.json
new file mode 100644
index 0000000..9f65fd4
--- /dev/null
+++ b/src/plugins/wasm/wasm.json
@@ -0,0 +1,3 @@
+{
+ "Keys": ["native"]
+}