Bug 1728955: part 8) Refactor `DisplayErrCode` in Windows' `nsClipboard`. r=masayuki
[gecko.git] / uriloader / exthandler / nsExternalProtocolHandler.cpp
blobdbb1b2d0a43e85e7539d242427d4fc815d87446c
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 "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"
29 class nsILoadInfo;
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 {
39 public:
40 NS_DECL_THREADSAFE_ISUPPORTS
41 NS_DECL_NSICHANNEL
42 NS_DECL_NSIREQUESTOBSERVER
43 NS_DECL_NSISTREAMLISTENER
44 NS_DECL_NSIREQUEST
45 NS_DECL_NSICHILDCHANNEL
46 NS_DECL_NSIPARENTCHANNEL
48 nsExtProtocolChannel(nsIURI* aURI, nsILoadInfo* aLoadInfo);
50 private:
51 virtual ~nsExtProtocolChannel();
53 nsresult OpenURL();
54 void Finish(nsresult aResult);
56 nsCOMPtr<nsIURI> mUrl;
57 nsCOMPtr<nsIURI> mOriginalURI;
58 nsresult mStatus;
59 nsLoadFlags mLoadFlags;
60 bool mWasOpened;
61 bool mCanceled;
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)
85 NS_INTERFACE_MAP_END
87 nsExtProtocolChannel::nsExtProtocolChannel(nsIURI* aURI, nsILoadInfo* aLoadInfo)
88 : mUrl(aURI),
89 mOriginalURI(aURI),
90 mStatus(NS_OK),
91 mLoadFlags(nsIRequest::LOAD_NORMAL),
92 mWasOpened(false),
93 mCanceled(false),
94 mConnectedParent(false),
95 mLoadInfo(aLoadInfo) {}
97 nsExtProtocolChannel::~nsExtProtocolChannel() {}
99 NS_IMETHODIMP nsExtProtocolChannel::GetLoadGroup(nsILoadGroup** aLoadGroup) {
100 NS_IF_ADDREF(*aLoadGroup = mLoadGroup);
101 return NS_OK;
104 NS_IMETHODIMP nsExtProtocolChannel::SetLoadGroup(nsILoadGroup* aLoadGroup) {
105 mLoadGroup = aLoadGroup;
106 return NS_OK;
109 NS_IMETHODIMP nsExtProtocolChannel::GetNotificationCallbacks(
110 nsIInterfaceRequestor** aCallbacks) {
111 NS_IF_ADDREF(*aCallbacks = mCallbacks);
112 return NS_OK;
115 NS_IMETHODIMP nsExtProtocolChannel::SetNotificationCallbacks(
116 nsIInterfaceRequestor* aCallbacks) {
117 mCallbacks = aCallbacks;
118 return NS_OK;
121 NS_IMETHODIMP
122 nsExtProtocolChannel::GetSecurityInfo(nsISupports** aSecurityInfo) {
123 *aSecurityInfo = nullptr;
124 return NS_OK;
127 NS_IMETHODIMP nsExtProtocolChannel::GetOriginalURI(nsIURI** aURI) {
128 NS_ADDREF(*aURI = mOriginalURI);
129 return NS_OK;
132 NS_IMETHODIMP nsExtProtocolChannel::SetOriginalURI(nsIURI* aURI) {
133 NS_ENSURE_ARG_POINTER(aURI);
134 mOriginalURI = aURI;
135 return NS_OK;
138 NS_IMETHODIMP nsExtProtocolChannel::GetURI(nsIURI** aURI) {
139 *aURI = mUrl;
140 NS_IF_ADDREF(*aURI);
141 return NS_OK;
144 nsresult nsExtProtocolChannel::OpenURL() {
145 nsresult rv = NS_ERROR_FAILURE;
146 nsCOMPtr<nsIExternalProtocolService> extProtService(
147 do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID));
149 if (extProtService) {
150 #ifdef DEBUG
151 nsAutoCString urlScheme;
152 mUrl->GetScheme(urlScheme);
153 bool haveHandler = false;
154 extProtService->ExternalProtocolHandlerExists(urlScheme.get(),
155 &haveHandler);
156 NS_ASSERTION(haveHandler,
157 "Why do we have a channel for this url if we don't support "
158 "the protocol?");
159 #endif
161 RefPtr<mozilla::dom::BrowsingContext> ctx;
162 rv = mLoadInfo->GetTargetBrowsingContext(getter_AddRefs(ctx));
163 if (NS_FAILED(rv)) {
164 goto finish;
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);
180 }));
184 finish:
185 mCallbacks = nullptr;
186 mListener = nullptr;
187 return rv;
190 NS_IMETHODIMP nsExtProtocolChannel::Open(nsIInputStream** aStream) {
191 nsCOMPtr<nsIStreamListener> listener;
192 nsresult rv =
193 nsContentSecurityManager::doContentSecurityCheck(this, listener);
194 NS_ENSURE_SUCCESS(rv, rv);
196 return OpenURL();
199 NS_IMETHODIMP nsExtProtocolChannel::AsyncOpen(nsIStreamListener* aListener) {
200 nsCOMPtr<nsIStreamListener> listener = aListener;
201 nsresult rv =
202 nsContentSecurityManager::doContentSecurityCheck(this, listener);
203 if (NS_FAILED(rv)) {
204 mCallbacks = nullptr;
205 return rv;
208 if (mConnectedParent) {
209 return NS_OK;
212 MOZ_ASSERT(
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);
224 mWasOpened = true;
225 mListener = listener;
227 return OpenURL();
230 NS_IMETHODIMP nsExtProtocolChannel::GetLoadFlags(nsLoadFlags* aLoadFlags) {
231 *aLoadFlags = mLoadFlags;
232 return NS_OK;
235 NS_IMETHODIMP nsExtProtocolChannel::SetLoadFlags(nsLoadFlags aLoadFlags) {
236 mLoadFlags = aLoadFlags;
237 return NS_OK;
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;
298 return NS_OK;
301 NS_IMETHODIMP
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);
317 return NS_OK;
320 NS_IMETHODIMP nsExtProtocolChannel::SetLoadInfo(nsILoadInfo* aLoadInfo) {
321 MOZ_RELEASE_ASSERT(aLoadInfo, "loadinfo can't be null");
322 mLoadInfo = aLoadInfo;
323 return NS_OK;
326 ////////////////////////////////////////////////////////////////////////////////
327 // From nsIRequest
328 ////////////////////////////////////////////////////////////////////////////////
330 NS_IMETHODIMP nsExtProtocolChannel::GetName(nsACString& result) {
331 return mUrl->GetSpec(result);
334 NS_IMETHODIMP nsExtProtocolChannel::IsPending(bool* result) {
335 *result = false;
336 return NS_OK;
339 NS_IMETHODIMP nsExtProtocolChannel::GetStatus(nsresult* status) {
340 *status = mStatus;
341 return NS_OK;
344 NS_IMETHODIMP nsExtProtocolChannel::Cancel(nsresult status) {
345 if (NS_SUCCEEDED(mStatus)) {
346 mStatus = status;
348 mCanceled = true;
349 return NS_OK;
352 NS_IMETHODIMP nsExtProtocolChannel::GetCanceled(bool* aCanceled) {
353 *aCanceled = mCanceled;
354 return NS_OK;
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);
374 return NS_OK;
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;
397 return NS_OK;
400 NS_IMETHODIMP nsExtProtocolChannel::SetClassifierMatchedInfo(
401 const nsACString& aList, const nsACString& aProvider,
402 const nsACString& aFullHash) {
403 // nothing to do
404 return NS_OK;
407 NS_IMETHODIMP nsExtProtocolChannel::SetClassifierMatchedTrackingInfo(
408 const nsACString& aLists, const nsACString& aFullHashes) {
409 // nothing to do
410 return NS_OK;
413 NS_IMETHODIMP nsExtProtocolChannel::NotifyClassificationFlags(
414 uint32_t aClassificationFlags, bool aIsThirdParty) {
415 // nothing to do
416 return NS_OK;
419 NS_IMETHODIMP nsExtProtocolChannel::NotifyFlashPluginStateChanged(
420 nsIHttpChannel::FlashPluginState aState) {
421 // nothing to do
422 return NS_OK;
425 NS_IMETHODIMP nsExtProtocolChannel::Delete() {
426 // nothing to do
427 return NS_OK;
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,
446 uint32_t aCount) {
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)
470 NS_INTERFACE_MAP_END
472 NS_IMETHODIMP nsExternalProtocolHandler::GetScheme(nsACString& aScheme) {
473 aScheme = m_schemeName;
474 return NS_OK;
477 NS_IMETHODIMP nsExternalProtocolHandler::GetDefaultPort(int32_t* aDefaultPort) {
478 *aDefaultPort = 0;
479 return NS_OK;
482 NS_IMETHODIMP
483 nsExternalProtocolHandler::AllowPort(int32_t port, const char* scheme,
484 bool* _retval) {
485 // don't override anything.
486 *_retval = false;
487 return NS_OK;
489 // returns TRUE if the OS can handle this protocol scheme and false otherwise.
490 bool nsExternalProtocolHandler::HaveExternalProtocolHandler(nsIURI* aURI) {
491 MOZ_ASSERT(aURI);
492 nsAutoCString scheme;
493 aURI->GetScheme(scheme);
495 nsCOMPtr<nsIExternalProtocolService> extProtSvc(
496 do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID));
497 if (!extProtSvc) {
498 return false;
501 bool haveHandler = false;
502 extProtSvc->ExternalProtocolHandlerExists(scheme.get(), &haveHandler);
503 return 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;
510 return NS_OK;
513 NS_IMETHODIMP
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
522 // gnome_url_show.)
523 if (!HaveExternalProtocolHandler(aURI)) {
524 return NS_ERROR_UNKNOWN_PROTOCOL;
527 nsCOMPtr<nsIChannel> channel = new nsExtProtocolChannel(aURI, aLoadInfo);
528 channel.forget(aRetval);
529 return NS_OK;
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));
539 if (extProtSvc)
540 return extProtSvc->ExternalProtocolHandlerExists(
541 PromiseFlatCString(aScheme).get(), _retval);
543 // In case we don't have external protocol service.
544 *_retval = false;
545 return NS_OK;