Merge mozilla-central to autoland. a=merge CLOSED TREE
[gecko.git] / dom / security / nsHTTPSOnlyStreamListener.cpp
blobe6026e5e90869f37169eb7b38d55e9aa52aef4f9
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 "nsIWebProgressListener.h"
20 #include "nsPrintfCString.h"
21 #include "secerr.h"
22 #include "sslerr.h"
24 NS_IMPL_ISUPPORTS(nsHTTPSOnlyStreamListener, nsIStreamListener,
25 nsIRequestObserver)
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.
35 if (wgp) {
36 wgp->TopWindowContext()->AddSecurityState(
37 nsIWebProgressListener::STATE_HTTPS_ONLY_MODE_UPGRADED);
41 NS_IMETHODIMP
42 nsHTTPSOnlyStreamListener::OnDataAvailable(nsIRequest* aRequest,
43 nsIInputStream* aInputStream,
44 uint64_t aOffset, uint32_t aCount) {
45 return mListener->OnDataAvailable(aRequest, aInputStream, aOffset, aCount);
48 NS_IMETHODIMP
49 nsHTTPSOnlyStreamListener::OnStartRequest(nsIRequest* request) {
50 return mListener->OnStartRequest(request);
53 NS_IMETHODIMP
54 nsHTTPSOnlyStreamListener::OnStopRequest(nsIRequest* request,
55 nsresult aStatus) {
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());
74 if (wgp) {
75 wgp->TopWindowContext()->AddSecurityState(
76 nsIWebProgressListener::STATE_HTTPS_ONLY_MODE_UPGRADE_FAILED);
81 return mListener->OnStopRequest(request, aStatus);
84 void nsHTTPSOnlyStreamListener::RecordUpgradeTelemetry(nsIRequest* request,
85 nsresult aStatus) {
86 // 1. Get time between now and when the initial upgrade request started
87 int64_t duration =
88 (mozilla::TimeStamp::Now() - mCreationStart).ToMilliseconds();
90 // 2. Assemble the category string
91 // [!] All strings have to be present in Histograms.json
92 nsresult rv;
93 nsCOMPtr<nsIChannel> channel = do_QueryInterface(request, &rv);
94 if (NS_FAILED(rv)) {
95 return;
98 nsAutoCString category;
99 nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
100 nsContentPolicyType internalType = loadInfo->InternalContentPolicyType();
102 if (internalType == nsIContentPolicy::TYPE_DOCUMENT) {
103 category.AppendLiteral("top_");
104 } else {
105 category.AppendLiteral("sub_");
108 if (NS_SUCCEEDED(aStatus)) {
109 category.AppendLiteral("successful");
110 } else {
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)) {
123 switch (code) {
124 case mozilla::pkix::MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT:
125 category.AppendLiteral("f_ssl_selfsignd");
126 break;
127 case SSL_ERROR_BAD_CERT_DOMAIN:
128 category.AppendLiteral("f_ssl_badcertdm");
129 break;
130 case SEC_ERROR_UNKNOWN_ISSUER:
131 category.AppendLiteral("f_ssl_unkwnissr");
132 break;
133 default:
134 category.AppendLiteral("f_ssl_other");
135 break;
137 } else {
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;
153 break;
155 case nsIContentPolicy::TYPE_INTERNAL_VIDEO:
156 typeKey = "video"_ns;
157 break;
159 default:
160 MOZ_ASSERT_UNREACHABLE();
161 break;
163 } else {
164 switch (externalType) {
165 case ExtContentPolicy::TYPE_SCRIPT:
166 typeKey = "script"_ns;
167 break;
169 case ExtContentPolicy::TYPE_OBJECT:
170 case ExtContentPolicy::TYPE_OBJECT_SUBREQUEST:
171 typeKey = "object"_ns;
172 break;
174 case ExtContentPolicy::TYPE_DOCUMENT:
175 typeKey = "document"_ns;
176 break;
178 case ExtContentPolicy::TYPE_SUBDOCUMENT:
179 typeKey = "subdocument"_ns;
180 break;
182 case ExtContentPolicy::TYPE_XMLHTTPREQUEST:
183 typeKey = "xmlhttprequest"_ns;
184 break;
186 case ExtContentPolicy::TYPE_IMAGE:
187 case ExtContentPolicy::TYPE_IMAGESET:
188 typeKey = "image"_ns;
189 break;
191 case ExtContentPolicy::TYPE_DTD:
192 typeKey = "dtd"_ns;
193 break;
195 case ExtContentPolicy::TYPE_FONT:
196 case ExtContentPolicy::TYPE_UA_FONT:
197 typeKey = "font"_ns;
198 break;
200 case ExtContentPolicy::TYPE_FETCH:
201 typeKey = "fetch"_ns;
202 break;
204 case ExtContentPolicy::TYPE_WEBSOCKET:
205 typeKey = "websocket"_ns;
206 break;
208 case ExtContentPolicy::TYPE_STYLESHEET:
209 typeKey = "stylesheet"_ns;
210 break;
212 case ExtContentPolicy::TYPE_CSP_REPORT:
213 typeKey = "cspreport"_ns;
214 break;
216 case ExtContentPolicy::TYPE_WEB_MANIFEST:
217 typeKey = "webmanifest"_ns;
218 break;
220 case ExtContentPolicy::TYPE_PING:
221 typeKey = "ping"_ns;
222 break;
224 case ExtContentPolicy::TYPE_XSLT:
225 typeKey = "xslt"_ns;
226 break;
228 case ExtContentPolicy::TYPE_PROXIED_WEBRTC_MEDIA:
229 typeKey = "proxied-webrtc"_ns;
230 break;
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:
240 break;
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,
250 nsresult aStatus) {
251 // If the request failed we'll log it to the console with the error-code
252 if (NS_SUCCEEDED(aStatus)) {
253 return;
255 nsresult rv;
256 // Try to query for the channel-object
257 nsCOMPtr<nsIChannel> channel = do_QueryInterface(request, &rv);
258 if (NS_FAILED(rv)) {
259 return;
262 nsCOMPtr<nsIURI> uri;
263 rv = channel->GetURI(getter_AddRefs(uri));
264 if (NS_FAILED(rv)) {
265 return;
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,
277 uri);