Modified: trunk/Source/WebKit2/UIProcess/API/qt/qquickwebview.cpp (116419 => 116420)
--- trunk/Source/WebKit2/UIProcess/API/qt/qquickwebview.cpp 2012-05-08 14:09:37 UTC (rev 116419)
+++ trunk/Source/WebKit2/UIProcess/API/qt/qquickwebview.cpp 2012-05-08 14:12:55 UTC (rev 116420)
@@ -52,10 +52,14 @@
#include "qwebviewportinfo_p.h"
#include <_javascript_Core/InitializeThreading.h>
+#include <_javascript_Core/JSBase.h>
+#include <_javascript_Core/JSRetainPtr.h>
#include <QDateTime>
+#include <QtQml/QJSValue>
+#include <WKOpenPanelResultListener.h>
+#include <WKSerializedScriptValue.h>
#include <WebCore/IntPoint.h>
#include <WebCore/IntRect.h>
-#include <WKOpenPanelResultListener.h>
#include <wtf/Assertions.h>
#include <wtf/MainThread.h>
#include <wtf/text/WTFString.h>
@@ -68,6 +72,105 @@
static const qreal kAxisLockVelocityThreshold = 300;
static const qreal kAxisLockVelocityDirectionThreshold = 50;
+struct JSCallbackClosure {
+ QPointer<QObject> receiver;
+ QByteArray method;
+ QJSValue value;
+};
+
+static inline QString toQString(JSStringRef string)
+{
+ return QString(reinterpret_cast<const QChar*>(JSStringGetCharactersPtr(string)), JSStringGetLength(string));
+}
+
+static inline QJSValue toQJSValue(JSStringRef string)
+{
+ return QJSValue(toQString(string));
+}
+
+static QJSValue buildQJSValue(QJSEngine* engine, JSGlobalContextRef context, JSValueRef value, int depth)
+{
+ QJSValue var;
+ JSValueRef exception = 0;
+
+ if (depth > 10)
+ return var;
+
+ switch (JSValueGetType(context, value)) {
+ case kJSTypeBoolean:
+ var = QJSValue(JSValueToBoolean(context, value));
+ break;
+ case kJSTypeNumber:
+ {
+ double number = JSValueToNumber(context, value, &exception);
+ if (!exception)
+ var = QJSValue(number);
+ }
+ break;
+ case kJSTypeString:
+ {
+ JSRetainPtr<JSStringRef> string = JSValueToStringCopy(context, value, &exception);
+ if (!exception)
+ var = toQJSValue(string.get());
+ }
+ break;
+ case kJSTypeObject:
+ {
+ JSObjectRef obj = JSValueToObject(context, value, &exception);
+
+ JSPropertyNameArrayRef names = JSObjectCopyPropertyNames(context, obj);
+ size_t length = JSPropertyNameArrayGetCount(names);
+
+ var = engine->newObject();
+
+ for (size_t i = 0; i < length; ++i) {
+ JSRetainPtr<JSStringRef> name = JSPropertyNameArrayGetNameAtIndex(names, i);
+ JSValueRef property = JSObjectGetProperty(context, obj, name.get(), &exception);
+
+ if (!exception) {
+ QJSValue value = buildQJSValue(engine, context, property, depth + 1);
+ var.setProperty(toQString(name.get()), value);
+ }
+ }
+ }
+ break;
+ }
+ return var;
+}
+
+static void _javascript_Callback(WKSerializedScriptValueRef valueRef, WKErrorRef, void* data)
+{
+ JSCallbackClosure* closure = reinterpret_cast<JSCallbackClosure*>(data);
+
+ if (closure->method.size())
+ QMetaObject::invokeMethod(closure->receiver, closure->method);
+ else {
+ QJSValue function = closure->value;
+
+ // If a callable function is supplied, we build a _javascript_ value accessible
+ // in the QML engine, and calls the function with that.
+ if (function.isCallable()) {
+ QJSValue var;
+ if (valueRef) {
+ // FIXME: Slow but OK for now.
+ JSGlobalContextRef context = JSGlobalContextCreate(0);
+
+ JSValueRef exception = 0;
+ JSValueRef value = WKSerializedScriptValueDeserialize(valueRef, context, &exception);
+ var = buildQJSValue(function.engine(), context, value, /* depth */ 0);
+
+ JSGlobalContextRelease(context);
+ }
+
+ QList<QJSValue> args;
+ args.append(var);
+ function.call(args);
+ }
+ }
+
+ delete closure;
+}
+
static QQuickWebViewPrivate* createPrivateObject(QQuickWebView* publicObject)
{
if (s_flickableViewportEnabled)
@@ -1171,6 +1274,24 @@
emit devicePixelRatioChanged();
}
+/*!
+ \internal
+
+ \qmlmethod void WebViewExperimental::evaluateJavaScript(string script [, function(result)])
+
+ \brief Evaluates the specified _javascript_ and, if supplied, calls a function with the result.
+*/
+
+void QQuickWebViewExperimental::evaluateJavaScript(const QString& script, const QJSValue& value)
+{
+ JSCallbackClosure* closure = new JSCallbackClosure;
+
+ closure->receiver = this;
+ closure->value = value;
+
+ d_ptr->webPageProxy.get()->runJavaScriptInMainFrame(script, ScriptValueCallback::create(closure, _javascript_Callback));
+}
+
QQuickUrlSchemeDelegate* QQuickWebViewExperimental::schemeDelegates_At(QDeclarativeListProperty<QQuickUrlSchemeDelegate>* property, int index)
{
const QObjectList children = property->object->children();
@@ -1725,24 +1846,14 @@
d->setZoomFactor(factor);
}
-struct JSCallbackClosure {
- QPointer<QObject> receiver;
- QByteArray method;
-};
-
-static void _javascript_Callback(WKSerializedScriptValueRef, WKErrorRef, void* context)
-{
- JSCallbackClosure* closure = reinterpret_cast<JSCallbackClosure*>(context);
- QMetaObject::invokeMethod(closure->receiver, closure->method);
- delete closure;
-}
-
void QQuickWebView::runJavaScriptInMainFrame(const QString &script, QObject *receiver, const char *method)
{
Q_D(QQuickWebView);
+
JSCallbackClosure* closure = new JSCallbackClosure;
closure->receiver = receiver;
closure->method = method;
+
d->webPageProxy.get()->runJavaScriptInMainFrame(script, ScriptValueCallback::create(closure, _javascript_Callback));
}
Modified: trunk/Source/WebKit2/UIProcess/API/qt/qquickwebview_p.h (116419 => 116420)
--- trunk/Source/WebKit2/UIProcess/API/qt/qquickwebview_p.h 2012-05-08 14:09:37 UTC (rev 116419)
+++ trunk/Source/WebKit2/UIProcess/API/qt/qquickwebview_p.h 2012-05-08 14:12:55 UTC (rev 116420)
@@ -65,6 +65,7 @@
class QPainter;
class QUrl;
class QQuickFlickable;
+class QJSValue;
QT_END_NAMESPACE
@@ -332,6 +333,7 @@
void goBackTo(int index);
void goForwardTo(int index);
void postMessage(const QString&);
+ void evaluateJavaScript(const QString& script, const QJSValue& value = QJSValue());
Q_SIGNALS:
void alertDialogChanged();
Added: trunk/Source/WebKit2/UIProcess/API/qt/tests/qmltests/WebView/tst_evaluateJavaScript.qml (0 => 116420)
--- trunk/Source/WebKit2/UIProcess/API/qt/tests/qmltests/WebView/tst_evaluateJavaScript.qml (rev 0)
+++ trunk/Source/WebKit2/UIProcess/API/qt/tests/qmltests/WebView/tst_evaluateJavaScript.qml 2012-05-08 14:12:55 UTC (rev 116420)
@@ -0,0 +1,87 @@
+import QtQuick 2.0
+import QtTest 1.0
+import QtWebKit 3.0
+import QtWebKit.experimental 1.0
+import "../common"
+
+Item {
+ TestWebView {
+ id: webView
+ property variant lastMessage
+ property variant lastResult
+
+ signal resultReceived
+
+ experimental.preferences.navigatorQtObjectEnabled: true
+ experimental.onMessageReceived: {
+ lastMessage = message
+ }
+ }
+
+ SignalSpy {
+ id: messageSpy
+ target: webView.experimental
+ signalName: "messageReceived"
+ }
+
+ SignalSpy {
+ id: resultSpy
+ target: webView
+ signalName: "resultReceived"
+ }
+
+ TestCase {
+ name: "_javascript_Evaluation"
+
+ function init() {
+ messageSpy.clear()
+ webView.lastMessage = null
+
+ resultSpy.clear()
+ webView.lastResult = null
+ }
+
+ function test_basic() {
+ messageSpy.clear()
+ webView.url = ""
+ verify(webView.waitForLoadSucceeded())
+
+ webView.experimental.evaluateJavaScript(
+ "navigator.qt._onmessage_ = function(message) {" +
+ " var result = message.data.split('');" +
+ " result = result.reverse().join('');" +
+ " navigator.qt.postMessage(result);" +
+ "}");
+
+ webView.experimental.postMessage("DLROW OLLEH");
+ messageSpy.wait()
+ compare(webView.lastMessage.data, "HELLO WORLD")
+ }
+
+ function test_propertyObjectWithChild() {
+ messageSpy.clear()
+ webView.url = ""
+ verify(webView.waitForLoadSucceeded())
+
+ webView.experimental.evaluateJavaScript(
+ "(function() {" +
+ " var parent = new Object;" +
+ " var child = new Object;" +
+ " parent['level'] = '1';" +
+ " child['level'] = 2;" +
+ " parent['child'] = child;" +
+ " return parent;" +
+ "})()",
+
+ function(result) {
+ webView.lastResult = result
+ webView.resultReceived()
+ });
+
+ resultSpy.wait()
+
+ compare(JSON.stringify(webView.lastResult),
+ '{"child":{"level":2},"level":"1"}')
+ }
+ }
+}