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 "nsIWebProgressListener.h"
20 #include "nsPrintfCString.h"
24 NS_IMPL_ISUPPORTS(nsHTTPSOnlyStreamListener
, nsIStreamListener
,
27 nsHTTPSOnlyStreamListener::nsHTTPSOnlyStreamListener(
28 nsIStreamListener
* aListener
, nsILoadInfo
* aLoadInfo
)
29 : mListener(aListener
), mCreationStart(mozilla::TimeStamp::Now()) {
30 RefPtr
<mozilla::dom::WindowGlobalParent
> wgp
=
31 mozilla::dom::WindowGlobalParent::GetByInnerWindowId(
32 aLoadInfo
->GetInnerWindowID());
33 // For Top-level document loads (which don't have a requesting window-context)
34 // we compute these flags once we create the Document in nsSecureBrowserUI.
36 wgp
->TopWindowContext()->AddSecurityState(
37 nsIWebProgressListener::STATE_HTTPS_ONLY_MODE_UPGRADED
);
42 nsHTTPSOnlyStreamListener::OnDataAvailable(nsIRequest
* aRequest
,
43 nsIInputStream
* aInputStream
,
44 uint64_t aOffset
, uint32_t aCount
) {
45 return mListener
->OnDataAvailable(aRequest
, aInputStream
, aOffset
, aCount
);
49 nsHTTPSOnlyStreamListener::OnStartRequest(nsIRequest
* request
) {
50 return mListener
->OnStartRequest(request
);
54 nsHTTPSOnlyStreamListener::OnStopRequest(nsIRequest
* request
,
56 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(request
);
58 // Note: CouldBeHttpsOnlyError also returns true if there was no error
59 if (nsHTTPSOnlyUtils::CouldBeHttpsOnlyError(channel
, aStatus
)) {
60 RecordUpgradeTelemetry(request
, aStatus
);
61 LogUpgradeFailure(request
, aStatus
);
63 // If the request failed and there is a requesting window-context, set
64 // HTTPS-Only state flag to indicate a failed upgrade.
65 // For Top-level document loads (which don't have a requesting
66 // window-context) we simply check in the UI code whether we landed on the
67 // HTTPS-Only error page.
68 if (NS_FAILED(aStatus
)) {
69 nsCOMPtr
<nsILoadInfo
> loadInfo
= channel
->LoadInfo();
70 RefPtr
<mozilla::dom::WindowGlobalParent
> wgp
=
71 mozilla::dom::WindowGlobalParent::GetByInnerWindowId(
72 loadInfo
->GetInnerWindowID());
75 wgp
->TopWindowContext()->AddSecurityState(
76 nsIWebProgressListener::STATE_HTTPS_ONLY_MODE_UPGRADE_FAILED
);
81 return mListener
->OnStopRequest(request
, aStatus
);
84 void nsHTTPSOnlyStreamListener::RecordUpgradeTelemetry(nsIRequest
* request
,
86 // 1. Get time between now and when the initial upgrade request started
88 (mozilla::TimeStamp::Now() - mCreationStart
).ToMilliseconds();
90 // 2. Assemble the category string
91 // [!] All strings have to be present in Histograms.json
93 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(request
, &rv
);
98 nsAutoCString category
;
99 nsCOMPtr
<nsILoadInfo
> loadInfo
= channel
->LoadInfo();
100 nsContentPolicyType internalType
= loadInfo
->InternalContentPolicyType();
102 if (internalType
== nsIContentPolicy::TYPE_DOCUMENT
) {
103 category
.AppendLiteral("top_");
105 category
.AppendLiteral("sub_");
108 if (NS_SUCCEEDED(aStatus
)) {
109 category
.AppendLiteral("successful");
111 int32_t code
= -1 * NS_ERROR_GET_CODE(aStatus
);
113 if (aStatus
== NS_ERROR_REDIRECT_LOOP
) {
114 category
.AppendLiteral("f_redirectloop");
115 } else if (aStatus
== NS_ERROR_NET_TIMEOUT
||
116 aStatus
== NS_ERROR_NET_TIMEOUT_EXTERNAL
) {
117 category
.AppendLiteral("f_timeout");
118 } else if (aStatus
== NS_BINDING_ABORTED
) {
119 category
.AppendLiteral("f_aborted");
120 } else if (aStatus
== NS_ERROR_CONNECTION_REFUSED
) {
121 category
.AppendLiteral("f_cxnrefused");
122 } else if (mozilla::psm::IsNSSErrorCode(code
)) {
124 case mozilla::pkix::MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT
:
125 category
.AppendLiteral("f_ssl_selfsignd");
127 case SSL_ERROR_BAD_CERT_DOMAIN
:
128 category
.AppendLiteral("f_ssl_badcertdm");
130 case SEC_ERROR_UNKNOWN_ISSUER
:
131 category
.AppendLiteral("f_ssl_unkwnissr");
134 category
.AppendLiteral("f_ssl_other");
138 category
.AppendLiteral("f_other");
141 mozilla::Telemetry::Accumulate(
142 mozilla::Telemetry::HTTPS_ONLY_MODE_UPGRADE_TIME_MS
, category
, duration
);
144 bool success
= NS_SUCCEEDED(aStatus
);
145 ExtContentPolicyType externalType
= loadInfo
->GetExternalContentPolicyType();
146 auto typeKey
= nsAutoCString("unknown");
148 if (externalType
== ExtContentPolicy::TYPE_MEDIA
) {
149 switch (internalType
) {
150 case nsIContentPolicy::TYPE_INTERNAL_AUDIO
:
151 case nsIContentPolicy::TYPE_INTERNAL_TRACK
:
152 typeKey
= "audio"_ns
;
155 case nsIContentPolicy::TYPE_INTERNAL_VIDEO
:
156 typeKey
= "video"_ns
;
160 MOZ_ASSERT_UNREACHABLE();
164 switch (externalType
) {
165 case ExtContentPolicy::TYPE_SCRIPT
:
166 typeKey
= "script"_ns
;
169 case ExtContentPolicy::TYPE_OBJECT
:
170 case ExtContentPolicy::TYPE_OBJECT_SUBREQUEST
:
171 typeKey
= "object"_ns
;
174 case ExtContentPolicy::TYPE_DOCUMENT
:
175 typeKey
= "document"_ns
;
178 case ExtContentPolicy::TYPE_SUBDOCUMENT
:
179 typeKey
= "subdocument"_ns
;
182 case ExtContentPolicy::TYPE_XMLHTTPREQUEST
:
183 typeKey
= "xmlhttprequest"_ns
;
186 case ExtContentPolicy::TYPE_IMAGE
:
187 case ExtContentPolicy::TYPE_IMAGESET
:
188 typeKey
= "image"_ns
;
191 case ExtContentPolicy::TYPE_DTD
:
195 case ExtContentPolicy::TYPE_FONT
:
196 case ExtContentPolicy::TYPE_UA_FONT
:
200 case ExtContentPolicy::TYPE_FETCH
:
201 typeKey
= "fetch"_ns
;
204 case ExtContentPolicy::TYPE_WEBSOCKET
:
205 typeKey
= "websocket"_ns
;
208 case ExtContentPolicy::TYPE_STYLESHEET
:
209 typeKey
= "stylesheet"_ns
;
212 case ExtContentPolicy::TYPE_CSP_REPORT
:
213 typeKey
= "cspreport"_ns
;
216 case ExtContentPolicy::TYPE_WEB_MANIFEST
:
217 typeKey
= "webmanifest"_ns
;
220 case ExtContentPolicy::TYPE_PING
:
224 case ExtContentPolicy::TYPE_XSLT
:
228 case ExtContentPolicy::TYPE_PROXIED_WEBRTC_MEDIA
:
229 typeKey
= "proxied-webrtc"_ns
;
232 case ExtContentPolicy::TYPE_INVALID
:
233 case ExtContentPolicy::TYPE_OTHER
:
234 case ExtContentPolicy::TYPE_MEDIA
: // already handled above
235 case ExtContentPolicy::TYPE_BEACON
:
236 case ExtContentPolicy::TYPE_SAVEAS_DOWNLOAD
:
237 case ExtContentPolicy::TYPE_SPECULATIVE
:
238 case ExtContentPolicy::TYPE_WEB_TRANSPORT
:
239 case ExtContentPolicy::TYPE_WEB_IDENTITY
:
241 // Do not add default: so that compilers can catch the missing case.
245 mozilla::Telemetry::Accumulate(
246 mozilla::Telemetry::HTTPS_ONLY_MODE_UPGRADE_TYPE
, typeKey
, success
);
249 void nsHTTPSOnlyStreamListener::LogUpgradeFailure(nsIRequest
* request
,
251 // If the request failed we'll log it to the console with the error-code
252 if (NS_SUCCEEDED(aStatus
)) {
256 // Try to query for the channel-object
257 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(request
, &rv
);
262 nsCOMPtr
<nsIURI
> uri
;
263 rv
= channel
->GetURI(getter_AddRefs(uri
));
267 // Logging URI as well as Module- and Error-Code
268 AutoTArray
<nsString
, 2> params
= {
269 NS_ConvertUTF8toUTF16(uri
->GetSpecOrDefault()),
270 NS_ConvertUTF8toUTF16(nsPrintfCString("M%u-C%u",
271 NS_ERROR_GET_MODULE(aStatus
),
272 NS_ERROR_GET_CODE(aStatus
)))};
274 nsCOMPtr
<nsILoadInfo
> loadInfo
= channel
->LoadInfo();
275 nsHTTPSOnlyUtils::LogLocalizedString("HTTPSOnlyFailedRequest", params
,
276 nsIScriptError::errorFlag
, loadInfo
,