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"
11 #include "nsExternalProtocolHandler.h"
13 #include "nsReadableUtils.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"
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
{
41 NS_DECL_THREADSAFE_ISUPPORTS
43 NS_DECL_NSIREQUESTOBSERVER
44 NS_DECL_NSISTREAMLISTENER
46 NS_DECL_NSICHILDCHANNEL
47 NS_DECL_NSIPARENTCHANNEL
49 nsExtProtocolChannel(nsIURI
* aURI
, nsILoadInfo
* aLoadInfo
);
52 virtual ~nsExtProtocolChannel();
55 void Finish(nsresult aResult
);
57 nsCOMPtr
<nsIURI
> mUrl
;
58 nsCOMPtr
<nsIURI
> mOriginalURI
;
60 nsLoadFlags mLoadFlags
;
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
)
88 nsExtProtocolChannel::nsExtProtocolChannel(nsIURI
* aURI
, nsILoadInfo
* aLoadInfo
)
92 mLoadFlags(nsIRequest::LOAD_NORMAL
),
95 mConnectedParent(false),
96 mLoadInfo(aLoadInfo
) {}
98 nsExtProtocolChannel::~nsExtProtocolChannel() {}
100 NS_IMETHODIMP
nsExtProtocolChannel::GetLoadGroup(nsILoadGroup
** aLoadGroup
) {
101 NS_IF_ADDREF(*aLoadGroup
= mLoadGroup
);
105 NS_IMETHODIMP
nsExtProtocolChannel::SetLoadGroup(nsILoadGroup
* aLoadGroup
) {
106 mLoadGroup
= aLoadGroup
;
110 NS_IMETHODIMP
nsExtProtocolChannel::GetNotificationCallbacks(
111 nsIInterfaceRequestor
** aCallbacks
) {
112 NS_IF_ADDREF(*aCallbacks
= mCallbacks
);
116 NS_IMETHODIMP
nsExtProtocolChannel::SetNotificationCallbacks(
117 nsIInterfaceRequestor
* aCallbacks
) {
118 mCallbacks
= aCallbacks
;
123 nsExtProtocolChannel::GetSecurityInfo(
124 nsITransportSecurityInfo
** aSecurityInfo
) {
125 *aSecurityInfo
= nullptr;
129 NS_IMETHODIMP
nsExtProtocolChannel::GetOriginalURI(nsIURI
** aURI
) {
130 NS_ADDREF(*aURI
= mOriginalURI
);
134 NS_IMETHODIMP
nsExtProtocolChannel::SetOriginalURI(nsIURI
* aURI
) {
135 NS_ENSURE_ARG_POINTER(aURI
);
140 NS_IMETHODIMP
nsExtProtocolChannel::GetURI(nsIURI
** aURI
) {
146 nsresult
nsExtProtocolChannel::OpenURL() {
147 nsresult rv
= NS_ERROR_FAILURE
;
148 nsCOMPtr
<nsIExternalProtocolService
> extProtService(
149 do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID
));
151 if (extProtService
) {
153 nsAutoCString urlScheme
;
154 mUrl
->GetScheme(urlScheme
);
155 bool haveHandler
= false;
156 extProtService
->ExternalProtocolHandlerExists(urlScheme
.get(),
158 NS_ASSERTION(haveHandler
,
159 "Why do we have a channel for this url if we don't support "
163 RefPtr
<mozilla::dom::BrowsingContext
> ctx
;
164 rv
= mLoadInfo
->GetTargetBrowsingContext(getter_AddRefs(ctx
));
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
);
193 mCallbacks
= nullptr;
198 NS_IMETHODIMP
nsExtProtocolChannel::Open(nsIInputStream
** aStream
) {
199 nsCOMPtr
<nsIStreamListener
> listener
;
201 nsContentSecurityManager::doContentSecurityCheck(this, listener
);
202 NS_ENSURE_SUCCESS(rv
, rv
);
207 NS_IMETHODIMP
nsExtProtocolChannel::AsyncOpen(nsIStreamListener
* aListener
) {
208 nsCOMPtr
<nsIStreamListener
> listener
= aListener
;
210 nsContentSecurityManager::doContentSecurityCheck(this, listener
);
212 mCallbacks
= nullptr;
216 if (mConnectedParent
) {
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
);
233 mListener
= listener
;
238 NS_IMETHODIMP
nsExtProtocolChannel::GetLoadFlags(nsLoadFlags
* aLoadFlags
) {
239 *aLoadFlags
= mLoadFlags
;
243 NS_IMETHODIMP
nsExtProtocolChannel::SetLoadFlags(nsLoadFlags aLoadFlags
) {
244 mLoadFlags
= aLoadFlags
;
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;
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
);
328 NS_IMETHODIMP
nsExtProtocolChannel::SetLoadInfo(nsILoadInfo
* aLoadInfo
) {
329 MOZ_RELEASE_ASSERT(aLoadInfo
, "loadinfo can't be null");
330 mLoadInfo
= aLoadInfo
;
334 ////////////////////////////////////////////////////////////////////////////////
336 ////////////////////////////////////////////////////////////////////////////////
338 NS_IMETHODIMP
nsExtProtocolChannel::GetName(nsACString
& result
) {
339 return mUrl
->GetSpec(result
);
342 NS_IMETHODIMP
nsExtProtocolChannel::IsPending(bool* result
) {
347 NS_IMETHODIMP
nsExtProtocolChannel::GetStatus(nsresult
* status
) {
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
)) {
374 NS_IMETHODIMP
nsExtProtocolChannel::GetCanceled(bool* aCanceled
) {
375 *aCanceled
= mCanceled
;
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
);
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;
422 NS_IMETHODIMP
nsExtProtocolChannel::SetClassifierMatchedInfo(
423 const nsACString
& aList
, const nsACString
& aProvider
,
424 const nsACString
& aFullHash
) {
429 NS_IMETHODIMP
nsExtProtocolChannel::SetClassifierMatchedTrackingInfo(
430 const nsACString
& aLists
, const nsACString
& aFullHashes
) {
435 NS_IMETHODIMP
nsExtProtocolChannel::NotifyClassificationFlags(
436 uint32_t aClassificationFlags
, bool aIsThirdParty
) {
441 NS_IMETHODIMP
nsExtProtocolChannel::Delete() {
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
,
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
)
488 NS_IMETHODIMP
nsExternalProtocolHandler::GetScheme(nsACString
& aScheme
) {
489 aScheme
= m_schemeName
;
494 nsExternalProtocolHandler::AllowPort(int32_t port
, const char* scheme
,
496 // don't override anything.
500 // returns TRUE if the OS can handle this protocol scheme and false otherwise.
501 bool nsExternalProtocolHandler::HaveExternalProtocolHandler(nsIURI
* aURI
) {
503 nsAutoCString scheme
;
504 aURI
->GetScheme(scheme
);
506 nsCOMPtr
<nsIExternalProtocolService
> extProtSvc(
507 do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID
));
512 bool haveHandler
= false;
513 extProtSvc
->ExternalProtocolHandlerExists(scheme
.get(), &haveHandler
);
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
527 if (!HaveExternalProtocolHandler(aURI
)) {
528 return NS_ERROR_UNKNOWN_PROTOCOL
;
531 nsCOMPtr
<nsIChannel
> channel
= new nsExtProtocolChannel(aURI
, aLoadInfo
);
532 channel
.forget(aRetval
);
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
));
544 return extProtSvc
->ExternalProtocolHandlerExists(
545 PromiseFlatCString(aScheme
).get(), _retval
);
547 // In case we don't have external protocol service.