Chromium Code Reviews| 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 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 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 Loading... | |
| 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 } | |
| OLD | NEW |