Bug 1845017 - Disable the TestPHCExhaustion test r=glandium
[gecko.git] / uriloader / exthandler / nsExternalProtocolHandler.cpp
blob4e93977b8f397f1d85052719f0e65df1e2135769
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim:set ts=2 sts=2 sw=2 et cin:
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include "mozilla/dom/ContentChild.h"
9 #include "mozilla/BasePrincipal.h"
10 #include "nsIURI.h"
11 #include "nsExternalProtocolHandler.h"
12 #include "nsString.h"
13 #include "nsReadableUtils.h"
14 #include "nsCOMPtr.h"
15 #include "nsContentUtils.h"
16 #include "nsServiceManagerUtils.h"
17 #include "nsIInterfaceRequestor.h"
18 #include "nsIInterfaceRequestorUtils.h"
19 #include "nsIRedirectHistoryEntry.h"
20 #include "nsNetUtil.h"
21 #include "nsContentSecurityManager.h"
22 #include "nsExternalHelperAppService.h"
24 // used to dispatch urls to default protocol handlers
25 #include "nsCExternalHandlerService.h"
26 #include "nsIExternalProtocolService.h"
27 #include "nsIChildChannel.h"
28 #include "nsIParentChannel.h"
30 class nsILoadInfo;
32 ////////////////////////////////////////////////////////////////////////
33 // a stub channel implemenation which will map calls to AsyncRead and
34 // OpenInputStream to calls in the OS for loading the url.
35 ////////////////////////////////////////////////////////////////////////
37 class nsExtProtocolChannel : public nsIChannel,
38 public nsIChildChannel,
39 public nsIParentChannel {
40 public:
41 NS_DECL_THREADSAFE_ISUPPORTS
42 NS_DECL_NSICHANNEL
43 NS_DECL_NSIREQUESTOBSERVER
44 NS_DECL_NSISTREAMLISTENER
45 NS_DECL_NSIREQUEST
46 NS_DECL_NSICHILDCHANNEL
47 NS_DECL_NSIPARENTCHANNEL
49 nsExtProtocolChannel(nsIURI* aURI, nsILoadInfo* aLoadInfo);
51 private:
52 virtual ~nsExtProtocolChannel();
54 nsresult OpenURL();
55 void Finish(nsresult aResult);
57 nsCOMPtr<nsIURI> mUrl;
58 nsCOMPtr<nsIURI> mOriginalURI;
59 nsresult mStatus;
60 nsLoadFlags mLoadFlags;
61 bool mWasOpened;
62 bool mCanceled;
63 // Set true (as a result of ConnectParent invoked from child process)
64 // when this channel is on the parent process and is being used as
65 // a redirect target channel. It turns AsyncOpen into a no-op since
66 // we do it on the child.
67 bool mConnectedParent;
69 nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
70 nsCOMPtr<nsILoadGroup> mLoadGroup;
71 nsCOMPtr<nsILoadInfo> mLoadInfo;
72 nsCOMPtr<nsIStreamListener> mListener;
75 NS_IMPL_ADDREF(nsExtProtocolChannel)
76 NS_IMPL_RELEASE(nsExtProtocolChannel)
78 NS_INTERFACE_MAP_BEGIN(nsExtProtocolChannel)
79 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIChannel)
80 NS_INTERFACE_MAP_ENTRY(nsIChannel)
81 NS_INTERFACE_MAP_ENTRY(nsIRequest)
82 NS_INTERFACE_MAP_ENTRY(nsIChildChannel)
83 NS_INTERFACE_MAP_ENTRY(nsIParentChannel)
84 NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
85 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
86 NS_INTERFACE_MAP_END
88 nsExtProtocolChannel::nsExtProtocolChannel(nsIURI* aURI, nsILoadInfo* aLoadInfo)
89 : mUrl(aURI),
90 mOriginalURI(aURI),
91 mStatus(NS_OK),
92 mLoadFlags(nsIRequest::LOAD_NORMAL),
93 mWasOpened(false),
94 mCanceled(false),
95 mConnectedParent(false),
96 mLoadInfo(aLoadInfo) {}
98 nsExtProtocolChannel::~nsExtProtocolChannel() {}
100 NS_IMETHODIMP nsExtProtocolChannel::GetLoadGroup(nsILoadGroup** aLoadGroup) {
101 NS_IF_ADDREF(*aLoadGroup = mLoadGroup);
102 return NS_OK;
105 NS_IMETHODIMP nsExtProtocolChannel::SetLoadGroup(nsILoadGroup* aLoadGroup) {
106 mLoadGroup = aLoadGroup;
107 return NS_OK;
110 NS_IMETHODIMP nsExtProtocolChannel::GetNotificationCallbacks(
111 nsIInterfaceRequestor** aCallbacks) {
112 NS_IF_ADDREF(*aCallbacks = mCallbacks);
113 return NS_OK;
116 NS_IMETHODIMP nsExtProtocolChannel::SetNotificationCallbacks(
117 nsIInterfaceRequestor* aCallbacks) {
118 mCallbacks = aCallbacks;
119 return NS_OK;
122 NS_IMETHODIMP
123 nsExtProtocolChannel::GetSecurityInfo(
124 nsITransportSecurityInfo** aSecurityInfo) {
125 *aSecurityInfo = nullptr;
126 return NS_OK;
129 NS_IMETHODIMP nsExtProtocolChannel::GetOriginalURI(nsIURI** aURI) {
130 NS_ADDREF(*aURI = mOriginalURI);
131 return NS_OK;
134 NS_IMETHODIMP nsExtProtocolChannel::SetOriginalURI(nsIURI* aURI) {
135 NS_ENSURE_ARG_POINTER(aURI);
136 mOriginalURI = aURI;
137 return NS_OK;
140 NS_IMETHODIMP nsExtProtocolChannel::GetURI(nsIURI** aURI) {
141 *aURI = mUrl;
142 NS_IF_ADDREF(*aURI);
143 return NS_OK;
146 nsresult nsExtProtocolChannel::OpenURL() {
147 nsresult rv = NS_ERROR_FAILURE;
148 nsCOMPtr<nsIExternalProtocolService> extProtService(
149 do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID));
151 if (extProtService) {
152 #ifdef DEBUG
153 nsAutoCString urlScheme;
154 mUrl->GetScheme(urlScheme);
155 bool haveHandler = false;
156 extProtService->ExternalProtocolHandlerExists(urlScheme.get(),
157 &haveHandler);
158 NS_ASSERTION(haveHandler,
159 "Why do we have a channel for this url if we don't support "
160 "the protocol?");
161 #endif
163 RefPtr<mozilla::dom::BrowsingContext> ctx;
164 rv = mLoadInfo->GetTargetBrowsingContext(getter_AddRefs(ctx));
165 if (NS_FAILED(rv)) {
166 goto finish;
169 RefPtr<nsIPrincipal> triggeringPrincipal = mLoadInfo->TriggeringPrincipal();
170 RefPtr<nsIPrincipal> redirectPrincipal;
171 if (!mLoadInfo->RedirectChain().IsEmpty()) {
172 mLoadInfo->RedirectChain().LastElement()->GetPrincipal(
173 getter_AddRefs(redirectPrincipal));
175 rv = extProtService->LoadURI(mUrl, triggeringPrincipal, redirectPrincipal,
176 ctx, mLoadInfo->GetLoadTriggeredFromExternal(),
177 mLoadInfo->GetHasValidUserGestureActivation());
179 if (NS_SUCCEEDED(rv) && mListener) {
180 mStatus = NS_ERROR_NO_CONTENT;
182 RefPtr<nsExtProtocolChannel> self = this;
183 nsCOMPtr<nsIStreamListener> listener = mListener;
184 MessageLoop::current()->PostTask(NS_NewRunnableFunction(
185 "nsExtProtocolChannel::OpenURL", [self, listener]() {
186 listener->OnStartRequest(self);
187 listener->OnStopRequest(self, self->mStatus);
188 }));
192 finish:
193 mCallbacks = nullptr;
194 mListener = nullptr;
195 return rv;
198 NS_IMETHODIMP nsExtProtocolChannel::Open(nsIInputStream** aStream) {
199 nsCOMPtr<nsIStreamListener> listener;
200 nsresult rv =
201 nsContentSecurityManager::doContentSecurityCheck(this, listener);
202 NS_ENSURE_SUCCESS(rv, rv);
204 return OpenURL();
207 NS_IMETHODIMP nsExtProtocolChannel::AsyncOpen(nsIStreamListener* aListener) {
208 nsCOMPtr<nsIStreamListener> listener = aListener;
209 nsresult rv =
210 nsContentSecurityManager::doContentSecurityCheck(this, listener);
211 if (NS_FAILED(rv)) {
212 mCallbacks = nullptr;
213 return rv;
216 if (mConnectedParent) {
217 return NS_OK;
220 MOZ_ASSERT(
221 mLoadInfo->GetSecurityMode() == 0 ||
222 mLoadInfo->GetInitialSecurityCheckDone() ||
223 (mLoadInfo->GetSecurityMode() ==
224 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL &&
225 mLoadInfo->GetLoadingPrincipal() &&
226 mLoadInfo->GetLoadingPrincipal()->IsSystemPrincipal()),
227 "security flags in loadInfo but doContentSecurityCheck() not called");
229 NS_ENSURE_ARG_POINTER(listener);
230 NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
232 mWasOpened = true;
233 mListener = listener;
235 return OpenURL();
238 NS_IMETHODIMP nsExtProtocolChannel::GetLoadFlags(nsLoadFlags* aLoadFlags) {
239 *aLoadFlags = mLoadFlags;
240 return NS_OK;
243 NS_IMETHODIMP nsExtProtocolChannel::SetLoadFlags(nsLoadFlags aLoadFlags) {
244 mLoadFlags = aLoadFlags;
245 return NS_OK;
248 NS_IMETHODIMP nsExtProtocolChannel::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
249 return GetTRRModeImpl(aTRRMode);
252 NS_IMETHODIMP nsExtProtocolChannel::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
253 return SetTRRModeImpl(aTRRMode);
256 NS_IMETHODIMP nsExtProtocolChannel::GetIsDocument(bool* aIsDocument) {
257 return NS_GetIsDocumentChannel(this, aIsDocument);
260 NS_IMETHODIMP nsExtProtocolChannel::GetContentType(nsACString& aContentType) {
261 return NS_ERROR_NOT_IMPLEMENTED;
264 NS_IMETHODIMP nsExtProtocolChannel::SetContentType(
265 const nsACString& aContentType) {
266 return NS_ERROR_FAILURE;
269 NS_IMETHODIMP nsExtProtocolChannel::GetContentCharset(
270 nsACString& aContentCharset) {
271 return NS_ERROR_NOT_IMPLEMENTED;
274 NS_IMETHODIMP nsExtProtocolChannel::SetContentCharset(
275 const nsACString& aContentCharset) {
276 return NS_ERROR_NOT_IMPLEMENTED;
279 NS_IMETHODIMP nsExtProtocolChannel::GetContentDisposition(
280 uint32_t* aContentDisposition) {
281 return NS_ERROR_NOT_AVAILABLE;
284 NS_IMETHODIMP nsExtProtocolChannel::SetContentDisposition(
285 uint32_t aContentDisposition) {
286 return NS_ERROR_NOT_AVAILABLE;
289 NS_IMETHODIMP nsExtProtocolChannel::GetContentDispositionFilename(
290 nsAString& aContentDispositionFilename) {
291 return NS_ERROR_NOT_AVAILABLE;
294 NS_IMETHODIMP nsExtProtocolChannel::SetContentDispositionFilename(
295 const nsAString& aContentDispositionFilename) {
296 return NS_ERROR_NOT_AVAILABLE;
299 NS_IMETHODIMP nsExtProtocolChannel::GetContentDispositionHeader(
300 nsACString& aContentDispositionHeader) {
301 return NS_ERROR_NOT_AVAILABLE;
304 NS_IMETHODIMP nsExtProtocolChannel::GetContentLength(int64_t* aContentLength) {
305 *aContentLength = -1;
306 return NS_OK;
309 NS_IMETHODIMP
310 nsExtProtocolChannel::SetContentLength(int64_t aContentLength) {
311 MOZ_ASSERT_UNREACHABLE("SetContentLength");
312 return NS_ERROR_NOT_IMPLEMENTED;
315 NS_IMETHODIMP nsExtProtocolChannel::GetOwner(nsISupports** aPrincipal) {
316 return NS_ERROR_NOT_IMPLEMENTED;
319 NS_IMETHODIMP nsExtProtocolChannel::SetOwner(nsISupports* aPrincipal) {
320 return NS_ERROR_NOT_IMPLEMENTED;
323 NS_IMETHODIMP nsExtProtocolChannel::GetLoadInfo(nsILoadInfo** aLoadInfo) {
324 NS_IF_ADDREF(*aLoadInfo = mLoadInfo);
325 return NS_OK;
328 NS_IMETHODIMP nsExtProtocolChannel::SetLoadInfo(nsILoadInfo* aLoadInfo) {
329 MOZ_RELEASE_ASSERT(aLoadInfo, "loadinfo can't be null");
330 mLoadInfo = aLoadInfo;
331 return NS_OK;
334 ////////////////////////////////////////////////////////////////////////////////
335 // From nsIRequest
336 ////////////////////////////////////////////////////////////////////////////////
338 NS_IMETHODIMP nsExtProtocolChannel::GetName(nsACString& result) {
339 return mUrl->GetSpec(result);
342 NS_IMETHODIMP nsExtProtocolChannel::IsPending(bool* result) {
343 *result = false;
344 return NS_OK;
347 NS_IMETHODIMP nsExtProtocolChannel::GetStatus(nsresult* status) {
348 *status = mStatus;
349 return NS_OK;
352 NS_IMETHODIMP nsExtProtocolChannel::SetCanceledReason(
353 const nsACString& aReason) {
354 return SetCanceledReasonImpl(aReason);
357 NS_IMETHODIMP nsExtProtocolChannel::GetCanceledReason(nsACString& aReason) {
358 return GetCanceledReasonImpl(aReason);
361 NS_IMETHODIMP nsExtProtocolChannel::CancelWithReason(
362 nsresult aStatus, const nsACString& aReason) {
363 return CancelWithReasonImpl(aStatus, aReason);
366 NS_IMETHODIMP nsExtProtocolChannel::Cancel(nsresult status) {
367 if (NS_SUCCEEDED(mStatus)) {
368 mStatus = status;
370 mCanceled = true;
371 return NS_OK;
374 NS_IMETHODIMP nsExtProtocolChannel::GetCanceled(bool* aCanceled) {
375 *aCanceled = mCanceled;
376 return NS_OK;
379 NS_IMETHODIMP nsExtProtocolChannel::Suspend() {
380 MOZ_ASSERT_UNREACHABLE("Suspend");
381 return NS_ERROR_NOT_IMPLEMENTED;
384 NS_IMETHODIMP nsExtProtocolChannel::Resume() {
385 MOZ_ASSERT_UNREACHABLE("Resume");
386 return NS_ERROR_NOT_IMPLEMENTED;
389 ///////////////////////////////////////////////////////////////////////
390 // From nsIChildChannel
391 //////////////////////////////////////////////////////////////////////
393 NS_IMETHODIMP nsExtProtocolChannel::ConnectParent(uint32_t registrarId) {
394 mozilla::dom::ContentChild::GetSingleton()
395 ->SendExtProtocolChannelConnectParent(registrarId);
396 return NS_OK;
399 NS_IMETHODIMP nsExtProtocolChannel::CompleteRedirectSetup(
400 nsIStreamListener* listener) {
401 // For redirects to external protocols we AsyncOpen on the child
402 // (not the parent) because child channel has the right docshell
403 // (which is needed for the select dialog).
404 return AsyncOpen(listener);
407 ///////////////////////////////////////////////////////////////////////
408 // From nsIParentChannel (derives from nsIStreamListener)
409 //////////////////////////////////////////////////////////////////////
411 NS_IMETHODIMP nsExtProtocolChannel::SetParentListener(
412 mozilla::net::ParentChannelListener* aListener) {
413 // This is called as part of the connect parent operation from
414 // ContentParent::RecvExtProtocolChannelConnectParent. Setting
415 // this flag tells this channel to not proceed and makes AsyncOpen
416 // just no-op. Actual operation will happen from the child process
417 // via CompleteRedirectSetup call on the child channel.
418 mConnectedParent = true;
419 return NS_OK;
422 NS_IMETHODIMP nsExtProtocolChannel::SetClassifierMatchedInfo(
423 const nsACString& aList, const nsACString& aProvider,
424 const nsACString& aFullHash) {
425 // nothing to do
426 return NS_OK;
429 NS_IMETHODIMP nsExtProtocolChannel::SetClassifierMatchedTrackingInfo(
430 const nsACString& aLists, const nsACString& aFullHashes) {
431 // nothing to do
432 return NS_OK;
435 NS_IMETHODIMP nsExtProtocolChannel::NotifyClassificationFlags(
436 uint32_t aClassificationFlags, bool aIsThirdParty) {
437 // nothing to do
438 return NS_OK;
441 NS_IMETHODIMP nsExtProtocolChannel::Delete() {
442 // nothing to do
443 return NS_OK;
446 NS_IMETHODIMP nsExtProtocolChannel::GetRemoteType(nsACString& aRemoteType) {
447 return NS_ERROR_NOT_IMPLEMENTED;
450 NS_IMETHODIMP nsExtProtocolChannel::OnStartRequest(nsIRequest* aRequest) {
451 return NS_ERROR_UNEXPECTED;
454 NS_IMETHODIMP nsExtProtocolChannel::OnStopRequest(nsIRequest* aRequest,
455 nsresult aStatusCode) {
456 MOZ_ASSERT(NS_FAILED(aStatusCode));
457 return NS_ERROR_UNEXPECTED;
460 NS_IMETHODIMP nsExtProtocolChannel::OnDataAvailable(
461 nsIRequest* aRequest, nsIInputStream* aInputStream, uint64_t aOffset,
462 uint32_t aCount) {
463 // no data is expected
464 MOZ_CRASH("No data expected from external protocol channel");
465 return NS_ERROR_UNEXPECTED;
468 ///////////////////////////////////////////////////////////////////////
469 // the default protocol handler implementation
470 //////////////////////////////////////////////////////////////////////
472 nsExternalProtocolHandler::nsExternalProtocolHandler() {
473 m_schemeName = "default";
476 nsExternalProtocolHandler::~nsExternalProtocolHandler() {}
478 NS_IMPL_ADDREF(nsExternalProtocolHandler)
479 NS_IMPL_RELEASE(nsExternalProtocolHandler)
481 NS_INTERFACE_MAP_BEGIN(nsExternalProtocolHandler)
482 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIProtocolHandler)
483 NS_INTERFACE_MAP_ENTRY(nsIProtocolHandler)
484 NS_INTERFACE_MAP_ENTRY(nsIExternalProtocolHandler)
485 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
486 NS_INTERFACE_MAP_END
488 NS_IMETHODIMP nsExternalProtocolHandler::GetScheme(nsACString& aScheme) {
489 aScheme = m_schemeName;
490 return NS_OK;
493 NS_IMETHODIMP
494 nsExternalProtocolHandler::AllowPort(int32_t port, const char* scheme,
495 bool* _retval) {
496 // don't override anything.
497 *_retval = false;
498 return NS_OK;
500 // returns TRUE if the OS can handle this protocol scheme and false otherwise.
501 bool nsExternalProtocolHandler::HaveExternalProtocolHandler(nsIURI* aURI) {
502 MOZ_ASSERT(aURI);
503 nsAutoCString scheme;
504 aURI->GetScheme(scheme);
506 nsCOMPtr<nsIExternalProtocolService> extProtSvc(
507 do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID));
508 if (!extProtSvc) {
509 return false;
512 bool haveHandler = false;
513 extProtSvc->ExternalProtocolHandlerExists(scheme.get(), &haveHandler);
514 return haveHandler;
517 NS_IMETHODIMP
518 nsExternalProtocolHandler::NewChannel(nsIURI* aURI, nsILoadInfo* aLoadInfo,
519 nsIChannel** aRetval) {
520 NS_ENSURE_TRUE(aURI, NS_ERROR_UNKNOWN_PROTOCOL);
521 NS_ENSURE_TRUE(aRetval, NS_ERROR_UNKNOWN_PROTOCOL);
523 // Only try to return a channel if we have a protocol handler for the url.
524 // nsOSHelperAppService::LoadUriInternal relies on this to check trustedness
525 // for some platforms at least. (win uses ::ShellExecute and unix uses
526 // gnome_url_show.)
527 if (!HaveExternalProtocolHandler(aURI)) {
528 return NS_ERROR_UNKNOWN_PROTOCOL;
531 nsCOMPtr<nsIChannel> channel = new nsExtProtocolChannel(aURI, aLoadInfo);
532 channel.forget(aRetval);
533 return NS_OK;
536 ///////////////////////////////////////////////////////////////////////
537 // External protocol handler interface implementation
538 //////////////////////////////////////////////////////////////////////
539 NS_IMETHODIMP nsExternalProtocolHandler::ExternalAppExistsForScheme(
540 const nsACString& aScheme, bool* _retval) {
541 nsCOMPtr<nsIExternalProtocolService> extProtSvc(
542 do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID));
543 if (extProtSvc)
544 return extProtSvc->ExternalProtocolHandlerExists(
545 PromiseFlatCString(aScheme).get(), _retval);
547 // In case we don't have external protocol service.
548 *_retval = false;
549 return NS_OK;