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 "nsNetUtil.h"
20 #include "nsContentSecurityManager.h"
21 #include "nsExternalHelperAppService.h"
23 // used to dispatch urls to default protocol handlers
24 #include "nsCExternalHandlerService.h"
25 #include "nsIExternalProtocolService.h"
26 #include "nsIChildChannel.h"
27 #include "nsIParentChannel.h"
31 ////////////////////////////////////////////////////////////////////////
32 // a stub channel implemenation which will map calls to AsyncRead and
33 // OpenInputStream to calls in the OS for loading the url.
34 ////////////////////////////////////////////////////////////////////////
36 class nsExtProtocolChannel
: public nsIChannel
,
37 public nsIChildChannel
,
38 public nsIParentChannel
{
40 NS_DECL_THREADSAFE_ISUPPORTS
42 NS_DECL_NSIREQUESTOBSERVER
43 NS_DECL_NSISTREAMLISTENER
45 NS_DECL_NSICHILDCHANNEL
46 NS_DECL_NSIPARENTCHANNEL
48 nsExtProtocolChannel(nsIURI
* aURI
, nsILoadInfo
* aLoadInfo
);
51 virtual ~nsExtProtocolChannel();
54 void Finish(nsresult aResult
);
56 nsCOMPtr
<nsIURI
> mUrl
;
57 nsCOMPtr
<nsIURI
> mOriginalURI
;
59 nsLoadFlags mLoadFlags
;
62 // Set true (as a result of ConnectParent invoked from child process)
63 // when this channel is on the parent process and is being used as
64 // a redirect target channel. It turns AsyncOpen into a no-op since
65 // we do it on the child.
66 bool mConnectedParent
;
68 nsCOMPtr
<nsIInterfaceRequestor
> mCallbacks
;
69 nsCOMPtr
<nsILoadGroup
> mLoadGroup
;
70 nsCOMPtr
<nsILoadInfo
> mLoadInfo
;
71 nsCOMPtr
<nsIStreamListener
> mListener
;
74 NS_IMPL_ADDREF(nsExtProtocolChannel
)
75 NS_IMPL_RELEASE(nsExtProtocolChannel
)
77 NS_INTERFACE_MAP_BEGIN(nsExtProtocolChannel
)
78 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIChannel
)
79 NS_INTERFACE_MAP_ENTRY(nsIChannel
)
80 NS_INTERFACE_MAP_ENTRY(nsIRequest
)
81 NS_INTERFACE_MAP_ENTRY(nsIChildChannel
)
82 NS_INTERFACE_MAP_ENTRY(nsIParentChannel
)
83 NS_INTERFACE_MAP_ENTRY(nsIStreamListener
)
84 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver
)
87 nsExtProtocolChannel::nsExtProtocolChannel(nsIURI
* aURI
, nsILoadInfo
* aLoadInfo
)
91 mLoadFlags(nsIRequest::LOAD_NORMAL
),
94 mConnectedParent(false),
95 mLoadInfo(aLoadInfo
) {}
97 nsExtProtocolChannel::~nsExtProtocolChannel() {}
99 NS_IMETHODIMP
nsExtProtocolChannel::GetLoadGroup(nsILoadGroup
** aLoadGroup
) {
100 NS_IF_ADDREF(*aLoadGroup
= mLoadGroup
);
104 NS_IMETHODIMP
nsExtProtocolChannel::SetLoadGroup(nsILoadGroup
* aLoadGroup
) {
105 mLoadGroup
= aLoadGroup
;
109 NS_IMETHODIMP
nsExtProtocolChannel::GetNotificationCallbacks(
110 nsIInterfaceRequestor
** aCallbacks
) {
111 NS_IF_ADDREF(*aCallbacks
= mCallbacks
);
115 NS_IMETHODIMP
nsExtProtocolChannel::SetNotificationCallbacks(
116 nsIInterfaceRequestor
* aCallbacks
) {
117 mCallbacks
= aCallbacks
;
122 nsExtProtocolChannel::GetSecurityInfo(nsISupports
** aSecurityInfo
) {
123 *aSecurityInfo
= nullptr;
127 NS_IMETHODIMP
nsExtProtocolChannel::GetOriginalURI(nsIURI
** aURI
) {
128 NS_ADDREF(*aURI
= mOriginalURI
);
132 NS_IMETHODIMP
nsExtProtocolChannel::SetOriginalURI(nsIURI
* aURI
) {
133 NS_ENSURE_ARG_POINTER(aURI
);
138 NS_IMETHODIMP
nsExtProtocolChannel::GetURI(nsIURI
** aURI
) {
144 nsresult
nsExtProtocolChannel::OpenURL() {
145 nsresult rv
= NS_ERROR_FAILURE
;
146 nsCOMPtr
<nsIExternalProtocolService
> extProtService(
147 do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID
));
149 if (extProtService
) {
151 nsAutoCString urlScheme
;
152 mUrl
->GetScheme(urlScheme
);
153 bool haveHandler
= false;
154 extProtService
->ExternalProtocolHandlerExists(urlScheme
.get(),
156 NS_ASSERTION(haveHandler
,
157 "Why do we have a channel for this url if we don't support "
161 RefPtr
<mozilla::dom::BrowsingContext
> ctx
;
162 rv
= mLoadInfo
->GetTargetBrowsingContext(getter_AddRefs(ctx
));
167 RefPtr
<nsIPrincipal
> principal
= mLoadInfo
->TriggeringPrincipal();
168 rv
= extProtService
->LoadURI(mUrl
, principal
, ctx
,
169 mLoadInfo
->GetLoadTriggeredFromExternal());
171 if (NS_SUCCEEDED(rv
) && mListener
) {
172 mStatus
= NS_ERROR_NO_CONTENT
;
174 RefPtr
<nsExtProtocolChannel
> self
= this;
175 nsCOMPtr
<nsIStreamListener
> listener
= mListener
;
176 MessageLoop::current()->PostTask(NS_NewRunnableFunction(
177 "nsExtProtocolChannel::OpenURL", [self
, listener
]() {
178 listener
->OnStartRequest(self
);
179 listener
->OnStopRequest(self
, self
->mStatus
);
185 mCallbacks
= nullptr;
190 NS_IMETHODIMP
nsExtProtocolChannel::Open(nsIInputStream
** aStream
) {
191 nsCOMPtr
<nsIStreamListener
> listener
;
193 nsContentSecurityManager::doContentSecurityCheck(this, listener
);
194 NS_ENSURE_SUCCESS(rv
, rv
);
199 NS_IMETHODIMP
nsExtProtocolChannel::AsyncOpen(nsIStreamListener
* aListener
) {
200 nsCOMPtr
<nsIStreamListener
> listener
= aListener
;
202 nsContentSecurityManager::doContentSecurityCheck(this, listener
);
204 mCallbacks
= nullptr;
208 if (mConnectedParent
) {
213 mLoadInfo
->GetSecurityMode() == 0 ||
214 mLoadInfo
->GetInitialSecurityCheckDone() ||
215 (mLoadInfo
->GetSecurityMode() ==
216 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL
&&
217 mLoadInfo
->GetLoadingPrincipal() &&
218 mLoadInfo
->GetLoadingPrincipal()->IsSystemPrincipal()),
219 "security flags in loadInfo but doContentSecurityCheck() not called");
221 NS_ENSURE_ARG_POINTER(listener
);
222 NS_ENSURE_TRUE(!mWasOpened
, NS_ERROR_ALREADY_OPENED
);
225 mListener
= listener
;
230 NS_IMETHODIMP
nsExtProtocolChannel::GetLoadFlags(nsLoadFlags
* aLoadFlags
) {
231 *aLoadFlags
= mLoadFlags
;
235 NS_IMETHODIMP
nsExtProtocolChannel::SetLoadFlags(nsLoadFlags aLoadFlags
) {
236 mLoadFlags
= aLoadFlags
;
240 NS_IMETHODIMP
nsExtProtocolChannel::GetTRRMode(nsIRequest::TRRMode
* aTRRMode
) {
241 return GetTRRModeImpl(aTRRMode
);
244 NS_IMETHODIMP
nsExtProtocolChannel::SetTRRMode(nsIRequest::TRRMode aTRRMode
) {
245 return SetTRRModeImpl(aTRRMode
);
248 NS_IMETHODIMP
nsExtProtocolChannel::GetIsDocument(bool* aIsDocument
) {
249 return NS_GetIsDocumentChannel(this, aIsDocument
);
252 NS_IMETHODIMP
nsExtProtocolChannel::GetContentType(nsACString
& aContentType
) {
253 return NS_ERROR_NOT_IMPLEMENTED
;
256 NS_IMETHODIMP
nsExtProtocolChannel::SetContentType(
257 const nsACString
& aContentType
) {
258 return NS_ERROR_FAILURE
;
261 NS_IMETHODIMP
nsExtProtocolChannel::GetContentCharset(
262 nsACString
& aContentCharset
) {
263 return NS_ERROR_NOT_IMPLEMENTED
;
266 NS_IMETHODIMP
nsExtProtocolChannel::SetContentCharset(
267 const nsACString
& aContentCharset
) {
268 return NS_ERROR_NOT_IMPLEMENTED
;
271 NS_IMETHODIMP
nsExtProtocolChannel::GetContentDisposition(
272 uint32_t* aContentDisposition
) {
273 return NS_ERROR_NOT_AVAILABLE
;
276 NS_IMETHODIMP
nsExtProtocolChannel::SetContentDisposition(
277 uint32_t aContentDisposition
) {
278 return NS_ERROR_NOT_AVAILABLE
;
281 NS_IMETHODIMP
nsExtProtocolChannel::GetContentDispositionFilename(
282 nsAString
& aContentDispositionFilename
) {
283 return NS_ERROR_NOT_AVAILABLE
;
286 NS_IMETHODIMP
nsExtProtocolChannel::SetContentDispositionFilename(
287 const nsAString
& aContentDispositionFilename
) {
288 return NS_ERROR_NOT_AVAILABLE
;
291 NS_IMETHODIMP
nsExtProtocolChannel::GetContentDispositionHeader(
292 nsACString
& aContentDispositionHeader
) {
293 return NS_ERROR_NOT_AVAILABLE
;
296 NS_IMETHODIMP
nsExtProtocolChannel::GetContentLength(int64_t* aContentLength
) {
297 *aContentLength
= -1;
302 nsExtProtocolChannel::SetContentLength(int64_t aContentLength
) {
303 MOZ_ASSERT_UNREACHABLE("SetContentLength");
304 return NS_ERROR_NOT_IMPLEMENTED
;
307 NS_IMETHODIMP
nsExtProtocolChannel::GetOwner(nsISupports
** aPrincipal
) {
308 return NS_ERROR_NOT_IMPLEMENTED
;
311 NS_IMETHODIMP
nsExtProtocolChannel::SetOwner(nsISupports
* aPrincipal
) {
312 return NS_ERROR_NOT_IMPLEMENTED
;
315 NS_IMETHODIMP
nsExtProtocolChannel::GetLoadInfo(nsILoadInfo
** aLoadInfo
) {
316 NS_IF_ADDREF(*aLoadInfo
= mLoadInfo
);
320 NS_IMETHODIMP
nsExtProtocolChannel::SetLoadInfo(nsILoadInfo
* aLoadInfo
) {
321 MOZ_RELEASE_ASSERT(aLoadInfo
, "loadinfo can't be null");
322 mLoadInfo
= aLoadInfo
;
326 ////////////////////////////////////////////////////////////////////////////////
328 ////////////////////////////////////////////////////////////////////////////////
330 NS_IMETHODIMP
nsExtProtocolChannel::GetName(nsACString
& result
) {
331 return mUrl
->GetSpec(result
);
334 NS_IMETHODIMP
nsExtProtocolChannel::IsPending(bool* result
) {
339 NS_IMETHODIMP
nsExtProtocolChannel::GetStatus(nsresult
* status
) {
344 NS_IMETHODIMP
nsExtProtocolChannel::Cancel(nsresult status
) {
345 if (NS_SUCCEEDED(mStatus
)) {
352 NS_IMETHODIMP
nsExtProtocolChannel::GetCanceled(bool* aCanceled
) {
353 *aCanceled
= mCanceled
;
357 NS_IMETHODIMP
nsExtProtocolChannel::Suspend() {
358 MOZ_ASSERT_UNREACHABLE("Suspend");
359 return NS_ERROR_NOT_IMPLEMENTED
;
362 NS_IMETHODIMP
nsExtProtocolChannel::Resume() {
363 MOZ_ASSERT_UNREACHABLE("Resume");
364 return NS_ERROR_NOT_IMPLEMENTED
;
367 ///////////////////////////////////////////////////////////////////////
368 // From nsIChildChannel
369 //////////////////////////////////////////////////////////////////////
371 NS_IMETHODIMP
nsExtProtocolChannel::ConnectParent(uint32_t registrarId
) {
372 mozilla::dom::ContentChild::GetSingleton()
373 ->SendExtProtocolChannelConnectParent(registrarId
);
377 NS_IMETHODIMP
nsExtProtocolChannel::CompleteRedirectSetup(
378 nsIStreamListener
* listener
) {
379 // For redirects to external protocols we AsyncOpen on the child
380 // (not the parent) because child channel has the right docshell
381 // (which is needed for the select dialog).
382 return AsyncOpen(listener
);
385 ///////////////////////////////////////////////////////////////////////
386 // From nsIParentChannel (derives from nsIStreamListener)
387 //////////////////////////////////////////////////////////////////////
389 NS_IMETHODIMP
nsExtProtocolChannel::SetParentListener(
390 mozilla::net::ParentChannelListener
* aListener
) {
391 // This is called as part of the connect parent operation from
392 // ContentParent::RecvExtProtocolChannelConnectParent. Setting
393 // this flag tells this channel to not proceed and makes AsyncOpen
394 // just no-op. Actual operation will happen from the child process
395 // via CompleteRedirectSetup call on the child channel.
396 mConnectedParent
= true;
400 NS_IMETHODIMP
nsExtProtocolChannel::SetClassifierMatchedInfo(
401 const nsACString
& aList
, const nsACString
& aProvider
,
402 const nsACString
& aFullHash
) {
407 NS_IMETHODIMP
nsExtProtocolChannel::SetClassifierMatchedTrackingInfo(
408 const nsACString
& aLists
, const nsACString
& aFullHashes
) {
413 NS_IMETHODIMP
nsExtProtocolChannel::NotifyClassificationFlags(
414 uint32_t aClassificationFlags
, bool aIsThirdParty
) {
419 NS_IMETHODIMP
nsExtProtocolChannel::NotifyFlashPluginStateChanged(
420 nsIHttpChannel::FlashPluginState aState
) {
425 NS_IMETHODIMP
nsExtProtocolChannel::Delete() {
430 NS_IMETHODIMP
nsExtProtocolChannel::GetRemoteType(nsACString
& aRemoteType
) {
431 return NS_ERROR_NOT_IMPLEMENTED
;
434 NS_IMETHODIMP
nsExtProtocolChannel::OnStartRequest(nsIRequest
* aRequest
) {
435 return NS_ERROR_UNEXPECTED
;
438 NS_IMETHODIMP
nsExtProtocolChannel::OnStopRequest(nsIRequest
* aRequest
,
439 nsresult aStatusCode
) {
440 MOZ_ASSERT(NS_FAILED(aStatusCode
));
441 return NS_ERROR_UNEXPECTED
;
444 NS_IMETHODIMP
nsExtProtocolChannel::OnDataAvailable(
445 nsIRequest
* aRequest
, nsIInputStream
* aInputStream
, uint64_t aOffset
,
447 // no data is expected
448 MOZ_CRASH("No data expected from external protocol channel");
449 return NS_ERROR_UNEXPECTED
;
452 ///////////////////////////////////////////////////////////////////////
453 // the default protocol handler implementation
454 //////////////////////////////////////////////////////////////////////
456 nsExternalProtocolHandler::nsExternalProtocolHandler() {
457 m_schemeName
= "default";
460 nsExternalProtocolHandler::~nsExternalProtocolHandler() {}
462 NS_IMPL_ADDREF(nsExternalProtocolHandler
)
463 NS_IMPL_RELEASE(nsExternalProtocolHandler
)
465 NS_INTERFACE_MAP_BEGIN(nsExternalProtocolHandler
)
466 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIProtocolHandler
)
467 NS_INTERFACE_MAP_ENTRY(nsIProtocolHandler
)
468 NS_INTERFACE_MAP_ENTRY(nsIExternalProtocolHandler
)
469 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference
)
472 NS_IMETHODIMP
nsExternalProtocolHandler::GetScheme(nsACString
& aScheme
) {
473 aScheme
= m_schemeName
;
477 NS_IMETHODIMP
nsExternalProtocolHandler::GetDefaultPort(int32_t* aDefaultPort
) {
483 nsExternalProtocolHandler::AllowPort(int32_t port
, const char* scheme
,
485 // don't override anything.
489 // returns TRUE if the OS can handle this protocol scheme and false otherwise.
490 bool nsExternalProtocolHandler::HaveExternalProtocolHandler(nsIURI
* aURI
) {
492 nsAutoCString scheme
;
493 aURI
->GetScheme(scheme
);
495 nsCOMPtr
<nsIExternalProtocolService
> extProtSvc(
496 do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID
));
501 bool haveHandler
= false;
502 extProtSvc
->ExternalProtocolHandlerExists(scheme
.get(), &haveHandler
);
506 NS_IMETHODIMP
nsExternalProtocolHandler::GetProtocolFlags(uint32_t* aUritype
) {
507 // Make it norelative since it is a simple uri
508 *aUritype
= URI_NORELATIVE
| URI_NOAUTH
| URI_LOADABLE_BY_ANYONE
|
509 URI_NON_PERSISTABLE
| URI_DOES_NOT_RETURN_DATA
;
514 nsExternalProtocolHandler::NewChannel(nsIURI
* aURI
, nsILoadInfo
* aLoadInfo
,
515 nsIChannel
** aRetval
) {
516 NS_ENSURE_TRUE(aURI
, NS_ERROR_UNKNOWN_PROTOCOL
);
517 NS_ENSURE_TRUE(aRetval
, NS_ERROR_UNKNOWN_PROTOCOL
);
519 // Only try to return a channel if we have a protocol handler for the url.
520 // nsOSHelperAppService::LoadUriInternal relies on this to check trustedness
521 // for some platforms at least. (win uses ::ShellExecute and unix uses
523 if (!HaveExternalProtocolHandler(aURI
)) {
524 return NS_ERROR_UNKNOWN_PROTOCOL
;
527 nsCOMPtr
<nsIChannel
> channel
= new nsExtProtocolChannel(aURI
, aLoadInfo
);
528 channel
.forget(aRetval
);
532 ///////////////////////////////////////////////////////////////////////
533 // External protocol handler interface implementation
534 //////////////////////////////////////////////////////////////////////
535 NS_IMETHODIMP
nsExternalProtocolHandler::ExternalAppExistsForScheme(
536 const nsACString
& aScheme
, bool* _retval
) {
537 nsCOMPtr
<nsIExternalProtocolService
> extProtSvc(
538 do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID
));
540 return extProtSvc
->ExternalProtocolHandlerExists(
541 PromiseFlatCString(aScheme
).get(), _retval
);
543 // In case we don't have external protocol service.