|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* vim:set ts=2 sw=2 sts=2 et: */ |
|
3 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #include "nsMIMEInfoImpl.h" |
|
8 #include "nsXPIDLString.h" |
|
9 #include "nsReadableUtils.h" |
|
10 #include "nsStringEnumerator.h" |
|
11 #include "nsIFile.h" |
|
12 #include "nsIFileURL.h" |
|
13 #include "nsEscape.h" |
|
14 #include "nsNetUtil.h" |
|
15 #include "nsIURILoader.h" |
|
16 #include "nsCURILoader.h" |
|
17 |
|
18 // nsISupports methods |
|
19 NS_IMPL_ADDREF(nsMIMEInfoBase) |
|
20 NS_IMPL_RELEASE(nsMIMEInfoBase) |
|
21 |
|
22 NS_INTERFACE_MAP_BEGIN(nsMIMEInfoBase) |
|
23 NS_INTERFACE_MAP_ENTRY(nsIHandlerInfo) |
|
24 // This is only an nsIMIMEInfo if it's a MIME handler. |
|
25 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIMIMEInfo, mClass == eMIMEInfo) |
|
26 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIHandlerInfo) |
|
27 NS_INTERFACE_MAP_END_THREADSAFE |
|
28 |
|
29 // nsMIMEInfoImpl methods |
|
30 |
|
31 // Constructors for a MIME handler. |
|
32 nsMIMEInfoBase::nsMIMEInfoBase(const char *aMIMEType) : |
|
33 mSchemeOrType(aMIMEType), |
|
34 mClass(eMIMEInfo), |
|
35 mPreferredAction(nsIMIMEInfo::saveToDisk), |
|
36 mAlwaysAskBeforeHandling(true) |
|
37 { |
|
38 } |
|
39 |
|
40 nsMIMEInfoBase::nsMIMEInfoBase(const nsACString& aMIMEType) : |
|
41 mSchemeOrType(aMIMEType), |
|
42 mClass(eMIMEInfo), |
|
43 mPreferredAction(nsIMIMEInfo::saveToDisk), |
|
44 mAlwaysAskBeforeHandling(true) |
|
45 { |
|
46 } |
|
47 |
|
48 // Constructor for a handler that lets the caller specify whether this is a |
|
49 // MIME handler or a protocol handler. In the long run, these will be distinct |
|
50 // classes (f.e. nsMIMEInfo and nsProtocolInfo), but for now we reuse this class |
|
51 // for both and distinguish between the two kinds of handlers via the aClass |
|
52 // argument to this method, which can be either eMIMEInfo or eProtocolInfo. |
|
53 nsMIMEInfoBase::nsMIMEInfoBase(const nsACString& aType, HandlerClass aClass) : |
|
54 mSchemeOrType(aType), |
|
55 mClass(aClass), |
|
56 mPreferredAction(nsIMIMEInfo::saveToDisk), |
|
57 mAlwaysAskBeforeHandling(true) |
|
58 { |
|
59 } |
|
60 |
|
61 nsMIMEInfoBase::~nsMIMEInfoBase() |
|
62 { |
|
63 } |
|
64 |
|
65 NS_IMETHODIMP |
|
66 nsMIMEInfoBase::GetFileExtensions(nsIUTF8StringEnumerator** aResult) |
|
67 { |
|
68 return NS_NewUTF8StringEnumerator(aResult, &mExtensions, this); |
|
69 } |
|
70 |
|
71 NS_IMETHODIMP |
|
72 nsMIMEInfoBase::ExtensionExists(const nsACString& aExtension, bool *_retval) |
|
73 { |
|
74 NS_ASSERTION(!aExtension.IsEmpty(), "no extension"); |
|
75 bool found = false; |
|
76 uint32_t extCount = mExtensions.Length(); |
|
77 if (extCount < 1) return NS_OK; |
|
78 |
|
79 for (uint8_t i=0; i < extCount; i++) { |
|
80 const nsCString& ext = mExtensions[i]; |
|
81 if (ext.Equals(aExtension, nsCaseInsensitiveCStringComparator())) { |
|
82 found = true; |
|
83 break; |
|
84 } |
|
85 } |
|
86 |
|
87 *_retval = found; |
|
88 return NS_OK; |
|
89 } |
|
90 |
|
91 NS_IMETHODIMP |
|
92 nsMIMEInfoBase::GetPrimaryExtension(nsACString& _retval) |
|
93 { |
|
94 if (!mExtensions.Length()) |
|
95 return NS_ERROR_NOT_INITIALIZED; |
|
96 |
|
97 _retval = mExtensions[0]; |
|
98 return NS_OK; |
|
99 } |
|
100 |
|
101 NS_IMETHODIMP |
|
102 nsMIMEInfoBase::SetPrimaryExtension(const nsACString& aExtension) |
|
103 { |
|
104 NS_ASSERTION(!aExtension.IsEmpty(), "no extension"); |
|
105 uint32_t extCount = mExtensions.Length(); |
|
106 uint8_t i; |
|
107 bool found = false; |
|
108 for (i=0; i < extCount; i++) { |
|
109 const nsCString& ext = mExtensions[i]; |
|
110 if (ext.Equals(aExtension, nsCaseInsensitiveCStringComparator())) { |
|
111 found = true; |
|
112 break; |
|
113 } |
|
114 } |
|
115 if (found) { |
|
116 mExtensions.RemoveElementAt(i); |
|
117 } |
|
118 |
|
119 mExtensions.InsertElementAt(0, aExtension); |
|
120 |
|
121 return NS_OK; |
|
122 } |
|
123 |
|
124 NS_IMETHODIMP |
|
125 nsMIMEInfoBase::AppendExtension(const nsACString& aExtension) |
|
126 { |
|
127 mExtensions.AppendElement(aExtension); |
|
128 return NS_OK; |
|
129 } |
|
130 |
|
131 NS_IMETHODIMP |
|
132 nsMIMEInfoBase::GetType(nsACString& aType) |
|
133 { |
|
134 if (mSchemeOrType.IsEmpty()) |
|
135 return NS_ERROR_NOT_INITIALIZED; |
|
136 |
|
137 aType = mSchemeOrType; |
|
138 return NS_OK; |
|
139 } |
|
140 |
|
141 NS_IMETHODIMP |
|
142 nsMIMEInfoBase::GetMIMEType(nsACString& aMIMEType) |
|
143 { |
|
144 if (mSchemeOrType.IsEmpty()) |
|
145 return NS_ERROR_NOT_INITIALIZED; |
|
146 |
|
147 aMIMEType = mSchemeOrType; |
|
148 return NS_OK; |
|
149 } |
|
150 |
|
151 NS_IMETHODIMP |
|
152 nsMIMEInfoBase::GetDescription(nsAString& aDescription) |
|
153 { |
|
154 aDescription = mDescription; |
|
155 return NS_OK; |
|
156 } |
|
157 |
|
158 NS_IMETHODIMP |
|
159 nsMIMEInfoBase::SetDescription(const nsAString& aDescription) |
|
160 { |
|
161 mDescription = aDescription; |
|
162 return NS_OK; |
|
163 } |
|
164 |
|
165 NS_IMETHODIMP |
|
166 nsMIMEInfoBase::Equals(nsIMIMEInfo *aMIMEInfo, bool *_retval) |
|
167 { |
|
168 if (!aMIMEInfo) return NS_ERROR_NULL_POINTER; |
|
169 |
|
170 nsAutoCString type; |
|
171 nsresult rv = aMIMEInfo->GetMIMEType(type); |
|
172 if (NS_FAILED(rv)) return rv; |
|
173 |
|
174 *_retval = mSchemeOrType.Equals(type); |
|
175 |
|
176 return NS_OK; |
|
177 } |
|
178 |
|
179 NS_IMETHODIMP |
|
180 nsMIMEInfoBase::SetFileExtensions(const nsACString& aExtensions) |
|
181 { |
|
182 mExtensions.Clear(); |
|
183 nsCString extList( aExtensions ); |
|
184 |
|
185 int32_t breakLocation = -1; |
|
186 while ( (breakLocation= extList.FindChar(',') )!= -1) |
|
187 { |
|
188 mExtensions.AppendElement(Substring(extList.get(), extList.get() + breakLocation)); |
|
189 extList.Cut(0, breakLocation+1 ); |
|
190 } |
|
191 if ( !extList.IsEmpty() ) |
|
192 mExtensions.AppendElement( extList ); |
|
193 return NS_OK; |
|
194 } |
|
195 |
|
196 NS_IMETHODIMP |
|
197 nsMIMEInfoBase::GetDefaultDescription(nsAString& aDefaultDescription) |
|
198 { |
|
199 aDefaultDescription = mDefaultAppDescription; |
|
200 return NS_OK; |
|
201 } |
|
202 |
|
203 NS_IMETHODIMP |
|
204 nsMIMEInfoBase::GetPreferredApplicationHandler(nsIHandlerApp ** aPreferredAppHandler) |
|
205 { |
|
206 *aPreferredAppHandler = mPreferredApplication; |
|
207 NS_IF_ADDREF(*aPreferredAppHandler); |
|
208 return NS_OK; |
|
209 } |
|
210 |
|
211 NS_IMETHODIMP |
|
212 nsMIMEInfoBase::SetPreferredApplicationHandler(nsIHandlerApp * aPreferredAppHandler) |
|
213 { |
|
214 mPreferredApplication = aPreferredAppHandler; |
|
215 return NS_OK; |
|
216 } |
|
217 |
|
218 NS_IMETHODIMP |
|
219 nsMIMEInfoBase::GetPossibleApplicationHandlers(nsIMutableArray ** aPossibleAppHandlers) |
|
220 { |
|
221 if (!mPossibleApplications) |
|
222 mPossibleApplications = do_CreateInstance(NS_ARRAY_CONTRACTID); |
|
223 |
|
224 if (!mPossibleApplications) |
|
225 return NS_ERROR_OUT_OF_MEMORY; |
|
226 |
|
227 *aPossibleAppHandlers = mPossibleApplications; |
|
228 NS_IF_ADDREF(*aPossibleAppHandlers); |
|
229 return NS_OK; |
|
230 } |
|
231 |
|
232 NS_IMETHODIMP |
|
233 nsMIMEInfoBase::GetPreferredAction(nsHandlerInfoAction * aPreferredAction) |
|
234 { |
|
235 *aPreferredAction = mPreferredAction; |
|
236 return NS_OK; |
|
237 } |
|
238 |
|
239 NS_IMETHODIMP |
|
240 nsMIMEInfoBase::SetPreferredAction(nsHandlerInfoAction aPreferredAction) |
|
241 { |
|
242 mPreferredAction = aPreferredAction; |
|
243 return NS_OK; |
|
244 } |
|
245 |
|
246 NS_IMETHODIMP |
|
247 nsMIMEInfoBase::GetAlwaysAskBeforeHandling(bool * aAlwaysAsk) |
|
248 { |
|
249 *aAlwaysAsk = mAlwaysAskBeforeHandling; |
|
250 |
|
251 return NS_OK; |
|
252 } |
|
253 |
|
254 NS_IMETHODIMP |
|
255 nsMIMEInfoBase::SetAlwaysAskBeforeHandling(bool aAlwaysAsk) |
|
256 { |
|
257 mAlwaysAskBeforeHandling = aAlwaysAsk; |
|
258 return NS_OK; |
|
259 } |
|
260 |
|
261 /* static */ |
|
262 nsresult |
|
263 nsMIMEInfoBase::GetLocalFileFromURI(nsIURI *aURI, nsIFile **aFile) |
|
264 { |
|
265 nsresult rv; |
|
266 |
|
267 nsCOMPtr<nsIFileURL> fileUrl = do_QueryInterface(aURI, &rv); |
|
268 if (NS_FAILED(rv)) { |
|
269 return rv; |
|
270 } |
|
271 |
|
272 nsCOMPtr<nsIFile> file; |
|
273 rv = fileUrl->GetFile(getter_AddRefs(file)); |
|
274 if (NS_FAILED(rv)) { |
|
275 return rv; |
|
276 } |
|
277 |
|
278 file.forget(aFile); |
|
279 return NS_OK; |
|
280 } |
|
281 |
|
282 NS_IMETHODIMP |
|
283 nsMIMEInfoBase::LaunchWithFile(nsIFile* aFile) |
|
284 { |
|
285 nsresult rv; |
|
286 |
|
287 // it doesn't make any sense to call this on protocol handlers |
|
288 NS_ASSERTION(mClass == eMIMEInfo, |
|
289 "nsMIMEInfoBase should have mClass == eMIMEInfo"); |
|
290 |
|
291 if (mPreferredAction == useSystemDefault) { |
|
292 return LaunchDefaultWithFile(aFile); |
|
293 } |
|
294 |
|
295 if (mPreferredAction == useHelperApp) { |
|
296 if (!mPreferredApplication) |
|
297 return NS_ERROR_FILE_NOT_FOUND; |
|
298 |
|
299 // at the moment, we only know how to hand files off to local handlers |
|
300 nsCOMPtr<nsILocalHandlerApp> localHandler = |
|
301 do_QueryInterface(mPreferredApplication, &rv); |
|
302 NS_ENSURE_SUCCESS(rv, rv); |
|
303 |
|
304 nsCOMPtr<nsIFile> executable; |
|
305 rv = localHandler->GetExecutable(getter_AddRefs(executable)); |
|
306 NS_ENSURE_SUCCESS(rv, rv); |
|
307 |
|
308 nsAutoCString path; |
|
309 aFile->GetNativePath(path); |
|
310 return LaunchWithIProcess(executable, path); |
|
311 } |
|
312 |
|
313 return NS_ERROR_INVALID_ARG; |
|
314 } |
|
315 |
|
316 NS_IMETHODIMP |
|
317 nsMIMEInfoBase::LaunchWithURI(nsIURI* aURI, |
|
318 nsIInterfaceRequestor* aWindowContext) |
|
319 { |
|
320 // for now, this is only being called with protocol handlers; that |
|
321 // will change once we get to more general registerContentHandler |
|
322 // support |
|
323 NS_ASSERTION(mClass == eProtocolInfo, |
|
324 "nsMIMEInfoBase should be a protocol handler"); |
|
325 |
|
326 if (mPreferredAction == useSystemDefault) { |
|
327 return LoadUriInternal(aURI); |
|
328 } |
|
329 |
|
330 if (mPreferredAction == useHelperApp) { |
|
331 if (!mPreferredApplication) |
|
332 return NS_ERROR_FILE_NOT_FOUND; |
|
333 |
|
334 return mPreferredApplication->LaunchWithURI(aURI, aWindowContext); |
|
335 } |
|
336 |
|
337 return NS_ERROR_INVALID_ARG; |
|
338 } |
|
339 |
|
340 void |
|
341 nsMIMEInfoBase::CopyBasicDataTo(nsMIMEInfoBase* aOther) |
|
342 { |
|
343 aOther->mSchemeOrType = mSchemeOrType; |
|
344 aOther->mDefaultAppDescription = mDefaultAppDescription; |
|
345 aOther->mExtensions = mExtensions; |
|
346 } |
|
347 |
|
348 /* static */ |
|
349 already_AddRefed<nsIProcess> |
|
350 nsMIMEInfoBase::InitProcess(nsIFile* aApp, nsresult* aResult) |
|
351 { |
|
352 NS_ASSERTION(aApp, "Unexpected null pointer, fix caller"); |
|
353 |
|
354 nsCOMPtr<nsIProcess> process = do_CreateInstance(NS_PROCESS_CONTRACTID, |
|
355 aResult); |
|
356 if (NS_FAILED(*aResult)) |
|
357 return nullptr; |
|
358 |
|
359 *aResult = process->Init(aApp); |
|
360 if (NS_FAILED(*aResult)) |
|
361 return nullptr; |
|
362 |
|
363 return process.forget(); |
|
364 } |
|
365 |
|
366 /* static */ |
|
367 nsresult |
|
368 nsMIMEInfoBase::LaunchWithIProcess(nsIFile* aApp, const nsCString& aArg) |
|
369 { |
|
370 nsresult rv; |
|
371 nsCOMPtr<nsIProcess> process = InitProcess(aApp, &rv); |
|
372 if (NS_FAILED(rv)) |
|
373 return rv; |
|
374 |
|
375 const char *string = aArg.get(); |
|
376 |
|
377 return process->Run(false, &string, 1); |
|
378 } |
|
379 |
|
380 /* static */ |
|
381 nsresult |
|
382 nsMIMEInfoBase::LaunchWithIProcess(nsIFile* aApp, const nsString& aArg) |
|
383 { |
|
384 nsresult rv; |
|
385 nsCOMPtr<nsIProcess> process = InitProcess(aApp, &rv); |
|
386 if (NS_FAILED(rv)) |
|
387 return rv; |
|
388 |
|
389 const char16_t *string = aArg.get(); |
|
390 |
|
391 return process->Runw(false, &string, 1); |
|
392 } |
|
393 |
|
394 // nsMIMEInfoImpl implementation |
|
395 NS_IMETHODIMP |
|
396 nsMIMEInfoImpl::GetDefaultDescription(nsAString& aDefaultDescription) |
|
397 { |
|
398 if (mDefaultAppDescription.IsEmpty() && mDefaultApplication) { |
|
399 // Don't want to cache this, just in case someone resets the app |
|
400 // without changing the description.... |
|
401 mDefaultApplication->GetLeafName(aDefaultDescription); |
|
402 } else { |
|
403 aDefaultDescription = mDefaultAppDescription; |
|
404 } |
|
405 |
|
406 return NS_OK; |
|
407 } |
|
408 |
|
409 NS_IMETHODIMP |
|
410 nsMIMEInfoImpl::GetHasDefaultHandler(bool * _retval) |
|
411 { |
|
412 *_retval = !mDefaultAppDescription.IsEmpty(); |
|
413 if (mDefaultApplication) { |
|
414 bool exists; |
|
415 *_retval = NS_SUCCEEDED(mDefaultApplication->Exists(&exists)) && exists; |
|
416 } |
|
417 return NS_OK; |
|
418 } |
|
419 |
|
420 nsresult |
|
421 nsMIMEInfoImpl::LaunchDefaultWithFile(nsIFile* aFile) |
|
422 { |
|
423 if (!mDefaultApplication) |
|
424 return NS_ERROR_FILE_NOT_FOUND; |
|
425 |
|
426 nsAutoCString nativePath; |
|
427 aFile->GetNativePath(nativePath); |
|
428 |
|
429 return LaunchWithIProcess(mDefaultApplication, nativePath); |
|
430 } |
|
431 |
|
432 NS_IMETHODIMP |
|
433 nsMIMEInfoBase::GetPossibleLocalHandlers(nsIArray **_retval) |
|
434 { |
|
435 return NS_ERROR_NOT_IMPLEMENTED; |
|
436 } |