Bug 1685822 [wpt PR 27117] - [Import Maps] Add tests for rejecting multiple import...
[gecko.git] / dom / security / nsHTTPSOnlyStreamListener.cpp
blob0cc33cf161d452c17c9cf359c7f3df741b7d7618
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"
12 #include "nsCOMPtr.h"
13 #include "nsHTTPSOnlyStreamListener.h"
14 #include "nsHTTPSOnlyUtils.h"
15 #include "nsIChannel.h"
16 #include "nsIRequest.h"
17 #include "nsITransportSecurityInfo.h"
18 #include "nsIURI.h"
19 #include "nsPrintfCString.h"
20 #include "secerr.h"
21 #include "sslerr.h"
23 NS_IMPL_ISUPPORTS(nsHTTPSOnlyStreamListener, nsIStreamListener,
24 nsIRequestObserver)
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.
34 if (wgp) {
35 wgp->TopWindowContext()->AddSecurityState(
36 nsIWebProgressListener::STATE_HTTPS_ONLY_MODE_UPGRADED);
40 NS_IMETHODIMP
41 nsHTTPSOnlyStreamListener::OnDataAvailable(nsIRequest* aRequest,
42 nsIInputStream* aInputStream,
43 uint64_t aOffset, uint32_t aCount) {
44 return mListener->OnDataAvailable(aRequest, aInputStream, aOffset, aCount);
47 NS_IMETHODIMP
48 nsHTTPSOnlyStreamListener::OnStartRequest(nsIRequest* request) {
49 return mListener->OnStartRequest(request);
52 NS_IMETHODIMP
53 nsHTTPSOnlyStreamListener::OnStopRequest(nsIRequest* request,
54 nsresult aStatus) {
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());
73 if (wgp) {
74 wgp->TopWindowContext()->AddSecurityState(
75 nsIWebProgressListener::STATE_HTTPS_ONLY_MODE_UPGRADE_FAILED);
80 return mListener->OnStopRequest(request, aStatus);
83 void nsHTTPSOnlyStreamListener::RecordUpgradeTelemetry(nsIRequest* request,
84 nsresult aStatus) {
85 // 1. Get time between now and when the initial upgrade request started
86 int64_t duration =
87 (mozilla::TimeStamp::Now() - mCreationStart).ToMilliseconds();
89 // 2. Assemble the category string
90 // [!] All strings have to be present in Histograms.json
91 nsresult rv;
92 nsCOMPtr<nsIChannel> channel = do_QueryInterface(request, &rv);
93 if (NS_FAILED(rv)) {
94 return;
97 nsAutoCString category;
98 nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
99 nsContentPolicyType internalType = loadInfo->InternalContentPolicyType();
101 if (internalType == nsIContentPolicy::TYPE_DOCUMENT) {
102 category.AppendLiteral("top_");
103 } else {
104 category.AppendLiteral("sub_");
107 if (NS_SUCCEEDED(aStatus)) {
108 category.AppendLiteral("successful");
109 } else {
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)) {
121 switch (code) {
122 case mozilla::pkix::MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT:
123 category.AppendLiteral("f_ssl_selfsignd");
124 break;
125 case SSL_ERROR_BAD_CERT_DOMAIN:
126 category.AppendLiteral("f_ssl_badcertdm");
127 break;
128 case SEC_ERROR_UNKNOWN_ISSUER:
129 category.AppendLiteral("f_ssl_unkwnissr");
130 break;
131 default:
132 category.AppendLiteral("f_ssl_other");
133 break;
135 } else {
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;
151 break;
153 case nsIContentPolicy::TYPE_INTERNAL_VIDEO:
154 typeKey = "video"_ns;
155 break;
157 default:
158 MOZ_ASSERT_UNREACHABLE();
159 break;
161 } else {
162 switch (externalType) {
163 case ExtContentPolicy::TYPE_SCRIPT:
164 typeKey = "script"_ns;
165 break;
167 case ExtContentPolicy::TYPE_OBJECT:
168 case ExtContentPolicy::TYPE_OBJECT_SUBREQUEST:
169 typeKey = "object"_ns;
170 break;
172 case ExtContentPolicy::TYPE_DOCUMENT:
173 typeKey = "document"_ns;
174 break;
176 case ExtContentPolicy::TYPE_SUBDOCUMENT:
177 typeKey = "subdocument"_ns;
178 break;
180 case ExtContentPolicy::TYPE_XMLHTTPREQUEST:
181 typeKey = "xmlhttprequest"_ns;
182 break;
184 case ExtContentPolicy::TYPE_IMAGE:
185 case ExtContentPolicy::TYPE_IMAGESET:
186 typeKey = "image"_ns;
187 break;
189 case ExtContentPolicy::TYPE_DTD:
190 typeKey = "dtd"_ns;
191 break;
193 case ExtContentPolicy::TYPE_FONT:
194 typeKey = "font"_ns;
195 break;
197 case ExtContentPolicy::TYPE_FETCH:
198 typeKey = "fetch"_ns;
199 break;
201 case ExtContentPolicy::TYPE_WEBSOCKET:
202 typeKey = "websocket"_ns;
203 break;
205 case ExtContentPolicy::TYPE_STYLESHEET:
206 typeKey = "stylesheet"_ns;
207 break;
209 case ExtContentPolicy::TYPE_CSP_REPORT:
210 typeKey = "cspreport"_ns;
211 break;
213 case ExtContentPolicy::TYPE_WEB_MANIFEST:
214 typeKey = "webmanifest"_ns;
215 break;
217 case ExtContentPolicy::TYPE_PING:
218 typeKey = "ping"_ns;
219 break;
221 case ExtContentPolicy::TYPE_REFRESH:
222 typeKey = "refresh"_ns;
223 break;
225 case ExtContentPolicy::TYPE_XSLT:
226 typeKey = "xslt"_ns;
227 break;
229 default:
230 break;
234 mozilla::Telemetry::Accumulate(
235 mozilla::Telemetry::HTTPS_ONLY_MODE_UPGRADE_TYPE, typeKey, success);
238 void nsHTTPSOnlyStreamListener::LogUpgradeFailure(nsIRequest* request,
239 nsresult aStatus) {
240 // If the request failed we'll log it to the console with the error-code
241 if (NS_SUCCEEDED(aStatus)) {
242 return;
244 nsresult rv;
245 // Try to query for the channel-object
246 nsCOMPtr<nsIChannel> channel = do_QueryInterface(request, &rv);
247 if (NS_FAILED(rv)) {
248 return;
251 nsCOMPtr<nsIURI> uri;
252 rv = channel->GetURI(getter_AddRefs(uri));
253 if (NS_FAILED(rv)) {
254 return;
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,
266 uri);