diff options
Diffstat (limited to 'examples')
-rw-r--r-- | examples/examples.pro | 5 | ||||
-rw-r--r-- | examples/webengine/quicknanobrowser/ApplicationRoot.qml | 2 | ||||
-rw-r--r-- | examples/webengine/quicknanobrowser/BrowserDialog.qml | 2 | ||||
-rw-r--r-- | examples/webengine/quicknanobrowser/BrowserWindow.qml | 119 | ||||
-rw-r--r-- | examples/webengine/quicknanobrowser/DownloadView.qml | 2 | ||||
-rw-r--r-- | examples/webengine/quicknanobrowser/FullScreenNotification.qml | 99 | ||||
-rw-r--r-- | examples/webengine/quicknanobrowser/doc/src/quicknanobrowser.qdoc | 2 | ||||
-rw-r--r-- | examples/webengine/quicknanobrowser/quicknanobrowser.pro | 3 | ||||
-rw-r--r-- | examples/webengine/quicknanobrowser/resources.qrc | 1 | ||||
-rw-r--r-- | examples/webenginewidgets/browser/browser.rc | 1 | ||||
-rw-r--r-- | examples/webenginewidgets/browser/doc/images/browser-demo.png | bin | 156342 -> 0 bytes | |||
-rw-r--r-- | examples/webenginewidgets/browser/networkaccessmanager.cpp | 160 | ||||
-rw-r--r-- | examples/webenginewidgets/browser/networkaccessmanager.h | 75 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/Info_mac.plist (renamed from examples/webenginewidgets/browser/Info_mac.plist) | 0 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/addbookmarkdialog.ui (renamed from examples/webenginewidgets/browser/addbookmarkdialog.ui) | 0 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/autosaver.cpp (renamed from examples/webenginewidgets/browser/autosaver.cpp) | 0 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/autosaver.h (renamed from examples/webenginewidgets/browser/autosaver.h) | 0 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/bookmarks.cpp (renamed from examples/webenginewidgets/browser/bookmarks.cpp) | 0 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/bookmarks.h (renamed from examples/webenginewidgets/browser/bookmarks.h) | 0 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/bookmarks.ui (renamed from examples/webenginewidgets/browser/bookmarks.ui) | 0 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/browserapplication.cpp (renamed from examples/webenginewidgets/browser/browserapplication.cpp) | 110 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/browserapplication.h (renamed from examples/webenginewidgets/browser/browserapplication.h) | 19 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/browsermainwindow.cpp (renamed from examples/webenginewidgets/browser/browsermainwindow.cpp) | 0 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/browsermainwindow.h (renamed from examples/webenginewidgets/browser/browsermainwindow.h) | 0 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/chasewidget.cpp (renamed from examples/webenginewidgets/browser/chasewidget.cpp) | 0 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/chasewidget.h (renamed from examples/webenginewidgets/browser/chasewidget.h) | 0 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/cookiejar.cpp (renamed from examples/webenginewidgets/browser/cookiejar.cpp) | 0 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/cookiejar.h (renamed from examples/webenginewidgets/browser/cookiejar.h) | 0 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/cookies.ui (renamed from examples/webenginewidgets/browser/cookies.ui) | 0 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/cookiesexceptions.ui (renamed from examples/webenginewidgets/browser/cookiesexceptions.ui) | 0 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/data/addtab.png (renamed from examples/webenginewidgets/browser/data/addtab.png) | bin | 469 -> 469 bytes | |||
-rw-r--r-- | examples/webenginewidgets/demobrowser/data/closetab.png (renamed from examples/webenginewidgets/browser/data/closetab.png) | bin | 516 -> 516 bytes | |||
-rw-r--r-- | examples/webenginewidgets/demobrowser/data/data.qrc (renamed from examples/webenginewidgets/browser/data/data.qrc) | 2 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/data/defaultbookmarks.xbel (renamed from examples/webenginewidgets/browser/data/defaultbookmarks.xbel) | 0 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/data/defaulticon.png (renamed from examples/webenginewidgets/browser/data/defaulticon.png) | bin | 1473 -> 1473 bytes | |||
-rw-r--r-- | examples/webenginewidgets/demobrowser/data/demobrowser.svg (renamed from examples/webenginewidgets/browser/data/browser.svg) | 3 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/data/history.png (renamed from examples/webenginewidgets/browser/data/history.png) | bin | 1527 -> 1527 bytes | |||
-rw-r--r-- | examples/webenginewidgets/demobrowser/data/loading.gif (renamed from examples/webenginewidgets/browser/data/loading.gif) | bin | 847 -> 847 bytes | |||
-rw-r--r-- | examples/webenginewidgets/demobrowser/demobrowser.icns (renamed from examples/webenginewidgets/browser/browser.icns) | bin | 50218 -> 50218 bytes | |||
-rw-r--r-- | examples/webenginewidgets/demobrowser/demobrowser.ico (renamed from examples/webenginewidgets/browser/browser.ico) | bin | 15374 -> 15374 bytes | |||
-rw-r--r-- | examples/webenginewidgets/demobrowser/demobrowser.pro (renamed from examples/webenginewidgets/browser/browser.pro) | 20 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/demobrowser.rc | 1 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/doc/images/browser-demo.png | bin | 0 -> 199354 bytes | |||
-rw-r--r-- | examples/webenginewidgets/demobrowser/doc/src/demobrowser.qdoc (renamed from examples/webenginewidgets/browser/doc/src/browser.qdoc) | 10 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/downloaditem.ui (renamed from examples/webenginewidgets/browser/downloaditem.ui) | 0 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/downloadmanager.cpp (renamed from examples/webenginewidgets/browser/downloadmanager.cpp) | 1 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/downloadmanager.h (renamed from examples/webenginewidgets/browser/downloadmanager.h) | 2 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/downloads.ui (renamed from examples/webenginewidgets/browser/downloads.ui) | 0 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/edittableview.cpp (renamed from examples/webenginewidgets/browser/edittableview.cpp) | 0 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/edittableview.h (renamed from examples/webenginewidgets/browser/edittableview.h) | 0 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/edittreeview.cpp (renamed from examples/webenginewidgets/browser/edittreeview.cpp) | 0 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/edittreeview.h (renamed from examples/webenginewidgets/browser/edittreeview.h) | 0 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/featurepermissionbar.cpp (renamed from examples/webenginewidgets/browser/featurepermissionbar.cpp) | 0 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/featurepermissionbar.h (renamed from examples/webenginewidgets/browser/featurepermissionbar.h) | 0 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/fullscreennotification.cpp | 112 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/fullscreennotification.h | 76 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/history.cpp (renamed from examples/webenginewidgets/browser/history.cpp) | 0 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/history.h (renamed from examples/webenginewidgets/browser/history.h) | 0 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/history.ui (renamed from examples/webenginewidgets/browser/history.ui) | 0 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/htmls/htmls.qrc (renamed from examples/webenginewidgets/browser/htmls/htmls.qrc) | 0 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/htmls/notfound.html (renamed from examples/webenginewidgets/browser/htmls/notfound.html) | 0 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/main.cpp (renamed from examples/webenginewidgets/browser/main.cpp) | 0 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/modelmenu.cpp (renamed from examples/webenginewidgets/browser/modelmenu.cpp) | 0 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/modelmenu.h (renamed from examples/webenginewidgets/browser/modelmenu.h) | 0 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/passworddialog.ui (renamed from examples/webenginewidgets/browser/passworddialog.ui) | 0 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/proxy.ui (renamed from examples/webenginewidgets/browser/proxy.ui) | 0 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/searchlineedit.cpp (renamed from examples/webenginewidgets/browser/searchlineedit.cpp) | 0 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/searchlineedit.h (renamed from examples/webenginewidgets/browser/searchlineedit.h) | 0 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/settings.cpp (renamed from examples/webenginewidgets/browser/settings.cpp) | 33 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/settings.h (renamed from examples/webenginewidgets/browser/settings.h) | 3 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/settings.ui (renamed from examples/webenginewidgets/browser/settings.ui) | 27 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/squeezelabel.cpp (renamed from examples/webenginewidgets/browser/squeezelabel.cpp) | 0 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/squeezelabel.h (renamed from examples/webenginewidgets/browser/squeezelabel.h) | 0 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/tabwidget.cpp (renamed from examples/webenginewidgets/browser/tabwidget.cpp) | 89 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/tabwidget.h (renamed from examples/webenginewidgets/browser/tabwidget.h) | 11 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/toolbarsearch.cpp (renamed from examples/webenginewidgets/browser/toolbarsearch.cpp) | 0 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/toolbarsearch.h (renamed from examples/webenginewidgets/browser/toolbarsearch.h) | 0 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/urllineedit.cpp (renamed from examples/webenginewidgets/browser/urllineedit.cpp) | 0 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/urllineedit.h (renamed from examples/webenginewidgets/browser/urllineedit.h) | 0 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/webview.cpp (renamed from examples/webenginewidgets/browser/webview.cpp) | 74 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/webview.h (renamed from examples/webenginewidgets/browser/webview.h) | 5 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/xbel.cpp (renamed from examples/webenginewidgets/browser/xbel.cpp) | 0 | ||||
-rw-r--r-- | examples/webenginewidgets/demobrowser/xbel.h (renamed from examples/webenginewidgets/browser/xbel.h) | 0 | ||||
-rw-r--r-- | examples/webenginewidgets/fancybrowser/doc/src/fancybrowser.qdoc | 22 | ||||
-rw-r--r-- | examples/webenginewidgets/markdowneditor/3RDPARTY.md | 23 | ||||
-rw-r--r-- | examples/webenginewidgets/markdowneditor/doc/images/markdowneditor-example.png | bin | 0 -> 41883 bytes | |||
-rw-r--r-- | examples/webenginewidgets/markdowneditor/doc/src/markdowneditor.qdoc | 164 | ||||
-rw-r--r-- | examples/webenginewidgets/markdowneditor/document.cpp | 50 | ||||
-rw-r--r-- | examples/webenginewidgets/markdowneditor/document.h | 64 | ||||
-rw-r--r-- | examples/webenginewidgets/markdowneditor/main.cpp | 56 | ||||
-rw-r--r-- | examples/webenginewidgets/markdowneditor/mainwindow.cpp | 172 | ||||
-rw-r--r-- | examples/webenginewidgets/markdowneditor/mainwindow.h | 77 | ||||
-rw-r--r-- | examples/webenginewidgets/markdowneditor/mainwindow.ui | 115 | ||||
-rw-r--r-- | examples/webenginewidgets/markdowneditor/markdowneditor.pro | 28 | ||||
-rw-r--r-- | examples/webenginewidgets/markdowneditor/previewpage.cpp | 55 | ||||
-rw-r--r-- | examples/webenginewidgets/markdowneditor/previewpage.h | 57 | ||||
-rw-r--r-- | examples/webenginewidgets/markdowneditor/resources/default.md | 12 | ||||
-rw-r--r-- | examples/webenginewidgets/markdowneditor/resources/index.html | 32 | ||||
-rw-r--r-- | examples/webenginewidgets/markdowneditor/resources/markdown.css | 260 | ||||
-rw-r--r-- | examples/webenginewidgets/markdowneditor/resources/markdowneditor.qrc | 9 | ||||
-rw-r--r-- | examples/webenginewidgets/markdowneditor/resources/marked.min.js | 6 | ||||
-rw-r--r-- | examples/webenginewidgets/markdowneditor/resources/qwebchannel.js | 413 |
102 files changed, 2310 insertions, 374 deletions
diff --git a/examples/examples.pro b/examples/examples.pro index 3d6872d55..b1e1ade46 100644 --- a/examples/examples.pro +++ b/examples/examples.pro @@ -6,6 +6,7 @@ qtHaveModule(webengine) { qtHaveModule(webenginewidgets) { SUBDIRS += \ - webenginewidgets/browser \ - webenginewidgets/fancybrowser + webenginewidgets/demobrowser \ + webenginewidgets/fancybrowser \ + webenginewidgets/markdowneditor } diff --git a/examples/webengine/quicknanobrowser/ApplicationRoot.qml b/examples/webengine/quicknanobrowser/ApplicationRoot.qml index b409696be..91d1551a7 100644 --- a/examples/webengine/quicknanobrowser/ApplicationRoot.qml +++ b/examples/webengine/quicknanobrowser/ApplicationRoot.qml @@ -39,7 +39,7 @@ ****************************************************************************/ import QtQuick 2.1 -import QtWebEngine 1.1 +import QtWebEngine 1.2 QtObject { id: root diff --git a/examples/webengine/quicknanobrowser/BrowserDialog.qml b/examples/webengine/quicknanobrowser/BrowserDialog.qml index 6202d02f7..0577bf642 100644 --- a/examples/webengine/quicknanobrowser/BrowserDialog.qml +++ b/examples/webengine/quicknanobrowser/BrowserDialog.qml @@ -40,7 +40,7 @@ import QtQuick 2.1 import QtQuick.Window 2.2 -import QtWebEngine 1.1 +import QtWebEngine 1.2 Window { property alias currentWebView: webView diff --git a/examples/webengine/quicknanobrowser/BrowserWindow.qml b/examples/webengine/quicknanobrowser/BrowserWindow.qml index c4c0270a4..e8a9cb9ce 100644 --- a/examples/webengine/quicknanobrowser/BrowserWindow.qml +++ b/examples/webengine/quicknanobrowser/BrowserWindow.qml @@ -38,8 +38,8 @@ ** ****************************************************************************/ -import QtQuick 2.1 -import QtWebEngine 1.1 +import QtQuick 2.2 +import QtWebEngine 1.2 import QtQuick.Controls 1.0 import QtQuick.Controls.Styles 1.0 import QtQuick.Layouts 1.0 @@ -59,8 +59,10 @@ ApplicationWindow { // This is for the case where the system forces us to leave fullscreen. if (currentWebView && !isFullScreen) { currentWebView.state = "" - if (currentWebView.isFullScreen) + if (currentWebView.isFullScreen) { currentWebView.fullScreenCancelled() + fullScreenNotification.hide() + } } } @@ -82,6 +84,8 @@ ApplicationWindow { property alias autoLoadImages: loadImages.checked; property alias javaScriptEnabled: javaScriptEnabled.checked; property alias errorPageEnabled: errorPageEnabled.checked; + property alias pluginsEnabled: pluginsEnabled.checked; + property alias fullScreenSupportEnabled: fullScreenSupportEnabled.checked; } Action { @@ -99,14 +103,14 @@ ApplicationWindow { } } Action { - shortcut: "Ctrl+R" + shortcut: StandardKey.Refresh onTriggered: { if (currentWebView) currentWebView.reload() } } Action { - shortcut: "Ctrl+T" + shortcut: StandardKey.AddTab onTriggered: { tabs.createEmptyTab(currentWebView.profile) tabs.currentIndex = tabs.count - 1 @@ -115,12 +119,9 @@ ApplicationWindow { } } Action { - shortcut: "Ctrl+W" + shortcut: StandardKey.Close onTriggered: { - if (tabs.count == 1) - browserWindow.close() - else - tabs.removeTab(tabs.currentIndex) + currentWebView.triggerWebAction(WebEngineView.RequestClose); } } Action { @@ -135,14 +136,51 @@ ApplicationWindow { onTriggered: currentWebView.zoomFactor = 1.0; } Action { - shortcut: "Ctrl+-" + shortcut: StandardKey.ZoomOut onTriggered: currentWebView.zoomFactor -= 0.1; } Action { - shortcut: "Ctrl+=" + shortcut: StandardKey.ZoomIn onTriggered: currentWebView.zoomFactor += 0.1; } + Action { + shortcut: StandardKey.Copy + onTriggered: currentWebView.triggerWebAction(WebEngineView.Copy) + } + Action { + shortcut: StandardKey.Cut + onTriggered: currentWebView.triggerWebAction(WebEngineView.Cut) + } + Action { + shortcut: StandardKey.Paste + onTriggered: currentWebView.triggerWebAction(WebEngineView.Paste) + } + Action { + shortcut: "Shift+"+StandardKey.Paste + onTriggered: currentWebView.triggerWebAction(WebEngineView.PasteAndMatchStyle) + } + Action { + shortcut: StandardKey.SelectAll + onTriggered: currentWebView.triggerWebAction(WebEngineView.SelectAll) + } + Action { + shortcut: StandardKey.Undo + onTriggered: currentWebView.triggerWebAction(WebEngineView.Undo) + } + Action { + shortcut: StandardKey.Redo + onTriggered: currentWebView.triggerWebAction(WebEngineView.Redo) + } + Action { + shortcut: StandardKey.Back + onTriggered: currentWebView.triggerWebAction(WebEngineView.Back) + } + Action { + shortcut: StandardKey.Forward + onTriggered: currentWebView.triggerWebAction(WebEngineView.Forward) + } + toolBar: ToolBar { id: navigationBar RowLayout { @@ -230,6 +268,18 @@ ApplicationWindow { checked: WebEngine.settings.errorPageEnabled } MenuItem { + id: pluginsEnabled + text: "Plugins On" + checkable: true + checked: WebEngine.settings.pluginsEnabled + } + MenuItem { + id: fullScreenSupportEnabled + text: "FullScreen On" + checkable: true + checked: WebEngine.settings.fullScreenSupportEnabled + } + MenuItem { id: offTheRecordEnabled text: "Off The Record" checkable: true @@ -312,6 +362,8 @@ ApplicationWindow { settings.autoLoadImages: appSettings.autoLoadImages settings.javascriptEnabled: appSettings.javaScriptEnabled settings.errorPageEnabled: appSettings.errorPageEnabled + settings.pluginsEnabled: appSettings.pluginsEnabled + settings.fullScreenSupportEnabled: appSettings.fullScreenSupportEnabled onCertificateError: { error.defer() @@ -342,12 +394,50 @@ ApplicationWindow { webEngineView.state = "FullScreen" browserWindow.previousVisibility = browserWindow.visibility browserWindow.showFullScreen() + fullScreenNotification.show() } else { webEngineView.state = "" browserWindow.visibility = browserWindow.previousVisibility + fullScreenNotification.hide() } request.accept() } + + onRenderProcessTerminated: { + var status = "" + switch (terminationStatus) { + case WebEngineView.NormalTerminationStatus: + status = "(normal exit)" + break; + case WebEngineView.AbnormalTerminationStatus: + status = "(abnormal exit)" + break; + case WebEngineView.CrashedTerminationStatus: + status = "(crashed)" + break; + case WebEngineView.KilledTerminationStatus: + status = "(killed)" + break; + } + + print("Render process exited with code " + exitCode + " " + status) + reloadTimer.running = true + } + + onWindowCloseRequested: { + if (tabs.count == 1) + browserWindow.close() + else + tabs.removeTab(tabs.currentIndex) + } + + Timer { + id: reloadTimer + interval: 0 + running: false + repeat: false + onTriggered: currentWebView.reload() + } } } } @@ -382,6 +472,11 @@ ApplicationWindow { visible = certErrors.length > 0 } } + + FullScreenNotification { + id: fullScreenNotification + } + DownloadView { id: downloadView visible: false diff --git a/examples/webengine/quicknanobrowser/DownloadView.qml b/examples/webengine/quicknanobrowser/DownloadView.qml index 9a5cdd28f..c17a8bd60 100644 --- a/examples/webengine/quicknanobrowser/DownloadView.qml +++ b/examples/webengine/quicknanobrowser/DownloadView.qml @@ -41,7 +41,7 @@ import QtQuick 2.1 import QtQuick.Controls 1.0 import QtQuick.Controls.Styles 1.0 -import QtWebEngine 1.0 +import QtWebEngine 1.2 import QtQuick.Layouts 1.0 Rectangle { diff --git a/examples/webengine/quicknanobrowser/FullScreenNotification.qml b/examples/webengine/quicknanobrowser/FullScreenNotification.qml new file mode 100644 index 000000000..80a63d479 --- /dev/null +++ b/examples/webengine/quicknanobrowser/FullScreenNotification.qml @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** 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: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 The Qt Company Ltd 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.5 + +Rectangle { + id: fullScreenNotification + width: 500 + height: 40 + color: "white" + radius: 7 + + visible: false + opacity: 0 + + function show() { + visible = true + opacity = 1 + reset.start() + } + + function hide() { + reset.stop() + opacity = 0 + } + + Behavior on opacity { + NumberAnimation { + duration: 750 + onStopped: { + if (opacity == 0) + visible = false + } + } + } + + Timer { + id: reset + interval: 5000 + onTriggered: hide() + } + + anchors.horizontalCenter: parent.horizontalCenter + y: 125 + + Text { + id: message + width: parent.width + + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + + wrapMode: Text.WordWrap + elide: Text.ElideNone + clip: true + + text: qsTr("You are now in fullscreen mode. Press ESC to quit!") + } +} diff --git a/examples/webengine/quicknanobrowser/doc/src/quicknanobrowser.qdoc b/examples/webengine/quicknanobrowser/doc/src/quicknanobrowser.qdoc index 4fac9fe09..211951929 100644 --- a/examples/webengine/quicknanobrowser/doc/src/quicknanobrowser.qdoc +++ b/examples/webengine/quicknanobrowser/doc/src/quicknanobrowser.qdoc @@ -26,7 +26,7 @@ ****************************************************************************/ /*! - \example quicknanobrowser + \example webengine/quicknanobrowser \title WebEngine Quick Nano Browser \ingroup webengine-examples \brief A web browser implemented using the WebEngineView QML type. diff --git a/examples/webengine/quicknanobrowser/quicknanobrowser.pro b/examples/webengine/quicknanobrowser/quicknanobrowser.pro index 1447af927..6cf556984 100644 --- a/examples/webengine/quicknanobrowser/quicknanobrowser.pro +++ b/examples/webengine/quicknanobrowser/quicknanobrowser.pro @@ -9,7 +9,8 @@ SOURCES = main.cpp OTHER_FILES += ApplicationRoot.qml \ BrowserDialog.qml \ BrowserWindow.qml \ - DownloadView.qml + DownloadView.qml \ + FullScreenNotification.qml RESOURCES += resources.qrc diff --git a/examples/webengine/quicknanobrowser/resources.qrc b/examples/webengine/quicknanobrowser/resources.qrc index 28fd7b7dc..694f8d19b 100644 --- a/examples/webengine/quicknanobrowser/resources.qrc +++ b/examples/webengine/quicknanobrowser/resources.qrc @@ -4,6 +4,7 @@ <file>BrowserDialog.qml</file> <file>BrowserWindow.qml</file> <file>DownloadView.qml</file> + <file>FullScreenNotification.qml</file> </qresource> <qresource prefix="icons"> <file alias="go-next.png">icons/go-next.png</file> diff --git a/examples/webenginewidgets/browser/browser.rc b/examples/webenginewidgets/browser/browser.rc deleted file mode 100644 index 39e17e973..000000000 --- a/examples/webenginewidgets/browser/browser.rc +++ /dev/null @@ -1 +0,0 @@ -IDI_ICON1 ICON DISCARDABLE "browser.ico" diff --git a/examples/webenginewidgets/browser/doc/images/browser-demo.png b/examples/webenginewidgets/browser/doc/images/browser-demo.png Binary files differdeleted file mode 100644 index 09d065095..000000000 --- a/examples/webenginewidgets/browser/doc/images/browser-demo.png +++ /dev/null diff --git a/examples/webenginewidgets/browser/networkaccessmanager.cpp b/examples/webenginewidgets/browser/networkaccessmanager.cpp deleted file mode 100644 index d2b4f827f..000000000 --- a/examples/webenginewidgets/browser/networkaccessmanager.cpp +++ /dev/null @@ -1,160 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** 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 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 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. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company 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 "networkaccessmanager.h" - -#include "browserapplication.h" -#include "browsermainwindow.h" - -#include <QtCore/QSettings> - -#include <QtGui/QDesktopServices> -#include <QtWidgets/QDialog> -#include <QtWidgets/QMessageBox> -#include <QtWidgets/QStyle> -#include <QtGui/QTextDocument> - -#include <QtNetwork/QAuthenticator> -#include <QtNetwork/QNetworkDiskCache> -#include <QtNetwork/QNetworkProxy> -#include <QtNetwork/QNetworkRequest> -#include <QtNetwork/QNetworkReply> -#include <QtNetwork/QSslError> - -NetworkAccessManager::NetworkAccessManager(QObject *parent) - : QNetworkAccessManager(parent), - requestFinishedCount(0), requestFinishedFromCacheCount(0), requestFinishedPipelinedCount(0), - requestFinishedSecureCount(0), requestFinishedDownloadBufferCount(0) -{ - connect(this, SIGNAL(finished(QNetworkReply*)), - SLOT(requestFinished(QNetworkReply*))); -#ifndef QT_NO_OPENSSL - connect(this, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)), - SLOT(sslErrors(QNetworkReply*,QList<QSslError>))); -#endif - loadSettings(); - - QNetworkDiskCache *diskCache = new QNetworkDiskCache(this); - QString location = QStandardPaths::writableLocation(QStandardPaths::CacheLocation); - diskCache->setCacheDirectory(location); - setCache(diskCache); -} - -QNetworkReply* NetworkAccessManager::createRequest(Operation op, const QNetworkRequest & req, QIODevice * outgoingData) -{ - QNetworkRequest request = req; // copy so we can modify - // this is a temporary hack until we properly use the pipelining flags from QtWebkit - // pipeline everything! :) - request.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); - return QNetworkAccessManager::createRequest(op, request, outgoingData); -} - -void NetworkAccessManager::requestFinished(QNetworkReply *reply) -{ - requestFinishedCount++; - - if (reply->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool() == true) - requestFinishedFromCacheCount++; - - if (reply->attribute(QNetworkRequest::HttpPipeliningWasUsedAttribute).toBool() == true) - requestFinishedPipelinedCount++; - - if (reply->attribute(QNetworkRequest::ConnectionEncryptedAttribute).toBool() == true) - requestFinishedSecureCount++; - - if (reply->attribute(QNetworkRequest::DownloadBufferAttribute).isValid() == true) - requestFinishedDownloadBufferCount++; - - if (requestFinishedCount % 10) - return; - -#ifdef QT_DEBUG - double pctCached = (double(requestFinishedFromCacheCount) * 100.0/ double(requestFinishedCount)); - double pctPipelined = (double(requestFinishedPipelinedCount) * 100.0/ double(requestFinishedCount)); - double pctSecure = (double(requestFinishedSecureCount) * 100.0/ double(requestFinishedCount)); - double pctDownloadBuffer = (double(requestFinishedDownloadBufferCount) * 100.0/ double(requestFinishedCount)); - - qDebug("STATS [%lli requests total] [%3.2f%% from cache] [%3.2f%% pipelined] [%3.2f%% SSL/TLS] [%3.2f%% Zerocopy]", requestFinishedCount, pctCached, pctPipelined, pctSecure, pctDownloadBuffer); -#endif -} - -void NetworkAccessManager::loadSettings() -{ - QSettings settings; - settings.beginGroup(QLatin1String("proxy")); - QNetworkProxy proxy; - if (settings.value(QLatin1String("enabled"), false).toBool()) { - if (settings.value(QLatin1String("type"), 0).toInt() == 0) - proxy = QNetworkProxy::Socks5Proxy; - else - proxy = QNetworkProxy::HttpProxy; - proxy.setHostName(settings.value(QLatin1String("hostName")).toString()); - proxy.setPort(settings.value(QLatin1String("port"), 1080).toInt()); - proxy.setUser(settings.value(QLatin1String("userName")).toString()); - proxy.setPassword(settings.value(QLatin1String("password")).toString()); - } - setProxy(proxy); -} - -#ifndef QT_NO_OPENSSL -void NetworkAccessManager::sslErrors(QNetworkReply *reply, const QList<QSslError> &error) -{ - // check if SSL certificate has been trusted already - QString replyHost = reply->url().host() + QString(":%1").arg(reply->url().port()); - if (! sslTrustedHostList.contains(replyHost)) { - BrowserMainWindow *mainWindow = BrowserApplication::instance()->mainWindow(); - - QStringList errorStrings; - for (int i = 0; i < error.count(); ++i) - errorStrings += error.at(i).errorString(); - QString errors = errorStrings.join(QLatin1String("\n")); - int ret = QMessageBox::warning(mainWindow, QCoreApplication::applicationName(), - tr("SSL Errors:\n\n%1\n\n%2\n\n" - "Do you want to ignore these errors for this host?").arg(reply->url().toString()).arg(errors), - QMessageBox::Yes | QMessageBox::No, - QMessageBox::No); - if (ret == QMessageBox::Yes) { - reply->ignoreSslErrors(); - sslTrustedHostList.append(replyHost); - } - } -} -#endif diff --git a/examples/webenginewidgets/browser/networkaccessmanager.h b/examples/webenginewidgets/browser/networkaccessmanager.h deleted file mode 100644 index f3ee2d338..000000000 --- a/examples/webenginewidgets/browser/networkaccessmanager.h +++ /dev/null @@ -1,75 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** 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 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 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. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company 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 NETWORKACCESSMANAGER_H -#define NETWORKACCESSMANAGER_H - -#include <QtNetwork/QNetworkAccessManager> -#include <QtNetwork/QNetworkRequest> - -class NetworkAccessManager : public QNetworkAccessManager -{ - Q_OBJECT - -public: - NetworkAccessManager(QObject *parent = 0); - - virtual QNetworkReply* createRequest ( Operation op, const QNetworkRequest & req, QIODevice * outgoingData = 0 ); - -private: - QList<QString> sslTrustedHostList; - qint64 requestFinishedCount; - qint64 requestFinishedFromCacheCount; - qint64 requestFinishedPipelinedCount; - qint64 requestFinishedSecureCount; - qint64 requestFinishedDownloadBufferCount; - -public slots: - void loadSettings(); - void requestFinished(QNetworkReply *reply); - -private slots: -#ifndef QT_NO_OPENSSL - void sslErrors(QNetworkReply *reply, const QList<QSslError> &error); -#endif -}; - -#endif // NETWORKACCESSMANAGER_H diff --git a/examples/webenginewidgets/browser/Info_mac.plist b/examples/webenginewidgets/demobrowser/Info_mac.plist index ccd4b3f28..ccd4b3f28 100644 --- a/examples/webenginewidgets/browser/Info_mac.plist +++ b/examples/webenginewidgets/demobrowser/Info_mac.plist diff --git a/examples/webenginewidgets/browser/addbookmarkdialog.ui b/examples/webenginewidgets/demobrowser/addbookmarkdialog.ui index 3460d7bb8..3460d7bb8 100644 --- a/examples/webenginewidgets/browser/addbookmarkdialog.ui +++ b/examples/webenginewidgets/demobrowser/addbookmarkdialog.ui diff --git a/examples/webenginewidgets/browser/autosaver.cpp b/examples/webenginewidgets/demobrowser/autosaver.cpp index dc72090dd..dc72090dd 100644 --- a/examples/webenginewidgets/browser/autosaver.cpp +++ b/examples/webenginewidgets/demobrowser/autosaver.cpp diff --git a/examples/webenginewidgets/browser/autosaver.h b/examples/webenginewidgets/demobrowser/autosaver.h index b0c73846f..b0c73846f 100644 --- a/examples/webenginewidgets/browser/autosaver.h +++ b/examples/webenginewidgets/demobrowser/autosaver.h diff --git a/examples/webenginewidgets/browser/bookmarks.cpp b/examples/webenginewidgets/demobrowser/bookmarks.cpp index 15078caa3..15078caa3 100644 --- a/examples/webenginewidgets/browser/bookmarks.cpp +++ b/examples/webenginewidgets/demobrowser/bookmarks.cpp diff --git a/examples/webenginewidgets/browser/bookmarks.h b/examples/webenginewidgets/demobrowser/bookmarks.h index 244f003c6..244f003c6 100644 --- a/examples/webenginewidgets/browser/bookmarks.h +++ b/examples/webenginewidgets/demobrowser/bookmarks.h diff --git a/examples/webenginewidgets/browser/bookmarks.ui b/examples/webenginewidgets/demobrowser/bookmarks.ui index c893e941d..c893e941d 100644 --- a/examples/webenginewidgets/browser/bookmarks.ui +++ b/examples/webenginewidgets/demobrowser/bookmarks.ui diff --git a/examples/webenginewidgets/browser/browserapplication.cpp b/examples/webenginewidgets/demobrowser/browserapplication.cpp index ca28b2d0b..a85bce2c3 100644 --- a/examples/webenginewidgets/browser/browserapplication.cpp +++ b/examples/webenginewidgets/demobrowser/browserapplication.cpp @@ -46,7 +46,6 @@ #include "cookiejar.h" #include "downloadmanager.h" #include "history.h" -#include "networkaccessmanager.h" #include "tabwidget.h" #include "webview.h" @@ -64,6 +63,7 @@ #include <QtNetwork/QLocalServer> #include <QtNetwork/QLocalSocket> #include <QtNetwork/QNetworkProxy> +#include <QtNetwork/QNetworkReply> #include <QtNetwork/QSslSocket> #include <QWebEngineProfile> @@ -191,25 +191,24 @@ BrowserApplication::~BrowserApplication() delete s_bookmarksManager; } -#if defined(Q_OS_OSX) void BrowserApplication::lastWindowClosed() { +#if defined(Q_OS_OSX) clean(); BrowserMainWindow *mw = new BrowserMainWindow; mw->slotHome(); m_mainWindows.prepend(mw); -} #endif +} BrowserApplication *BrowserApplication::instance() { return (static_cast<BrowserApplication *>(QCoreApplication::instance())); } -#if defined(Q_OS_OSX) -#include <QtWidgets/QMessageBox> void BrowserApplication::quitBrowser() { +#if defined(Q_OS_OSX) clean(); int tabCount = 0; for (int i = 0; i < m_mainWindows.count(); ++i) { @@ -227,8 +226,8 @@ void BrowserApplication::quitBrowser() } exit(0); -} #endif +} /*! Any actions that can be delayed until the window is visible @@ -243,7 +242,7 @@ void BrowserApplication::postLaunch() QWebEngineSettings::setOfflineStoragePath(directory); #endif - setWindowIcon(QIcon(QLatin1String(":browser.svg"))); + setWindowIcon(QIcon(QLatin1String(":demobrowser.svg"))); loadSettings(); @@ -283,14 +282,15 @@ void BrowserApplication::loadSettings() defaultSettings->setAttribute(QWebEngineSettings::JavascriptEnabled, settings.value(QLatin1String("enableJavascript"), true).toBool()); defaultSettings->setAttribute(QWebEngineSettings::ScrollAnimatorEnabled, settings.value(QLatin1String("enableScrollAnimator"), true).toBool()); -#if defined(QTWEBENGINE_PLUGINS) defaultSettings->setAttribute(QWebEngineSettings::PluginsEnabled, settings.value(QLatin1String("enablePlugins"), true).toBool()); -#endif + + defaultSettings->setAttribute(QWebEngineSettings::FullScreenSupportEnabled, true); QString css = settings.value(QLatin1String("userStyleSheet")).toString(); setUserStyleSheet(defaultProfile, css, mainWindow()); defaultProfile->setHttpUserAgent(settings.value(QLatin1String("httpUserAgent")).toString()); + defaultProfile->setHttpAcceptLanguage(settings.value(QLatin1String("httpAcceptLanguage")).toString()); settings.endGroup(); settings.beginGroup(QLatin1String("cookies")); @@ -300,6 +300,21 @@ void BrowserApplication::loadSettings() defaultProfile->setPersistentStoragePath(pdataPath); settings.endGroup(); + + settings.beginGroup(QLatin1String("proxy")); + QNetworkProxy proxy; + if (settings.value(QLatin1String("enabled"), false).toBool()) { + if (settings.value(QLatin1String("type"), 0).toInt() == 0) + proxy = QNetworkProxy::Socks5Proxy; + else + proxy = QNetworkProxy::HttpProxy; + proxy.setHostName(settings.value(QLatin1String("hostName")).toString()); + proxy.setPort(settings.value(QLatin1String("port"), 1080).toInt()); + proxy.setUser(settings.value(QLatin1String("userName")).toString()); + proxy.setPassword(settings.value(QLatin1String("password")).toString()); + } + QNetworkProxy::setApplicationProxy(proxy); + settings.endGroup(); } QList<BrowserMainWindow*> BrowserApplication::mainWindows() @@ -475,18 +490,14 @@ DownloadManager *BrowserApplication::downloadManager() QNetworkAccessManager *BrowserApplication::networkAccessManager() { -#if defined(QWEBENGINEPAGE_SETNETWORKACCESSMANAGER) - if (!s_networkAccessManager) { - s_networkAccessManager = new NetworkAccessManager(); - s_networkAccessManager->setCookieJar(new CookieJar); - } - return s_networkAccessManager; -#else if (!s_networkAccessManager) { s_networkAccessManager = new QNetworkAccessManager(); + connect(s_networkAccessManager, &QNetworkAccessManager::authenticationRequired, + BrowserApplication::instance(), &BrowserApplication::authenticationRequired); + connect(s_networkAccessManager, &QNetworkAccessManager::proxyAuthenticationRequired, + BrowserApplication::instance(), &BrowserApplication::proxyAuthenticationRequired); } return s_networkAccessManager; -#endif } HistoryManager *BrowserApplication::historyManager() @@ -543,3 +554,68 @@ void BrowserApplication::setPrivateBrowsing(bool privateBrowsing) } emit privateBrowsingChanged(privateBrowsing); } + +void BrowserApplication::setLastAuthenticator(QAuthenticator *authenticator) +{ + m_lastAuthenticator = QAuthenticator(*authenticator); +} + +void BrowserApplication::setLastProxyAuthenticator(QAuthenticator *authenticator) +{ + m_lastProxyAuthenticator = QAuthenticator(*authenticator); +} + +void BrowserApplication::authenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator) +{ + if (m_lastAuthenticator.isNull()) + return; + + + Q_ASSERT(m_lastAuthenticator.option("key").isValid()); + QByteArray lastKey = m_lastAuthenticator.option("key").toByteArray(); + QByteArray key = BrowserApplication::authenticationKey(reply->url(), authenticator->realm()); + + if (lastKey == key) + *authenticator = m_lastAuthenticator; +} + +void BrowserApplication::proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator) +{ + if (m_lastProxyAuthenticator.isNull()) + return; + + QNetworkProxy::ProxyType proxyType = proxy.type(); + if (proxyType != QNetworkProxy::HttpProxy || proxyType != QNetworkProxy::HttpCachingProxy) + return; + + Q_ASSERT(m_lastProxyAuthenticator.option("host").isValid()); + QByteArray lastKey = m_lastProxyAuthenticator.option("key").toByteArray(); + QByteArray key = BrowserApplication::proxyAuthenticationKey(proxy, authenticator->realm()); + + if (lastKey == key) + *authenticator = m_lastAuthenticator; +} + +// TODO: Remove these functions (QTBUG-47967) +QByteArray BrowserApplication::authenticationKey(const QUrl &url, const QString &realm) +{ + QUrl copy = url; + copy.setFragment(realm); + return "auth:" + copy.toEncoded(QUrl::RemovePassword | QUrl::RemovePath | QUrl::RemoveQuery); +} + +QByteArray BrowserApplication::proxyAuthenticationKey(const QNetworkProxy &proxy, const QString &realm) +{ + QString host = QString("%1:%2").arg(proxy.hostName()).arg(proxy.port()); + return BrowserApplication::proxyAuthenticationKey(proxy.user(), host, realm); +} + +QByteArray BrowserApplication::proxyAuthenticationKey(const QString &user, const QString &host, const QString &realm) +{ + QUrl key; + key.setScheme(QLatin1String("proxy-http")); + key.setUserName(user); + key.setHost(host); + key.setFragment(realm); + return "auth:" + key.toEncoded(); +} diff --git a/examples/webenginewidgets/browser/browserapplication.h b/examples/webenginewidgets/demobrowser/browserapplication.h index 26557b8f9..75ae57c64 100644 --- a/examples/webenginewidgets/browser/browserapplication.h +++ b/examples/webenginewidgets/demobrowser/browserapplication.h @@ -49,9 +49,13 @@ #include <QtGui/QIcon> +#include <QtNetwork/QAuthenticator> + QT_BEGIN_NAMESPACE class QLocalServer; class QNetworkAccessManager; +class QNetworkProxy; +class QNetworkReply; class QWebEngineProfile; QT_END_NAMESPACE @@ -80,6 +84,14 @@ public: bool canRestoreSession() const; bool privateBrowsing() const { return m_privateBrowsing; } + void setLastAuthenticator(QAuthenticator *); + void setLastProxyAuthenticator(QAuthenticator *); + + // TODO: Remove these functions (QTBUG-47967) + static QByteArray authenticationKey(const QUrl &, const QString &); + static QByteArray proxyAuthenticationKey(const QNetworkProxy &, const QString &); + static QByteArray proxyAuthenticationKey(const QString &, const QString &, const QString &); + static HistoryManager *historyManager(); static CookieJar *cookieJar(); static DownloadManager *downloadManager(); @@ -93,11 +105,11 @@ public: public slots: BrowserMainWindow *newMainWindow(); void restoreLastSession(); -#if defined(Q_OS_OSX) void lastWindowClosed(); void quitBrowser(); -#endif void setPrivateBrowsing(bool); + void authenticationRequired(QNetworkReply *, QAuthenticator *); + void proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *); signals: void privateBrowsingChanged(bool); @@ -122,6 +134,9 @@ private: QWebEngineProfile *m_privateProfile; bool m_privateBrowsing; mutable QIcon m_defaultIcon; + + QAuthenticator m_lastAuthenticator; + QAuthenticator m_lastProxyAuthenticator; }; #endif // BROWSERAPPLICATION_H diff --git a/examples/webenginewidgets/browser/browsermainwindow.cpp b/examples/webenginewidgets/demobrowser/browsermainwindow.cpp index 9595ca7ba..9595ca7ba 100644 --- a/examples/webenginewidgets/browser/browsermainwindow.cpp +++ b/examples/webenginewidgets/demobrowser/browsermainwindow.cpp diff --git a/examples/webenginewidgets/browser/browsermainwindow.h b/examples/webenginewidgets/demobrowser/browsermainwindow.h index a2bbc443f..a2bbc443f 100644 --- a/examples/webenginewidgets/browser/browsermainwindow.h +++ b/examples/webenginewidgets/demobrowser/browsermainwindow.h diff --git a/examples/webenginewidgets/browser/chasewidget.cpp b/examples/webenginewidgets/demobrowser/chasewidget.cpp index fb0e9ce76..fb0e9ce76 100644 --- a/examples/webenginewidgets/browser/chasewidget.cpp +++ b/examples/webenginewidgets/demobrowser/chasewidget.cpp diff --git a/examples/webenginewidgets/browser/chasewidget.h b/examples/webenginewidgets/demobrowser/chasewidget.h index a90389034..a90389034 100644 --- a/examples/webenginewidgets/browser/chasewidget.h +++ b/examples/webenginewidgets/demobrowser/chasewidget.h diff --git a/examples/webenginewidgets/browser/cookiejar.cpp b/examples/webenginewidgets/demobrowser/cookiejar.cpp index d9a7465ca..d9a7465ca 100644 --- a/examples/webenginewidgets/browser/cookiejar.cpp +++ b/examples/webenginewidgets/demobrowser/cookiejar.cpp diff --git a/examples/webenginewidgets/browser/cookiejar.h b/examples/webenginewidgets/demobrowser/cookiejar.h index 374156e89..374156e89 100644 --- a/examples/webenginewidgets/browser/cookiejar.h +++ b/examples/webenginewidgets/demobrowser/cookiejar.h diff --git a/examples/webenginewidgets/browser/cookies.ui b/examples/webenginewidgets/demobrowser/cookies.ui index c4bccc548..c4bccc548 100644 --- a/examples/webenginewidgets/browser/cookies.ui +++ b/examples/webenginewidgets/demobrowser/cookies.ui diff --git a/examples/webenginewidgets/browser/cookiesexceptions.ui b/examples/webenginewidgets/demobrowser/cookiesexceptions.ui index 3d9ef6241..3d9ef6241 100644 --- a/examples/webenginewidgets/browser/cookiesexceptions.ui +++ b/examples/webenginewidgets/demobrowser/cookiesexceptions.ui diff --git a/examples/webenginewidgets/browser/data/addtab.png b/examples/webenginewidgets/demobrowser/data/addtab.png Binary files differindex 20928fb40..20928fb40 100644 --- a/examples/webenginewidgets/browser/data/addtab.png +++ b/examples/webenginewidgets/demobrowser/data/addtab.png diff --git a/examples/webenginewidgets/browser/data/closetab.png b/examples/webenginewidgets/demobrowser/data/closetab.png Binary files differindex ab9d669ee..ab9d669ee 100644 --- a/examples/webenginewidgets/browser/data/closetab.png +++ b/examples/webenginewidgets/demobrowser/data/closetab.png diff --git a/examples/webenginewidgets/browser/data/data.qrc b/examples/webenginewidgets/demobrowser/data/data.qrc index c7d0294c1..5cb8a97c0 100644 --- a/examples/webenginewidgets/browser/data/data.qrc +++ b/examples/webenginewidgets/demobrowser/data/data.qrc @@ -3,7 +3,7 @@ <file>addtab.png</file> <file>closetab.png</file> <file>history.png</file> - <file>browser.svg</file> + <file>demobrowser.svg</file> <file>defaultbookmarks.xbel</file> <file>loading.gif</file> <file>defaulticon.png</file> diff --git a/examples/webenginewidgets/browser/data/defaultbookmarks.xbel b/examples/webenginewidgets/demobrowser/data/defaultbookmarks.xbel index fef7f5514..fef7f5514 100644 --- a/examples/webenginewidgets/browser/data/defaultbookmarks.xbel +++ b/examples/webenginewidgets/demobrowser/data/defaultbookmarks.xbel diff --git a/examples/webenginewidgets/browser/data/defaulticon.png b/examples/webenginewidgets/demobrowser/data/defaulticon.png Binary files differindex 01a0920c9..01a0920c9 100644 --- a/examples/webenginewidgets/browser/data/defaulticon.png +++ b/examples/webenginewidgets/demobrowser/data/defaulticon.png diff --git a/examples/webenginewidgets/browser/data/browser.svg b/examples/webenginewidgets/demobrowser/data/demobrowser.svg index 2127a0a6e..f61a54c67 100644 --- a/examples/webenginewidgets/browser/data/browser.svg +++ b/examples/webenginewidgets/demobrowser/data/demobrowser.svg @@ -14,11 +14,10 @@ id="svg2160" sodipodi:version="0.32" inkscape:version="0.46" - inkscape:export-filename="c:\icons\qtbrowser48.png" inkscape:export-xdpi="90" inkscape:export-ydpi="90" sodipodi:docbase="C:\icons" - sodipodi:docname="browser.svg" + sodipodi:docname="demobrowser.svg" inkscape:output_extension="org.inkscape.output.svg.inkscape"> <defs id="defs2162"><linearGradient diff --git a/examples/webenginewidgets/browser/data/history.png b/examples/webenginewidgets/demobrowser/data/history.png Binary files differindex 552a1cbd8..552a1cbd8 100644 --- a/examples/webenginewidgets/browser/data/history.png +++ b/examples/webenginewidgets/demobrowser/data/history.png diff --git a/examples/webenginewidgets/browser/data/loading.gif b/examples/webenginewidgets/demobrowser/data/loading.gif Binary files differindex c1545eb0e..c1545eb0e 100644 --- a/examples/webenginewidgets/browser/data/loading.gif +++ b/examples/webenginewidgets/demobrowser/data/loading.gif diff --git a/examples/webenginewidgets/browser/browser.icns b/examples/webenginewidgets/demobrowser/demobrowser.icns Binary files differindex f591ae48a..f591ae48a 100644 --- a/examples/webenginewidgets/browser/browser.icns +++ b/examples/webenginewidgets/demobrowser/demobrowser.icns diff --git a/examples/webenginewidgets/browser/browser.ico b/examples/webenginewidgets/demobrowser/demobrowser.ico Binary files differindex 7f9be934d..7f9be934d 100644 --- a/examples/webenginewidgets/browser/browser.ico +++ b/examples/webenginewidgets/demobrowser/demobrowser.ico diff --git a/examples/webenginewidgets/browser/browser.pro b/examples/webenginewidgets/demobrowser/demobrowser.pro index f54186856..113eeb40e 100644 --- a/examples/webenginewidgets/browser/browser.pro +++ b/examples/webenginewidgets/demobrowser/demobrowser.pro @@ -1,6 +1,7 @@ TEMPLATE = app -TARGET = browser +TARGET = demobrowser QT += webenginewidgets network widgets printsupport +CONFIG += c++11 qtHaveModule(uitools):!embedded: QT += uitools else: DEFINES += QT_NO_UITOOLS @@ -27,6 +28,7 @@ HEADERS += \ edittableview.h \ edittreeview.h \ featurepermissionbar.h\ + fullscreennotification.h \ history.h \ modelmenu.h \ searchlineedit.h \ @@ -48,6 +50,7 @@ SOURCES += \ edittableview.cpp \ edittreeview.cpp \ featurepermissionbar.cpp\ + fullscreennotification.cpp \ history.cpp \ modelmenu.cpp \ searchlineedit.cpp \ @@ -62,28 +65,23 @@ SOURCES += \ RESOURCES += data/data.qrc htmls/htmls.qrc -contains(DEFINES, QWEBENGINEPAGE_SETNETWORKACCESSMANAGER) { - HEADERS += cookiejar.h networkaccessmanager.h - SOURCES += cookiejar.cpp networkaccessmanager.cpp -} - build_all:!build_pass { CONFIG -= build_all CONFIG += release } win32 { - RC_FILE = browser.rc + RC_FILE = demobrowser.rc } mac { - ICON = browser.icns + ICON = demobrowser.icns QMAKE_INFO_PLIST = Info_mac.plist - TARGET = Browser + TARGET = Demobrowser } -EXAMPLE_FILES = Info_mac.plist browser.icns browser.ico browser.rc +EXAMPLE_FILES = Info_mac.plist demobrowser.icns demobrowser.ico demobrowser.rc # install -target.path = $$[QT_INSTALL_EXAMPLES]/webenginewidgets/browser +target.path = $$[QT_INSTALL_EXAMPLES]/webenginewidgets/demobrowser INSTALLS += target diff --git a/examples/webenginewidgets/demobrowser/demobrowser.rc b/examples/webenginewidgets/demobrowser/demobrowser.rc new file mode 100644 index 000000000..70cd8bb1c --- /dev/null +++ b/examples/webenginewidgets/demobrowser/demobrowser.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON DISCARDABLE "demobrowser.ico" diff --git a/examples/webenginewidgets/demobrowser/doc/images/browser-demo.png b/examples/webenginewidgets/demobrowser/doc/images/browser-demo.png Binary files differnew file mode 100644 index 000000000..e8695dbca --- /dev/null +++ b/examples/webenginewidgets/demobrowser/doc/images/browser-demo.png diff --git a/examples/webenginewidgets/browser/doc/src/browser.qdoc b/examples/webenginewidgets/demobrowser/doc/src/demobrowser.qdoc index 99581d8a9..ba61dd79d 100644 --- a/examples/webenginewidgets/browser/doc/src/browser.qdoc +++ b/examples/webenginewidgets/demobrowser/doc/src/demobrowser.qdoc @@ -26,13 +26,13 @@ ****************************************************************************/ /*! - \example browser - \title WebEngine Tab Browser Example + \example webenginewidgets/demobrowser + \title WebEngine Demo Browser Example \ingroup webengine-widgetexamples - \brief The QtWebKit browser example ported to use QtWebEngine + \brief A demo browser based on Qt WebEngine Widgets - The Tab Browser example shows the \l{Qt WebEngine Widgets} module in action, - providing a little Web browser application with support for tabs. + \e {Demo Browser} demonstrates how to use the \l{Qt WebEngine Widgets C++ Classes} + {Qt WebEngine C++ classes} to develop a small Web browser application with support for tabs. \image browser-demo.png diff --git a/examples/webenginewidgets/browser/downloaditem.ui b/examples/webenginewidgets/demobrowser/downloaditem.ui index b7f7deb72..b7f7deb72 100644 --- a/examples/webenginewidgets/browser/downloaditem.ui +++ b/examples/webenginewidgets/demobrowser/downloaditem.ui diff --git a/examples/webenginewidgets/browser/downloadmanager.cpp b/examples/webenginewidgets/demobrowser/downloadmanager.cpp index aa83d7145..1c33f12c7 100644 --- a/examples/webenginewidgets/browser/downloadmanager.cpp +++ b/examples/webenginewidgets/demobrowser/downloadmanager.cpp @@ -43,7 +43,6 @@ #include "autosaver.h" #include "browserapplication.h" -#include "networkaccessmanager.h" #include <math.h> diff --git a/examples/webenginewidgets/browser/downloadmanager.h b/examples/webenginewidgets/demobrowser/downloadmanager.h index 877682d77..cd96f35a5 100644 --- a/examples/webenginewidgets/browser/downloadmanager.h +++ b/examples/webenginewidgets/demobrowser/downloadmanager.h @@ -98,7 +98,6 @@ class DownloadManager : public QDialog, public Ui_DownloadDialog { Q_OBJECT Q_PROPERTY(RemovePolicy removePolicy READ removePolicy WRITE setRemovePolicy) - Q_ENUMS(RemovePolicy) public: enum RemovePolicy { @@ -106,6 +105,7 @@ public: Exit, SuccessFullDownload }; + Q_ENUM(RemovePolicy) DownloadManager(QWidget *parent = 0); ~DownloadManager(); diff --git a/examples/webenginewidgets/browser/downloads.ui b/examples/webenginewidgets/demobrowser/downloads.ui index a2e256935..a2e256935 100644 --- a/examples/webenginewidgets/browser/downloads.ui +++ b/examples/webenginewidgets/demobrowser/downloads.ui diff --git a/examples/webenginewidgets/browser/edittableview.cpp b/examples/webenginewidgets/demobrowser/edittableview.cpp index 866ee6f1f..866ee6f1f 100644 --- a/examples/webenginewidgets/browser/edittableview.cpp +++ b/examples/webenginewidgets/demobrowser/edittableview.cpp diff --git a/examples/webenginewidgets/browser/edittableview.h b/examples/webenginewidgets/demobrowser/edittableview.h index 315427eac..315427eac 100644 --- a/examples/webenginewidgets/browser/edittableview.h +++ b/examples/webenginewidgets/demobrowser/edittableview.h diff --git a/examples/webenginewidgets/browser/edittreeview.cpp b/examples/webenginewidgets/demobrowser/edittreeview.cpp index d515ea058..d515ea058 100644 --- a/examples/webenginewidgets/browser/edittreeview.cpp +++ b/examples/webenginewidgets/demobrowser/edittreeview.cpp diff --git a/examples/webenginewidgets/browser/edittreeview.h b/examples/webenginewidgets/demobrowser/edittreeview.h index 1cf87d54f..1cf87d54f 100644 --- a/examples/webenginewidgets/browser/edittreeview.h +++ b/examples/webenginewidgets/demobrowser/edittreeview.h diff --git a/examples/webenginewidgets/browser/featurepermissionbar.cpp b/examples/webenginewidgets/demobrowser/featurepermissionbar.cpp index b7275dfc2..b7275dfc2 100644 --- a/examples/webenginewidgets/browser/featurepermissionbar.cpp +++ b/examples/webenginewidgets/demobrowser/featurepermissionbar.cpp diff --git a/examples/webenginewidgets/browser/featurepermissionbar.h b/examples/webenginewidgets/demobrowser/featurepermissionbar.h index 1bfe1b483..1bfe1b483 100644 --- a/examples/webenginewidgets/browser/featurepermissionbar.h +++ b/examples/webenginewidgets/demobrowser/featurepermissionbar.h diff --git a/examples/webenginewidgets/demobrowser/fullscreennotification.cpp b/examples/webenginewidgets/demobrowser/fullscreennotification.cpp new file mode 100644 index 000000000..8ee968bec --- /dev/null +++ b/examples/webenginewidgets/demobrowser/fullscreennotification.cpp @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** 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 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 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. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company 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 "fullscreennotification.h" + +#include <QApplication> +#include <QDesktopWidget> +#include <QGridLayout> +#include <QLabel> +#include <QPropertyAnimation> +#include <QTimer> + +FullScreenNotification::FullScreenNotification(QWidget *parent) + : QWidget(parent) + , width(400) + , height(80) + , x((parent->geometry().width() - width) / 2) + , y(80) +{ + setVisible(false); + setWindowFlags(Qt::ToolTip | Qt::WindowDoesNotAcceptFocus); + + QGridLayout *layout = new QGridLayout(this); + + m_label = new QLabel(tr("You are now in fullscreen mode. Press ESC to quit!"), this); + layout->addWidget(m_label, 0, 0, 0, 0, Qt::AlignHCenter | Qt::AlignVCenter); + + setGeometry(x, y, width, height); + + setStyleSheet("background-color: white;\ + color: black;"); + + m_animation = new QPropertyAnimation(this, "windowOpacity"); + connect(m_animation, SIGNAL(finished()), this, SLOT(fadeOutFinished())); +} + +FullScreenNotification::~FullScreenNotification() +{ +} + +void FullScreenNotification::show() +{ + setWindowOpacity(1.0); + QTimer::singleShot(300, [&] { + QWidget *parent = parentWidget(); + x = (parent->geometry().width() - width) / 2; + QPoint topLeft = parent->mapToGlobal(QPoint(x, y)); + QWidget::move(topLeft.x(), topLeft.y()); + QWidget::show(); + QWidget::raise(); + }); + QTimer::singleShot(5000, this, SLOT(fadeOut())); +} + +void FullScreenNotification::hide() +{ + QWidget::hide(); + m_animation->stop(); +} + +void FullScreenNotification::fadeOut() +{ + m_animation->setDuration(800); + m_animation->setStartValue(1.0); + m_animation->setEndValue(0.0); + m_animation->setEasingCurve(QEasingCurve::OutQuad); + m_animation->start(); +} + +void FullScreenNotification::fadeOutFinished() +{ + hide(); + setWindowOpacity(1.0); +} diff --git a/examples/webenginewidgets/demobrowser/fullscreennotification.h b/examples/webenginewidgets/demobrowser/fullscreennotification.h new file mode 100644 index 000000000..051075ab3 --- /dev/null +++ b/examples/webenginewidgets/demobrowser/fullscreennotification.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** 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 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 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. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company 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 FULLSCREENNOTIFICATION_H +#define FULLSCREENNOTIFICATION_H + +#include <QWidget> + +class QLabel; +class QGridLayout; +class QPropertyAnimation; + +class FullScreenNotification : public QWidget +{ + Q_OBJECT +public: + FullScreenNotification(QWidget *parent = 0); + ~FullScreenNotification(); + + void show(); + void hide(); + +public slots: + void fadeOut(); + void fadeOutFinished(); + +private: + QLabel *m_label; + QGridLayout *m_layout; + QPropertyAnimation *m_animation; + int width; + int height; + int x; + int y; +}; + + +#endif // FULLSCREENNOTIFICATION_H diff --git a/examples/webenginewidgets/browser/history.cpp b/examples/webenginewidgets/demobrowser/history.cpp index ce104e982..ce104e982 100644 --- a/examples/webenginewidgets/browser/history.cpp +++ b/examples/webenginewidgets/demobrowser/history.cpp diff --git a/examples/webenginewidgets/browser/history.h b/examples/webenginewidgets/demobrowser/history.h index bcb9c9b28..bcb9c9b28 100644 --- a/examples/webenginewidgets/browser/history.h +++ b/examples/webenginewidgets/demobrowser/history.h diff --git a/examples/webenginewidgets/browser/history.ui b/examples/webenginewidgets/demobrowser/history.ui index 0944940e7..0944940e7 100644 --- a/examples/webenginewidgets/browser/history.ui +++ b/examples/webenginewidgets/demobrowser/history.ui diff --git a/examples/webenginewidgets/browser/htmls/htmls.qrc b/examples/webenginewidgets/demobrowser/htmls/htmls.qrc index 03b256ccb..03b256ccb 100644 --- a/examples/webenginewidgets/browser/htmls/htmls.qrc +++ b/examples/webenginewidgets/demobrowser/htmls/htmls.qrc diff --git a/examples/webenginewidgets/browser/htmls/notfound.html b/examples/webenginewidgets/demobrowser/htmls/notfound.html index e89845aa6..e89845aa6 100644 --- a/examples/webenginewidgets/browser/htmls/notfound.html +++ b/examples/webenginewidgets/demobrowser/htmls/notfound.html diff --git a/examples/webenginewidgets/browser/main.cpp b/examples/webenginewidgets/demobrowser/main.cpp index 86859f741..86859f741 100644 --- a/examples/webenginewidgets/browser/main.cpp +++ b/examples/webenginewidgets/demobrowser/main.cpp diff --git a/examples/webenginewidgets/browser/modelmenu.cpp b/examples/webenginewidgets/demobrowser/modelmenu.cpp index ec0eaffab..ec0eaffab 100644 --- a/examples/webenginewidgets/browser/modelmenu.cpp +++ b/examples/webenginewidgets/demobrowser/modelmenu.cpp diff --git a/examples/webenginewidgets/browser/modelmenu.h b/examples/webenginewidgets/demobrowser/modelmenu.h index c0a7d56b8..c0a7d56b8 100644 --- a/examples/webenginewidgets/browser/modelmenu.h +++ b/examples/webenginewidgets/demobrowser/modelmenu.h diff --git a/examples/webenginewidgets/browser/passworddialog.ui b/examples/webenginewidgets/demobrowser/passworddialog.ui index 7c1665867..7c1665867 100644 --- a/examples/webenginewidgets/browser/passworddialog.ui +++ b/examples/webenginewidgets/demobrowser/passworddialog.ui diff --git a/examples/webenginewidgets/browser/proxy.ui b/examples/webenginewidgets/demobrowser/proxy.ui index 62a8be627..62a8be627 100644 --- a/examples/webenginewidgets/browser/proxy.ui +++ b/examples/webenginewidgets/demobrowser/proxy.ui diff --git a/examples/webenginewidgets/browser/searchlineedit.cpp b/examples/webenginewidgets/demobrowser/searchlineedit.cpp index 5152ac1c0..5152ac1c0 100644 --- a/examples/webenginewidgets/browser/searchlineedit.cpp +++ b/examples/webenginewidgets/demobrowser/searchlineedit.cpp diff --git a/examples/webenginewidgets/browser/searchlineedit.h b/examples/webenginewidgets/demobrowser/searchlineedit.h index b8b8337fd..b8b8337fd 100644 --- a/examples/webenginewidgets/browser/searchlineedit.h +++ b/examples/webenginewidgets/demobrowser/searchlineedit.h diff --git a/examples/webenginewidgets/browser/settings.cpp b/examples/webenginewidgets/demobrowser/settings.cpp index 7ed5f707b..35e5e26c9 100644 --- a/examples/webenginewidgets/browser/settings.cpp +++ b/examples/webenginewidgets/demobrowser/settings.cpp @@ -47,9 +47,9 @@ #include "cookiejar.h" #endif #include "history.h" -#include "networkaccessmanager.h" #include "webview.h" +#include <QtCore/QLocale> #include <QtCore/QSettings> #include <QtWidgets/QtWidgets> #include <QtWebEngineWidgets/QtWebEngineWidgets> @@ -66,6 +66,21 @@ SettingsDialog::SettingsDialog(QWidget *parent) loadFromSettings(); } +static QString defaultAcceptLanguage() +{ + const QStringList langs = QLocale().uiLanguages(); + if (langs.isEmpty()) + return QString(); + QString str = langs.first(); + const float qstep = 1.0f / float(langs.count()); + float q = 1.0f - qstep; + for (int i = 1; i < langs.count(); ++i) { + str += QStringLiteral(", ") + langs.at(i) + QStringLiteral(";q=") + QString::number(q, 'f', 2); + q -= qstep; + } + return str; +} + void SettingsDialog::loadDefaults() { QWebEngineSettings *defaultSettings = QWebEngineSettings::globalSettings(); @@ -82,15 +97,14 @@ void SettingsDialog::loadDefaults() downloadsLocation->setText(QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)); enableJavascript->setChecked(defaultSettings->testAttribute(QWebEngineSettings::JavascriptEnabled)); -#if defined(QTWEBENGINE_PLUGINS) enablePlugins->setChecked(defaultSettings->testAttribute(QWebEngineSettings::PluginsEnabled)); -#endif enableScrollAnimator->setChecked(defaultSettings->testAttribute(QWebEngineSettings::ScrollAnimatorEnabled)); persistentDataPath->setText(QWebEngineProfile::defaultProfile()->persistentStoragePath()); sessionCookiesCombo->setCurrentIndex(QWebEngineProfile::defaultProfile()->persistentCookiesPolicy()); httpUserAgent->setText(QWebEngineProfile::defaultProfile()->httpUserAgent()); + httpAcceptLanguage->setText(defaultAcceptLanguage()); } void SettingsDialog::loadFromSettings() @@ -140,6 +154,7 @@ void SettingsDialog::loadFromSettings() userStyleSheet->setPlainText(settings.value(QLatin1String("userStyleSheet")).toString()); enableScrollAnimator->setChecked(settings.value(QLatin1String("enableScrollAnimator"), enableScrollAnimator->isChecked()).toBool()); httpUserAgent->setText(settings.value(QLatin1String("httpUserAgent"), httpUserAgent->text()).toString()); + httpAcceptLanguage->setText(settings.value(QLatin1String("httpAcceptLanguage"), httpAcceptLanguage->text()).toString()); settings.endGroup(); // Privacy @@ -198,6 +213,7 @@ void SettingsDialog::saveToSettings() settings.setValue(QLatin1String("enableScrollAnimator"), enableScrollAnimator->isChecked()); settings.setValue(QLatin1String("userStyleSheet"), userStyleSheet->toPlainText()); settings.setValue(QLatin1String("httpUserAgent"), httpUserAgent->text()); + settings.setValue(QLatin1String("httpAcceptLanguage"), httpAcceptLanguage->text()); settings.endGroup(); //Privacy @@ -223,7 +239,6 @@ void SettingsDialog::saveToSettings() BrowserApplication::instance()->loadSettings(); #if defined(QWEBENGINEPAGE_SETNETWORKACCESSMANAGER) - BrowserApplication::networkAccessManager()->loadSettings(); BrowserApplication::cookieJar()->loadSettings(); #endif BrowserApplication::historyManager()->loadSettings(); @@ -271,6 +286,16 @@ void SettingsDialog::chooseFixedFont() } } +void SettingsDialog::on_httpUserAgent_editingFinished() +{ + QWebEngineProfile::defaultProfile()->setHttpUserAgent(httpUserAgent->text()); +} + +void SettingsDialog::on_httpAcceptLanguage_editingFinished() +{ + QWebEngineProfile::defaultProfile()->setHttpAcceptLanguage(httpAcceptLanguage->text()); +} + void SettingsDialog::setHomeToCurrentPage() { BrowserMainWindow *mw = static_cast<BrowserMainWindow*>(parent()); diff --git a/examples/webenginewidgets/browser/settings.h b/examples/webenginewidgets/demobrowser/settings.h index 6def66928..3a50dd29f 100644 --- a/examples/webenginewidgets/browser/settings.h +++ b/examples/webenginewidgets/demobrowser/settings.h @@ -65,6 +65,9 @@ private slots: void chooseFont(); void chooseFixedFont(); + void on_httpUserAgent_editingFinished(); + void on_httpAcceptLanguage_editingFinished(); + private: QFont standardFont; QFont fixedFont; diff --git a/examples/webenginewidgets/browser/settings.ui b/examples/webenginewidgets/demobrowser/settings.ui index 11bdee4d3..38a7af344 100644 --- a/examples/webenginewidgets/browser/settings.ui +++ b/examples/webenginewidgets/demobrowser/settings.ui @@ -7,7 +7,7 @@ <x>0</x> <y>0</y> <width>657</width> - <height>322</height> + <height>336</height> </rect> </property> <property name="windowTitle"> @@ -499,19 +499,29 @@ <string>Advanced</string> </attribute> <layout class="QGridLayout" name="gridLayout_2"> - <item row="2" column="1"> - <widget class="QTextEdit" name="userStyleSheet"/> + <item row="0" column="0"> + <widget class="QLabel" name="label_15"> + <property name="text"> + <string>HTTP User-Agent:</string> + </property> + </widget> </item> <item row="0" column="1"> <widget class="QLineEdit" name="httpUserAgent"/> </item> - <item row="0" column="0"> - <widget class="QLabel" name="label_15"> + <item row="1" column="0"> + <widget class="QLabel" name="label_16"> <property name="text"> - <string>HTTP User-Agent:</string> + <string>HTTP Accept-&Language:</string> + </property> + <property name="buddy"> + <cstring>httpAcceptLanguage</cstring> </property> </widget> </item> + <item row="1" column="1"> + <widget class="QLineEdit" name="httpAcceptLanguage"/> + </item> <item row="2" column="0"> <widget class="QLabel" name="label_14"> <property name="text"> @@ -519,7 +529,10 @@ </property> </widget> </item> - <item row="4" column="1"> + <item row="2" column="1"> + <widget class="QTextEdit" name="userStyleSheet"/> + </item> + <item row="3" column="1"> <spacer name="verticalSpacer_3"> <property name="orientation"> <enum>Qt::Vertical</enum> diff --git a/examples/webenginewidgets/browser/squeezelabel.cpp b/examples/webenginewidgets/demobrowser/squeezelabel.cpp index 78198c3f0..78198c3f0 100644 --- a/examples/webenginewidgets/browser/squeezelabel.cpp +++ b/examples/webenginewidgets/demobrowser/squeezelabel.cpp diff --git a/examples/webenginewidgets/browser/squeezelabel.h b/examples/webenginewidgets/demobrowser/squeezelabel.h index 628b7350a..628b7350a 100644 --- a/examples/webenginewidgets/browser/squeezelabel.h +++ b/examples/webenginewidgets/demobrowser/squeezelabel.h diff --git a/examples/webenginewidgets/browser/tabwidget.cpp b/examples/webenginewidgets/demobrowser/tabwidget.cpp index c093b804a..95b79aaac 100644 --- a/examples/webenginewidgets/browser/tabwidget.cpp +++ b/examples/webenginewidgets/demobrowser/tabwidget.cpp @@ -44,14 +44,16 @@ #include "browserapplication.h" #include "browsermainwindow.h" #include "downloadmanager.h" +#include "fullscreennotification.h" #include "history.h" #include "urllineedit.h" #include "webview.h" +#include <QWebEngineDownloadItem> +#include <QWebEngineProfile> +#include <QWebEngineFullScreenRequest> #include <QtCore/QMimeData> #include <QtGui/QClipboard> -#include <QtWebEngineWidgets/QWebEngineDownloadItem> -#include <QtWebEngineWidgets/QWebEngineProfile> #include <QtWidgets/QCompleter> #include <QtWidgets/QListView> #include <QtWidgets/QMenu> @@ -85,6 +87,12 @@ TabBar::TabBar(QWidget *parent) setMovable(true); } +TabWidget::~TabWidget() +{ + delete m_fullScreenNotification; + delete m_fullScreenView; +} + void TabBar::selectTabAction() { if (QShortcut *shortCut = qobject_cast<QShortcut*>(sender())) { @@ -213,11 +221,13 @@ TabWidget::TabWidget(QWidget *parent) , m_lineEdits(0) , m_tabBar(new TabBar(this)) , m_profile(QWebEngineProfile::defaultProfile()) + , m_fullScreenView(0) + , m_fullScreenNotification(0) { setElideMode(Qt::ElideRight); connect(m_tabBar, SIGNAL(newTab()), this, SLOT(newTab())); - connect(m_tabBar, SIGNAL(closeTab(int)), this, SLOT(closeTab(int))); + connect(m_tabBar, SIGNAL(closeTab(int)), this, SLOT(requestCloseTab(int))); connect(m_tabBar, SIGNAL(cloneTab(int)), this, SLOT(cloneTab(int))); connect(m_tabBar, SIGNAL(closeOtherTabs(int)), this, SLOT(closeOtherTabs(int))); connect(m_tabBar, SIGNAL(reloadTab(int)), this, SLOT(reloadTab(int))); @@ -235,7 +245,7 @@ TabWidget::TabWidget(QWidget *parent) m_closeTabAction = new QAction(QIcon(QLatin1String(":closetab.png")), tr("&Close Tab"), this); m_closeTabAction->setShortcuts(QKeySequence::Close); m_closeTabAction->setIconVisibleInMenu(false); - connect(m_closeTabAction, SIGNAL(triggered()), this, SLOT(closeTab())); + connect(m_closeTabAction, SIGNAL(triggered()), this, SLOT(requestCloseTab())); m_nextTabAction = new QAction(tr("Show Next Tab"), this); QList<QKeySequence> shortcuts; @@ -315,6 +325,8 @@ void TabWidget::currentChanged(int index) this, SIGNAL(loadProgress(int))); disconnect(oldWebView->page()->profile(), SIGNAL(downloadRequested(QWebEngineDownloadItem*)), this, SLOT(downloadRequested(QWebEngineDownloadItem*))); + disconnect(oldWebView->page(), SIGNAL(fullScreenRequested(QWebEngineFullScreenRequest)), + this, SLOT(fullScreenRequested(QWebEngineFullScreenRequest))); } #if defined(QWEBENGINEVIEW_STATUSBARMESSAGE) @@ -327,6 +339,8 @@ void TabWidget::currentChanged(int index) this, SIGNAL(loadProgress(int))); connect(webView->page()->profile(), SIGNAL(downloadRequested(QWebEngineDownloadItem*)), this, SLOT(downloadRequested(QWebEngineDownloadItem*))); + connect(webView->page(), SIGNAL(fullScreenRequested(QWebEngineFullScreenRequest)), + this, SLOT(fullScreenRequested(QWebEngineFullScreenRequest))); for (int i = 0; i < m_actions.count(); ++i) { WebActionMapper *mapper = m_actions[i]; @@ -342,6 +356,38 @@ void TabWidget::currentChanged(int index) webView->setFocus(); } +void TabWidget::fullScreenRequested(QWebEngineFullScreenRequest request) +{ + WebPage *webPage = qobject_cast<WebPage*>(sender()); + if (request.toggleOn()) { + if (!m_fullScreenView) { + m_fullScreenView = new QWebEngineView(); + m_fullScreenNotification = new FullScreenNotification(m_fullScreenView); + + QAction *exitFullScreenAction = new QAction(m_fullScreenView); + exitFullScreenAction->setShortcut(Qt::Key_Escape); + connect(exitFullScreenAction, &QAction::triggered, [webPage] { + webPage->triggerAction(QWebEnginePage::ExitFullScreen); + }); + m_fullScreenView->addAction(exitFullScreenAction); + } + webPage->setView(m_fullScreenView); + request.accept(); + m_fullScreenView->showFullScreen(); + m_fullScreenView->raise(); + m_fullScreenNotification->show(); + } else { + if (!m_fullScreenView) + return; + WebView *oldWebView = this->webView(m_lineEdits->currentIndex()); + webPage->setView(oldWebView); + request.accept(); + raise(); + m_fullScreenView->hide(); + m_fullScreenNotification->hide(); + } +} + QAction *TabWidget::newTabAction() const { return m_newTabAction; @@ -522,12 +568,8 @@ void TabWidget::windowCloseRequested() WebPage *webPage = qobject_cast<WebPage*>(sender()); WebView *webView = qobject_cast<WebView*>(webPage->view()); int index = webViewIndex(webView); - if (index >= 0) { - if (count() == 1) - webView->webPage()->mainWindow()->close(); - else - closeTab(index); - } + if (index >= 0) + closeTab(index); } void TabWidget::closeOtherTabs(int index) @@ -552,30 +594,25 @@ void TabWidget::cloneTab(int index) } // When index is -1 index chooses the current tab -void TabWidget::closeTab(int index) +void TabWidget::requestCloseTab(int index) { if (index < 0) index = currentIndex(); if (index < 0 || index >= count()) return; + WebView *tab = webView(index); + if (!tab) + return; + tab->page()->triggerAction(QWebEnginePage::RequestClose); +} + +void TabWidget::closeTab(int index) +{ + if (index < 0 || index >= count()) + return; bool hasFocus = false; if (WebView *tab = webView(index)) { -#if defined(QWEBENGINEPAGE_ISMODIFIED) - if (tab->isModified()) { - QMessageBox closeConfirmation(tab); - closeConfirmation.setWindowFlags(Qt::Sheet); - closeConfirmation.setWindowTitle(tr("Do you really want to close this page?")); - closeConfirmation.setInformativeText(tr("You have modified this page and when closing it you would lose the modification.\n" - "Do you really want to close this page?\n")); - closeConfirmation.setIcon(QMessageBox::Question); - closeConfirmation.addButton(QMessageBox::Yes); - closeConfirmation.addButton(QMessageBox::No); - closeConfirmation.setEscapeButton(QMessageBox::No); - if (closeConfirmation.exec() == QMessageBox::No) - return; - } -#endif hasFocus = tab->hasFocus(); if (m_profile == QWebEngineProfile::defaultProfile()) { diff --git a/examples/webenginewidgets/browser/tabwidget.h b/examples/webenginewidgets/demobrowser/tabwidget.h index 5d7f1e2c5..b00131130 100644 --- a/examples/webenginewidgets/browser/tabwidget.h +++ b/examples/webenginewidgets/demobrowser/tabwidget.h @@ -42,13 +42,14 @@ #ifndef TABWIDGET_H #define TABWIDGET_H +#include <QtWebEngineWidgets/QWebEngineFullScreenRequest> #include <QtWidgets/QTabBar> - #include <QtWidgets/QShortcut> QT_BEGIN_NAMESPACE class QWebEngineDownloadItem; class QWebEngineProfile; +class QWebEngineView; QT_END_NAMESPACE /* @@ -128,6 +129,7 @@ private: #include <QtCore/QUrl> #include <QtWidgets/QTabWidget> QT_BEGIN_NAMESPACE +class FullScreenNotification; class QCompleter; class QLineEdit; class QMenu; @@ -164,6 +166,7 @@ signals: public: TabWidget(QWidget *parent = 0); + ~TabWidget(); void clear(); void addWebAction(QAction *action, QWebEnginePage::WebAction webAction); @@ -194,7 +197,8 @@ public slots: void loadUrlInCurrentTab(const QUrl &url); WebView *newTab(bool makeCurrent = true); void cloneTab(int index = -1); - void closeTab(int index = -1); + void requestCloseTab(int index = -1); + void closeTab(int index); void closeOtherTabs(int index); void reloadTab(int index = -1); void reloadAllTabs(); @@ -213,6 +217,7 @@ private slots: void lineEditReturnPressed(); void windowCloseRequested(); void moveTab(int fromIndex, int toIndex); + void fullScreenRequested(QWebEngineFullScreenRequest request); private: QAction *m_recentlyClosedTabsAction; @@ -230,6 +235,8 @@ private: QStackedWidget *m_lineEdits; TabBar *m_tabBar; QWebEngineProfile *m_profile; + QWebEngineView *m_fullScreenView; + FullScreenNotification *m_fullScreenNotification; }; #endif // TABWIDGET_H diff --git a/examples/webenginewidgets/browser/toolbarsearch.cpp b/examples/webenginewidgets/demobrowser/toolbarsearch.cpp index 42cdaec18..42cdaec18 100644 --- a/examples/webenginewidgets/browser/toolbarsearch.cpp +++ b/examples/webenginewidgets/demobrowser/toolbarsearch.cpp diff --git a/examples/webenginewidgets/browser/toolbarsearch.h b/examples/webenginewidgets/demobrowser/toolbarsearch.h index cbe00366c..cbe00366c 100644 --- a/examples/webenginewidgets/browser/toolbarsearch.h +++ b/examples/webenginewidgets/demobrowser/toolbarsearch.h diff --git a/examples/webenginewidgets/browser/urllineedit.cpp b/examples/webenginewidgets/demobrowser/urllineedit.cpp index 685277d9c..685277d9c 100644 --- a/examples/webenginewidgets/browser/urllineedit.cpp +++ b/examples/webenginewidgets/demobrowser/urllineedit.cpp diff --git a/examples/webenginewidgets/browser/urllineedit.h b/examples/webenginewidgets/demobrowser/urllineedit.h index 775aa7d18..775aa7d18 100644 --- a/examples/webenginewidgets/browser/urllineedit.h +++ b/examples/webenginewidgets/demobrowser/urllineedit.h diff --git a/examples/webenginewidgets/browser/webview.cpp b/examples/webenginewidgets/demobrowser/webview.cpp index 99986642e..79a6cf344 100644 --- a/examples/webenginewidgets/browser/webview.cpp +++ b/examples/webenginewidgets/demobrowser/webview.cpp @@ -44,7 +44,6 @@ #include "cookiejar.h" #include "downloadmanager.h" #include "featurepermissionbar.h" -#include "networkaccessmanager.h" #include "ui_passworddialog.h" #include "ui_proxy.h" #include "tabwidget.h" @@ -67,12 +66,12 @@ #include <QtCore/QDebug> #include <QtCore/QBuffer> +#include <QtCore/QTimer> WebPage::WebPage(QWebEngineProfile *profile, QObject *parent) : QWebEnginePage(profile, parent) , m_keyboardModifiers(Qt::NoModifier) , m_pressedButtons(Qt::NoButton) - , m_openInNewTab(false) { #if defined(QWEBENGINEPAGE_SETNETWORKACCESSMANAGER) setNetworkAccessManager(BrowserApplication::networkAccessManager()); @@ -175,8 +174,7 @@ private: QWebEnginePage *WebPage::createWindow(QWebEnginePage::WebWindowType type) { - if (m_openInNewTab || type == QWebEnginePage::WebBrowserTab) { - m_openInNewTab = false; + if (type == QWebEnginePage::WebBrowserTab) { return mainWindow()->tabWidget()->newTab()->page(); } else if (type == QWebEnginePage::WebBrowserWindow) { BrowserApplication::instance()->newMainWindow(); @@ -274,8 +272,14 @@ void WebPage::authenticationRequired(const QUrl &requestUrl, QAuthenticator *aut passwordDialog.introLabel->setWordWrap(true); if (dialog.exec() == QDialog::Accepted) { + QByteArray key = BrowserApplication::authenticationKey(requestUrl, auth->realm()); auth->setUser(passwordDialog.userNameLineEdit->text()); auth->setPassword(passwordDialog.passwordLineEdit->text()); + auth->setOption("key", key); + BrowserApplication::instance()->setLastAuthenticator(auth); + } else { + // Set authenticator null if dialog is cancelled + *auth = QAuthenticator(); } } @@ -299,8 +303,15 @@ void WebPage::proxyAuthenticationRequired(const QUrl &requestUrl, QAuthenticator proxyDialog.introLabel->setWordWrap(true); if (dialog.exec() == QDialog::Accepted) { - auth->setUser(proxyDialog.userNameLineEdit->text()); + QString user = proxyDialog.userNameLineEdit->text(); + QByteArray key = BrowserApplication::proxyAuthenticationKey(user, proxyHost, auth->realm()); + auth->setUser(user); auth->setPassword(proxyDialog.passwordLineEdit->text()); + auth->setOption("key", key); + BrowserApplication::instance()->setLastProxyAuthenticator(auth); + } else { + // Set authenticator null if dialog is cancelled + *auth = QAuthenticator(); } } @@ -314,6 +325,27 @@ WebView::WebView(QWidget* parent) this, SLOT(setProgress(int))); connect(this, SIGNAL(loadFinished(bool)), this, SLOT(loadFinished(bool))); + connect(this, &QWebEngineView::renderProcessTerminated, + [=](QWebEnginePage::RenderProcessTerminationStatus termStatus, int statusCode) { + const char *status = ""; + switch (termStatus) { + case QWebEnginePage::NormalTerminationStatus: + status = "(normal exit)"; + break; + case QWebEnginePage::AbnormalTerminationStatus: + status = "(abnormal exit)"; + break; + case QWebEnginePage::CrashedTerminationStatus: + status = "(crashed)"; + break; + case QWebEnginePage::KilledTerminationStatus: + status = "(killed)"; + break; + } + + qInfo() << "Render process exited with code" << statusCode << status; + QTimer::singleShot(0, [this] { reload(); }); + }); } void WebView::setPage(WebPage *_page) @@ -338,24 +370,17 @@ void WebView::setPage(WebPage *_page) void WebView::contextMenuEvent(QContextMenuEvent *event) { -#if defined(QWEBENGINEPAGE_HITTESTCONTENT) - QWebEngineHitTestResult r = page()->hitTestContent(event->pos()); - if (!r.linkUrl().isEmpty()) { - QMenu menu(this); - menu.addAction(pageAction(QWebEnginePage::OpenLinkInNewWindow)); - menu.addAction(tr("Open in New Tab"), this, SLOT(openLinkInNewTab())); - menu.addSeparator(); - menu.addAction(pageAction(QWebEnginePage::DownloadLinkToDisk)); - // Add link to bookmarks... - menu.addSeparator(); - menu.addAction(pageAction(QWebEnginePage::CopyLinkToClipboard)); - if (page()->settings()->testAttribute(QWebEngineSettings::DeveloperExtrasEnabled)) - menu.addAction(pageAction(QWebEnginePage::InspectElement)); - menu.exec(mapToGlobal(event->pos())); - return; + QMenu *menu = page()->createStandardContextMenu(); + const QList<QAction*> actions = menu->actions(); + QList<QAction*>::const_iterator it = qFind(actions.cbegin(), actions.cend(), page()->action(QWebEnginePage::OpenLinkInThisWindow)); + if (it != actions.cend()) { + (*it)->setText(tr("Open Link in This Window")); + ++it; + menu->insertAction(*it, page()->action(QWebEnginePage::OpenLinkInNewWindow)); + menu->insertAction(*it, page()->action(QWebEnginePage::OpenLinkInNewTab)); } -#endif - QWebEngineView::contextMenuEvent(event); + + menu->popup(event->globalPos()); } void WebView::wheelEvent(QWheelEvent *event) @@ -374,10 +399,7 @@ void WebView::wheelEvent(QWheelEvent *event) void WebView::openLinkInNewTab() { -#if defined(QWEBENGINEPAGE_WEBACTION_OPENLINKINNEWWINDOW) - m_page->m_openInNewTab = true; - pageAction(QWebEnginePage::OpenLinkInNewWindow)->trigger(); -#endif + pageAction(QWebEnginePage::OpenLinkInNewTab)->trigger(); } void WebView::onFeaturePermissionRequested(const QUrl &securityOrigin, QWebEnginePage::Feature feature) diff --git a/examples/webenginewidgets/browser/webview.h b/examples/webenginewidgets/demobrowser/webview.h index a147d780a..34188a259 100644 --- a/examples/webenginewidgets/browser/webview.h +++ b/examples/webenginewidgets/demobrowser/webview.h @@ -65,8 +65,8 @@ public: BrowserMainWindow *mainWindow(); protected: - bool acceptNavigationRequest(const QUrl &url, NavigationType type, bool isMainFrame); - QWebEnginePage *createWindow(QWebEnginePage::WebWindowType type); + bool acceptNavigationRequest(const QUrl &url, NavigationType type, bool isMainFrame) Q_DECL_OVERRIDE; + QWebEnginePage *createWindow(QWebEnginePage::WebWindowType type) Q_DECL_OVERRIDE; #if !defined(QT_NO_UITOOLS) QObject *createPlugin(const QString &classId, const QUrl &url, const QStringList ¶mNames, const QStringList ¶mValues); #endif @@ -85,7 +85,6 @@ private: // set the webview mousepressedevent Qt::KeyboardModifiers m_keyboardModifiers; Qt::MouseButtons m_pressedButtons; - bool m_openInNewTab; QUrl m_loadingUrl; }; diff --git a/examples/webenginewidgets/browser/xbel.cpp b/examples/webenginewidgets/demobrowser/xbel.cpp index 6b4f53d2e..6b4f53d2e 100644 --- a/examples/webenginewidgets/browser/xbel.cpp +++ b/examples/webenginewidgets/demobrowser/xbel.cpp diff --git a/examples/webenginewidgets/browser/xbel.h b/examples/webenginewidgets/demobrowser/xbel.h index d95549dd8..d95549dd8 100644 --- a/examples/webenginewidgets/browser/xbel.h +++ b/examples/webenginewidgets/demobrowser/xbel.h diff --git a/examples/webenginewidgets/fancybrowser/doc/src/fancybrowser.qdoc b/examples/webenginewidgets/fancybrowser/doc/src/fancybrowser.qdoc index e6c6ada34..b798e4832 100644 --- a/examples/webenginewidgets/fancybrowser/doc/src/fancybrowser.qdoc +++ b/examples/webenginewidgets/fancybrowser/doc/src/fancybrowser.qdoc @@ -26,7 +26,7 @@ ****************************************************************************/ /*! - \example fancybrowser + \example webenginewidgets/fancybrowser \title WebEngine Fancy Browser Example \ingroup webengine-widgetexamples \brief Demonstrates how to use browse web and manipulate content @@ -48,7 +48,7 @@ The \c MainWindow class inherits QMainWindow. It implements a number of slots to perform actions on both the application and on the web content. - \snippet fancybrowser/mainwindow.h 1 + \snippet webenginewidgets/fancybrowser/mainwindow.h 1 We also declare a QString that contains the jQuery, a QWebView that displays the web content, and a QLineEdit that acts as the @@ -58,7 +58,7 @@ We start by implementing the constructor. - \snippet fancybrowser/mainwindow.cpp 1 + \snippet webenginewidgets/fancybrowser/mainwindow.cpp 1 The first part of the constructor sets the value of \c progress to 0. This value will be used later in the code to visualize the @@ -68,7 +68,7 @@ content. The jQuery library is a JavaScript library that provides different functions for manipulating HTML. - \snippet fancybrowser/mainwindow.cpp 2 + \snippet webenginewidgets/fancybrowser/mainwindow.cpp 2 The second part of the constructor creates a QWebView and connects slots to the views signals. Furthermore, we create a QLineEdit as @@ -77,13 +77,13 @@ QLineEdit to a QToolbar together with a set of navigation actions from QWebView::pageAction. - \snippet fancybrowser/mainwindow.cpp 3 + \snippet webenginewidgets/fancybrowser/mainwindow.cpp 3 The third and last part of the constructor implements two QMenus and assigns a set of actions to them. The last line sets the QWebView as the central widget in the QMainWindow. - \snippet fancybrowser/mainwindow.cpp 4 + \snippet webenginewidgets/fancybrowser/mainwindow.cpp 4 When the page is loaded, \c adjustLocation() updates the address bar; \c adjustLocation() is triggered by the \c loadFinished() @@ -92,13 +92,13 @@ the new web page has finished loading, \c adjustLocation() will be run once more to update the address bar. - \snippet fancybrowser/mainwindow.cpp 5 + \snippet webenginewidgets/fancybrowser/mainwindow.cpp 5 \c adjustTitle() sets the window title and displays the loading progress. This slot is triggered by the \c titleChanged() signal in QWebView. - \snippet fancybrowser/mainwindow.cpp 6 + \snippet webenginewidgets/fancybrowser/mainwindow.cpp 6 When a web page has loaded, \c finishLoading() is triggered by the \c loadFinished() signal in QWebView. \c finishLoading() then updates the @@ -113,7 +113,7 @@ that the images of the newly loaded page respect the state of the toggle action. - \snippet fancybrowser/mainwindow.cpp 7 + \snippet webenginewidgets/fancybrowser/mainwindow.cpp 7 The first jQuery-based function, \c highlightAllLinks(), is designed to highlight all links in the current webpage. The JavaScript code looks @@ -121,14 +121,14 @@ For each such element, the background color is set to be yellow by using CSS. - \snippet fancybrowser/mainwindow.cpp 8 + \snippet webenginewidgets/fancybrowser/mainwindow.cpp 8 The \c rotateImages() function rotates the images on the current web page. This JavaScript code relies on CSS transforms and looks up all \e {img} elements and rotates the images 180 degrees and then back again. - \snippet fancybrowser/mainwindow.cpp 9 + \snippet webenginewidgets/fancybrowser/mainwindow.cpp 9 The remaining four methods remove different elements from the current web page. \c removeGifImages() removes all GIF images on the page by looking up diff --git a/examples/webenginewidgets/markdowneditor/3RDPARTY.md b/examples/webenginewidgets/markdowneditor/3RDPARTY.md new file mode 100644 index 000000000..9e91ab302 --- /dev/null +++ b/examples/webenginewidgets/markdowneditor/3RDPARTY.md @@ -0,0 +1,23 @@ +## markd license + +``` +Copyright (c) 2011-2014, Christopher Jeffrey (https://github.com/chjj/) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +``` diff --git a/examples/webenginewidgets/markdowneditor/doc/images/markdowneditor-example.png b/examples/webenginewidgets/markdowneditor/doc/images/markdowneditor-example.png Binary files differnew file mode 100644 index 000000000..9f456c4db --- /dev/null +++ b/examples/webenginewidgets/markdowneditor/doc/images/markdowneditor-example.png diff --git a/examples/webenginewidgets/markdowneditor/doc/src/markdowneditor.qdoc b/examples/webenginewidgets/markdowneditor/doc/src/markdowneditor.qdoc new file mode 100644 index 000000000..44b1246b3 --- /dev/null +++ b/examples/webenginewidgets/markdowneditor/doc/src/markdowneditor.qdoc @@ -0,0 +1,164 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: http://www.gnu.org/copyleft/fdl.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \example webenginewidgets/markdowneditor + \title Markdown Editor Example + \ingroup webengine-widgetexamples + \brief Demonstrates how to integrate a web engine in a hybrid desktop + application. + + \image markdowneditor-example.png + + \e {Markdown Editor} demonstrates how to use QWebChannel and JavaScript + libraries to provide a rich text preview tool for a custom markup language. + + \l{http://daringfireball.net/projects/markdown/}{Markdown} is a lightweight + markup language with a plain text formatting syntax. + Some services, such as \l{http://github.com}{github}, acknowledge the + format, and render the content as rich text when viewed in a browser. + + The Markdown Editor main window is split into an editor and a preview area. + The editor supports the Markdown syntax and is implemented by using + QPlainTextEdit. The document is rendered as rich text in the preview area, + which is implemented by using QWebEngineView. To render the text, the + Markdown text is converted to HTML format with the help of a JavaScript + library inside the web engine. The preview is updated from the editor + through QWebChannel. + + \include examples-run.qdocinc + + \section1 Exposing Document Text + + Because we expose the current Markdown text to be rendered to the web engine + through QWebChannel, we need to somehow make the current text available + through the Qt metatype system. This is done by using a dedicated + \c Document class that exposes the document text as a \c{Q_PROPERTY}: + + \quotefromfile webenginewidgets/markdowneditor/document.h + \skipto class Document + \printto #endif + + The \c Document class wraps a QString to be set on the C++ side with + the \c setText() method and exposes it at runtime as a \c text property + with a \c textChanged signal. + + We define the \c setText method as follows: + + \quotefromfile webenginewidgets/markdowneditor/document.cpp + \skipto Document::setText + \printuntil + + \section1 Previewing Text + + We implement our own \c PreviewPage class that publicly inherits from + \c QWebEnginePage: + + \quotefromfile webenginewidgets/markdowneditor/previewpage.h + \skipto class PreviewPage + \printto #endif + + We reimplement the virtual \c acceptNavigationRequest method to + stop the page from navigating away from the current document. Instead, + we redirect external links to the system browser: + + \quotefromfile webenginewidgets/markdowneditor/previewpage.cpp + \skipto acceptNavigationRequest + \printuntil + + \section1 Creating the Main Window + + The \c MainWindow class inherits the QMainWindow class: + + \quotefromfile webenginewidgets/markdowneditor/mainwindow.h + \skipto class MainWindow : + \printto endif + + The class declares private slots that match the actions in the menu, + as well as the \c isModified() helper method. + + The actual layout of the main window is specified in a \c .ui file. + The widgets and actions are available at runtime in the \c ui member + variable. + + \c m_filePath holds the file path to the currently loaded document. + \c m_content is an instance of the \c Document class. + + The actual setup of the different objects is done in the \c MainWindow + constructor: + + \quotefromfile webenginewidgets/markdowneditor/mainwindow.cpp + \skipto MainWindow::MainWindow + \printto connect + + The constructor first calls \c setupUi to construct the widgets and menu + actions according to the UI file. It then makes sure our custom + \c PreviewPage is used by the QWebEngineView instance in \c{ui->preview}. + + \printto ui->preview + + Here the \c textChanged signal of the editor is connected to a lambda that + updates the text in \c m_content. This object is then exposed to the JS side + by \c QWebChannel under the name \c{content}. + + \printto connect + + Now we can actually load the \e index.html file from the + resources. For more information about the file, see + \l{Creating an Index File}. + + \printto defaultTextFile + + The menu items are connected to the mapping member slots. The + \uicontrol Save item is activated or deactivated depending on whether + the user has edited the content. + + \printuntil } + + Finally, we load a default document \e default.md from the resources. + + \section1 Creating an Index File + + \quotefile webenginewidgets/markdowneditor/resources/index.html + + In the \e index.html, we load a custom stylesheet and two JavaScript + libraries. \l{http://kevinburke.bitbucket.org/markdowncss/}{markdown.css} is + a markdown-friendly stylesheet created by Kevin Burke. + \l{https://github.com/chjj/marked}{marked.min.js} is a markdown parser and + compiler designed for speed written by Christopher Jeffrey and + \e qwebchannel.js is part of the \l{QWebChannel} module. + + In the \c <body> element we first define a \c placeholder element, and + make it available as a JavaScript variable. We then define the \c updateText + helper method that updates the content of \c placeholder with the HTML + that the JavaScript method \c marked() returns. + + Finally, we set up the web channel to access the \c content proxy object + and make sure that \c updateText() is called whenever \c content.text + changes. +*/ + diff --git a/examples/webenginewidgets/markdowneditor/document.cpp b/examples/webenginewidgets/markdowneditor/document.cpp new file mode 100644 index 000000000..98665680c --- /dev/null +++ b/examples/webenginewidgets/markdowneditor/document.cpp @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** 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 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 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. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company 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 "document.h" + +void Document::setText(const QString &text) +{ + if (text == m_text) + return; + m_text = text; + emit textChanged(m_text); +} diff --git a/examples/webenginewidgets/markdowneditor/document.h b/examples/webenginewidgets/markdowneditor/document.h new file mode 100644 index 000000000..d2f33ec81 --- /dev/null +++ b/examples/webenginewidgets/markdowneditor/document.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** 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 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 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. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company 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 DOCUMENT_H +#define DOCUMENT_H + +#include <QObject> +#include <QString> + +class Document : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString text MEMBER m_text NOTIFY textChanged) +public: + explicit Document(QObject *parent = nullptr) : QObject(parent) {} + + void setText(const QString &text); + +signals: + void textChanged(const QString &text); + +private: + QString m_text; +}; + +#endif // DOCUMENT_H diff --git a/examples/webenginewidgets/markdowneditor/main.cpp b/examples/webenginewidgets/markdowneditor/main.cpp new file mode 100644 index 000000000..2cf4e1f60 --- /dev/null +++ b/examples/webenginewidgets/markdowneditor/main.cpp @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** 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 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 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. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company 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 "document.h" +#include "mainwindow.h" + +#include <QApplication> +#include <QFile> + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + + MainWindow window; + window.show(); + + return a.exec(); +} diff --git a/examples/webenginewidgets/markdowneditor/mainwindow.cpp b/examples/webenginewidgets/markdowneditor/mainwindow.cpp new file mode 100644 index 000000000..fec7c661e --- /dev/null +++ b/examples/webenginewidgets/markdowneditor/mainwindow.cpp @@ -0,0 +1,172 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** 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 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 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. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company 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 "mainwindow.h" +#include "previewpage.h" +#include "ui_mainwindow.h" + +#include <QFile> +#include <QFileDialog> +#include <QMessageBox> +#include <QTextStream> +#include <QWebChannel> + +MainWindow::MainWindow(QWidget *parent) : + QMainWindow(parent), + ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + PreviewPage *page = new PreviewPage(this); + ui->preview->setPage(page); + + connect(ui->editor, &QPlainTextEdit::textChanged, + [this]() { m_content.setText(ui->editor->toPlainText()); }); + + QWebChannel *channel = new QWebChannel(this); + channel->registerObject(QStringLiteral("content"), &m_content); + page->setWebChannel(channel); + + ui->preview->setUrl(QUrl("qrc:/index.html")); + + connect(ui->actionNew, &QAction::triggered, this, &MainWindow::onFileNew); + connect(ui->actionOpen, &QAction::triggered, this, &MainWindow::onFileOpen); + connect(ui->actionSave, &QAction::triggered, this, &MainWindow::onFileSave); + connect(ui->actionSaveAs, &QAction::triggered, this, &MainWindow::onFileSaveAs); + connect(ui->actionExit, &QAction::triggered, this, &MainWindow::onExit); + + connect(ui->editor->document(), &QTextDocument::modificationChanged, + ui->actionSave, &QAction::setEnabled); + + QFile defaultTextFile(":/default.md"); + defaultTextFile.open(QIODevice::ReadOnly); + ui->editor->setPlainText(defaultTextFile.readAll()); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + +bool MainWindow::isModified() const +{ + return ui->editor->document()->isModified(); +} + +void MainWindow::onFileNew() +{ + if (isModified()) { + QMessageBox::StandardButton button = QMessageBox::question(this, windowTitle(), + tr("You have unsaved changes. Do you want to create a new document anyway?")); + if (button != QMessageBox::Yes) + return; + } + + m_filePath.clear(); + ui->editor->setPlainText(tr("## New document")); + ui->editor->document()->setModified(false); +} + +void MainWindow::onFileOpen() +{ + if (isModified()) { + QMessageBox::StandardButton button = QMessageBox::question(this, windowTitle(), + tr("You have unsaved changes. Do you want to open a new document anyway?")); + if (button != QMessageBox::Yes) + return; + } + + QString path = QFileDialog::getOpenFileName(this, + tr("Open MarkDown File"), "", tr("MarkDown File (*.md)")); + if (path.isEmpty()) + return; + + QFile f(path); + if (!f.open(QIODevice::ReadOnly)) { + QMessageBox::warning(this, windowTitle(), + tr("Could not open file %1: %2").arg( + QDir::toNativeSeparators(path), f.errorString())); + return; + } + m_filePath = path; + ui->editor->setPlainText(f.readAll()); +} + +void MainWindow::onFileSave() +{ + if (m_filePath.isEmpty()) { + onFileSaveAs(); + return; + } + + QFile f(m_filePath); + if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) { + QMessageBox::warning(this, windowTitle(), + tr("Could not write to file %1: %2").arg( + QDir::toNativeSeparators(m_filePath), f.errorString())); + return; + } + QTextStream str(&f); + str << ui->editor->toPlainText(); + + ui->editor->document()->setModified(false); +} + +void MainWindow::onFileSaveAs() +{ + QString path = QFileDialog::getSaveFileName(this, + tr("Save MarkDown File"), "", tr("MarkDown File (*.md, *.markdown)")); + if (path.isEmpty()) + return; + m_filePath = path; + onFileSave(); +} + +void MainWindow::onExit() +{ + if (isModified()) { + QMessageBox::StandardButton button = QMessageBox::question(this, windowTitle(), + tr("You have unsaved changes. Do you want to exit anyway?")); + if (button != QMessageBox::Yes) + return; + } + close(); +} diff --git a/examples/webenginewidgets/markdowneditor/mainwindow.h b/examples/webenginewidgets/markdowneditor/mainwindow.h new file mode 100644 index 000000000..6099f1f79 --- /dev/null +++ b/examples/webenginewidgets/markdowneditor/mainwindow.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** 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 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 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. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company 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 MAINWINDOW_H +#define MAINWINDOW_H + +#include "document.h" + +#include <QMainWindow> +#include <QString> + +namespace Ui { +class MainWindow; +} + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(QWidget *parent = 0); + ~MainWindow(); + +private slots: + void onFileNew(); + void onFileOpen(); + void onFileSave(); + void onFileSaveAs(); + void onExit(); + +private: + bool isModified() const; + + Ui::MainWindow *ui; + QString m_filePath; + Document m_content; +}; + +#endif // MAINWINDOW_H diff --git a/examples/webenginewidgets/markdowneditor/mainwindow.ui b/examples/webenginewidgets/markdowneditor/mainwindow.ui new file mode 100644 index 000000000..36ab352b7 --- /dev/null +++ b/examples/webenginewidgets/markdowneditor/mainwindow.ui @@ -0,0 +1,115 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>MainWindow</class> + <widget class="QMainWindow" name="MainWindow"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>800</width> + <height>600</height> + </rect> + </property> + <property name="windowTitle"> + <string>MarkDown Editor</string> + </property> + <widget class="QWidget" name="centralwidget"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QSplitter" name="splitter"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <widget class="QPlainTextEdit" name="editor"/> + <widget class="QWebEngineView" name="preview" native="true"/> + </widget> + </item> + </layout> + </widget> + <widget class="QMenuBar" name="menubar"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>800</width> + <height>26</height> + </rect> + </property> + <widget class="QMenu" name="menu_File"> + <property name="title"> + <string>&File</string> + </property> + <addaction name="actionNew"/> + <addaction name="actionOpen"/> + <addaction name="actionSave"/> + <addaction name="actionSaveAs"/> + <addaction name="separator"/> + <addaction name="actionExit"/> + </widget> + <addaction name="menu_File"/> + </widget> + <widget class="QStatusBar" name="statusbar"/> + <action name="actionOpen"> + <property name="text"> + <string>&Open...</string> + </property> + <property name="toolTip"> + <string>Open document</string> + </property> + <property name="shortcut"> + <string>Ctrl+O</string> + </property> + </action> + <action name="actionSave"> + <property name="text"> + <string>&Save</string> + </property> + <property name="toolTip"> + <string>Save current document</string> + </property> + <property name="shortcut"> + <string>Ctrl+S</string> + </property> + </action> + <action name="actionExit"> + <property name="text"> + <string>E&xit</string> + </property> + <property name="toolTip"> + <string>Exit editor</string> + </property> + <property name="shortcut"> + <string>Ctrl+Q</string> + </property> + </action> + <action name="actionSaveAs"> + <property name="text"> + <string>Save &As...</string> + </property> + <property name="toolTip"> + <string>Save document under different name</string> + </property> + </action> + <action name="actionNew"> + <property name="text"> + <string>&New</string> + </property> + <property name="toolTip"> + <string>Create new document</string> + </property> + <property name="shortcut"> + <string>Ctrl+N</string> + </property> + </action> + </widget> + <customwidgets> + <customwidget> + <class>QWebEngineView</class> + <extends>QWidget</extends> + <header>qwebengineview.h</header> + <container>1</container> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> diff --git a/examples/webenginewidgets/markdowneditor/markdowneditor.pro b/examples/webenginewidgets/markdowneditor/markdowneditor.pro new file mode 100644 index 000000000..bfd16698a --- /dev/null +++ b/examples/webenginewidgets/markdowneditor/markdowneditor.pro @@ -0,0 +1,28 @@ +TEMPLATE = app + +QT += webenginewidgets webchannel +CONFIG += c++11 + +HEADERS += \ + mainwindow.h \ + previewpage.h \ + document.h + +SOURCES = \ + main.cpp \ + mainwindow.cpp \ + previewpage.cpp \ + document.cpp + +RESOURCES = \ + resources/markdowneditor.qrc + +FORMS += \ + mainwindow.ui + +DISTFILES += \ + 3RDPARTY.md + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/webenginewidgets/markdowneditor +INSTALLS += target diff --git a/examples/webenginewidgets/markdowneditor/previewpage.cpp b/examples/webenginewidgets/markdowneditor/previewpage.cpp new file mode 100644 index 000000000..2d4322664 --- /dev/null +++ b/examples/webenginewidgets/markdowneditor/previewpage.cpp @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** 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 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 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. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company 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 "previewpage.h" + +#include <QDesktopServices> + +bool PreviewPage::acceptNavigationRequest(const QUrl &url, + QWebEnginePage::NavigationType /*type*/, + bool /*isMainFrame*/) +{ + // Only allow qrc:/index.html. + if (url.scheme() == QString("qrc")) + return true; + QDesktopServices::openUrl(url); + return false; +} diff --git a/examples/webenginewidgets/markdowneditor/previewpage.h b/examples/webenginewidgets/markdowneditor/previewpage.h new file mode 100644 index 000000000..53b0e23e6 --- /dev/null +++ b/examples/webenginewidgets/markdowneditor/previewpage.h @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** 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 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 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. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company 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 PREVIEWPAGE_H +#define PREVIEWPAGE_H + +#include <QWebEnginePage> + +class PreviewPage : public QWebEnginePage +{ + Q_OBJECT +public: + explicit PreviewPage(QObject *parent = nullptr) : QWebEnginePage(parent) {} + +protected: + bool acceptNavigationRequest(const QUrl &url, NavigationType type, bool isMainFrame); +}; + +#endif // PREVIEWPAGE_H diff --git a/examples/webenginewidgets/markdowneditor/resources/default.md b/examples/webenginewidgets/markdowneditor/resources/default.md new file mode 100644 index 000000000..e5905b101 --- /dev/null +++ b/examples/webenginewidgets/markdowneditor/resources/default.md @@ -0,0 +1,12 @@ +## Markdown Editor Example + +This example uses [QWebEngineView](http://doc.qt.io/qt-5/qwebengineview.html) +to preview text written using the [Markdown](https://en.wikipedia.org/wiki/Markdown) +syntax. + +### Acknowledgments + +The conversion from Markdown to HTML is done with the help of the +[marked JavaScript library](https://github.com/chjj/marked) by _Christopher Jeffrey_. +The [style sheet](http://kevinburke.bitbucket.org/markdowncss/markdown.css) +was created by _Kevin Burke_. diff --git a/examples/webenginewidgets/markdowneditor/resources/index.html b/examples/webenginewidgets/markdowneditor/resources/index.html new file mode 100644 index 000000000..2f45479ed --- /dev/null +++ b/examples/webenginewidgets/markdowneditor/resources/index.html @@ -0,0 +1,32 @@ +<!doctype html> +<html lang="en"> +<meta charset="utf-8"> +<head> + <link rel="stylesheet" type="text/css" href="markdown.css"> + <script src="marked.min.js"></script> + <script src="qwebchannel.js"></script> +</head> +<body> + <div id="placeholder"></div> + <script> + 'use strict'; + + var placeholder = document.getElementById('placeholder'); + + var updateText = function(text) { + placeholder.innerHTML = marked(text); + } + + new QWebChannel(qt.webChannelTransport, + function(channel) { + var content = channel.objects.content; + updateText(content.text); + content.textChanged.connect(updateText); + } + ); + </script> +</body> +</html> + + + diff --git a/examples/webenginewidgets/markdowneditor/resources/markdown.css b/examples/webenginewidgets/markdowneditor/resources/markdown.css new file mode 100644 index 000000000..24fc2ffe2 --- /dev/null +++ b/examples/webenginewidgets/markdowneditor/resources/markdown.css @@ -0,0 +1,260 @@ +body{ + margin: 0 auto; + font-family: Georgia, Palatino, serif; + color: #444444; + line-height: 1; + max-width: 960px; + padding: 30px; +} +h1, h2, h3, h4 { + color: #111111; + font-weight: 400; +} +h1, h2, h3, h4, h5, p { + margin-bottom: 24px; + padding: 0; +} +h1 { + font-size: 48px; +} +h2 { + font-size: 36px; + /* The bottom margin is small. It's designed to be used with gray meta text + * below a post title. */ + margin: 24px 0 6px; +} +h3 { + font-size: 24px; +} +h4 { + font-size: 21px; +} +h5 { + font-size: 18px; +} +a { + color: #0099ff; + margin: 0; + padding: 0; + vertical-align: baseline; +} +a:hover { + text-decoration: none; + color: #ff6600; +} +a:visited { + color: purple; +} +ul, ol { + padding: 0; + margin: 0; +} +li { + line-height: 24px; +} +li ul, li ul { + margin-left: 24px; +} +p, ul, ol { + font-size: 16px; + line-height: 24px; + max-width: 540px; +} +pre { + padding: 0px 24px; + max-width: 800px; + white-space: pre-wrap; +} +code { + font-family: Consolas, Monaco, Andale Mono, monospace; + line-height: 1.5; + font-size: 13px; +} +aside { + display: block; + float: right; + width: 390px; +} +blockquote { + border-left:.5em solid #eee; + padding: 0 2em; + margin-left:0; + max-width: 476px; +} +blockquote cite { + font-size:14px; + line-height:20px; + color:#bfbfbf; +} +blockquote cite:before { + content: '\2014 \00A0'; +} + +blockquote p { + color: #666; + max-width: 460px; +} +hr { + width: 540px; + text-align: left; + margin: 0 auto 0 0; + color: #999; +} + +/* Code below this line is copyright Twitter Inc. */ + +button, +input, +select, +textarea { + font-size: 100%; + margin: 0; + vertical-align: baseline; + *vertical-align: middle; +} +button, input { + line-height: normal; + *overflow: visible; +} +button::-moz-focus-inner, input::-moz-focus-inner { + border: 0; + padding: 0; +} +button, +input[type="button"], +input[type="reset"], +input[type="submit"] { + cursor: pointer; + -webkit-appearance: button; +} +input[type=checkbox], input[type=radio] { + cursor: pointer; +} +/* override default chrome & firefox settings */ +input:not([type="image"]), textarea { + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; +} + +input[type="search"] { + -webkit-appearance: textfield; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; +} +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} +label, +input, +select, +textarea { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 13px; + font-weight: normal; + line-height: normal; + margin-bottom: 18px; +} +input[type=checkbox], input[type=radio] { + cursor: pointer; + margin-bottom: 0; +} +input[type=text], +input[type=password], +textarea, +select { + display: inline-block; + width: 210px; + padding: 4px; + font-size: 13px; + font-weight: normal; + line-height: 18px; + height: 18px; + color: #808080; + border: 1px solid #ccc; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} +select, input[type=file] { + height: 27px; + line-height: 27px; +} +textarea { + height: auto; +} + +/* grey out placeholders */ +:-moz-placeholder { + color: #bfbfbf; +} +::-webkit-input-placeholder { + color: #bfbfbf; +} + +input[type=text], +input[type=password], +select, +textarea { + -webkit-transition: border linear 0.2s, box-shadow linear 0.2s; + -moz-transition: border linear 0.2s, box-shadow linear 0.2s; + transition: border linear 0.2s, box-shadow linear 0.2s; + -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1); + -moz-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1); + box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1); +} +input[type=text]:focus, input[type=password]:focus, textarea:focus { + outline: none; + border-color: rgba(82, 168, 236, 0.8); + -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6); + -moz-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6); + box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6); +} + +/* buttons */ +button { + display: inline-block; + padding: 4px 14px; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 13px; + line-height: 18px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + background-color: #0064cd; + background-repeat: repeat-x; + background-image: -khtml-gradient(linear, left top, left bottom, from(#049cdb), to(#0064cd)); + background-image: -moz-linear-gradient(top, #049cdb, #0064cd); + background-image: -ms-linear-gradient(top, #049cdb, #0064cd); + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #049cdb), color-stop(100%, #0064cd)); + background-image: -webkit-linear-gradient(top, #049cdb, #0064cd); + background-image: -o-linear-gradient(top, #049cdb, #0064cd); + background-image: linear-gradient(top, #049cdb, #0064cd); + color: #fff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + border: 1px solid #004b9a; + border-bottom-color: #003f81; + -webkit-transition: 0.1s linear all; + -moz-transition: 0.1s linear all; + transition: 0.1s linear all; + border-color: #0064cd #0064cd #003f81; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); +} +button:hover { + color: #fff; + background-position: 0 -15px; + text-decoration: none; +} +button:active { + -webkit-box-shadow: inset 0 3px 7px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 3px 7px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 3px 7px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); +} +button::-moz-focus-inner { + padding: 0; + border: 0; +} diff --git a/examples/webenginewidgets/markdowneditor/resources/markdowneditor.qrc b/examples/webenginewidgets/markdowneditor/resources/markdowneditor.qrc new file mode 100644 index 000000000..8b7471a9c --- /dev/null +++ b/examples/webenginewidgets/markdowneditor/resources/markdowneditor.qrc @@ -0,0 +1,9 @@ +<RCC> + <qresource prefix="/"> + <file>index.html</file> + <file>qwebchannel.js</file> + <file>marked.min.js</file> + <file>default.md</file> + <file>markdown.css</file> + </qresource> +</RCC> diff --git a/examples/webenginewidgets/markdowneditor/resources/marked.min.js b/examples/webenginewidgets/markdowneditor/resources/marked.min.js new file mode 100644 index 000000000..f3542fff0 --- /dev/null +++ b/examples/webenginewidgets/markdowneditor/resources/marked.min.js @@ -0,0 +1,6 @@ +/** + * marked - a markdown parser + * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed) + * https://github.com/chjj/marked + */ +(function(){var block={newline:/^\n+/,code:/^( {4}[^\n]+\n*)+/,fences:noop,hr:/^( *[-*_]){3,} *(?:\n+|$)/,heading:/^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,nptable:noop,lheading:/^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,blockquote:/^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,list:/^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,html:/^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,def:/^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,table:noop,paragraph:/^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,text:/^[^\n]+/};block.bullet=/(?:[*+-]|\d+\.)/;block.item=/^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;block.item=replace(block.item,"gm")(/bull/g,block.bullet)();block.list=replace(block.list)(/bull/g,block.bullet)("hr","\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))")("def","\\n+(?="+block.def.source+")")();block.blockquote=replace(block.blockquote)("def",block.def)();block._tag="(?!(?:"+"a|em|strong|small|s|cite|q|dfn|abbr|data|time|code"+"|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo"+"|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b";block.html=replace(block.html)("comment",/<!--[\s\S]*?-->/)("closed",/<(tag)[\s\S]+?<\/\1>/)("closing",/<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)(/tag/g,block._tag)();block.paragraph=replace(block.paragraph)("hr",block.hr)("heading",block.heading)("lheading",block.lheading)("blockquote",block.blockquote)("tag","<"+block._tag)("def",block.def)();block.normal=merge({},block);block.gfm=merge({},block.normal,{fences:/^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,paragraph:/^/,heading:/^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/});block.gfm.paragraph=replace(block.paragraph)("(?!","(?!"+block.gfm.fences.source.replace("\\1","\\2")+"|"+block.list.source.replace("\\1","\\3")+"|")();block.tables=merge({},block.gfm,{nptable:/^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,table:/^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/});function Lexer(options){this.tokens=[];this.tokens.links={};this.options=options||marked.defaults;this.rules=block.normal;if(this.options.gfm){if(this.options.tables){this.rules=block.tables}else{this.rules=block.gfm}}}Lexer.rules=block;Lexer.lex=function(src,options){var lexer=new Lexer(options);return lexer.lex(src)};Lexer.prototype.lex=function(src){src=src.replace(/\r\n|\r/g,"\n").replace(/\t/g," ").replace(/\u00a0/g," ").replace(/\u2424/g,"\n");return this.token(src,true)};Lexer.prototype.token=function(src,top,bq){var src=src.replace(/^ +$/gm,""),next,loose,cap,bull,b,item,space,i,l;while(src){if(cap=this.rules.newline.exec(src)){src=src.substring(cap[0].length);if(cap[0].length>1){this.tokens.push({type:"space"})}}if(cap=this.rules.code.exec(src)){src=src.substring(cap[0].length);cap=cap[0].replace(/^ {4}/gm,"");this.tokens.push({type:"code",text:!this.options.pedantic?cap.replace(/\n+$/,""):cap});continue}if(cap=this.rules.fences.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"code",lang:cap[2],text:cap[3]||""});continue}if(cap=this.rules.heading.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"heading",depth:cap[1].length,text:cap[2]});continue}if(top&&(cap=this.rules.nptable.exec(src))){src=src.substring(cap[0].length);item={type:"table",header:cap[1].replace(/^ *| *\| *$/g,"").split(/ *\| */),align:cap[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:cap[3].replace(/\n$/,"").split("\n")};for(i=0;i<item.align.length;i++){if(/^ *-+: *$/.test(item.align[i])){item.align[i]="right"}else if(/^ *:-+: *$/.test(item.align[i])){item.align[i]="center"}else if(/^ *:-+ *$/.test(item.align[i])){item.align[i]="left"}else{item.align[i]=null}}for(i=0;i<item.cells.length;i++){item.cells[i]=item.cells[i].split(/ *\| */)}this.tokens.push(item);continue}if(cap=this.rules.lheading.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"heading",depth:cap[2]==="="?1:2,text:cap[1]});continue}if(cap=this.rules.hr.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"hr"});continue}if(cap=this.rules.blockquote.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"blockquote_start"});cap=cap[0].replace(/^ *> ?/gm,"");this.token(cap,top,true);this.tokens.push({type:"blockquote_end"});continue}if(cap=this.rules.list.exec(src)){src=src.substring(cap[0].length);bull=cap[2];this.tokens.push({type:"list_start",ordered:bull.length>1});cap=cap[0].match(this.rules.item);next=false;l=cap.length;i=0;for(;i<l;i++){item=cap[i];space=item.length;item=item.replace(/^ *([*+-]|\d+\.) +/,"");if(~item.indexOf("\n ")){space-=item.length;item=!this.options.pedantic?item.replace(new RegExp("^ {1,"+space+"}","gm"),""):item.replace(/^ {1,4}/gm,"")}if(this.options.smartLists&&i!==l-1){b=block.bullet.exec(cap[i+1])[0];if(bull!==b&&!(bull.length>1&&b.length>1)){src=cap.slice(i+1).join("\n")+src;i=l-1}}loose=next||/\n\n(?!\s*$)/.test(item);if(i!==l-1){next=item.charAt(item.length-1)==="\n";if(!loose)loose=next}this.tokens.push({type:loose?"loose_item_start":"list_item_start"});this.token(item,false,bq);this.tokens.push({type:"list_item_end"})}this.tokens.push({type:"list_end"});continue}if(cap=this.rules.html.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:this.options.sanitize?"paragraph":"html",pre:!this.options.sanitizer&&(cap[1]==="pre"||cap[1]==="script"||cap[1]==="style"),text:cap[0]});continue}if(!bq&&top&&(cap=this.rules.def.exec(src))){src=src.substring(cap[0].length);this.tokens.links[cap[1].toLowerCase()]={href:cap[2],title:cap[3]};continue}if(top&&(cap=this.rules.table.exec(src))){src=src.substring(cap[0].length);item={type:"table",header:cap[1].replace(/^ *| *\| *$/g,"").split(/ *\| */),align:cap[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:cap[3].replace(/(?: *\| *)?\n$/,"").split("\n")};for(i=0;i<item.align.length;i++){if(/^ *-+: *$/.test(item.align[i])){item.align[i]="right"}else if(/^ *:-+: *$/.test(item.align[i])){item.align[i]="center"}else if(/^ *:-+ *$/.test(item.align[i])){item.align[i]="left"}else{item.align[i]=null}}for(i=0;i<item.cells.length;i++){item.cells[i]=item.cells[i].replace(/^ *\| *| *\| *$/g,"").split(/ *\| */)}this.tokens.push(item);continue}if(top&&(cap=this.rules.paragraph.exec(src))){src=src.substring(cap[0].length);this.tokens.push({type:"paragraph",text:cap[1].charAt(cap[1].length-1)==="\n"?cap[1].slice(0,-1):cap[1]});continue}if(cap=this.rules.text.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"text",text:cap[0]});continue}if(src){throw new Error("Infinite loop on byte: "+src.charCodeAt(0))}}return this.tokens};var inline={escape:/^\\([\\`*{}\[\]()#+\-.!_>])/,autolink:/^<([^ >]+(@|:\/)[^ >]+)>/,url:noop,tag:/^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,link:/^!?\[(inside)\]\(href\)/,reflink:/^!?\[(inside)\]\s*\[([^\]]*)\]/,nolink:/^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,strong:/^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,em:/^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,code:/^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,br:/^ {2,}\n(?!\s*$)/,del:noop,text:/^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/};inline._inside=/(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;inline._href=/\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;inline.link=replace(inline.link)("inside",inline._inside)("href",inline._href)();inline.reflink=replace(inline.reflink)("inside",inline._inside)();inline.normal=merge({},inline);inline.pedantic=merge({},inline.normal,{strong:/^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,em:/^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/});inline.gfm=merge({},inline.normal,{escape:replace(inline.escape)("])","~|])")(),url:/^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,del:/^~~(?=\S)([\s\S]*?\S)~~/,text:replace(inline.text)("]|","~]|")("|","|https?://|")()});inline.breaks=merge({},inline.gfm,{br:replace(inline.br)("{2,}","*")(),text:replace(inline.gfm.text)("{2,}","*")()});function InlineLexer(links,options){this.options=options||marked.defaults;this.links=links;this.rules=inline.normal;this.renderer=this.options.renderer||new Renderer;this.renderer.options=this.options;if(!this.links){throw new Error("Tokens array requires a `links` property.")}if(this.options.gfm){if(this.options.breaks){this.rules=inline.breaks}else{this.rules=inline.gfm}}else if(this.options.pedantic){this.rules=inline.pedantic}}InlineLexer.rules=inline;InlineLexer.output=function(src,links,options){var inline=new InlineLexer(links,options);return inline.output(src)};InlineLexer.prototype.output=function(src){var out="",link,text,href,cap;while(src){if(cap=this.rules.escape.exec(src)){src=src.substring(cap[0].length);out+=cap[1];continue}if(cap=this.rules.autolink.exec(src)){src=src.substring(cap[0].length);if(cap[2]==="@"){text=cap[1].charAt(6)===":"?this.mangle(cap[1].substring(7)):this.mangle(cap[1]);href=this.mangle("mailto:")+text}else{text=escape(cap[1]);href=text}out+=this.renderer.link(href,null,text);continue}if(!this.inLink&&(cap=this.rules.url.exec(src))){src=src.substring(cap[0].length);text=escape(cap[1]);href=text;out+=this.renderer.link(href,null,text);continue}if(cap=this.rules.tag.exec(src)){if(!this.inLink&&/^<a /i.test(cap[0])){this.inLink=true}else if(this.inLink&&/^<\/a>/i.test(cap[0])){this.inLink=false}src=src.substring(cap[0].length);out+=this.options.sanitize?this.options.sanitizer?this.options.sanitizer(cap[0]):escape(cap[0]):cap[0];continue}if(cap=this.rules.link.exec(src)){src=src.substring(cap[0].length);this.inLink=true;out+=this.outputLink(cap,{href:cap[2],title:cap[3]});this.inLink=false;continue}if((cap=this.rules.reflink.exec(src))||(cap=this.rules.nolink.exec(src))){src=src.substring(cap[0].length);link=(cap[2]||cap[1]).replace(/\s+/g," ");link=this.links[link.toLowerCase()];if(!link||!link.href){out+=cap[0].charAt(0);src=cap[0].substring(1)+src;continue}this.inLink=true;out+=this.outputLink(cap,link);this.inLink=false;continue}if(cap=this.rules.strong.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.strong(this.output(cap[2]||cap[1]));continue}if(cap=this.rules.em.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.em(this.output(cap[2]||cap[1]));continue}if(cap=this.rules.code.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.codespan(escape(cap[2],true));continue}if(cap=this.rules.br.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.br();continue}if(cap=this.rules.del.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.del(this.output(cap[1]));continue}if(cap=this.rules.text.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.text(escape(this.smartypants(cap[0])));continue}if(src){throw new Error("Infinite loop on byte: "+src.charCodeAt(0))}}return out};InlineLexer.prototype.outputLink=function(cap,link){var href=escape(link.href),title=link.title?escape(link.title):null;return cap[0].charAt(0)!=="!"?this.renderer.link(href,title,this.output(cap[1])):this.renderer.image(href,title,escape(cap[1]))};InlineLexer.prototype.smartypants=function(text){if(!this.options.smartypants)return text;return text.replace(/---/g,"—").replace(/--/g,"–").replace(/(^|[-\u2014/(\[{"\s])'/g,"$1‘").replace(/'/g,"’").replace(/(^|[-\u2014/(\[{\u2018\s])"/g,"$1“").replace(/"/g,"”").replace(/\.{3}/g,"…")};InlineLexer.prototype.mangle=function(text){if(!this.options.mangle)return text;var out="",l=text.length,i=0,ch;for(;i<l;i++){ch=text.charCodeAt(i);if(Math.random()>.5){ch="x"+ch.toString(16)}out+="&#"+ch+";"}return out};function Renderer(options){this.options=options||{}}Renderer.prototype.code=function(code,lang,escaped){if(this.options.highlight){var out=this.options.highlight(code,lang);if(out!=null&&out!==code){escaped=true;code=out}}if(!lang){return"<pre><code>"+(escaped?code:escape(code,true))+"\n</code></pre>"}return'<pre><code class="'+this.options.langPrefix+escape(lang,true)+'">'+(escaped?code:escape(code,true))+"\n</code></pre>\n"};Renderer.prototype.blockquote=function(quote){return"<blockquote>\n"+quote+"</blockquote>\n"};Renderer.prototype.html=function(html){return html};Renderer.prototype.heading=function(text,level,raw){return"<h"+level+' id="'+this.options.headerPrefix+raw.toLowerCase().replace(/[^\w]+/g,"-")+'">'+text+"</h"+level+">\n"};Renderer.prototype.hr=function(){return this.options.xhtml?"<hr/>\n":"<hr>\n"};Renderer.prototype.list=function(body,ordered){var type=ordered?"ol":"ul";return"<"+type+">\n"+body+"</"+type+">\n"};Renderer.prototype.listitem=function(text){return"<li>"+text+"</li>\n"};Renderer.prototype.paragraph=function(text){return"<p>"+text+"</p>\n"};Renderer.prototype.table=function(header,body){return"<table>\n"+"<thead>\n"+header+"</thead>\n"+"<tbody>\n"+body+"</tbody>\n"+"</table>\n"};Renderer.prototype.tablerow=function(content){return"<tr>\n"+content+"</tr>\n"};Renderer.prototype.tablecell=function(content,flags){var type=flags.header?"th":"td";var tag=flags.align?"<"+type+' style="text-align:'+flags.align+'">':"<"+type+">";return tag+content+"</"+type+">\n"};Renderer.prototype.strong=function(text){return"<strong>"+text+"</strong>"};Renderer.prototype.em=function(text){return"<em>"+text+"</em>"};Renderer.prototype.codespan=function(text){return"<code>"+text+"</code>"};Renderer.prototype.br=function(){return this.options.xhtml?"<br/>":"<br>"};Renderer.prototype.del=function(text){return"<del>"+text+"</del>"};Renderer.prototype.link=function(href,title,text){if(this.options.sanitize){try{var prot=decodeURIComponent(unescape(href)).replace(/[^\w:]/g,"").toLowerCase()}catch(e){return""}if(prot.indexOf("javascript:")===0||prot.indexOf("vbscript:")===0){return""}}var out='<a href="'+href+'"';if(title){out+=' title="'+title+'"'}out+=">"+text+"</a>";return out};Renderer.prototype.image=function(href,title,text){var out='<img src="'+href+'" alt="'+text+'"';if(title){out+=' title="'+title+'"'}out+=this.options.xhtml?"/>":">";return out};Renderer.prototype.text=function(text){return text};function Parser(options){this.tokens=[];this.token=null;this.options=options||marked.defaults;this.options.renderer=this.options.renderer||new Renderer;this.renderer=this.options.renderer;this.renderer.options=this.options}Parser.parse=function(src,options,renderer){var parser=new Parser(options,renderer);return parser.parse(src)};Parser.prototype.parse=function(src){this.inline=new InlineLexer(src.links,this.options,this.renderer);this.tokens=src.reverse();var out="";while(this.next()){out+=this.tok()}return out};Parser.prototype.next=function(){return this.token=this.tokens.pop()};Parser.prototype.peek=function(){return this.tokens[this.tokens.length-1]||0};Parser.prototype.parseText=function(){var body=this.token.text;while(this.peek().type==="text"){body+="\n"+this.next().text}return this.inline.output(body)};Parser.prototype.tok=function(){switch(this.token.type){case"space":{return""}case"hr":{return this.renderer.hr()}case"heading":{return this.renderer.heading(this.inline.output(this.token.text),this.token.depth,this.token.text)}case"code":{return this.renderer.code(this.token.text,this.token.lang,this.token.escaped)}case"table":{var header="",body="",i,row,cell,flags,j;cell="";for(i=0;i<this.token.header.length;i++){flags={header:true,align:this.token.align[i]};cell+=this.renderer.tablecell(this.inline.output(this.token.header[i]),{header:true,align:this.token.align[i]})}header+=this.renderer.tablerow(cell);for(i=0;i<this.token.cells.length;i++){row=this.token.cells[i];cell="";for(j=0;j<row.length;j++){cell+=this.renderer.tablecell(this.inline.output(row[j]),{header:false,align:this.token.align[j]})}body+=this.renderer.tablerow(cell)}return this.renderer.table(header,body)}case"blockquote_start":{var body="";while(this.next().type!=="blockquote_end"){body+=this.tok()}return this.renderer.blockquote(body)}case"list_start":{var body="",ordered=this.token.ordered;while(this.next().type!=="list_end"){body+=this.tok()}return this.renderer.list(body,ordered)}case"list_item_start":{var body="";while(this.next().type!=="list_item_end"){body+=this.token.type==="text"?this.parseText():this.tok()}return this.renderer.listitem(body)}case"loose_item_start":{var body="";while(this.next().type!=="list_item_end"){body+=this.tok()}return this.renderer.listitem(body)}case"html":{var html=!this.token.pre&&!this.options.pedantic?this.inline.output(this.token.text):this.token.text;return this.renderer.html(html)}case"paragraph":{return this.renderer.paragraph(this.inline.output(this.token.text))}case"text":{return this.renderer.paragraph(this.parseText())}}};function escape(html,encode){return html.replace(!encode?/&(?!#?\w+;)/g:/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}function unescape(html){return html.replace(/&([#\w]+);/g,function(_,n){n=n.toLowerCase();if(n==="colon")return":";if(n.charAt(0)==="#"){return n.charAt(1)==="x"?String.fromCharCode(parseInt(n.substring(2),16)):String.fromCharCode(+n.substring(1))}return""})}function replace(regex,opt){regex=regex.source;opt=opt||"";return function self(name,val){if(!name)return new RegExp(regex,opt);val=val.source||val;val=val.replace(/(^|[^\[])\^/g,"$1");regex=regex.replace(name,val);return self}}function noop(){}noop.exec=noop;function merge(obj){var i=1,target,key;for(;i<arguments.length;i++){target=arguments[i];for(key in target){if(Object.prototype.hasOwnProperty.call(target,key)){obj[key]=target[key]}}}return obj}function marked(src,opt,callback){if(callback||typeof opt==="function"){if(!callback){callback=opt;opt=null}opt=merge({},marked.defaults,opt||{});var highlight=opt.highlight,tokens,pending,i=0;try{tokens=Lexer.lex(src,opt)}catch(e){return callback(e)}pending=tokens.length;var done=function(err){if(err){opt.highlight=highlight;return callback(err)}var out;try{out=Parser.parse(tokens,opt)}catch(e){err=e}opt.highlight=highlight;return err?callback(err):callback(null,out)};if(!highlight||highlight.length<3){return done()}delete opt.highlight;if(!pending)return done();for(;i<tokens.length;i++){(function(token){if(token.type!=="code"){return--pending||done()}return highlight(token.text,token.lang,function(err,code){if(err)return done(err);if(code==null||code===token.text){return--pending||done()}token.text=code;token.escaped=true;--pending||done()})})(tokens[i])}return}try{if(opt)opt=merge({},marked.defaults,opt);return Parser.parse(Lexer.lex(src,opt),opt)}catch(e){e.message+="\nPlease report this to https://github.com/chjj/marked.";if((opt||marked.defaults).silent){return"<p>An error occurred:</p><pre>"+escape(e.message+"",true)+"</pre>"}throw e}}marked.options=marked.setOptions=function(opt){merge(marked.defaults,opt);return marked};marked.defaults={gfm:true,tables:true,breaks:false,pedantic:false,sanitize:false,sanitizer:null,mangle:true,smartLists:false,silent:false,highlight:null,langPrefix:"lang-",smartypants:false,headerPrefix:"",renderer:new Renderer,xhtml:false};marked.Parser=Parser;marked.parser=Parser.parse;marked.Renderer=Renderer;marked.Lexer=Lexer;marked.lexer=Lexer.lex;marked.InlineLexer=InlineLexer;marked.inlineLexer=InlineLexer.output;marked.parse=marked;if(typeof module!=="undefined"&&typeof exports==="object"){module.exports=marked}else if(typeof define==="function"&&define.amd){define(function(){return marked})}else{this.marked=marked}}).call(function(){return this||(typeof window!=="undefined"?window:global)}());
\ No newline at end of file diff --git a/examples/webenginewidgets/markdowneditor/resources/qwebchannel.js b/examples/webenginewidgets/markdowneditor/resources/qwebchannel.js new file mode 100644 index 000000000..d8c28bc66 --- /dev/null +++ b/examples/webenginewidgets/markdowneditor/resources/qwebchannel.js @@ -0,0 +1,413 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Copyright (C) 2014 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff <milian.wolff@kdab.com> +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtWebChannel module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +"use strict"; + +var QWebChannelMessageTypes = { + signal: 1, + propertyUpdate: 2, + init: 3, + idle: 4, + debug: 5, + invokeMethod: 6, + connectToSignal: 7, + disconnectFromSignal: 8, + setProperty: 9, + response: 10, +}; + +var QWebChannel = function(transport, initCallback) +{ + if (typeof transport !== "object" || typeof transport.send !== "function") { + console.error("The QWebChannel expects a transport object with a send function and onmessage callback property." + + " Given is: transport: " + typeof(transport) + ", transport.send: " + typeof(transport.send)); + return; + } + + var channel = this; + this.transport = transport; + + this.send = function(data) + { + if (typeof(data) !== "string") { + data = JSON.stringify(data); + } + channel.transport.send(data); + } + + this.transport.onmessage = function(message) + { + var data = message.data; + if (typeof data === "string") { + data = JSON.parse(data); + } + switch (data.type) { + case QWebChannelMessageTypes.signal: + channel.handleSignal(data); + break; + case QWebChannelMessageTypes.response: + channel.handleResponse(data); + break; + case QWebChannelMessageTypes.propertyUpdate: + channel.handlePropertyUpdate(data); + break; + default: + console.error("invalid message received:", message.data); + break; + } + } + + this.execCallbacks = {}; + this.execId = 0; + this.exec = function(data, callback) + { + if (!callback) { + // if no callback is given, send directly + channel.send(data); + return; + } + if (channel.execId === Number.MAX_VALUE) { + // wrap + channel.execId = Number.MIN_VALUE; + } + if (data.hasOwnProperty("id")) { + console.error("Cannot exec message with property id: " + JSON.stringify(data)); + return; + } + data.id = channel.execId++; + channel.execCallbacks[data.id] = callback; + channel.send(data); + }; + + this.objects = {}; + + this.handleSignal = function(message) + { + var object = channel.objects[message.object]; + if (object) { + object.signalEmitted(message.signal, message.args); + } else { + console.warn("Unhandled signal: " + message.object + "::" + message.signal); + } + } + + this.handleResponse = function(message) + { + if (!message.hasOwnProperty("id")) { + console.error("Invalid response message received: ", JSON.stringify(message)); + return; + } + channel.execCallbacks[message.id](message.data); + delete channel.execCallbacks[message.id]; + } + + this.handlePropertyUpdate = function(message) + { + for (var i in message.data) { + var data = message.data[i]; + var object = channel.objects[data.object]; + if (object) { + object.propertyUpdate(data.signals, data.properties); + } else { + console.warn("Unhandled property update: " + data.object + "::" + data.signal); + } + } + channel.exec({type: QWebChannelMessageTypes.idle}); + } + + this.debug = function(message) + { + channel.send({type: QWebChannelMessageTypes.debug, data: message}); + }; + + channel.exec({type: QWebChannelMessageTypes.init}, function(data) { + for (var objectName in data) { + var object = new QObject(objectName, data[objectName], channel); + } + // now unwrap properties, which might reference other registered objects + for (var objectName in channel.objects) { + channel.objects[objectName].unwrapProperties(); + } + if (initCallback) { + initCallback(channel); + } + channel.exec({type: QWebChannelMessageTypes.idle}); + }); +}; + +function QObject(name, data, webChannel) +{ + this.__id__ = name; + webChannel.objects[name] = this; + + // List of callbacks that get invoked upon signal emission + this.__objectSignals__ = {}; + + // Cache of all properties, updated when a notify signal is emitted + this.__propertyCache__ = {}; + + var object = this; + + // ---------------------------------------------------------------------- + + this.unwrapQObject = function(response) + { + if (response instanceof Array) { + // support list of objects + var ret = new Array(response.length); + for (var i = 0; i < response.length; ++i) { + ret[i] = object.unwrapQObject(response[i]); + } + return ret; + } + if (!response + || !response["__QObject*__"] + || response.id === undefined) { + return response; + } + + var objectId = response.id; + if (webChannel.objects[objectId]) + return webChannel.objects[objectId]; + + if (!response.data) { + console.error("Cannot unwrap unknown QObject " + objectId + " without data."); + return; + } + + var qObject = new QObject( objectId, response.data, webChannel ); + qObject.destroyed.connect(function() { + if (webChannel.objects[objectId] === qObject) { + delete webChannel.objects[objectId]; + // reset the now deleted QObject to an empty {} object + // just assigning {} though would not have the desired effect, but the + // below also ensures all external references will see the empty map + // NOTE: this detour is necessary to workaround QTBUG-40021 + var propertyNames = []; + for (var propertyName in qObject) { + propertyNames.push(propertyName); + } + for (var idx in propertyNames) { + delete qObject[propertyNames[idx]]; + } + } + }); + // here we are already initialized, and thus must directly unwrap the properties + qObject.unwrapProperties(); + return qObject; + } + + this.unwrapProperties = function() + { + for (var propertyIdx in object.__propertyCache__) { + object.__propertyCache__[propertyIdx] = object.unwrapQObject(object.__propertyCache__[propertyIdx]); + } + } + + function addSignal(signalData, isPropertyNotifySignal) + { + var signalName = signalData[0]; + var signalIndex = signalData[1]; + object[signalName] = { + connect: function(callback) { + if (typeof(callback) !== "function") { + console.error("Bad callback given to connect to signal " + signalName); + return; + } + + object.__objectSignals__[signalIndex] = object.__objectSignals__[signalIndex] || []; + object.__objectSignals__[signalIndex].push(callback); + + if (!isPropertyNotifySignal && signalName !== "destroyed") { + // only required for "pure" signals, handled separately for properties in propertyUpdate + // also note that we always get notified about the destroyed signal + webChannel.exec({ + type: QWebChannelMessageTypes.connectToSignal, + object: object.__id__, + signal: signalIndex + }); + } + }, + disconnect: function(callback) { + if (typeof(callback) !== "function") { + console.error("Bad callback given to disconnect from signal " + signalName); + return; + } + object.__objectSignals__[signalIndex] = object.__objectSignals__[signalIndex] || []; + var idx = object.__objectSignals__[signalIndex].indexOf(callback); + if (idx === -1) { + console.error("Cannot find connection of signal " + signalName + " to " + callback.name); + return; + } + object.__objectSignals__[signalIndex].splice(idx, 1); + if (!isPropertyNotifySignal && object.__objectSignals__[signalIndex].length === 0) { + // only required for "pure" signals, handled separately for properties in propertyUpdate + webChannel.exec({ + type: QWebChannelMessageTypes.disconnectFromSignal, + object: object.__id__, + signal: signalIndex + }); + } + } + }; + } + + /** + * Invokes all callbacks for the given signalname. Also works for property notify callbacks. + */ + function invokeSignalCallbacks(signalName, signalArgs) + { + var connections = object.__objectSignals__[signalName]; + if (connections) { + connections.forEach(function(callback) { + callback.apply(callback, signalArgs); + }); + } + } + + this.propertyUpdate = function(signals, propertyMap) + { + // update property cache + for (var propertyIndex in propertyMap) { + var propertyValue = propertyMap[propertyIndex]; + object.__propertyCache__[propertyIndex] = propertyValue; + } + + for (var signalName in signals) { + // Invoke all callbacks, as signalEmitted() does not. This ensures the + // property cache is updated before the callbacks are invoked. + invokeSignalCallbacks(signalName, signals[signalName]); + } + } + + this.signalEmitted = function(signalName, signalArgs) + { + invokeSignalCallbacks(signalName, signalArgs); + } + + function addMethod(methodData) + { + var methodName = methodData[0]; + var methodIdx = methodData[1]; + object[methodName] = function() { + var args = []; + var callback; + for (var i = 0; i < arguments.length; ++i) { + if (typeof arguments[i] === "function") + callback = arguments[i]; + else + args.push(arguments[i]); + } + + webChannel.exec({ + "type": QWebChannelMessageTypes.invokeMethod, + "object": object.__id__, + "method": methodIdx, + "args": args + }, function(response) { + if (response !== undefined) { + var result = object.unwrapQObject(response); + if (callback) { + (callback)(result); + } + } + }); + }; + } + + function bindGetterSetter(propertyInfo) + { + var propertyIndex = propertyInfo[0]; + var propertyName = propertyInfo[1]; + var notifySignalData = propertyInfo[2]; + // initialize property cache with current value + // NOTE: if this is an object, it is not directly unwrapped as it might + // reference other QObject that we do not know yet + object.__propertyCache__[propertyIndex] = propertyInfo[3]; + + if (notifySignalData) { + if (notifySignalData[0] === 1) { + // signal name is optimized away, reconstruct the actual name + notifySignalData[0] = propertyName + "Changed"; + } + addSignal(notifySignalData, true); + } + + Object.defineProperty(object, propertyName, { + configurable: true, + get: function () { + var propertyValue = object.__propertyCache__[propertyIndex]; + if (propertyValue === undefined) { + // This shouldn't happen + console.warn("Undefined value in property cache for property \"" + propertyName + "\" in object " + object.__id__); + } + + return propertyValue; + }, + set: function(value) { + if (value === undefined) { + console.warn("Property setter for " + propertyName + " called with undefined value!"); + return; + } + object.__propertyCache__[propertyIndex] = value; + webChannel.exec({ + "type": QWebChannelMessageTypes.setProperty, + "object": object.__id__, + "property": propertyIndex, + "value": value + }); + } + }); + + } + + // ---------------------------------------------------------------------- + + data.methods.forEach(addMethod); + + data.properties.forEach(bindGetterSetter); + + data.signals.forEach(function(signal) { addSignal(signal, false); }); + + for (var name in data.enums) { + object[name] = data.enums[name]; + } +} + +//required for use with nodejs +if (typeof module === 'object') { + module.exports = { + QWebChannel: QWebChannel + }; +} |