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