OLD | NEW |
---|---|
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/ui/cocoa/renderer_context_menu/render_view_context_menu _mac.h" | 5 #include "chrome/browser/ui/cocoa/renderer_context_menu/render_view_context_menu _mac.h" |
6 | 6 |
7 #include <utility> | 7 #include <utility> |
8 | 8 |
9 #include "base/compiler_specific.h" | 9 #include "base/compiler_specific.h" |
10 #include "base/mac/mac_util.h" | 10 #include "base/mac/mac_util.h" |
11 #import "base/mac/scoped_objc_class_swizzler.h" | 11 #import "base/mac/scoped_objc_class_swizzler.h" |
12 #import "base/mac/scoped_sending_event.h" | 12 #import "base/mac/scoped_sending_event.h" |
13 #include "base/macros.h" | 13 #include "base/macros.h" |
14 #include "base/message_loop/message_loop.h" | 14 #include "base/message_loop/message_loop.h" |
15 #include "base/strings/sys_string_conversions.h" | 15 #include "base/strings/sys_string_conversions.h" |
16 #include "base/tracked_objects.h" | 16 #include "base/tracked_objects.h" |
17 #include "chrome/app/chrome_command_ids.h" | 17 #include "chrome/app/chrome_command_ids.h" |
18 #import "chrome/browser/mac/nsprocessinfo_additions.h" | 18 #import "chrome/browser/mac/nsprocessinfo_additions.h" |
19 #include "chrome/grit/generated_resources.h" | 19 #include "chrome/grit/generated_resources.h" |
20 #include "content/public/browser/render_view_host.h" | 20 #include "content/public/browser/render_view_host.h" |
21 #include "content/public/browser/render_widget_host.h" | 21 #include "content/public/browser/render_widget_host.h" |
22 #include "content/public/browser/render_widget_host_view.h" | 22 #include "content/public/browser/render_widget_host_view.h" |
23 #import "ui/base/cocoa/menu_controller.h" | 23 #import "ui/base/cocoa/menu_controller.h" |
24 #include "ui/base/l10n/l10n_util.h" | 24 #include "ui/base/l10n/l10n_util.h" |
25 #include "ui/strings/grit/ui_strings.h" | |
25 | 26 |
26 using content::WebContents; | 27 using content::WebContents; |
27 | 28 |
28 namespace { | 29 namespace { |
29 | 30 |
30 IMP g_original_populatemenu_implementation = nullptr; | 31 IMP g_original_populatemenu_implementation = nullptr; |
31 | 32 |
32 // |g_filtered_entries_array| is only set during testing (see | 33 // |g_filtered_entries_array| is only set during testing (see |
33 // +[ChromeSwizzleServicesMenuUpdater storeFilteredEntriesForTestingInArray:]). | 34 // +[ChromeSwizzleServicesMenuUpdater storeFilteredEntriesForTestingInArray:]). |
34 // Otherwise it remains nil. | 35 // Otherwise it remains nil. |
(...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
168 bool hidden, | 169 bool hidden, |
169 const base::string16& title) override { | 170 const base::string16& title) override { |
170 context_menu_->UpdateToolkitMenuItem( | 171 context_menu_->UpdateToolkitMenuItem( |
171 command_id, enabled, hidden, title); | 172 command_id, enabled, hidden, title); |
172 } | 173 } |
173 | 174 |
174 RenderViewContextMenuMac* context_menu_; | 175 RenderViewContextMenuMac* context_menu_; |
175 DISALLOW_COPY_AND_ASSIGN(ToolkitDelegateMac); | 176 DISALLOW_COPY_AND_ASSIGN(ToolkitDelegateMac); |
176 }; | 177 }; |
177 | 178 |
178 // Obj-C bridge class that is the target of all items in the context menu. | |
179 // Relies on the tag being set to the command id. | |
180 | |
181 RenderViewContextMenuMac::RenderViewContextMenuMac( | 179 RenderViewContextMenuMac::RenderViewContextMenuMac( |
182 content::RenderFrameHost* render_frame_host, | 180 content::RenderFrameHost* render_frame_host, |
183 const content::ContextMenuParams& params, | 181 const content::ContextMenuParams& params, |
184 NSView* parent_view) | 182 NSView* parent_view) |
185 : RenderViewContextMenu(render_frame_host, params), | 183 : RenderViewContextMenu(render_frame_host, params), |
186 speech_submenu_model_(this), | 184 parent_view_(parent_view), |
187 bidi_submenu_model_(this), | 185 text_services_context_menu_(this) { |
188 parent_view_(parent_view) { | |
189 std::unique_ptr<ToolkitDelegate> delegate(new ToolkitDelegateMac(this)); | 186 std::unique_ptr<ToolkitDelegate> delegate(new ToolkitDelegateMac(this)); |
190 set_toolkit_delegate(std::move(delegate)); | 187 set_toolkit_delegate(std::move(delegate)); |
191 } | 188 } |
192 | 189 |
193 RenderViewContextMenuMac::~RenderViewContextMenuMac() { | 190 RenderViewContextMenuMac::~RenderViewContextMenuMac() { |
194 } | 191 } |
195 | 192 |
193 bool RenderViewContextMenuMac::IsCommandIdChecked(int command_id) const { | |
194 if (command_id == IDC_CONTENT_CONTEXT_LOOK_UP) | |
195 return false; | |
196 | |
197 return RenderViewContextMenu::IsCommandIdChecked(command_id); | |
198 } | |
199 | |
200 bool RenderViewContextMenuMac::IsCommandIdEnabled(int command_id) const { | |
201 if (command_id == IDC_CONTENT_CONTEXT_LOOK_UP) | |
202 return true; | |
203 | |
204 return RenderViewContextMenu::IsCommandIdEnabled(command_id); | |
205 } | |
206 | |
207 void RenderViewContextMenuMac::ExecuteCommand(int command_id, int event_flags) { | |
208 if (command_id == IDC_CONTENT_CONTEXT_LOOK_UP) | |
209 LookUpInDictionary(); | |
210 else | |
211 RenderViewContextMenu::ExecuteCommand(command_id, event_flags); | |
212 } | |
213 | |
214 base::string16 RenderViewContextMenuMac::GetSelectedText() const { | |
215 return params_.selection_text; | |
216 } | |
217 | |
218 bool RenderViewContextMenuMac::IsTextDirectionEnabled( | |
219 base::i18n::TextDirection direction) const { | |
220 return BiDiParamsForDirection(direction) & | |
221 blink::WebContextMenuData::CheckableMenuItemEnabled; | |
222 } | |
223 | |
224 bool RenderViewContextMenuMac::IsTextDirectionChecked( | |
225 base::i18n::TextDirection direction) const { | |
226 return BiDiParamsForDirection(direction) & | |
227 blink::WebContextMenuData::CheckableMenuItemChecked; | |
228 } | |
229 | |
230 void RenderViewContextMenuMac::UpdateTextDirection( | |
231 base::i18n::TextDirection direction) { | |
232 DCHECK_NE(direction, base::i18n::UNKNOWN_DIRECTION); | |
233 | |
234 content::RenderViewHost* view_host = GetRenderViewHost(); | |
Alexei Svitkine (slow)
2017/02/03 15:43:27
Nit: Move this above line 242 right before it's us
spqchan
2017/02/03 23:32:10
Done.
| |
235 blink::WebTextDirection dir = blink::WebTextDirectionLeftToRight; | |
236 int command_id = IDC_WRITING_DIRECTION_LTR; | |
237 if (direction == base::i18n::RIGHT_TO_LEFT) { | |
238 dir = blink::WebTextDirectionRightToLeft; | |
239 command_id = IDC_WRITING_DIRECTION_RTL; | |
240 } | |
241 | |
242 view_host->GetWidget()->UpdateTextDirection(dir); | |
243 view_host->GetWidget()->NotifyTextDirection(); | |
244 | |
245 RenderViewContextMenu::RecordUsedItem(command_id); | |
246 } | |
247 | |
196 void RenderViewContextMenuMac::Show() { | 248 void RenderViewContextMenuMac::Show() { |
197 menu_controller_.reset( | 249 menu_controller_.reset( |
198 [[MenuController alloc] initWithModel:&menu_model_ | 250 [[MenuController alloc] initWithModel:&menu_model_ |
199 useWithPopUpButtonCell:NO]); | 251 useWithPopUpButtonCell:NO]); |
200 | 252 |
201 gfx::Point params_position(params_.x, params_.y); | 253 gfx::Point params_position(params_.x, params_.y); |
202 params_position += RenderViewContextMenu::GetOffset(GetRenderFrameHost()); | 254 params_position += RenderViewContextMenu::GetOffset(GetRenderFrameHost()); |
203 | 255 |
204 // Synthesize an event for the click, as there is no certainty that | 256 // Synthesize an event for the click, as there is no certainty that |
205 // [NSApp currentEvent] will return a valid event. | 257 // [NSApp currentEvent] will return a valid event. |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
237 tracked_objects::TaskStopwatch stopwatch; | 289 tracked_objects::TaskStopwatch stopwatch; |
238 stopwatch.Start(); | 290 stopwatch.Start(); |
239 // Show the menu. | 291 // Show the menu. |
240 [NSMenu popUpContextMenu:[menu_controller_ menu] | 292 [NSMenu popUpContextMenu:[menu_controller_ menu] |
241 withEvent:clickEvent | 293 withEvent:clickEvent |
242 forView:parent_view_]; | 294 forView:parent_view_]; |
243 stopwatch.Stop(); | 295 stopwatch.Stop(); |
244 } | 296 } |
245 } | 297 } |
246 | 298 |
247 void RenderViewContextMenuMac::ExecuteCommand(int command_id, int event_flags) { | |
248 switch (command_id) { | |
249 case IDC_CONTENT_CONTEXT_LOOK_UP: | |
250 LookUpInDictionary(); | |
251 break; | |
252 | |
253 case IDC_CONTENT_CONTEXT_SPEECH_START_SPEAKING: | |
254 StartSpeaking(); | |
255 break; | |
256 | |
257 case IDC_CONTENT_CONTEXT_SPEECH_STOP_SPEAKING: | |
258 StopSpeaking(); | |
259 break; | |
260 | |
261 case IDC_WRITING_DIRECTION_DEFAULT: | |
262 // WebKit's current behavior is for this menu item to always be disabled. | |
263 NOTREACHED(); | |
264 break; | |
265 | |
266 case IDC_WRITING_DIRECTION_RTL: | |
267 case IDC_WRITING_DIRECTION_LTR: { | |
268 content::RenderViewHost* view_host = GetRenderViewHost(); | |
269 blink::WebTextDirection dir = blink::WebTextDirectionLeftToRight; | |
270 if (command_id == IDC_WRITING_DIRECTION_RTL) | |
271 dir = blink::WebTextDirectionRightToLeft; | |
272 view_host->GetWidget()->UpdateTextDirection(dir); | |
273 view_host->GetWidget()->NotifyTextDirection(); | |
274 RenderViewContextMenu::RecordUsedItem(command_id); | |
275 break; | |
276 } | |
277 | |
278 default: | |
279 RenderViewContextMenu::ExecuteCommand(command_id, event_flags); | |
280 break; | |
281 } | |
282 } | |
283 | |
284 bool RenderViewContextMenuMac::IsCommandIdChecked(int command_id) const { | |
285 switch (command_id) { | |
286 case IDC_WRITING_DIRECTION_DEFAULT: | |
287 return params_.writing_direction_default & | |
288 blink::WebContextMenuData::CheckableMenuItemChecked; | |
289 case IDC_WRITING_DIRECTION_RTL: | |
290 return params_.writing_direction_right_to_left & | |
291 blink::WebContextMenuData::CheckableMenuItemChecked; | |
292 case IDC_WRITING_DIRECTION_LTR: | |
293 return params_.writing_direction_left_to_right & | |
294 blink::WebContextMenuData::CheckableMenuItemChecked; | |
295 | |
296 default: | |
297 return RenderViewContextMenu::IsCommandIdChecked(command_id); | |
298 } | |
299 } | |
300 | |
301 bool RenderViewContextMenuMac::IsCommandIdEnabled(int command_id) const { | |
302 switch (command_id) { | |
303 case IDC_CONTENT_CONTEXT_LOOK_UP: | |
304 // This is OK because the menu is not shown when it isn't | |
305 // appropriate. | |
306 return true; | |
307 | |
308 case IDC_CONTENT_CONTEXT_SPEECH_START_SPEAKING: | |
309 // This is OK because the menu is not shown when it isn't | |
310 // appropriate. | |
311 return true; | |
312 | |
313 case IDC_CONTENT_CONTEXT_SPEECH_STOP_SPEAKING: { | |
314 content::RenderWidgetHostView* view = | |
315 GetRenderViewHost()->GetWidget()->GetView(); | |
316 return view && view->IsSpeaking(); | |
317 } | |
318 | |
319 case IDC_WRITING_DIRECTION_DEFAULT: // Provided to match OS defaults. | |
320 return params_.writing_direction_default & | |
321 blink::WebContextMenuData::CheckableMenuItemEnabled; | |
322 case IDC_WRITING_DIRECTION_RTL: | |
323 return params_.writing_direction_right_to_left & | |
324 blink::WebContextMenuData::CheckableMenuItemEnabled; | |
325 case IDC_WRITING_DIRECTION_LTR: | |
326 return params_.writing_direction_left_to_right & | |
327 blink::WebContextMenuData::CheckableMenuItemEnabled; | |
328 | |
329 default: | |
330 return RenderViewContextMenu::IsCommandIdEnabled(command_id); | |
331 } | |
332 } | |
333 | |
334 void RenderViewContextMenuMac::AppendPlatformEditableItems() { | |
335 // OS X provides a contextual menu to set writing direction for BiDi | |
336 // languages. | |
337 // This functionality is exposed as a keyboard shortcut on Windows & Linux. | |
338 AppendBidiSubMenu(); | |
339 } | |
340 | |
341 void RenderViewContextMenuMac::InitToolkitMenu() { | 299 void RenderViewContextMenuMac::InitToolkitMenu() { |
342 if (params_.selection_text.empty()) | |
343 return; | |
344 | |
345 if (params_.link_url.is_empty()) { | 300 if (params_.link_url.is_empty()) { |
346 // In case the user has selected a word that triggers spelling suggestions, | 301 // In case the user has selected a word that triggers spelling suggestions, |
347 // show the dictionary lookup under the group that contains the command to | 302 // show the dictionary lookup under the group that contains the command to |
348 // “Add to Dictionary.” | 303 // “Add to Dictionary.” |
349 int index = menu_model_.GetIndexOfCommandId( | 304 int index = |
350 IDC_SPELLCHECK_ADD_TO_DICTIONARY); | 305 menu_model_.GetIndexOfCommandId(IDC_SPELLCHECK_ADD_TO_DICTIONARY); |
351 if (index < 0) { | 306 if (index < 0) { |
352 index = 0; | 307 index = 0; |
353 } else { | 308 } else { |
354 while (menu_model_.GetTypeAt(index) != ui::MenuModel::TYPE_SEPARATOR) { | 309 while (menu_model_.GetTypeAt(index) != ui::MenuModel::TYPE_SEPARATOR) |
355 index++; | 310 index++; |
356 } | 311 |
357 index += 1; // Place it below the separator. | 312 index += 1; // Place it below the separator. |
358 } | 313 } |
359 | 314 |
360 base::string16 printable_selection_text = PrintableSelectionText(); | 315 base::string16 printable_selection_text = PrintableSelectionText(); |
361 EscapeAmpersands(&printable_selection_text); | 316 EscapeAmpersands(&printable_selection_text); |
362 menu_model_.InsertItemAt( | 317 menu_model_.InsertItemAt( |
363 index++, | 318 index++, IDC_CONTENT_CONTEXT_LOOK_UP, |
364 IDC_CONTENT_CONTEXT_LOOK_UP, | |
365 l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_LOOK_UP, | 319 l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_LOOK_UP, |
366 printable_selection_text)); | 320 printable_selection_text)); |
367 menu_model_.InsertSeparatorAt(index++, ui::NORMAL_SEPARATOR); | 321 menu_model_.InsertSeparatorAt(index, ui::NORMAL_SEPARATOR); |
368 } | 322 } |
369 | 323 |
370 content::RenderWidgetHostView* view = | 324 text_services_context_menu_.AppendToContextMenu(&menu_model_); |
371 GetRenderViewHost()->GetWidget()->GetView(); | |
372 if (view && view->SupportsSpeech()) { | |
373 menu_model_.AddSeparator(ui::NORMAL_SEPARATOR); | |
374 speech_submenu_model_.AddItemWithStringId( | |
375 IDC_CONTENT_CONTEXT_SPEECH_START_SPEAKING, | |
376 IDS_SPEECH_START_SPEAKING_MAC); | |
377 speech_submenu_model_.AddItemWithStringId( | |
378 IDC_CONTENT_CONTEXT_SPEECH_STOP_SPEAKING, | |
379 IDS_SPEECH_STOP_SPEAKING_MAC); | |
380 menu_model_.AddSubMenu( | |
381 IDC_CONTENT_CONTEXT_SPEECH_MENU, | |
382 l10n_util::GetStringUTF16(IDS_SPEECH_MAC), | |
383 &speech_submenu_model_); | |
384 } | |
385 } | 325 } |
386 | 326 |
387 void RenderViewContextMenuMac::CancelToolkitMenu() { | 327 void RenderViewContextMenuMac::CancelToolkitMenu() { |
388 [menu_controller_ cancel]; | 328 [menu_controller_ cancel]; |
389 } | 329 } |
390 | 330 |
391 void RenderViewContextMenuMac::UpdateToolkitMenuItem( | 331 void RenderViewContextMenuMac::UpdateToolkitMenuItem( |
392 int command_id, | 332 int command_id, |
393 bool enabled, | 333 bool enabled, |
394 bool hidden, | 334 bool hidden, |
395 const base::string16& title) { | 335 const base::string16& title) { |
396 NSMenuItem* item = GetMenuItemByID(&menu_model_, [menu_controller_ menu], | 336 NSMenuItem* item = GetMenuItemByID(&menu_model_, [menu_controller_ menu], |
397 command_id); | 337 command_id); |
398 if (!item) | 338 if (!item) |
399 return; | 339 return; |
400 | 340 |
401 // Update the returned NSMenuItem directly so we can update it immediately. | 341 // Update the returned NSMenuItem directly so we can update it immediately. |
402 [item setEnabled:enabled]; | 342 [item setEnabled:enabled]; |
403 [item setTitle:base::SysUTF16ToNSString(title)]; | 343 [item setTitle:base::SysUTF16ToNSString(title)]; |
404 [item setHidden:hidden]; | 344 [item setHidden:hidden]; |
405 [[item menu] itemChanged:item]; | 345 [[item menu] itemChanged:item]; |
406 } | 346 } |
407 | 347 |
408 void RenderViewContextMenuMac::AppendBidiSubMenu() { | |
409 bidi_submenu_model_.AddCheckItem(IDC_WRITING_DIRECTION_DEFAULT, | |
410 l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_WRITING_DIRECTION_DEFAULT)); | |
411 bidi_submenu_model_.AddCheckItem(IDC_WRITING_DIRECTION_LTR, | |
412 l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_WRITING_DIRECTION_LTR)); | |
413 bidi_submenu_model_.AddCheckItem(IDC_WRITING_DIRECTION_RTL, | |
414 l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_WRITING_DIRECTION_RTL)); | |
415 | |
416 menu_model_.AddSubMenu( | |
417 IDC_WRITING_DIRECTION_MENU, | |
418 l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_WRITING_DIRECTION_MENU), | |
419 &bidi_submenu_model_); | |
420 } | |
421 | |
422 void RenderViewContextMenuMac::LookUpInDictionary() { | 348 void RenderViewContextMenuMac::LookUpInDictionary() { |
423 content::RenderWidgetHostView* view = | 349 content::RenderWidgetHostView* view = |
424 GetRenderViewHost()->GetWidget()->GetView(); | 350 GetRenderViewHost()->GetWidget()->GetView(); |
425 if (view) | 351 if (view) |
426 view->ShowDefinitionForSelection(); | 352 view->ShowDefinitionForSelection(); |
427 } | 353 } |
428 | 354 |
429 void RenderViewContextMenuMac::StartSpeaking() { | 355 int RenderViewContextMenuMac::BiDiParamsForDirection( |
430 content::RenderWidgetHostView* view = | 356 base::i18n::TextDirection direction) const { |
431 GetRenderViewHost()->GetWidget()->GetView(); | 357 switch (direction) { |
432 if (view) | 358 case base::i18n::UNKNOWN_DIRECTION: |
433 view->SpeakSelection(); | 359 return params_.writing_direction_default; |
360 case base::i18n::RIGHT_TO_LEFT: | |
361 return params_.writing_direction_right_to_left; | |
362 case base::i18n::LEFT_TO_RIGHT: | |
363 return params_.writing_direction_left_to_right; | |
364 } | |
434 } | 365 } |
435 | |
436 void RenderViewContextMenuMac::StopSpeaking() { | |
437 content::RenderWidgetHostView* view = | |
438 GetRenderViewHost()->GetWidget()->GetView(); | |
439 if (view) | |
440 view->StopSpeaking(); | |
441 } | |
OLD | NEW |