summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--examples/quick/quicknanobrowser/quickwindow.cpp2
-rw-r--r--examples/quick/quicknanobrowser/quickwindow.qml86
-rw-r--r--src/webengine/api/qquickwebengineview.cpp84
-rw-r--r--src/webengine/api/qquickwebengineview_p_p.h16
-rw-r--r--src/webengine/render_widget_host_view_qt_delegate_quick.h1
5 files changed, 168 insertions, 21 deletions
diff --git a/examples/quick/quicknanobrowser/quickwindow.cpp b/examples/quick/quicknanobrowser/quickwindow.cpp
index 3b89aa205..df93cd218 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 3d3dd9470..7ef33c14e 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 e9375148e..d159ed8fc 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 b356b8881..0af1dcb45 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 e8e073c24..b31a9c873 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