|
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: |
|
3 * |
|
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/. */ |
|
7 |
|
8 #include "nsIURI.h" |
|
9 #include "nsIURL.h" |
|
10 #include "nsExternalProtocolHandler.h" |
|
11 #include "nsXPIDLString.h" |
|
12 #include "nsReadableUtils.h" |
|
13 #include "nsCOMPtr.h" |
|
14 #include "nsIServiceManager.h" |
|
15 #include "nsServiceManagerUtils.h" |
|
16 #include "nsIInterfaceRequestor.h" |
|
17 #include "nsIInterfaceRequestorUtils.h" |
|
18 #include "nsIStringBundle.h" |
|
19 #include "nsIPrefService.h" |
|
20 #include "nsIPrompt.h" |
|
21 #include "nsNetUtil.h" |
|
22 #include "nsExternalHelperAppService.h" |
|
23 |
|
24 // used to dispatch urls to default protocol handlers |
|
25 #include "nsCExternalHandlerService.h" |
|
26 #include "nsIExternalProtocolService.h" |
|
27 |
|
28 //////////////////////////////////////////////////////////////////////// |
|
29 // a stub channel implemenation which will map calls to AsyncRead and OpenInputStream |
|
30 // to calls in the OS for loading the url. |
|
31 //////////////////////////////////////////////////////////////////////// |
|
32 |
|
33 class nsExtProtocolChannel : public nsIChannel |
|
34 { |
|
35 public: |
|
36 NS_DECL_THREADSAFE_ISUPPORTS |
|
37 NS_DECL_NSICHANNEL |
|
38 NS_DECL_NSIREQUEST |
|
39 |
|
40 nsExtProtocolChannel(); |
|
41 virtual ~nsExtProtocolChannel(); |
|
42 |
|
43 nsresult SetURI(nsIURI*); |
|
44 |
|
45 private: |
|
46 nsresult OpenURL(); |
|
47 void Finish(nsresult aResult); |
|
48 |
|
49 nsCOMPtr<nsIURI> mUrl; |
|
50 nsCOMPtr<nsIURI> mOriginalURI; |
|
51 nsresult mStatus; |
|
52 nsLoadFlags mLoadFlags; |
|
53 bool mWasOpened; |
|
54 |
|
55 nsCOMPtr<nsIInterfaceRequestor> mCallbacks; |
|
56 nsCOMPtr<nsILoadGroup> mLoadGroup; |
|
57 }; |
|
58 |
|
59 NS_IMPL_ADDREF(nsExtProtocolChannel) |
|
60 NS_IMPL_RELEASE(nsExtProtocolChannel) |
|
61 |
|
62 NS_INTERFACE_MAP_BEGIN(nsExtProtocolChannel) |
|
63 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIChannel) |
|
64 NS_INTERFACE_MAP_ENTRY(nsIChannel) |
|
65 NS_INTERFACE_MAP_ENTRY(nsIRequest) |
|
66 NS_INTERFACE_MAP_END_THREADSAFE |
|
67 |
|
68 nsExtProtocolChannel::nsExtProtocolChannel() : mStatus(NS_OK), |
|
69 mWasOpened(false) |
|
70 { |
|
71 } |
|
72 |
|
73 nsExtProtocolChannel::~nsExtProtocolChannel() |
|
74 {} |
|
75 |
|
76 NS_IMETHODIMP nsExtProtocolChannel::GetLoadGroup(nsILoadGroup * *aLoadGroup) |
|
77 { |
|
78 NS_IF_ADDREF(*aLoadGroup = mLoadGroup); |
|
79 return NS_OK; |
|
80 } |
|
81 |
|
82 NS_IMETHODIMP nsExtProtocolChannel::SetLoadGroup(nsILoadGroup * aLoadGroup) |
|
83 { |
|
84 mLoadGroup = aLoadGroup; |
|
85 return NS_OK; |
|
86 } |
|
87 |
|
88 NS_IMETHODIMP nsExtProtocolChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aCallbacks) |
|
89 { |
|
90 NS_IF_ADDREF(*aCallbacks = mCallbacks); |
|
91 return NS_OK; |
|
92 } |
|
93 |
|
94 NS_IMETHODIMP nsExtProtocolChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks) |
|
95 { |
|
96 mCallbacks = aCallbacks; |
|
97 return NS_OK; |
|
98 } |
|
99 |
|
100 NS_IMETHODIMP |
|
101 nsExtProtocolChannel::GetSecurityInfo(nsISupports * *aSecurityInfo) |
|
102 { |
|
103 *aSecurityInfo = nullptr; |
|
104 return NS_OK; |
|
105 } |
|
106 |
|
107 NS_IMETHODIMP nsExtProtocolChannel::GetOriginalURI(nsIURI* *aURI) |
|
108 { |
|
109 NS_ADDREF(*aURI = mOriginalURI); |
|
110 return NS_OK; |
|
111 } |
|
112 |
|
113 NS_IMETHODIMP nsExtProtocolChannel::SetOriginalURI(nsIURI* aURI) |
|
114 { |
|
115 NS_ENSURE_ARG_POINTER(aURI); |
|
116 mOriginalURI = aURI; |
|
117 return NS_OK; |
|
118 } |
|
119 |
|
120 NS_IMETHODIMP nsExtProtocolChannel::GetURI(nsIURI* *aURI) |
|
121 { |
|
122 *aURI = mUrl; |
|
123 NS_IF_ADDREF(*aURI); |
|
124 return NS_OK; |
|
125 } |
|
126 |
|
127 nsresult nsExtProtocolChannel::SetURI(nsIURI* aURI) |
|
128 { |
|
129 mUrl = aURI; |
|
130 return NS_OK; |
|
131 } |
|
132 |
|
133 nsresult nsExtProtocolChannel::OpenURL() |
|
134 { |
|
135 nsresult rv = NS_ERROR_FAILURE; |
|
136 nsCOMPtr<nsIExternalProtocolService> extProtService (do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID)); |
|
137 |
|
138 if (extProtService) |
|
139 { |
|
140 #ifdef DEBUG |
|
141 nsAutoCString urlScheme; |
|
142 mUrl->GetScheme(urlScheme); |
|
143 bool haveHandler = false; |
|
144 extProtService->ExternalProtocolHandlerExists(urlScheme.get(), &haveHandler); |
|
145 NS_ASSERTION(haveHandler, "Why do we have a channel for this url if we don't support the protocol?"); |
|
146 #endif |
|
147 |
|
148 nsCOMPtr<nsIInterfaceRequestor> aggCallbacks; |
|
149 rv = NS_NewNotificationCallbacksAggregation(mCallbacks, mLoadGroup, |
|
150 getter_AddRefs(aggCallbacks)); |
|
151 if (NS_FAILED(rv)) { |
|
152 goto finish; |
|
153 } |
|
154 |
|
155 rv = extProtService->LoadURI(mUrl, aggCallbacks); |
|
156 if (NS_SUCCEEDED(rv)) { |
|
157 // despite success, we need to abort this channel, at the very least |
|
158 // to make it clear to the caller that no on{Start,Stop}Request |
|
159 // should be expected. |
|
160 rv = NS_ERROR_NO_CONTENT; |
|
161 } |
|
162 } |
|
163 |
|
164 finish: |
|
165 mCallbacks = 0; |
|
166 return rv; |
|
167 } |
|
168 |
|
169 NS_IMETHODIMP nsExtProtocolChannel::Open(nsIInputStream **_retval) |
|
170 { |
|
171 return OpenURL(); |
|
172 } |
|
173 |
|
174 NS_IMETHODIMP nsExtProtocolChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctxt) |
|
175 { |
|
176 NS_ENSURE_ARG_POINTER(listener); |
|
177 NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED); |
|
178 |
|
179 mWasOpened = true; |
|
180 |
|
181 return OpenURL(); |
|
182 } |
|
183 |
|
184 NS_IMETHODIMP nsExtProtocolChannel::GetLoadFlags(nsLoadFlags *aLoadFlags) |
|
185 { |
|
186 *aLoadFlags = mLoadFlags; |
|
187 return NS_OK; |
|
188 } |
|
189 |
|
190 NS_IMETHODIMP nsExtProtocolChannel::SetLoadFlags(nsLoadFlags aLoadFlags) |
|
191 { |
|
192 mLoadFlags = aLoadFlags; |
|
193 return NS_OK; |
|
194 } |
|
195 |
|
196 NS_IMETHODIMP nsExtProtocolChannel::GetContentType(nsACString &aContentType) |
|
197 { |
|
198 return NS_ERROR_NOT_IMPLEMENTED; |
|
199 } |
|
200 |
|
201 NS_IMETHODIMP nsExtProtocolChannel::SetContentType(const nsACString &aContentType) |
|
202 { |
|
203 return NS_ERROR_FAILURE; |
|
204 } |
|
205 |
|
206 NS_IMETHODIMP nsExtProtocolChannel::GetContentCharset(nsACString &aContentCharset) |
|
207 { |
|
208 return NS_ERROR_NOT_IMPLEMENTED; |
|
209 } |
|
210 |
|
211 NS_IMETHODIMP nsExtProtocolChannel::SetContentCharset(const nsACString &aContentCharset) |
|
212 { |
|
213 return NS_ERROR_NOT_IMPLEMENTED; |
|
214 } |
|
215 |
|
216 NS_IMETHODIMP nsExtProtocolChannel::GetContentDisposition(uint32_t *aContentDisposition) |
|
217 { |
|
218 return NS_ERROR_NOT_AVAILABLE; |
|
219 } |
|
220 |
|
221 NS_IMETHODIMP nsExtProtocolChannel::SetContentDisposition(uint32_t aContentDisposition) |
|
222 { |
|
223 return NS_ERROR_NOT_AVAILABLE; |
|
224 } |
|
225 |
|
226 NS_IMETHODIMP nsExtProtocolChannel::GetContentDispositionFilename(nsAString &aContentDispositionFilename) |
|
227 { |
|
228 return NS_ERROR_NOT_AVAILABLE; |
|
229 } |
|
230 |
|
231 NS_IMETHODIMP nsExtProtocolChannel::SetContentDispositionFilename(const nsAString &aContentDispositionFilename) |
|
232 { |
|
233 return NS_ERROR_NOT_AVAILABLE; |
|
234 } |
|
235 |
|
236 NS_IMETHODIMP nsExtProtocolChannel::GetContentDispositionHeader(nsACString &aContentDispositionHeader) |
|
237 { |
|
238 return NS_ERROR_NOT_AVAILABLE; |
|
239 } |
|
240 |
|
241 NS_IMETHODIMP nsExtProtocolChannel::GetContentLength(int64_t * aContentLength) |
|
242 { |
|
243 *aContentLength = -1; |
|
244 return NS_OK; |
|
245 } |
|
246 |
|
247 NS_IMETHODIMP |
|
248 nsExtProtocolChannel::SetContentLength(int64_t aContentLength) |
|
249 { |
|
250 NS_NOTREACHED("SetContentLength"); |
|
251 return NS_ERROR_NOT_IMPLEMENTED; |
|
252 } |
|
253 |
|
254 NS_IMETHODIMP nsExtProtocolChannel::GetOwner(nsISupports * *aPrincipal) |
|
255 { |
|
256 NS_NOTREACHED("GetOwner"); |
|
257 return NS_ERROR_NOT_IMPLEMENTED; |
|
258 } |
|
259 |
|
260 NS_IMETHODIMP nsExtProtocolChannel::SetOwner(nsISupports * aPrincipal) |
|
261 { |
|
262 NS_NOTREACHED("SetOwner"); |
|
263 return NS_ERROR_NOT_IMPLEMENTED; |
|
264 } |
|
265 |
|
266 //////////////////////////////////////////////////////////////////////////////// |
|
267 // From nsIRequest |
|
268 //////////////////////////////////////////////////////////////////////////////// |
|
269 |
|
270 NS_IMETHODIMP nsExtProtocolChannel::GetName(nsACString &result) |
|
271 { |
|
272 return mUrl->GetSpec(result); |
|
273 } |
|
274 |
|
275 NS_IMETHODIMP nsExtProtocolChannel::IsPending(bool *result) |
|
276 { |
|
277 *result = false; |
|
278 return NS_OK; |
|
279 } |
|
280 |
|
281 NS_IMETHODIMP nsExtProtocolChannel::GetStatus(nsresult *status) |
|
282 { |
|
283 *status = mStatus; |
|
284 return NS_OK; |
|
285 } |
|
286 |
|
287 NS_IMETHODIMP nsExtProtocolChannel::Cancel(nsresult status) |
|
288 { |
|
289 mStatus = status; |
|
290 return NS_OK; |
|
291 } |
|
292 |
|
293 NS_IMETHODIMP nsExtProtocolChannel::Suspend() |
|
294 { |
|
295 NS_NOTREACHED("Suspend"); |
|
296 return NS_ERROR_NOT_IMPLEMENTED; |
|
297 } |
|
298 |
|
299 NS_IMETHODIMP nsExtProtocolChannel::Resume() |
|
300 { |
|
301 NS_NOTREACHED("Resume"); |
|
302 return NS_ERROR_NOT_IMPLEMENTED; |
|
303 } |
|
304 |
|
305 /////////////////////////////////////////////////////////////////////// |
|
306 // the default protocol handler implementation |
|
307 ////////////////////////////////////////////////////////////////////// |
|
308 |
|
309 nsExternalProtocolHandler::nsExternalProtocolHandler() |
|
310 { |
|
311 m_schemeName = "default"; |
|
312 } |
|
313 |
|
314 |
|
315 nsExternalProtocolHandler::~nsExternalProtocolHandler() |
|
316 {} |
|
317 |
|
318 NS_IMPL_ADDREF(nsExternalProtocolHandler) |
|
319 NS_IMPL_RELEASE(nsExternalProtocolHandler) |
|
320 |
|
321 NS_INTERFACE_MAP_BEGIN(nsExternalProtocolHandler) |
|
322 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIProtocolHandler) |
|
323 NS_INTERFACE_MAP_ENTRY(nsIProtocolHandler) |
|
324 NS_INTERFACE_MAP_ENTRY(nsIExternalProtocolHandler) |
|
325 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) |
|
326 NS_INTERFACE_MAP_END_THREADSAFE |
|
327 |
|
328 NS_IMETHODIMP nsExternalProtocolHandler::GetScheme(nsACString &aScheme) |
|
329 { |
|
330 aScheme = m_schemeName; |
|
331 return NS_OK; |
|
332 } |
|
333 |
|
334 NS_IMETHODIMP nsExternalProtocolHandler::GetDefaultPort(int32_t *aDefaultPort) |
|
335 { |
|
336 *aDefaultPort = 0; |
|
337 return NS_OK; |
|
338 } |
|
339 |
|
340 NS_IMETHODIMP |
|
341 nsExternalProtocolHandler::AllowPort(int32_t port, const char *scheme, bool *_retval) |
|
342 { |
|
343 // don't override anything. |
|
344 *_retval = false; |
|
345 return NS_OK; |
|
346 } |
|
347 // returns TRUE if the OS can handle this protocol scheme and false otherwise. |
|
348 bool nsExternalProtocolHandler::HaveExternalProtocolHandler(nsIURI * aURI) |
|
349 { |
|
350 bool haveHandler = false; |
|
351 if (aURI) |
|
352 { |
|
353 nsAutoCString scheme; |
|
354 aURI->GetScheme(scheme); |
|
355 nsCOMPtr<nsIExternalProtocolService> extProtSvc(do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID)); |
|
356 if (extProtSvc) |
|
357 extProtSvc->ExternalProtocolHandlerExists(scheme.get(), &haveHandler); |
|
358 } |
|
359 |
|
360 return haveHandler; |
|
361 } |
|
362 |
|
363 NS_IMETHODIMP nsExternalProtocolHandler::GetProtocolFlags(uint32_t *aUritype) |
|
364 { |
|
365 // Make it norelative since it is a simple uri |
|
366 *aUritype = URI_NORELATIVE | URI_NOAUTH | URI_LOADABLE_BY_ANYONE | |
|
367 URI_NON_PERSISTABLE | URI_DOES_NOT_RETURN_DATA; |
|
368 return NS_OK; |
|
369 } |
|
370 |
|
371 NS_IMETHODIMP nsExternalProtocolHandler::NewURI(const nsACString &aSpec, |
|
372 const char *aCharset, // ignore charset info |
|
373 nsIURI *aBaseURI, |
|
374 nsIURI **_retval) |
|
375 { |
|
376 nsresult rv; |
|
377 nsCOMPtr<nsIURI> uri = do_CreateInstance(NS_SIMPLEURI_CONTRACTID, &rv); |
|
378 NS_ENSURE_SUCCESS(rv, rv); |
|
379 |
|
380 rv = uri->SetSpec(aSpec); |
|
381 NS_ENSURE_SUCCESS(rv, rv); |
|
382 |
|
383 NS_ADDREF(*_retval = uri); |
|
384 return NS_OK; |
|
385 } |
|
386 |
|
387 NS_IMETHODIMP nsExternalProtocolHandler::NewChannel(nsIURI *aURI, nsIChannel **_retval) |
|
388 { |
|
389 // Only try to return a channel if we have a protocol handler for the url. |
|
390 // nsOSHelperAppService::LoadUriInternal relies on this to check trustedness |
|
391 // for some platforms at least. (win uses ::ShellExecute and unix uses |
|
392 // gnome_url_show.) |
|
393 bool haveExternalHandler = HaveExternalProtocolHandler(aURI); |
|
394 if (haveExternalHandler) |
|
395 { |
|
396 nsCOMPtr<nsIChannel> channel = new nsExtProtocolChannel(); |
|
397 if (!channel) return NS_ERROR_OUT_OF_MEMORY; |
|
398 |
|
399 ((nsExtProtocolChannel*) channel.get())->SetURI(aURI); |
|
400 channel->SetOriginalURI(aURI); |
|
401 |
|
402 if (_retval) |
|
403 { |
|
404 *_retval = channel; |
|
405 NS_IF_ADDREF(*_retval); |
|
406 return NS_OK; |
|
407 } |
|
408 } |
|
409 |
|
410 return NS_ERROR_UNKNOWN_PROTOCOL; |
|
411 } |
|
412 |
|
413 /////////////////////////////////////////////////////////////////////// |
|
414 // External protocol handler interface implementation |
|
415 ////////////////////////////////////////////////////////////////////// |
|
416 NS_IMETHODIMP nsExternalProtocolHandler::ExternalAppExistsForScheme(const nsACString& aScheme, bool *_retval) |
|
417 { |
|
418 nsCOMPtr<nsIExternalProtocolService> extProtSvc(do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID)); |
|
419 if (extProtSvc) |
|
420 return extProtSvc->ExternalProtocolHandlerExists( |
|
421 PromiseFlatCString(aScheme).get(), _retval); |
|
422 |
|
423 // In case we don't have external protocol service. |
|
424 *_retval = false; |
|
425 return NS_OK; |
|
426 } |