1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 "ChannelWrapper.h"
8 
9 #include "jsapi.h"
10 #include "xpcpublic.h"
11 
12 #include "mozilla/BasePrincipal.h"
13 #include "SystemPrincipal.h"
14 
15 #include "NSSErrorsService.h"
16 #include "nsITransportSecurityInfo.h"
17 
18 #include "mozilla/AddonManagerWebAPI.h"
19 #include "mozilla/ErrorNames.h"
20 #include "mozilla/ResultExtensions.h"
21 #include "mozilla/Unused.h"
22 #include "mozilla/dom/Event.h"
23 #include "mozilla/dom/TabParent.h"
24 #include "nsIContentPolicy.h"
25 #include "nsIHttpChannelInternal.h"
26 #include "nsIHttpHeaderVisitor.h"
27 #include "nsIInterfaceRequestor.h"
28 #include "nsIInterfaceRequestorUtils.h"
29 #include "nsILoadContext.h"
30 #include "nsILoadGroup.h"
31 #include "nsIProxiedChannel.h"
32 #include "nsIProxyInfo.h"
33 #include "nsITraceableChannel.h"
34 #include "nsIWritablePropertyBag2.h"
35 #include "nsNetUtil.h"
36 #include "nsProxyRelease.h"
37 #include "nsPrintfCString.h"
38 
39 using namespace mozilla::dom;
40 using namespace JS;
41 
42 namespace mozilla {
43 namespace extensions {
44 
45 #define CHANNELWRAPPER_PROP_KEY \
46   NS_LITERAL_STRING("ChannelWrapper::CachedInstance")
47 
48 /*****************************************************************************
49  * Initialization
50  *****************************************************************************/
51 
52 /* static */
53 
Get(const GlobalObject & global,nsIChannel * channel)54 already_AddRefed<ChannelWrapper> ChannelWrapper::Get(const GlobalObject& global,
55                                                      nsIChannel* channel) {
56   RefPtr<ChannelWrapper> wrapper;
57 
58   nsCOMPtr<nsIWritablePropertyBag2> props = do_QueryInterface(channel);
59   if (props) {
60     Unused << props->GetPropertyAsInterface(CHANNELWRAPPER_PROP_KEY,
61                                             NS_GET_IID(ChannelWrapper),
62                                             getter_AddRefs(wrapper));
63 
64     if (wrapper) {
65       // Assume cached attributes may have changed at this point.
66       wrapper->ClearCachedAttributes();
67     }
68   }
69 
70   if (!wrapper) {
71     wrapper = new ChannelWrapper(global.GetAsSupports(), channel);
72     if (props) {
73       Unused << props->SetPropertyAsInterface(CHANNELWRAPPER_PROP_KEY, wrapper);
74     }
75   }
76 
77   return wrapper.forget();
78 }
79 
SetChannel(nsIChannel * aChannel)80 void ChannelWrapper::SetChannel(nsIChannel* aChannel) {
81   detail::ChannelHolder::SetChannel(aChannel);
82   ClearCachedAttributes();
83   ChannelWrapperBinding::ClearCachedFinalURIValue(this);
84   ChannelWrapperBinding::ClearCachedFinalURLValue(this);
85   mFinalURLInfo.reset();
86   ChannelWrapperBinding::ClearCachedProxyInfoValue(this);
87 }
88 
ClearCachedAttributes()89 void ChannelWrapper::ClearCachedAttributes() {
90   ChannelWrapperBinding::ClearCachedRemoteAddressValue(this);
91   ChannelWrapperBinding::ClearCachedStatusCodeValue(this);
92   ChannelWrapperBinding::ClearCachedStatusLineValue(this);
93   if (!mFiredErrorEvent) {
94     ChannelWrapperBinding::ClearCachedErrorStringValue(this);
95   }
96 }
97 
98 /*****************************************************************************
99  * ...
100  *****************************************************************************/
101 
Cancel(uint32_t aResult,ErrorResult & aRv)102 void ChannelWrapper::Cancel(uint32_t aResult, ErrorResult& aRv) {
103   nsresult rv = NS_ERROR_UNEXPECTED;
104   if (nsCOMPtr<nsIChannel> chan = MaybeChannel()) {
105     rv = chan->Cancel(nsresult(aResult));
106     ErrorCheck();
107   }
108   if (NS_FAILED(rv)) {
109     aRv.Throw(rv);
110   }
111 }
112 
RedirectTo(nsIURI * aURI,ErrorResult & aRv)113 void ChannelWrapper::RedirectTo(nsIURI* aURI, ErrorResult& aRv) {
114   nsresult rv = NS_ERROR_UNEXPECTED;
115   if (nsCOMPtr<nsIHttpChannel> chan = MaybeHttpChannel()) {
116     rv = chan->RedirectTo(aURI);
117   }
118   if (NS_FAILED(rv)) {
119     aRv.Throw(rv);
120   }
121 }
122 
UpgradeToSecure(ErrorResult & aRv)123 void ChannelWrapper::UpgradeToSecure(ErrorResult& aRv) {
124   nsresult rv = NS_ERROR_UNEXPECTED;
125   if (nsCOMPtr<nsIHttpChannel> chan = MaybeHttpChannel()) {
126     rv = chan->UpgradeToSecure();
127   }
128   if (NS_FAILED(rv)) {
129     aRv.Throw(rv);
130   }
131 }
132 
SetSuspended(bool aSuspended,ErrorResult & aRv)133 void ChannelWrapper::SetSuspended(bool aSuspended, ErrorResult& aRv) {
134   if (aSuspended != mSuspended) {
135     nsresult rv = NS_ERROR_UNEXPECTED;
136     if (nsCOMPtr<nsIChannel> chan = MaybeChannel()) {
137       if (aSuspended) {
138         rv = chan->Suspend();
139       } else {
140         rv = chan->Resume();
141       }
142     }
143     if (NS_FAILED(rv)) {
144       aRv.Throw(rv);
145     } else {
146       mSuspended = aSuspended;
147     }
148   }
149 }
150 
GetContentType(nsCString & aContentType) const151 void ChannelWrapper::GetContentType(nsCString& aContentType) const {
152   if (nsCOMPtr<nsIHttpChannel> chan = MaybeHttpChannel()) {
153     Unused << chan->GetContentType(aContentType);
154   }
155 }
156 
SetContentType(const nsACString & aContentType)157 void ChannelWrapper::SetContentType(const nsACString& aContentType) {
158   if (nsCOMPtr<nsIHttpChannel> chan = MaybeHttpChannel()) {
159     Unused << chan->SetContentType(aContentType);
160   }
161 }
162 
163 /*****************************************************************************
164  * Headers
165  *****************************************************************************/
166 
167 namespace {
168 
169 class MOZ_STACK_CLASS HeaderVisitor final : public nsIHttpHeaderVisitor {
170  public:
171   NS_DECL_NSIHTTPHEADERVISITOR
172 
HeaderVisitor(nsTArray<dom::MozHTTPHeader> & aHeaders)173   explicit HeaderVisitor(nsTArray<dom::MozHTTPHeader>& aHeaders)
174       : mHeaders(aHeaders) {}
175 
HeaderVisitor(nsTArray<dom::MozHTTPHeader> & aHeaders,const nsCString & aContentTypeHdr)176   HeaderVisitor(nsTArray<dom::MozHTTPHeader>& aHeaders,
177                 const nsCString& aContentTypeHdr)
178       : mHeaders(aHeaders), mContentTypeHdr(aContentTypeHdr) {}
179 
VisitRequestHeaders(nsIHttpChannel * aChannel,ErrorResult & aRv)180   void VisitRequestHeaders(nsIHttpChannel* aChannel, ErrorResult& aRv) {
181     CheckResult(aChannel->VisitRequestHeaders(this), aRv);
182   }
183 
VisitResponseHeaders(nsIHttpChannel * aChannel,ErrorResult & aRv)184   void VisitResponseHeaders(nsIHttpChannel* aChannel, ErrorResult& aRv) {
185     CheckResult(aChannel->VisitResponseHeaders(this), aRv);
186   }
187 
188   NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override;
189 
190   // Stub AddRef/Release since this is a stack class.
AddRef(void)191   NS_IMETHOD_(MozExternalRefCountType) AddRef(void) override {
192     return ++mRefCnt;
193   }
194 
Release(void)195   NS_IMETHOD_(MozExternalRefCountType) Release(void) override {
196     return --mRefCnt;
197   }
198 
~HeaderVisitor()199   virtual ~HeaderVisitor() { MOZ_DIAGNOSTIC_ASSERT(mRefCnt == 0); }
200 
201  private:
CheckResult(nsresult aNSRv,ErrorResult & aRv)202   bool CheckResult(nsresult aNSRv, ErrorResult& aRv) {
203     if (NS_FAILED(aNSRv)) {
204       aRv.Throw(aNSRv);
205       return false;
206     }
207     return true;
208   }
209 
210   nsTArray<dom::MozHTTPHeader>& mHeaders;
211   nsCString mContentTypeHdr = VoidCString();
212 
213   nsrefcnt mRefCnt = 0;
214 };
215 
216 NS_IMETHODIMP
VisitHeader(const nsACString & aHeader,const nsACString & aValue)217 HeaderVisitor::VisitHeader(const nsACString& aHeader,
218                            const nsACString& aValue) {
219   auto dict = mHeaders.AppendElement(fallible);
220   if (!dict) {
221     return NS_ERROR_OUT_OF_MEMORY;
222   }
223   dict->mName = aHeader;
224 
225   if (!mContentTypeHdr.IsVoid() &&
226       aHeader.LowerCaseEqualsLiteral("content-type")) {
227     dict->mValue = mContentTypeHdr;
228   } else {
229     dict->mValue = aValue;
230   }
231 
232   return NS_OK;
233 }
234 
235 NS_IMPL_QUERY_INTERFACE(HeaderVisitor, nsIHttpHeaderVisitor)
236 
237 }  // anonymous namespace
238 
GetRequestHeaders(nsTArray<dom::MozHTTPHeader> & aRetVal,ErrorResult & aRv) const239 void ChannelWrapper::GetRequestHeaders(nsTArray<dom::MozHTTPHeader>& aRetVal,
240                                        ErrorResult& aRv) const {
241   if (nsCOMPtr<nsIHttpChannel> chan = MaybeHttpChannel()) {
242     HeaderVisitor visitor(aRetVal);
243     visitor.VisitRequestHeaders(chan, aRv);
244   } else {
245     aRv.Throw(NS_ERROR_UNEXPECTED);
246   }
247 }
248 
GetResponseHeaders(nsTArray<dom::MozHTTPHeader> & aRetVal,ErrorResult & aRv) const249 void ChannelWrapper::GetResponseHeaders(nsTArray<dom::MozHTTPHeader>& aRetVal,
250                                         ErrorResult& aRv) const {
251   if (nsCOMPtr<nsIHttpChannel> chan = MaybeHttpChannel()) {
252     HeaderVisitor visitor(aRetVal, mContentTypeHdr);
253     visitor.VisitResponseHeaders(chan, aRv);
254   } else {
255     aRv.Throw(NS_ERROR_UNEXPECTED);
256   }
257 }
258 
SetRequestHeader(const nsCString & aHeader,const nsCString & aValue,bool aMerge,ErrorResult & aRv)259 void ChannelWrapper::SetRequestHeader(const nsCString& aHeader,
260                                       const nsCString& aValue, bool aMerge,
261                                       ErrorResult& aRv) {
262   nsresult rv = NS_ERROR_UNEXPECTED;
263   if (nsCOMPtr<nsIHttpChannel> chan = MaybeHttpChannel()) {
264     rv = chan->SetRequestHeader(aHeader, aValue, aMerge);
265   }
266   if (NS_FAILED(rv)) {
267     aRv.Throw(rv);
268   }
269 }
270 
SetResponseHeader(const nsCString & aHeader,const nsCString & aValue,bool aMerge,ErrorResult & aRv)271 void ChannelWrapper::SetResponseHeader(const nsCString& aHeader,
272                                        const nsCString& aValue, bool aMerge,
273                                        ErrorResult& aRv) {
274   nsresult rv = NS_ERROR_UNEXPECTED;
275   if (nsCOMPtr<nsIHttpChannel> chan = MaybeHttpChannel()) {
276     if (aHeader.LowerCaseEqualsLiteral("content-type")) {
277       rv = chan->SetContentType(aValue);
278       if (NS_SUCCEEDED(rv)) {
279         mContentTypeHdr = aValue;
280       }
281     } else {
282       rv = chan->SetResponseHeader(aHeader, aValue, aMerge);
283     }
284   }
285   if (NS_FAILED(rv)) {
286     aRv.Throw(rv);
287   }
288 }
289 
290 /*****************************************************************************
291  * LoadInfo
292  *****************************************************************************/
293 
GetLoadContext() const294 already_AddRefed<nsILoadContext> ChannelWrapper::GetLoadContext() const {
295   if (nsCOMPtr<nsIChannel> chan = MaybeChannel()) {
296     nsCOMPtr<nsILoadContext> ctxt;
297     NS_QueryNotificationCallbacks(chan, ctxt);
298     return ctxt.forget();
299   }
300   return nullptr;
301 }
302 
GetBrowserElement() const303 already_AddRefed<nsIDOMElement> ChannelWrapper::GetBrowserElement() const {
304   if (nsCOMPtr<nsILoadContext> ctxt = GetLoadContext()) {
305     nsCOMPtr<nsIDOMElement> elem;
306     if (NS_SUCCEEDED(ctxt->GetTopFrameElement(getter_AddRefs(elem)))) {
307       return elem.forget();
308     }
309   }
310   return nullptr;
311 }
312 
IsSystemPrincipal(nsIPrincipal * aPrincipal)313 static inline bool IsSystemPrincipal(nsIPrincipal* aPrincipal) {
314   return BasePrincipal::Cast(aPrincipal)->Is<SystemPrincipal>();
315 }
316 
IsSystemLoad() const317 bool ChannelWrapper::IsSystemLoad() const {
318   if (nsCOMPtr<nsILoadInfo> loadInfo = GetLoadInfo()) {
319     if (nsIPrincipal* prin = loadInfo->LoadingPrincipal()) {
320       return IsSystemPrincipal(prin);
321     }
322 
323     if (loadInfo->GetOuterWindowID() == loadInfo->GetTopOuterWindowID()) {
324       return false;
325     }
326 
327     if (nsIPrincipal* prin = loadInfo->PrincipalToInherit()) {
328       return IsSystemPrincipal(prin);
329     }
330     if (nsIPrincipal* prin = loadInfo->TriggeringPrincipal()) {
331       return IsSystemPrincipal(prin);
332     }
333   }
334   return false;
335 }
336 
CanModify() const337 bool ChannelWrapper::CanModify() const {
338   if (WebExtensionPolicy::IsRestrictedURI(FinalURLInfo())) {
339     return false;
340   }
341 
342   if (nsCOMPtr<nsILoadInfo> loadInfo = GetLoadInfo()) {
343     if (nsIPrincipal* prin = loadInfo->LoadingPrincipal()) {
344       if (IsSystemPrincipal(prin)) {
345         return false;
346       }
347 
348       auto* docURI = DocumentURLInfo();
349       if (docURI && WebExtensionPolicy::IsRestrictedURI(*docURI)) {
350         return false;
351       }
352     }
353   }
354   return true;
355 }
356 
GetOriginURI() const357 already_AddRefed<nsIURI> ChannelWrapper::GetOriginURI() const {
358   nsCOMPtr<nsIURI> uri;
359   if (nsCOMPtr<nsILoadInfo> loadInfo = GetLoadInfo()) {
360     if (nsIPrincipal* prin = loadInfo->TriggeringPrincipal()) {
361       if (prin->GetIsCodebasePrincipal()) {
362         Unused << prin->GetURI(getter_AddRefs(uri));
363       }
364     }
365   }
366   return uri.forget();
367 }
368 
GetDocumentURI() const369 already_AddRefed<nsIURI> ChannelWrapper::GetDocumentURI() const {
370   nsCOMPtr<nsIURI> uri;
371   if (nsCOMPtr<nsILoadInfo> loadInfo = GetLoadInfo()) {
372     if (nsIPrincipal* prin = loadInfo->LoadingPrincipal()) {
373       if (prin->GetIsCodebasePrincipal()) {
374         Unused << prin->GetURI(getter_AddRefs(uri));
375       }
376     }
377   }
378   return uri.forget();
379 }
380 
GetOriginURL(nsCString & aRetVal) const381 void ChannelWrapper::GetOriginURL(nsCString& aRetVal) const {
382   if (nsCOMPtr<nsIURI> uri = GetOriginURI()) {
383     Unused << uri->GetSpec(aRetVal);
384   }
385 }
386 
GetDocumentURL(nsCString & aRetVal) const387 void ChannelWrapper::GetDocumentURL(nsCString& aRetVal) const {
388   if (nsCOMPtr<nsIURI> uri = GetDocumentURI()) {
389     Unused << uri->GetSpec(aRetVal);
390   }
391 }
392 
FinalURLInfo() const393 const URLInfo& ChannelWrapper::FinalURLInfo() const {
394   if (mFinalURLInfo.isNothing()) {
395     ErrorResult rv;
396     nsCOMPtr<nsIURI> uri = FinalURI();
397     MOZ_ASSERT(uri);
398     mFinalURLInfo.emplace(uri.get(), true);
399 
400     // If this is a WebSocket request, mangle the URL so that the scheme is
401     // ws: or wss:, as appropriate.
402     auto& url = mFinalURLInfo.ref();
403     if (Type() == MozContentPolicyType::Websocket &&
404         (url.Scheme() == nsGkAtoms::http || url.Scheme() == nsGkAtoms::https)) {
405       nsAutoCString spec(url.CSpec());
406       spec.Replace(0, 4, NS_LITERAL_CSTRING("ws"));
407 
408       Unused << NS_NewURI(getter_AddRefs(uri), spec);
409       MOZ_RELEASE_ASSERT(uri);
410       mFinalURLInfo.reset();
411       mFinalURLInfo.emplace(uri.get(), true);
412     }
413   }
414   return mFinalURLInfo.ref();
415 }
416 
DocumentURLInfo() const417 const URLInfo* ChannelWrapper::DocumentURLInfo() const {
418   if (mDocumentURLInfo.isNothing()) {
419     nsCOMPtr<nsIURI> uri = GetDocumentURI();
420     if (!uri) {
421       return nullptr;
422     }
423     mDocumentURLInfo.emplace(uri.get(), true);
424   }
425   return &mDocumentURLInfo.ref();
426 }
427 
Matches(const dom::MozRequestFilter & aFilter,const WebExtensionPolicy * aExtension,const dom::MozRequestMatchOptions & aOptions) const428 bool ChannelWrapper::Matches(
429     const dom::MozRequestFilter& aFilter, const WebExtensionPolicy* aExtension,
430     const dom::MozRequestMatchOptions& aOptions) const {
431   if (!HaveChannel()) {
432     return false;
433   }
434 
435   if (!aFilter.mTypes.IsNull() && !aFilter.mTypes.Value().Contains(Type())) {
436     return false;
437   }
438 
439   auto& urlInfo = FinalURLInfo();
440   if (aFilter.mUrls && !aFilter.mUrls->Matches(urlInfo)) {
441     return false;
442   }
443 
444   if (aExtension) {
445     bool isProxy =
446         aOptions.mIsProxy && aExtension->HasPermission(nsGkAtoms::proxy);
447     // Proxies are allowed access to all urls, including restricted urls.
448     if (!aExtension->CanAccessURI(urlInfo, false, !isProxy)) {
449       return false;
450     }
451 
452     // If this isn't the proxy phase of the request, check that the extension
453     // has origin permissions for origin that originated the request.
454     if (!isProxy) {
455       if (IsSystemLoad()) {
456         return false;
457       }
458 
459       if (auto origin = DocumentURLInfo()) {
460         nsAutoCString baseURL;
461         aExtension->GetBaseURL(baseURL);
462 
463         if (!StringBeginsWith(origin->CSpec(), baseURL) &&
464             !aExtension->CanAccessURI(*origin)) {
465           return false;
466         }
467       }
468     }
469   }
470 
471   return true;
472 }
473 
NormalizeWindowID(nsILoadInfo * aLoadInfo,uint64_t windowID)474 int64_t NormalizeWindowID(nsILoadInfo* aLoadInfo, uint64_t windowID) {
475   if (windowID == aLoadInfo->GetTopOuterWindowID()) {
476     return 0;
477   }
478   return windowID;
479 }
480 
WindowId(nsILoadInfo * aLoadInfo) const481 uint64_t ChannelWrapper::WindowId(nsILoadInfo* aLoadInfo) const {
482   auto frameID = aLoadInfo->GetFrameOuterWindowID();
483   if (!frameID) {
484     frameID = aLoadInfo->GetOuterWindowID();
485   }
486   return frameID;
487 }
488 
WindowId() const489 int64_t ChannelWrapper::WindowId() const {
490   if (nsCOMPtr<nsILoadInfo> loadInfo = GetLoadInfo()) {
491     return NormalizeWindowID(loadInfo, WindowId(loadInfo));
492   }
493   return 0;
494 }
495 
ParentWindowId() const496 int64_t ChannelWrapper::ParentWindowId() const {
497   if (nsCOMPtr<nsILoadInfo> loadInfo = GetLoadInfo()) {
498     if (WindowId(loadInfo) == loadInfo->GetTopOuterWindowID()) {
499       return -1;
500     }
501 
502     uint64_t parentID;
503     if (loadInfo->GetFrameOuterWindowID()) {
504       parentID = loadInfo->GetOuterWindowID();
505     } else {
506       parentID = loadInfo->GetParentOuterWindowID();
507     }
508     return NormalizeWindowID(loadInfo, parentID);
509   }
510   return -1;
511 }
512 
GetFrameAncestors(dom::Nullable<nsTArray<dom::MozFrameAncestorInfo>> & aFrameAncestors,ErrorResult & aRv) const513 void ChannelWrapper::GetFrameAncestors(
514     dom::Nullable<nsTArray<dom::MozFrameAncestorInfo>>& aFrameAncestors,
515     ErrorResult& aRv) const {
516   nsCOMPtr<nsILoadInfo> loadInfo = GetLoadInfo();
517   if (!loadInfo || WindowId(loadInfo) == 0) {
518     aFrameAncestors.SetNull();
519     return;
520   }
521 
522   nsresult rv = GetFrameAncestors(loadInfo, aFrameAncestors.SetValue());
523   if (NS_FAILED(rv)) {
524     aRv.Throw(rv);
525   }
526 }
527 
GetFrameAncestors(nsILoadInfo * aLoadInfo,nsTArray<dom::MozFrameAncestorInfo> & aFrameAncestors) const528 nsresult ChannelWrapper::GetFrameAncestors(
529     nsILoadInfo* aLoadInfo,
530     nsTArray<dom::MozFrameAncestorInfo>& aFrameAncestors) const {
531   const nsTArray<nsCOMPtr<nsIPrincipal>>& ancestorPrincipals =
532       aLoadInfo->AncestorPrincipals();
533   const nsTArray<uint64_t>& ancestorOuterWindowIDs =
534       aLoadInfo->AncestorOuterWindowIDs();
535   uint32_t size = ancestorPrincipals.Length();
536   MOZ_DIAGNOSTIC_ASSERT(size == ancestorOuterWindowIDs.Length());
537   if (size != ancestorOuterWindowIDs.Length()) {
538     return NS_ERROR_UNEXPECTED;
539   }
540 
541   bool subFrame = aLoadInfo->GetExternalContentPolicyType() ==
542                   nsIContentPolicy::TYPE_SUBDOCUMENT;
543   if (!aFrameAncestors.SetCapacity(subFrame ? size : size + 1, fallible)) {
544     return NS_ERROR_OUT_OF_MEMORY;
545   }
546 
547   // The immediate parent is always the first element in the ancestor arrays,
548   // however SUBDOCUMENTs do not have their immediate parent included, so we
549   // inject it here. This will force wrapper.parentWindowId ==
550   // wrapper.frameAncestors[0].frameId to always be true.  All ather requests
551   // already match this way.
552   if (subFrame) {
553     auto ancestor = aFrameAncestors.AppendElement();
554     GetDocumentURL(ancestor->mUrl);
555     ancestor->mFrameId = ParentWindowId();
556   }
557 
558   for (uint32_t i = 0; i < size; ++i) {
559     auto ancestor = aFrameAncestors.AppendElement();
560     nsCOMPtr<nsIURI> uri;
561     MOZ_TRY(ancestorPrincipals[i]->GetURI(getter_AddRefs(uri)));
562     if (!uri) {
563       return NS_ERROR_UNEXPECTED;
564     }
565     MOZ_TRY(uri->GetSpec(ancestor->mUrl));
566     ancestor->mFrameId =
567         NormalizeWindowID(aLoadInfo, ancestorOuterWindowIDs[i]);
568   }
569   return NS_OK;
570 }
571 
572 /*****************************************************************************
573  * Response filtering
574  *****************************************************************************/
575 
RegisterTraceableChannel(const WebExtensionPolicy & aAddon,nsITabParent * aTabParent)576 void ChannelWrapper::RegisterTraceableChannel(const WebExtensionPolicy& aAddon,
577                                               nsITabParent* aTabParent) {
578   // We can't attach new listeners after the response has started, so don't
579   // bother registering anything.
580   if (mResponseStarted || !CanModify()) {
581     return;
582   }
583 
584   mAddonEntries.Put(aAddon.Id(), aTabParent);
585   if (!mChannelEntry) {
586     mChannelEntry = WebRequestService::GetSingleton().RegisterChannel(this);
587     CheckEventListeners();
588   }
589 }
590 
GetTraceableChannel(nsAtom * aAddonId,dom::nsIContentParent * aContentParent) const591 already_AddRefed<nsITraceableChannel> ChannelWrapper::GetTraceableChannel(
592     nsAtom* aAddonId, dom::nsIContentParent* aContentParent) const {
593   nsCOMPtr<nsITabParent> tabParent;
594   if (mAddonEntries.Get(aAddonId, getter_AddRefs(tabParent))) {
595     nsIContentParent* contentParent = nullptr;
596     if (tabParent) {
597       contentParent = static_cast<nsIContentParent*>(
598           static_cast<TabParent*>(tabParent.get())->Manager());
599     }
600 
601     if (contentParent == aContentParent) {
602       nsCOMPtr<nsITraceableChannel> chan = QueryChannel();
603       return chan.forget();
604     }
605   }
606   return nullptr;
607 }
608 
609 /*****************************************************************************
610  * ...
611  *****************************************************************************/
612 
GetContentPolicyType(uint32_t aType)613 MozContentPolicyType GetContentPolicyType(uint32_t aType) {
614   // Note: Please keep this function in sync with the external types in
615   // nsIContentPolicy.idl
616   switch (aType) {
617     case nsIContentPolicy::TYPE_DOCUMENT:
618       return MozContentPolicyType::Main_frame;
619     case nsIContentPolicy::TYPE_SUBDOCUMENT:
620       return MozContentPolicyType::Sub_frame;
621     case nsIContentPolicy::TYPE_STYLESHEET:
622       return MozContentPolicyType::Stylesheet;
623     case nsIContentPolicy::TYPE_SCRIPT:
624       return MozContentPolicyType::Script;
625     case nsIContentPolicy::TYPE_IMAGE:
626       return MozContentPolicyType::Image;
627     case nsIContentPolicy::TYPE_OBJECT:
628       return MozContentPolicyType::Object;
629     case nsIContentPolicy::TYPE_OBJECT_SUBREQUEST:
630       return MozContentPolicyType::Object_subrequest;
631     case nsIContentPolicy::TYPE_XMLHTTPREQUEST:
632       return MozContentPolicyType::Xmlhttprequest;
633     // TYPE_FETCH returns xmlhttprequest for cross-browser compatibility.
634     case nsIContentPolicy::TYPE_FETCH:
635       return MozContentPolicyType::Xmlhttprequest;
636     case nsIContentPolicy::TYPE_XBL:
637       return MozContentPolicyType::Xbl;
638     case nsIContentPolicy::TYPE_XSLT:
639       return MozContentPolicyType::Xslt;
640     case nsIContentPolicy::TYPE_PING:
641       return MozContentPolicyType::Ping;
642     case nsIContentPolicy::TYPE_BEACON:
643       return MozContentPolicyType::Beacon;
644     case nsIContentPolicy::TYPE_DTD:
645       return MozContentPolicyType::Xml_dtd;
646     case nsIContentPolicy::TYPE_FONT:
647       return MozContentPolicyType::Font;
648     case nsIContentPolicy::TYPE_MEDIA:
649       return MozContentPolicyType::Media;
650     case nsIContentPolicy::TYPE_WEBSOCKET:
651       return MozContentPolicyType::Websocket;
652     case nsIContentPolicy::TYPE_CSP_REPORT:
653       return MozContentPolicyType::Csp_report;
654     case nsIContentPolicy::TYPE_IMAGESET:
655       return MozContentPolicyType::Imageset;
656     case nsIContentPolicy::TYPE_WEB_MANIFEST:
657       return MozContentPolicyType::Web_manifest;
658     default:
659       return MozContentPolicyType::Other;
660   }
661 }
662 
Type() const663 MozContentPolicyType ChannelWrapper::Type() const {
664   if (nsCOMPtr<nsILoadInfo> loadInfo = GetLoadInfo()) {
665     return GetContentPolicyType(loadInfo->GetExternalContentPolicyType());
666   }
667   return MozContentPolicyType::Other;
668 }
669 
GetMethod(nsCString & aMethod) const670 void ChannelWrapper::GetMethod(nsCString& aMethod) const {
671   if (nsCOMPtr<nsIHttpChannel> chan = MaybeHttpChannel()) {
672     Unused << chan->GetRequestMethod(aMethod);
673   }
674 }
675 
676 /*****************************************************************************
677  * ...
678  *****************************************************************************/
679 
StatusCode() const680 uint32_t ChannelWrapper::StatusCode() const {
681   uint32_t result = 0;
682   if (nsCOMPtr<nsIHttpChannel> chan = MaybeHttpChannel()) {
683     Unused << chan->GetResponseStatus(&result);
684   }
685   return result;
686 }
687 
GetStatusLine(nsCString & aRetVal) const688 void ChannelWrapper::GetStatusLine(nsCString& aRetVal) const {
689   nsCOMPtr<nsIHttpChannel> chan = MaybeHttpChannel();
690   nsCOMPtr<nsIHttpChannelInternal> internal = do_QueryInterface(chan);
691 
692   if (internal) {
693     nsAutoCString statusText;
694     uint32_t major, minor, status;
695     if (NS_FAILED(chan->GetResponseStatus(&status)) ||
696         NS_FAILED(chan->GetResponseStatusText(statusText)) ||
697         NS_FAILED(internal->GetResponseVersion(&major, &minor))) {
698       return;
699     }
700 
701     aRetVal = nsPrintfCString("HTTP/%u.%u %u %s", major, minor, status,
702                               statusText.get());
703   }
704 }
705 
706 /*****************************************************************************
707  * ...
708  *****************************************************************************/
709 
FinalURI() const710 already_AddRefed<nsIURI> ChannelWrapper::FinalURI() const {
711   nsCOMPtr<nsIURI> uri;
712   if (nsCOMPtr<nsIChannel> chan = MaybeChannel()) {
713     NS_GetFinalChannelURI(chan, getter_AddRefs(uri));
714   }
715   return uri.forget();
716 }
717 
GetFinalURL(nsString & aRetVal) const718 void ChannelWrapper::GetFinalURL(nsString& aRetVal) const {
719   if (HaveChannel()) {
720     aRetVal = FinalURLInfo().Spec();
721   }
722 }
723 
724 /*****************************************************************************
725  * ...
726  *****************************************************************************/
727 
FillProxyInfo(MozProxyInfo & aDict,nsIProxyInfo * aProxyInfo)728 nsresult FillProxyInfo(MozProxyInfo& aDict, nsIProxyInfo* aProxyInfo) {
729   MOZ_TRY(aProxyInfo->GetHost(aDict.mHost));
730   MOZ_TRY(aProxyInfo->GetPort(&aDict.mPort));
731   MOZ_TRY(aProxyInfo->GetType(aDict.mType));
732   MOZ_TRY(aProxyInfo->GetUsername(aDict.mUsername));
733   MOZ_TRY(aProxyInfo->GetFailoverTimeout(&aDict.mFailoverTimeout.Construct()));
734 
735   uint32_t flags;
736   MOZ_TRY(aProxyInfo->GetFlags(&flags));
737   aDict.mProxyDNS = flags & nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST;
738 
739   return NS_OK;
740 }
741 
GetProxyInfo(dom::Nullable<MozProxyInfo> & aRetVal,ErrorResult & aRv) const742 void ChannelWrapper::GetProxyInfo(dom::Nullable<MozProxyInfo>& aRetVal,
743                                   ErrorResult& aRv) const {
744   nsCOMPtr<nsIProxyInfo> proxyInfo;
745   if (nsCOMPtr<nsIProxiedChannel> proxied = QueryChannel()) {
746     Unused << proxied->GetProxyInfo(getter_AddRefs(proxyInfo));
747   }
748   if (proxyInfo) {
749     MozProxyInfo result;
750 
751     nsresult rv = FillProxyInfo(result, proxyInfo);
752     if (NS_FAILED(rv)) {
753       aRv.Throw(rv);
754     } else {
755       aRetVal.SetValue(Move(result));
756     }
757   }
758 }
759 
GetRemoteAddress(nsCString & aRetVal) const760 void ChannelWrapper::GetRemoteAddress(nsCString& aRetVal) const {
761   aRetVal.SetIsVoid(true);
762   if (nsCOMPtr<nsIHttpChannelInternal> internal = QueryChannel()) {
763     Unused << internal->GetRemoteAddress(aRetVal);
764   }
765 }
766 
767 /*****************************************************************************
768  * Error handling
769  *****************************************************************************/
770 
GetErrorString(nsString & aRetVal) const771 void ChannelWrapper::GetErrorString(nsString& aRetVal) const {
772   if (nsCOMPtr<nsIChannel> chan = MaybeChannel()) {
773     nsCOMPtr<nsISupports> securityInfo;
774     Unused << chan->GetSecurityInfo(getter_AddRefs(securityInfo));
775     if (nsCOMPtr<nsITransportSecurityInfo> tsi =
776             do_QueryInterface(securityInfo)) {
777       auto errorCode = tsi->GetErrorCode();
778       if (psm::IsNSSErrorCode(errorCode)) {
779         nsCOMPtr<nsINSSErrorsService> nsserr =
780             do_GetService(NS_NSS_ERRORS_SERVICE_CONTRACTID);
781 
782         nsresult rv = psm::GetXPCOMFromNSSError(errorCode);
783         if (nsserr && NS_SUCCEEDED(nsserr->GetErrorMessage(rv, aRetVal))) {
784           return;
785         }
786       }
787     }
788 
789     nsresult status;
790     if (NS_SUCCEEDED(chan->GetStatus(&status)) && NS_FAILED(status)) {
791       nsAutoCString name;
792       GetErrorName(status, name);
793       AppendUTF8toUTF16(name, aRetVal);
794     } else {
795       aRetVal.SetIsVoid(true);
796     }
797   } else {
798     aRetVal.AssignLiteral("NS_ERROR_UNEXPECTED");
799   }
800 }
801 
ErrorCheck()802 void ChannelWrapper::ErrorCheck() {
803   if (!mFiredErrorEvent) {
804     nsAutoString error;
805     GetErrorString(error);
806     if (error.Length()) {
807       mChannelEntry = nullptr;
808       mFiredErrorEvent = true;
809       ChannelWrapperBinding::ClearCachedErrorStringValue(this);
810       FireEvent(NS_LITERAL_STRING("error"));
811     }
812   }
813 }
814 
815 /*****************************************************************************
816  * nsIWebRequestListener
817  *****************************************************************************/
818 
NS_IMPL_ISUPPORTS(ChannelWrapper::RequestListener,nsIStreamListener,nsIRequestObserver,nsIThreadRetargetableStreamListener)819 NS_IMPL_ISUPPORTS(ChannelWrapper::RequestListener, nsIStreamListener,
820                   nsIRequestObserver, nsIThreadRetargetableStreamListener)
821 
822 ChannelWrapper::RequestListener::~RequestListener() {
823   NS_ReleaseOnMainThreadSystemGroup("RequestListener::mChannelWrapper",
824                                     mChannelWrapper.forget());
825 }
826 
Init()827 nsresult ChannelWrapper::RequestListener::Init() {
828   if (nsCOMPtr<nsITraceableChannel> chan = mChannelWrapper->QueryChannel()) {
829     return chan->SetNewListener(this, getter_AddRefs(mOrigStreamListener));
830   }
831   return NS_ERROR_UNEXPECTED;
832 }
833 
834 NS_IMETHODIMP
OnStartRequest(nsIRequest * request,nsISupports * aCtxt)835 ChannelWrapper::RequestListener::OnStartRequest(nsIRequest* request,
836                                                 nsISupports* aCtxt) {
837   MOZ_ASSERT(mOrigStreamListener, "Should have mOrigStreamListener");
838 
839   mChannelWrapper->mChannelEntry = nullptr;
840   mChannelWrapper->mResponseStarted = true;
841   mChannelWrapper->ErrorCheck();
842   mChannelWrapper->FireEvent(NS_LITERAL_STRING("start"));
843 
844   return mOrigStreamListener->OnStartRequest(request, aCtxt);
845 }
846 
847 NS_IMETHODIMP
OnStopRequest(nsIRequest * request,nsISupports * aCtxt,nsresult aStatus)848 ChannelWrapper::RequestListener::OnStopRequest(nsIRequest* request,
849                                                nsISupports* aCtxt,
850                                                nsresult aStatus) {
851   MOZ_ASSERT(mOrigStreamListener, "Should have mOrigStreamListener");
852 
853   mChannelWrapper->mChannelEntry = nullptr;
854   mChannelWrapper->ErrorCheck();
855   mChannelWrapper->FireEvent(NS_LITERAL_STRING("stop"));
856 
857   return mOrigStreamListener->OnStopRequest(request, aCtxt, aStatus);
858 }
859 
860 NS_IMETHODIMP
OnDataAvailable(nsIRequest * request,nsISupports * aCtxt,nsIInputStream * inStr,uint64_t sourceOffset,uint32_t count)861 ChannelWrapper::RequestListener::OnDataAvailable(nsIRequest* request,
862                                                  nsISupports* aCtxt,
863                                                  nsIInputStream* inStr,
864                                                  uint64_t sourceOffset,
865                                                  uint32_t count) {
866   MOZ_ASSERT(mOrigStreamListener, "Should have mOrigStreamListener");
867   return mOrigStreamListener->OnDataAvailable(request, aCtxt, inStr,
868                                               sourceOffset, count);
869 }
870 
871 NS_IMETHODIMP
CheckListenerChain()872 ChannelWrapper::RequestListener::CheckListenerChain() {
873   MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread!");
874   nsresult rv;
875   nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener =
876       do_QueryInterface(mOrigStreamListener, &rv);
877   if (retargetableListener) {
878     return retargetableListener->CheckListenerChain();
879   }
880   return rv;
881 }
882 
883 /*****************************************************************************
884  * Event dispatching
885  *****************************************************************************/
886 
FireEvent(const nsAString & aType)887 void ChannelWrapper::FireEvent(const nsAString& aType) {
888   EventInit init;
889   init.mBubbles = false;
890   init.mCancelable = false;
891 
892   RefPtr<Event> event = Event::Constructor(this, aType, init);
893   event->SetTrusted(true);
894 
895   bool defaultPrevented;
896   DispatchEvent(event, &defaultPrevented);
897 }
898 
CheckEventListeners()899 void ChannelWrapper::CheckEventListeners() {
900   if (!mAddedStreamListener &&
901       (HasListenersFor(nsGkAtoms::onerror) ||
902        HasListenersFor(nsGkAtoms::onstart) ||
903        HasListenersFor(nsGkAtoms::onstop) || mChannelEntry)) {
904     auto listener = MakeRefPtr<RequestListener>(this);
905     if (!NS_WARN_IF(NS_FAILED(listener->Init()))) {
906       mAddedStreamListener = true;
907     }
908   }
909 }
910 
EventListenerAdded(nsAtom * aType)911 void ChannelWrapper::EventListenerAdded(nsAtom* aType) {
912   CheckEventListeners();
913 }
914 
EventListenerRemoved(nsAtom * aType)915 void ChannelWrapper::EventListenerRemoved(nsAtom* aType) {
916   CheckEventListeners();
917 }
918 
919 /*****************************************************************************
920  * Glue
921  *****************************************************************************/
922 
WrapObject(JSContext * aCx,HandleObject aGivenProto)923 JSObject* ChannelWrapper::WrapObject(JSContext* aCx, HandleObject aGivenProto) {
924   return ChannelWrapperBinding::Wrap(aCx, this, aGivenProto);
925 }
926 
927 NS_IMPL_CYCLE_COLLECTION_CLASS(ChannelWrapper)
928 
929 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ChannelWrapper)
930   NS_INTERFACE_MAP_ENTRY(ChannelWrapper)
931 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
932 
933 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ChannelWrapper,
934                                                 DOMEventTargetHelper)
935   NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
936 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
937 
938 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ChannelWrapper,
939                                                   DOMEventTargetHelper)
940   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
941 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
942 
943 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(ChannelWrapper,
944                                                DOMEventTargetHelper)
945 NS_IMPL_CYCLE_COLLECTION_TRACE_END
946 
947 NS_IMPL_ADDREF_INHERITED(ChannelWrapper, DOMEventTargetHelper)
948 NS_IMPL_RELEASE_INHERITED(ChannelWrapper, DOMEventTargetHelper)
949 
950 }  // namespace extensions
951 }  // namespace mozilla
952