Title: [116420] trunk/Source/WebKit2
Revision
116420
Author
[email protected]
Date
2012-05-08 07:12:55 -0700 (Tue, 08 May 2012)

Log Message

[Qt] Add QML/WK2 evaluateJavaScript experimental API
https://bugs.webkit.org/show_bug.cgi?id=85496

Reviewed by Simon Hausmann.

This is an initial patch adding support for strings,
numbers and bools as return values.

Test: qmltests/WebView/tst_evaluateJavaScript.qml

* UIProcess/API/qt/qquickwebview.cpp:
(JSCallbackClosure):
(toQString):
(toQJSValue):
(buildQJSValue):
(_javascript_Callback):
(QQuickWebViewExperimental::evaluateJavaScript):
(QQuickWebView::runJavaScriptInMainFrame):
* UIProcess/API/qt/qquickwebview_p.h:
* UIProcess/API/qt/tests/qmltests/WebView/tst_evaluateJavaScript.qml: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/Source/WebKit2/ChangeLog (116419 => 116420)


--- trunk/Source/WebKit2/ChangeLog	2012-05-08 14:09:37 UTC (rev 116419)
+++ trunk/Source/WebKit2/ChangeLog	2012-05-08 14:12:55 UTC (rev 116420)
@@ -1,3 +1,26 @@
+2012-05-08  Kenneth Rohde Christiansen  <[email protected]>
+
+        [Qt] Add QML/WK2 evaluateJavaScript experimental API
+        https://bugs.webkit.org/show_bug.cgi?id=85496
+
+        Reviewed by Simon Hausmann.
+
+        This is an initial patch adding support for strings,
+        numbers and bools as return values.
+
+        Test: qmltests/WebView/tst_evaluateJavaScript.qml
+
+        * UIProcess/API/qt/qquickwebview.cpp:
+        (JSCallbackClosure):
+        (toQString):
+        (toQJSValue):
+        (buildQJSValue):
+        (_javascript_Callback):
+        (QQuickWebViewExperimental::evaluateJavaScript):
+        (QQuickWebView::runJavaScriptInMainFrame):
+        * UIProcess/API/qt/qquickwebview_p.h:
+        * UIProcess/API/qt/tests/qmltests/WebView/tst_evaluateJavaScript.qml: Added.
+
 2012-05-07  Alexis Menard  <[email protected]>
 
         [Qt] Unbreak debugging of WebKit2.

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"}')
+        }
+    }
+}
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to