1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "NSSErrorsService.h"
8 #include "mozilla/Telemetry.h"
9 #include "mozilla/TimeStamp.h"
10 #include "mozilla/dom/WindowGlobalParent.h"
11 #include "mozpkix/pkixnss.h"
13 #include "nsHTTPSOnlyStreamListener.h"
14 #include "nsHTTPSOnlyUtils.h"
15 #include "nsIChannel.h"
16 #include "nsIRequest.h"
17 #include "nsITransportSecurityInfo.h"
19 #include "nsPrintfCString.h"
23 NS_IMPL_ISUPPORTS(nsHTTPSOnlyStreamListener
, nsIStreamListener
,
26 nsHTTPSOnlyStreamListener::nsHTTPSOnlyStreamListener(
27 nsIStreamListener
* aListener
, nsILoadInfo
* aLoadInfo
)
28 : mListener(aListener
), mCreationStart(mozilla::TimeStamp::Now()) {
29 RefPtr
<mozilla::dom::WindowGlobalParent
> wgp
=
30 mozilla::dom::WindowGlobalParent::GetByInnerWindowId(
31 aLoadInfo
->GetInnerWindowID());
32 // For Top-level document loads (which don't have a requesting window-context)
33 // we compute these flags once we create the Document in nsSecureBrowserUI.
35 wgp
->TopWindowContext()->AddSecurityState(
36 nsIWebProgressListener::STATE_HTTPS_ONLY_MODE_UPGRADED
);
41 nsHTTPSOnlyStreamListener::OnDataAvailable(nsIRequest
* aRequest
,
42 nsIInputStream
* aInputStream
,
43 uint64_t aOffset
, uint32_t aCount
) {
44 return mListener
->OnDataAvailable(aRequest
, aInputStream
, aOffset
, aCount
);
48 nsHTTPSOnlyStreamListener::OnStartRequest(nsIRequest
* request
) {
49 return mListener
->OnStartRequest(request
);
53 nsHTTPSOnlyStreamListener::OnStopRequest(nsIRequest
* request
,
55 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(request
);
57 // Note: CouldBeHttpsOnlyError also returns true if there was no error
58 if (nsHTTPSOnlyUtils::CouldBeHttpsOnlyError(channel
, aStatus
)) {
59 RecordUpgradeTelemetry(request
, aStatus
);
60 LogUpgradeFailure(request
, aStatus
);
62 // If the request failed and there is a requesting window-context, set
63 // HTTPS-Only state flag to indicate a failed upgrade.
64 // For Top-level document loads (which don't have a requesting
65 // window-context) we simply check in the UI code whether we landed on the
66 // HTTPS-Only error page.
67 if (NS_FAILED(aStatus
)) {
68 nsCOMPtr
<nsILoadInfo
> loadInfo
= channel
->LoadInfo();
69 RefPtr
<mozilla::dom::WindowGlobalParent
> wgp
=
70 mozilla::dom::WindowGlobalParent::GetByInnerWindowId(
71 loadInfo
->GetInnerWindowID());
74 wgp
->TopWindowContext()->AddSecurityState(
75 nsIWebProgressListener::STATE_HTTPS_ONLY_MODE_UPGRADE_FAILED
);
80 return mListener
->OnStopRequest(request
, aStatus
);
83 void nsHTTPSOnlyStreamListener::RecordUpgradeTelemetry(nsIRequest
* request
,
85 // 1. Get time between now and when the initial upgrade request started
87 (mozilla::TimeStamp::Now() - mCreationStart
).ToMilliseconds();
89 // 2. Assemble the category string
90 // [!] All strings have to be present in Histograms.json
92 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(request
, &rv
);
97 nsAutoCString category
;
98 nsCOMPtr
<nsILoadInfo
> loadInfo
= channel
->LoadInfo();
99 nsContentPolicyType internalType
= loadInfo
->InternalContentPolicyType();
101 if (internalType
== nsIContentPolicy::TYPE_DOCUMENT
) {
102 category
.AppendLiteral("top_");
104 category
.AppendLiteral("sub_");
107 if (NS_SUCCEEDED(aStatus
)) {
108 category
.AppendLiteral("successful");
110 int32_t code
= -1 * NS_ERROR_GET_CODE(aStatus
);
112 if (aStatus
== NS_ERROR_REDIRECT_LOOP
) {
113 category
.AppendLiteral("f_redirectloop");
114 } else if (aStatus
== NS_ERROR_NET_TIMEOUT
||
115 aStatus
== NS_ERROR_NET_TIMEOUT_EXTERNAL
) {
116 category
.AppendLiteral("f_timeout");
117 } else if (aStatus
== NS_BINDING_ABORTED
) {
118 category
.AppendLiteral("f_aborted");
119 } else if (aStatus
== NS_ERROR_CONNECTION_REFUSED
) {
120 category
.AppendLiteral("f_cxnrefused");
121 } else if (mozilla::psm::IsNSSErrorCode(code
)) {
123 case mozilla::pkix::MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT
:
124 category
.AppendLiteral("f_ssl_selfsignd");
126 case SSL_ERROR_BAD_CERT_DOMAIN
:
127 category
.AppendLiteral("f_ssl_badcertdm");
129 case SEC_ERROR_UNKNOWN_ISSUER
:
130 category
.AppendLiteral("f_ssl_unkwnissr");
133 category
.AppendLiteral("f_ssl_other");
137 category
.AppendLiteral("f_other");
140 mozilla::Telemetry::Accumulate(
141 mozilla::Telemetry::HTTPS_ONLY_MODE_UPGRADE_TIME_MS
, category
, duration
);
143 bool success
= NS_SUCCEEDED(aStatus
);
144 ExtContentPolicyType externalType
= loadInfo
->GetExternalContentPolicyType();
145 auto typeKey
= nsAutoCString("unknown");
147 if (externalType
== ExtContentPolicy::TYPE_MEDIA
) {
148 switch (internalType
) {
149 case nsIContentPolicy::TYPE_INTERNAL_AUDIO
:
150 case nsIContentPolicy::TYPE_INTERNAL_TRACK
:
151 typeKey
= "audio"_ns
;
154 case nsIContentPolicy::TYPE_INTERNAL_VIDEO
:
155 typeKey
= "video"_ns
;
159 MOZ_ASSERT_UNREACHABLE();
163 switch (externalType
) {
164 case ExtContentPolicy::TYPE_SCRIPT
:
165 typeKey
= "script"_ns
;
168 case ExtContentPolicy::TYPE_OBJECT
:
169 case ExtContentPolicy::TYPE_OBJECT_SUBREQUEST
:
170 typeKey
= "object"_ns
;
173 case ExtContentPolicy::TYPE_DOCUMENT
:
174 typeKey
= "document"_ns
;
177 case ExtContentPolicy::TYPE_SUBDOCUMENT
:
178 typeKey
= "subdocument"_ns
;
181 case ExtContentPolicy::TYPE_XMLHTTPREQUEST
:
182 typeKey
= "xmlhttprequest"_ns
;
185 case ExtContentPolicy::TYPE_IMAGE
:
186 case ExtContentPolicy::TYPE_IMAGESET
:
187 typeKey
= "image"_ns
;
190 case ExtContentPolicy::TYPE_DTD
:
194 case ExtContentPolicy::TYPE_FONT
:
195 case ExtContentPolicy::TYPE_UA_FONT
:
199 case ExtContentPolicy::TYPE_FETCH
:
200 typeKey
= "fetch"_ns
;
203 case ExtContentPolicy::TYPE_WEBSOCKET
:
204 typeKey
= "websocket"_ns
;
207 case ExtContentPolicy::TYPE_STYLESHEET
:
208 typeKey
= "stylesheet"_ns
;
211 case ExtContentPolicy::TYPE_CSP_REPORT
:
212 typeKey
= "cspreport"_ns
;
215 case ExtContentPolicy::TYPE_WEB_MANIFEST
:
216 typeKey
= "webmanifest"_ns
;
219 case ExtContentPolicy::TYPE_PING
:
223 case ExtContentPolicy::TYPE_XSLT
:
227 case ExtContentPolicy::TYPE_PROXIED_WEBRTC_MEDIA
:
228 typeKey
= "proxied-webrtc"_ns
;
231 case ExtContentPolicy::TYPE_INVALID
:
232 case ExtContentPolicy::TYPE_OTHER
:
233 case ExtContentPolicy::TYPE_MEDIA
: // already handled above
234 case ExtContentPolicy::TYPE_BEACON
:
235 case ExtContentPolicy::TYPE_SAVEAS_DOWNLOAD
:
236 case ExtContentPolicy::TYPE_SPECULATIVE
:
238 // Do not add default: so that compilers can catch the missing case.
242 mozilla::Telemetry::Accumulate(
243 mozilla::Telemetry::HTTPS_ONLY_MODE_UPGRADE_TYPE
, typeKey
, success
);
246 void nsHTTPSOnlyStreamListener::LogUpgradeFailure(nsIRequest
* request
,
248 // If the request failed we'll log it to the console with the error-code
249 if (NS_SUCCEEDED(aStatus
)) {
253 // Try to query for the channel-object
254 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(request
, &rv
);
259 nsCOMPtr
<nsIURI
> uri
;
260 rv
= channel
->GetURI(getter_AddRefs(uri
));
264 // Logging URI as well as Module- and Error-Code
265 AutoTArray
<nsString
, 2> params
= {
266 NS_ConvertUTF8toUTF16(uri
->GetSpecOrDefault()),
267 NS_ConvertUTF8toUTF16(nsPrintfCString("M%u-C%u",
268 NS_ERROR_GET_MODULE(aStatus
),
269 NS_ERROR_GET_CODE(aStatus
)))};
271 nsCOMPtr
<nsILoadInfo
> loadInfo
= channel
->LoadInfo();
272 nsHTTPSOnlyUtils::LogLocalizedString("HTTPSOnlyFailedRequest", params
,
273 nsIScriptError::errorFlag
, loadInfo
,