WebView.qml 8.85 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/***************************************************************************
 *                                                                         *
 *   Copyright 2014-2015 Sebastian Kügler <sebas@kde.org>                  *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA .        *
 *                                                                         *
 ***************************************************************************/

import QtQuick 2.3
23
import QtQuick.Controls 2.4 as Controls
Jonah Brüchert's avatar
Jonah Brüchert committed
24
import QtQuick.Window 2.1
25
import QtQuick.Layouts 1.3
26
import QtWebEngine 1.7
27

28
import org.kde.kirigami 2.4 as Kirigami
29 30
import org.kde.mobile.angelfish 1.0

31

32 33 34 35 36
WebEngineView {
    id: webEngineView

    property string errorCode: ""
    property string errorString: ""
Jonah Brüchert's avatar
Jonah Brüchert committed
37

38 39
    property bool privateMode: false

40
    property alias userAgent: userAgent
41
    property alias thumb: thumb
42

43 44
    property bool reloadOnVisible: false

45 46 47
    UserAgentGenerator {
        id: userAgent
        isMobile: Kirigami.Settings.isMobile
48
        onUserAgentChanged: tabs.tabsModel.setTab(tabs.tabsModel.currentTab, webEngineView.url, isMobile)
49
    }
50

51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
    Image {
        id: thumb
        visible: false
    }

    Timer {
        id: snaphotTimer
        interval: 1000
        repeat: false
        onTriggered: {
            if (webEngineView.visible)
                grabThumb();
        }
    }

66
    profile {
67
        offTheRecord: privateMode
68

69 70
        httpUserAgent: userAgent.userAgent

71
        onDownloadRequested: {
72 73 74 75 76
            // if we don't accept the request right away, it will be deleted
            download.accept()
            // therefore just stop the download again as quickly as possible,
            // and ask the user for confirmation
            download.pause()
77 78 79 80

            questionLoader.setSource("DownloadQuestion.qml")
            questionLoader.item.download = download
            questionLoader.item.visible = true
81 82 83 84 85 86 87 88 89 90 91 92 93
        }

        onDownloadFinished: {
            if (download.state === WebEngineDownloadItem.DownloadCompleted) {
                showPassiveNotification(i18n("Download finished"))
            }
            else if (download.state === WebEngineDownloadItem.DownloadInterrupted) {
                showPassiveNotification(i18n("Download failed"))
                console.log("Download interrupt reason: " + download.interruptReason)
            }
            else if (download.state === WebEngineDownloadItem.DownloadCancelled) {
                console.log("Download cancelled by the user")
            }
94 95
        }
    }
96

97
    settings {
Jonah Brüchert's avatar
Jonah Brüchert committed
98
        // Disable builtin error pages in favor of our own
99
        errorPageEnabled: false
Rinigus Saar's avatar
Rinigus Saar committed
100 101
        // Disable scrollbars on mobile
        showScrollBars: !Kirigami.Settings.isMobile
102 103
    }

Jonah Brüchert's avatar
Jonah Brüchert committed
104
    // Custom context menu
105 106 107 108 109
    Controls.Menu {
        property var request
        id: contextMenu

        Controls.MenuItem {
110
            enabled: contextMenu.request != null && (contextMenu.request.editFlags & ContextMenuRequest.CanCopy) != 0
111 112 113 114
            text: i18n("Copy")
            onTriggered: webEngineView.triggerWebAction(WebEngineView.Copy)
        }
        Controls.MenuItem {
115
            enabled: contextMenu.request != null && (contextMenu.request.editFlags & ContextMenuRequest.CanCut) != 0
116 117 118 119
            text: i18n("Cut")
            onTriggered: webEngineView.triggerWebAction(WebEngineView.Cut)
        }
        Controls.MenuItem {
120
            enabled: contextMenu.request != null && (contextMenu.request.editFlags & ContextMenuRequest.CanPaste) != 0
121 122 123 124
            text: i18n("Paste")
            onTriggered: webEngineView.triggerWebAction(WebEngineView.Paste)
        }
        Controls.MenuItem {
125
            enabled: contextMenu.request !== null && contextMenu.request.linkUrl !== ""
126
            text: i18n("Copy Url")
127
            onTriggered: webEngineView.triggerWebAction(WebEngineView.CopyLinkToClipboard)
128 129 130
        }
        Controls.MenuItem {
            text: i18n("View source")
131
            onTriggered: tabsModel.newTab("view-source:" + webEngineView.url)
132 133 134 135 136 137
        }
        Controls.MenuItem {
            text: i18n("Download")
            onTriggered: webEngineView.triggerWebAction(WebEngineView.DownloadLinkToDisk)
        }
        Controls.MenuItem {
138
            enabled: contextMenu.request !== null && contextMenu.request.linkUrl !== ""
139 140 141 142
            text: i18n("Open in new Tab")
            onTriggered: webEngineView.triggerWebAction(WebEngineView.OpenLinkInNewTab)
        }
    }
143 144

    focus: true
145
    onLoadingChanged: {
146 147 148 149 150 151 152 153 154 155 156 157 158
        //print("Loading: " + loading);
        print("    url: " + loadRequest.url)
        //print("   icon: " + webEngineView.icon)
        //print("  title: " + webEngineView.title)

        /* Handle
        *  - WebEngineView::LoadStartedStatus,
        *  - WebEngineView::LoadStoppedStatus,
        *  - WebEngineView::LoadSucceededStatus and
        *  - WebEngineView::LoadFailedStatus
        */
        var ec = "";
        var es = "";
159 160 161
        if (loadRequest.status === WebEngineView.LoadStartedStatus) {
            thumb.source = "";
        }
162
        if (loadRequest.status === WebEngineView.LoadSucceededStatus) {
163
            if (!privateMode) {
Jonah Brüchert's avatar
Jonah Brüchert committed
164
                addHistoryEntry();
165
            }
166
            grabThumb();
167
        }
168
        if (loadRequest.status === WebEngineView.LoadFailedStatus) {
169 170 171
            print("Load failed: " + loadRequest.errorCode + " " + loadRequest.errorString);
            ec = loadRequest.errorCode;
            es = loadRequest.errorString;
172
            thumb.source = "";
173 174 175 176 177 178 179
        }
        errorCode = ec;
        errorString = es;
    }

    Component.onCompleted: {
        print("WebView completed.");
Jonah Brüchert's avatar
Jonah Brüchert committed
180
        var settings = webEngineView.settings;
181 182
        print("Settings: " + settings);
    }
183 184 185

    onIconChanged: {
        if (icon)
186
            BrowserManager.history.updateIcon(url, icon)
187
    }
188 189

    onNewViewRequested: {
190
        if (request.userInitiated) {
191
            tabsModel.newTab(request.requestedUrl.toString())
Pino Toscano's avatar
Pino Toscano committed
192
            showPassiveNotification(i18n("Website was opened in a new tab"))
193
        } else {
194 195 196
            questionLoader.setSource("NewTabQuestion.qml")
            questionLoader.item.url = request.requestedUrl
            questionLoader.item.visible = true
197
        }
198 199
    }

200 201
    onUrlChanged: thumb.source = ""

Jonah Brüchert's avatar
Jonah Brüchert committed
202 203 204 205 206 207 208 209
    onFullScreenRequested: {
        request.accept()
        if (webBrowser.visibility !== Window.FullScreen)
            webBrowser.showFullScreen()
        else
            webBrowser.showNormal()
    }

210
    onContextMenuRequested: {
Jonah Brüchert's avatar
Jonah Brüchert committed
211
        request.accepted = true // Make sure QtWebEngine doesn't show its own context menu.
212 213 214 215 216
        contextMenu.request = request
        contextMenu.x = request.x
        contextMenu.y = request.y
        contextMenu.open()
    }
217 218 219

    onAuthenticationDialogRequested: {
        request.accepted = true
220 221 222
        sheetLoader.setSource("AuthSheet.qml")
        sheetLoader.item.request = request
        sheetLoader.item.open()
223
    }
224 225

    onFeaturePermissionRequested: {
226 227 228 229
        questionLoader.setSource("PermissionQuestion.qml")
        questionLoader.item.permission = feature
        questionLoader.item.origin = securityOrigin
        questionLoader.item.visible = true
230
    }
231 232 233 234

    onVisibleChanged: {
        // set user agent to the current displayed tab
        // this ensures that we follow mobile preference
235 236 237 238 239 240
        // of the current webview. also update the current
        // snapshot image with short delay to be sure that
        // all kirigami pages have moved into place
        if (visible) {
            profile.httpUserAgent = Qt.binding(function() { return userAgent.userAgent; });
            snaphotTimer.start();
241 242 243 244
            if (reloadOnVisible) {
                reloadOnVisible = false;
                reload();
            }
245 246 247 248 249 250 251
        }
    }

    function grabThumb() {
        webEngineView.grabToImage(function(result) {
            webEngineView.thumb.source = result.url;
        });
252
    }
253
}