Merge lp:~michael-sheldon/webbrowser-app/ssl-status into lp:webbrowser-app

Proposed by Michael Sheldon on 2014-09-19
Status: Merged
Approved by: Olivier Tilloy on 2014-09-22
Approved revision: 731
Merged at revision: 718
Proposed branch: lp:~michael-sheldon/webbrowser-app/ssl-status
Merge into: lp:webbrowser-app
Diff against target: 874 lines (+658/-70)
6 files modified
po/webbrowser-app.pot (+168/-11)
src/app/InvalidCertificateErrorSheet.qml (+215/-0)
src/app/WebViewImpl.qml (+11/-0)
src/app/webbrowser/AddressBar.qml (+245/-59)
src/app/webbrowser/Browser.qml (+18/-0)
src/app/webbrowser/Chrome.qml (+1/-0)
To merge this branch: bzr merge lp:~michael-sheldon/webbrowser-app/ssl-status
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Needs Fixing on 2014-09-22
Olivier Tilloy 2014-09-19 Approve on 2014-09-22
Review via email: mp+235309@code.launchpad.net

Commit message

* Display padlock when viewing pages over HTTPS.
* Provide certificate details when padlock is clicked.
* Display error pages when invalid SSL certificates are presented and allow users to override these warnings.
* Display warning symbol when insecure content is loaded over HTTPS (currently incorrect icon, should be updated when dialog-warning-symbol lands in ubuntu-theme).

Description of the change

* Display padlock when viewing pages over HTTPS.
* Provide certificate details when padlock is clicked.
* Display error pages when invalid SSL certificates are presented and allow users to override these warnings.
* Display warning symbol when insecure content is loaded over HTTPS (currently incorrect icon, should be updated when dialog-warning-symbol lands in ubuntu-theme).

To post a comment you must log in.
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Olivier Tilloy (osomon) wrote :

I don’t see changes to the translation template, despite the new strings. Is that on purpose (i.e. planning to update the file and push to trunk directly after landing)?
So far we’ve been merging translation template updates along with the corresponding code changes, and dealing with conflicts on a case-by-case basis, but I’m open to revising this approach.

Olivier Tilloy (osomon) wrote :

6 + * Copyright 2013-2014 Canonical Ltd.

This should probably be only copyright 2014.

722. By Michael Sheldon on 2014-09-22

Fix copyright statement

723. By Michael Sheldon on 2014-09-22

Update translation template

Michael Sheldon (michael-sheldon) wrote :

Here are a few sites that can be used to test some different SSL failure conditions:

Expired certificate: https://testssl-expire.disig.sk/
Bad Identity (only valid for https://www.halifax.co.uk): https://halifax.co.uk
Mix of secure/insecure content: https://www.bennish.net/mixed-content.html
Dynamic mix of secure/insecure content (secure until play is pressed on a track): https://m.7digital.com/GB/releases/1530991
Revoked EV certificate (will cause a crash due to bug 1371569 until the next oxide release): https://test-sspev.verisign.com:2443/test-SSPEV-revoked-verisign.html

Olivier Tilloy (osomon) wrote :

I don’t think this is explicitly mentioned in the spec, but I think it would be nice to enable tap on the certificate details popover to dismiss it.

Olivier Tilloy (osomon) wrote :

There seems to be a regression in what the address bar displays, probably not introduced by this MR (maybe by http://bazaar.launchpad.net/~phablet-team/webbrowser-app/trunk/revision/710), which becomes very visible when trying to browse a site with an invalid security certificate: browse to any site, wait for it to fully load, then tap on the address bar and try to browse to e.g. https://www.pcwebshop.co.uk/ : you’re presented with the security warning page, but the shortened address in the address bar is that of the previously browsed site.

We might want to address this issue as part of this MR, considering that it makes it much more visible to the user.

Olivier Tilloy (osomon) wrote :

If I browse to a site with an invalid security certificate (e.g. https://www.pcwebshop.co.uk/) and choose to proceed anyway, despite the security warning, the site is loaded but there is no visual feedback in the address bar, there should probably be a broken padlock icon or something similar. Anyway, this is something we need to discuss with design.

PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Olivier Tilloy (osomon) wrote :

On the invalid security certificate warning page, the "proceed anyway" and "back to safety" button are pretty narrow, translations in many languages that are more verbose than English won’t fit, and they will end up ellipsized. E.g. in French:

    "Poursuivre quand même" → "Poursuivre qu…"
    "Retour à la sécurité" → "Retour à la séc…"

And the buttons are not resized when changing the orientation of the device to landscape, even though there is more space available. Let’s request design to comment, and in the meantime I would put the two buttons in a column and not constrain their width to ensure the text fits.

Olivier Tilloy (osomon) wrote :

It looks like the dates in the details of the security certificate warning page are incorrectly formatted (or maybe incorrectly exposed by oxide?). For https://testssl-expire.disig.sk/ I’m seeing an expiry date of "jeu. juil. 23 15:17:38 2381 GMT+0100". The year looks incorrect (and the formatting too, for a date localized in French, but I guess that’s a separate issue).

Olivier Tilloy (osomon) wrote :

> On the invalid security certificate warning page, the "proceed anyway" and
> "back to safety" button are pretty narrow, translations in many languages that
> are more verbose than English won’t fit, and they will end up ellipsized. E.g.
> in French:

A screenshot taken on my krillin to illustrate the issue: http://people.canonical.com/~osomon/security-certificate-warning.png

Olivier Tilloy (osomon) wrote :

408 + // TRANSLATORS: %1 refers to the SSL certificate's start date

Copy/paste error, this should read "expiry date".

review: Needs Fixing
Olivier Tilloy (osomon) wrote :

Some sentences in the invalid certificate warning page are missing a full stop at the end:

    "This site security certificate is not trusted"

    "You should not proceed, especially if you have never seen this warning before for this site"

Olivier Tilloy (osomon) wrote :

    "This site security certificate is not trusted\n…"

The above is repeated in a number of situations, would it be worth making it a separate string in its own label, to save translators some work?

Olivier Tilloy (osomon) wrote :

486 + console.log("Switch failed")

is this really needed?

Olivier Tilloy (osomon) wrote :

285 + target: certificateError ? certificateError : null

isn’t this equivalent to:

    target: certificateError

724. By Michael Sheldon on 2014-09-22

Place buttons in column and don't restrict their width

725. By Michael Sheldon on 2014-09-22

Add missing full stops from certificate error

726. By Michael Sheldon on 2014-09-22

Remove stray debug statement

727. By Michael Sheldon on 2014-09-22

Fix incorrect translator hint

Olivier Tilloy (osomon) wrote :

365 + onTextChanged: {
366 + // Remove any blank lines caused by missing entries
367 + while(subjectAddress.text.indexOf("\n\n") != -1) {
368 + subjectAddress.text = subjectAddress.text.replace("\n\n", "\n")
369 + }
370 + }

Wouldn’t it be much easier to just do that where 'text' is assigned:

    text: i18n.tr("[…]").arg([…]).replace(/\n\n/g, "\n")

728. By Michael Sheldon on 2014-09-22

Use locale based date display in certificate errors

729. By Michael Sheldon on 2014-09-22

Dismiss certificate popover when clicked

Michael Sheldon (michael-sheldon) wrote :

> 285 + target: certificateError ? certificateError : null
>
> isn’t this equivalent to:
>
> target: certificateError

 This would cause a warning due to the certificateError being undefined on start up (whereas explicitly setting a null target won't).

Olivier Tilloy (osomon) wrote :

> > 285 + target: certificateError ? certificateError : null
> >
> > isn’t this equivalent to:
> >
> > target: certificateError
>
> This would cause a warning due to the certificateError being undefined on
> start up (whereas explicitly setting a null target won't).

Ah, ok. I would have expected it to be 'null' rather than 'undefined'. The conditional makes sense then.

Michael Sheldon (michael-sheldon) wrote :

I've switched the dates to using the locale date, the incorrect year is a bug in oxide which is being tracked here: https://bugs.launchpad.net/oxide/+bug/1372414

730. By Michael Sheldon on 2014-09-22

Simplify removal of extra whitespace in certificate addresses

Michael Sheldon (michael-sheldon) wrote :

> 365 + onTextChanged: {
> 366 + // Remove any blank lines caused by missing
> entries
> 367 + while(subjectAddress.text.indexOf("\n\n") !=
> -1) {
> 368 + subjectAddress.text =
> subjectAddress.text.replace("\n\n", "\n")
> 369 + }
> 370 + }
>
> Wouldn’t it be much easier to just do that where 'text' is assigned:
>
> text: i18n.tr("[…]").arg([…]).replace(/\n\n/g, "\n")

Yeah, the problem with that was that since it was only a single pass it could leave extra white space when there'd been three blank lines. But I just realised I could match against /\n+/g to fix that, so I've committed a tidier implementation now.

PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
731. By Michael Sheldon on 2014-09-22

Avoid duplication of string on SSL certificate error page

Olivier Tilloy (osomon) wrote :

It all looks good to me now. Thanks!

review: Approve
PS Jenkins bot (ps-jenkins) wrote :

FAILED: Continuous integration, rev:731
http://jenkins.qa.ubuntu.com/job/webbrowser-app-ci/1096/
Executed test runs:
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-utopic-touch/5055/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-utopic/3691
    SUCCESS: http://jenkins.qa.ubuntu.com/job/webbrowser-app-utopic-amd64-ci/295
    SUCCESS: http://jenkins.qa.ubuntu.com/job/webbrowser-app-utopic-armhf-ci/295
        deb: http://jenkins.qa.ubuntu.com/job/webbrowser-app-utopic-armhf-ci/295/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/webbrowser-app-utopic-i386-ci/295
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-runner-mako/4805/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-armhf/6307
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-armhf/6307/artifact/work/output/*zip*/output.zip
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/13545
    SUCCESS: http://jenkins.qa.ubuntu.com/job/autopilot-testrunner-otto-utopic/3084
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-amd64/4000
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-amd64/4000/artifact/work/output/*zip*/output.zip

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/webbrowser-app-ci/1096/rebuild

review: Needs Fixing (continuous-integration)
PS Jenkins bot (ps-jenkins) wrote :

FAILED: Continuous integration, rev:731
http://jenkins.qa.ubuntu.com/job/webbrowser-app-ci/1097/
Executed test runs:
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-utopic-touch/5060/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-utopic/3694
    SUCCESS: http://jenkins.qa.ubuntu.com/job/webbrowser-app-utopic-amd64-ci/296
    SUCCESS: http://jenkins.qa.ubuntu.com/job/webbrowser-app-utopic-armhf-ci/296
        deb: http://jenkins.qa.ubuntu.com/job/webbrowser-app-utopic-armhf-ci/296/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/webbrowser-app-utopic-i386-ci/296
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-deb-autopilot-runner-mako/4808/console
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-armhf/6312
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-armhf/6312/artifact/work/output/*zip*/output.zip
    SUCCESS: http://s-jenkins.ubuntu-ci:8080/job/touch-flash-device/13549
    SUCCESS: http://jenkins.qa.ubuntu.com/job/autopilot-testrunner-otto-utopic/3086
    SUCCESS: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-amd64/4003
        deb: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-utopic-amd64/4003/artifact/work/output/*zip*/output.zip

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/webbrowser-app-ci/1097/rebuild

review: Needs Fixing (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'po/webbrowser-app.pot'
2--- po/webbrowser-app.pot 2014-08-21 11:28:58 +0000
3+++ po/webbrowser-app.pot 2014-09-22 14:01:07 +0000
4@@ -8,7 +8,7 @@
5 msgstr ""
6 "Project-Id-Version: webbrowser-app\n"
7 "Report-Msgid-Bugs-To: \n"
8-"POT-Creation-Date: 2014-08-21 13:27+0200\n"
9+"POT-Creation-Date: 2014-09-22 14:59+0100\n"
10 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
11 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
12 "Language-Team: LANGUAGE <LL@li.org>\n"
13@@ -75,10 +75,12 @@
14 msgstr ""
15
16 #: src/app/CertificateVerificationDialog.qml:29
17+#: src/app/InvalidCertificateErrorSheet.qml:162
18 msgid "Proceed anyway"
19 msgstr ""
20
21 #: src/app/CertificateVerificationDialog.qml:35
22+#: src/app/InvalidCertificateErrorSheet.qml:175
23 msgid "Back to safety"
24 msgstr ""
25
26@@ -129,6 +131,149 @@
27 msgid "Allow"
28 msgstr ""
29
30+#: src/app/InvalidCertificateErrorSheet.qml:58
31+msgid "This site security certificate is not trusted.\n"
32+msgstr ""
33+
34+#: src/app/InvalidCertificateErrorSheet.qml:66
35+msgid "Learn more"
36+msgstr ""
37+
38+#. TRANSLATORS: %1 refers to the SSL certificate's serial number
39+#: src/app/InvalidCertificateErrorSheet.qml:90
40+#, qt-format
41+msgid ""
42+"Serial number:\n"
43+"%1"
44+msgstr ""
45+
46+#. TRANSLATORS: %1 refers to the SSL certificate's subject display name
47+#: src/app/InvalidCertificateErrorSheet.qml:97
48+#, qt-format
49+msgid ""
50+"Subject:\n"
51+"%1"
52+msgstr ""
53+
54+#. TRANSLATORS: %1 refers to the SSL certificate's subject's address
55+#: src/app/InvalidCertificateErrorSheet.qml:105
56+#, qt-format
57+msgid ""
58+"Subject address:\n"
59+"%1"
60+msgstr ""
61+
62+#. TRANSLATORS: %1 refers to the SSL certificate's issuer display name
63+#: src/app/InvalidCertificateErrorSheet.qml:116
64+#, qt-format
65+msgid ""
66+"Issuer:\n"
67+"%1"
68+msgstr ""
69+
70+#. TRANSLATORS: %1 refers to the SSL certificate's issuer's address
71+#: src/app/InvalidCertificateErrorSheet.qml:124
72+#, qt-format
73+msgid ""
74+"Issuer address:\n"
75+"%1"
76+msgstr ""
77+
78+#. TRANSLATORS: %1 refers to the SSL certificate's start date
79+#: src/app/InvalidCertificateErrorSheet.qml:135
80+#, qt-format
81+msgid ""
82+"Valid from:\n"
83+"%1"
84+msgstr ""
85+
86+#. TRANSLATORS: %1 refers to the SSL certificate's expiry date
87+#: src/app/InvalidCertificateErrorSheet.qml:142
88+#, qt-format
89+msgid ""
90+"Valid until:\n"
91+"%1"
92+msgstr ""
93+
94+#. TRANSLATORS: %1 refers to the SSL certificate's SHA1 fingerprint
95+#: src/app/InvalidCertificateErrorSheet.qml:149
96+#, qt-format
97+msgid ""
98+"Fingerprint (SHA1):\n"
99+"%1"
100+msgstr ""
101+
102+#: src/app/InvalidCertificateErrorSheet.qml:155
103+msgid ""
104+"You should not proceed, especially if you have never seen this warning "
105+"before for this site."
106+msgstr ""
107+
108+#. TRANSLATORS: %1 refers to the domain name of the SSL certificate
109+#: src/app/InvalidCertificateErrorSheet.qml:190
110+#, qt-format
111+msgid ""
112+"You attempted to reach %1 but the server presented a security certificate "
113+"which does not match the identity of the site."
114+msgstr ""
115+
116+#. TRANSLATORS: %1 refers to the domain name of the SSL certificate
117+#: src/app/InvalidCertificateErrorSheet.qml:193
118+#, qt-format
119+msgid ""
120+"You attempted to reach %1 but the server presented a security certificate "
121+"which has expired."
122+msgstr ""
123+
124+#. TRANSLATORS: %1 refers to the domain name of the SSL certificate
125+#: src/app/InvalidCertificateErrorSheet.qml:196
126+#, qt-format
127+msgid ""
128+"You attempted to reach %1 but the server presented a security certificate "
129+"which contains invalid dates."
130+msgstr ""
131+
132+#. TRANSLATORS: %1 refers to the domain name of the SSL certificate
133+#: src/app/InvalidCertificateErrorSheet.qml:199
134+#, qt-format
135+msgid ""
136+"You attempted to reach %1 but the server presented a security certificate "
137+"issued by an entity that is not trusted."
138+msgstr ""
139+
140+#. TRANSLATORS: %1 refers to the domain name of the SSL certificate
141+#: src/app/InvalidCertificateErrorSheet.qml:202
142+#, qt-format
143+msgid ""
144+"You attempted to reach %1 but the server presented a security certificate "
145+"that has been revoked."
146+msgstr ""
147+
148+#. TRANSLATORS: %1 refers to the domain name of the SSL certificate
149+#: src/app/InvalidCertificateErrorSheet.qml:205
150+#, qt-format
151+msgid ""
152+"You attempted to reach %1 but the server presented an invalid security "
153+"certificate."
154+msgstr ""
155+
156+#. TRANSLATORS: %1 refers to the domain name of the SSL certificate
157+#: src/app/InvalidCertificateErrorSheet.qml:208
158+#, qt-format
159+msgid ""
160+"You attempted to reach %1 but the server presented an insecure security "
161+"certificate."
162+msgstr ""
163+
164+#. TRANSLATORS: %1 refers to the domain name of the SSL certificate
165+#: src/app/InvalidCertificateErrorSheet.qml:211
166+#, qt-format
167+msgid ""
168+"This site security certificate is not trusted\n"
169+"You attempted to reach %1 but the server presented a security certificate "
170+"which failed our security checks for an unknown reason."
171+msgstr ""
172+
173 #: src/app/PromptDialog.qml:23
174 msgid "JavaScript Prompt"
175 msgstr ""
176@@ -245,23 +390,35 @@
177 msgid "Share…"
178 msgstr ""
179
180-#: src/app/webbrowser/AddressBar.qml:148
181+#: src/app/webbrowser/AddressBar.qml:214
182+msgid "This site has insecure content"
183+msgstr ""
184+
185+#: src/app/webbrowser/AddressBar.qml:229
186+msgid "You are connected to"
187+msgstr ""
188+
189+#: src/app/webbrowser/AddressBar.qml:251
190+msgid "Which is run by"
191+msgstr ""
192+
193+#: src/app/webbrowser/AddressBar.qml:335
194 msgid "search or enter an address"
195 msgstr ""
196
197-#: src/app/webbrowser/Browser.qml:154
198+#: src/app/webbrowser/Browser.qml:179
199 msgid "Share"
200 msgstr ""
201
202-#: src/app/webbrowser/Browser.qml:168
203+#: src/app/webbrowser/Browser.qml:193
204 msgid "History"
205 msgstr ""
206
207-#: src/app/webbrowser/Browser.qml:174
208+#: src/app/webbrowser/Browser.qml:200
209 msgid "Open tabs"
210 msgstr ""
211
212-#: src/app/webbrowser/Browser.qml:180 src/app/webbrowser/TabsView.qml:57
213+#: src/app/webbrowser/Browser.qml:206 src/app/webbrowser/TabsView.qml:57
214 msgid "New tab"
215 msgstr ""
216
217@@ -330,25 +487,25 @@
218
219 #. TRANSLATORS: %1 refers to the current page’s title
220 #: src/app/webbrowser/webbrowser-app.qml:39
221-#: src/app/webcontainer/webapp-container.qml:57
222+#: src/app/webcontainer/webapp-container.qml:60
223 #, qt-format
224 msgid "%1 - Ubuntu Web Browser"
225 msgstr ""
226
227 #: src/app/webbrowser/webbrowser-app.qml:41
228-#: src/app/webcontainer/webapp-container.qml:59
229+#: src/app/webcontainer/webapp-container.qml:62
230 msgid "Ubuntu Web Browser"
231 msgstr ""
232
233-#: src/app/webcontainer/AccountsLoginPage.qml:70
234+#: src/app/webcontainer/AccountsLoginPage.qml:87
235 msgid "No local account found for "
236 msgstr ""
237
238-#: src/app/webcontainer/AccountsLoginPage.qml:75
239+#: src/app/webcontainer/AccountsLoginPage.qml:92
240 msgid "Skip account creation step"
241 msgstr ""
242
243-#: src/app/webcontainer/AccountsLoginPage.qml:124
244+#: src/app/webcontainer/AccountsLoginPage.qml:141
245 msgid "Add account"
246 msgstr ""
247
248
249=== added file 'src/app/InvalidCertificateErrorSheet.qml'
250--- src/app/InvalidCertificateErrorSheet.qml 1970-01-01 00:00:00 +0000
251+++ src/app/InvalidCertificateErrorSheet.qml 2014-09-22 14:01:07 +0000
252@@ -0,0 +1,215 @@
253+/*
254+ * Copyright 2014 Canonical Ltd.
255+ *
256+ * This file is part of webbrowser-app.
257+ *
258+ * webbrowser-app is free software; you can redistribute it and/or modify
259+ * it under the terms of the GNU General Public License as published by
260+ * the Free Software Foundation; version 3.
261+ *
262+ * webbrowser-app is distributed in the hope that it will be useful,
263+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
264+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
265+ * GNU General Public License for more details.
266+ *
267+ * You should have received a copy of the GNU General Public License
268+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
269+ */
270+
271+import QtQuick 2.0
272+import Ubuntu.Components 1.1
273+import com.canonical.Oxide 1.0 as Oxide
274+
275+Rectangle {
276+ property var certificateError
277+
278+ signal allowed()
279+ signal denied()
280+
281+ Connections {
282+ target: certificateError ? certificateError : null
283+ onCancelled: {
284+ moreInfo.visible = false
285+ denied()
286+ }
287+ }
288+
289+ Flickable {
290+ anchors.fill: parent
291+ anchors.margins: units.gu(4)
292+ contentHeight: errorCol.height
293+
294+ Column {
295+ id: errorCol
296+ anchors.centerIn: parent
297+ width: parent.width
298+
299+ spacing: units.gu(3)
300+
301+ Icon {
302+ anchors.horizontalCenter: parent.horizontalCenter
303+ name: "security-alert"
304+ width: units.gu(4)
305+ height: width
306+ }
307+
308+ Label {
309+ width: parent.width
310+ text: certificateError ? i18n.tr("This site security certificate is not trusted.\n") + textForError(certificateError.certError) : ""
311+ wrapMode: Text.Wrap
312+ horizontalAlignment: Text.AlignHCenter
313+ fontSize: "x-small"
314+ }
315+
316+ Label {
317+ width: parent.width
318+ text: i18n.tr("Learn more")
319+ font.underline: true
320+ fontSize: "x-small"
321+ horizontalAlignment: Text.AlignHCenter
322+ visible: !moreInfo.visible
323+ MouseArea {
324+ anchors.fill: parent
325+ onClicked: {
326+ moreInfo.visible = true
327+ }
328+ }
329+ }
330+
331+ Column {
332+ id: moreInfo
333+ width: parent.width
334+ visible: false
335+ spacing: units.gu(1)
336+
337+ Label {
338+ fontSize: "x-small"
339+ width: parent.width
340+ wrapMode: Text.Wrap
341+ // TRANSLATORS: %1 refers to the SSL certificate's serial number
342+ text: i18n.tr("Serial number:\n%1").arg(certificateError ? certificateError.certificate.serialNumber : "")
343+ }
344+ Label {
345+ fontSize: "x-small"
346+ width: parent.width
347+ wrapMode: Text.Wrap
348+ // TRANSLATORS: %1 refers to the SSL certificate's subject display name
349+ text: i18n.tr("Subject:\n%1").arg(certificateError ? certificateError.certificate.subjectDisplayName : "")
350+ }
351+ Label {
352+ id: subjectAddress
353+ fontSize: "x-small"
354+ width: parent.width
355+ wrapMode: Text.Wrap
356+ // TRANSLATORS: %1 refers to the SSL certificate's subject's address
357+ text: i18n.tr("Subject address:\n%1").arg(certificateError ?
358+ (certificateError.certificate.getSubjectInfo(Oxide.SslCertificate.PrincipalAttrOrganizationName).join(", ") + "\n" +
359+ certificateError.certificate.getSubjectInfo(Oxide.SslCertificate.PrincipalAttrLocalityName).join(", ") + "\n" +
360+ certificateError.certificate.getSubjectInfo(Oxide.SslCertificate.PrincipalAttrStateOrProvinceName).join(", ") + "\n" +
361+ certificateError.certificate.getSubjectInfo(Oxide.SslCertificate.PrincipalAttrCountryName).join(", ")).replace(/\n+/g, "\n") : "")
362+ }
363+ Label {
364+ fontSize: "x-small"
365+ width: parent.width
366+ wrapMode: Text.Wrap
367+ // TRANSLATORS: %1 refers to the SSL certificate's issuer display name
368+ text: i18n.tr("Issuer:\n%1").arg(certificateError ? certificateError.certificate.issuerDisplayName : "")
369+ }
370+ Label {
371+ id: issuerAddress
372+ fontSize: "x-small"
373+ width: parent.width
374+ wrapMode: Text.Wrap
375+ // TRANSLATORS: %1 refers to the SSL certificate's issuer's address
376+ text: i18n.tr("Issuer address:\n%1").arg(certificateError ?
377+ (certificateError.certificate.getIssuerInfo(Oxide.SslCertificate.PrincipalAttrOrganizationName).join(", ") + "\n" +
378+ certificateError.certificate.getIssuerInfo(Oxide.SslCertificate.PrincipalAttrLocalityName).join(", ") + "\n" +
379+ certificateError.certificate.getIssuerInfo(Oxide.SslCertificate.PrincipalAttrStateOrProvinceName).join(", ") + "\n" +
380+ certificateError.certificate.getIssuerInfo(Oxide.SslCertificate.PrincipalAttrCountryName).join(", ")).replace(/\n+/g, "\n") : "")
381+ }
382+ Label {
383+ fontSize: "x-small"
384+ width: parent.width
385+ wrapMode: Text.Wrap
386+ // TRANSLATORS: %1 refers to the SSL certificate's start date
387+ text: i18n.tr("Valid from:\n%1").arg(certificateError ? certificateError.certificate.effectiveDate.toLocaleString() : "")
388+ }
389+ Label {
390+ fontSize: "x-small"
391+ width: parent.width
392+ wrapMode: Text.Wrap
393+ // TRANSLATORS: %1 refers to the SSL certificate's expiry date
394+ text: i18n.tr("Valid until:\n%1").arg(certificateError ? certificateError.certificate.expiryDate.toLocaleString() : "")
395+ }
396+ Label {
397+ fontSize: "x-small"
398+ width: parent.width
399+ wrapMode: Text.Wrap
400+ // TRANSLATORS: %1 refers to the SSL certificate's SHA1 fingerprint
401+ text: i18n.tr("Fingerprint (SHA1):\n%1").arg(certificateError ? certificateError.certificate.fingerprintSHA1 : "")
402+ }
403+ }
404+
405+ Label {
406+ width: parent.width
407+ text: i18n.tr("You should not proceed, especially if you have never seen this warning before for this site.")
408+ wrapMode: Text.Wrap
409+ fontSize: "x-small"
410+ horizontalAlignment: Text.AlignHCenter
411+ }
412+
413+ Button {
414+ text: i18n.tr("Proceed anyway")
415+ anchors.horizontalCenter: parent.horizontalCenter
416+ visible: certificateError ? certificateError.overridable : false
417+ onClicked: {
418+ moreInfo.visible = false
419+ certificateError.allow()
420+ allowed()
421+ }
422+ }
423+
424+ Button {
425+ id: backButton
426+ anchors.horizontalCenter: parent.horizontalCenter
427+ text: i18n.tr("Back to safety")
428+ onClicked: {
429+ moreInfo.visible = false
430+ certificateError.deny()
431+ denied()
432+ }
433+ color: UbuntuColors.orange
434+ }
435+ }
436+ }
437+
438+ function textForError(error) {
439+ switch(error) {
440+ case Oxide.CertificateError.ErrorBadIdentity:
441+ // TRANSLATORS: %1 refers to the domain name of the SSL certificate
442+ return i18n.tr("You attempted to reach %1 but the server presented a security certificate which does not match the identity of the site.").arg(certificateError ? certificateError.url : "")
443+ case Oxide.CertificateError.ErrorExpired:
444+ // TRANSLATORS: %1 refers to the domain name of the SSL certificate
445+ return i18n.tr("You attempted to reach %1 but the server presented a security certificate which has expired.").arg(certificateError ? certificateError.url : "")
446+ case Oxide.CertificateError.ErrorDateInvalid:
447+ // TRANSLATORS: %1 refers to the domain name of the SSL certificate
448+ return i18n.tr("You attempted to reach %1 but the server presented a security certificate which contains invalid dates.").arg(certificateError ? certificateError.url : "")
449+ case Oxide.CertificateError.ErrorAuthorityInvalid:
450+ // TRANSLATORS: %1 refers to the domain name of the SSL certificate
451+ return i18n.tr("You attempted to reach %1 but the server presented a security certificate issued by an entity that is not trusted.").arg(certificateError ? certificateError.url : "")
452+ case Oxide.CertificateError.ErrorRevoked:
453+ // TRANSLATORS: %1 refers to the domain name of the SSL certificate
454+ return i18n.tr("You attempted to reach %1 but the server presented a security certificate that has been revoked.").arg(certificateError ? certificateError.url : "")
455+ case Oxide.CertificateError.ErrorInvalid:
456+ // TRANSLATORS: %1 refers to the domain name of the SSL certificate
457+ return i18n.tr("You attempted to reach %1 but the server presented an invalid security certificate.").arg(certificateError ? certificateError.url : "")
458+ case Oxide.CertificateError.ErrorInsecure:
459+ // TRANSLATORS: %1 refers to the domain name of the SSL certificate
460+ return i18n.tr("You attempted to reach %1 but the server presented an insecure security certificate.").arg(certificateError ? certificateError.url : "")
461+ default:
462+ // TRANSLATORS: %1 refers to the domain name of the SSL certificate
463+ return i18n.tr("This site security certificate is not trusted\nYou attempted to reach %1 but the server presented a security certificate which failed our security checks for an unknown reason.").arg(certificateError ? certificateError.url : "")
464+ }
465+ }
466+
467+}
468
469=== modified file 'src/app/WebViewImpl.qml'
470--- src/app/WebViewImpl.qml 2014-08-21 16:45:23 +0000
471+++ src/app/WebViewImpl.qml 2014-09-22 14:01:07 +0000
472@@ -26,6 +26,9 @@
473 id: webview
474
475 property var currentWebview: webview
476+ property var certificateError
477+ // Invalid certificates the user has explicitly allowed for this session
478+ property var allowedCertificates: []
479
480 /*experimental.certificateVerificationDialog: CertificateVerificationDialog {}
481 experimental.authenticationDialog: AuthenticationDialog {}
482@@ -74,4 +77,12 @@
483 // TODO: we might want to store the answer to avoid requesting
484 // the permission everytime the user visits this site.
485 }
486+
487+ onCertificateError: {
488+ if(webview.allowedCertificates.indexOf(error.certificate.fingerprintSHA1) != -1) {
489+ error.allow()
490+ } else {
491+ certificateError = error
492+ }
493+ }
494 }
495
496=== modified file 'src/app/webbrowser/AddressBar.qml'
497--- src/app/webbrowser/AddressBar.qml 2014-09-08 10:10:48 +0000
498+++ src/app/webbrowser/AddressBar.qml 2014-09-22 14:01:07 +0000
499@@ -18,6 +18,9 @@
500
501 import QtQuick 2.0
502 import Ubuntu.Components 1.1
503+import Ubuntu.Components.Popups 1.0
504+import Ubuntu.Components.ListItems 1.0
505+import com.canonical.Oxide 1.0 as Oxide
506 import ".."
507
508 FocusScope {
509@@ -28,6 +31,7 @@
510 property bool bookmarked: false
511 property url requestedUrl
512 property url actualUrl
513+ property var securityStatus
514 signal validated()
515 property bool loading
516 signal requestReload()
517@@ -55,68 +59,250 @@
518 anchors.fill: parent
519
520 primaryItem: Item {
521- height: textField.height
522- width: height
523-
524- Favicon {
525- id: favicon
526- anchors.centerIn: parent
527- visible: (addressbar.state == "") && addressbar.actualUrl.toString()
528- }
529-
530- MouseArea {
531- id: actionButton
532- objectName: "actionButton"
533- anchors.fill: parent
534- enabled: addressbar.text
535- opacity: enabled ? 1.0 : 0.3
536-
537- Icon {
538- id: actionIcon
539- height: parent.height - units.gu(2)
540+ width: iconsRow.width
541+ height: iconsRow.height
542+ Row {
543+ id: iconsRow
544+ Item {
545+ height: textField.height
546 width: height
547- anchors.centerIn: parent
548- name: {
549- switch (addressbar.state) {
550- case "loading":
551- return "stop"
552- case "editing":
553- if (addressbar.text && (addressbar.text == addressbar.actualUrl)) {
554- return "reload"
555- } else if (looksLikeAUrl(addressbar.text.trim())) {
556- return "stock_website"
557- } else {
558- return "search"
559- }
560- default:
561- if (!favicon.visible) {
562- if (looksLikeAUrl(addressbar.text.trim())) {
563- return "stock_website"
564- } else {
565- return "search"
566+ visible: securityStatus ? securityStatus.securityLevel != Oxide.SecurityStatus.SecurityLevelWarning || addressbar.state != "" : true
567+
568+ Favicon {
569+ id: favicon
570+ anchors.centerIn: parent
571+ visible: securityStatus ? (securityStatus.securityLevel != Oxide.SecurityStatus.SecurityLevelWarning) && (addressbar.state == "") && addressbar.actualUrl.toString() : (addressbar.state == "") && addressbar.actualUrl.toString()
572+ }
573+
574+ Item {
575+ id: certificatePopoverPositioner
576+ anchors.bottom: favicon.bottom
577+ anchors.horizontalCenter: favicon.horizontalCenter
578+ }
579+
580+ MouseArea {
581+ id: actionButton
582+ objectName: "actionButton"
583+ anchors.fill: parent
584+ enabled: addressbar.text
585+ opacity: enabled ? 1.0 : 0.3
586+
587+ Icon {
588+ id: actionIcon
589+ height: parent.height - units.gu(2)
590+ width: height
591+ anchors.centerIn: parent
592+ name: {
593+ switch (addressbar.state) {
594+ case "loading":
595+ return "stop"
596+ case "editing":
597+ if (addressbar.text && (addressbar.text == addressbar.actualUrl)) {
598+ return "reload"
599+ } else if (looksLikeAUrl(addressbar.text.trim())) {
600+ return "stock_website"
601+ } else {
602+ return "search"
603+ }
604+ default:
605+ if (!favicon.visible) {
606+ if (looksLikeAUrl(addressbar.text.trim())) {
607+ return "stock_website"
608+ } else {
609+ return "search"
610+ }
611+ } else {
612+ return ""
613+ }
614 }
615- } else {
616- return ""
617- }
618- }
619- }
620- }
621+ }
622+ }
623+
624+ onClicked: {
625+ switch (actionIcon.name) {
626+ case "":
627+ break;
628+ case "stop":
629+ addressbar.requestStop()
630+ break
631+ case "reload":
632+ addressbar.requestReload()
633+ break
634+ default:
635+ textField.accepted()
636+ }
637+ }
638+ }
639+
640+ }
641+
642+ Item {
643+ id: securityDisplay
644+ height: textField.height
645+ width: securityIcon.width
646+ visible: securityStatus ? (securityStatus.securityLevel == Oxide.SecurityStatus.SecurityLevelSecure || securityStatus.securityLevel == Oxide.SecurityStatus.SecurityLevelSecureEV || securityStatus.securityLevel == Oxide.SecurityStatus.SecurityLevelWarning) && addressbar.state == "" : false
647+
648+ Icon {
649+ id: securityIcon
650+ anchors.centerIn: parent
651+ height: parent.height - units.gu(2)
652+ width: height
653+ name: "network-secure"
654+ }
655+ }
656+
657+ Item {
658+ id: securityWarning
659+ height: textField.height
660+ width: warningIcon.width
661+ visible: securityStatus ? securityStatus.securityLevel == Oxide.SecurityStatus.SecurityLevelWarning && addressbar.state == "" : false
662+
663+ Icon {
664+ id: warningIcon
665+ anchors.centerIn: parent
666+ height: parent.height - units.gu(2)
667+ width: height
668+ name: "security-alert"
669+ }
670+ }
671+
672+ }
673+
674+ MouseArea {
675+ enabled: securityDisplay.visible && addressbar.state != "editing" && addressbar.state != "loading"
676+ anchors.fill: parent
677
678 onClicked: {
679- switch (actionIcon.name) {
680- case "":
681- break;
682- case "stop":
683- addressbar.requestStop()
684- break
685- case "reload":
686- addressbar.requestReload()
687- break
688- default:
689- textField.accepted()
690- }
691- }
692- }
693+ PopupUtils.open(certificatePopoverComponent, certificatePopoverPositioner)
694+ }
695+ }
696+
697+
698+ Component {
699+ id: certificatePopoverComponent
700+ Popover {
701+ id: certificatePopover
702+ Column {
703+ id: certificateDetails
704+ width: parent.width - units.gu(4)
705+ anchors.horizontalCenter: parent.horizontalCenter
706+ spacing: units.gu(0.5)
707+
708+ Item {
709+ height: units.gu(1.5)
710+ width: parent.width
711+ }
712+
713+ Column {
714+ width: parent.width
715+ visible: securityStatus.securityLevel == Oxide.SecurityStatus.SecurityLevelWarning
716+ spacing: units.gu(0.5)
717+
718+ Row {
719+ width: parent.width
720+ spacing: units.gu(0.5)
721+
722+ Icon {
723+ name: "security-alert"
724+ height: units.gu(2)
725+ width: height
726+ }
727+
728+ Label {
729+ width: parent.width
730+ wrapMode: Text.WordWrap
731+ text: i18n.tr("This site has insecure content")
732+ fontSize: "x-small"
733+ }
734+ }
735+
736+ ThinDivider {
737+ width: parent.width
738+ anchors.leftMargin: 0
739+ anchors.rightMargin: 0
740+ }
741+ }
742+
743+ Label {
744+ width: parent.width
745+ wrapMode: Text.WordWrap
746+ text: i18n.tr("You are connected to")
747+ fontSize: "x-small"
748+ }
749+
750+ Label {
751+ width: parent.width
752+ wrapMode: Text.WordWrap
753+ text: securityStatus.certificate.subjectDisplayName
754+ fontSize: "x-small"
755+ }
756+
757+ ThinDivider {
758+ width: parent.width
759+ anchors.leftMargin: 0
760+ anchors.rightMargin: 0
761+ visible: orgName.visible || localityName.visible || stateName.visible || countryName.visible
762+ }
763+
764+ Label {
765+ width: parent.width
766+ wrapMode: Text.WordWrap
767+ visible: orgName.visible
768+ text: i18n.tr("Which is run by")
769+ fontSize: "x-small"
770+ }
771+
772+ Label {
773+ id: orgName
774+ width: parent.width
775+ wrapMode: Text.WordWrap
776+ visible: text.length > 0
777+ text: securityStatus.certificate.getSubjectInfo(Oxide.SslCertificate.PrincipalAttrOrganizationName).join(", ")
778+ fontSize: "x-small"
779+ }
780+
781+ Label {
782+ id: localityName
783+ width: parent.width
784+ wrapMode: Text.WordWrap
785+ visible: text.length > 0
786+ text: securityStatus.certificate.getSubjectInfo(Oxide.SslCertificate.PrincipalAttrLocalityName).join(", ")
787+ fontSize: "x-small"
788+ }
789+
790+ Label {
791+ id: stateName
792+ width: parent.width
793+ wrapMode: Text.WordWrap
794+ visible: text.length > 0
795+ text: securityStatus.certificate.getSubjectInfo(Oxide.SslCertificate.PrincipalAttrStateOrProvinceName).join(", ")
796+ fontSize: "x-small"
797+ }
798+
799+ Label {
800+ id: countryName
801+ width: parent.width
802+ wrapMode: Text.WordWrap
803+ visible: text.length > 0
804+ text: securityStatus.certificate.getSubjectInfo(Oxide.SslCertificate.PrincipalAttrCountryName).join(", ")
805+ fontSize: "x-small"
806+ }
807+
808+ Item {
809+ height: units.gu(1.5)
810+ width: parent.width
811+ }
812+
813+ }
814+
815+ MouseArea {
816+ anchors.fill: parent
817+ onClicked: PopupUtils.close(certificatePopover)
818+ }
819+
820+ }
821+ }
822+
823 }
824
825 secondaryItem: Item {
826@@ -174,7 +360,7 @@
827 MouseArea {
828 anchors {
829 fill: parent
830- leftMargin: actionButton.width
831+ leftMargin: iconsRow.width
832 rightMargin: bookmarkButton.width
833 }
834 visible: !textField.activeFocus
835
836=== modified file 'src/app/webbrowser/Browser.qml'
837--- src/app/webbrowser/Browser.qml 2014-08-25 16:56:58 +0000
838+++ src/app/webbrowser/Browser.qml 2014-09-22 14:01:07 +0000
839@@ -121,6 +121,24 @@
840 asynchronous: true
841 }
842
843+ Loader {
844+ anchors.fill: tabContainer
845+ sourceComponent: InvalidCertificateErrorSheet {
846+ visible: currentWebview && currentWebview.certificateError != null
847+ certificateError: currentWebview ? currentWebview.certificateError : null
848+ onAllowed: {
849+ // Automatically allow future requests involving this
850+ // certificate for the duration of the session.
851+ currentWebview.allowedCertificates.push(currentWebview.certificateError.certificate.fingerprintSHA1)
852+ currentWebview.certificateError = null
853+ }
854+ onDenied: {
855+ currentWebview.certificateError = null
856+ }
857+ }
858+ asynchronous: true
859+ }
860+
861 Chrome {
862 id: chrome
863
864
865=== modified file 'src/app/webbrowser/Chrome.qml'
866--- src/app/webbrowser/Chrome.qml 2014-08-22 12:22:37 +0000
867+++ src/app/webbrowser/Chrome.qml 2014-09-22 14:01:07 +0000
868@@ -152,6 +152,7 @@
869 onWebviewChanged: {
870 if (webview) {
871 addressbar.actualUrl = webview.url
872+ addressbar.securityStatus = webview.securityStatus
873 }
874 }
875

Subscribers

People subscribed via source and target branches

to status/vote changes: