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

Side by Side Diff: chrome/browser/ui/cocoa/renderer_context_menu/render_view_context_menu_mac.mm

Issue 2164483006: [MacViews] Implemented text context menu (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix for tapted 3 Created 4 years 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
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 141 matching lines...) Expand 10 before | Expand all | Expand 10 after
176 }; 177 };
177 178
178 // Obj-C bridge class that is the target of all items in the context menu. 179 // 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 // Relies on the tag being set to the command id.
180 181
181 RenderViewContextMenuMac::RenderViewContextMenuMac( 182 RenderViewContextMenuMac::RenderViewContextMenuMac(
182 content::RenderFrameHost* render_frame_host, 183 content::RenderFrameHost* render_frame_host,
183 const content::ContextMenuParams& params, 184 const content::ContextMenuParams& params,
184 NSView* parent_view) 185 NSView* parent_view)
185 : RenderViewContextMenu(render_frame_host, params), 186 : RenderViewContextMenu(render_frame_host, params),
186 speech_submenu_model_(this), 187 parent_view_(parent_view),
187 bidi_submenu_model_(this), 188 text_services_context_menu_(this) {
188 parent_view_(parent_view) {
189 std::unique_ptr<ToolkitDelegate> delegate(new ToolkitDelegateMac(this)); 189 std::unique_ptr<ToolkitDelegate> delegate(new ToolkitDelegateMac(this));
190 set_toolkit_delegate(std::move(delegate)); 190 set_toolkit_delegate(std::move(delegate));
191 } 191 }
192 192
193 RenderViewContextMenuMac::~RenderViewContextMenuMac() { 193 RenderViewContextMenuMac::~RenderViewContextMenuMac() {
194 } 194 }
195 195
196 void RenderViewContextMenuMac::Show() { 196 void RenderViewContextMenuMac::Show() {
197 menu_controller_.reset( 197 menu_controller_.reset(
198 [[MenuController alloc] initWithModel:&menu_model_ 198 [[MenuController alloc] initWithModel:&menu_model_
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
238 stopwatch.Start(); 238 stopwatch.Start();
239 // Show the menu. 239 // Show the menu.
240 [NSMenu popUpContextMenu:[menu_controller_ menu] 240 [NSMenu popUpContextMenu:[menu_controller_ menu]
241 withEvent:clickEvent 241 withEvent:clickEvent
242 forView:parent_view_]; 242 forView:parent_view_];
243 stopwatch.Stop(); 243 stopwatch.Stop();
244 } 244 }
245 } 245 }
246 246
247 void RenderViewContextMenuMac::ExecuteCommand(int command_id, int event_flags) { 247 void RenderViewContextMenuMac::ExecuteCommand(int command_id, int event_flags) {
248 switch (command_id) { 248 if (text_services_context_menu_.IsTextServicesCommandId(command_id))
tapted 2016/12/21 11:20:25 I think this isn't needed now (text services comma
spqchan 2016/12/21 22:00:13 Done.
249 case IDC_CONTENT_CONTEXT_LOOK_UP: 249 text_services_context_menu_.ExecuteCommand(command_id, event_flags);
250 LookUpInDictionary(); 250 else if (command_id == IDC_CONTENT_CONTEXT_LOOK_UP)
251 break; 251 LookUpInDictionary();
252 252 else
253 case IDC_CONTENT_CONTEXT_SPEECH_START_SPEAKING: 253 RenderViewContextMenu::ExecuteCommand(command_id, event_flags);
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 } 254 }
283 255
284 bool RenderViewContextMenuMac::IsCommandIdChecked(int command_id) const { 256 bool RenderViewContextMenuMac::IsCommandIdChecked(int command_id) const {
285 switch (command_id) { 257 if (text_services_context_menu_.IsTextServicesCommandId(command_id))
tapted 2016/12/21 11:20:26 same here, and below
spqchan 2016/12/21 22:00:13 Done.
286 case IDC_WRITING_DIRECTION_DEFAULT: 258 return text_services_context_menu_.IsCommandIdChecked(command_id);
287 return params_.writing_direction_default & 259 else if (command_id == IDC_CONTENT_CONTEXT_LOOK_UP)
288 blink::WebContextMenuData::CheckableMenuItemChecked; 260 return false;
289 case IDC_WRITING_DIRECTION_RTL: 261 else
tapted 2016/12/21 11:20:26 nit: "no else after return" - https://chromium.goo
spqchan 2016/12/21 22:00:13 Done.
290 return params_.writing_direction_right_to_left & 262 return RenderViewContextMenu::IsCommandIdChecked(command_id);
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 } 263 }
300 264
301 bool RenderViewContextMenuMac::IsCommandIdEnabled(int command_id) const { 265 bool RenderViewContextMenuMac::IsCommandIdEnabled(int command_id) const {
302 switch (command_id) { 266 if (text_services_context_menu_.IsTextServicesCommandId(command_id))
303 case IDC_CONTENT_CONTEXT_LOOK_UP: 267 return text_services_context_menu_.IsCommandIdEnabled(command_id);
304 // This is OK because the menu is not shown when it isn't 268 else if (command_id == IDC_CONTENT_CONTEXT_LOOK_UP)
305 // appropriate. 269 return true;
306 return true; 270 else
307 271 return RenderViewContextMenu::IsCommandIdEnabled(command_id);
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 } 272 }
340 273
341 void RenderViewContextMenuMac::InitToolkitMenu() { 274 void RenderViewContextMenuMac::InitToolkitMenu() {
342 if (params_.selection_text.empty())
343 return;
344
345 if (params_.link_url.is_empty()) { 275 if (params_.link_url.is_empty()) {
346 // In case the user has selected a word that triggers spelling suggestions, 276 // 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 277 // show the dictionary lookup under the group that contains the command to
348 // “Add to Dictionary.” 278 // “Add to Dictionary.”
349 int index = menu_model_.GetIndexOfCommandId( 279 int index =
350 IDC_SPELLCHECK_ADD_TO_DICTIONARY); 280 menu_model_.GetIndexOfCommandId(IDC_SPELLCHECK_ADD_TO_DICTIONARY);
351 if (index < 0) { 281 if (index < 0) {
352 index = 0; 282 index = 0;
353 } else { 283 } else {
354 while (menu_model_.GetTypeAt(index) != ui::MenuModel::TYPE_SEPARATOR) { 284 while (menu_model_.GetTypeAt(index) != ui::MenuModel::TYPE_SEPARATOR)
355 index++; 285 index++;
356 } 286
357 index += 1; // Place it below the separator. 287 index += 1; // Place it below the separator.
358 } 288 }
359 289
360 base::string16 printable_selection_text = PrintableSelectionText(); 290 base::string16 printable_selection_text = PrintableSelectionText();
361 EscapeAmpersands(&printable_selection_text); 291 EscapeAmpersands(&printable_selection_text);
362 menu_model_.InsertItemAt( 292 menu_model_.InsertItemAt(
363 index++, 293 index++, IDC_CONTENT_CONTEXT_LOOK_UP,
364 IDC_CONTENT_CONTEXT_LOOK_UP,
365 l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_LOOK_UP, 294 l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_LOOK_UP,
366 printable_selection_text)); 295 printable_selection_text));
367 menu_model_.InsertSeparatorAt(index++, ui::NORMAL_SEPARATOR); 296 menu_model_.InsertSeparatorAt(index + 1, ui::NORMAL_SEPARATOR);
tapted 2016/12/21 11:20:26 I don't think this will be the same due to postfix
spqchan 2016/12/21 22:00:12 Done.
368 } 297 }
369 298
370 content::RenderWidgetHostView* view = 299 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 } 300 }
386 301
387 void RenderViewContextMenuMac::CancelToolkitMenu() { 302 void RenderViewContextMenuMac::CancelToolkitMenu() {
388 [menu_controller_ cancel]; 303 [menu_controller_ cancel];
389 } 304 }
390 305
391 void RenderViewContextMenuMac::UpdateToolkitMenuItem( 306 void RenderViewContextMenuMac::UpdateToolkitMenuItem(
392 int command_id, 307 int command_id,
393 bool enabled, 308 bool enabled,
394 bool hidden, 309 bool hidden,
395 const base::string16& title) { 310 const base::string16& title) {
396 NSMenuItem* item = GetMenuItemByID(&menu_model_, [menu_controller_ menu], 311 NSMenuItem* item = GetMenuItemByID(&menu_model_, [menu_controller_ menu],
397 command_id); 312 command_id);
398 if (!item) 313 if (!item)
399 return; 314 return;
400 315
401 // Update the returned NSMenuItem directly so we can update it immediately. 316 // Update the returned NSMenuItem directly so we can update it immediately.
402 [item setEnabled:enabled]; 317 [item setEnabled:enabled];
403 [item setTitle:base::SysUTF16ToNSString(title)]; 318 [item setTitle:base::SysUTF16ToNSString(title)];
404 [item setHidden:hidden]; 319 [item setHidden:hidden];
405 [[item menu] itemChanged:item]; 320 [[item menu] itemChanged:item];
406 } 321 }
407 322
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() { 323 void RenderViewContextMenuMac::LookUpInDictionary() {
423 content::RenderWidgetHostView* view = 324 content::RenderWidgetHostView* view =
424 GetRenderViewHost()->GetWidget()->GetView(); 325 GetRenderViewHost()->GetWidget()->GetView();
425 if (view) 326 if (view)
426 view->ShowDefinitionForSelection(); 327 view->ShowDefinitionForSelection();
427 } 328 }
428 329
429 void RenderViewContextMenuMac::StartSpeaking() { 330 base::string16 RenderViewContextMenuMac::GetSelectedText() const {
331 return params_.selection_text;
332 }
333
334 void RenderViewContextMenuMac::OnSpeakRequested() {
430 content::RenderWidgetHostView* view = 335 content::RenderWidgetHostView* view =
431 GetRenderViewHost()->GetWidget()->GetView(); 336 GetRenderViewHost()->GetWidget()->GetView();
432 if (view) 337 if (view)
433 view->SpeakSelection(); 338 view->SpeakSelection();
434 } 339 }
435 340
436 void RenderViewContextMenuMac::StopSpeaking() { 341 bool RenderViewContextMenuMac::IsTextDirectionEnabled(
tapted 2016/12/21 11:20:25 nit: method order should match the header
spqchan 2016/12/21 22:00:13 Done.
437 content::RenderWidgetHostView* view = 342 base::i18n::TextDirection direction) const {
438 GetRenderViewHost()->GetWidget()->GetView(); 343 switch (direction) {
439 if (view) 344 case base::i18n::TextDirection::UNKNOWN_DIRECTION:
440 view->StopSpeaking(); 345 return params_.writing_direction_default &
tapted 2016/12/21 11:20:26 nit: I think this needs a private helper method:
spqchan 2016/12/21 22:00:13 Done.
346 blink::WebContextMenuData::CheckableMenuItemEnabled;
347 case base::i18n::TextDirection::RIGHT_TO_LEFT:
348 return params_.writing_direction_right_to_left &
349 blink::WebContextMenuData::CheckableMenuItemEnabled;
350 case base::i18n::TextDirection::LEFT_TO_RIGHT:
351 return params_.writing_direction_left_to_right &
352 blink::WebContextMenuData::CheckableMenuItemEnabled;
353 case base::i18n::TextDirection::TEXT_DIRECTION_NUM_DIRECTIONS:
354 NOTREACHED();
355 return false;
356 }
441 } 357 }
358
359 bool RenderViewContextMenuMac::IsTextDirectionChecked(
360 base::i18n::TextDirection direction) const {
361 switch (direction) {
362 case base::i18n::TextDirection::UNKNOWN_DIRECTION:
363 return params_.writing_direction_default &
364 blink::WebContextMenuData::CheckableMenuItemChecked;
365 case base::i18n::TextDirection::RIGHT_TO_LEFT:
366 return params_.writing_direction_right_to_left &
367 blink::WebContextMenuData::CheckableMenuItemChecked;
368 case base::i18n::TextDirection::LEFT_TO_RIGHT:
369 return params_.writing_direction_left_to_right &
370 blink::WebContextMenuData::CheckableMenuItemChecked;
371 case base::i18n::TextDirection::TEXT_DIRECTION_NUM_DIRECTIONS:
372 NOTREACHED();
373 return false;
374 }
375 }
376
377 void RenderViewContextMenuMac::UpdateTextDirection(
378 base::i18n::TextDirection direction) {
379 DCHECK_NE(direction, base::i18n::TextDirection::UNKNOWN_DIRECTION);
380
381 content::RenderViewHost* view_host = GetRenderViewHost();
382 blink::WebTextDirection dir = blink::WebTextDirectionLeftToRight;
383 int command_id = IDC_WRITING_DIRECTION_LTR;
384 if (direction == base::i18n::TextDirection::RIGHT_TO_LEFT) {
385 dir = blink::WebTextDirectionRightToLeft;
386 command_id = IDC_WRITING_DIRECTION_RTL;
387 }
388
389 view_host->GetWidget()->UpdateTextDirection(dir);
390 view_host->GetWidget()->NotifyTextDirection();
391
392 RenderViewContextMenu::RecordUsedItem(command_id);
393 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698