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 category
.AppendLiteral("f_timeout");
116 } else if (aStatus
== NS_BINDING_ABORTED
) {
117 category
.AppendLiteral("f_aborted");
118 } else if (aStatus
== NS_ERROR_CONNECTION_REFUSED
) {
119 category
.AppendLiteral("f_cxnrefused");
120 } else if (mozilla::psm::IsNSSErrorCode(code
)) {
122 case mozilla::pkix::MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT
:
123 category
.AppendLiteral("f_ssl_selfsignd");
125 case SSL_ERROR_BAD_CERT_DOMAIN
:
126 category
.AppendLiteral("f_ssl_badcertdm");
128 case SEC_ERROR_UNKNOWN_ISSUER
:
129 category
.AppendLiteral("f_ssl_unkwnissr");
132 category
.AppendLiteral("f_ssl_other");
136 category
.AppendLiteral("f_other");
139 mozilla::Telemetry::Accumulate(
140 mozilla::Telemetry::HTTPS_ONLY_MODE_UPGRADE_TIME_MS
, category
, duration
);
142 bool success
= NS_SUCCEEDED(aStatus
);
143 ExtContentPolicyType externalType
= loadInfo
->GetExternalContentPolicyType();
144 auto typeKey
= nsAutoCString("unknown");
146 if (externalType
== ExtContentPolicy::TYPE_MEDIA
) {
147 switch (internalType
) {
148 case nsIContentPolicy::TYPE_INTERNAL_AUDIO
:
149 case nsIContentPolicy::TYPE_INTERNAL_TRACK
:
150 typeKey
= "audio"_ns
;
153 case nsIContentPolicy::TYPE_INTERNAL_VIDEO
:
154 typeKey
= "video"_ns
;
158 MOZ_ASSERT_UNREACHABLE();
162 switch (externalType
) {
163 case ExtContentPolicy::TYPE_SCRIPT
:
164 typeKey
= "script"_ns
;
167 case ExtContentPolicy::TYPE_OBJECT
:
168 case ExtContentPolicy::TYPE_OBJECT_SUBREQUEST
:
169 typeKey
= "object"_ns
;
172 case ExtContentPolicy::TYPE_DOCUMENT
:
173 typeKey
= "document"_ns
;
176 case ExtContentPolicy::TYPE_SUBDOCUMENT
:
177 typeKey
= "subdocument"_ns
;
180 case ExtContentPolicy::TYPE_XMLHTTPREQUEST
:
181 typeKey
= "xmlhttprequest"_ns
;
184 case ExtContentPolicy::TYPE_IMAGE
:
185 case ExtContentPolicy::TYPE_IMAGESET
:
186 typeKey
= "image"_ns
;
189 case ExtContentPolicy::TYPE_DTD
:
193 case ExtContentPolicy::TYPE_FONT
:
197 case ExtContentPolicy::TYPE_FETCH
:
198 typeKey
= "fetch"_ns
;
201 case ExtContentPolicy::TYPE_WEBSOCKET
:
202 typeKey
= "websocket"_ns
;
205 case ExtContentPolicy::TYPE_STYLESHEET
:
206 typeKey
= "stylesheet"_ns
;
209 case ExtContentPolicy::TYPE_CSP_REPORT
:
210 typeKey
= "cspreport"_ns
;
213 case ExtContentPolicy::TYPE_WEB_MANIFEST
:
214 typeKey
= "webmanifest"_ns
;
217 case ExtContentPolicy::TYPE_PING
:
221 case ExtContentPolicy::TYPE_REFRESH
:
222 typeKey
= "refresh"_ns
;
225 case ExtContentPolicy::TYPE_XSLT
:
234 mozilla::Telemetry::Accumulate(
235 mozilla::Telemetry::HTTPS_ONLY_MODE_UPGRADE_TYPE
, typeKey
, success
);
238 void nsHTTPSOnlyStreamListener::LogUpgradeFailure(nsIRequest
* request
,
240 // If the request failed we'll log it to the console with the error-code
241 if (NS_SUCCEEDED(aStatus
)) {
245 // Try to query for the channel-object
246 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(request
, &rv
);
251 nsCOMPtr
<nsIURI
> uri
;
252 rv
= channel
->GetURI(getter_AddRefs(uri
));
256 // Logging URI as well as Module- and Error-Code
257 AutoTArray
<nsString
, 2> params
= {
258 NS_ConvertUTF8toUTF16(uri
->GetSpecOrDefault()),
259 NS_ConvertUTF8toUTF16(nsPrintfCString("M%u-C%u",
260 NS_ERROR_GET_MODULE(aStatus
),
261 NS_ERROR_GET_CODE(aStatus
)))};
263 nsCOMPtr
<nsILoadInfo
> loadInfo
= channel
->LoadInfo();
264 nsHTTPSOnlyUtils::LogLocalizedString("HTTPSOnlyFailedRequest", params
,
265 nsIScriptError::errorFlag
, loadInfo
,