|
1 /* -*- Mode: C++; tab-width: 3; 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 "nsOSHelperAppService.h" |
|
9 #include "nsISupports.h" |
|
10 #include "nsString.h" |
|
11 #include "nsXPIDLString.h" |
|
12 #include "nsIURL.h" |
|
13 #include "nsIMIMEInfo.h" |
|
14 #include "nsMIMEInfoWin.h" |
|
15 #include "nsMimeTypes.h" |
|
16 #include "nsILocalFileWin.h" |
|
17 #include "nsIProcess.h" |
|
18 #include "plstr.h" |
|
19 #include "nsAutoPtr.h" |
|
20 #include "nsNativeCharsetUtils.h" |
|
21 #include "nsIWindowsRegKey.h" |
|
22 |
|
23 // shellapi.h is needed to build with WIN32_LEAN_AND_MEAN |
|
24 #include <shellapi.h> |
|
25 |
|
26 #define LOG(args) PR_LOG(mLog, PR_LOG_DEBUG, args) |
|
27 |
|
28 // helper methods: forward declarations... |
|
29 static nsresult GetExtensionFrom4xRegistryInfo(const nsACString& aMimeType, |
|
30 nsString& aFileExtension); |
|
31 static nsresult GetExtensionFromWindowsMimeDatabase(const nsACString& aMimeType, |
|
32 nsString& aFileExtension); |
|
33 |
|
34 nsOSHelperAppService::nsOSHelperAppService() : |
|
35 nsExternalHelperAppService() |
|
36 , mAppAssoc(nullptr) |
|
37 { |
|
38 CoInitialize(nullptr); |
|
39 CoCreateInstance(CLSID_ApplicationAssociationRegistration, nullptr, |
|
40 CLSCTX_INPROC, IID_IApplicationAssociationRegistration, |
|
41 (void**)&mAppAssoc); |
|
42 } |
|
43 |
|
44 nsOSHelperAppService::~nsOSHelperAppService() |
|
45 { |
|
46 if (mAppAssoc) |
|
47 mAppAssoc->Release(); |
|
48 mAppAssoc = nullptr; |
|
49 CoUninitialize(); |
|
50 } |
|
51 |
|
52 // The windows registry provides a mime database key which lists a set of mime types and corresponding "Extension" values. |
|
53 // we can use this to look up our mime type to see if there is a preferred extension for the mime type. |
|
54 static nsresult GetExtensionFromWindowsMimeDatabase(const nsACString& aMimeType, |
|
55 nsString& aFileExtension) |
|
56 { |
|
57 nsAutoString mimeDatabaseKey; |
|
58 mimeDatabaseKey.AssignLiteral("MIME\\Database\\Content Type\\"); |
|
59 |
|
60 AppendASCIItoUTF16(aMimeType, mimeDatabaseKey); |
|
61 |
|
62 nsCOMPtr<nsIWindowsRegKey> regKey = |
|
63 do_CreateInstance("@mozilla.org/windows-registry-key;1"); |
|
64 if (!regKey) |
|
65 return NS_ERROR_NOT_AVAILABLE; |
|
66 |
|
67 nsresult rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, |
|
68 mimeDatabaseKey, |
|
69 nsIWindowsRegKey::ACCESS_QUERY_VALUE); |
|
70 |
|
71 if (NS_SUCCEEDED(rv)) |
|
72 regKey->ReadStringValue(NS_LITERAL_STRING("Extension"), aFileExtension); |
|
73 |
|
74 return NS_OK; |
|
75 } |
|
76 |
|
77 // We have a serious problem!! I have this content type and the windows registry only gives me |
|
78 // helper apps based on extension. Right now, we really don't have a good place to go for |
|
79 // trying to figure out the extension for a particular mime type....One short term hack is to look |
|
80 // this information in 4.x (it's stored in the windows regsitry). |
|
81 static nsresult GetExtensionFrom4xRegistryInfo(const nsACString& aMimeType, |
|
82 nsString& aFileExtension) |
|
83 { |
|
84 nsCOMPtr<nsIWindowsRegKey> regKey = |
|
85 do_CreateInstance("@mozilla.org/windows-registry-key;1"); |
|
86 if (!regKey) |
|
87 return NS_ERROR_NOT_AVAILABLE; |
|
88 |
|
89 nsresult rv = regKey-> |
|
90 Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER, |
|
91 NS_LITERAL_STRING("Software\\Netscape\\Netscape Navigator\\Suffixes"), |
|
92 nsIWindowsRegKey::ACCESS_QUERY_VALUE); |
|
93 if (NS_FAILED(rv)) |
|
94 return NS_ERROR_NOT_AVAILABLE; |
|
95 |
|
96 rv = regKey->ReadStringValue(NS_ConvertASCIItoUTF16(aMimeType), |
|
97 aFileExtension); |
|
98 if (NS_FAILED(rv)) |
|
99 return NS_OK; |
|
100 |
|
101 aFileExtension.Insert(char16_t('.'), 0); |
|
102 |
|
103 // this may be a comma separated list of extensions...just take the |
|
104 // first one for now... |
|
105 |
|
106 int32_t pos = aFileExtension.FindChar(char16_t(',')); |
|
107 if (pos > 0) { |
|
108 // we have a comma separated list of types... |
|
109 // truncate everything after the first comma (including the comma) |
|
110 aFileExtension.Truncate(pos); |
|
111 } |
|
112 |
|
113 return NS_OK; |
|
114 } |
|
115 |
|
116 nsresult nsOSHelperAppService::OSProtocolHandlerExists(const char * aProtocolScheme, bool * aHandlerExists) |
|
117 { |
|
118 // look up the protocol scheme in the windows registry....if we find a match then we have a handler for it... |
|
119 *aHandlerExists = false; |
|
120 if (aProtocolScheme && *aProtocolScheme) |
|
121 { |
|
122 // Vista: use new application association interface |
|
123 if (mAppAssoc) { |
|
124 wchar_t * pResult = nullptr; |
|
125 NS_ConvertASCIItoUTF16 scheme(aProtocolScheme); |
|
126 // We are responsible for freeing returned strings. |
|
127 HRESULT hr = mAppAssoc->QueryCurrentDefault(scheme.get(), |
|
128 AT_URLPROTOCOL, AL_EFFECTIVE, |
|
129 &pResult); |
|
130 if (SUCCEEDED(hr)) { |
|
131 CoTaskMemFree(pResult); |
|
132 *aHandlerExists = true; |
|
133 } |
|
134 return NS_OK; |
|
135 } |
|
136 |
|
137 HKEY hKey; |
|
138 LONG err = ::RegOpenKeyExW(HKEY_CLASSES_ROOT, |
|
139 NS_ConvertASCIItoUTF16(aProtocolScheme).get(), |
|
140 0, |
|
141 KEY_QUERY_VALUE, |
|
142 &hKey); |
|
143 if (err == ERROR_SUCCESS) |
|
144 { |
|
145 err = ::RegQueryValueExW(hKey, L"URL Protocol", |
|
146 nullptr, nullptr, nullptr, nullptr); |
|
147 *aHandlerExists = (err == ERROR_SUCCESS); |
|
148 // close the key |
|
149 ::RegCloseKey(hKey); |
|
150 } |
|
151 } |
|
152 |
|
153 return NS_OK; |
|
154 } |
|
155 |
|
156 NS_IMETHODIMP nsOSHelperAppService::GetApplicationDescription(const nsACString& aScheme, nsAString& _retval) |
|
157 { |
|
158 nsCOMPtr<nsIWindowsRegKey> regKey = |
|
159 do_CreateInstance("@mozilla.org/windows-registry-key;1"); |
|
160 if (!regKey) |
|
161 return NS_ERROR_NOT_AVAILABLE; |
|
162 |
|
163 NS_ConvertASCIItoUTF16 buf(aScheme); |
|
164 |
|
165 // Vista: use new application association interface |
|
166 if (mAppAssoc) { |
|
167 wchar_t * pResult = nullptr; |
|
168 // We are responsible for freeing returned strings. |
|
169 HRESULT hr = mAppAssoc->QueryCurrentDefault(buf.get(), |
|
170 AT_URLPROTOCOL, AL_EFFECTIVE, |
|
171 &pResult); |
|
172 if (SUCCEEDED(hr)) { |
|
173 nsCOMPtr<nsIFile> app; |
|
174 nsAutoString appInfo(pResult); |
|
175 CoTaskMemFree(pResult); |
|
176 if (NS_SUCCEEDED(GetDefaultAppInfo(appInfo, _retval, getter_AddRefs(app)))) |
|
177 return NS_OK; |
|
178 } |
|
179 return NS_ERROR_NOT_AVAILABLE; |
|
180 } |
|
181 |
|
182 nsCOMPtr<nsIFile> app; |
|
183 GetDefaultAppInfo(buf, _retval, getter_AddRefs(app)); |
|
184 |
|
185 if (!_retval.Equals(buf)) |
|
186 return NS_OK; |
|
187 |
|
188 // Fall back to full path |
|
189 buf.AppendLiteral("\\shell\\open\\command"); |
|
190 nsresult rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, |
|
191 buf, |
|
192 nsIWindowsRegKey::ACCESS_QUERY_VALUE); |
|
193 if (NS_FAILED(rv)) |
|
194 return NS_ERROR_NOT_AVAILABLE; |
|
195 |
|
196 rv = regKey->ReadStringValue(EmptyString(), _retval); |
|
197 |
|
198 return NS_SUCCEEDED(rv) ? NS_OK : NS_ERROR_NOT_AVAILABLE; |
|
199 } |
|
200 |
|
201 // GetMIMEInfoFromRegistry: This function obtains the values of some of the nsIMIMEInfo |
|
202 // attributes for the mimeType/extension associated with the input registry key. The default |
|
203 // entry for that key is the name of a registry key under HKEY_CLASSES_ROOT. The default |
|
204 // value for *that* key is the descriptive name of the type. The EditFlags value is a binary |
|
205 // value; the low order bit of the third byte of which indicates that the user does not need |
|
206 // to be prompted. |
|
207 // |
|
208 // This function sets only the Description attribute of the input nsIMIMEInfo. |
|
209 /* static */ |
|
210 nsresult nsOSHelperAppService::GetMIMEInfoFromRegistry(const nsAFlatString& fileType, nsIMIMEInfo *pInfo) |
|
211 { |
|
212 nsresult rv = NS_OK; |
|
213 |
|
214 NS_ENSURE_ARG(pInfo); |
|
215 nsCOMPtr<nsIWindowsRegKey> regKey = |
|
216 do_CreateInstance("@mozilla.org/windows-registry-key;1"); |
|
217 if (!regKey) |
|
218 return NS_ERROR_NOT_AVAILABLE; |
|
219 |
|
220 rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, |
|
221 fileType, nsIWindowsRegKey::ACCESS_QUERY_VALUE); |
|
222 if (NS_FAILED(rv)) |
|
223 return NS_ERROR_FAILURE; |
|
224 |
|
225 // OK, the default value here is the description of the type. |
|
226 nsAutoString description; |
|
227 rv = regKey->ReadStringValue(EmptyString(), description); |
|
228 if (NS_SUCCEEDED(rv)) |
|
229 pInfo->SetDescription(description); |
|
230 |
|
231 return NS_OK; |
|
232 } |
|
233 |
|
234 ///////////////////////////////////////////////////////////////////////////////////////////////// |
|
235 // method overrides used to gather information from the windows registry for |
|
236 // various mime types. |
|
237 //////////////////////////////////////////////////////////////////////////////////////////////// |
|
238 |
|
239 /// Looks up the type for the extension aExt and compares it to aType |
|
240 /* static */ bool |
|
241 nsOSHelperAppService::typeFromExtEquals(const char16_t* aExt, const char *aType) |
|
242 { |
|
243 if (!aType) |
|
244 return false; |
|
245 nsAutoString fileExtToUse; |
|
246 if (aExt[0] != char16_t('.')) |
|
247 fileExtToUse = char16_t('.'); |
|
248 |
|
249 fileExtToUse.Append(aExt); |
|
250 |
|
251 bool eq = false; |
|
252 nsCOMPtr<nsIWindowsRegKey> regKey = |
|
253 do_CreateInstance("@mozilla.org/windows-registry-key;1"); |
|
254 if (!regKey) |
|
255 return eq; |
|
256 |
|
257 nsresult rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, |
|
258 fileExtToUse, |
|
259 nsIWindowsRegKey::ACCESS_QUERY_VALUE); |
|
260 if (NS_FAILED(rv)) |
|
261 return eq; |
|
262 |
|
263 nsAutoString type; |
|
264 rv = regKey->ReadStringValue(NS_LITERAL_STRING("Content Type"), type); |
|
265 if (NS_SUCCEEDED(rv)) |
|
266 eq = type.EqualsASCII(aType); |
|
267 |
|
268 return eq; |
|
269 } |
|
270 |
|
271 // Strip a handler command string of its quotes and parameters. |
|
272 static void CleanupHandlerPath(nsString& aPath) |
|
273 { |
|
274 // Example command strings passed into this routine: |
|
275 |
|
276 // 1) C:\Program Files\Company\some.exe -foo -bar |
|
277 // 2) C:\Program Files\Company\some.dll |
|
278 // 3) C:\Windows\some.dll,-foo -bar |
|
279 // 4) C:\Windows\some.cpl,-foo -bar |
|
280 |
|
281 int32_t lastCommaPos = aPath.RFindChar(','); |
|
282 if (lastCommaPos != kNotFound) |
|
283 aPath.Truncate(lastCommaPos); |
|
284 |
|
285 aPath.AppendLiteral(" "); |
|
286 |
|
287 // case insensitive |
|
288 uint32_t index = aPath.Find(".exe ", true); |
|
289 if (index == kNotFound) |
|
290 index = aPath.Find(".dll ", true); |
|
291 if (index == kNotFound) |
|
292 index = aPath.Find(".cpl ", true); |
|
293 |
|
294 if (index != kNotFound) |
|
295 aPath.Truncate(index + 4); |
|
296 aPath.Trim(" ", true, true); |
|
297 } |
|
298 |
|
299 // Strip the windows host process bootstrap executable rundll32.exe |
|
300 // from a handler's command string if it exists. |
|
301 static void StripRundll32(nsString& aCommandString) |
|
302 { |
|
303 // Example rundll formats: |
|
304 // C:\Windows\System32\rundll32.exe "path to dll" |
|
305 // rundll32.exe "path to dll" |
|
306 // C:\Windows\System32\rundll32.exe "path to dll", var var |
|
307 // rundll32.exe "path to dll", var var |
|
308 |
|
309 NS_NAMED_LITERAL_STRING(rundllSegment, "rundll32.exe "); |
|
310 NS_NAMED_LITERAL_STRING(rundllSegmentShort, "rundll32 "); |
|
311 |
|
312 // case insensitive |
|
313 int32_t strLen = rundllSegment.Length(); |
|
314 int32_t index = aCommandString.Find(rundllSegment, true); |
|
315 if (index == kNotFound) { |
|
316 strLen = rundllSegmentShort.Length(); |
|
317 index = aCommandString.Find(rundllSegmentShort, true); |
|
318 } |
|
319 |
|
320 if (index != kNotFound) { |
|
321 uint32_t rundllSegmentLength = index + strLen; |
|
322 aCommandString.Cut(0, rundllSegmentLength); |
|
323 } |
|
324 } |
|
325 |
|
326 // Returns the fully qualified path to an application handler based on |
|
327 // a parameterized command string. Note this routine should not be used |
|
328 // to launch the associated application as it strips parameters and |
|
329 // rundll.exe from the string. Designed for retrieving display information |
|
330 // on a particular handler. |
|
331 /* static */ bool nsOSHelperAppService::CleanupCmdHandlerPath(nsAString& aCommandHandler) |
|
332 { |
|
333 nsAutoString handlerCommand(aCommandHandler); |
|
334 |
|
335 // Straight command path: |
|
336 // |
|
337 // %SystemRoot%\system32\NOTEPAD.EXE var |
|
338 // "C:\Program Files\iTunes\iTunes.exe" var var |
|
339 // C:\Program Files\iTunes\iTunes.exe var var |
|
340 // |
|
341 // Example rundll handlers: |
|
342 // |
|
343 // rundll32.exe "%ProgramFiles%\Win...ery\PhotoViewer.dll", var var |
|
344 // rundll32.exe "%ProgramFiles%\Windows Photo Gallery\PhotoViewer.dll" |
|
345 // C:\Windows\System32\rundll32.exe "path to dll", var var |
|
346 // %SystemRoot%\System32\rundll32.exe "%ProgramFiles%\Win...ery\Photo |
|
347 // Viewer.dll", var var |
|
348 |
|
349 // Expand environment variables so we have full path strings. |
|
350 uint32_t bufLength = ::ExpandEnvironmentStringsW(handlerCommand.get(), |
|
351 L"", 0); |
|
352 if (bufLength == 0) // Error |
|
353 return false; |
|
354 |
|
355 nsAutoArrayPtr<wchar_t> destination(new wchar_t[bufLength]); |
|
356 if (!destination) |
|
357 return false; |
|
358 if (!::ExpandEnvironmentStringsW(handlerCommand.get(), destination, |
|
359 bufLength)) |
|
360 return false; |
|
361 |
|
362 handlerCommand = static_cast<const wchar_t*>(destination); |
|
363 |
|
364 // Remove quotes around paths |
|
365 handlerCommand.StripChars("\""); |
|
366 |
|
367 // Strip windows host process bootstrap so we can get to the actual |
|
368 // handler. |
|
369 StripRundll32(handlerCommand); |
|
370 |
|
371 // Trim any command parameters so that we have a native path we can |
|
372 // initialize a local file with. |
|
373 CleanupHandlerPath(handlerCommand); |
|
374 |
|
375 aCommandHandler.Assign(handlerCommand); |
|
376 return true; |
|
377 } |
|
378 |
|
379 // The "real" name of a given helper app (as specified by the path to the |
|
380 // executable file held in various registry keys) is stored n the VERSIONINFO |
|
381 // block in the file's resources. We need to find the path to the executable |
|
382 // and then retrieve the "FileDescription" field value from the file. |
|
383 nsresult |
|
384 nsOSHelperAppService::GetDefaultAppInfo(const nsAString& aAppInfo, |
|
385 nsAString& aDefaultDescription, |
|
386 nsIFile** aDefaultApplication) |
|
387 { |
|
388 nsAutoString handlerCommand; |
|
389 |
|
390 // If all else fails, use the file type key name, which will be |
|
391 // something like "pngfile" for .pngs, "WMVFile" for .wmvs, etc. |
|
392 aDefaultDescription = aAppInfo; |
|
393 *aDefaultApplication = nullptr; |
|
394 |
|
395 if (aAppInfo.IsEmpty()) |
|
396 return NS_ERROR_FAILURE; |
|
397 |
|
398 // aAppInfo may be a file, file path, program id, or |
|
399 // Applications reference - |
|
400 // c:\dir\app.exe |
|
401 // Applications\appfile.exe/dll (shell\open...) |
|
402 // ProgID.progid (shell\open...) |
|
403 |
|
404 nsAutoString handlerKeyName(aAppInfo); |
|
405 |
|
406 nsCOMPtr<nsIWindowsRegKey> chkKey = |
|
407 do_CreateInstance("@mozilla.org/windows-registry-key;1"); |
|
408 if (!chkKey) |
|
409 return NS_ERROR_FAILURE; |
|
410 |
|
411 nsresult rv = chkKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, |
|
412 handlerKeyName, |
|
413 nsIWindowsRegKey::ACCESS_QUERY_VALUE); |
|
414 if (NS_FAILED(rv)) { |
|
415 // It's a file system path to a handler |
|
416 handlerCommand.Assign(aAppInfo); |
|
417 } |
|
418 else { |
|
419 handlerKeyName.AppendLiteral("\\shell\\open\\command"); |
|
420 nsCOMPtr<nsIWindowsRegKey> regKey = |
|
421 do_CreateInstance("@mozilla.org/windows-registry-key;1"); |
|
422 if (!regKey) |
|
423 return NS_ERROR_FAILURE; |
|
424 |
|
425 nsresult rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, |
|
426 handlerKeyName, |
|
427 nsIWindowsRegKey::ACCESS_QUERY_VALUE); |
|
428 if (NS_FAILED(rv)) |
|
429 return NS_ERROR_FAILURE; |
|
430 |
|
431 // OK, the default value here is the description of the type. |
|
432 rv = regKey->ReadStringValue(EmptyString(), handlerCommand); |
|
433 if (NS_FAILED(rv)) { |
|
434 |
|
435 // Check if there is a DelegateExecute string |
|
436 nsAutoString delegateExecute; |
|
437 rv = regKey->ReadStringValue(NS_LITERAL_STRING("DelegateExecute"), delegateExecute); |
|
438 NS_ENSURE_SUCCESS(rv, rv); |
|
439 |
|
440 // Look for InProcServer32 |
|
441 nsAutoString delegateExecuteRegPath; |
|
442 delegateExecuteRegPath.AssignLiteral("CLSID\\"); |
|
443 delegateExecuteRegPath.Append(delegateExecute); |
|
444 delegateExecuteRegPath.AppendLiteral("\\InProcServer32"); |
|
445 rv = chkKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, |
|
446 delegateExecuteRegPath, |
|
447 nsIWindowsRegKey::ACCESS_QUERY_VALUE); |
|
448 if (NS_SUCCEEDED(rv)) { |
|
449 rv = chkKey->ReadStringValue(EmptyString(), handlerCommand); |
|
450 } |
|
451 |
|
452 if (NS_FAILED(rv)) { |
|
453 // Look for LocalServer32 |
|
454 delegateExecuteRegPath.AssignLiteral("CLSID\\"); |
|
455 delegateExecuteRegPath.Append(delegateExecute); |
|
456 delegateExecuteRegPath.AppendLiteral("\\LocalServer32"); |
|
457 rv = chkKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, |
|
458 delegateExecuteRegPath, |
|
459 nsIWindowsRegKey::ACCESS_QUERY_VALUE); |
|
460 NS_ENSURE_SUCCESS(rv, rv); |
|
461 rv = chkKey->ReadStringValue(EmptyString(), handlerCommand); |
|
462 NS_ENSURE_SUCCESS(rv, rv); |
|
463 } |
|
464 } |
|
465 } |
|
466 |
|
467 if (!CleanupCmdHandlerPath(handlerCommand)) |
|
468 return NS_ERROR_FAILURE; |
|
469 |
|
470 // XXX FIXME: If this fails, the UI will display the full command |
|
471 // string. |
|
472 // There are some rare cases this can happen - ["url.dll" -foo] |
|
473 // for example won't resolve correctly to the system dir. The |
|
474 // subsequent launch of the helper app will work though. |
|
475 nsCOMPtr<nsIFile> lf; |
|
476 NS_NewLocalFile(handlerCommand, true, getter_AddRefs(lf)); |
|
477 if (!lf) |
|
478 return NS_ERROR_FILE_NOT_FOUND; |
|
479 |
|
480 nsILocalFileWin* lfw = nullptr; |
|
481 CallQueryInterface(lf, &lfw); |
|
482 |
|
483 if (lfw) { |
|
484 // The "FileDescription" field contains the actual name of the application. |
|
485 lfw->GetVersionInfoField("FileDescription", aDefaultDescription); |
|
486 // QI addref'ed for us. |
|
487 *aDefaultApplication = lfw; |
|
488 } |
|
489 |
|
490 return NS_OK; |
|
491 } |
|
492 |
|
493 already_AddRefed<nsMIMEInfoWin> nsOSHelperAppService::GetByExtension(const nsAFlatString& aFileExt, const char *aTypeHint) |
|
494 { |
|
495 if (aFileExt.IsEmpty()) |
|
496 return nullptr; |
|
497 |
|
498 // windows registry assumes your file extension is going to include the '.'. |
|
499 // so make sure it's there... |
|
500 nsAutoString fileExtToUse; |
|
501 if (aFileExt.First() != char16_t('.')) |
|
502 fileExtToUse = char16_t('.'); |
|
503 |
|
504 fileExtToUse.Append(aFileExt); |
|
505 |
|
506 // Try to get an entry from the windows registry. |
|
507 nsCOMPtr<nsIWindowsRegKey> regKey = |
|
508 do_CreateInstance("@mozilla.org/windows-registry-key;1"); |
|
509 if (!regKey) |
|
510 return nullptr; |
|
511 |
|
512 nsresult rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, |
|
513 fileExtToUse, |
|
514 nsIWindowsRegKey::ACCESS_QUERY_VALUE); |
|
515 if (NS_FAILED(rv)) |
|
516 return nullptr; |
|
517 |
|
518 nsAutoCString typeToUse; |
|
519 if (aTypeHint && *aTypeHint) { |
|
520 typeToUse.Assign(aTypeHint); |
|
521 } |
|
522 else { |
|
523 nsAutoString temp; |
|
524 if (NS_FAILED(regKey->ReadStringValue(NS_LITERAL_STRING("Content Type"), |
|
525 temp)) || temp.IsEmpty()) { |
|
526 return nullptr; |
|
527 } |
|
528 // Content-Type is always in ASCII |
|
529 LossyAppendUTF16toASCII(temp, typeToUse); |
|
530 } |
|
531 |
|
532 nsRefPtr<nsMIMEInfoWin> mimeInfo = new nsMIMEInfoWin(typeToUse); |
|
533 |
|
534 // don't append the '.' |
|
535 mimeInfo->AppendExtension(NS_ConvertUTF16toUTF8(Substring(fileExtToUse, 1))); |
|
536 mimeInfo->SetPreferredAction(nsIMIMEInfo::useSystemDefault); |
|
537 |
|
538 nsAutoString appInfo; |
|
539 bool found; |
|
540 |
|
541 // Retrieve the default application for this extension |
|
542 if (mAppAssoc) { |
|
543 // Vista: use the new application association COM interfaces |
|
544 // for resolving helpers. |
|
545 nsString assocType(fileExtToUse); |
|
546 wchar_t * pResult = nullptr; |
|
547 HRESULT hr = mAppAssoc->QueryCurrentDefault(assocType.get(), |
|
548 AT_FILEEXTENSION, AL_EFFECTIVE, |
|
549 &pResult); |
|
550 if (SUCCEEDED(hr)) { |
|
551 found = true; |
|
552 appInfo.Assign(pResult); |
|
553 CoTaskMemFree(pResult); |
|
554 } |
|
555 else { |
|
556 found = false; |
|
557 } |
|
558 } |
|
559 else |
|
560 { |
|
561 found = NS_SUCCEEDED(regKey->ReadStringValue(EmptyString(), |
|
562 appInfo)); |
|
563 } |
|
564 |
|
565 // Bug 358297 - ignore the default handler, force the user to choose app |
|
566 if (appInfo.EqualsLiteral("XPSViewer.Document")) |
|
567 found = false; |
|
568 |
|
569 if (!found) { |
|
570 return nullptr; |
|
571 } |
|
572 |
|
573 // Get other nsIMIMEInfo fields from registry, if possible. |
|
574 nsAutoString defaultDescription; |
|
575 nsCOMPtr<nsIFile> defaultApplication; |
|
576 |
|
577 if (NS_FAILED(GetDefaultAppInfo(appInfo, defaultDescription, |
|
578 getter_AddRefs(defaultApplication)))) { |
|
579 return nullptr; |
|
580 } |
|
581 |
|
582 mimeInfo->SetDefaultDescription(defaultDescription); |
|
583 mimeInfo->SetDefaultApplicationHandler(defaultApplication); |
|
584 |
|
585 // Grab the general description |
|
586 GetMIMEInfoFromRegistry(appInfo, mimeInfo); |
|
587 |
|
588 return mimeInfo.forget(); |
|
589 } |
|
590 |
|
591 already_AddRefed<nsIMIMEInfo> nsOSHelperAppService::GetMIMEInfoFromOS(const nsACString& aMIMEType, const nsACString& aFileExt, bool *aFound) |
|
592 { |
|
593 *aFound = true; |
|
594 |
|
595 const nsCString& flatType = PromiseFlatCString(aMIMEType); |
|
596 const nsCString& flatExt = PromiseFlatCString(aFileExt); |
|
597 |
|
598 nsAutoString fileExtension; |
|
599 /* XXX The Equals is a gross hack to wallpaper over the most common Win32 |
|
600 * extension issues caused by the fix for bug 116938. See bug |
|
601 * 120327, comment 271 for why this is needed. Not even sure we |
|
602 * want to remove this once we have fixed all this stuff to work |
|
603 * right; any info we get from the OS on this type is pretty much |
|
604 * useless.... |
|
605 * We'll do extension-based lookup for this type later in this function. |
|
606 */ |
|
607 if (!aMIMEType.LowerCaseEqualsLiteral(APPLICATION_OCTET_STREAM)) { |
|
608 // (1) try to use the windows mime database to see if there is a mapping to a file extension |
|
609 // (2) try to see if we have some left over 4.x registry info we can peek at... |
|
610 GetExtensionFromWindowsMimeDatabase(aMIMEType, fileExtension); |
|
611 LOG(("Windows mime database: extension '%s'\n", fileExtension.get())); |
|
612 if (fileExtension.IsEmpty()) { |
|
613 GetExtensionFrom4xRegistryInfo(aMIMEType, fileExtension); |
|
614 LOG(("4.x Registry: extension '%s'\n", fileExtension.get())); |
|
615 } |
|
616 } |
|
617 // If we found an extension for the type, do the lookup |
|
618 nsRefPtr<nsMIMEInfoWin> mi; |
|
619 if (!fileExtension.IsEmpty()) |
|
620 mi = GetByExtension(fileExtension, flatType.get()); |
|
621 LOG(("Extension lookup on '%s' found: 0x%p\n", fileExtension.get(), mi.get())); |
|
622 |
|
623 bool hasDefault = false; |
|
624 if (mi) { |
|
625 mi->GetHasDefaultHandler(&hasDefault); |
|
626 // OK. We might have the case that |aFileExt| is a valid extension for the |
|
627 // mimetype we were given. In that case, we do want to append aFileExt |
|
628 // to the mimeinfo that we have. (E.g.: We are asked for video/mpeg and |
|
629 // .mpg, but the primary extension for video/mpeg is .mpeg. But because |
|
630 // .mpg is an extension for video/mpeg content, we want to append it) |
|
631 if (!aFileExt.IsEmpty() && typeFromExtEquals(NS_ConvertUTF8toUTF16(flatExt).get(), flatType.get())) { |
|
632 LOG(("Appending extension '%s' to mimeinfo, because its mimetype is '%s'\n", |
|
633 flatExt.get(), flatType.get())); |
|
634 bool extExist = false; |
|
635 mi->ExtensionExists(aFileExt, &extExist); |
|
636 if (!extExist) |
|
637 mi->AppendExtension(aFileExt); |
|
638 } |
|
639 } |
|
640 if (!mi || !hasDefault) { |
|
641 nsRefPtr<nsMIMEInfoWin> miByExt = |
|
642 GetByExtension(NS_ConvertUTF8toUTF16(aFileExt), flatType.get()); |
|
643 LOG(("Ext. lookup for '%s' found 0x%p\n", flatExt.get(), miByExt.get())); |
|
644 if (!miByExt && mi) |
|
645 return mi.forget(); |
|
646 if (miByExt && !mi) { |
|
647 return miByExt.forget(); |
|
648 } |
|
649 if (!miByExt && !mi) { |
|
650 *aFound = false; |
|
651 mi = new nsMIMEInfoWin(flatType); |
|
652 if (!aFileExt.IsEmpty()) { |
|
653 mi->AppendExtension(aFileExt); |
|
654 } |
|
655 |
|
656 return mi.forget(); |
|
657 } |
|
658 |
|
659 // if we get here, mi has no default app. copy from extension lookup. |
|
660 nsCOMPtr<nsIFile> defaultApp; |
|
661 nsAutoString desc; |
|
662 miByExt->GetDefaultDescription(desc); |
|
663 |
|
664 mi->SetDefaultDescription(desc); |
|
665 } |
|
666 return mi.forget(); |
|
667 } |
|
668 |
|
669 NS_IMETHODIMP |
|
670 nsOSHelperAppService::GetProtocolHandlerInfoFromOS(const nsACString &aScheme, |
|
671 bool *found, |
|
672 nsIHandlerInfo **_retval) |
|
673 { |
|
674 NS_ASSERTION(!aScheme.IsEmpty(), "No scheme was specified!"); |
|
675 |
|
676 nsresult rv = OSProtocolHandlerExists(nsPromiseFlatCString(aScheme).get(), |
|
677 found); |
|
678 if (NS_FAILED(rv)) |
|
679 return rv; |
|
680 |
|
681 nsMIMEInfoWin *handlerInfo = |
|
682 new nsMIMEInfoWin(aScheme, nsMIMEInfoBase::eProtocolInfo); |
|
683 NS_ENSURE_TRUE(handlerInfo, NS_ERROR_OUT_OF_MEMORY); |
|
684 NS_ADDREF(*_retval = handlerInfo); |
|
685 |
|
686 if (!*found) { |
|
687 // Code that calls this requires an object regardless if the OS has |
|
688 // something for us, so we return the empty object. |
|
689 return NS_OK; |
|
690 } |
|
691 |
|
692 nsAutoString desc; |
|
693 GetApplicationDescription(aScheme, desc); |
|
694 handlerInfo->SetDefaultDescription(desc); |
|
695 |
|
696 return NS_OK; |
|
697 } |
|
698 |