Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(263)

Side by Side Diff: chrome/browser/ui/cocoa/share_menu_controller.mm

Issue 2950403002: [Mac] Add system share menu
Patch Set: CL comments Created 3 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #import "chrome/browser/ui/cocoa/share_menu_controller.h"
6
7 #include "base/mac/foundation_util.h"
8 #include "base/mac/mac_util.h"
9 #include "base/strings/sys_string_conversions.h"
10 #include "chrome/app/chrome_command_ids.h"
11 #include "chrome/browser/ui/browser.h"
12 #include "chrome/browser/ui/browser_commands.h"
13 #include "chrome/browser/ui/browser_window.h"
14 #import "chrome/browser/ui/cocoa/accelerators_cocoa.h"
15 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
16 #import "chrome/browser/ui/cocoa/fast_resize_view.h"
17 #import "chrome/browser/ui/cocoa/last_active_browser_cocoa.h"
18 #include "chrome/grit/generated_resources.h"
19 #include "net/base/mac/url_conversions.h"
20 #include "ui/base/accelerators/platform_accelerator_cocoa.h"
21 #include "ui/base/l10n/l10n_util_mac.h"
22 #include "ui/gfx/geometry/rect.h"
23 #include "ui/gfx/image/image.h"
24 #include "ui/snapshot/snapshot.h"
25
26 namespace {
27
28 NSString* const kExtensionPrefPanePath =
29 @"/System/Library/PreferencePanes/Extensions.prefPane";
30 // Undocumented, used by Safari.
31 const UInt32 kOpenSharingSubpaneDescriptorType = 'ptru';
32 NSString* const kOpenSharingSubpaneActionKey = @"action";
33 NSString* const kOpenSharingSubpaneActionValue = @"revealExtensionPoint";
34 NSString* const kOpenSharingSubpaneProtocolKey = @"protocol";
35 NSString* const kOpenSharingSubpaneProtocolValue = @"com.apple.share-services";
36
37 } // namespace
38
39 @implementation ShareMenuController {
40 // The following three are provided to the system via NSSharingService
41 // delegates. They're needed for the transition animation, and to provide a
42 // screenshot of the shared site for services that support it.
43 NSWindow* windowForShare_; // weak
44 NSRect rectForShare_;
45 base::scoped_nsobject<NSImage> snapshotForShare_;
46 }
47
48 + (BOOL)shouldShowMoreItem {
49 return base::mac::IsAtLeastOS10_10();
50 }
51
52 // NSMenuDelegate
53 - (void)menuNeedsUpdate:(NSMenu*)menu {
54 [menu removeAllItems];
55 [menu setAutoenablesItems:NO];
56
57 Browser* lastActiveBrowser = chrome::GetLastActiveBrowser();
58 BOOL canShare =
59 lastActiveBrowser != nullptr &&
60 // Avoid |CanEmailPageLocation| segfault in interactive UI tests
61 lastActiveBrowser->tab_strip_model()->GetActiveWebContents() != nullptr &&
62 chrome::CanEmailPageLocation(chrome::GetLastActiveBrowser());
63 // Using a real URL instead of empty string to avoid system log about relative
64 // URLs in the pasteboard.
65 NSArray* services = [NSSharingService
66 sharingServicesForItems:@[ [NSURL URLWithString:@"https://google.com"] ]];
Matt Giuca 2017/07/05 07:02:55 I'm confused what this URL is used for (why there
67 NSSharingService* readingListService = [NSSharingService
68 sharingServiceNamed:NSSharingServiceNameAddToSafariReadingList];
69 for (NSSharingService* service in services) {
70 // Don't include "Add to Reading List"
Robert Sesek 2017/06/30 21:09:34 nit: period
71 if ([service isEqual:readingListService])
72 continue;
73 NSMenuItem* item = [self menuItemForService:service];
74 [item setEnabled:canShare];
75 [menu addItem:item];
76 }
77 if (![[self class] shouldShowMoreItem]) {
78 return;
79 }
80 base::scoped_nsobject<NSMenuItem> moreItem([[NSMenuItem alloc]
81 initWithTitle:l10n_util::GetNSString(IDS_SHARING_MORE_MAC)
82 action:@selector(openSharingPrefs:)
83 keyEquivalent:@""]);
84 [moreItem setTarget:self];
85 [moreItem setImage:[self moreImage]];
86 [menu addItem:moreItem.get()];
Robert Sesek 2017/06/30 21:09:34 You shouldn't need .get() in most cases. scoped_ns
87 }
88
89 // NSSharingServiceDelegate
90
91 - (void)sharingService:(NSSharingService*)service
92 didShareItems:(NSArray*)items {
93 // TODO(lgrey): Add an UMA stat.
94 [self clearTransitionData];
95 }
96
97 - (void)sharingService:(NSSharingService*)service
98 didFailToShareItems:(NSArray*)items
99 error:(NSError*)error {
100 // TODO(lgrey): Add an UMA stat.
101 [self clearTransitionData];
102 }
103
104 - (NSRect)sharingService:(NSSharingService*)service
105 sourceFrameOnScreenForShareItem:(id)item {
106 return rectForShare_;
107 }
108
109 - (NSWindow*)sharingService:(NSSharingService*)service
110 sourceWindowForShareItems:(NSArray*)items
111 sharingContentScope:(NSSharingContentScope*)scope {
112 *scope = NSSharingContentScopeFull;
113 return windowForShare_;
114 }
115
116 - (NSImage*)sharingService:(NSSharingService*)service
117 transitionImageForShareItem:(id)item
118 contentRect:(NSRect*)contentRect {
119 return snapshotForShare_.get();
120 }
121
122 // Private methods
123
124 // Saves details required by delegate methods for the transition animation.
125 - (void)saveTransitionDataFromBrowser:(Browser*)browser {
126 windowForShare_ = browser->window()->GetNativeWindow();
127
128 NSView* contentsView = [[windowForShare_ windowController] tabContentArea];
129 NSRect rectInWindow =
130 [[contentsView superview] convertRect:[contentsView frame] toView:nil];
131 rectForShare_ = [windowForShare_ convertRectToScreen:rectInWindow];
132
133 gfx::Image image;
134 gfx::Rect rect = gfx::Rect(NSRectToCGRect([contentsView bounds]));
135 if (ui::GrabViewSnapshot(contentsView, rect, &image)) {
136 snapshotForShare_.reset(image.CopyNSImage());
137 }
138 }
139
140 - (void)clearTransitionData {
141 windowForShare_ = nil;
142 rectForShare_ = NSZeroRect;
143 snapshotForShare_.reset();
144 }
145
146 // Performs the share action using the sharing service represented by |sender|.
147 - (void)performShare:(NSMenuItem*)sender {
Matt Giuca 2017/07/05 07:02:55 I think this method is too tightly coupling the br
148 Browser* browser = chrome::GetLastActiveBrowser();
149 DCHECK(browser);
150 [self saveTransitionDataFromBrowser:browser];
151
152 content::WebContents* contents =
153 browser->tab_strip_model()->GetActiveWebContents();
154 NSURL* url = net::NSURLWithGURL(contents->GetLastCommittedURL());
155 NSString* title = base::SysUTF16ToNSString(contents->GetTitle());
156
157 NSSharingService* service =
158 base::mac::ObjCCastStrict<NSSharingService>([sender representedObject]);
159 [service setDelegate:self];
160 [service setSubject:title];
161
162 NSArray* itemsToShare;
163 if ([service
164 isEqual:[NSSharingService
165 sharingServiceNamed:NSSharingServiceNamePostOnTwitter]]) {
166 itemsToShare = @[ url, title ];
Robert Sesek 2017/06/30 21:09:34 Should we always put the title in the item?
167 } else {
168 itemsToShare = @[ url ];
169 }
170 [service performWithItems:itemsToShare];
171 }
172
173 // Opens the "Sharing" subpane of the "Extensions" macOS preference pane.
174 - (void)openSharingPrefs:(NSMenuItem*)sender {
175 DCHECK([[self class] shouldShowMoreItem]);
176 NSURL* prefPaneURL =
177 [NSURL fileURLWithPath:kExtensionPrefPanePath isDirectory:YES];
178 NSDictionary* args = @{
179 kOpenSharingSubpaneActionKey : kOpenSharingSubpaneActionValue,
180 kOpenSharingSubpaneProtocolKey : kOpenSharingSubpaneProtocolValue
181 };
182 NSData* data = [NSPropertyListSerialization
183 dataWithPropertyList:args
184 format:NSPropertyListXMLFormat_v1_0
185 options:0
186 error:nil];
187 base::scoped_nsobject<NSAppleEventDescriptor> descriptor(
188 [[NSAppleEventDescriptor alloc]
189 initWithDescriptorType:kOpenSharingSubpaneDescriptorType
190 data:data]);
191 [[NSWorkspace sharedWorkspace] openURLs:@[ prefPaneURL ]
192 withAppBundleIdentifier:nil
193 options:NSWorkspaceLaunchAsync
194 additionalEventParamDescriptor:descriptor.get()
195 launchIdentifiers:NULL];
196 }
197
198 // Returns the image to be used for the "More..." menu item, or nil on macOS
199 // version where this private method is unsupported.
200 - (NSImage*)moreImage {
201 if ([NSSharingServicePicker
202 respondsToSelector:@selector(sharedMoreMenuImage)]) {
203 return
204 [NSSharingServicePicker performSelector:@selector(sharedMoreMenuImage)];
205 }
206 return nil;
207 }
208
209 // Creates a menu item that calls |service| when invoked.
210 - (NSMenuItem*)menuItemForService:(NSSharingService*)service {
211 BOOL isMail = [service
212 isEqual:[NSSharingService
213 sharingServiceNamed:NSSharingServiceNameComposeEmail]];
214 NSString* keyEquivalent = isMail ? [self keyEquivalentForMail] : @"";
215 NSString* title = isMail ? l10n_util::GetNSString(IDS_EMAIL_PAGE_LOCATION_MAC)
216 : service.menuItemTitle;
217 base::scoped_nsobject<NSMenuItem> item([[NSMenuItem alloc]
218 initWithTitle:title
219 action:@selector(performShare:)
220 keyEquivalent:keyEquivalent]);
221 [item setTarget:self];
222 [item setImage:[service image]];
223 [item setRepresentedObject:service];
224 return item.autorelease();
225 }
226
227 - (NSString*)keyEquivalentForMail {
228 AcceleratorsCocoa* keymap = AcceleratorsCocoa::GetInstance();
229 const ui::Accelerator* accelerator =
230 keymap->GetAcceleratorForCommand(IDC_EMAIL_PAGE_LOCATION);
231 const ui::PlatformAcceleratorCocoa* platform =
232 static_cast<const ui::PlatformAcceleratorCocoa*>(
233 accelerator->platform_accelerator());
234 return platform->characters();
235 }
236
237 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698