2 * Copyright (C) 2014 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
27 #import "ServicesController.h"
29 #if ENABLE(SERVICE_CONTROLS)
31 #import "WebContext.h"
32 #import "WebProcessMessages.h"
33 #import <AppKit/NSSharingService.h>
34 #import <wtf/NeverDestroyed.h>
36 #if __has_include(<AppKit/NSSharingService_Private.h>)
37 #import <AppKit/NSSharingService_Private.h>
39 typedef NS_ENUM(NSInteger, NSSharingServicePickerStyle) {
40 NSSharingServicePickerStyleMenu = 0,
41 NSSharingServicePickerStyleRollover = 1,
42 NSSharingServicePickerStyleTextSelection = 2,
43 NSSharingServicePickerStyleDataDetector = 3
44 } NS_ENUM_AVAILABLE_MAC(10_10);
46 typedef NS_ENUM(NSInteger, NSSharingServiceType) {
47 NSSharingServiceTypeShare = 0,
48 NSSharingServiceTypeViewer = 1,
49 NSSharingServiceTypeEditor = 2
50 } NS_ENUM_AVAILABLE_MAC(10_10);
52 typedef NS_OPTIONS(NSUInteger, NSSharingServiceMask) {
53 NSSharingServiceMaskShare = (1 << NSSharingServiceTypeShare),
54 NSSharingServiceMaskViewer = (1 << NSSharingServiceTypeViewer),
55 NSSharingServiceMaskEditor = (1 << NSSharingServiceTypeEditor),
57 NSSharingServiceMaskAllTypes = 0xFFFF
58 } NS_ENUM_AVAILABLE_MAC(10_10);
61 @interface NSSharingServicePicker (WKDetails)
62 @property NSSharingServicePickerStyle style;
66 @interface NSSharingService (WKDetails)
67 + (NSArray *)sharingServicesForItems:(NSArray *)items mask:(NSSharingServiceMask)maskForFiltering;
71 #if __has_include(<Foundation/NSExtension.h>)
72 #import <Foundation/NSExtension.h>
74 @interface NSExtension
78 @interface NSExtension (WKDetails)
79 + (id)beginMatchingExtensionsWithAttributes:(NSDictionary *)attributes completion:(void (^)(NSArray *matchingExtensions, NSError *error))handler;
85 ServicesController& ServicesController::shared()
87 static NeverDestroyed<ServicesController> sharedController;
88 return sharedController;
91 ServicesController::ServicesController()
92 : m_refreshQueue(dispatch_queue_create("com.apple.WebKit.ServicesController", DISPATCH_QUEUE_SERIAL))
93 , m_hasPendingRefresh(false)
94 , m_hasImageServices(false)
95 , m_hasSelectionServices(false)
96 , m_hasRichContentServices(false)
98 refreshExistingServices();
101 auto refreshCallback = [](NSArray *, NSError *) {
102 // We coalese refreshes from the notification callbacks because they can come in small batches.
103 ServicesController::shared().refreshExistingServices(false);
106 auto extensionAttributes = @{ @"NSExtensionPointName" : @"com.apple.services" };
107 m_extensionWatcher = [NSExtension beginMatchingExtensionsWithAttributes:extensionAttributes completion:refreshCallback];
108 auto uiExtensionAttributes = @{ @"NSExtensionPointName" : @"com.apple.ui-services" };
109 m_uiExtensionWatcher = [NSExtension beginMatchingExtensionsWithAttributes:uiExtensionAttributes completion:refreshCallback];
113 static bool hasCompatibleServicesForItems(NSArray *items)
115 return [NSSharingService sharingServicesForItems:items mask:NSSharingServiceMaskViewer | NSSharingServiceMaskEditor].count;
118 void ServicesController::refreshExistingServices(bool refreshImmediately)
120 if (m_hasPendingRefresh)
123 m_hasPendingRefresh = true;
125 auto refreshTime = dispatch_time(DISPATCH_TIME_NOW, refreshImmediately ? 0 : (int64_t)(1 * NSEC_PER_SEC));
126 dispatch_after(refreshTime, m_refreshQueue, ^{
127 static NeverDestroyed<NSImage *> image([[NSImage alloc] init]);
128 bool hasImageServices = hasCompatibleServicesForItems(@[ image ]);
130 static NeverDestroyed<NSAttributedString *> attributedString([[NSAttributedString alloc] initWithString:@"a"]);
131 bool hasSelectionServices = hasCompatibleServicesForItems(@[ attributedString ]);
133 static NSAttributedString *attributedStringWithRichContent;
134 if (!attributedStringWithRichContent) {
135 NSTextAttachment *attachment = [[NSTextAttachment alloc] init];
136 NSTextAttachmentCell *cell = [[NSTextAttachmentCell alloc] initImageCell:image.get()];
137 [attachment setAttachmentCell:cell];
138 NSMutableAttributedString *richString = (NSMutableAttributedString *)[NSMutableAttributedString attributedStringWithAttachment:attachment];
139 [richString appendAttributedString: attributedString];
140 attributedStringWithRichContent = [richString retain];
143 bool hasRichContentServices = hasCompatibleServicesForItems(@[ attributedStringWithRichContent ]);
145 dispatch_async(dispatch_get_main_queue(), ^{
146 bool availableServicesChanged = (hasImageServices != m_hasImageServices) || (hasSelectionServices != m_hasSelectionServices) || (hasRichContentServices != m_hasRichContentServices);
148 m_hasSelectionServices = hasSelectionServices;
149 m_hasImageServices = hasImageServices;
150 m_hasRichContentServices = hasRichContentServices;
152 if (availableServicesChanged) {
153 for (auto& context : WebContext::allContexts())
154 context->sendToAllProcesses(Messages::WebProcess::SetEnabledServices(m_hasImageServices, m_hasSelectionServices, m_hasRichContentServices));
157 m_hasPendingRefresh = false;
162 } // namespace WebKit
164 #endif // ENABLE(SERVICE_CONTROLS)