diff --git a/examples/quick/quicknanobrowser/ContextMenuExtras.qml b/examples/quick/quicknanobrowser/ContextMenuExtras.qml
new file mode 100644
index 0000000000000000000000000000000000000000..cf54e9ba0d4cd3ca056d3c2267df843d856fbccc
--- /dev/null
+++ b/examples/quick/quicknanobrowser/ContextMenuExtras.qml
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**   * Redistributions of source code must retain the above copyright
+**     notice, this list of conditions and the following disclaimer.
+**   * Redistributions in binary form must reproduce the above copyright
+**     notice, this list of conditions and the following disclaimer in
+**     the documentation and/or other materials provided with the
+**     distribution.
+**   * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+**     of its contributors may be used to endorse or promote products derived
+**     from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.1
+import QtWebEngine.UIDelegates 1.0
+
+VisualItemModel {
+    MenuItem {
+        text: "An application specific entry"
+        onTriggered: console.log("Application specific action triggered")
+    }
+    Menu {
+        title: "Extras Submenu"
+        MenuItem {
+            text: "something"
+            onTriggered: console.log("something triggered")
+        }
+        MenuItem {
+            text: "something else"
+            enabled: false
+        }
+    }
+}
+
diff --git a/examples/quick/quicknanobrowser/quicknanobrowser.pro b/examples/quick/quicknanobrowser/quicknanobrowser.pro
index 2c4ae9080268ed509bf8efd5055e033c5bf034cc..3455f02e04c7f70b63706cdf8d0fd2f00356f470 100644
--- a/examples/quick/quicknanobrowser/quicknanobrowser.pro
+++ b/examples/quick/quicknanobrowser/quicknanobrowser.pro
@@ -4,9 +4,11 @@ TARGET = quicknanobrowser
 macx: CONFIG -= app_bundle
 
 HEADERS = quickwindow.h
-SOURCES = quickwindow.cpp main.cpp
+SOURCES = quickwindow.cpp \
+          main.cpp
 
-OTHER_FILES += quickwindow.qml
+OTHER_FILES += ContextMenuExtras.qml \
+               quickwindow.qml
 
 RESOURCES += resources.qrc
 RESOURCES += ../../common/common_resources.qrc
diff --git a/examples/quick/quicknanobrowser/quickwindow.qml b/examples/quick/quicknanobrowser/quickwindow.qml
index b99b6ff25cac188ed64d24186cb2efe144d6bad7..0aff9e36acad813107a776fcab8f0f69361bac83 100644
--- a/examples/quick/quicknanobrowser/quickwindow.qml
+++ b/examples/quick/quicknanobrowser/quickwindow.qml
@@ -38,7 +38,7 @@
 **
 ****************************************************************************/
 
-import QtQuick 2.0
+import QtQuick 2.1
 import QtWebEngine 1.0
 import QtWebEngine.experimental 1.0
 import QtQuick.Controls 1.0
@@ -177,6 +177,7 @@ ApplicationWindow {
                             window.adoptHandle(newViewHandle)
                         }
                     }
+                    extraContextMenuEntriesComponent: ContextMenuExtras {}
                 }
             }
         }
diff --git a/examples/quick/quicknanobrowser/resources.qrc b/examples/quick/quicknanobrowser/resources.qrc
index de8232440899594fda2b62c5848c0ba4ef7f69f5..5294b7ff66e8aaa06782a522d7542a909257f953 100644
--- a/examples/quick/quicknanobrowser/resources.qrc
+++ b/examples/quick/quicknanobrowser/resources.qrc
@@ -1,5 +1,6 @@
 <!DOCTYPE RCC><RCC version="1.0">
-<qresource>
-    <file>quickwindow.qml</file>
-</qresource>
+    <qresource prefix="/">
+        <file>quickwindow.qml</file>
+        <file>ContextMenuExtras.qml</file>
+    </qresource>
 </RCC>
diff --git a/src/src.pro b/src/src.pro
index 59fcce66f12b80fb60d15ba1c44e1f78c9465a25..536fcad5cb290eba4791f44cf9fcdc23cb41be26 100644
--- a/src/src.pro
+++ b/src/src.pro
@@ -17,6 +17,10 @@ SUBDIRS += core \
            webengine_plugin \
            webengine_experimental_plugin
 
+# FIXME: We probably want a bit more control over config options to tweak what to build/ship or not.
+# Another example of where this could be necessary is to make it easy to build proprietery codecs support.
+!contains(WEBENGINE_CONFIG, no_ui_delegates): SUBDIRS += webengine/ui
+
 qtHaveModule(widgets) {
     SUBDIRS += webenginewidgets
 }
diff --git a/src/webengine/api/qquickwebengineview.cpp b/src/webengine/api/qquickwebengineview.cpp
index ac2dc681e5bb3d8366b17da9d63b7950e72d1d7f..1dcc2acd00f395f8e327849e9be142078b638eaa 100644
--- a/src/webengine/api/qquickwebengineview.cpp
+++ b/src/webengine/api/qquickwebengineview.cpp
@@ -44,20 +44,29 @@
 
 #include "net/base/net_errors.h"
 #include "qquickwebengineloadrequest_p.h"
+#include "render_widget_host_view_qt_delegate_quick.h"
+#include "ui_delegates_manager.h"
 #include "web_contents_adapter.h"
 #include "web_engine_error.h"
-#include "render_widget_host_view_qt_delegate_quick.h"
 
+#include <QQmlEngine>
+#include <QQmlComponent>
+#include <QQmlContext>
+#include <QQmlProperty>
 #include <QScreen>
+#include <QStringBuilder>
 #include <QUrl>
 #include <QQmlEngine>
 
+#include <private/qqmlmetatype_p.h>
+
 QT_BEGIN_NAMESPACE
 
 QQuickWebEngineViewPrivate::QQuickWebEngineViewPrivate()
     : adapter(new WebContentsAdapter(qApp->property("QQuickWebEngineView_DisableHardwareAcceleration").toBool() ? SoftwareRenderingMode : HardwareAccelerationMode))
     , e(new QQuickWebEngineViewExperimental(this))
     , v(new QQuickWebEngineViewport(this))
+    , contextMenuExtraItems(0)
     , loadProgress(0)
     , inspectable(false)
     , m_isLoading(false)
@@ -94,6 +103,14 @@ QQuickWebEngineViewport *QQuickWebEngineViewPrivate::viewport() const
     return v.data();
 }
 
+UIDelegatesManager *QQuickWebEngineViewPrivate::ui()
+{
+    Q_Q(QQuickWebEngineView);
+    if (m_uIDelegatesManager.isNull())
+        m_uIDelegatesManager.reset(new UIDelegatesManager(q));
+    return m_uIDelegatesManager.data();
+}
+
 RenderWidgetHostViewQtDelegate *QQuickWebEngineViewPrivate::CreateRenderWidgetHostViewQtDelegate(RenderWidgetHostViewQtDelegateClient *client, RenderingMode mode)
 {
 #if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0))
@@ -103,6 +120,63 @@ RenderWidgetHostViewQtDelegate *QQuickWebEngineViewPrivate::CreateRenderWidgetHo
     return new RenderWidgetHostViewQtDelegateQuickPainted(client);
 }
 
+bool QQuickWebEngineViewPrivate::contextMenuRequested(const WebEngineContextMenuData &data)
+{
+    Q_Q(QQuickWebEngineView);
+
+    QObject *menu = ui()->addMenu(0, QString(), data.pos);
+    if (!menu)
+        return false;
+
+    // Populate our menu
+    MenuItemHandler *item = 0;
+
+    if (data.selectedText.isEmpty()) {
+        item = new MenuItemHandler(menu);
+        QObject::connect(item, &MenuItemHandler::triggered, q, &QQuickWebEngineView::goBack);
+        ui()->addMenuItem(item, QObject::tr("Back"), QStringLiteral("go-previous"), q->canGoBack());
+
+        item = new MenuItemHandler(menu);
+        QObject::connect(item, &MenuItemHandler::triggered, q, &QQuickWebEngineView::goForward);
+        ui()->addMenuItem(item, QObject::tr("Forward"), QStringLiteral("go-next"), q->canGoForward());
+
+        item = new MenuItemHandler(menu);
+        QObject::connect(item, &MenuItemHandler::triggered, q, &QQuickWebEngineView::reload);
+        ui()->addMenuItem(item, QObject::tr("Reload"), QStringLiteral("view-refresh"));
+    } else {
+        item = new CopyMenuItem(menu, data.selectedText);
+        ui()->addMenuItem(item, QObject::tr("Copy..."));
+    }
+
+    if (!data.linkText.isEmpty() && data.linkUrl.isValid()) {
+        item = new NavigateMenuItem(menu, adapter, data.linkUrl);
+        ui()->addMenuItem(item, QObject::tr("Navigate to..."));
+        item = new CopyMenuItem(menu, data.linkUrl.toString());
+        ui()->addMenuItem(item, QObject::tr("Copy link address"));
+    }
+
+    // FIXME: expose the context menu data as an attached property to make this more useful
+    if (contextMenuExtraItems) {
+        ui()->addMenuSeparator(menu);
+        if (QObject* menuExtras = contextMenuExtraItems->create(ui()->creationContextForComponent(contextMenuExtraItems))) {
+            menuExtras->setParent(menu);
+            QQmlListReference entries(menu, QQmlMetaType::defaultProperty(menu).name(), qmlEngine(q));
+            if (entries.isValid())
+                entries.append(menuExtras);
+        }
+    }
+
+    // Now fire the popup() method on the top level menu
+    QMetaObject::invokeMethod(menu, "popup");
+    return true;
+}
+
+bool QQuickWebEngineViewPrivate::javascriptDialog(JavascriptDialogType type, const QString &message, const QString &defaultValue, QString *result)
+{
+    Q_UNUSED(message); Q_UNUSED(defaultValue); Q_UNUSED(result);
+    return false;
+}
+
 void QQuickWebEngineViewPrivate::titleChanged(const QString &title)
 {
     Q_Q(QQuickWebEngineView);
@@ -338,6 +412,19 @@ void QQuickWebEngineView::setInspectable(bool enable)
     d->adapter->enableInspector(enable);
 }
 
+void QQuickWebEngineViewExperimental::setExtraContextMenuEntriesComponent(QQmlComponent *contextMenuExtras)
+{
+    if (d_ptr->contextMenuExtraItems == contextMenuExtras)
+        return;
+    d_ptr->contextMenuExtraItems = contextMenuExtras;
+    emit extraContextMenuEntriesComponentChanged();
+}
+
+QQmlComponent *QQuickWebEngineViewExperimental::extraContextMenuEntriesComponent() const
+{
+    return d_ptr->contextMenuExtraItems;
+}
+
 void QQuickWebEngineView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
 {
     QQuickItem::geometryChanged(newGeometry, oldGeometry);
diff --git a/src/webengine/api/qquickwebengineview_p_p.h b/src/webengine/api/qquickwebengineview_p_p.h
index 110a1b9b607eb1f3a41deb03e5c7412034c4a740..67337baeb1cf564d6b424fffed3aef34c816615a 100644
--- a/src/webengine/api/qquickwebengineview_p_p.h
+++ b/src/webengine/api/qquickwebengineview_p_p.h
@@ -47,13 +47,17 @@
 
 #include <QScopedPointer>
 #include <QSharedData>
+#include <QString>
 #include <QtCore/qcompilerdetection.h>
 #include <QtQuick/private/qquickitem_p.h>
 
 class WebContentsAdapter;
+class UIDelegatesManager;
 
 QT_BEGIN_NAMESPACE
 class QQuickWebEngineView;
+class QQmlComponent;
+class QQmlContext;
 
 class Q_WEBENGINE_PRIVATE_EXPORT QQuickWebEngineViewport : public QObject {
     Q_OBJECT
@@ -88,12 +92,17 @@ private:
 class Q_WEBENGINE_PRIVATE_EXPORT QQuickWebEngineViewExperimental : public QObject {
     Q_OBJECT
     Q_PROPERTY(QQuickWebEngineViewport *viewport READ viewport)
+    Q_PROPERTY(QQmlComponent *extraContextMenuEntriesComponent READ extraContextMenuEntriesComponent WRITE setExtraContextMenuEntriesComponent NOTIFY extraContextMenuEntriesComponentChanged)
+
 public:
     QQuickWebEngineViewport *viewport() const;
     Q_INVOKABLE void adoptHandle(QQuickWebEngineViewHandle *viewHandle);
+    void setExtraContextMenuEntriesComponent(QQmlComponent *);
+    QQmlComponent *extraContextMenuEntriesComponent() const;
 
 Q_SIGNALS:
     void createWindow(const QJSValue &newViewHandle, const QString &newViewDisposition);
+    void extraContextMenuEntriesComponentChanged();
 
 private:
     QQuickWebEngineViewExperimental(QQuickWebEngineViewPrivate* viewPrivate);
@@ -112,6 +121,7 @@ public:
 
     QQuickWebEngineViewExperimental *experimental() const;
     QQuickWebEngineViewport *viewport() const;
+    UIDelegatesManager *ui();
 
     virtual RenderWidgetHostViewQtDelegate* CreateRenderWidgetHostViewQtDelegate(RenderWidgetHostViewQtDelegateClient *client, RenderingMode) Q_DECL_OVERRIDE;
     virtual void titleChanged(const QString&) Q_DECL_OVERRIDE;
@@ -125,8 +135,8 @@ public:
     virtual void focusContainer() Q_DECL_OVERRIDE;
     virtual void adoptNewWindow(WebContentsAdapter *newWebContents, WindowOpenDisposition disposition, const QRect &) Q_DECL_OVERRIDE;
     virtual void close() Q_DECL_OVERRIDE;
-    virtual bool contextMenuRequested(const WebEngineContextMenuData &) Q_DECL_OVERRIDE { return false;}
-    virtual bool javascriptDialog(JavascriptDialogType type, const QString &message, const QString &defaultValue = QString(), QString *result = 0) Q_DECL_OVERRIDE { return false; }
+    virtual bool contextMenuRequested(const WebEngineContextMenuData &) Q_DECL_OVERRIDE;
+    virtual bool javascriptDialog(JavascriptDialogType, const QString &message, const QString &defaultValue = QString(), QString *result = 0) Q_DECL_OVERRIDE { Q_UNUSED(message); Q_UNUSED(defaultValue); Q_UNUSED(result); return false; }
     virtual void runFileChooser(FileChooserMode, const QString &defaultFileName, const QStringList &acceptedMimeTypes) { Q_UNUSED(defaultFileName); Q_UNUSED(acceptedMimeTypes);}
 
     void setDevicePixelRatio(qreal);
@@ -134,6 +144,7 @@ public:
     QExplicitlySharedDataPointer<WebContentsAdapter> adapter;
     QScopedPointer<QQuickWebEngineViewExperimental> e;
     QScopedPointer<QQuickWebEngineViewport> v;
+    QQmlComponent *contextMenuExtraItems;
     QUrl icon;
     int loadProgress;
     bool inspectable;
@@ -141,6 +152,7 @@ public:
     qreal devicePixelRatio;
 
 private:
+    QScopedPointer<UIDelegatesManager> m_uIDelegatesManager;
     qreal m_dpiScale;
 };
 
diff --git a/src/webengine/ui/Menu.qml b/src/webengine/ui/Menu.qml
new file mode 100644
index 0000000000000000000000000000000000000000..e9cd25ed17ff0b07fe5c8d8909a9310eae5040a2
--- /dev/null
+++ b/src/webengine/ui/Menu.qml
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the demonstration applications 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 Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 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 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.1
+import QtQuick.Controls 1.0 as Controls
+
+Controls.Menu {
+    signal done()
+
+    // Use private API for now
+    on__MenuClosed: done();
+}
diff --git a/src/webengine/ui/MenuItem.qml b/src/webengine/ui/MenuItem.qml
new file mode 100644
index 0000000000000000000000000000000000000000..25b4ecb2b32294ae4b772321f54c9d3aa6c6c83d
--- /dev/null
+++ b/src/webengine/ui/MenuItem.qml
@@ -0,0 +1,46 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the demonstration applications 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 Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 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 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.1
+import QtQuick.Controls 1.0 as Controls
+
+Controls.MenuItem { }
+
diff --git a/src/webengine/ui/MenuSeparator.qml b/src/webengine/ui/MenuSeparator.qml
new file mode 100644
index 0000000000000000000000000000000000000000..b04d61b358d8d04ada15d6eca9253ff6f7313de9
--- /dev/null
+++ b/src/webengine/ui/MenuSeparator.qml
@@ -0,0 +1,45 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the demonstration applications 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 Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 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 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.1
+import QtQuick.Controls 1.0 as Controls
+
+Controls.MenuSeparator { }
diff --git a/src/webengine/ui/qmldir b/src/webengine/ui/qmldir
new file mode 100644
index 0000000000000000000000000000000000000000..bc0e2e300dbc283e3df71a7a78720b5ca6d96ff1
--- /dev/null
+++ b/src/webengine/ui/qmldir
@@ -0,0 +1,4 @@
+module QtWebEngine.UIDelegates
+Menu 1.0 Menu.qml
+MenuItem 1.0 MenuItem.qml
+MenuSeparator 1.0 MenuSeparator.qml
diff --git a/src/webengine/ui/ui.pro b/src/webengine/ui/ui.pro
new file mode 100644
index 0000000000000000000000000000000000000000..42a5777dcec6425ab40570a56b7d266b5d9b57b9
--- /dev/null
+++ b/src/webengine/ui/ui.pro
@@ -0,0 +1,8 @@
+TARGETPATH = QtWebEngine/UIDelegates
+
+QML_FILES += \
+    Menu.qml \
+    MenuItem.qml \
+    MenuSeparator.qml
+
+load(qml_module)
diff --git a/src/webengine/ui_delegates_manager.cpp b/src/webengine/ui_delegates_manager.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..34edbd4790571ccfdc3c5afff47ec582581e799a
--- /dev/null
+++ b/src/webengine/ui_delegates_manager.cpp
@@ -0,0 +1,233 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 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 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "ui_delegates_manager.h"
+#include "api/qquickwebengineview_p.h"
+
+#include <QAbstractListModel>
+#include <QClipboard>
+#include <QFileInfo>
+#include <QGuiApplication>
+#include <QQmlContext>
+#include <QQmlEngine>
+#include <QQmlProperty>
+#include <QStringBuilder>
+#include <private/qqmlmetatype_p.h>
+
+// Uncomment for QML debugging
+//#define UI_DELEGATES_DEBUG
+
+#define NO_SEPARATOR
+#define FILE_NAME_CASE_STATEMENT(TYPE, COMPONENT) \
+    case UIDelegatesManager::TYPE:\
+        return QStringLiteral(#TYPE".qml");
+
+static QString fileNameForComponent(UIDelegatesManager::ComponentType type)
+{
+    switch (type) {
+    FOR_EACH_COMPONENT_TYPE(FILE_NAME_CASE_STATEMENT, NO_SEPARATOR)
+    default:
+        Q_UNREACHABLE();
+    }
+    return QString();
+}
+
+MenuItemHandler::MenuItemHandler(QObject *parent)
+    : QObject(parent)
+{
+}
+
+
+CopyMenuItem::CopyMenuItem(QObject *parent, const QString &textToCopy)
+    : MenuItemHandler(parent)
+    , m_textToCopy(textToCopy)
+{
+    connect(this, &MenuItemHandler::triggered, this, &CopyMenuItem::onTriggered);
+}
+
+void CopyMenuItem::onTriggered()
+{
+    qApp->clipboard()->setText(m_textToCopy);
+}
+
+NavigateMenuItem::NavigateMenuItem(QObject *parent, const QExplicitlySharedDataPointer<WebContentsAdapter> &adapter, const QUrl &targetUrl)
+    : MenuItemHandler(parent)
+    , m_adapter(adapter)
+    , m_targetUrl(targetUrl)
+{
+    connect(this, &MenuItemHandler::triggered, this, &NavigateMenuItem::onTriggered);
+}
+
+void NavigateMenuItem::onTriggered()
+{
+    m_adapter->load(m_targetUrl);
+}
+
+UIDelegatesManager::UIDelegatesManager(QQuickWebEngineView *view)
+    : m_view(view)
+{
+}
+
+#define COMPONENT_MEMBER_CASE_STATEMENT(TYPE, COMPONENT) \
+    case TYPE: \
+        component = &COMPONENT##Component; \
+        break;
+
+bool UIDelegatesManager::ensureComponentLoaded(ComponentType type)
+{
+    QScopedPointer<QQmlComponent> *component;
+    switch (type) {
+    FOR_EACH_COMPONENT_TYPE(COMPONENT_MEMBER_CASE_STATEMENT, NO_SEPARATOR)
+    default:
+        Q_UNREACHABLE();
+        return false;
+    }
+    QString fileName(fileNameForComponent(type));
+#ifndef UI_DELEGATES_DEBUG
+    if (!(*component).isNull())
+        return true;
+#else // Unconditionally reload the components each time.
+    fprintf(stderr, "%s: %s\n", Q_FUNC_INFO, qPrintable(fileName));
+#endif
+    QQmlEngine* engine = qmlEngine(m_view);
+    if (!engine)
+        return false;
+    QString absolutePath;
+    Q_FOREACH (const QString &path, engine->importPathList()) {
+        QFileInfo fi(path % QStringLiteral("/QtWebEngine/UIDelegates/") % fileName);
+        if (fi.exists())
+            absolutePath = fi.absoluteFilePath();
+    }
+    // FIXME: handle async loading
+    (*component).reset(new QQmlComponent(engine, QUrl(absolutePath), QQmlComponent::PreferSynchronous, m_view));
+
+    if ((*component)->status() != QQmlComponent::Ready) {
+#ifdef UI_DELEGATES_DEBUG
+        Q_FOREACH (const QQmlError& err, (*component)->errors())
+            fprintf(stderr, "  component error: %s\n", qPrintable(err.toString()));
+#endif
+        return false;
+    }
+    return true;
+}
+
+QQmlContext *UIDelegatesManager::creationContextForComponent(QQmlComponent *component)
+{
+    Q_ASSERT(component);
+
+    QQmlContext* baseContext = component->creationContext() ? component->creationContext() : qmlContext(m_view);
+    Q_ASSERT(baseContext);
+    return baseContext;
+}
+
+#define CHECK_QML_SIGNAL_PROPERTY(prop, type, location) \
+    if (!prop.isSignalProperty()) \
+        qWarning(#type "component (Loaded from %s) is missing %s signal property.\n", qPrintable(location.toString()), qPrintable(prop.name()));
+
+void UIDelegatesManager::addMenuItem(MenuItemHandler *menuItemHandler, const QString &text, const QString &iconName, bool enabled)
+{
+    Q_ASSERT(menuItemHandler);
+    if (!ensureComponentLoaded(MenuItem))
+        return;
+    QObject *it = menuItemComponent->beginCreate(creationContextForComponent(menuItemComponent.data()));
+
+    QQmlProperty(it, QStringLiteral("text")).write(text);
+    QQmlProperty(it, QStringLiteral("iconName")).write(iconName);
+    QQmlProperty(it, QStringLiteral("enabled")).write(enabled);
+
+    QQmlProperty signal(it, QStringLiteral("onTriggered"));
+    CHECK_QML_SIGNAL_PROPERTY(signal, menuItemComponent->url());
+    QObject::connect(it, signal.method(), menuItemHandler, QMetaMethod::fromSignal(&MenuItemHandler::triggered));
+    menuItemComponent->completeCreate();
+
+    QObject *menu = menuItemHandler->parent();
+    it->setParent(menu);
+
+    QQmlListReference entries(menu, QQmlMetaType::defaultProperty(menu).name(), qmlEngine(m_view));
+    if (entries.isValid())
+        entries.append(it);
+}
+
+void UIDelegatesManager::addMenuSeparator(QObject *menu)
+{
+    if (!ensureComponentLoaded(MenuSeparator))
+        return;
+
+    QQmlContext *itemContext = creationContextForComponent(menuSeparatorComponent.data());
+    QObject *sep = menuSeparatorComponent->create(itemContext);
+    sep->setParent(menu);
+
+    QQmlListReference entries(menu, QQmlMetaType::defaultProperty(menu).name(), qmlEngine(m_view));
+    if (entries.isValid())
+        entries.append(sep);
+}
+
+QObject *UIDelegatesManager::addMenu(QObject *parentMenu, const QString &title, const QPoint& pos)
+{
+
+    if (!ensureComponentLoaded(Menu))
+        return 0;
+    QQmlContext *context(creationContextForComponent(menuComponent.data()));
+    QObject *menu = menuComponent->beginCreate(context);
+    // Useful when not using Qt Quick Controls' Menu
+    if (QQuickItem* item = qobject_cast<QQuickItem*>(menu))
+        item->setParentItem(m_view);
+
+    if (!title.isEmpty())
+        QQmlProperty(menu, QStringLiteral("title")).write(title);
+    if (!pos.isNull())
+        QQmlProperty(menu, QStringLiteral("pos")).write(pos);
+    if (!parentMenu) {
+        QQmlProperty doneSignal(menu, QStringLiteral("onDone"));
+        static int deleteLaterIndex = menu->metaObject()->indexOfSlot("deleteLater()");
+        if (doneSignal.isSignalProperty())
+            QObject::connect(menu, doneSignal.method(), menu, menu->metaObject()->method(deleteLaterIndex));
+    } else {
+        menu->setParent(parentMenu);
+
+        QQmlListReference entries(parentMenu, QQmlMetaType::defaultProperty(parentMenu).name(), qmlEngine(m_view));
+        if (entries.isValid())
+            entries.append(menu);
+    }
+    menuComponent->completeCreate();
+    return menu;
+}
+
diff --git a/src/webengine/ui_delegates_manager.h b/src/webengine/ui_delegates_manager.h
new file mode 100644
index 0000000000000000000000000000000000000000..65f994b9397ad5aba953e6b52355ef62bdf926b7
--- /dev/null
+++ b/src/webengine/ui_delegates_manager.h
@@ -0,0 +1,130 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 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 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef UI_DELEGATES_MANAGER_H
+#define UI_DELEGATES_MANAGER_H
+
+#include "qglobal.h"
+#include "web_contents_adapter.h"
+
+#include <QExplicitlySharedDataPointer>
+#include <QPoint>
+#include <QPointer>
+#include <QQmlComponent>
+#include <QUrl>
+
+#define FOR_EACH_COMPONENT_TYPE(F, SEPARATOR) \
+    F(Menu, menu) SEPARATOR \
+    F(MenuItem, menuItem) SEPARATOR \
+    F(MenuSeparator, menuSeparator) SEPARATOR
+
+#define COMMA_SEPARATOR ,
+#define SEMICOLON_SEPARATOR ;
+#define ENUM_DECLARATION(TYPE, COMPONENT) \
+    TYPE
+#define MEMBER_DECLARATION(TYPE, COMPONENT) \
+    QScopedPointer<QQmlComponent> COMPONENT##Component
+
+QT_BEGIN_NAMESPACE
+class QObject;
+class QQmlContext;
+class QQuickWebEngineView;
+QT_END_NAMESPACE
+
+
+class MenuItemHandler : public QObject {
+Q_OBJECT
+public:
+    MenuItemHandler(QObject *parent);
+
+Q_SIGNALS:
+    void triggered();
+};
+
+class CopyMenuItem : public MenuItemHandler {
+    Q_OBJECT
+public:
+    CopyMenuItem(QObject *parent, const QString &textToCopy);
+
+private:
+    void onTriggered();
+
+    QString m_textToCopy;
+};
+
+class NavigateMenuItem : public MenuItemHandler {
+    Q_OBJECT
+public:
+    NavigateMenuItem(QObject *parent, const QExplicitlySharedDataPointer<WebContentsAdapter> &adapter, const QUrl &targetUrl);
+
+private:
+    void onTriggered();
+
+    QExplicitlySharedDataPointer<WebContentsAdapter> m_adapter;
+    QUrl m_targetUrl;
+};
+
+class UIDelegatesManager {
+
+public:
+    enum ComponentType {
+        FOR_EACH_COMPONENT_TYPE(ENUM_DECLARATION, COMMA_SEPARATOR)
+    };
+
+    UIDelegatesManager(QQuickWebEngineView *);
+
+    void addMenuItem(MenuItemHandler *menuItemHandler, const QString &text, const QString &iconName = QString(), bool enabled = true);
+    void addMenuSeparator(QObject *menu);
+    QObject *addMenu(QObject *parentMenu, const QString &title, const QPoint &pos = QPoint());
+    QQmlContext *creationContextForComponent(QQmlComponent *);
+
+private:
+    bool ensureComponentLoaded(ComponentType);
+
+    QQuickWebEngineView *m_view;
+
+    FOR_EACH_COMPONENT_TYPE(MEMBER_DECLARATION, SEMICOLON_SEPARATOR)
+
+    Q_DISABLE_COPY(UIDelegatesManager);
+
+};
+
+#endif // UI_DELEGATES_MANAGER_H
diff --git a/src/webengine/webengine.pro b/src/webengine/webengine.pro
index 96724d465935cfaf5f0d4ac459828757913c2628..877c515a95632fbbc945c71ccfd6baec69e558d3 100644
--- a/src/webengine/webengine.pro
+++ b/src/webengine/webengine.pro
@@ -22,7 +22,8 @@ QMAKE_RPATHDIR += $$LIBPATH
 SOURCES = \
         api/qquickwebengineloadrequest.cpp \
         api/qquickwebengineview.cpp \
-        render_widget_host_view_qt_delegate_quick.cpp
+        render_widget_host_view_qt_delegate_quick.cpp \
+        ui_delegates_manager.cpp
 
 HEADERS = \
         api/qtwebengineglobal.h \
@@ -30,6 +31,7 @@ HEADERS = \
         api/qquickwebengineloadrequest_p.h \
         api/qquickwebengineview_p.h \
         api/qquickwebengineview_p_p.h \
-        render_widget_host_view_qt_delegate_quick.h
+        render_widget_host_view_qt_delegate_quick.h \
+        ui_delegates_manager.h
 
 load(qt_module)