[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):

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@118756 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 3c87fa24
2012-05-29 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 Sheriff Bot <webkit.review.bot@gmail.com>
Unreviewed, rolling out r118752.
......
......@@ -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();
......
......@@ -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;
......
......@@ -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;
......
......@@ -16,3 +16,5 @@ DEFINES += IMPORT_DIR=\"\\\"$${ROOT_BUILD_DIR}$${QMAKE_DIR_SEP}imports\\\"\"
OTHER_FILES += \
WebView/* \
common/*
RESOURCES = resources.qrc
import QtQuick 2.0
import QtTest 1.0
import QtWebKit 3.0
import QtWebKit.experimental 1.0
import "../common"
Item {
TestWebView {
id: webView
width: 400
height: 300
}
TestWebView {
id: webViewWithConditionalUserScripts
width: 400
height: 300
onNavigationRequested: {
var urlString = request.url.toString();
if (urlString.indexOf("test1.html") !== -1)
experimental.userScripts = [Qt.resolvedUrl("../common/change-document-title.js")];
else if (urlString.indexOf("test2.html") !== -1)
experimental.userScripts = [Qt.resolvedUrl("../common/append-document-title.js")];
else
experimental.userScripts = [];
}
}
TestCase {
name: "WebViewUserScripts"
function init() {
webView.url = "";
webView.experimental.userScripts = [];
}
function test_oneScript() {
webView.url = Qt.resolvedUrl("../common/test1.html");
webView.waitForLoadSucceeded();
compare(webView.title, "Test page 1");
webView.experimental.userScripts = [Qt.resolvedUrl("../common/change-document-title.js")];
compare(webView.title, "Test page 1");
webView.reload();
webView.waitForLoadSucceeded();
compare(webView.title, "New title");
webView.url = Qt.resolvedUrl("../common/test2.html");
webView.waitForLoadSucceeded();
compare(webView.title, "New title");
webView.experimental.userScripts = [];
compare(webView.title, "New title");
webView.reload();
webView.waitForLoadSucceeded();
compare(webView.title, "Test page with huge link area");
}
function test_twoScripts() {
webView.url = Qt.resolvedUrl("../common/test1.html");
webView.waitForLoadSucceeded();
compare(webView.title, "Test page 1");
webView.experimental.userScripts = [Qt.resolvedUrl("../common/change-document-title.js"), Qt.resolvedUrl("../common/append-document-title.js")];
webView.reload();
webView.waitForLoadSucceeded();
compare(webView.title, "New title with appendix");
// Make sure we can remove scripts from the preload list.
webView.experimental.userScripts = [Qt.resolvedUrl("../common/append-document-title.js")];
webView.reload();
webView.waitForLoadSucceeded();
compare(webView.title, "Test page 1 with appendix");
// Make sure the scripts are loaded in order.
webView.experimental.userScripts = [Qt.resolvedUrl("../common/append-document-title.js"), Qt.resolvedUrl("../common/change-document-title.js")];
webView.reload();
webView.waitForLoadSucceeded();
compare(webView.title, "New title");
}
function test_setUserScriptsConditionally() {
webViewWithConditionalUserScripts.url = Qt.resolvedUrl("../common/test1.html");
webViewWithConditionalUserScripts.waitForLoadSucceeded();
compare(webViewWithConditionalUserScripts.title, "New title");
webViewWithConditionalUserScripts.url = Qt.resolvedUrl("../common/test2.html");
webViewWithConditionalUserScripts.waitForLoadSucceeded();
compare(webViewWithConditionalUserScripts.title, "Test page with huge link area with appendix");
webViewWithConditionalUserScripts.url = Qt.resolvedUrl("../common/test3.html");
webViewWithConditionalUserScripts.waitForLoadSucceeded();
compare(webViewWithConditionalUserScripts.title, "Test page 3");
}
function test_bigScript() {
webView.experimental.userScripts = [Qt.resolvedUrl("../common/big-user-script.js")];
webView.url = Qt.resolvedUrl("../common/test1.html");
webView.waitForLoadSucceeded();
compare(webView.title, "Big user script changed title");
}
function test_fromResourceFile() {
webView.experimental.userScripts = ["qrc:///common/change-document-title.js"];
webView.url = Qt.resolvedUrl("../common/test1.html");
webView.waitForLoadSucceeded();
compare(webView.title, "New title");
}
}
}
// Used to make sure serialization of user scripts between UI process and Web process can handle files bigger
// than the maximum message size defined in ConnectionXXX classes.
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////////////////////////////
document.title = "Big user script changed title";
<RCC>
<qresource prefix="/">
<file>common/change-document-title.js</file>
</qresource>
</RCC>
......@@ -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)
......
......@@ -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)
{
......
......@@ -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)
......
......@@ -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)
......
......@@ -32,10 +32,12 @@
#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
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment