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