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/. */
6
7 #include "InternalRequest.h"
8
9 #include "mozilla/ErrorResult.h"
10 #include "mozilla/dom/Document.h"
11 #include "mozilla/dom/FetchTypes.h"
12 #include "mozilla/dom/IPCBlobInputStreamChild.h"
13 #include "mozilla/dom/ScriptSettings.h"
14 #include "mozilla/dom/WorkerCommon.h"
15 #include "mozilla/dom/WorkerPrivate.h"
16 #include "mozilla/ipc/IPCStreamUtils.h"
17 #include "mozilla/ipc/PBackgroundChild.h"
18 #include "nsIContentPolicy.h"
19 #include "nsStreamUtils.h"
20
21 namespace mozilla {
22 namespace dom {
23 // The global is used to extract the principal.
GetRequestConstructorCopy(nsIGlobalObject * aGlobal,ErrorResult & aRv) const24 SafeRefPtr<InternalRequest> InternalRequest::GetRequestConstructorCopy(
25 nsIGlobalObject* aGlobal, ErrorResult& aRv) const {
26 MOZ_RELEASE_ASSERT(!mURLList.IsEmpty(),
27 "Internal Request's urlList should not be empty when "
28 "copied from constructor.");
29 auto copy =
30 MakeSafeRefPtr<InternalRequest>(mURLList.LastElement(), mFragment);
31 copy->SetMethod(mMethod);
32 copy->mHeaders = new InternalHeaders(*mHeaders);
33 copy->SetUnsafeRequest();
34 copy->mBodyStream = mBodyStream;
35 copy->mBodyLength = mBodyLength;
36 // The "client" is not stored in our implementation. Fetch API users should
37 // use the appropriate window/document/principal and other Gecko security
38 // mechanisms as appropriate.
39 copy->mReferrer = mReferrer;
40 copy->mReferrerPolicy = mReferrerPolicy;
41 copy->mEnvironmentReferrerPolicy = mEnvironmentReferrerPolicy;
42 copy->mIntegrity = mIntegrity;
43 copy->mMozErrors = mMozErrors;
44
45 copy->mContentPolicyType = mContentPolicyTypeOverridden
46 ? mContentPolicyType
47 : nsIContentPolicy::TYPE_FETCH;
48 copy->mMode = mMode;
49 copy->mCredentialsMode = mCredentialsMode;
50 copy->mCacheMode = mCacheMode;
51 copy->mRedirectMode = mRedirectMode;
52 copy->mContentPolicyTypeOverridden = mContentPolicyTypeOverridden;
53
54 copy->mPreferredAlternativeDataType = mPreferredAlternativeDataType;
55 return copy;
56 }
57
Clone()58 SafeRefPtr<InternalRequest> InternalRequest::Clone() {
59 auto clone = MakeSafeRefPtr<InternalRequest>(*this, ConstructorGuard{});
60
61 if (!mBodyStream) {
62 return clone;
63 }
64
65 nsCOMPtr<nsIInputStream> clonedBody;
66 nsCOMPtr<nsIInputStream> replacementBody;
67
68 nsresult rv = NS_CloneInputStream(mBodyStream, getter_AddRefs(clonedBody),
69 getter_AddRefs(replacementBody));
70 if (NS_WARN_IF(NS_FAILED(rv))) {
71 return nullptr;
72 }
73
74 clone->mBodyStream.swap(clonedBody);
75 if (replacementBody) {
76 mBodyStream.swap(replacementBody);
77 }
78 return clone;
79 }
InternalRequest(const nsACString & aURL,const nsACString & aFragment)80 InternalRequest::InternalRequest(const nsACString& aURL,
81 const nsACString& aFragment)
82 : mMethod("GET"),
83 mHeaders(new InternalHeaders(HeadersGuardEnum::None)),
84 mBodyLength(InternalResponse::UNKNOWN_BODY_SIZE),
85 mContentPolicyType(nsIContentPolicy::TYPE_FETCH),
86 mReferrer(NS_LITERAL_STRING(kFETCH_CLIENT_REFERRER_STR)),
87 mReferrerPolicy(ReferrerPolicy::_empty),
88 mEnvironmentReferrerPolicy(ReferrerPolicy::_empty),
89 mMode(RequestMode::No_cors),
90 mCredentialsMode(RequestCredentials::Omit),
91 mCacheMode(RequestCache::Default),
92 mRedirectMode(RequestRedirect::Follow) {
93 MOZ_ASSERT(!aURL.IsEmpty());
94 AddURL(aURL, aFragment);
95 }
InternalRequest(const nsACString & aURL,const nsACString & aFragment,const nsACString & aMethod,already_AddRefed<InternalHeaders> aHeaders,RequestCache aCacheMode,RequestMode aMode,RequestRedirect aRequestRedirect,RequestCredentials aRequestCredentials,const nsAString & aReferrer,ReferrerPolicy aReferrerPolicy,nsContentPolicyType aContentPolicyType,const nsAString & aIntegrity)96 InternalRequest::InternalRequest(
97 const nsACString& aURL, const nsACString& aFragment,
98 const nsACString& aMethod, already_AddRefed<InternalHeaders> aHeaders,
99 RequestCache aCacheMode, RequestMode aMode,
100 RequestRedirect aRequestRedirect, RequestCredentials aRequestCredentials,
101 const nsAString& aReferrer, ReferrerPolicy aReferrerPolicy,
102 nsContentPolicyType aContentPolicyType, const nsAString& aIntegrity)
103 : mMethod(aMethod),
104 mHeaders(aHeaders),
105 mBodyLength(InternalResponse::UNKNOWN_BODY_SIZE),
106 mContentPolicyType(aContentPolicyType),
107 mReferrer(aReferrer),
108 mReferrerPolicy(aReferrerPolicy),
109 mEnvironmentReferrerPolicy(ReferrerPolicy::_empty),
110 mMode(aMode),
111 mCredentialsMode(aRequestCredentials),
112 mCacheMode(aCacheMode),
113 mRedirectMode(aRequestRedirect),
114 mIntegrity(aIntegrity) {
115 MOZ_ASSERT(!aURL.IsEmpty());
116 AddURL(aURL, aFragment);
117 }
InternalRequest(const InternalRequest & aOther,ConstructorGuard)118 InternalRequest::InternalRequest(const InternalRequest& aOther,
119 ConstructorGuard)
120 : mMethod(aOther.mMethod),
121 mURLList(aOther.mURLList.Clone()),
122 mHeaders(new InternalHeaders(*aOther.mHeaders)),
123 mBodyLength(InternalResponse::UNKNOWN_BODY_SIZE),
124 mContentPolicyType(aOther.mContentPolicyType),
125 mReferrer(aOther.mReferrer),
126 mReferrerPolicy(aOther.mReferrerPolicy),
127 mEnvironmentReferrerPolicy(aOther.mEnvironmentReferrerPolicy),
128 mMode(aOther.mMode),
129 mCredentialsMode(aOther.mCredentialsMode),
130 mResponseTainting(aOther.mResponseTainting),
131 mCacheMode(aOther.mCacheMode),
132 mRedirectMode(aOther.mRedirectMode),
133 mIntegrity(aOther.mIntegrity),
134 mMozErrors(aOther.mMozErrors),
135 mFragment(aOther.mFragment),
136 mSkipServiceWorker(aOther.mSkipServiceWorker),
137 mSynchronous(aOther.mSynchronous),
138 mUnsafeRequest(aOther.mUnsafeRequest),
139 mUseURLCredentials(aOther.mUseURLCredentials),
140 mContentPolicyTypeOverridden(aOther.mContentPolicyTypeOverridden) {
141 // NOTE: does not copy body stream... use the fallible Clone() for that
142 }
143
InternalRequest(const IPCInternalRequest & aIPCRequest)144 InternalRequest::InternalRequest(const IPCInternalRequest& aIPCRequest)
145 : mMethod(aIPCRequest.method()),
146 mURLList(aIPCRequest.urlList().Clone()),
147 mHeaders(new InternalHeaders(aIPCRequest.headers(),
148 aIPCRequest.headersGuard())),
149 mBodyLength(aIPCRequest.bodySize()),
150 mPreferredAlternativeDataType(aIPCRequest.preferredAlternativeDataType()),
151 mContentPolicyType(
152 static_cast<nsContentPolicyType>(aIPCRequest.contentPolicyType())),
153 mReferrer(aIPCRequest.referrer()),
154 mReferrerPolicy(aIPCRequest.referrerPolicy()),
155 mMode(aIPCRequest.requestMode()),
156 mCredentialsMode(aIPCRequest.requestCredentials()),
157 mCacheMode(aIPCRequest.cacheMode()),
158 mRedirectMode(aIPCRequest.requestRedirect()),
159 mIntegrity(aIPCRequest.integrity()),
160 mFragment(aIPCRequest.fragment()) {
161 if (aIPCRequest.principalInfo()) {
162 mPrincipalInfo = MakeUnique<mozilla::ipc::PrincipalInfo>(
163 aIPCRequest.principalInfo().ref());
164 }
165
166 const Maybe<BodyStreamVariant>& body = aIPCRequest.body();
167
168 // This constructor is (currently) only used for parent -> child communication
169 // (constructed on the child side).
170 if (body) {
171 MOZ_ASSERT(body->type() == BodyStreamVariant::TParentToChildStream);
172 mBodyStream = static_cast<IPCBlobInputStreamChild*>(
173 body->get_ParentToChildStream().actorChild())
174 ->CreateStream();
175 }
176 }
177
178 InternalRequest::~InternalRequest() = default;
179
SetContentPolicyType(nsContentPolicyType aContentPolicyType)180 void InternalRequest::SetContentPolicyType(
181 nsContentPolicyType aContentPolicyType) {
182 mContentPolicyType = aContentPolicyType;
183 }
184
OverrideContentPolicyType(nsContentPolicyType aContentPolicyType)185 void InternalRequest::OverrideContentPolicyType(
186 nsContentPolicyType aContentPolicyType) {
187 SetContentPolicyType(aContentPolicyType);
188 mContentPolicyTypeOverridden = true;
189 }
190
191 /* static */
MapContentPolicyTypeToRequestDestination(nsContentPolicyType aContentPolicyType)192 RequestDestination InternalRequest::MapContentPolicyTypeToRequestDestination(
193 nsContentPolicyType aContentPolicyType) {
194 RequestDestination destination = RequestDestination::_empty;
195 switch (aContentPolicyType) {
196 case nsIContentPolicy::TYPE_OTHER:
197 destination = RequestDestination::_empty;
198 break;
199 case nsIContentPolicy::TYPE_INTERNAL_SCRIPT:
200 case nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD:
201 case nsIContentPolicy::TYPE_INTERNAL_MODULE:
202 case nsIContentPolicy::TYPE_INTERNAL_MODULE_PRELOAD:
203 case nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER:
204 case nsIContentPolicy::TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS:
205 case nsIContentPolicy::TYPE_SCRIPT:
206 destination = RequestDestination::Script;
207 break;
208 case nsIContentPolicy::TYPE_INTERNAL_WORKER:
209 destination = RequestDestination::Worker;
210 break;
211 case nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER:
212 destination = RequestDestination::Sharedworker;
213 break;
214 case nsIContentPolicy::TYPE_IMAGESET:
215 case nsIContentPolicy::TYPE_INTERNAL_IMAGE:
216 case nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD:
217 case nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON:
218 case nsIContentPolicy::TYPE_IMAGE:
219 destination = RequestDestination::Image;
220 break;
221 case nsIContentPolicy::TYPE_STYLESHEET:
222 case nsIContentPolicy::TYPE_INTERNAL_STYLESHEET:
223 case nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD:
224 destination = RequestDestination::Style;
225 break;
226 case nsIContentPolicy::TYPE_OBJECT:
227 case nsIContentPolicy::TYPE_INTERNAL_OBJECT:
228 destination = RequestDestination::Object;
229 break;
230 case nsIContentPolicy::TYPE_INTERNAL_EMBED:
231 destination = RequestDestination::Embed;
232 break;
233 case nsIContentPolicy::TYPE_DOCUMENT:
234 case nsIContentPolicy::TYPE_SUBDOCUMENT:
235 case nsIContentPolicy::TYPE_INTERNAL_IFRAME:
236 destination = RequestDestination::Document;
237 break;
238 case nsIContentPolicy::TYPE_INTERNAL_FRAME:
239 destination = RequestDestination::_empty;
240 break;
241 case nsIContentPolicy::TYPE_REFRESH:
242 destination = RequestDestination::_empty;
243 break;
244 case nsIContentPolicy::TYPE_PING:
245 destination = RequestDestination::_empty;
246 break;
247 case nsIContentPolicy::TYPE_XMLHTTPREQUEST:
248 case nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST:
249 destination = RequestDestination::_empty;
250 break;
251 case nsIContentPolicy::TYPE_INTERNAL_EVENTSOURCE:
252 destination = RequestDestination::_empty;
253 break;
254 case nsIContentPolicy::TYPE_OBJECT_SUBREQUEST:
255 destination = RequestDestination::_empty;
256 break;
257 case nsIContentPolicy::TYPE_DTD:
258 case nsIContentPolicy::TYPE_INTERNAL_DTD:
259 case nsIContentPolicy::TYPE_INTERNAL_FORCE_ALLOWED_DTD:
260 destination = RequestDestination::_empty;
261 break;
262 case nsIContentPolicy::TYPE_FONT:
263 case nsIContentPolicy::TYPE_INTERNAL_FONT_PRELOAD:
264 destination = RequestDestination::Font;
265 break;
266 case nsIContentPolicy::TYPE_MEDIA:
267 destination = RequestDestination::_empty;
268 break;
269 case nsIContentPolicy::TYPE_INTERNAL_AUDIO:
270 destination = RequestDestination::Audio;
271 break;
272 case nsIContentPolicy::TYPE_INTERNAL_VIDEO:
273 destination = RequestDestination::Video;
274 break;
275 case nsIContentPolicy::TYPE_INTERNAL_TRACK:
276 destination = RequestDestination::Track;
277 break;
278 case nsIContentPolicy::TYPE_WEBSOCKET:
279 destination = RequestDestination::_empty;
280 break;
281 case nsIContentPolicy::TYPE_CSP_REPORT:
282 destination = RequestDestination::_empty;
283 break;
284 case nsIContentPolicy::TYPE_XSLT:
285 destination = RequestDestination::Xslt;
286 break;
287 case nsIContentPolicy::TYPE_BEACON:
288 destination = RequestDestination::_empty;
289 break;
290 case nsIContentPolicy::TYPE_FETCH:
291 destination = RequestDestination::_empty;
292 break;
293 case nsIContentPolicy::TYPE_WEB_MANIFEST:
294 destination = RequestDestination::Manifest;
295 break;
296 case nsIContentPolicy::TYPE_SAVEAS_DOWNLOAD:
297 destination = RequestDestination::_empty;
298 break;
299 case nsIContentPolicy::TYPE_SPECULATIVE:
300 destination = RequestDestination::_empty;
301 break;
302 case nsIContentPolicy::TYPE_INTERNAL_AUDIOWORKLET:
303 destination = RequestDestination::Audioworklet;
304 break;
305 case nsIContentPolicy::TYPE_INTERNAL_PAINTWORKLET:
306 destination = RequestDestination::Paintworklet;
307 break;
308 default:
309 MOZ_ASSERT(false, "Unhandled nsContentPolicyType value");
310 break;
311 }
312
313 return destination;
314 }
315
316 // static
IsNavigationContentPolicy(nsContentPolicyType aContentPolicyType)317 bool InternalRequest::IsNavigationContentPolicy(
318 nsContentPolicyType aContentPolicyType) {
319 // https://fetch.spec.whatwg.org/#navigation-request-context
320 //
321 // A navigation request context is one of "form", "frame", "hyperlink",
322 // "iframe", "internal" (as long as context frame type is not "none"),
323 // "location", "metarefresh", and "prerender".
324 //
325 // Note, all of these request types are effectively initiated by nsDocShell.
326 //
327 // The TYPE_REFRESH is used in some code paths for metarefresh, but will not
328 // be seen during the actual load. Instead the new load gets a normal
329 // nsDocShell policy type. We include it here in case this utility method
330 // is called before the load starts.
331 return aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT ||
332 aContentPolicyType == nsIContentPolicy::TYPE_SUBDOCUMENT ||
333 aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_FRAME ||
334 aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_IFRAME ||
335 aContentPolicyType == nsIContentPolicy::TYPE_REFRESH;
336 }
337
338 // static
IsWorkerContentPolicy(nsContentPolicyType aContentPolicyType)339 bool InternalRequest::IsWorkerContentPolicy(
340 nsContentPolicyType aContentPolicyType) {
341 // https://fetch.spec.whatwg.org/#worker-request-context
342 //
343 // A worker request context is one of "serviceworker", "sharedworker", and
344 // "worker".
345 //
346 // Note, service workers are not included here because currently there is
347 // no way to generate a Request with a "serviceworker" RequestDestination.
348 // ServiceWorker scripts cannot be intercepted.
349 return aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_WORKER ||
350 aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER;
351 }
352
IsNavigationRequest() const353 bool InternalRequest::IsNavigationRequest() const {
354 return IsNavigationContentPolicy(mContentPolicyType);
355 }
356
IsWorkerRequest() const357 bool InternalRequest::IsWorkerRequest() const {
358 return IsWorkerContentPolicy(mContentPolicyType);
359 }
360
IsClientRequest() const361 bool InternalRequest::IsClientRequest() const {
362 return IsNavigationRequest() || IsWorkerRequest();
363 }
364
365 // static
MapChannelToRequestMode(nsIChannel * aChannel)366 RequestMode InternalRequest::MapChannelToRequestMode(nsIChannel* aChannel) {
367 MOZ_ASSERT(aChannel);
368
369 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
370
371 nsContentPolicyType contentPolicy = loadInfo->InternalContentPolicyType();
372 if (IsNavigationContentPolicy(contentPolicy)) {
373 return RequestMode::Navigate;
374 }
375
376 // TODO: remove the worker override once securityMode is fully implemented
377 // (bug 1189945)
378 if (IsWorkerContentPolicy(contentPolicy)) {
379 return RequestMode::Same_origin;
380 }
381
382 uint32_t securityMode = loadInfo->GetSecurityMode();
383
384 switch (securityMode) {
385 case nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS:
386 case nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED:
387 return RequestMode::Same_origin;
388 case nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS:
389 case nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL:
390 return RequestMode::No_cors;
391 case nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS:
392 // TODO: Check additional flag force-preflight after bug 1199693 (bug
393 // 1189945)
394 return RequestMode::Cors;
395 default:
396 MOZ_ASSERT_UNREACHABLE("Unexpected security mode!");
397 return RequestMode::Same_origin;
398 }
399 }
400
401 // static
MapChannelToRequestCredentials(nsIChannel * aChannel)402 RequestCredentials InternalRequest::MapChannelToRequestCredentials(
403 nsIChannel* aChannel) {
404 MOZ_ASSERT(aChannel);
405
406 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
407
408 uint32_t cookiePolicy = loadInfo->GetCookiePolicy();
409
410 if (cookiePolicy == nsILoadInfo::SEC_COOKIES_INCLUDE) {
411 return RequestCredentials::Include;
412 } else if (cookiePolicy == nsILoadInfo::SEC_COOKIES_OMIT) {
413 return RequestCredentials::Omit;
414 } else if (cookiePolicy == nsILoadInfo::SEC_COOKIES_SAME_ORIGIN) {
415 return RequestCredentials::Same_origin;
416 }
417
418 MOZ_ASSERT_UNREACHABLE("Unexpected cookie policy!");
419 return RequestCredentials::Same_origin;
420 }
421
MaybeSkipCacheIfPerformingRevalidation()422 void InternalRequest::MaybeSkipCacheIfPerformingRevalidation() {
423 if (mCacheMode == RequestCache::Default &&
424 mHeaders->HasRevalidationHeaders()) {
425 mCacheMode = RequestCache::No_store;
426 }
427 }
428
SetPrincipalInfo(UniquePtr<mozilla::ipc::PrincipalInfo> aPrincipalInfo)429 void InternalRequest::SetPrincipalInfo(
430 UniquePtr<mozilla::ipc::PrincipalInfo> aPrincipalInfo) {
431 mPrincipalInfo = std::move(aPrincipalInfo);
432 }
433
434 } // namespace dom
435 } // namespace mozilla
436