Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / widget / cocoa / nsMacSharingService.mm
blobc8509af00f354b30932225546d85519f527cad1f
1 /* -*- Mode: c++; tab-width: 2; indent-tabs-mode: nil; -*- */
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/. */
6 #import <Cocoa/Cocoa.h>
8 #include "nsMacSharingService.h"
10 #include "jsapi.h"
11 #include "js/Array.h"               // JS::NewArrayObject
12 #include "js/PropertyAndElement.h"  // JS_SetElement, JS_SetProperty
13 #include "nsCocoaUtils.h"
14 #include "mozilla/MacStringHelpers.h"
16 NS_IMPL_ISUPPORTS(nsMacSharingService, nsIMacSharingService)
18 NSString* const remindersServiceName =
19     @"com.apple.reminders.RemindersShareExtension";
21 // These are some undocumented constants also used by Safari
22 // to let us open the preferences window
23 NSString* const extensionPrefPanePath =
24     @"/System/Library/PreferencePanes/Extensions.prefPane";
25 const UInt32 openSharingSubpaneDescriptorType = 'ptru';
26 NSString* const openSharingSubpaneActionKey = @"action";
27 NSString* const openSharingSubpaneActionValue = @"revealExtensionPoint";
28 NSString* const openSharingSubpaneProtocolKey = @"protocol";
29 NSString* const openSharingSubpaneProtocolValue = @"com.apple.share-services";
31 // Expose the id so we can pass reference through to JS and back
32 @interface NSSharingService (ExposeName)
33 - (id)name;
34 @end
36 // Filter providers that we do not want to expose to the user, because they are
37 // duplicates or do not work correctly within the context
38 static bool ShouldIgnoreProvider(NSString* aProviderName) {
39   return [aProviderName
40       isEqualToString:@"com.apple.share.System.add-to-safari-reading-list"];
43 // Clean up the activity once the share is complete
44 @interface SharingServiceDelegate : NSObject <NSSharingServiceDelegate> {
45   NSUserActivity* mShareActivity;
48 - (void)cleanup;
50 @end
52 @implementation SharingServiceDelegate
54 - (id)initWithActivity:(NSUserActivity*)activity {
55   self = [super init];
56   mShareActivity = [activity retain];
57   return self;
60 - (void)cleanup {
61   [mShareActivity resignCurrent];
62   [mShareActivity invalidate];
63   [mShareActivity release];
64   mShareActivity = nil;
67 - (void)sharingService:(NSSharingService*)sharingService
68          didShareItems:(NSArray*)items {
69   [self cleanup];
72 - (void)sharingService:(NSSharingService*)service
73     didFailToShareItems:(NSArray*)items
74                   error:(NSError*)error {
75   [self cleanup];
78 - (void)dealloc {
79   [mShareActivity release];
80   [super dealloc];
83 @end
85 static NSString* NSImageToBase64(const NSImage* aImage) {
86   CGImageRef cgRef = [aImage CGImageForProposedRect:nil context:nil hints:nil];
87   NSBitmapImageRep* bitmapRep =
88       [[NSBitmapImageRep alloc] initWithCGImage:cgRef];
89   [bitmapRep setSize:[aImage size]];
90   NSData* imageData =
91       [bitmapRep representationUsingType:NSBitmapImageFileTypePNG
92                               properties:@{}];
93   NSString* base64Encoded = [imageData base64EncodedStringWithOptions:0];
94   [bitmapRep release];
95   return [NSString stringWithFormat:@"data:image/png;base64,%@", base64Encoded];
98 static void SetStrAttribute(JSContext* aCx, JS::Rooted<JSObject*>& aObj,
99                             const char* aKey, NSString* aVal) {
100   nsAutoString strVal;
101   mozilla::CopyNSStringToXPCOMString(aVal, strVal);
102   JS::Rooted<JSString*> title(aCx, JS_NewUCStringCopyZ(aCx, strVal.get()));
103   JS::Rooted<JS::Value> attVal(aCx, JS::StringValue(title));
104   JS_SetProperty(aCx, aObj, aKey, attVal);
107 nsresult nsMacSharingService::GetSharingProviders(
108     const nsAString& aPageUrl, JSContext* aCx,
109     JS::MutableHandle<JS::Value> aResult) {
110   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
112   NSURL* url = nsCocoaUtils::ToNSURL(aPageUrl);
113   if (!url) {
114     // aPageUrl is not a valid URL.
115     return NS_ERROR_FAILURE;
116   }
118   NSArray* sharingService = [NSSharingService sharingServicesForItems:@[ url ]];
119   int32_t serviceCount = 0;
120   JS::Rooted<JSObject*> array(aCx, JS::NewArrayObject(aCx, 0));
122   for (NSSharingService* currentService in sharingService) {
123     if (ShouldIgnoreProvider([currentService name])) {
124       continue;
125     }
126     JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
128     SetStrAttribute(aCx, obj, "name", [currentService name]);
129     SetStrAttribute(aCx, obj, "menuItemTitle", currentService.menuItemTitle);
130     SetStrAttribute(aCx, obj, "image", NSImageToBase64(currentService.image));
132     JS::Rooted<JS::Value> element(aCx, JS::ObjectValue(*obj));
133     JS_SetElement(aCx, array, serviceCount++, element);
134   }
136   aResult.setObject(*array);
138   return NS_OK;
139   NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
142 NS_IMETHODIMP
143 nsMacSharingService::OpenSharingPreferences() {
144   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
146   NSURL* prefPaneURL = [NSURL fileURLWithPath:extensionPrefPanePath
147                                   isDirectory:YES];
148   NSDictionary* args = @{
149     openSharingSubpaneActionKey : openSharingSubpaneActionValue,
150     openSharingSubpaneProtocolKey : openSharingSubpaneProtocolValue
151   };
152   NSData* data = [NSPropertyListSerialization
153       dataWithPropertyList:args
154                     format:NSPropertyListXMLFormat_v1_0
155                    options:0
156                      error:nil];
157   NSAppleEventDescriptor* descriptor = [[NSAppleEventDescriptor alloc]
158       initWithDescriptorType:openSharingSubpaneDescriptorType
159                         data:data];
161   [[NSWorkspace sharedWorkspace] openURLs:@[ prefPaneURL ]
162                   withAppBundleIdentifier:nil
163                                   options:NSWorkspaceLaunchAsync
164            additionalEventParamDescriptor:descriptor
165                         launchIdentifiers:NULL];
167   [descriptor release];
169   return NS_OK;
170   NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
173 NS_IMETHODIMP
174 nsMacSharingService::ShareUrl(const nsAString& aServiceName,
175                               const nsAString& aPageUrl,
176                               const nsAString& aPageTitle) {
177   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
179   NSString* serviceName = nsCocoaUtils::ToNSString(aServiceName);
180   NSURL* pageUrl = nsCocoaUtils::ToNSURL(aPageUrl);
181   NSString* pageTitle = nsCocoaUtils::ToNSString(aPageTitle);
182   NSSharingService* service =
183       [NSSharingService sharingServiceNamed:serviceName];
185   // Reminders fetch its data from an activity, not the share data
186   if ([[service name] isEqual:remindersServiceName]) {
187     NSUserActivity* shareActivity = [[NSUserActivity alloc]
188         initWithActivityType:NSUserActivityTypeBrowsingWeb];
190     if ([pageUrl.scheme hasPrefix:@"http"]) {
191       [shareActivity setWebpageURL:pageUrl];
192     }
193     [shareActivity setEligibleForHandoff:NO];
194     [shareActivity setTitle:pageTitle];
195     [shareActivity becomeCurrent];
197     // Pass ownership of shareActivity to shareDelegate, which will release the
198     // activity once sharing has completed.
199     SharingServiceDelegate* shareDelegate =
200         [[SharingServiceDelegate alloc] initWithActivity:shareActivity];
201     [shareActivity release];
203     [service setDelegate:shareDelegate];
204     [shareDelegate release];
205   }
207   // Twitter likes the the title as an additional share item
208   NSArray* toShare = [[service name] isEqual:NSSharingServiceNamePostOnTwitter]
209                          ? @[ pageUrl, pageTitle ]
210                          : @[ pageUrl ];
212   [service setSubject:pageTitle];
213   [service performWithItems:toShare];
215   return NS_OK;
217   NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);