summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/imports/plugins.qmltypes9
-rw-r--r--src/jar/src/org/qtproject/qt5/android/view/QtAndroidWebViewController.java35
-rw-r--r--src/webview/qquickwebview.cpp75
-rw-r--r--src/webview/qquickwebview.h7
-rw-r--r--src/webview/qwebview.cpp9
-rw-r--r--src/webview/qwebview_android.cpp53
-rw-r--r--src/webview/qwebview_android_p.h4
-rw-r--r--src/webview/qwebview_ios.mm8
-rw-r--r--src/webview/qwebview_ios_p.h4
-rw-r--r--src/webview/qwebview_p.h3
-rw-r--r--src/webview/qwebviewinterface_p.h3
-rw-r--r--tests/auto/webview/qwebview/qwebview.pro4
-rw-r--r--tests/auto/webview/qwebview/tst_qwebview.cpp31
13 files changed, 244 insertions, 1 deletions
diff --git a/src/imports/plugins.qmltypes b/src/imports/plugins.qmltypes
index 8acf6fd..a0a57bf 100644
--- a/src/imports/plugins.qmltypes
+++ b/src/imports/plugins.qmltypes
@@ -20,5 +20,14 @@ Module {
Method { name: "goForward" }
Method { name: "reload" }
Method { name: "stop" }
+ Method {
+ name: "runJavaScript"
+ Parameter { name: "script"; type: "string" }
+ Parameter { name: "callback"; type: "QJValue" }
+ }
+ Method {
+ name: "runJavaScript"
+ Parameter { name: "script"; type: "string" }
+ }
}
}
diff --git a/src/jar/src/org/qtproject/qt5/android/view/QtAndroidWebViewController.java b/src/jar/src/org/qtproject/qt5/android/view/QtAndroidWebViewController.java
index dd3cab0..404fa3a 100644
--- a/src/jar/src/org/qtproject/qt5/android/view/QtAndroidWebViewController.java
+++ b/src/jar/src/org/qtproject/qt5/android/view/QtAndroidWebViewController.java
@@ -63,12 +63,16 @@ public class QtAndroidWebViewController
private Method m_webViewOnPause = null;
private Method m_webSettingsSetDisplayZoomControls = null;
+ // API 19 methods
+ private Method m_webViewEvaluateJavascript = null;
+
// Native callbacks
private native void c_onPageFinished(long id, String url);
private native void c_onPageStarted(long id, String url, Bitmap icon);
private native void c_onProgressChanged(long id, int newProgress);
private native void c_onReceivedIcon(long id, Bitmap icon);
private native void c_onReceivedTitle(long id, String title);
+ private native void c_onRunJavaScriptResult(long id, long callbackId, String result);
private class QtAndroidWebViewClient extends WebViewClient
{
@@ -136,6 +140,11 @@ public class QtAndroidWebViewController
m_webViewOnResume = m_webView.getClass().getMethod("onResume");
m_webViewOnPause = m_webView.getClass().getMethod("onPause");
m_webSettingsSetDisplayZoomControls = webSettings.getClass().getMethod("setDisplayZoomControls", boolean.class);
+ if (Build.VERSION.SDK_INT > 18) {
+ m_webViewEvaluateJavascript = m_webView.getClass().getMethod("evaluateJavascript",
+ String.class,
+ ValueCallback.class);
+ }
} catch (Exception e) { /* Do nothing */ e.printStackTrace(); }
}
@@ -300,6 +309,32 @@ public class QtAndroidWebViewController
return progress[0];
}
+ public void runJavaScript(final String script, final long callbackId)
+ {
+ if (script == null)
+ return;
+
+ if (Build.VERSION.SDK_INT < 19 || m_webViewEvaluateJavascript == null)
+ return;
+
+ m_activity.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ m_webViewEvaluateJavascript.invoke(m_webView, script, callbackId == -1 ? null :
+ new ValueCallback<String>() {
+ @Override
+ public void onReceiveValue(String result) {
+ c_onRunJavaScriptResult(m_id, callbackId, result);
+ }
+ });
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ });
+ }
+
public String getUrl()
{
final String[] url = {""};
diff --git a/src/webview/qquickwebview.cpp b/src/webview/qquickwebview.cpp
index be51fb7..1803994 100644
--- a/src/webview/qquickwebview.cpp
+++ b/src/webview/qquickwebview.cpp
@@ -38,6 +38,38 @@
#include <QtQml/qqmlengine.h>
#include <QtCore/qmutex.h>
+namespace {
+
+class CallbackStorage
+{
+public:
+ int insertCallback(const QJSValue &callback)
+ {
+ QMutexLocker locker(&m_mtx);
+ const int nextId = qMax(++m_counter, 0);
+ if (nextId == 0)
+ m_counter = 1;
+
+ m_callbacks.insert(nextId, callback);
+ return nextId;
+ }
+
+ QJSValue takeCallback(int callbackId)
+ {
+ QMutexLocker lock(&m_mtx);
+ return m_callbacks.take(callbackId);
+ }
+
+private:
+ QMutex m_mtx;
+ int m_counter;
+ QHash<int, QJSValue> m_callbacks;
+};
+
+} // namespace
+
+Q_GLOBAL_STATIC(CallbackStorage, callbacks)
+
/*!
\qmltype WebView
\inqmlmodule QtWebView
@@ -64,6 +96,7 @@ QQuickWebView::QQuickWebView(QQuickItem *parent)
connect(m_webView.data(), &QWebView::loadingChanged, this, &QQuickWebView::loadingChanged);
connect(m_webView.data(), &QWebView::loadProgressChanged, this, &QQuickWebView::loadProgressChanged);
connect(m_webView.data(), &QWebView::requestFocus, this, &QQuickWebView::onFocusRequest);
+ connect(m_webView.data(), &QWebView::javaScriptResult, this, &QQuickWebView::onRunJavaScriptResult);
}
QQuickWebView::~QQuickWebView()
@@ -185,6 +218,48 @@ void QQuickWebView::stop()
m_webView->stop();
}
+/*!
+ \qmlmethod void QtWebView::WebView::runJavaScript(string script, variant callback)
+
+ Runs the specified JavaScript.
+ In case a callback function is provided, it will be invoked after the script finished running.
+
+ \badcode
+ runJavaScript("document.title", function(result) { console.log(result); });
+ \endcode
+*/
+void QQuickWebView::runJavaScript(const QString &script, const QJSValue &callback)
+{
+ const int callbackId = callback.isCallable() ? callbacks->insertCallback(callback)
+ : -1;
+ runJavaScriptPrivate(script, callbackId);
+}
+
+void QQuickWebView::runJavaScriptPrivate(const QString &script, int callbackId)
+{
+ m_webView->runJavaScriptPrivate(script, callbackId);
+}
+
+void QQuickWebView::onRunJavaScriptResult(int id, const QVariant &variant)
+{
+ if (id == -1)
+ return;
+
+ QJSValue callback = callbacks->takeCallback(id);
+ if (callback.isUndefined())
+ return;
+
+ QQmlEngine *engine = qmlEngine(this);
+ if (engine == 0) {
+ qWarning() << "No JavaScript engine, unable to handle JavaScript callback!";
+ return;
+ }
+
+ QJSValueList args;
+ args.append(engine->toScriptValue(variant));
+ callback.call(args);
+}
+
void QQuickWebView::onFocusRequest(bool focus)
{
setFocus(focus);
diff --git a/src/webview/qquickwebview.h b/src/webview/qquickwebview.h
index aab13b1..c36cc9d 100644
--- a/src/webview/qquickwebview.h
+++ b/src/webview/qquickwebview.h
@@ -84,6 +84,8 @@ public Q_SLOTS:
void goForward() Q_DECL_OVERRIDE;
void reload() Q_DECL_OVERRIDE;
void stop() Q_DECL_OVERRIDE;
+ void runJavaScript(const QString& script,
+ const QJSValue &callback = QJSValue());
Q_SIGNALS:
void titleChanged();
@@ -91,7 +93,12 @@ Q_SIGNALS:
void loadingChanged();
void loadProgressChanged();
+protected:
+ void runJavaScriptPrivate(const QString& script,
+ int callbackId) Q_DECL_OVERRIDE;
+
private Q_SLOTS:
+ void onRunJavaScriptResult(int id, const QVariant &variant);
void onFocusRequest(bool focus);
private:
diff --git a/src/webview/qwebview.cpp b/src/webview/qwebview.cpp
index 9d55331..1865c7e 100644
--- a/src/webview/qwebview.cpp
+++ b/src/webview/qwebview.cpp
@@ -48,6 +48,8 @@ QWebView::QWebView(QObject *p)
connect(d, SIGNAL(loadingChanged()), this, SIGNAL(loadingChanged()));
connect(d, SIGNAL(loadProgressChanged()), this, SIGNAL(loadProgressChanged()));
connect(d, SIGNAL(requestFocus(bool)), this, SIGNAL(requestFocus(bool)));
+ connect(d, &QWebViewPrivate::javaScriptResult,
+ this, &QWebView::javaScriptResult);
}
QWebView::~QWebView()
@@ -150,6 +152,13 @@ void QWebView::setFocus(bool focus)
d->setFocus(focus);
}
+void QWebView::runJavaScriptPrivate(const QString &script,
+ int callbackId)
+{
+ Q_D(QWebView);
+ d->runJavaScriptPrivate(script, callbackId);
+}
+
void QWebView::init()
{
Q_D(QWebView);
diff --git a/src/webview/qwebview_android.cpp b/src/webview/qwebview_android.cpp
index be87a56..1ff4271 100644
--- a/src/webview/qwebview_android.cpp
+++ b/src/webview/qwebview_android.cpp
@@ -45,6 +45,7 @@
#include <QtCore/qjsondocument.h>
#include <QtCore/qjsonobject.h>
#include <QtCore/qurl.h>
+#include <QtCore/qdebug.h>
QT_BEGIN_NAMESPACE
@@ -152,6 +153,24 @@ void QAndroidWebViewPrivate::setVisibility(QWindow::Visibility visibility)
m_window->setVisibility(visibility);
}
+void QAndroidWebViewPrivate::runJavaScriptPrivate(const QString &script,
+ int callbackId)
+{
+ if (QtAndroidPrivate::androidSdkVersion() < 19) {
+ qWarning() << "runJavaScript() requires API level 19 or higher.";
+ if (callbackId == -1)
+ return;
+
+ // Emit signal here to remove the callback.
+ Q_EMIT javaScriptResult(callbackId, QVariant());
+ }
+
+ m_viewController.callMethod<void>("runJavaScript",
+ "(Ljava/lang/String;J)V",
+ static_cast<jstring>(QJNIObjectPrivate::fromString(script).object()),
+ callbackId);
+}
+
void QAndroidWebViewPrivate::setVisible(bool visible)
{
m_window->setVisible(visible);
@@ -195,6 +214,37 @@ void QAndroidWebViewPrivate::onApplicationStateChanged(Qt::ApplicationState stat
QT_END_NAMESPACE
+static void c_onRunJavaScriptResult(JNIEnv *env,
+ jobject thiz,
+ jlong id,
+ jlong callbackId,
+ jstring result)
+{
+ Q_UNUSED(env)
+ Q_UNUSED(thiz)
+
+ const WebViews &wv = (*g_webViews);
+ QAndroidWebViewPrivate *wc = static_cast<QAndroidWebViewPrivate *>(wv[id]);
+ if (!wc)
+ return;
+
+ const QString &resultString = QJNIObjectPrivate(result).toString();
+
+ // The result string is in JSON format, lets parse it to see what we got.
+ QJsonValue jsonValue;
+ const QByteArray &jsonData = "{ \"data\": " + resultString.toUtf8() + " }";
+ QJsonParseError error;
+ const QJsonDocument &jsonDoc = QJsonDocument::fromJson(jsonData, &error);
+ if (error.error == QJsonParseError::NoError && jsonDoc.isObject()) {
+ const QJsonObject &object = jsonDoc.object();
+ jsonValue = object.value(QStringLiteral("data"));
+ }
+
+ Q_EMIT wc->javaScriptResult(int(callbackId),
+ jsonValue.isNull() ? resultString
+ : jsonValue.toVariant());
+}
+
static void c_onPageFinished(JNIEnv *env,
jobject thiz,
jlong id,
@@ -315,7 +365,8 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* /*reserved*/)
{"c_onPageStarted", "(JLjava/lang/String;Landroid/graphics/Bitmap;)V", reinterpret_cast<void *>(c_onPageStarted)},
{"c_onProgressChanged", "(JI)V", reinterpret_cast<void *>(c_onProgressChanged)},
{"c_onReceivedIcon", "(JLandroid/graphics/Bitmap;)V", reinterpret_cast<void *>(c_onReceivedIcon)},
- {"c_onReceivedTitle", "(JLjava/lang/String;)V", reinterpret_cast<void *>(c_onReceivedTitle)}
+ {"c_onReceivedTitle", "(JLjava/lang/String;)V", reinterpret_cast<void *>(c_onReceivedTitle)},
+ {"c_onRunJavaScriptResult", "(JJLjava/lang/String;)V", reinterpret_cast<void *>(c_onRunJavaScriptResult)}
};
const int nMethods = sizeof(methods) / sizeof(methods[0]);
diff --git a/src/webview/qwebview_android_p.h b/src/webview/qwebview_android_p.h
index 751b44f..45a0213 100644
--- a/src/webview/qwebview_android_p.h
+++ b/src/webview/qwebview_android_p.h
@@ -83,6 +83,10 @@ public Q_SLOTS:
void reload() Q_DECL_OVERRIDE;
void stop() Q_DECL_OVERRIDE;
+protected:
+ void runJavaScriptPrivate(const QString& script,
+ int callbackId) Q_DECL_OVERRIDE;
+
private Q_SLOTS:
void onApplicationStateChanged(Qt::ApplicationState state);
diff --git a/src/webview/qwebview_ios.mm b/src/webview/qwebview_ios.mm
index faa82c3..b9fdff9 100644
--- a/src/webview/qwebview_ios.mm
+++ b/src/webview/qwebview_ios.mm
@@ -283,4 +283,12 @@ void QIosWebViewPrivate::reload()
[uiWebView reload];
}
+void QIosWebViewPrivate::runJavaScriptPrivate(const QString &script, int callbackId)
+{
+ // ### TODO needs more async
+ NSString *result = [uiWebView stringByEvaluatingJavaScriptFromString:script.toNSString()];
+ if (callbackId != -1)
+ Q_EMIT javaScriptResult(callbackId, QString::fromNSString(result));
+}
+
QT_END_NAMESPACE
diff --git a/src/webview/qwebview_ios_p.h b/src/webview/qwebview_ios_p.h
index 774d707..8302a68 100644
--- a/src/webview/qwebview_ios_p.h
+++ b/src/webview/qwebview_ios_p.h
@@ -86,6 +86,10 @@ public Q_SLOTS:
void reload() Q_DECL_OVERRIDE;
void stop() Q_DECL_OVERRIDE;
+protected:
+ void runJavaScriptPrivate(const QString& script,
+ int callbackId) Q_DECL_OVERRIDE;
+
public:
UIWebView *uiWebView;
UIGestureRecognizer *m_recognizer;
diff --git a/src/webview/qwebview_p.h b/src/webview/qwebview_p.h
index 2a92620..578403a 100644
--- a/src/webview/qwebview_p.h
+++ b/src/webview/qwebview_p.h
@@ -93,10 +93,13 @@ Q_SIGNALS:
void urlChanged();
void loadingChanged();
void loadProgressChanged();
+ void javaScriptResult(int id, const QVariant &result);
void requestFocus(bool focus);
protected:
void init();
+ void runJavaScriptPrivate(const QString &script,
+ int callbackId) Q_DECL_OVERRIDE;
private:
friend class QQuickViewController;
diff --git a/src/webview/qwebviewinterface_p.h b/src/webview/qwebviewinterface_p.h
index 86d370c..ef02042 100644
--- a/src/webview/qwebviewinterface_p.h
+++ b/src/webview/qwebviewinterface_p.h
@@ -76,6 +76,9 @@ public:
virtual void goForward() = 0;
virtual void stop() = 0;
virtual void reload() = 0;
+
+ virtual void runJavaScriptPrivate(const QString &script,
+ int callbackId) = 0;
};
#endif // QWEBVIEWINTERFACE_H
diff --git a/tests/auto/webview/qwebview/qwebview.pro b/tests/auto/webview/qwebview/qwebview.pro
index 3cfaca9..198bb21 100644
--- a/tests/auto/webview/qwebview/qwebview.pro
+++ b/tests/auto/webview/qwebview/qwebview.pro
@@ -2,6 +2,10 @@ CONFIG += testcase parallel_test
TARGET = tst_qwebview
osx:CONFIG -= app_bundle
+!qtHaveModule(quick) {
+ DEFINES += QT_NO_QQUICKWEBVIEW_TESTS
+}
+
!android: qtHaveModule(webengine) {
QT += webengine webengine-private
DEFINES += QT_WEBVIEW_WEBENGINE_BACKEND
diff --git a/tests/auto/webview/qwebview/tst_qwebview.cpp b/tests/auto/webview/qwebview/tst_qwebview.cpp
index e9f8d51..9702324 100644
--- a/tests/auto/webview/qwebview/tst_qwebview.cpp
+++ b/tests/auto/webview/qwebview/tst_qwebview.cpp
@@ -41,6 +41,11 @@
#include <QtCore/qfileinfo.h>
#include <QtWebView/private/qwebview_p.h>
#include <QtQml/qqmlengine.h>
+
+#ifndef QT_NO_QQUICKWEBVIEW_TESTS
+#include <QtWebView/qquickwebview.h>
+#endif // QT_NO_QQUICKWEBVIEW_TESTS
+
#ifdef QT_WEBVIEW_WEBENGINE_BACKEND
#include <QtWebEngine>
#endif // QT_WEBVIEW_WEBENGINE_BACKEND
@@ -54,6 +59,7 @@ public:
private slots:
void initTestCase();
void load();
+ void runJavaScript();
private:
const QString m_cacheLocation;
@@ -92,6 +98,31 @@ void tst_QWebView::load()
QCOMPARE(view.url(), url);
}
+void tst_QWebView::runJavaScript()
+{
+#ifndef QT_NO_QQUICKWEBVIEW_TESTS
+ const QString tstProperty = QString(QLatin1String("Qt.tst_data"));
+ const QString title = QString(QLatin1String("WebViewTitle"));
+
+ QQuickWebView view;
+ QQmlEngine engine;
+ QQmlContext *rootContext = engine.rootContext();
+ QQmlEngine::setContextForObject(&view, rootContext);
+
+ QCOMPARE(view.loadProgress(), 0);
+ view.loadHtml(QString("<html><head><title>%1</title></head><body /></html>").arg(title));
+ QTRY_COMPARE(view.loadProgress(), 100);
+ QTRY_VERIFY(!view.isLoading());
+ QCOMPARE(view.title(), title);
+ QJSValue callback = engine.evaluate(QString("function(result) { %1 = result; }").arg(tstProperty));
+ QVERIFY2(!callback.isError(), qPrintable(callback.toString()));
+ QVERIFY(!callback.isUndefined());
+ QVERIFY(callback.isCallable());
+ view.runJavaScript(QString(QLatin1String("document.title")), callback);
+ QTRY_COMPARE(engine.evaluate(tstProperty).toString(), title);
+#endif // QT_NO_QQUICKWEBVIEW_TESTS
+}
+
QTEST_MAIN(tst_QWebView)
#include "tst_qwebview.moc"