[Qt] [WK2] Allow user to inject JS scripts when the page loads
authorcaio.oliveira@openbossa.org <caio.oliveira@openbossa.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 29 May 2012 11:22:09 +0000 (11:22 +0000)
committercaio.oliveira@openbossa.org <caio.oliveira@openbossa.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 29 May 2012 11:22:09 +0000 (11:22 +0000)
https://bugs.webkit.org/show_bug.cgi?id=85827

Reviewed by Simon Hausmann.

Create a new experimental property to list URLs of JS scripts that should be
loaded when a page is loaded. These scripts will run in the normal JS environment
of the page.

The supported URL schemes are file:/// and qrc:///. The scripts are read from the
UI process and transfered to the Web process.

Together with the experimental messaging API this provides a way for the
application to manipulate the DOM (by injecting a script that does the
manipulation and communicating with it via postMessage). This covers some of the
use cases of QWebElement in our WK1 API.

* UIProcess/API/qt/qquickwebview.cpp:
(QQuickWebViewPrivate::didRelaunchProcess):
(readUserScript):
(QQuickWebViewPrivate::updateUserScripts):
(QQuickWebViewExperimental::userScripts):
(QQuickWebViewExperimental::setUserScripts):
* UIProcess/API/qt/qquickwebview_p.h:
* UIProcess/API/qt/qquickwebview_p_p.h:
(QQuickWebViewPrivate):
* UIProcess/API/qt/tests/qmltests/WebView.pro:
* UIProcess/API/qt/tests/qmltests/WebView/tst_userScripts.qml: Added.
* UIProcess/API/qt/tests/qmltests/common/append-document-title.js: Added.
* UIProcess/API/qt/tests/qmltests/common/big-user-script.js: Added.
* UIProcess/API/qt/tests/qmltests/common/change-document-title.js: Added.
* UIProcess/API/qt/tests/qmltests/resources.qrc: Added.
* UIProcess/WebPageProxy.h:
(WebPageProxy):
* UIProcess/qt/WebPageProxyQt.cpp:
(WebKit::WebPageProxy::setUserScripts):
(WebKit):
* WebProcess/WebPage/WebPage.h:
(WebPage):
* WebProcess/WebPage/WebPage.messages.in:
* WebProcess/WebPage/qt/WebPageQt.cpp:
(WebKit::WebPage::setUserScripts):
(WebKit):

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

Source/WebKit2/ChangeLog
Source/WebKit2/UIProcess/API/qt/qquickwebview.cpp
Source/WebKit2/UIProcess/API/qt/qquickwebview_p.h
Source/WebKit2/UIProcess/API/qt/qquickwebview_p_p.h
Source/WebKit2/UIProcess/API/qt/tests/qmltests/WebView.pro
Source/WebKit2/UIProcess/WebPageProxy.h
Source/WebKit2/UIProcess/qt/WebPageProxyQt.cpp
Source/WebKit2/WebProcess/WebPage/WebPage.h
Source/WebKit2/WebProcess/WebPage/WebPage.messages.in
Source/WebKit2/WebProcess/WebPage/qt/WebPageQt.cpp

index 409b2c5..65db626 100644 (file)
@@ -1,3 +1,49 @@
+2012-05-21  Caio Marcelo de Oliveira Filho  <caio.oliveira@openbossa.org>
+
+        [Qt] [WK2] Allow user to inject JS scripts when the page loads
+        https://bugs.webkit.org/show_bug.cgi?id=85827
+
+        Reviewed by Simon Hausmann.
+
+        Create a new experimental property to list URLs of JS scripts that should be
+        loaded when a page is loaded. These scripts will run in the normal JS environment
+        of the page.
+
+        The supported URL schemes are file:/// and qrc:///. The scripts are read from the
+        UI process and transfered to the Web process.
+
+        Together with the experimental messaging API this provides a way for the
+        application to manipulate the DOM (by injecting a script that does the
+        manipulation and communicating with it via postMessage). This covers some of the
+        use cases of QWebElement in our WK1 API.
+
+        * UIProcess/API/qt/qquickwebview.cpp:
+        (QQuickWebViewPrivate::didRelaunchProcess):
+        (readUserScript):
+        (QQuickWebViewPrivate::updateUserScripts):
+        (QQuickWebViewExperimental::userScripts):
+        (QQuickWebViewExperimental::setUserScripts):
+        * UIProcess/API/qt/qquickwebview_p.h:
+        * UIProcess/API/qt/qquickwebview_p_p.h:
+        (QQuickWebViewPrivate):
+        * UIProcess/API/qt/tests/qmltests/WebView.pro:
+        * UIProcess/API/qt/tests/qmltests/WebView/tst_userScripts.qml: Added.
+        * UIProcess/API/qt/tests/qmltests/common/append-document-title.js: Added.
+        * UIProcess/API/qt/tests/qmltests/common/big-user-script.js: Added.
+        * UIProcess/API/qt/tests/qmltests/common/change-document-title.js: Added.
+        * UIProcess/API/qt/tests/qmltests/resources.qrc: Added.
+        * UIProcess/WebPageProxy.h:
+        (WebPageProxy):
+        * UIProcess/qt/WebPageProxyQt.cpp:
+        (WebKit::WebPageProxy::setUserScripts):
+        (WebKit):
+        * WebProcess/WebPage/WebPage.h:
+        (WebPage):
+        * WebProcess/WebPage/WebPage.messages.in:
+        * WebProcess/WebPage/qt/WebPageQt.cpp:
+        (WebKit::WebPage::setUserScripts):
+        (WebKit):
+
 2012-05-29  Carlos Garcia Campos  <cgarcia@igalia.com>
 
         [GTK] Add return value information to WebKitWebView::load-failed signal documentation
index 9d77171..7f269e2 100644 (file)
@@ -54,6 +54,7 @@
 #include <JavaScriptCore/JSBase.h>
 #include <JavaScriptCore/JSRetainPtr.h>
 #include <QDateTime>
+#include <QtCore/QFile>
 #include <QtQml/QJSValue>
 #include <WKOpenPanelResultListener.h>
 #include <WKSerializedScriptValue.h>
@@ -61,6 +62,7 @@
 #include <WebCore/IntRect.h>
 #include <wtf/Assertions.h>
 #include <wtf/MainThread.h>
+#include <wtf/Vector.h>
 #include <wtf/text/WTFString.h>
 
 using namespace WebCore;
@@ -452,6 +454,7 @@ void QQuickWebViewPrivate::didRelaunchProcess()
 
     webPageProxy->drawingArea()->setSize(viewSize(), IntSize());
     updateViewportSize();
+    updateUserScripts();
 }
 
 PassOwnPtr<DrawingAreaProxy> QQuickWebViewPrivate::createDrawingAreaProxy()
@@ -679,6 +682,52 @@ void QQuickWebViewPrivate::setNavigatorQtObjectEnabled(bool enabled)
     context->setNavigatorQtObjectEnabled(webPageProxy.get(), enabled);
 }
 
+static QString readUserScript(const QUrl& url)
+{
+    QString path;
+    if (url.isLocalFile())
+        path = url.toLocalFile();
+    else if (url.scheme() == QLatin1String("qrc"))
+        path = QStringLiteral(":") + url.path();
+    else {
+        qWarning("QQuickWebView: Couldn't open '%s' as user script because only file:/// and qrc:/// URLs are supported.", qPrintable(url.toString()));
+        return QString();
+    }
+
+    QFile file(path);
+    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+        qWarning("QQuickWebView: Couldn't open '%s' as user script due to error '%s'.", qPrintable(url.toString()), qPrintable(file.errorString()));
+        return QString();
+    }
+
+    QString contents = QString::fromUtf8(file.readAll());
+    if (contents.isEmpty())
+        qWarning("QQuickWebView: Ignoring '%s' as user script because file is empty.", qPrintable(url.toString()));
+
+    return contents;
+}
+
+void QQuickWebViewPrivate::updateUserScripts()
+{
+    Vector<String> scripts;
+    scripts.reserveCapacity(userScripts.size());
+
+    for (unsigned i = 0; i < userScripts.size(); ++i) {
+        const QUrl& url = userScripts.at(i);
+        if (!url.isValid()) {
+            qWarning("QQuickWebView: Couldn't open '%s' as user script because URL is invalid.", qPrintable(url.toString()));
+            continue;
+        }
+
+        QString contents = readUserScript(url);
+        if (contents.isEmpty())
+            continue;
+        scripts.append(String(contents));
+    }
+
+    webPageProxy->setUserScripts(scripts);
+}
+
 QPointF QQuickWebViewPrivate::contentPos() const
 {
     Q_Q(const QQuickWebView);
@@ -1296,6 +1345,22 @@ void QQuickWebViewExperimental::evaluateJavaScript(const QString& script, const
     d_ptr->webPageProxy.get()->runJavaScriptInMainFrame(script, ScriptValueCallback::create(closure, javaScriptCallback));
 }
 
+QList<QUrl> QQuickWebViewExperimental::userScripts() const
+{
+    Q_D(const QQuickWebView);
+    return d->userScripts;
+}
+
+void QQuickWebViewExperimental::setUserScripts(const QList<QUrl>& userScripts)
+{
+    Q_D(QQuickWebView);
+    if (d->userScripts == userScripts)
+        return;
+    d->userScripts = userScripts;
+    d->updateUserScripts();
+    emit userScriptsChanged();
+}
+
 QQuickUrlSchemeDelegate* QQuickWebViewExperimental::schemeDelegates_At(QQmlListProperty<QQuickUrlSchemeDelegate>* property, int index)
 {
     const QObjectList children = property->object->children();
index f33c6e6..770fccf 100644 (file)
@@ -268,6 +268,7 @@ class QWEBKIT_EXPORT QQuickWebViewExperimental : public QObject {
     Q_PROPERTY(QQmlListProperty<QQuickUrlSchemeDelegate> urlSchemeDelegates READ schemeDelegates)
     Q_PROPERTY(QString userAgent READ userAgent WRITE setUserAgent NOTIFY userAgentChanged)
     Q_PROPERTY(double devicePixelRatio READ devicePixelRatio WRITE setDevicePixelRatio NOTIFY devicePixelRatioChanged)
+    Q_PROPERTY(QList<QUrl> userScripts READ userScripts WRITE setUserScripts NOTIFY userScriptsChanged)
     Q_ENUMS(NavigationRequestActionExperimental)
 
 public:
@@ -300,6 +301,8 @@ public:
     void setUserAgent(const QString& userAgent);
     double devicePixelRatio() const;
     void setDevicePixelRatio(double);
+    QList<QUrl> userScripts() const;
+    void setUserScripts(const QList<QUrl>& userScripts);
 
     QWebKitTest* test();
 
@@ -353,6 +356,7 @@ Q_SIGNALS:
     void devicePixelRatioChanged();
     void enterFullScreenRequested();
     void exitFullScreenRequested();
+    void userScriptsChanged();
 
 private:
     QQuickWebView* q_ptr;
index 572a7f2..29234a9 100644 (file)
@@ -124,6 +124,7 @@ public:
     bool renderToOffscreenBuffer() const { return m_renderToOffscreenBuffer; }
     bool transparentBackground() const;
     void setNavigatorQtObjectEnabled(bool);
+    void updateUserScripts();
 
     QPointF contentPos() const;
     void setContentPos(const QPointF&);
@@ -192,6 +193,8 @@ protected:
 
     WebCore::ViewportAttributes attributes;
 
+    QList<QUrl> userScripts;
+
     bool m_useDefaultContentItemSize;
     bool m_navigatorQtObjectEnabled;
     bool m_renderToOffscreenBuffer;
index 5cc6f35..fd940d4 100644 (file)
@@ -16,3 +16,5 @@ DEFINES += IMPORT_DIR=\"\\\"$${ROOT_BUILD_DIR}$${QMAKE_DIR_SEP}imports\\\"\"
 OTHER_FILES += \
     WebView/* \
     common/*
+
+RESOURCES = resources.qrc
index 72d4695..95e6c13 100644 (file)
@@ -336,6 +336,7 @@ public:
     void authenticationRequiredRequest(const String& hostname, const String& realm, const String& prefilledUsername, String& username, String& password);
     void certificateVerificationRequest(const String& hostname, bool& ignoreErrors);
     void proxyAuthenticationRequiredRequest(const String& hostname, uint16_t port, const String& prefilledUsername, String& username, String& password);
+    void setUserScripts(const Vector<String>&);
 #endif // PLATFORM(QT).
 
 #if PLATFORM(QT)
index ae3c4aa..5cafad5 100644 (file)
@@ -102,6 +102,11 @@ void WebPageProxy::sendApplicationSchemeReply(const QQuickNetworkReply* reply)
     }
 }
 
+void WebPageProxy::setUserScripts(const Vector<String>& scripts)
+{
+    process()->send(Messages::WebPage::SetUserScripts(scripts), m_pageID);
+}
+
 #if PLUGIN_ARCHITECTURE(X11)
 void WebPageProxy::createPluginContainer(uint64_t& windowID)
 {
index 4b2b76c..3b7e6e6 100644 (file)
@@ -523,6 +523,7 @@ public:
     void registerApplicationScheme(const String& scheme);
     void applicationSchemeReply(const QtNetworkReplyData&);
     void receivedApplicationSchemeRequest(const QNetworkRequest&, QtNetworkReply*);
+    void setUserScripts(const Vector<String>&);
 #endif
     void wheelEvent(const WebWheelEvent&);
 #if ENABLE(GESTURE_EVENTS)
index 4bab8ac..899a2a9 100644 (file)
@@ -68,6 +68,7 @@ messages -> WebPage {
 #if PLATFORM(QT)
     ApplicationSchemeReply(WebKit::QtNetworkReplyData reply)
     RegisterApplicationScheme(WTF::String scheme)
+    SetUserScripts(WTF::Vector<WTF::String> script)
 #endif
 
     StopLoadingFrame(uint64_t frameID)
index 4007198..2e90a73 100644 (file)
 #include "WebEvent.h"
 #include "WebPageProxyMessages.h"
 #include "WebProcess.h"
+#include <WebCore/DOMWrapperWorld.h>
 #include <WebCore/FocusController.h>
 #include <WebCore/Frame.h>
 #include <WebCore/KeyboardEvent.h>
 #include <WebCore/Page.h>
+#include <WebCore/PageGroup.h>
 #include <WebCore/PlatformKeyboardEvent.h>
 #include <WebCore/Range.h>
 #include <WebCore/Settings.h>
@@ -411,4 +413,13 @@ void WebPage::applicationSchemeReply(const QtNetworkReplyData& replyData)
     networkReply->finalize();
 }
 
+void WebPage::setUserScripts(const Vector<String>& scripts)
+{
+    // This works because we keep an unique page group for each Page.
+    PageGroup* pageGroup = PageGroup::pageGroup(this->pageGroup()->identifier());
+    pageGroup->removeUserScriptsFromWorld(mainThreadNormalWorld());
+    for (unsigned i = 0; i < scripts.size(); ++i)
+        pageGroup->addUserScriptToWorld(mainThreadNormalWorld(), scripts.at(i), KURL(), nullptr, nullptr, InjectAtDocumentEnd, InjectInTopFrameOnly);
+}
+
 } // namespace WebKit