|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #include "nsDirectoryServiceDefs.h" |
|
7 #include "nsIDOMElement.h" |
|
8 #include "nsIDOMHTMLImageElement.h" |
|
9 #include "nsIImageLoadingContent.h" |
|
10 #include "nsIDocument.h" |
|
11 #include "nsIContent.h" |
|
12 #include "nsILocalFileMac.h" |
|
13 #include "nsIObserverService.h" |
|
14 #include "nsIPrefService.h" |
|
15 #include "nsIServiceManager.h" |
|
16 #include "nsIStringBundle.h" |
|
17 #include "nsIURL.h" |
|
18 #include "nsIWebBrowserPersist.h" |
|
19 #include "nsMacShellService.h" |
|
20 #include "nsNetUtil.h" |
|
21 #include "nsShellService.h" |
|
22 #include "nsStringAPI.h" |
|
23 #include "nsIDocShell.h" |
|
24 #include "nsILoadContext.h" |
|
25 |
|
26 #include <CoreFoundation/CoreFoundation.h> |
|
27 #include <ApplicationServices/ApplicationServices.h> |
|
28 |
|
29 #define NETWORK_PREFPANE NS_LITERAL_CSTRING("/System/Library/PreferencePanes/Network.prefPane") |
|
30 #define DESKTOP_PREFPANE NS_LITERAL_CSTRING("/System/Library/PreferencePanes/DesktopScreenEffectsPref.prefPane") |
|
31 |
|
32 #define SAFARI_BUNDLE_IDENTIFIER "com.apple.Safari" |
|
33 |
|
34 NS_IMPL_ISUPPORTS(nsMacShellService, nsIMacShellService, nsIShellService, nsIWebProgressListener) |
|
35 |
|
36 NS_IMETHODIMP |
|
37 nsMacShellService::IsDefaultBrowser(bool aStartupCheck, |
|
38 bool aForAllTypes, |
|
39 bool* aIsDefaultBrowser) |
|
40 { |
|
41 *aIsDefaultBrowser = false; |
|
42 |
|
43 CFStringRef firefoxID = ::CFBundleGetIdentifier(::CFBundleGetMainBundle()); |
|
44 if (!firefoxID) { |
|
45 // CFBundleGetIdentifier is expected to return nullptr only if the specified |
|
46 // bundle doesn't have a bundle identifier in its plist. In this case, that |
|
47 // means a failure, since our bundle does have an identifier. |
|
48 return NS_ERROR_FAILURE; |
|
49 } |
|
50 |
|
51 // Get the default http handler's bundle ID (or nullptr if it has not been |
|
52 // explicitly set) |
|
53 CFStringRef defaultBrowserID = ::LSCopyDefaultHandlerForURLScheme(CFSTR("http")); |
|
54 if (defaultBrowserID) { |
|
55 *aIsDefaultBrowser = ::CFStringCompare(firefoxID, defaultBrowserID, 0) == kCFCompareEqualTo; |
|
56 ::CFRelease(defaultBrowserID); |
|
57 } |
|
58 |
|
59 // If this is the first browser window, maintain internal state that we've |
|
60 // checked this session (so that subsequent window opens don't show the |
|
61 // default browser dialog). |
|
62 if (aStartupCheck) |
|
63 mCheckedThisSession = true; |
|
64 |
|
65 return NS_OK; |
|
66 } |
|
67 |
|
68 NS_IMETHODIMP |
|
69 nsMacShellService::SetDefaultBrowser(bool aClaimAllTypes, bool aForAllUsers) |
|
70 { |
|
71 // Note: We don't support aForAllUsers on Mac OS X. |
|
72 |
|
73 CFStringRef firefoxID = ::CFBundleGetIdentifier(::CFBundleGetMainBundle()); |
|
74 if (!firefoxID) { |
|
75 return NS_ERROR_FAILURE; |
|
76 } |
|
77 |
|
78 if (::LSSetDefaultHandlerForURLScheme(CFSTR("http"), firefoxID) != noErr) { |
|
79 return NS_ERROR_FAILURE; |
|
80 } |
|
81 if (::LSSetDefaultHandlerForURLScheme(CFSTR("https"), firefoxID) != noErr) { |
|
82 return NS_ERROR_FAILURE; |
|
83 } |
|
84 |
|
85 if (aClaimAllTypes) { |
|
86 if (::LSSetDefaultHandlerForURLScheme(CFSTR("ftp"), firefoxID) != noErr) { |
|
87 return NS_ERROR_FAILURE; |
|
88 } |
|
89 if (::LSSetDefaultRoleHandlerForContentType(kUTTypeHTML, kLSRolesAll, firefoxID) != noErr) { |
|
90 return NS_ERROR_FAILURE; |
|
91 } |
|
92 } |
|
93 |
|
94 return NS_OK; |
|
95 } |
|
96 |
|
97 NS_IMETHODIMP |
|
98 nsMacShellService::GetShouldCheckDefaultBrowser(bool* aResult) |
|
99 { |
|
100 // If we've already checked, the browser has been started and this is a |
|
101 // new window open, and we don't want to check again. |
|
102 if (mCheckedThisSession) { |
|
103 *aResult = false; |
|
104 return NS_OK; |
|
105 } |
|
106 |
|
107 nsCOMPtr<nsIPrefBranch> prefs; |
|
108 nsCOMPtr<nsIPrefService> pserve(do_GetService(NS_PREFSERVICE_CONTRACTID)); |
|
109 if (pserve) |
|
110 pserve->GetBranch("", getter_AddRefs(prefs)); |
|
111 |
|
112 prefs->GetBoolPref(PREF_CHECKDEFAULTBROWSER, aResult); |
|
113 |
|
114 return NS_OK; |
|
115 } |
|
116 |
|
117 NS_IMETHODIMP |
|
118 nsMacShellService::SetShouldCheckDefaultBrowser(bool aShouldCheck) |
|
119 { |
|
120 nsCOMPtr<nsIPrefBranch> prefs; |
|
121 nsCOMPtr<nsIPrefService> pserve(do_GetService(NS_PREFSERVICE_CONTRACTID)); |
|
122 if (pserve) |
|
123 pserve->GetBranch("", getter_AddRefs(prefs)); |
|
124 |
|
125 prefs->SetBoolPref(PREF_CHECKDEFAULTBROWSER, aShouldCheck); |
|
126 |
|
127 return NS_OK; |
|
128 } |
|
129 |
|
130 NS_IMETHODIMP |
|
131 nsMacShellService::GetCanSetDesktopBackground(bool* aResult) |
|
132 { |
|
133 *aResult = true; |
|
134 return NS_OK; |
|
135 } |
|
136 |
|
137 NS_IMETHODIMP |
|
138 nsMacShellService::SetDesktopBackground(nsIDOMElement* aElement, |
|
139 int32_t aPosition) |
|
140 { |
|
141 // Note: We don't support aPosition on OS X. |
|
142 |
|
143 // Get the image URI: |
|
144 nsresult rv; |
|
145 nsCOMPtr<nsIImageLoadingContent> imageContent = do_QueryInterface(aElement, |
|
146 &rv); |
|
147 NS_ENSURE_SUCCESS(rv, rv); |
|
148 nsCOMPtr<nsIURI> imageURI; |
|
149 rv = imageContent->GetCurrentURI(getter_AddRefs(imageURI)); |
|
150 NS_ENSURE_SUCCESS(rv, rv); |
|
151 |
|
152 // We need the referer URI for nsIWebBrowserPersist::saveURI |
|
153 nsCOMPtr<nsIContent> content = do_QueryInterface(aElement, &rv); |
|
154 NS_ENSURE_SUCCESS(rv, rv); |
|
155 |
|
156 nsIURI *docURI = content->OwnerDoc()->GetDocumentURI(); |
|
157 if (!docURI) |
|
158 return NS_ERROR_FAILURE; |
|
159 |
|
160 // Get the desired image file name |
|
161 nsCOMPtr<nsIURL> imageURL(do_QueryInterface(imageURI)); |
|
162 if (!imageURL) { |
|
163 // XXXmano (bug 300293): Non-URL images (e.g. the data: protocol) are not |
|
164 // yet supported. What filename should we take here? |
|
165 return NS_ERROR_NOT_IMPLEMENTED; |
|
166 } |
|
167 |
|
168 nsAutoCString fileName; |
|
169 imageURL->GetFileName(fileName); |
|
170 nsCOMPtr<nsIProperties> fileLocator |
|
171 (do_GetService("@mozilla.org/file/directory_service;1", &rv)); |
|
172 NS_ENSURE_SUCCESS(rv, rv); |
|
173 |
|
174 // Get the current user's "Pictures" folder (That's ~/Pictures): |
|
175 fileLocator->Get(NS_OSX_PICTURE_DOCUMENTS_DIR, NS_GET_IID(nsIFile), |
|
176 getter_AddRefs(mBackgroundFile)); |
|
177 if (!mBackgroundFile) |
|
178 return NS_ERROR_OUT_OF_MEMORY; |
|
179 |
|
180 nsAutoString fileNameUnicode; |
|
181 CopyUTF8toUTF16(fileName, fileNameUnicode); |
|
182 |
|
183 // and add the imgage file name itself: |
|
184 mBackgroundFile->Append(fileNameUnicode); |
|
185 |
|
186 // Download the image; the desktop background will be set in OnStateChange() |
|
187 nsCOMPtr<nsIWebBrowserPersist> wbp |
|
188 (do_CreateInstance("@mozilla.org/embedding/browser/nsWebBrowserPersist;1", &rv)); |
|
189 NS_ENSURE_SUCCESS(rv, rv); |
|
190 |
|
191 uint32_t flags = nsIWebBrowserPersist::PERSIST_FLAGS_NO_CONVERSION | |
|
192 nsIWebBrowserPersist::PERSIST_FLAGS_REPLACE_EXISTING_FILES | |
|
193 nsIWebBrowserPersist::PERSIST_FLAGS_FROM_CACHE; |
|
194 |
|
195 wbp->SetPersistFlags(flags); |
|
196 wbp->SetProgressListener(this); |
|
197 |
|
198 nsCOMPtr<nsILoadContext> loadContext; |
|
199 nsCOMPtr<nsISupports> container = content->OwnerDoc()->GetContainer(); |
|
200 nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(container); |
|
201 if (docShell) { |
|
202 loadContext = do_QueryInterface(docShell); |
|
203 } |
|
204 |
|
205 return wbp->SaveURI(imageURI, nullptr, docURI, nullptr, nullptr, |
|
206 mBackgroundFile, loadContext); |
|
207 } |
|
208 |
|
209 NS_IMETHODIMP |
|
210 nsMacShellService::OnProgressChange(nsIWebProgress* aWebProgress, |
|
211 nsIRequest* aRequest, |
|
212 int32_t aCurSelfProgress, |
|
213 int32_t aMaxSelfProgress, |
|
214 int32_t aCurTotalProgress, |
|
215 int32_t aMaxTotalProgress) |
|
216 { |
|
217 return NS_OK; |
|
218 } |
|
219 |
|
220 NS_IMETHODIMP |
|
221 nsMacShellService::OnLocationChange(nsIWebProgress* aWebProgress, |
|
222 nsIRequest* aRequest, |
|
223 nsIURI* aLocation, |
|
224 uint32_t aFlags) |
|
225 { |
|
226 return NS_OK; |
|
227 } |
|
228 |
|
229 NS_IMETHODIMP |
|
230 nsMacShellService::OnStatusChange(nsIWebProgress* aWebProgress, |
|
231 nsIRequest* aRequest, |
|
232 nsresult aStatus, |
|
233 const char16_t* aMessage) |
|
234 { |
|
235 return NS_OK; |
|
236 } |
|
237 |
|
238 NS_IMETHODIMP |
|
239 nsMacShellService::OnSecurityChange(nsIWebProgress* aWebProgress, |
|
240 nsIRequest* aRequest, |
|
241 uint32_t aState) |
|
242 { |
|
243 return NS_OK; |
|
244 } |
|
245 |
|
246 NS_IMETHODIMP |
|
247 nsMacShellService::OnStateChange(nsIWebProgress* aWebProgress, |
|
248 nsIRequest* aRequest, |
|
249 uint32_t aStateFlags, |
|
250 nsresult aStatus) |
|
251 { |
|
252 if (aStateFlags & STATE_STOP) { |
|
253 nsCOMPtr<nsIObserverService> os(do_GetService("@mozilla.org/observer-service;1")); |
|
254 if (os) |
|
255 os->NotifyObservers(nullptr, "shell:desktop-background-changed", nullptr); |
|
256 |
|
257 bool exists = false; |
|
258 mBackgroundFile->Exists(&exists); |
|
259 if (!exists) |
|
260 return NS_OK; |
|
261 |
|
262 nsAutoCString nativePath; |
|
263 mBackgroundFile->GetNativePath(nativePath); |
|
264 |
|
265 AEDesc tAEDesc = { typeNull, nil }; |
|
266 OSErr err = noErr; |
|
267 AliasHandle aliasHandle = nil; |
|
268 FSRef pictureRef; |
|
269 OSStatus status; |
|
270 |
|
271 // Convert the path into a FSRef |
|
272 status = ::FSPathMakeRef((const UInt8*)nativePath.get(), &pictureRef, |
|
273 nullptr); |
|
274 if (status == noErr) { |
|
275 err = ::FSNewAlias(nil, &pictureRef, &aliasHandle); |
|
276 if (err == noErr && aliasHandle == nil) |
|
277 err = paramErr; |
|
278 |
|
279 if (err == noErr) { |
|
280 // We need the descriptor (based on the picture file reference) |
|
281 // for the 'Set Desktop Picture' apple event. |
|
282 char handleState = ::HGetState((Handle)aliasHandle); |
|
283 ::HLock((Handle)aliasHandle); |
|
284 err = ::AECreateDesc(typeAlias, *aliasHandle, |
|
285 GetHandleSize((Handle)aliasHandle), &tAEDesc); |
|
286 // unlock the alias handler |
|
287 ::HSetState((Handle)aliasHandle, handleState); |
|
288 ::DisposeHandle((Handle)aliasHandle); |
|
289 } |
|
290 if (err == noErr) { |
|
291 AppleEvent tAppleEvent; |
|
292 OSType sig = 'MACS'; |
|
293 AEBuildError tAEBuildError; |
|
294 // Create a 'Set Desktop Pictue' Apple Event |
|
295 err = ::AEBuildAppleEvent(kAECoreSuite, kAESetData, typeApplSignature, |
|
296 &sig, sizeof(OSType), kAutoGenerateReturnID, |
|
297 kAnyTransactionID, &tAppleEvent, &tAEBuildError, |
|
298 "'----':'obj '{want:type (prop),form:prop" \ |
|
299 ",seld:type('dpic'),from:'null'()},data:(@)", |
|
300 &tAEDesc); |
|
301 if (err == noErr) { |
|
302 AppleEvent reply = { typeNull, nil }; |
|
303 // Sent the event we built, the reply event isn't necessary |
|
304 err = ::AESend(&tAppleEvent, &reply, kAENoReply, kAENormalPriority, |
|
305 kNoTimeOut, nil, nil); |
|
306 ::AEDisposeDesc(&tAppleEvent); |
|
307 } |
|
308 } |
|
309 } |
|
310 } |
|
311 |
|
312 return NS_OK; |
|
313 } |
|
314 |
|
315 NS_IMETHODIMP |
|
316 nsMacShellService::OpenApplication(int32_t aApplication) |
|
317 { |
|
318 nsresult rv = NS_OK; |
|
319 CFURLRef appURL = nil; |
|
320 OSStatus err = noErr; |
|
321 |
|
322 switch (aApplication) { |
|
323 case nsIShellService::APPLICATION_MAIL: |
|
324 { |
|
325 CFURLRef tempURL = ::CFURLCreateWithString(kCFAllocatorDefault, |
|
326 CFSTR("mailto:"), nullptr); |
|
327 err = ::LSGetApplicationForURL(tempURL, kLSRolesAll, nullptr, &appURL); |
|
328 ::CFRelease(tempURL); |
|
329 } |
|
330 break; |
|
331 case nsIShellService::APPLICATION_NEWS: |
|
332 { |
|
333 CFURLRef tempURL = ::CFURLCreateWithString(kCFAllocatorDefault, |
|
334 CFSTR("news:"), nullptr); |
|
335 err = ::LSGetApplicationForURL(tempURL, kLSRolesAll, nullptr, &appURL); |
|
336 ::CFRelease(tempURL); |
|
337 } |
|
338 break; |
|
339 case nsIMacShellService::APPLICATION_KEYCHAIN_ACCESS: |
|
340 err = ::LSGetApplicationForInfo('APPL', 'kcmr', nullptr, kLSRolesAll, |
|
341 nullptr, &appURL); |
|
342 break; |
|
343 case nsIMacShellService::APPLICATION_NETWORK: |
|
344 { |
|
345 nsCOMPtr<nsIFile> lf; |
|
346 rv = NS_NewNativeLocalFile(NETWORK_PREFPANE, true, getter_AddRefs(lf)); |
|
347 NS_ENSURE_SUCCESS(rv, rv); |
|
348 bool exists; |
|
349 lf->Exists(&exists); |
|
350 if (!exists) |
|
351 return NS_ERROR_FILE_NOT_FOUND; |
|
352 return lf->Launch(); |
|
353 } |
|
354 break; |
|
355 case nsIMacShellService::APPLICATION_DESKTOP: |
|
356 { |
|
357 nsCOMPtr<nsIFile> lf; |
|
358 rv = NS_NewNativeLocalFile(DESKTOP_PREFPANE, true, getter_AddRefs(lf)); |
|
359 NS_ENSURE_SUCCESS(rv, rv); |
|
360 bool exists; |
|
361 lf->Exists(&exists); |
|
362 if (!exists) |
|
363 return NS_ERROR_FILE_NOT_FOUND; |
|
364 return lf->Launch(); |
|
365 } |
|
366 break; |
|
367 } |
|
368 |
|
369 if (appURL && err == noErr) { |
|
370 err = ::LSOpenCFURLRef(appURL, nullptr); |
|
371 rv = err != noErr ? NS_ERROR_FAILURE : NS_OK; |
|
372 |
|
373 ::CFRelease(appURL); |
|
374 } |
|
375 |
|
376 return rv; |
|
377 } |
|
378 |
|
379 NS_IMETHODIMP |
|
380 nsMacShellService::GetDesktopBackgroundColor(uint32_t *aColor) |
|
381 { |
|
382 // This method and |SetDesktopBackgroundColor| has no meaning on Mac OS X. |
|
383 // The mac desktop preferences UI uses pictures for the few solid colors it |
|
384 // supports. |
|
385 return NS_ERROR_NOT_IMPLEMENTED; |
|
386 } |
|
387 |
|
388 NS_IMETHODIMP |
|
389 nsMacShellService::SetDesktopBackgroundColor(uint32_t aColor) |
|
390 { |
|
391 // This method and |GetDesktopBackgroundColor| has no meaning on Mac OS X. |
|
392 // The mac desktop preferences UI uses pictures for the few solid colors it |
|
393 // supports. |
|
394 return NS_ERROR_NOT_IMPLEMENTED; |
|
395 } |
|
396 |
|
397 NS_IMETHODIMP |
|
398 nsMacShellService::OpenApplicationWithURI(nsIFile* aApplication, const nsACString& aURI) |
|
399 { |
|
400 nsCOMPtr<nsILocalFileMac> lfm(do_QueryInterface(aApplication)); |
|
401 CFURLRef appURL; |
|
402 nsresult rv = lfm->GetCFURL(&appURL); |
|
403 if (NS_FAILED(rv)) |
|
404 return rv; |
|
405 |
|
406 const nsCString spec(aURI); |
|
407 const UInt8* uriString = (const UInt8*)spec.get(); |
|
408 CFURLRef uri = ::CFURLCreateWithBytes(nullptr, uriString, aURI.Length(), |
|
409 kCFStringEncodingUTF8, nullptr); |
|
410 if (!uri) |
|
411 return NS_ERROR_OUT_OF_MEMORY; |
|
412 |
|
413 CFArrayRef uris = ::CFArrayCreate(nullptr, (const void**)&uri, 1, nullptr); |
|
414 if (!uris) { |
|
415 ::CFRelease(uri); |
|
416 return NS_ERROR_OUT_OF_MEMORY; |
|
417 } |
|
418 |
|
419 LSLaunchURLSpec launchSpec; |
|
420 launchSpec.appURL = appURL; |
|
421 launchSpec.itemURLs = uris; |
|
422 launchSpec.passThruParams = nullptr; |
|
423 launchSpec.launchFlags = kLSLaunchDefaults; |
|
424 launchSpec.asyncRefCon = nullptr; |
|
425 |
|
426 OSErr err = ::LSOpenFromURLSpec(&launchSpec, nullptr); |
|
427 |
|
428 ::CFRelease(uris); |
|
429 ::CFRelease(uri); |
|
430 |
|
431 return err != noErr ? NS_ERROR_FAILURE : NS_OK; |
|
432 } |
|
433 |
|
434 NS_IMETHODIMP |
|
435 nsMacShellService::GetDefaultFeedReader(nsIFile** _retval) |
|
436 { |
|
437 nsresult rv = NS_ERROR_FAILURE; |
|
438 *_retval = nullptr; |
|
439 |
|
440 CFStringRef defaultHandlerID = ::LSCopyDefaultHandlerForURLScheme(CFSTR("feed")); |
|
441 if (!defaultHandlerID) { |
|
442 defaultHandlerID = ::CFStringCreateWithCString(kCFAllocatorDefault, |
|
443 SAFARI_BUNDLE_IDENTIFIER, |
|
444 kCFStringEncodingASCII); |
|
445 } |
|
446 |
|
447 CFURLRef defaultHandlerURL = nullptr; |
|
448 OSStatus status = ::LSFindApplicationForInfo(kLSUnknownCreator, |
|
449 defaultHandlerID, |
|
450 nullptr, // inName |
|
451 nullptr, // outAppRef |
|
452 &defaultHandlerURL); |
|
453 |
|
454 if (status == noErr && defaultHandlerURL) { |
|
455 nsCOMPtr<nsILocalFileMac> defaultReader = |
|
456 do_CreateInstance("@mozilla.org/file/local;1", &rv); |
|
457 if (NS_SUCCEEDED(rv)) { |
|
458 rv = defaultReader->InitWithCFURL(defaultHandlerURL); |
|
459 if (NS_SUCCEEDED(rv)) { |
|
460 NS_ADDREF(*_retval = defaultReader); |
|
461 rv = NS_OK; |
|
462 } |
|
463 } |
|
464 |
|
465 ::CFRelease(defaultHandlerURL); |
|
466 } |
|
467 |
|
468 ::CFRelease(defaultHandlerID); |
|
469 |
|
470 return rv; |
|
471 } |