Dekko
Loading...
Searching...
No Matches
DekkoWebView.qml
1/* Copyright (C) 2014-2016 Dan Chapman <dpniel@ubuntu.com>
2
3 This file is part of Dekko email client for Ubuntu Devices/
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License as
7 published by the Free Software Foundation; either version 2 of
8 the License or (at your option) version 3
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18import QtQuick 2.4
19import QtFeedback 5.0
20import Lomiri.Components 1.3
21import Lomiri.Components.Popups 1.3
22import Lomiri.Components.ListItems 1.3 as ListItem
23import Lomiri.Content 1.3
24import QtQuick.Controls 2.1 as QQC2
25import QtWebEngine 1.5
26import Dekko.Components 1.0
27import Dekko.Mail.Settings 1.0
28import Dekko.Lomiri.Components 1.0
29import Dekko.Lomiri.Dialogs 1.0
30import Dekko.WebContext 1.0
31import QtQuick.Controls.Suru 2.2
32
33WebEngineView {
34 id: webview
35
36 property bool fullScreen: false
37 property int messageUid
38 readonly property alias contentBlocked: internal.remoteContentBlockRunning
39 property alias context: webview.profile
40
41 zoomFactor: units.gu(1) / 8
42
43 function setCidQuery(uid) {
44 messageUid = uid;
45 }
46
47 function setBodyUrl(body) {
48 webview.url = body
49 }
50
51 function allowBlockedContent() {
52 internal.remoteContentBlockRunning = false
53 ctxt.remoteContentAllowed = true;
54 reload()
55 }
56
57 // LOCK IT DOWN!!
58 // incognito: true can't set his as it disables userscripts.
59 settings {
60 // We should NEVER allow javascript to run in
61 // a message body. See https://miki.it/blog/2013/9/24/mailboxapp-javascript-execution/ for
62 // how mailbox (by dropbox) got it wrong :-)
63 // http://doc.qt.io/qt-5/qml-qtwebengine-webenginesettings.html
64 javascriptEnabled: false
65 javascriptCanAccessClipboard: false
66 localContentCanAccessFileUrls: false
67 autoLoadImages: true
68 localStorageEnabled: false
69 hyperlinkAuditingEnabled: false
70 defaultTextEncoding: "UTF-8"
71 }
72
73 userScripts: [
74 WebEngineScript {
75 sourceUrl: Qt.resolvedUrl("./userscripts/font_user_script.js")
76 runOnSubframes: true
77 },
78 WebEngineScript {
79 sourceUrl: Qt.resolvedUrl("./userscripts/viewport_shim.js")
80 runOnSubframes: true
81 }
82 ]
83
84 context: DekkoWebContext {
85 id: ctxt
86
87 readonly property string defaultUserAgent: _ua.defaultUA
88
89 qmlEngineInjector: parent
90
91 httpUserAgent: defaultUserAgent
92 httpCacheMaximumSize: cacheSizeHint
93 cachePath: cacheLocation
94 persistentStoragePath: dataLocation
95
96 persistentCookiesPolicy: WebEngineProfile.NoPersistentCookies
97
98 messageUid: webview.messageUid
99 remoteContentAllowed: PolicyManager.privacy.allowRemoteContent
100
101 onRemoteContentBlocked: {
102 if (!internal.remoteContentBlockRunning) {
103 internal.remoteContentBlockRunning = true
104 }
105 }
106
107 property QtObject _ua: QtObject {
108 id: defaultUA
109 // Empirical value: screens smaller than 19cm are considered small enough that a
110 // mobile UA string is used, screens bigger than that will get desktop content.
111 readonly property bool smallScreen: ctxt.screenDiagonal < 190
112
113 // %1: Ubuntu version, e.g. "14.04"
114 // %2: optional token to specify further attributes of the platform, e.g. "like Android"
115 // %3: optional hardware ID token
116 // %4: WebKit version, e.g. "537.36"
117 // %5: Chromium version, e.g. "35.0.1870.2"
118 // %6: Optional token to provide additional free-form information, e.g. "Mobile"
119 // %7: Safari version, e.g. "537.36"
120 // %8: Optional token, in case some extra bits are needed to make things work (e.g. an extra form factor info etc.)
121 //
122 // note #1: "Mozilla/5.0" is misinformation, but it is a legacy token that
123 // virtually every single UA out there has, it seems unwise to remove it
124 // note #2: "AppleWebKit", as opposed to plain "WebKit", does make a
125 // difference in the content served by certain sites (e.g. gmail.com)
126 readonly property string _template: "Mozilla/5.0 (Linux; Ubuntu %1%2%3) AppleWebKit/%4 Chromium/%5 %6Safari/%7%8"
127
128 // FIXME: compute at build time (using lsb_release)
129 readonly property string _ubuntuVersion: "20.04"
130
131 readonly property string _attributes: smallScreen ? "like Android" : ""
132
133 readonly property string _hardwareID: ""
134
135 // See chromium/src/content/webkit_version.h.in in oxide’s source tree.
136 readonly property string _webkitVersion: "537.36"
137
138 // See chromium/src/chrome/VERSION in oxide’s source tree.
139 // Note: the actual version number probably doesn’t matter that much,
140 // however its format does, so we probably don’t need to bump it
141 // every time we rebase on a newer chromium.
142 readonly property string _chromiumVersion: "35.0.1870.2"
143
144 readonly property string _formFactor: smallScreen ? "Mobile" : ""
145
146 readonly property string _more: ""
147
148 property string defaultUA: {
149 var ua = _template
150 ua = ua.arg(_ubuntuVersion) // %1
151 ua = ua.arg((_attributes !== "") ? " %1".arg(_attributes) : "") // %2
152 ua = ua.arg((_hardwareID !== "") ? "; %1".arg(_hardwareID) : "") // %3
153 ua = ua.arg(_webkitVersion) // %4
154 ua = ua.arg(_chromiumVersion) // %5
155 ua = ua.arg((_formFactor !== "") ? "%1 ".arg(_formFactor) : "") // %6
156 ua = ua.arg(_webkitVersion) // %7
157 ua = ua.arg((_more !== "") ? " %1".arg(_more) : "") // %8
158 return ua
159 }
160 }
161 }
162
163 // TODO: is this text still relevant?
164 // Binding the contxt popover directly to this causes
165 // it to get rendered below message header.
166 // So instead just use this item to grab the model context property
167 // And popup the dialog outside of this binding.
168 //
169
170 onContextMenuRequested: {
171 request.accepted = true // The request is handled here
172 let openMenu = false
173
174 if (request.selectedText !== "") {
175 contextMenuShape.contextContentText = ""
176 openMenu = true
177 }
178 else if (request.linkText !== "") {
179 contextMenuShape.contextContentText = request.linkUrl
180 openMenu = true
181 }
182 if (openMenu && !contextMenuShape.visible) {
183 contextMenuShape.visible = true
184 contextMenuShape.x = webview.x + units.gu(10)
185 contextMenuShape.y = webview.y + units.gu(10)
186 }
187 }
188
189 onNewViewRequested: {
190 if (request.userInitiated) {
191 console.log("[Dekko Web View] New View requested, requestedUrl", request.requestedUrl)
192 PopupUtils.open(linkClickedDialog, dekko, {externalLink: request.requestedUrl})
193 } else {
194 console.log("[Dekko Web View] Ignoring non-user initiated New View Request with requestedUrl", request.requestedUrl)
195 }
196 }
197
198 onNavigationRequested: {
199 console.log("[Dekko Web View] RequestUrl", request.url)
200 if (("" + request.url).startsWith("dekko-msg") ||
201 ("" + request.url).startsWith("dekko-part") ||
202 ("" + request.url).startsWith("cid")) {
203 request.action = 0
204 }
205 else {
206 PopupUtils.open(linkClickedDialog, dekko, {externalLink: request.url})
207 request.action = 255
208 }
209 }
210
211 onJavaScriptConsoleMessage: {
212 var msg = "[Dekko Web View] [JS] (%1:%2) %3".arg(sourceID).arg(lineNumber).arg(message)
213 console.log(msg)
214 }
215
216 Item {
217 id: contextualRectangle
218 visible: true
219 readonly property real locationBarOffset: (webview.locationBarController ? (webview.locationBarController.height + webview.locationBarController.offset) : 0)
220 x: internal.contextModel ? internal.contextModel.position.x : 0
221 y: internal.contextModel ? internal.contextModel.position.y + locationBarOffset : 0
222 }
223
224 QtObject {
225 id: internal
226 property QtObject ctxtModel: null
227 property bool remoteContentBlockRunning: false
228 }
229
230 HapticsEffect {
231 id: clickEffect
232 attackIntensity: 0.0
233 attackTime: 50
234 intensity: 1.0
235 duration: 10
236 fadeTime: 50
237 fadeIntensity: 0.0
238 }
239
240 Component {
241 id: linkClickedDialog
243 id: linkDialog
244 property string externalLink
245 title: qsTr("Open in browser?")
246 text: qsTr("Confirm to open %1 in web browser").arg(externalLink.substring(0, 30))
247 onConfirmClicked: Qt.openUrlExternally(externalLink)
248 }
249 }
250}