diff --git a/examples/quick/quicknanobrowser/quickwindow.cpp b/examples/quick/quicknanobrowser/quickwindow.cpp
index 3b89aa2058204b8586180793e227f1837e9b526a..df93cd218de9fa76bf7550d0313bcbf5a721c96a 100644
--- a/examples/quick/quicknanobrowser/quickwindow.cpp
+++ b/examples/quick/quicknanobrowser/quickwindow.cpp
@@ -54,7 +54,6 @@ class Utils : public QObject {
 public:
     Utils(QObject* parent = 0) : QObject(parent) { }
     Q_INVOKABLE static QUrl fromUserInput(const QString& userInput) { return urlFromUserInput(userInput); }
-    Q_INVOKABLE static QUrl initialUrl() { return startupUrl(); }
 };
 
 #include "quickwindow.moc"
@@ -63,4 +62,5 @@ ApplicationEngine::ApplicationEngine()
 {
     rootContext()->setContextProperty("utils", new Utils(this));
     load(QUrl("qrc:/quickwindow.qml"));
+    QMetaObject::invokeMethod(rootObjects().first(), "load", Q_ARG(QVariant, startupUrl()));
 }
diff --git a/examples/quick/quicknanobrowser/quickwindow.qml b/examples/quick/quicknanobrowser/quickwindow.qml
index 3d3dd9470a56a8f0267501959a18c7e7b6b12513..7ef33c14e888f335b41df17def90743a210b6e74 100644
--- a/examples/quick/quicknanobrowser/quickwindow.qml
+++ b/examples/quick/quicknanobrowser/quickwindow.qml
@@ -40,26 +40,46 @@
 
 import QtQuick 2.0
 import QtWebEngine 1.0
+import QtWebEngine.experimental 1.0
 import QtQuick.Controls 1.0
 import QtQuick.Controls.Styles 1.0
 import QtQuick.Layouts 1.0
 
 ApplicationWindow {
     id: browserWindow
+    function load(url) { tabs.currentView.url = url }
+    function adoptHandle(viewHandle) { tabs.currentView.adoptHandle(viewHandle) }
+
     height: 600
     width: 800
     visible: true
-    title: webEngineView.title
+    title: tabs.currentView && tabs.currentView.title
 
-    // Focus and select text in URL bar
     Action {
         id: focus
-        shortcut: "Ctrl+L" // How to have Cmd + L on Mac ?
+        shortcut: "Ctrl+L"
         onTriggered: {
             addressBar.forceActiveFocus();
             addressBar.selectAll();
         }
     }
+    Action {
+        shortcut: "Ctrl+T"
+        onTriggered: {
+            tabs.createEmptyTab()
+            addressBar.forceActiveFocus();
+            addressBar.selectAll();
+        }
+    }
+    Action {
+        shortcut: "Ctrl+W"
+        onTriggered: {
+            if (tabs.count == 1)
+                browserWindow.close()
+            else
+                tabs.removeTab(tabs.currentIndex)
+        }
+    }
 
     toolBar: ToolBar {
         id: navigationBar
@@ -68,19 +88,19 @@ ApplicationWindow {
                 ToolButton {
                     id: backButton
                     iconSource: "icons/go-previous.png"
-                    onClicked: webEngineView.goBack()
-                    enabled: webEngineView.canGoBack
+                    onClicked: tabs.currentView.goBack()
+                    enabled: tabs.currentView && tabs.currentView.canGoBack
                 }
                 ToolButton {
                     id: forwardButton
                     iconSource: "icons/go-next.png"
-                    onClicked: webEngineView.goForward()
-                    enabled: webEngineView.canGoForward
+                    onClicked: tabs.currentView.goForward()
+                    enabled: tabs.currentView && tabs.currentView.canGoForward
                 }
                 ToolButton {
                     id: reloadButton
-                    iconSource: webEngineView.loading ? "icons/process-stop.png" : "icons/view-refresh.png"
-                    onClicked: webEngineView.reload()
+                    iconSource: tabs.currentView && tabs.currentView.loading ? "icons/process-stop.png" : "icons/view-refresh.png"
+                    onClicked: tabs.currentView.reload()
                 }
                 TextField {
                     id: addressBar
@@ -90,6 +110,7 @@ ApplicationWindow {
                         z: 2
                         id: faviconImage
                         width: 16; height: 16
+                        source: tabs.currentView && tabs.currentView.icon
                     }
                     style: TextFieldStyle {
                         padding {
@@ -98,7 +119,8 @@ ApplicationWindow {
                     }
                     focus: true
                     Layout.fillWidth: true
-                    onAccepted: webEngineView.url = utils.fromUserInput(text)
+                    text: tabs.currentView && tabs.currentView.url
+                    onAccepted: tabs.currentView.url = utils.fromUserInput(text)
                 }
             }
             ProgressBar {
@@ -117,16 +139,46 @@ ApplicationWindow {
                 z: -2;
                 minimumValue: 0
                 maximumValue: 100
+                value: tabs.currentView && tabs.currentView.loadProgress
             }
     }
-    WebEngineView {
-        id: webEngineView
-        focus: true
+
+    TabView {
+        id: tabs
+        property Item currentView: currentIndex < count ? getTab(currentIndex).item : null
+        function createEmptyTab() {
+            var tab = addTab("", tabComponent)
+            // We must do this first to make sure that tab.active gets set so that tab.item gets instantiated immediately.
+            tabs.currentIndex = tabs.count - 1
+            tab.title = Qt.binding(function() { return tab.item.title })
+            return tab
+        }
+
         anchors.fill: parent
-        url: utils.initialUrl()
+        Component.onCompleted: createEmptyTab()
+
+        Component {
+            id: tabComponent
+            WebEngineView {
+                function adoptHandle(viewHandle) { experimental.adoptHandle(viewHandle) }
+
+                focus: true
 
-        onUrlChanged: addressBar.text = url
-        onIconChanged: faviconImage.source = icon
-        onLoadProgressChanged: progressBar.value = loadProgress
+                experimental {
+                    onCreateWindow: {
+                        if (newViewDisposition == "popup")
+                            print("Warning: Ignored a popup window.")
+                        else if (newViewDisposition == "tab") {
+                            var tab = tabs.createEmptyTab()
+                            tab.item.adoptHandle(newViewHandle)
+                        } else {
+                            var component = Qt.createComponent("quickwindow.qml")
+                            var window = component.createObject()
+                            window.adoptHandle(newViewHandle)
+                        }
+                    }
+                }
+            }
+        }
     }
 }
diff --git a/src/webengine/api/qquickwebengineview.cpp b/src/webengine/api/qquickwebengineview.cpp
index e9375148ea6f2010c0ce28e60343f706f43d6b99..d159ed8fc62bd2ef3e92791acfeb8c406a794b69 100644
--- a/src/webengine/api/qquickwebengineview.cpp
+++ b/src/webengine/api/qquickwebengineview.cpp
@@ -47,6 +47,7 @@
 
 #include <QScreen>
 #include <QUrl>
+#include <QQmlEngine>
 
 QT_BEGIN_NAMESPACE
 
@@ -158,9 +159,48 @@ void QQuickWebEngineViewPrivate::focusContainer()
 
 void QQuickWebEngineViewPrivate::adoptNewWindow(WebContentsAdapter *newWebContents, WindowOpenDisposition disposition, const QRect &)
 {
-    Q_UNUSED(newWebContents);
-    Q_UNUSED(disposition);
-    Q_UNREACHABLE();
+    Q_Q(QQuickWebEngineView);
+    QQmlEngine *engine = QtQml::qmlEngine(q);
+    // This is currently only supported for QML instantiated WebEngineViews.
+    // We could emit a QObject* and set JavaScriptOwnership explicitly on it
+    // but this would make the signal cumbersome to use in C++ where one, and
+    // only one, of the connected slots would have to destroy the given handle.
+    // A virtual method instead of a signal would work better in this case.
+    if (!engine)
+        return;
+    static const QMetaMethod createWindowSignal = QMetaMethod::fromSignal(&QQuickWebEngineViewExperimental::createWindow);
+    if (!e->isSignalConnected(createWindowSignal))
+        return;
+
+    QQuickWebEngineViewHandle *handle = new QQuickWebEngineViewHandle;
+    // This increases the ref-count of newWebContents and will tell Chromium
+    // to start loading it and possibly return it to its parent page window.open().
+    handle->adapter = newWebContents;
+    // Clearly mark our wrapper as owned by JavaScript, we then depend on it
+    // being adopted or else eventually cleaned up by the GC.
+    QJSValue jsHandle = engine->newQObject(handle);
+
+    QString dispositionString;
+    switch (disposition) {
+    case WebContentsAdapterClient::NewForegroundTabDisposition:
+    case WebContentsAdapterClient::NewBackgroundTabDisposition:
+        dispositionString = QStringLiteral("tab");
+        break;
+    case WebContentsAdapterClient::NewPopupDisposition:
+        dispositionString = QStringLiteral("popup");
+        break;
+    case WebContentsAdapterClient::NewWindowDisposition:
+        dispositionString = QStringLiteral("window");
+        break;
+    default:
+        Q_UNREACHABLE();
+    }
+
+    emit e->createWindow(jsHandle, dispositionString);
+
+    // We currently require the adoption to happen within the signal handler to avoid having
+    // to support a null WebContentsAdapterClient for too long after having returned.
+    handle->adapter.reset();
 }
 
 void QQuickWebEngineViewPrivate::close()
@@ -285,6 +325,14 @@ void QQuickWebEngineView::geometryChanged(const QRectF &newGeometry, const QRect
     }
 }
 
+QQuickWebEngineViewHandle::QQuickWebEngineViewHandle()
+{
+}
+
+QQuickWebEngineViewHandle::~QQuickWebEngineViewHandle()
+{
+}
+
 QQuickWebEngineViewExperimental::QQuickWebEngineViewExperimental(QQuickWebEngineViewPrivate *viewPrivate)
     : q_ptr(0)
     , d_ptr(viewPrivate)
@@ -320,4 +368,34 @@ void QQuickWebEngineViewport::setDevicePixelRatio(qreal devicePixelRatio)
     Q_EMIT devicePixelRatioChanged();
 }
 
+void QQuickWebEngineViewExperimental::adoptHandle(QQuickWebEngineViewHandle *viewHandle)
+{
+    if (!viewHandle || !viewHandle->adapter) {
+        qWarning("Trying to adopt an empty handle, it was either already adopted or was invalidated."
+            "\nYou must do the adoption synchronously within the createWindow signal handler."
+            " If the handle hasn't been adopted before returning, it will be invalidated.");
+        return;
+    }
+
+    Q_Q(QQuickWebEngineView);
+    Q_D(QQuickWebEngineView);
+
+    // This throws away the WebContentsAdapter that has been used until now.
+    // All its states, particularly the loading URL, are replaced by the adopted WebContentsAdapter.
+    d->adapter = viewHandle->adapter;
+    viewHandle->adapter.reset();
+
+    d->adapter->initialize(d);
+
+    // Emit signals for values that might be different from the previous WebContentsAdapter.
+    emit q->titleChanged();
+    emit q->urlChanged();
+    emit q->iconChanged();
+    emit q->loadingStateChanged();
+    emit q->loadProgressChanged();
+}
+
 QT_END_NAMESPACE
+
+#include "moc_qquickwebengineview_p.cpp"
+#include "moc_qquickwebengineview_p_p.cpp"
diff --git a/src/webengine/api/qquickwebengineview_p_p.h b/src/webengine/api/qquickwebengineview_p_p.h
index b356b88813d244848d4975c493e6031160f597df..0af1dcb450c7f7c3bdf671c67cb7feea2a3df924 100644
--- a/src/webengine/api/qquickwebengineview_p_p.h
+++ b/src/webengine/api/qquickwebengineview_p_p.h
@@ -71,11 +71,27 @@ private:
     Q_DECLARE_PRIVATE(QQuickWebEngineView)
 };
 
+class Q_WEBENGINE_PRIVATE_EXPORT QQuickWebEngineViewHandle : public QObject {
+    Q_OBJECT
+public:
+    QQuickWebEngineViewHandle();
+    ~QQuickWebEngineViewHandle();
+
+private:
+    QExplicitlySharedDataPointer<WebContentsAdapter> adapter;
+    friend class QQuickWebEngineViewExperimental;
+    friend class QQuickWebEngineViewPrivate;
+};
+
 class Q_WEBENGINE_PRIVATE_EXPORT QQuickWebEngineViewExperimental : public QObject {
     Q_OBJECT
     Q_PROPERTY(QQuickWebEngineViewport *viewport READ viewport)
 public:
     QQuickWebEngineViewport *viewport() const;
+    Q_INVOKABLE void adoptHandle(QQuickWebEngineViewHandle *viewHandle);
+
+Q_SIGNALS:
+    void createWindow(const QJSValue &newViewHandle, const QString &newViewDisposition);
 
 private:
     QQuickWebEngineViewExperimental(QQuickWebEngineViewPrivate* viewPrivate);
diff --git a/src/webengine/render_widget_host_view_qt_delegate_quick.h b/src/webengine/render_widget_host_view_qt_delegate_quick.h
index e8e073c24c51e0bc548e881f1df79cc81f36950f..b31a9c873e8b179b0597505d201688b85ab299fb 100644
--- a/src/webengine/render_widget_host_view_qt_delegate_quick.h
+++ b/src/webengine/render_widget_host_view_qt_delegate_quick.h
@@ -68,6 +68,7 @@ public:
     {
         QQuickWebEngineViewPrivate *viewPrivate = static_cast<QQuickWebEngineViewPrivate *>(container);
         this->setParentItem(viewPrivate->q_func());
+        this->setSize(viewPrivate->q_func()->boundingRect().size());
     }
 
     virtual void initAsPopup(const QRect& rect) Q_DECL_OVERRIDE