diff --git a/src/core/browser_context_adapter.cpp b/src/core/browser_context_adapter.cpp
index 1749fc7085dfa941e8ab3d596c8811f7752c6d26..f89e671c792c9b2b804ad41b138d8e28f2cdaead 100644
--- a/src/core/browser_context_adapter.cpp
+++ b/src/core/browser_context_adapter.cpp
@@ -43,6 +43,7 @@
 #include "web_engine_context.h"
 #include "web_engine_visited_links_manager.h"
 #include "url_request_context_getter_qt.h"
+#include "user_script_controller_host.h"
 
 #include "net/proxy/proxy_service.h"
 
@@ -325,3 +326,10 @@ void BrowserContextAdapter::updateCustomUrlSchemeHandlers()
     if (m_browserContext->url_request_getter_.get())
         m_browserContext->url_request_getter_->updateStorageSettings();
 }
+
+UserScriptControllerHost *BrowserContextAdapter::userScriptController()
+{
+    if (!m_userScriptController)
+        m_userScriptController.reset(new UserScriptControllerHost);
+    return m_userScriptController.data();
+}
diff --git a/src/core/browser_context_adapter.h b/src/core/browser_context_adapter.h
index 6389d666c945692351d99d21d4ae228bc57150cb..2a7c3465f0397c4689a84e65fc7b6e9ff0b366d3 100644
--- a/src/core/browser_context_adapter.h
+++ b/src/core/browser_context_adapter.h
@@ -48,6 +48,7 @@ class BrowserContextAdapterClient;
 class BrowserContextQt;
 class CustomUrlSchemeHandler;
 class DownloadManagerDelegateQt;
+class UserScriptControllerHost;
 class WebEngineVisitedLinksManager;
 
 class QWEBENGINE_EXPORT BrowserContextAdapter : public QSharedData
@@ -123,6 +124,7 @@ public:
 
     QVector<CustomUrlSchemeHandler*> &customUrlSchemeHandlers();
     void updateCustomUrlSchemeHandlers();
+    UserScriptControllerHost *userScriptController();
 
 private:
     QString m_name;
@@ -130,6 +132,8 @@ private:
     QScopedPointer<BrowserContextQt> m_browserContext;
     QScopedPointer<WebEngineVisitedLinksManager> m_visitedLinksManager;
     QScopedPointer<DownloadManagerDelegateQt> m_downloadManagerDelegate;
+    QScopedPointer<UserScriptControllerHost> m_userScriptController;
+
     QString m_dataPath;
     QString m_cachePath;
     QString m_httpUserAgent;
diff --git a/src/core/browser_context_qt.h b/src/core/browser_context_qt.h
index 2798cc52f9c5d228f4a1d6eea91397605abc5c60..fb94ddf0643a15e339654d8deede2d9d6ae8a313 100644
--- a/src/core/browser_context_qt.h
+++ b/src/core/browser_context_qt.h
@@ -73,6 +73,8 @@ public:
 
     BrowserContextAdapter* adapter() { return m_adapter; }
 private:
+    friend class ContentBrowserClientQt;
+    friend class WebContentsAdapter;
     scoped_ptr<content::ResourceContext> resourceContext;
     scoped_refptr<URLRequestContextGetterQt> url_request_getter_;
     BrowserContextAdapter *m_adapter;
diff --git a/src/core/common/qt_messages.h b/src/core/common/qt_messages.h
index 5ae5fef7d929cb76ed89f2362cc58e8182af7981..c692ee5ca34c2118e7ba731293bc368371295d13 100644
--- a/src/core/common/qt_messages.h
+++ b/src/core/common/qt_messages.h
@@ -3,19 +3,21 @@
 // found in the LICENSE file.
 
 // Multiply-included file, no traditional include guard.
-#include "ipc/ipc_message_macros.h"
-
-// Singly-included section for enums and custom IPC traits.
-#ifndef RENDER_VIEW_MESSAGES_H
-#define RENDER_VIEW_MESSAGES_H
 
-namespace IPC {
+#include "content/public/common/common_param_traits.h"
+#include "ipc/ipc_message_macros.h"
 
-// TODO - add enums and custom IPC traits here when needed.
+#include "user_script_data.h"
 
-}  // namespace IPC
+IPC_STRUCT_TRAITS_BEGIN(UserScriptData)
+    IPC_STRUCT_TRAITS_MEMBER(source)
+    IPC_STRUCT_TRAITS_MEMBER(url)
+    IPC_STRUCT_TRAITS_MEMBER(injectionPoint)
+    IPC_STRUCT_TRAITS_MEMBER(injectForSubframes)
+    IPC_STRUCT_TRAITS_MEMBER(worldId)
+    IPC_STRUCT_TRAITS_MEMBER(scriptId)
+IPC_STRUCT_TRAITS_END()
 
-#endif  // RENDER_VIEW_MESSAGES_H
 
 #define IPC_MESSAGE_START QtMsgStart
 
@@ -31,6 +33,17 @@ IPC_MESSAGE_ROUTED1(QtRenderViewObserver_FetchDocumentInnerText,
 
 IPC_MESSAGE_ROUTED1(WebChannelIPCTransport_Message, std::vector<char> /*binaryJSON*/)
 
+// User scripts messages
+IPC_MESSAGE_ROUTED1(RenderViewObserverHelper_AddScript,
+                    UserScriptData /* script */)
+IPC_MESSAGE_ROUTED1(RenderViewObserverHelper_RemoveScript,
+                    UserScriptData /* script */)
+IPC_MESSAGE_ROUTED0(RenderViewObserverHelper_ClearScripts)
+
+IPC_MESSAGE_CONTROL1(UserScriptController_AddScript, UserScriptData /* scriptContents */)
+IPC_MESSAGE_CONTROL1(UserScriptController_RemoveScript, UserScriptData /* scriptContents */)
+IPC_MESSAGE_CONTROL0(UserScriptController_ClearScripts)
+
 //-----------------------------------------------------------------------------
 // WebContents messages
 // These are messages sent from the renderer back to the browser process.
diff --git a/src/core/common/user_script_data.cpp b/src/core/common/user_script_data.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f94ea640c130511ca3ca8ef6555180787434b941
--- /dev/null
+++ b/src/core/common/user_script_data.cpp
@@ -0,0 +1,46 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "user_script_data.h"
+#include "base/pickle.h"
+
+UserScriptData::UserScriptData() : injectionPoint(AfterLoad)
+  , injectForSubframes(false)
+  , worldId(1)
+{
+    static uint64 idCount = 0;
+    scriptId = idCount++;
+}
diff --git a/src/core/common/user_script_data.h b/src/core/common/user_script_data.h
new file mode 100644
index 0000000000000000000000000000000000000000..3dfec0ec3a71f02bb5311b4de310bdb5aa117d21
--- /dev/null
+++ b/src/core/common/user_script_data.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef USER_SCRIPT_DATA_H
+#define USER_SCRIPT_DATA_H
+
+#include <QtCore/QHash>
+#include <string>
+#include "base/basictypes.h"
+#include "ipc/ipc_message_utils.h"
+#include "url/gurl.h"
+
+struct UserScriptData {
+    enum InjectionPoint {
+        AfterLoad,
+        DocumentLoadFinished,
+        DocumentElementCreation
+    };
+
+    UserScriptData();
+
+    std::string source;
+    GURL url;
+    /*InjectionPoint*/uint8 injectionPoint;
+    bool injectForSubframes;
+    uint worldId;
+    uint64 scriptId;
+};
+
+#endif // USER_SCRIPT_DATA_H
diff --git a/src/core/content_browser_client_qt.cpp b/src/core/content_browser_client_qt.cpp
index 8fb6acf629ebd88543c890319bbb550b15a7b35c..8d2541495227e8c3fce4f0ed0710d9dd8c0789d4 100644
--- a/src/core/content_browser_client_qt.cpp
+++ b/src/core/content_browser_client_qt.cpp
@@ -56,6 +56,7 @@
 #include "ui/gl/gl_share_group.h"
 
 #include "access_token_store_qt.h"
+#include "browser_context_adapter.h"
 #include "browser_context_qt.h"
 #include "certificate_error_controller.h"
 #include "certificate_error_controller_p.h"
@@ -66,6 +67,7 @@
 #endif
 #include "media_capture_devices_dispatcher.h"
 #include "resource_dispatcher_host_delegate_qt.h"
+#include "user_script_controller_host.h"
 #include "web_contents_delegate_qt.h"
 #include "access_token_store_qt.h"
 
@@ -329,6 +331,7 @@ void ContentBrowserClientQt::RenderProcessWillLaunch(content::RenderProcessHost*
 {
     // FIXME: Add a settings variable to enable/disable the file scheme.
     content::ChildProcessSecurityPolicy::GetInstance()->GrantScheme(host->GetID(), url::kFileScheme);
+    static_cast<BrowserContextQt*>(host->GetBrowserContext())->m_adapter->userScriptController()->renderProcessHostCreated(host);
 }
 
 void ContentBrowserClientQt::ResourceDispatcherHostCreated()
diff --git a/src/core/core_gyp_generator.pro b/src/core/core_gyp_generator.pro
index b93ea0175da6a2a0742e944d348d30064b010806..b5722d2d359d195c30d1c391f3770ceac235626d 100644
--- a/src/core/core_gyp_generator.pro
+++ b/src/core/core_gyp_generator.pro
@@ -38,6 +38,7 @@ SOURCES = \
         chromium_overrides.cpp \
         clipboard_qt.cpp \
         common/qt_messages.cpp \
+        common/user_script_data.cpp \
         content_client_qt.cpp \
         content_browser_client_qt.cpp \
         content_main_delegate_qt.cpp \
@@ -61,6 +62,7 @@ SOURCES = \
         render_widget_host_view_qt.cpp \
         renderer/content_renderer_client_qt.cpp \
         renderer/qt_render_view_observer.cpp \
+        renderer/user_script_controller.cpp \
         renderer/web_channel_ipc_transport.cpp \
         resource_bundle_qt.cpp \
         resource_context_qt.cpp \
@@ -71,6 +73,8 @@ SOURCES = \
         url_request_custom_job.cpp \
         url_request_custom_job_delegate.cpp \
         url_request_qrc_job_qt.cpp \
+        user_script.cpp \
+        user_script_controller_host.cpp \
         web_channel_ipc_transport_host.cpp \
         web_contents_adapter.cpp \
         web_contents_delegate_qt.cpp \
@@ -95,6 +99,7 @@ HEADERS = \
         chromium_overrides.h \
         clipboard_qt.h \
         common/qt_messages.h \
+        common/user_script_data.h \
         content_client_qt.h \
         content_browser_client_qt.h \
         content_main_delegate_qt.h \
@@ -120,6 +125,7 @@ HEADERS = \
         render_widget_host_view_qt_delegate.h \
         renderer/content_renderer_client_qt.h \
         renderer/qt_render_view_observer.h \
+        renderer/user_script_controller.h \
         renderer/web_channel_ipc_transport.h \
         resource_context_qt.h \
         resource_dispatcher_host_delegate_qt.h \
@@ -130,6 +136,8 @@ HEADERS = \
         url_request_custom_job.h \
         url_request_custom_job_delegate.h \
         url_request_qrc_job_qt.h \
+        user_script.h \
+        user_script_controller_host.h \
         web_channel_ipc_transport_host.h \
         web_contents_adapter.h \
         web_contents_adapter_client.h \
diff --git a/src/core/renderer/content_renderer_client_qt.cpp b/src/core/renderer/content_renderer_client_qt.cpp
index bde8e84552bad9ca707b10d9decf2719962cb1e0..242c8ef6db37849b45291fcd5ebabde53a8bab43 100644
--- a/src/core/renderer/content_renderer_client_qt.cpp
+++ b/src/core/renderer/content_renderer_client_qt.cpp
@@ -52,6 +52,7 @@
 
 #include "renderer/web_channel_ipc_transport.h"
 #include "renderer/qt_render_view_observer.h"
+#include "renderer/user_script_controller.h"
 
 #include "grit/renderer_resources.h"
 
@@ -71,6 +72,7 @@ void ContentRendererClientQt::RenderThreadStarted()
     renderThread->RegisterExtension(WebChannelIPCTransport::getV8Extension());
     m_visitedLinkSlave.reset(new visitedlink::VisitedLinkSlave);
     renderThread->AddObserver(m_visitedLinkSlave.data());
+    renderThread->AddObserver(UserScriptController::instance());
 }
 
 void ContentRendererClientQt::RenderViewCreated(content::RenderView* render_view)
@@ -78,6 +80,7 @@ void ContentRendererClientQt::RenderViewCreated(content::RenderView* render_view
     // RenderViewObservers destroy themselves with their RenderView.
     new QtRenderViewObserver(render_view);
     new WebChannelIPCTransport(render_view);
+    UserScriptController::instance()->renderViewCreated(render_view);
 }
 
 bool ContentRendererClientQt::HasErrorPage(int httpStatusCode, std::string *errorDomain)
diff --git a/src/core/renderer/user_script_controller.cpp b/src/core/renderer/user_script_controller.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7295003417535926a2f202931a32d3e2e3b10d67
--- /dev/null
+++ b/src/core/renderer/user_script_controller.cpp
@@ -0,0 +1,259 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "user_script_controller.h"
+
+#include "content/public/renderer/render_view.h"
+#include "content/public/renderer/render_view_observer.h"
+#include "third_party/WebKit/public/web/WebLocalFrame.h"
+#include "third_party/WebKit/public/web/WebScriptSource.h"
+#include "third_party/WebKit/public/web/WebView.h"
+#include "v8/include/v8.h"
+
+#include "common/qt_messages.h"
+#include "common/user_script_data.h"
+
+Q_GLOBAL_STATIC(UserScriptController, qt_webengine_userScriptController)
+
+static content::RenderView * const globalScriptsIndex = 0;
+
+// Scripts meant to run after the load event will be run 500ms after DOMContentLoaded if the load event doesn't come within that delay.
+static const int afterLoadTimeout = 500;
+
+class UserScriptController::RenderViewObserverHelper : public content::RenderViewObserver
+{
+public:
+    RenderViewObserverHelper(content::RenderView *);
+private:
+    // RenderViewObserver implementation.
+    virtual void DidCreateDocumentElement(blink::WebLocalFrame* frame) Q_DECL_OVERRIDE;
+    virtual void DidFinishDocumentLoad(blink::WebLocalFrame* frame) Q_DECL_OVERRIDE;
+    virtual void DidFinishLoad(blink::WebLocalFrame* frame) Q_DECL_OVERRIDE;
+    virtual void DidStartProvisionalLoad(blink::WebLocalFrame* frame) Q_DECL_OVERRIDE;
+    virtual void FrameDetached(blink::WebFrame* frame) Q_DECL_OVERRIDE;
+    virtual void OnDestruct() Q_DECL_OVERRIDE;
+    virtual bool OnMessageReceived(const IPC::Message& message) Q_DECL_OVERRIDE;
+
+    void onUserScriptAdded(const UserScriptData &);
+    void onUserScriptRemoved(const UserScriptData &);
+    void onScriptsCleared();
+
+    void runScripts(UserScriptData::InjectionPoint, blink::WebLocalFrame *);
+    QSet<blink::WebLocalFrame *> m_pendingFrames;
+};
+
+void UserScriptController::RenderViewObserverHelper::runScripts(UserScriptData::InjectionPoint p, blink::WebLocalFrame *frame)
+{
+    if (p == UserScriptData::AfterLoad && !m_pendingFrames.remove(frame))
+        return;
+    content::RenderView *renderView = content::RenderView::FromWebView(frame->view());
+    const bool isMainFrame = (frame == renderView->GetWebView()->mainFrame());
+
+    QList<uint64> scriptsToRun = UserScriptController::instance()->m_viewUserScriptMap.value(globalScriptsIndex).toList();
+    scriptsToRun.append(UserScriptController::instance()->m_viewUserScriptMap.value(renderView).toList());
+
+    Q_FOREACH (uint64 id, scriptsToRun) {
+        const UserScriptData &script = UserScriptController::instance()->m_scripts.value(id);
+        if (script.injectionPoint != p
+                || (!script.injectForSubframes && !isMainFrame))
+            continue;
+        blink::WebScriptSource source(blink::WebString::fromUTF8(script.source), script.url);
+        if (script.worldId)
+            frame->executeScriptInIsolatedWorld(script.worldId, &source, /*numSources = */1, /*contentScriptExtentsionGroup = */ 1);
+        else
+            frame->executeScript(source);
+    }
+}
+
+
+UserScriptController::RenderViewObserverHelper::RenderViewObserverHelper(content::RenderView *renderView)
+    : content::RenderViewObserver(renderView)
+{
+}
+
+void UserScriptController::RenderViewObserverHelper::DidCreateDocumentElement(blink::WebLocalFrame *frame)
+{
+    runScripts(UserScriptData::DocumentElementCreation, frame);
+}
+
+void UserScriptController::RenderViewObserverHelper::DidFinishDocumentLoad(blink::WebLocalFrame *frame)
+{
+    runScripts(UserScriptData::DocumentLoadFinished, frame);
+    m_pendingFrames.insert(frame);
+    base::MessageLoop::current()->PostDelayedTask(FROM_HERE, base::Bind(&UserScriptController::RenderViewObserverHelper::runScripts,
+                                                                        base::Unretained(this), UserScriptData::AfterLoad, frame),
+                                                  base::TimeDelta::FromMilliseconds(afterLoadTimeout));
+}
+
+void UserScriptController::RenderViewObserverHelper::DidFinishLoad(blink::WebLocalFrame *frame)
+{
+    // DidFinishDocumentLoad always comes before this, so frame has already been marked as pending.
+    base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(&UserScriptController::RenderViewObserverHelper::runScripts,
+                                                                 base::Unretained(this), UserScriptData::AfterLoad, frame));
+}
+
+void UserScriptController::RenderViewObserverHelper::DidStartProvisionalLoad(blink::WebLocalFrame *frame)
+{
+    m_pendingFrames.remove(frame);
+}
+
+void UserScriptController::RenderViewObserverHelper::FrameDetached(blink::WebFrame *frame)
+{
+    if (frame->isWebLocalFrame())
+        m_pendingFrames.remove(frame->toWebLocalFrame());
+}
+
+void UserScriptController::RenderViewObserverHelper::OnDestruct()
+{
+    UserScriptController::instance()->renderViewDestroyed(render_view());
+}
+
+bool UserScriptController::RenderViewObserverHelper::OnMessageReceived(const IPC::Message &message)
+{
+    bool handled = true;
+    IPC_BEGIN_MESSAGE_MAP(UserScriptController::RenderViewObserverHelper, message)
+        IPC_MESSAGE_HANDLER(RenderViewObserverHelper_AddScript, onUserScriptAdded)
+        IPC_MESSAGE_HANDLER(RenderViewObserverHelper_RemoveScript, onUserScriptRemoved)
+        IPC_MESSAGE_HANDLER(RenderViewObserverHelper_ClearScripts, onScriptsCleared)
+        IPC_MESSAGE_UNHANDLED(handled = false)
+    IPC_END_MESSAGE_MAP()
+            return handled;
+}
+
+void UserScriptController::RenderViewObserverHelper::onUserScriptAdded(const UserScriptData &script)
+{
+    UserScriptController::instance()->addScriptForView(script, render_view());
+}
+
+void UserScriptController::RenderViewObserverHelper::onUserScriptRemoved(const UserScriptData &script)
+{
+    UserScriptController::instance()->removeScriptForView(script, render_view());
+}
+
+void UserScriptController::RenderViewObserverHelper::onScriptsCleared()
+{
+    UserScriptController::instance()->clearScriptsForView(render_view());
+}
+
+UserScriptController *UserScriptController::instance()
+{
+    return qt_webengine_userScriptController();
+}
+
+bool UserScriptController::OnControlMessageReceived(const IPC::Message &message)
+{
+    bool handled = true;
+    IPC_BEGIN_MESSAGE_MAP(UserScriptController, message)
+        IPC_MESSAGE_HANDLER(UserScriptController_AddScript, onAddScript)
+        IPC_MESSAGE_HANDLER(UserScriptController_RemoveScript, onRemoveScript)
+        IPC_MESSAGE_HANDLER(UserScriptController_ClearScripts, onClearScripts)
+        IPC_MESSAGE_UNHANDLED(handled = false)
+    IPC_END_MESSAGE_MAP()
+    return handled;
+}
+
+UserScriptController::UserScriptController()
+{
+#if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS)
+    static bool onlyCalledOnce = true;
+    Q_ASSERT(onlyCalledOnce);
+    onlyCalledOnce = false;
+#endif // !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS)
+}
+
+void UserScriptController::renderViewCreated(content::RenderView *renderView)
+{
+    // Will destroy itself with their RenderView.
+    new RenderViewObserverHelper(renderView);
+}
+
+void UserScriptController::renderViewDestroyed(content::RenderView *renderView)
+{
+    ViewUserScriptMap::iterator it = m_viewUserScriptMap.find(renderView);
+    if (it == m_viewUserScriptMap.end()) // ASSERT maybe?
+        return;
+    Q_FOREACH (uint64 id, it.value()) {
+        m_scripts.remove(id);
+    }
+    m_viewUserScriptMap.remove(renderView);
+}
+
+void UserScriptController::addScriptForView(const UserScriptData &script, content::RenderView *view)
+{
+    ViewUserScriptMap::iterator it = m_viewUserScriptMap.find(view);
+    if (it == m_viewUserScriptMap.end())
+        it = m_viewUserScriptMap.insert(view, UserScriptSet());
+
+    (*it).insert(script.scriptId);
+    m_scripts.insert(script.scriptId, script);
+}
+
+void UserScriptController::removeScriptForView(const UserScriptData &script, content::RenderView *view)
+{
+    ViewUserScriptMap::iterator it = m_viewUserScriptMap.find(view);
+    if (it == m_viewUserScriptMap.end())
+        return;
+
+    (*it).remove(script.scriptId);
+    m_scripts.remove(script.scriptId);
+}
+
+void UserScriptController::clearScriptsForView(content::RenderView *view)
+{
+    ViewUserScriptMap::iterator it = m_viewUserScriptMap.find(view);
+    if (it == m_viewUserScriptMap.end())
+        return;
+    Q_FOREACH (uint64 id, it.value())
+        m_scripts.remove(id);
+
+    m_viewUserScriptMap.remove(view);
+}
+
+void UserScriptController::onAddScript(const UserScriptData &script)
+{
+    addScriptForView(script, globalScriptsIndex);
+}
+
+void UserScriptController::onRemoveScript(const UserScriptData &script)
+{
+    removeScriptForView(script, globalScriptsIndex);
+}
+
+void UserScriptController::onClearScripts()
+{
+    clearScriptsForView(globalScriptsIndex);
+}
+
diff --git a/src/core/renderer/user_script_controller.h b/src/core/renderer/user_script_controller.h
new file mode 100644
index 0000000000000000000000000000000000000000..ed83d9dac4c48a9c3de9971cb983372de7e19e70
--- /dev/null
+++ b/src/core/renderer/user_script_controller.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef USER_SCRIPT_CONTROLLER_H
+#define USER_SCRIPT_CONTROLLER_H
+
+#include "content/public/renderer/render_process_observer.h"
+
+#include "common/user_script_data.h"
+
+#include <QtCore/qcompilerdetection.h>
+#include <QtCore/QHash>
+#include <QtCore/QSet>
+
+namespace content {
+class RenderView;
+}
+
+
+class UserScriptController : public content::RenderProcessObserver {
+
+public:
+    static UserScriptController *instance();
+    UserScriptController();
+    void renderViewCreated(content::RenderView *);
+    void renderViewDestroyed(content::RenderView *);
+    void addScriptForView(const UserScriptData &, content::RenderView *);
+    void removeScriptForView(const UserScriptData &, content::RenderView *);
+    void clearScriptsForView(content::RenderView *);
+
+private:
+    Q_DISABLE_COPY(UserScriptController)
+
+    class RenderViewObserverHelper;
+
+    // RenderProcessObserver implementation.
+    virtual bool OnControlMessageReceived(const IPC::Message &message) Q_DECL_OVERRIDE;
+
+    void onAddScript(const UserScriptData &);
+    void onRemoveScript(const UserScriptData &);
+    void onClearScripts();
+
+    typedef QSet<uint64> UserScriptSet;
+    typedef QHash<const content::RenderView *, UserScriptSet> ViewUserScriptMap;
+    ViewUserScriptMap m_viewUserScriptMap;
+    QHash<uint64, UserScriptData> m_scripts;
+};
+
+#endif // USER_SCRIPT_CONTROLLER_H
diff --git a/src/core/user_script.cpp b/src/core/user_script.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7e88f9223cc72e7d900335d3495d4894f97b845c
--- /dev/null
+++ b/src/core/user_script.cpp
@@ -0,0 +1,185 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "common/user_script_data.h"
+#include "user_script.h"
+#include "user_script_controller_host.h"
+#include "type_conversion.h"
+
+ASSERT_ENUMS_MATCH(UserScript::AfterLoad, UserScriptData::AfterLoad)
+ASSERT_ENUMS_MATCH(UserScript::DocumentLoadFinished, UserScriptData::DocumentLoadFinished)
+ASSERT_ENUMS_MATCH(UserScript::DocumentElementCreation, UserScriptData::DocumentElementCreation)
+
+UserScript::UserScript()
+    : QSharedData()
+{
+}
+
+UserScript::UserScript(const UserScript &other)
+    : QSharedData(other)
+{
+    if (other.isNull())
+        return;
+    scriptData.reset(new UserScriptData);
+    m_name = other.m_name;
+    scriptData->source = other.scriptData->source;
+    scriptData->url = other.scriptData->url;
+    scriptData->injectionPoint = other.scriptData->injectionPoint;
+    scriptData->injectForSubframes = other.scriptData->injectForSubframes;
+    scriptData->worldId = other.scriptData->worldId;
+}
+
+UserScript::~UserScript()
+{
+}
+
+UserScript &UserScript::operator=(const UserScript &other)
+{
+    if (other.isNull()) {
+        scriptData.reset();
+        m_name = QString();
+        return *this;
+    }
+    scriptData.reset(new UserScriptData);
+    m_name = other.m_name;
+    scriptData->source = other.scriptData->source;
+    scriptData->url = other.scriptData->url;
+    scriptData->injectionPoint = other.scriptData->injectionPoint;
+    scriptData->injectForSubframes = other.scriptData->injectForSubframes;
+    scriptData->worldId = other.scriptData->worldId;
+    return *this;
+}
+
+QString UserScript::name() const
+{
+    return m_name;
+}
+
+void UserScript::setName(const QString &name)
+{
+    m_name = name;
+    initData();
+    scriptData->url = GURL(QStringLiteral("userScript:%1").arg(name).toStdString());
+}
+
+QString UserScript::source() const
+{
+    if (isNull())
+        return QString();
+    return toQt(scriptData->source);
+}
+
+void UserScript::setSource(const QString &source)
+{
+    initData();
+    scriptData->source = source.toStdString();
+}
+
+UserScript::InjectionPoint UserScript::injectionPoint() const
+{
+    if (isNull())
+        return UserScript::AfterLoad;
+    return static_cast<UserScript::InjectionPoint>(scriptData->injectionPoint);
+}
+
+void UserScript::setInjectionPoint(UserScript::InjectionPoint p)
+{
+    initData();
+    scriptData->injectionPoint = p;
+}
+
+uint UserScript::worldId() const
+{
+    if (isNull())
+        return 1;
+    return scriptData->worldId;
+}
+
+void UserScript::setWorldId(uint id)
+{
+    initData();
+    scriptData->worldId = id;
+}
+
+bool UserScript::runsOnSubFrames() const
+{
+    if (isNull())
+        return false;
+    return scriptData->injectForSubframes;
+}
+
+void UserScript::setRunsOnSubFrames(bool on)
+{
+    initData();
+    scriptData->injectForSubframes = on;
+}
+
+bool UserScript::operator==(const UserScript &other) const
+{
+    if (isNull() != other.isNull())
+        return false;
+    if (isNull()) // neither is valid
+        return true;
+    return worldId() == other.worldId()
+            && runsOnSubFrames() == other.runsOnSubFrames()
+            && injectionPoint() == other.injectionPoint()
+            && name() == other.name() && source() == other.source();
+}
+
+void UserScript::initData()
+{
+    if (scriptData.isNull())
+        scriptData.reset(new UserScriptData);
+}
+
+bool UserScript::isNull() const
+{
+    return scriptData.isNull();
+}
+
+UserScriptData &UserScript::data() const
+{
+    return *(scriptData.data());
+}
+
+uint qHash(const UserScript &script, uint seed)
+{
+    if (script.isNull())
+        return 0;
+    return qHash(script.source(), seed) ^ qHash(script.name(), seed)
+           ^ (script.injectionPoint() | (script.runsOnSubFrames() << 4))
+           ^ script.worldId();
+}
diff --git a/src/core/user_script.h b/src/core/user_script.h
new file mode 100644
index 0000000000000000000000000000000000000000..17de212ea211faeae6d733d503f018aa507964fa
--- /dev/null
+++ b/src/core/user_script.h
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef USER_SCRIPT_H
+#define USER_SCRIPT_H
+
+#include "qtwebenginecoreglobal.h"
+
+#include <QtCore/QAtomicInt>
+#include <QtCore/QScopedPointer>
+#include <QtCore/QSharedData>
+#include <QtCore/QString>
+
+class UserScriptControllerHost;
+struct UserScriptData;
+
+class QWEBENGINE_EXPORT UserScript : public QSharedData {
+public:
+    enum InjectionPoint {
+        AfterLoad,
+        DocumentLoadFinished,
+        DocumentElementCreation
+    };
+
+    UserScript();
+    UserScript(const UserScript &other);
+    ~UserScript();
+    UserScript &operator=(const UserScript &other);
+
+    bool isNull() const;
+
+    QString name() const;
+    void setName(const QString &);
+
+    QString source() const;
+    void setSource(const QString &);
+
+    InjectionPoint injectionPoint() const;
+    void setInjectionPoint(InjectionPoint);
+
+    uint worldId() const;
+    void setWorldId(uint id);
+
+    bool runsOnSubFrames() const;
+    void setRunsOnSubFrames(bool on);
+
+    bool operator==(const UserScript &) const;
+
+private:
+    void initData();
+    UserScriptData &data() const;
+    friend class UserScriptControllerHost;
+
+    QScopedPointer<UserScriptData> scriptData;
+    QString m_name;
+};
+
+uint qHash(const UserScript &, uint seed = 0);
+
+#endif // USER_SCRIPT_H
diff --git a/src/core/user_script_controller_host.cpp b/src/core/user_script_controller_host.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..bf12485775b0d56b8aa166b2df2f6c0f190f00bd
--- /dev/null
+++ b/src/core/user_script_controller_host.cpp
@@ -0,0 +1,208 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "user_script_controller_host.h"
+
+#include "common/qt_messages.h"
+#include "type_conversion.h"
+#include "web_contents_adapter.h"
+#include "web_contents_adapter_p.h"
+
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/render_process_host_observer.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_observer.h"
+
+class UserScriptControllerHost::WebContentsObserverHelper : public content::WebContentsObserver {
+public:
+    WebContentsObserverHelper(UserScriptControllerHost *, content::WebContents *);
+    virtual void AboutToNavigateRenderView(content::RenderViewHost* renderViewHost) Q_DECL_OVERRIDE;
+
+    virtual void WebContentsDestroyed() Q_DECL_OVERRIDE;
+private:
+    UserScriptControllerHost *m_controllerHost;
+};
+
+UserScriptControllerHost::WebContentsObserverHelper::WebContentsObserverHelper(UserScriptControllerHost *controller, content::WebContents *contents)
+    : content::WebContentsObserver(contents)
+    , m_controllerHost(controller)
+{
+}
+
+void UserScriptControllerHost::WebContentsObserverHelper::AboutToNavigateRenderView(content::RenderViewHost *renderViewHost)
+{
+    content::WebContents *contents = web_contents();
+    Q_FOREACH (const UserScript &script, m_controllerHost->m_perContentsScripts.value(contents))
+        renderViewHost->Send(new RenderViewObserverHelper_AddScript(renderViewHost->GetRoutingID(), script.data()));
+}
+
+void UserScriptControllerHost::WebContentsObserverHelper::WebContentsDestroyed()
+{
+    m_controllerHost->webContentsDestroyed(web_contents());
+    delete this;
+}
+
+class UserScriptControllerHost::RenderProcessObserverHelper : public content::RenderProcessHostObserver {
+public:
+    RenderProcessObserverHelper(UserScriptControllerHost *);
+    virtual void RenderProcessHostDestroyed(content::RenderProcessHost *) Q_DECL_OVERRIDE;
+private:
+    UserScriptControllerHost *m_controllerHost;
+};
+
+UserScriptControllerHost::RenderProcessObserverHelper::RenderProcessObserverHelper(UserScriptControllerHost *controller)
+    : m_controllerHost(controller)
+{
+}
+
+void UserScriptControllerHost::RenderProcessObserverHelper::RenderProcessHostDestroyed(content::RenderProcessHost *renderer)
+{
+    Q_ASSERT(m_controllerHost);
+    m_controllerHost->m_observedProcesses.remove(renderer);
+}
+
+void UserScriptControllerHost::addUserScript(const UserScript &script, WebContentsAdapter *adapter)
+{
+    if (script.isNull())
+        return;
+    // Global scripts should be dispatched to all our render processes.
+    if (!adapter) {
+        m_profileWideScripts.insert(script);
+        Q_FOREACH (content::RenderProcessHost *renderer, m_observedProcesses)
+            renderer->Send(new UserScriptController_AddScript(script.data()));
+    } else {
+        content::WebContents *contents = adapter->webContents();
+        ContentsScriptsMap::iterator it = m_perContentsScripts.find(contents);
+        if (it == m_perContentsScripts.end()) {
+            // We need to keep track of RenderView/RenderViewHost changes for a given contents
+            // in order to make sure the scripts stay in sync
+            new WebContentsObserverHelper(this, contents);
+            it = m_perContentsScripts.insert(contents, (QSet<UserScript>() << script));
+        } else {
+            QSet<UserScript> currentScripts = it.value();
+            currentScripts.insert(script);
+            m_perContentsScripts.insert(contents, currentScripts);
+        }
+        contents->Send(new RenderViewObserverHelper_AddScript(contents->GetRoutingID(), script.data()));
+    }
+}
+
+bool UserScriptControllerHost::containsUserScript(const UserScript &script, WebContentsAdapter *adapter)
+{
+    if (script.isNull())
+        return false;
+    // Global scripts should be dispatched to all our render processes.
+    if (!adapter)
+        return m_profileWideScripts.contains(script);
+    return m_perContentsScripts.value(adapter->webContents()).contains(script);
+}
+
+bool UserScriptControllerHost::removeUserScript(const UserScript &script, WebContentsAdapter *adapter)
+{
+    if (script.isNull())
+        return false;
+    if (!adapter) {
+        QSet<UserScript>::iterator it = m_profileWideScripts.find(script);
+        if (it == m_profileWideScripts.end())
+            return false;
+        Q_FOREACH (content::RenderProcessHost *renderer, m_observedProcesses)
+            renderer->Send(new UserScriptController_RemoveScript((*it).data()));
+        m_profileWideScripts.erase(it);
+    } else {
+        content::WebContents *contents = adapter->webContents();
+        if (!m_perContentsScripts.contains(contents))
+            return false;
+        QSet<UserScript> &set(m_perContentsScripts[contents]);
+        QSet<UserScript>::iterator it = set.find(script);
+        if (it == set.end())
+            return false;
+        contents->Send(new RenderViewObserverHelper_RemoveScript(contents->GetRoutingID(), (*it).data()));
+        set.erase(it);
+    }
+    return true;
+}
+
+void UserScriptControllerHost::clearAllScripts(WebContentsAdapter *adapter)
+{
+    if (!adapter) {
+        m_profileWideScripts.clear();
+        Q_FOREACH (content::RenderProcessHost *renderer, m_observedProcesses)
+            renderer->Send(new UserScriptController_ClearScripts);
+    } else {
+        content::WebContents *contents = adapter->webContents();
+        m_perContentsScripts.remove(contents);
+        contents->Send(new RenderViewObserverHelper_ClearScripts(contents->GetRoutingID()));
+    }
+}
+
+const QSet<UserScript> UserScriptControllerHost::registeredScripts(WebContentsAdapter *adapter) const
+{
+    if (!adapter)
+        return m_profileWideScripts;
+    return m_perContentsScripts.value(adapter->webContents());
+}
+
+void UserScriptControllerHost::reserve(WebContentsAdapter *adapter, int count)
+{
+    if (!adapter)
+        m_profileWideScripts.reserve(count);
+    else
+        m_perContentsScripts[adapter->webContents()].reserve(count);
+}
+
+void UserScriptControllerHost::renderProcessHostCreated(content::RenderProcessHost *renderer)
+{
+    if (m_renderProcessObserver.isNull())
+        m_renderProcessObserver.reset(new RenderProcessObserverHelper(this));
+    renderer->AddObserver(m_renderProcessObserver.data());
+    m_observedProcesses.insert(renderer);
+    Q_FOREACH (const UserScript &script, m_profileWideScripts)
+        renderer->Send(new UserScriptController_AddScript(script.data()));
+}
+
+void UserScriptControllerHost::webContentsDestroyed(content::WebContents *contents)
+{
+    m_perContentsScripts.remove(contents);
+}
+
+UserScriptControllerHost::UserScriptControllerHost()
+{
+}
+
+UserScriptControllerHost::~UserScriptControllerHost()
+{
+}
diff --git a/src/core/user_script_controller_host.h b/src/core/user_script_controller_host.h
new file mode 100644
index 0000000000000000000000000000000000000000..790d40912b967f9b26b599c86aa5ee64b81a3766
--- /dev/null
+++ b/src/core/user_script_controller_host.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef USER_SCRIPT_CONTROLLER_HOST_H
+#define USER_SCRIPT_CONTROLLER_HOST_H
+
+#include "qtwebenginecoreglobal.h"
+
+#include <QtCore/QSet>
+#include <QtCore/QScopedPointer>
+#include "user_script.h"
+
+class WebContentsAdapterPrivate;
+namespace content {
+class RenderProcessHost;
+class WebContents;
+}
+class WebContentsAdapter;
+
+class QWEBENGINE_EXPORT UserScriptControllerHost {
+
+public:
+    UserScriptControllerHost();
+    ~UserScriptControllerHost();
+
+    void addUserScript(const UserScript &script, WebContentsAdapter *adapter);
+    bool containsUserScript(const UserScript &script, WebContentsAdapter *adapter);
+    bool removeUserScript(const UserScript &script, WebContentsAdapter *adapter);
+    void clearAllScripts(WebContentsAdapter *adapter);
+    void reserve(WebContentsAdapter *adapter, int count);
+    const QSet<UserScript> registeredScripts(WebContentsAdapter *adapter) const;
+
+    void renderProcessHostCreated(content::RenderProcessHost *renderer);
+
+private:
+    Q_DISABLE_COPY(UserScriptControllerHost)
+    class WebContentsObserverHelper;
+    class RenderProcessObserverHelper;
+
+    void webContentsDestroyed(content::WebContents *);
+
+    QSet<UserScript> m_profileWideScripts;
+    typedef QHash<content::WebContents *, QSet<UserScript>> ContentsScriptsMap;
+    ContentsScriptsMap m_perContentsScripts;
+    QSet<content::RenderProcessHost *> m_observedProcesses;
+    QScopedPointer<RenderProcessObserverHelper> m_renderProcessObserver;
+};
+
+#endif // USER_SCRIPT_CONTROLLER_HOST_H
diff --git a/src/core/web_contents_adapter_p.h b/src/core/web_contents_adapter_p.h
index e0b13013b58613446627cacbf2abf48161a1cbe8..6ddff47a525f14fa41a4c6dfb278ae5be9994310 100644
--- a/src/core/web_contents_adapter_p.h
+++ b/src/core/web_contents_adapter_p.h
@@ -46,6 +46,7 @@
 
 class BrowserContextAdapter;
 class QtRenderViewObserverHost;
+class UserScriptControllerHost;
 class WebChannelIPCTransportHost;
 class WebContentsAdapterClient;
 class WebContentsDelegateQt;
diff --git a/src/webenginewidgets/api/qwebenginepage.cpp b/src/webenginewidgets/api/qwebenginepage.cpp
index f2cc7e304d7fa0382201963c9314d85367e11cda..bdce0497806bc786b088a4a80b1ad8061f3210cd 100644
--- a/src/webenginewidgets/api/qwebenginepage.cpp
+++ b/src/webenginewidgets/api/qwebenginepage.cpp
@@ -30,6 +30,7 @@
 #include "qwebenginehistory_p.h"
 #include "qwebengineprofile.h"
 #include "qwebengineprofile_p.h"
+#include "qwebenginescriptcollection_p.h"
 #include "qwebenginesettings.h"
 #include "qwebengineview.h"
 #include "qwebengineview_p.h"
@@ -175,6 +176,7 @@ QWebEnginePagePrivate::QWebEnginePagePrivate(QWebEngineProfile *_profile)
     , settings(new QWebEngineSettings(profile->settings()))
     , view(0)
     , isLoading(false)
+    , scriptCollection(new QWebEngineScriptCollectionPrivate(browserContextAdapter()->userScriptController(), adapter.data()))
 {
     memset(actions, 0, sizeof(actions));
 }
@@ -977,6 +979,17 @@ void QWebEnginePage::runJavaScript(const QString& scriptSource, const QWebEngine
     d->m_callbacks.registerCallback(requestId, resultCallback.d);
 }
 
+/*!
+    Returns the script collection used by this page.
+    \sa QWebEngineScriptCollection
+*/
+
+QWebEngineScriptCollection &QWebEnginePage::scripts()
+{
+    Q_D(QWebEnginePage);
+    return d->scriptCollection;
+}
+
 QWebEnginePage *QWebEnginePage::createWindow(WebWindowType type)
 {
     Q_D(QWebEnginePage);
diff --git a/src/webenginewidgets/api/qwebenginepage.h b/src/webenginewidgets/api/qwebenginepage.h
index 7fcf000d977ab2c20e387a765c9dcc4e938b43d2..f2067c6aa356be771ce0a6010fd6c1b781a4dfd2 100644
--- a/src/webenginewidgets/api/qwebenginepage.h
+++ b/src/webenginewidgets/api/qwebenginepage.h
@@ -53,6 +53,7 @@ class QWebEngineHistory;
 class QWebEnginePage;
 class QWebEnginePagePrivate;
 class QWebEngineProfile;
+class QWebEngineScriptCollection;
 class QWebEngineSettings;
 
 namespace QtWebEnginePrivate {
@@ -230,7 +231,7 @@ public:
 #else
     void runJavaScript(const QString& scriptSource, const QWebEngineCallback<const QVariant &> &resultCallback);
 #endif
-
+    QWebEngineScriptCollection &scripts();
     QWebEngineSettings *settings() const;
 
     QWebChannel *webChannel() const;
diff --git a/src/webenginewidgets/api/qwebenginepage_p.h b/src/webenginewidgets/api/qwebenginepage_p.h
index e7bcedfc1f4f150fae833f73f5e0011c921caed2..dc07ce2c6fc9b8915c2dee8dc8a28f54668c15a3 100644
--- a/src/webenginewidgets/api/qwebenginepage_p.h
+++ b/src/webenginewidgets/api/qwebenginepage_p.h
@@ -39,6 +39,7 @@
 
 #include "qwebenginepage.h"
 
+#include "qwebenginescriptcollection.h"
 #include "web_contents_adapter_client.h"
 #include <QtCore/qcompilerdetection.h>
 #include <QSharedData>
@@ -163,6 +164,7 @@ public:
     QUrl explicitUrl;
     WebEngineContextMenuData m_menuData;
     bool isLoading;
+    QWebEngineScriptCollection scriptCollection;
 
     mutable CallbackDirectory m_callbacks;
     mutable QAction *actions[QWebEnginePage::WebActionCount];
diff --git a/src/webenginewidgets/api/qwebengineprofile.cpp b/src/webenginewidgets/api/qwebengineprofile.cpp
index 3de2fe521922ec47ca509cb113944674cee5c568..e2441ca542ac4cdc7b10b331972b59450324c0ae 100644
--- a/src/webenginewidgets/api/qwebengineprofile.cpp
+++ b/src/webenginewidgets/api/qwebengineprofile.cpp
@@ -42,6 +42,7 @@
 #include "qwebengineprofile_p.h"
 #include "qwebenginesettings.h"
 #include "qwebengineurlschemehandler_p_p.h"
+#include "qwebenginescriptcollection_p.h"
 
 #include "browser_context_adapter.h"
 #include "web_engine_visited_links_manager.h"
@@ -100,7 +101,8 @@ QT_BEGIN_NAMESPACE
 */
 
 QWebEngineProfilePrivate::QWebEngineProfilePrivate(BrowserContextAdapter* browserContext, bool ownsContext)
-        : m_settings(new QWebEngineSettings())
+        : scriptCollection(new QWebEngineScriptCollectionPrivate(browserContext->userScriptController()))
+        , m_settings(new QWebEngineSettings())
         , m_browserContext(browserContext)
 {
     if (ownsContext)
@@ -432,6 +434,16 @@ bool QWebEngineProfile::visitedLinksContainsUrl(const QUrl &url) const
     return d->browserContext()->visitedLinksManager()->containsUrl(url);
 }
 
+/*!
+    Returns the script collection used by this profile.
+    \sa QWebEngineScriptCollection
+*/
+QWebEngineScriptCollection &QWebEngineProfile::scripts()
+{
+    Q_D(QWebEngineProfile);
+    return d->scriptCollection;
+}
+
 /*!
     Returns the default profile.
 
diff --git a/src/webenginewidgets/api/qwebengineprofile.h b/src/webenginewidgets/api/qwebengineprofile.h
index a2523edad36d1864b08f0604ec4b4c3d8f646b46..a25cbcccd4f7fbdfe830fdb0279a81b28702d69c 100644
--- a/src/webenginewidgets/api/qwebengineprofile.h
+++ b/src/webenginewidgets/api/qwebengineprofile.h
@@ -52,6 +52,7 @@ class QWebEnginePage;
 class QWebEnginePagePrivate;
 class QWebEngineProfilePrivate;
 class QWebEngineSettings;
+class QWebEngineScriptCollection;
 
 class QWEBENGINEWIDGETS_EXPORT QWebEngineProfile : public QObject {
     Q_OBJECT
@@ -97,6 +98,7 @@ public:
     bool visitedLinksContainsUrl(const QUrl &url) const;
 
     QWebEngineSettings *settings() const;
+    QWebEngineScriptCollection &scripts();
 
     static QWebEngineProfile *defaultProfile();
 
diff --git a/src/webenginewidgets/api/qwebengineprofile_p.h b/src/webenginewidgets/api/qwebengineprofile_p.h
index 1fc2297c7cb193c47a976b3cb4a86a4550353584..6f2d53a813f42137960bf41a0566d31f15151d68 100644
--- a/src/webenginewidgets/api/qwebengineprofile_p.h
+++ b/src/webenginewidgets/api/qwebengineprofile_p.h
@@ -40,6 +40,7 @@
 #include "browser_context_adapter_client.h"
 #include "qwebengineprofile.h"
 #include "qwebengineurlschemehandler_p.h"
+#include "qwebenginescriptcollection.h"
 #include <QMap>
 #include <QPointer>
 
@@ -69,6 +70,7 @@ public:
     void removeUrlSchemeHandler(QWebEngineUrlSchemeHandler *);
     void clearUrlSchemeHandlers();
 
+    QWebEngineScriptCollection scriptCollection;
 private:
     QWebEngineProfile *q_ptr;
     QWebEngineSettings *m_settings;
diff --git a/src/webenginewidgets/api/qwebenginescript.cpp b/src/webenginewidgets/api/qwebenginescript.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..63459992b64bcb4dafa4e844b7e9168fa0f091e2
--- /dev/null
+++ b/src/webenginewidgets/api/qwebenginescript.cpp
@@ -0,0 +1,163 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwebenginescript.h"
+
+#include "user_script.h"
+#include <QtCore/QDebug>
+
+QWebEngineScript::QWebEngineScript()
+    : d(new UserScript)
+{
+}
+
+QWebEngineScript::QWebEngineScript(const QWebEngineScript &other)
+    : d(other.d)
+{
+}
+
+QWebEngineScript::~QWebEngineScript()
+{
+}
+
+QWebEngineScript &QWebEngineScript::operator=(const QWebEngineScript &other)
+{
+    d = other.d;
+    return *this;
+}
+
+bool QWebEngineScript::isNull() const
+{
+    return d->isNull();
+}
+
+QString QWebEngineScript::name() const
+{
+    return d->name();
+}
+
+void QWebEngineScript::setName(const QString &scriptName)
+{
+    if (scriptName == name())
+        return;
+    d->setName(scriptName);
+}
+
+QString QWebEngineScript::source() const
+{
+    return d->source();
+}
+
+void QWebEngineScript::setSource(const QString &scriptSource)
+{
+    if (scriptSource == source())
+        return;
+    d->setSource(scriptSource);
+}
+
+ASSERT_ENUMS_MATCH(QWebEngineScript::Deferred, UserScript::AfterLoad)
+ASSERT_ENUMS_MATCH(QWebEngineScript::DocumentReady, UserScript::DocumentLoadFinished)
+ASSERT_ENUMS_MATCH(QWebEngineScript::DocumentCreation, UserScript::DocumentElementCreation)
+
+QWebEngineScript::InjectionPoint QWebEngineScript::injectionPoint() const
+{
+    return static_cast<QWebEngineScript::InjectionPoint>(d->injectionPoint());
+}
+
+void QWebEngineScript::setInjectionPoint(QWebEngineScript::InjectionPoint p)
+{
+    if (p == injectionPoint())
+        return;
+    d->setInjectionPoint(static_cast<UserScript::InjectionPoint>(p));
+}
+
+quint32 QWebEngineScript::worldId() const
+{
+    return d->worldId();
+}
+
+void QWebEngineScript::setWorldId(quint32 id)
+{
+    if (id == d->worldId())
+        return;
+    d->setWorldId(id);
+}
+
+bool QWebEngineScript::runsOnSubFrames() const
+{
+    return d->runsOnSubFrames();
+}
+
+void QWebEngineScript::setRunsOnSubFrames(bool on)
+{
+    if (runsOnSubFrames() == on)
+        return;
+    d->setRunsOnSubFrames(on);
+}
+
+bool QWebEngineScript::operator==(const QWebEngineScript &other) const
+{
+    return d == other.d || *d == *(other.d);
+}
+
+QWebEngineScript::QWebEngineScript(const UserScript &coreScript)
+    : d(new UserScript(coreScript))
+{
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug d, const QWebEngineScript &script)
+{
+    if (script.isNull())
+        return d.maybeSpace() << "QWebEngineScript()";
+
+    d.nospace() << "QWebEngineScript(" << script.name() << ", ";
+    switch (script.injectionPoint()) {
+    case QWebEngineScript::DocumentCreation:
+        d << "QWebEngineScript::DocumentCreation" << ", ";
+        break;
+    case QWebEngineScript::DocumentReady:
+        d << "QWebEngineScript::DocumentReady" << ", ";
+        break;
+    case QWebEngineScript::Deferred:
+        d << "QWebEngineScript::Deferred" << ", ";
+        break;
+    }
+    d << script.worldId() << ", "
+      << script.runsOnSubFrames() << ", " << script.source() << ")";
+    return d.space();
+}
+#endif
diff --git a/src/webenginewidgets/api/qwebenginescript.h b/src/webenginewidgets/api/qwebenginescript.h
new file mode 100644
index 0000000000000000000000000000000000000000..735ee92e0b79c6fb7fcfbbfc7d84f5f6213ca3db
--- /dev/null
+++ b/src/webenginewidgets/api/qwebenginescript.h
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWEBENGINESCRIPT_H
+#define QWEBENGINESCRIPT_H
+#include "qtwebenginewidgetsglobal.h"
+
+#include <QtCore/QSharedDataPointer>
+#include <QtCore/QString>
+
+class UserScript;
+QT_BEGIN_NAMESPACE
+
+class QWEBENGINEWIDGETS_EXPORT QWebEngineScript {
+public:
+    enum InjectionPoint {
+        Deferred,
+        DocumentReady,
+        DocumentCreation
+    };
+
+    enum ScriptWorldId {
+        MainWorld = 0,
+        ApplicationWorld,
+        UserWorld
+    };
+
+    QWebEngineScript();
+    QWebEngineScript(const QWebEngineScript &other);
+    ~QWebEngineScript();
+
+    QWebEngineScript &operator=(const QWebEngineScript &other);
+
+    bool isNull() const;
+
+    QString name() const;
+    void setName(const QString &);
+
+    QString source() const;
+    void setSource(const QString &);
+
+    InjectionPoint injectionPoint() const;
+    void setInjectionPoint(InjectionPoint);
+
+    quint32 worldId() const;
+    void setWorldId(quint32);
+
+    bool runsOnSubFrames() const;
+    void setRunsOnSubFrames(bool on);
+
+    bool operator==(const QWebEngineScript &other) const;
+    inline bool operator!=(const QWebEngineScript &other) const
+    { return !operator==(other); }
+    void swap(QWebEngineScript &other) { qSwap(d, other.d); }
+
+
+private:
+    friend class QWebEngineScriptCollectionPrivate;
+    friend class QWebEngineScriptCollection;
+    QWebEngineScript(const UserScript &);
+
+    QSharedDataPointer<UserScript> d;
+};
+
+Q_DECLARE_SHARED(QWebEngineScript)
+
+#ifndef QT_NO_DEBUG_STREAM
+QWEBENGINEWIDGETS_EXPORT QDebug operator<<(QDebug, const QWebEngineScript &);
+#endif
+
+QT_END_NAMESPACE
+
+#endif // QWEBENGINESCRIPT_H
diff --git a/src/webenginewidgets/api/qwebenginescriptcollection.cpp b/src/webenginewidgets/api/qwebenginescriptcollection.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ffbb9052e0b20453c3a289262327c062c3e09f07
--- /dev/null
+++ b/src/webenginewidgets/api/qwebenginescriptcollection.cpp
@@ -0,0 +1,154 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwebenginescriptcollection.h"
+#include "qwebenginescriptcollection_p.h"
+
+#include "user_script_controller_host.h"
+
+QWebEngineScriptCollection::QWebEngineScriptCollection(QWebEngineScriptCollectionPrivate *collectionPrivate)
+    :d(collectionPrivate)
+{
+}
+
+QWebEngineScriptCollection::~QWebEngineScriptCollection()
+{
+}
+
+int QWebEngineScriptCollection::count() const
+{
+    return d->count();
+}
+
+bool QWebEngineScriptCollection::contains(const QWebEngineScript &value) const
+{
+    return d->contains(value);
+}
+
+QWebEngineScript QWebEngineScriptCollection::findScript(const QString &name) const
+{
+    return d->find(name);
+}
+
+QList<QWebEngineScript> QWebEngineScriptCollection::findScripts(const QString &name) const
+{
+    return d->toList(name);
+}
+
+void QWebEngineScriptCollection::insert(const QWebEngineScript &s)
+{
+    d->insert(s);
+}
+
+void QWebEngineScriptCollection::insert(const QList<QWebEngineScript> &list)
+{
+    d->reserve(list.size());
+    Q_FOREACH (const QWebEngineScript &s, list)
+        d->insert(s);
+}
+
+bool QWebEngineScriptCollection::remove(const QWebEngineScript &script)
+{
+    return d->remove(script);
+}
+
+void QWebEngineScriptCollection::clear()
+{
+    d->clear();
+}
+
+QList<QWebEngineScript> QWebEngineScriptCollection::toList() const
+{
+    return d->toList();
+}
+
+
+QWebEngineScriptCollectionPrivate::QWebEngineScriptCollectionPrivate(UserScriptControllerHost *controller, WebContentsAdapter *webContents)
+    : m_scriptController(controller)
+    , m_contents(webContents)
+{
+}
+
+int QWebEngineScriptCollectionPrivate::count() const
+{
+    return m_scriptController->registeredScripts(m_contents).count();
+}
+
+bool QWebEngineScriptCollectionPrivate::contains(const QWebEngineScript &s) const
+{
+    return m_scriptController->containsUserScript(*s.d, m_contents);
+}
+
+void QWebEngineScriptCollectionPrivate::insert(const QWebEngineScript &script)
+{
+    if (!script.d)
+        return;
+    m_scriptController->addUserScript(*script.d, m_contents);
+}
+
+bool QWebEngineScriptCollectionPrivate::remove(const QWebEngineScript &script)
+{
+    if (!script.d)
+        return false;
+    return m_scriptController->removeUserScript(*script.d, m_contents);
+}
+
+QList<QWebEngineScript> QWebEngineScriptCollectionPrivate::toList(const QString &scriptName) const
+{
+    QList<QWebEngineScript> ret;
+    Q_FOREACH (const UserScript &script, m_scriptController->registeredScripts(m_contents))
+        if (scriptName.isNull() || scriptName == script.name())
+            ret.append(QWebEngineScript(script));
+    return ret;
+}
+
+QWebEngineScript QWebEngineScriptCollectionPrivate::find(const QString &name) const
+{
+    Q_FOREACH (const UserScript &script, m_scriptController->registeredScripts(m_contents))
+        if (name == script.name())
+            return QWebEngineScript(script);
+    return QWebEngineScript();
+}
+
+void QWebEngineScriptCollectionPrivate::clear()
+{
+    m_scriptController->clearAllScripts(m_contents);
+}
+
+void QWebEngineScriptCollectionPrivate::reserve(int capacity)
+{
+    m_scriptController->reserve(m_contents, capacity);
+}
diff --git a/src/webenginewidgets/api/qwebenginescriptcollection.h b/src/webenginewidgets/api/qwebenginescriptcollection.h
new file mode 100644
index 0000000000000000000000000000000000000000..c8e40c5daaef48a4ae63d89c9405ac012767390f
--- /dev/null
+++ b/src/webenginewidgets/api/qwebenginescriptcollection.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWEBENGINESCRIPTCOLLECTION_H
+#define QWEBENGINESCRIPTCOLLECTION_H
+
+#include "qtwebengineglobal.h"
+
+#include "qwebenginescript.h"
+#include <QtCore/QScopedPointer>
+#include <QtCore/QList>
+#include <QtCore/QSet>
+
+QT_BEGIN_NAMESPACE
+class QWebEngineScriptCollectionPrivate;
+
+class Q_WEBENGINE_EXPORT QWebEngineScriptCollection {
+public:
+    ~QWebEngineScriptCollection();
+    bool isEmpty() const { return !count(); }
+    int count() const;
+    inline int size() const { return count(); }
+    bool contains(const QWebEngineScript &value) const;
+
+    QWebEngineScript findScript(const QString &name) const;
+    QList<QWebEngineScript> findScripts(const QString &name) const;
+
+    void insert(const QWebEngineScript &);
+    void insert(const QList<QWebEngineScript> &list);
+
+    bool remove(const QWebEngineScript &);
+    void clear();
+
+    QList<QWebEngineScript> toList() const;
+
+private:
+    friend class QWebEnginePagePrivate;
+    friend class QWebEngineProfilePrivate;
+    QWebEngineScriptCollection(QWebEngineScriptCollectionPrivate *);
+
+    QScopedPointer<QWebEngineScriptCollectionPrivate> d;
+};
+
+QT_END_NAMESPACE
+#endif // QWEBENGINESCRIPTCOLLECTION_H
diff --git a/src/webenginewidgets/api/qwebenginescriptcollection_p.h b/src/webenginewidgets/api/qwebenginescriptcollection_p.h
new file mode 100644
index 0000000000000000000000000000000000000000..baf09dbb473ce3942d0917238dcb126e9ab6e5f7
--- /dev/null
+++ b/src/webenginewidgets/api/qwebenginescriptcollection_p.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWEBENGINESCRIPTCOLLECTION_P_H
+#define QWEBENGINESCRIPTCOLLECTION_P_H
+
+#include "qtwebengineglobal.h"
+
+#include "qwebenginescript.h"
+
+#include <QtCore/QSet>
+
+class UserScriptControllerHost;
+class WebContentsAdapter;
+
+QT_BEGIN_NAMESPACE
+class QWebEngineScriptCollectionPrivate {
+public:
+    QWebEngineScriptCollectionPrivate(UserScriptControllerHost *, WebContentsAdapter * = 0);
+
+    int count() const;
+    bool contains(const QWebEngineScript &) const;
+    QList<QWebEngineScript> toList(const QString &scriptName = QString()) const;
+    QWebEngineScript find(const QString & name) const;
+
+    void insert(const QWebEngineScript &);
+    bool remove(const QWebEngineScript &);
+    void clear();
+    void reserve(int);
+
+private:
+    UserScriptControllerHost *m_scriptController;
+    WebContentsAdapter *m_contents;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWEBENGINESCRIPTCOLLECTION__PH
diff --git a/src/webenginewidgets/webenginewidgets.pro b/src/webenginewidgets/webenginewidgets.pro
index 7fa63070276b44c66312595ef59da4c708f78216..02e687c7c8a212e35914258cbca95d47c38dd993 100644
--- a/src/webenginewidgets/webenginewidgets.pro
+++ b/src/webenginewidgets/webenginewidgets.pro
@@ -17,6 +17,8 @@ SOURCES = \
         api/qwebenginehistory.cpp \
         api/qwebenginepage.cpp \
         api/qwebengineprofile.cpp \
+        api/qwebenginescript.cpp \
+        api/qwebenginescriptcollection.cpp \
         api/qwebenginesettings.cpp \
         api/qwebengineurlrequestjob.cpp \
         api/qwebengineurlschemehandler.cpp \
@@ -33,6 +35,8 @@ HEADERS = \
         api/qwebenginepage_p.h \
         api/qwebengineprofile.h \
         api/qwebengineprofile_p.h \
+        api/qwebenginescriptcollection.h \
+        api/qwebenginescriptcollection_p.h \
         api/qwebenginesettings.h \
         api/qwebengineurlrequestjob_p.h \
         api/qwebengineurlschemehandler_p.h \