Bug 785860 - fix sts preload list tests to skip private mode tests if private browsin...
[gecko.git] / netwerk / base / src / nsChannelClassifier.cpp
blob4a2e6db23d36585b8ff01382a5ac1384754068f2
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "nsChannelClassifier.h"
7 #include "nsNetUtil.h"
8 #include "nsIChannel.h"
9 #include "nsIProtocolHandler.h"
10 #include "nsICachingChannel.h"
11 #include "nsICacheEntryDescriptor.h"
12 #include "prlog.h"
13 #include "nsIScriptSecurityManager.h"
15 #if defined(PR_LOGGING)
17 // NSPR_LOG_MODULES=nsChannelClassifier:5
19 static PRLogModuleInfo *gChannelClassifierLog;
20 #endif
21 #define LOG(args) PR_LOG(gChannelClassifierLog, PR_LOG_DEBUG, args)
23 NS_IMPL_ISUPPORTS1(nsChannelClassifier,
24 nsIURIClassifierCallback)
26 nsChannelClassifier::nsChannelClassifier()
28 #if defined(PR_LOGGING)
29 if (!gChannelClassifierLog)
30 gChannelClassifierLog = PR_NewLogModule("nsChannelClassifier");
31 #endif
34 nsresult
35 nsChannelClassifier::Start(nsIChannel *aChannel)
37 // Don't bother to run the classifier on a load that has already failed.
38 // (this might happen after a redirect)
39 nsresult status;
40 aChannel->GetStatus(&status);
41 if (NS_FAILED(status))
42 return NS_OK;
44 // Don't bother to run the classifier on a cached load that was
45 // previously classified.
46 if (HasBeenClassified(aChannel)) {
47 return NS_OK;
50 nsCOMPtr<nsIURI> uri;
51 nsresult rv = aChannel->GetURI(getter_AddRefs(uri));
52 NS_ENSURE_SUCCESS(rv, rv);
54 // Don't bother checking certain types of URIs.
55 bool hasFlags;
56 rv = NS_URIChainHasFlags(uri,
57 nsIProtocolHandler::URI_DANGEROUS_TO_LOAD,
58 &hasFlags);
59 NS_ENSURE_SUCCESS(rv, rv);
60 if (hasFlags) return NS_OK;
62 rv = NS_URIChainHasFlags(uri,
63 nsIProtocolHandler::URI_IS_LOCAL_FILE,
64 &hasFlags);
65 NS_ENSURE_SUCCESS(rv, rv);
66 if (hasFlags) return NS_OK;
68 rv = NS_URIChainHasFlags(uri,
69 nsIProtocolHandler::URI_IS_UI_RESOURCE,
70 &hasFlags);
71 NS_ENSURE_SUCCESS(rv, rv);
72 if (hasFlags) return NS_OK;
74 rv = NS_URIChainHasFlags(uri,
75 nsIProtocolHandler::URI_IS_LOCAL_RESOURCE,
76 &hasFlags);
77 NS_ENSURE_SUCCESS(rv, rv);
78 if (hasFlags) return NS_OK;
80 nsCOMPtr<nsIURIClassifier> uriClassifier =
81 do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID, &rv);
82 if (rv == NS_ERROR_FACTORY_NOT_REGISTERED ||
83 rv == NS_ERROR_NOT_AVAILABLE) {
84 // no URI classifier, ignore this failure.
85 return NS_OK;
87 NS_ENSURE_SUCCESS(rv, rv);
89 nsCOMPtr<nsIScriptSecurityManager> securityManager =
90 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
91 NS_ENSURE_SUCCESS(rv, rv);
93 nsCOMPtr<nsIPrincipal> principal;
94 rv = securityManager->GetChannelPrincipal(aChannel,
95 getter_AddRefs(principal));
96 NS_ENSURE_SUCCESS(rv, rv);
98 bool expectCallback;
99 rv = uriClassifier->Classify(principal, this, &expectCallback);
100 if (NS_FAILED(rv)) return rv;
102 if (expectCallback) {
103 // Suspend the channel, it will be resumed when we get the classifier
104 // callback.
105 rv = aChannel->Suspend();
106 if (NS_FAILED(rv)) {
107 // Some channels (including nsJSChannel) fail on Suspend. This
108 // shouldn't be fatal, but will prevent malware from being
109 // blocked on these channels.
110 return NS_OK;
113 mSuspendedChannel = aChannel;
114 #ifdef DEBUG
115 LOG(("nsChannelClassifier[%p]: suspended channel %p",
116 this, mSuspendedChannel.get()));
117 #endif
120 return NS_OK;
123 // Note in the cache entry that this URL was classified, so that future
124 // cached loads don't need to be checked.
125 void
126 nsChannelClassifier::MarkEntryClassified(nsresult status)
128 nsCOMPtr<nsICachingChannel> cachingChannel =
129 do_QueryInterface(mSuspendedChannel);
130 if (!cachingChannel) {
131 return;
134 nsCOMPtr<nsISupports> cacheToken;
135 cachingChannel->GetCacheToken(getter_AddRefs(cacheToken));
136 if (!cacheToken) {
137 return;
140 nsCOMPtr<nsICacheEntryDescriptor> cacheEntry =
141 do_QueryInterface(cacheToken);
142 if (!cacheEntry) {
143 return;
146 cacheEntry->SetMetaDataElement("necko:classified",
147 NS_SUCCEEDED(status) ? "1" : nullptr);
150 bool
151 nsChannelClassifier::HasBeenClassified(nsIChannel *aChannel)
153 nsCOMPtr<nsICachingChannel> cachingChannel =
154 do_QueryInterface(aChannel);
155 if (!cachingChannel) {
156 return false;
159 // Only check the tag if we are loading from the cache without
160 // validation.
161 bool fromCache;
162 if (NS_FAILED(cachingChannel->IsFromCache(&fromCache)) || !fromCache) {
163 return false;
166 nsCOMPtr<nsISupports> cacheToken;
167 cachingChannel->GetCacheToken(getter_AddRefs(cacheToken));
168 if (!cacheToken) {
169 return false;
172 nsCOMPtr<nsICacheEntryDescriptor> cacheEntry =
173 do_QueryInterface(cacheToken);
174 if (!cacheEntry) {
175 return false;
178 nsXPIDLCString tag;
179 cacheEntry->GetMetaDataElement("necko:classified", getter_Copies(tag));
180 return tag.EqualsLiteral("1");
183 NS_IMETHODIMP
184 nsChannelClassifier::OnClassifyComplete(nsresult aErrorCode)
186 if (mSuspendedChannel) {
187 MarkEntryClassified(aErrorCode);
189 if (NS_FAILED(aErrorCode)) {
190 #ifdef DEBUG
191 LOG(("nsChannelClassifier[%p]: cancelling channel %p with error "
192 "code: %x", this, mSuspendedChannel.get(), aErrorCode));
193 #endif
194 mSuspendedChannel->Cancel(aErrorCode);
196 #ifdef DEBUG
197 LOG(("nsChannelClassifier[%p]: resuming channel %p from "
198 "OnClassifyComplete", this, mSuspendedChannel.get()));
199 #endif
200 mSuspendedChannel->Resume();
201 mSuspendedChannel = nullptr;
204 return NS_OK;