1/* 2 * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. 3 * Copyright (C) 2010 Igalia S.L 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include "config.h" 28#include "ContextMenuController.h" 29 30#if ENABLE(CONTEXT_MENUS) 31 32#include "BackForwardController.h" 33#include "Chrome.h" 34#include "ContextMenu.h" 35#include "ContextMenuClient.h" 36#include "ContextMenuItem.h" 37#include "ContextMenuProvider.h" 38#include "Document.h" 39#include "DocumentFragment.h" 40#include "DocumentLoader.h" 41#include "Editor.h" 42#include "EditorClient.h" 43#include "Event.h" 44#include "EventHandler.h" 45#include "EventNames.h" 46#include "ExceptionCodePlaceholder.h" 47#include "FormState.h" 48#include "FrameLoadRequest.h" 49#include "FrameLoader.h" 50#include "FrameLoaderClient.h" 51#include "FrameSelection.h" 52#include "HTMLFormElement.h" 53#include "HitTestRequest.h" 54#include "HitTestResult.h" 55#include "InspectorController.h" 56#include "LocalizedStrings.h" 57#include "MainFrame.h" 58#include "MouseEvent.h" 59#include "NavigationAction.h" 60#include "Node.h" 61#include "Page.h" 62#include "PlatformEvent.h" 63#include "RenderImage.h" 64#include "ReplaceSelectionCommand.h" 65#include "ResourceRequest.h" 66#include "Settings.h" 67#include "TextIterator.h" 68#include "TypingCommand.h" 69#include "UserTypingGestureIndicator.h" 70#include "WindowFeatures.h" 71#include "markup.h" 72#include <wtf/unicode/CharacterNames.h> 73 74using namespace WTF; 75using namespace Unicode; 76 77namespace WebCore { 78 79ContextMenuController::ContextMenuController(Page& page, ContextMenuClient& client) 80 : m_page(page) 81 , m_client(client) 82{ 83} 84 85ContextMenuController::~ContextMenuController() 86{ 87 m_client.contextMenuDestroyed(); 88} 89 90void ContextMenuController::clearContextMenu() 91{ 92 m_contextMenu.clear(); 93 if (m_menuProvider) 94 m_menuProvider->contextMenuCleared(); 95 m_menuProvider = 0; 96} 97 98void ContextMenuController::handleContextMenuEvent(Event* event) 99{ 100 m_contextMenu = maybeCreateContextMenu(event); 101 if (!m_contextMenu) 102 return; 103 104 populate(); 105 106 showContextMenu(event); 107} 108 109static PassOwnPtr<ContextMenuItem> separatorItem() 110{ 111 return adoptPtr(new ContextMenuItem(SeparatorType, ContextMenuItemTagNoAction, String())); 112} 113 114void ContextMenuController::showContextMenu(Event* event, PassRefPtr<ContextMenuProvider> menuProvider) 115{ 116 m_menuProvider = menuProvider; 117 118 m_contextMenu = maybeCreateContextMenu(event); 119 if (!m_contextMenu) { 120 clearContextMenu(); 121 return; 122 } 123 124 m_menuProvider->populateContextMenu(m_contextMenu.get()); 125 if (m_context.hitTestResult().isSelected()) { 126 appendItem(*separatorItem(), m_contextMenu.get()); 127 populate(); 128 } 129 showContextMenu(event); 130} 131 132#if ENABLE(SERVICE_CONTROLS) 133static Image* imageFromImageElementNode(Node& node) 134{ 135 RenderObject* renderer = node.renderer(); 136 if (!renderer) 137 return nullptr; 138 if (!renderer->isRenderImage()) 139 return nullptr; 140 CachedImage* image = toRenderImage(*renderer).cachedImage(); 141 if (!image || image->errorOccurred()) 142 return nullptr; 143 144 return image->imageForRenderer(renderer); 145} 146#endif 147 148PassOwnPtr<ContextMenu> ContextMenuController::maybeCreateContextMenu(Event* event) 149{ 150 ASSERT(event); 151 152 if (!event->isMouseEvent()) 153 return nullptr; 154 155 MouseEvent& mouseEvent = toMouseEvent(*event); 156 HitTestResult result(mouseEvent.absoluteLocation()); 157 158 Node* node = event->target()->toNode(); 159 if (Frame* frame = node->document().frame()) 160 result = frame->eventHandler().hitTestResultAtPoint(mouseEvent.absoluteLocation()); 161 162 if (!result.innerNonSharedNode()) 163 return nullptr; 164 165 m_context = ContextMenuContext(result); 166 167#if ENABLE(SERVICE_CONTROLS) 168 if (node->isImageControlsButtonElement()) { 169 if (Image* image = imageFromImageElementNode(*result.innerNonSharedNode())) 170 m_context.setControlledImage(image); 171 172 // FIXME: If we couldn't get the image then we shouldn't try to show the image controls menu for it. 173 return nullptr; 174 } 175#endif 176 177 return adoptPtr(new ContextMenu); 178} 179 180void ContextMenuController::showContextMenu(Event* event) 181{ 182#if ENABLE(INSPECTOR) 183 if (m_page.inspectorController().enabled()) 184 addInspectElementItem(); 185#endif 186 187#if USE(CROSS_PLATFORM_CONTEXT_MENUS) 188 m_contextMenu = m_client.customizeMenu(m_contextMenu.release()); 189#else 190 PlatformMenuDescription customMenu = m_client.getCustomMenuFromDefaultItems(m_contextMenu.get()); 191 m_contextMenu->setPlatformDescription(customMenu); 192#endif 193 event->setDefaultHandled(); 194} 195 196static void openNewWindow(const URL& urlToLoad, Frame* frame) 197{ 198 Page* oldPage = frame->page(); 199 if (!oldPage) 200 return; 201 202 FrameLoadRequest request(frame->document()->securityOrigin(), ResourceRequest(urlToLoad, frame->loader().outgoingReferrer())); 203 Page* newPage = oldPage; 204 205 newPage = oldPage->chrome().createWindow(frame, request, WindowFeatures(), NavigationAction(request.resourceRequest())); 206 if (!newPage) 207 return; 208 newPage->chrome().show(); 209 newPage->mainFrame().loader().loadFrameRequest(request, LockHistory::No, LockBackForwardList::No, 0, 0, MaybeSendReferrer, AllowNavigationToInvalidURL::Yes); 210} 211 212#if PLATFORM(GTK) 213static void insertUnicodeCharacter(UChar character, Frame* frame) 214{ 215 String text(&character, 1); 216 if (!frame->editor().shouldInsertText(text, frame->selection().toNormalizedRange().get(), EditorInsertActionTyped)) 217 return; 218 219 ASSERT(frame->document()); 220 TypingCommand::insertText(*frame->document(), text, 0, TypingCommand::TextCompositionNone); 221} 222#endif 223 224void ContextMenuController::contextMenuItemSelected(ContextMenuItem* item) 225{ 226 ASSERT(item->type() == ActionType || item->type() == CheckableActionType); 227 228 if (item->action() >= ContextMenuItemBaseApplicationTag) { 229 m_client.contextMenuItemSelected(item, m_contextMenu.get()); 230 return; 231 } 232 233 if (item->action() >= ContextMenuItemBaseCustomTag) { 234 ASSERT(m_menuProvider); 235 m_menuProvider->contextMenuItemSelected(item); 236 return; 237 } 238 239 Frame* frame = m_context.hitTestResult().innerNonSharedNode()->document().frame(); 240 if (!frame) 241 return; 242 243 switch (item->action()) { 244 case ContextMenuItemTagOpenLinkInNewWindow: 245 openNewWindow(m_context.hitTestResult().absoluteLinkURL(), frame); 246 break; 247 case ContextMenuItemTagDownloadLinkToDisk: 248 // FIXME: Some day we should be able to do this from within WebCore. (Bug 117709) 249 m_client.downloadURL(m_context.hitTestResult().absoluteLinkURL()); 250 break; 251 case ContextMenuItemTagCopyLinkToClipboard: 252 frame->editor().copyURL(m_context.hitTestResult().absoluteLinkURL(), m_context.hitTestResult().textContent()); 253 break; 254 case ContextMenuItemTagOpenImageInNewWindow: 255 openNewWindow(m_context.hitTestResult().absoluteImageURL(), frame); 256 break; 257 case ContextMenuItemTagDownloadImageToDisk: 258 // FIXME: Some day we should be able to do this from within WebCore. (Bug 117709) 259 m_client.downloadURL(m_context.hitTestResult().absoluteImageURL()); 260 break; 261 case ContextMenuItemTagCopyImageToClipboard: 262 // FIXME: The Pasteboard class is not written yet 263 // For now, call into the client. This is temporary! 264 frame->editor().copyImage(m_context.hitTestResult()); 265 break; 266#if PLATFORM(GTK) || PLATFORM(EFL) 267 case ContextMenuItemTagCopyImageUrlToClipboard: 268 frame->editor().copyURL(m_context.hitTestResult().absoluteImageURL(), m_context.hitTestResult().textContent()); 269 break; 270#endif 271 case ContextMenuItemTagOpenMediaInNewWindow: 272 openNewWindow(m_context.hitTestResult().absoluteMediaURL(), frame); 273 break; 274 case ContextMenuItemTagDownloadMediaToDisk: 275 // FIXME: Some day we should be able to do this from within WebCore. (Bug 117709) 276 m_client.downloadURL(m_context.hitTestResult().absoluteMediaURL()); 277 break; 278 case ContextMenuItemTagCopyMediaLinkToClipboard: 279 frame->editor().copyURL(m_context.hitTestResult().absoluteMediaURL(), m_context.hitTestResult().textContent()); 280 break; 281 case ContextMenuItemTagToggleMediaControls: 282 m_context.hitTestResult().toggleMediaControlsDisplay(); 283 break; 284 case ContextMenuItemTagToggleMediaLoop: 285 m_context.hitTestResult().toggleMediaLoopPlayback(); 286 break; 287 case ContextMenuItemTagToggleVideoFullscreen: 288 m_context.hitTestResult().toggleMediaFullscreenState(); 289 break; 290 case ContextMenuItemTagEnterVideoFullscreen: 291 m_context.hitTestResult().enterFullscreenForVideo(); 292 break; 293 case ContextMenuItemTagMediaPlayPause: 294 m_context.hitTestResult().toggleMediaPlayState(); 295 break; 296 case ContextMenuItemTagMediaMute: 297 m_context.hitTestResult().toggleMediaMuteState(); 298 break; 299 case ContextMenuItemTagOpenFrameInNewWindow: { 300 DocumentLoader* loader = frame->loader().documentLoader(); 301 if (!loader->unreachableURL().isEmpty()) 302 openNewWindow(loader->unreachableURL(), frame); 303 else 304 openNewWindow(loader->url(), frame); 305 break; 306 } 307 case ContextMenuItemTagCopy: 308 frame->editor().copy(); 309 break; 310 case ContextMenuItemTagGoBack: 311 if (Page* page = frame->page()) 312 page->backForward().goBackOrForward(-1); 313 break; 314 case ContextMenuItemTagGoForward: 315 if (Page* page = frame->page()) 316 page->backForward().goBackOrForward(1); 317 break; 318 case ContextMenuItemTagStop: 319 frame->loader().stop(); 320 break; 321 case ContextMenuItemTagReload: 322 frame->loader().reload(); 323 break; 324 case ContextMenuItemTagCut: 325 frame->editor().command("Cut").execute(); 326 break; 327 case ContextMenuItemTagPaste: 328 frame->editor().command("Paste").execute(); 329 break; 330#if PLATFORM(GTK) 331 case ContextMenuItemTagDelete: 332 frame->editor().performDelete(); 333 break; 334 case ContextMenuItemTagUnicodeInsertLRMMark: 335 insertUnicodeCharacter(leftToRightMark, frame); 336 break; 337 case ContextMenuItemTagUnicodeInsertRLMMark: 338 insertUnicodeCharacter(rightToLeftMark, frame); 339 break; 340 case ContextMenuItemTagUnicodeInsertLREMark: 341 insertUnicodeCharacter(leftToRightEmbed, frame); 342 break; 343 case ContextMenuItemTagUnicodeInsertRLEMark: 344 insertUnicodeCharacter(rightToLeftEmbed, frame); 345 break; 346 case ContextMenuItemTagUnicodeInsertLROMark: 347 insertUnicodeCharacter(leftToRightOverride, frame); 348 break; 349 case ContextMenuItemTagUnicodeInsertRLOMark: 350 insertUnicodeCharacter(rightToLeftOverride, frame); 351 break; 352 case ContextMenuItemTagUnicodeInsertPDFMark: 353 insertUnicodeCharacter(popDirectionalFormatting, frame); 354 break; 355 case ContextMenuItemTagUnicodeInsertZWSMark: 356 insertUnicodeCharacter(zeroWidthSpace, frame); 357 break; 358 case ContextMenuItemTagUnicodeInsertZWJMark: 359 insertUnicodeCharacter(zeroWidthJoiner, frame); 360 break; 361 case ContextMenuItemTagUnicodeInsertZWNJMark: 362 insertUnicodeCharacter(zeroWidthNonJoiner, frame); 363 break; 364#endif 365#if PLATFORM(GTK) || PLATFORM(EFL) 366 case ContextMenuItemTagSelectAll: 367 frame->editor().command("SelectAll").execute(); 368 break; 369#endif 370 case ContextMenuItemTagSpellingGuess: { 371 VisibleSelection selection = frame->selection().selection(); 372 if (frame->editor().shouldInsertText(item->title(), selection.toNormalizedRange().get(), EditorInsertActionPasted)) { 373 ReplaceSelectionCommand::CommandOptions replaceOptions = ReplaceSelectionCommand::MatchStyle | ReplaceSelectionCommand::PreventNesting; 374 375 if (frame->editor().behavior().shouldAllowSpellingSuggestionsWithoutSelection()) { 376 ASSERT(selection.isCaretOrRange()); 377 VisibleSelection wordSelection(selection.base()); 378 wordSelection.expandUsingGranularity(WordGranularity); 379 frame->selection().setSelection(wordSelection); 380 } else { 381 ASSERT(frame->editor().selectedText().length()); 382 replaceOptions |= ReplaceSelectionCommand::SelectReplacement; 383 } 384 385 Document* document = frame->document(); 386 ASSERT(document); 387 RefPtr<ReplaceSelectionCommand> command = ReplaceSelectionCommand::create(*document, createFragmentFromMarkup(*document, item->title(), ""), replaceOptions); 388 applyCommand(command); 389 frame->selection().revealSelection(ScrollAlignment::alignToEdgeIfNeeded); 390 } 391 break; 392 } 393 case ContextMenuItemTagIgnoreSpelling: 394 frame->editor().ignoreSpelling(); 395 break; 396 case ContextMenuItemTagLearnSpelling: 397 frame->editor().learnSpelling(); 398 break; 399 case ContextMenuItemTagSearchWeb: 400 m_client.searchWithGoogle(frame); 401 break; 402 case ContextMenuItemTagLookUpInDictionary: 403 // FIXME: Some day we may be able to do this from within WebCore. 404 m_client.lookUpInDictionary(frame); 405 break; 406 case ContextMenuItemTagOpenLink: 407 if (Frame* targetFrame = m_context.hitTestResult().targetFrame()) 408 targetFrame->loader().loadFrameRequest(FrameLoadRequest(frame->document()->securityOrigin(), ResourceRequest(m_context.hitTestResult().absoluteLinkURL(), frame->loader().outgoingReferrer())), LockHistory::No, LockBackForwardList::No, 0, 0, MaybeSendReferrer, AllowNavigationToInvalidURL::Yes); 409 else 410 openNewWindow(m_context.hitTestResult().absoluteLinkURL(), frame); 411 break; 412 case ContextMenuItemTagOpenLinkInThisWindow: 413 frame->loader().loadFrameRequest(FrameLoadRequest(frame->document()->securityOrigin(), ResourceRequest(m_context.hitTestResult().absoluteLinkURL(), frame->loader().outgoingReferrer())), LockHistory::No, LockBackForwardList::No, 0, 0, MaybeSendReferrer, AllowNavigationToInvalidURL::Yes); 414 break; 415 case ContextMenuItemTagBold: 416 frame->editor().command("ToggleBold").execute(); 417 break; 418 case ContextMenuItemTagItalic: 419 frame->editor().command("ToggleItalic").execute(); 420 break; 421 case ContextMenuItemTagUnderline: 422 frame->editor().toggleUnderline(); 423 break; 424 case ContextMenuItemTagOutline: 425 // We actually never enable this because CSS does not have a way to specify an outline font, 426 // which may make this difficult to implement. Maybe a special case of text-shadow? 427 break; 428 case ContextMenuItemTagStartSpeaking: { 429 RefPtr<Range> selectedRange = frame->selection().toNormalizedRange(); 430 if (!selectedRange || selectedRange->collapsed(IGNORE_EXCEPTION)) { 431 Document& document = m_context.hitTestResult().innerNonSharedNode()->document(); 432 selectedRange = document.createRange(); 433 selectedRange->selectNode(document.documentElement(), IGNORE_EXCEPTION); 434 } 435 m_client.speak(plainText(selectedRange.get())); 436 break; 437 } 438 case ContextMenuItemTagStopSpeaking: 439 m_client.stopSpeaking(); 440 break; 441 case ContextMenuItemTagDefaultDirection: 442 frame->editor().setBaseWritingDirection(NaturalWritingDirection); 443 break; 444 case ContextMenuItemTagLeftToRight: 445 frame->editor().setBaseWritingDirection(LeftToRightWritingDirection); 446 break; 447 case ContextMenuItemTagRightToLeft: 448 frame->editor().setBaseWritingDirection(RightToLeftWritingDirection); 449 break; 450 case ContextMenuItemTagTextDirectionDefault: 451 frame->editor().command("MakeTextWritingDirectionNatural").execute(); 452 break; 453 case ContextMenuItemTagTextDirectionLeftToRight: 454 frame->editor().command("MakeTextWritingDirectionLeftToRight").execute(); 455 break; 456 case ContextMenuItemTagTextDirectionRightToLeft: 457 frame->editor().command("MakeTextWritingDirectionRightToLeft").execute(); 458 break; 459#if PLATFORM(COCOA) 460 case ContextMenuItemTagSearchInSpotlight: 461 m_client.searchWithSpotlight(); 462 break; 463#endif 464 case ContextMenuItemTagShowSpellingPanel: 465 frame->editor().showSpellingGuessPanel(); 466 break; 467 case ContextMenuItemTagCheckSpelling: 468 frame->editor().advanceToNextMisspelling(); 469 break; 470 case ContextMenuItemTagCheckSpellingWhileTyping: 471 frame->editor().toggleContinuousSpellChecking(); 472 break; 473 case ContextMenuItemTagCheckGrammarWithSpelling: 474 frame->editor().toggleGrammarChecking(); 475 break; 476#if PLATFORM(COCOA) 477 case ContextMenuItemTagShowFonts: 478 frame->editor().showFontPanel(); 479 break; 480 case ContextMenuItemTagStyles: 481 frame->editor().showStylesPanel(); 482 break; 483 case ContextMenuItemTagShowColors: 484 frame->editor().showColorPanel(); 485 break; 486#endif 487#if USE(APPKIT) 488 case ContextMenuItemTagMakeUpperCase: 489 frame->editor().uppercaseWord(); 490 break; 491 case ContextMenuItemTagMakeLowerCase: 492 frame->editor().lowercaseWord(); 493 break; 494 case ContextMenuItemTagCapitalize: 495 frame->editor().capitalizeWord(); 496 break; 497#endif 498#if PLATFORM(COCOA) 499 case ContextMenuItemTagChangeBack: 500 frame->editor().changeBackToReplacedString(m_context.hitTestResult().replacedString()); 501 break; 502#endif 503#if USE(AUTOMATIC_TEXT_REPLACEMENT) 504 case ContextMenuItemTagShowSubstitutions: 505 frame->editor().showSubstitutionsPanel(); 506 break; 507 case ContextMenuItemTagSmartCopyPaste: 508 frame->editor().toggleSmartInsertDelete(); 509 break; 510 case ContextMenuItemTagSmartQuotes: 511 frame->editor().toggleAutomaticQuoteSubstitution(); 512 break; 513 case ContextMenuItemTagSmartDashes: 514 frame->editor().toggleAutomaticDashSubstitution(); 515 break; 516 case ContextMenuItemTagSmartLinks: 517 frame->editor().toggleAutomaticLinkDetection(); 518 break; 519 case ContextMenuItemTagTextReplacement: 520 frame->editor().toggleAutomaticTextReplacement(); 521 break; 522 case ContextMenuItemTagCorrectSpellingAutomatically: 523 frame->editor().toggleAutomaticSpellingCorrection(); 524 break; 525#endif 526#if ENABLE(INSPECTOR) 527 case ContextMenuItemTagInspectElement: 528 if (Page* page = frame->page()) 529 page->inspectorController().inspect(m_context.hitTestResult().innerNonSharedNode()); 530 break; 531#endif 532 case ContextMenuItemTagDictationAlternative: 533 frame->editor().applyDictationAlternativelternative(item->title()); 534 break; 535 default: 536 break; 537 } 538} 539 540void ContextMenuController::appendItem(ContextMenuItem& menuItem, ContextMenu* parentMenu) 541{ 542 checkOrEnableIfNeeded(menuItem); 543 if (parentMenu) 544 parentMenu->appendItem(menuItem); 545} 546 547void ContextMenuController::createAndAppendFontSubMenu(ContextMenuItem& fontMenuItem) 548{ 549 ContextMenu fontMenu; 550 551#if PLATFORM(COCOA) 552 ContextMenuItem showFonts(ActionType, ContextMenuItemTagShowFonts, contextMenuItemTagShowFonts()); 553#endif 554 ContextMenuItem bold(CheckableActionType, ContextMenuItemTagBold, contextMenuItemTagBold()); 555 ContextMenuItem italic(CheckableActionType, ContextMenuItemTagItalic, contextMenuItemTagItalic()); 556 ContextMenuItem underline(CheckableActionType, ContextMenuItemTagUnderline, contextMenuItemTagUnderline()); 557 ContextMenuItem outline(ActionType, ContextMenuItemTagOutline, contextMenuItemTagOutline()); 558#if PLATFORM(COCOA) 559 ContextMenuItem styles(ActionType, ContextMenuItemTagStyles, contextMenuItemTagStyles()); 560 ContextMenuItem showColors(ActionType, ContextMenuItemTagShowColors, contextMenuItemTagShowColors()); 561#endif 562 563#if PLATFORM(COCOA) 564 appendItem(showFonts, &fontMenu); 565#endif 566 appendItem(bold, &fontMenu); 567 appendItem(italic, &fontMenu); 568 appendItem(underline, &fontMenu); 569 appendItem(outline, &fontMenu); 570#if PLATFORM(COCOA) 571 appendItem(styles, &fontMenu); 572 appendItem(*separatorItem(), &fontMenu); 573 appendItem(showColors, &fontMenu); 574#endif 575 576 fontMenuItem.setSubMenu(&fontMenu); 577} 578 579 580#if !PLATFORM(GTK) 581 582void ContextMenuController::createAndAppendSpellingAndGrammarSubMenu(ContextMenuItem& spellingAndGrammarMenuItem) 583{ 584 ContextMenu spellingAndGrammarMenu; 585 586 ContextMenuItem showSpellingPanel(ActionType, ContextMenuItemTagShowSpellingPanel, 587 contextMenuItemTagShowSpellingPanel(true)); 588 ContextMenuItem checkSpelling(ActionType, ContextMenuItemTagCheckSpelling, 589 contextMenuItemTagCheckSpelling()); 590 ContextMenuItem checkAsYouType(CheckableActionType, ContextMenuItemTagCheckSpellingWhileTyping, 591 contextMenuItemTagCheckSpellingWhileTyping()); 592 ContextMenuItem grammarWithSpelling(CheckableActionType, ContextMenuItemTagCheckGrammarWithSpelling, 593 contextMenuItemTagCheckGrammarWithSpelling()); 594#if PLATFORM(COCOA) 595 ContextMenuItem correctSpelling(CheckableActionType, ContextMenuItemTagCorrectSpellingAutomatically, 596 contextMenuItemTagCorrectSpellingAutomatically()); 597#endif 598 599 appendItem(showSpellingPanel, &spellingAndGrammarMenu); 600 appendItem(checkSpelling, &spellingAndGrammarMenu); 601#if PLATFORM(COCOA) 602 appendItem(*separatorItem(), &spellingAndGrammarMenu); 603#endif 604 appendItem(checkAsYouType, &spellingAndGrammarMenu); 605 appendItem(grammarWithSpelling, &spellingAndGrammarMenu); 606#if PLATFORM(COCOA) 607 appendItem(correctSpelling, &spellingAndGrammarMenu); 608#endif 609 610 spellingAndGrammarMenuItem.setSubMenu(&spellingAndGrammarMenu); 611} 612 613#endif // !PLATFORM(GTK) 614 615 616#if PLATFORM(COCOA) 617 618void ContextMenuController::createAndAppendSpeechSubMenu(ContextMenuItem& speechMenuItem) 619{ 620 ContextMenu speechMenu; 621 622 ContextMenuItem start(ActionType, ContextMenuItemTagStartSpeaking, contextMenuItemTagStartSpeaking()); 623 ContextMenuItem stop(ActionType, ContextMenuItemTagStopSpeaking, contextMenuItemTagStopSpeaking()); 624 625 appendItem(start, &speechMenu); 626 appendItem(stop, &speechMenu); 627 628 speechMenuItem.setSubMenu(&speechMenu); 629} 630 631#endif 632 633#if PLATFORM(GTK) 634 635void ContextMenuController::createAndAppendUnicodeSubMenu(ContextMenuItem& unicodeMenuItem) 636{ 637 ContextMenu unicodeMenu; 638 639 ContextMenuItem leftToRightMarkMenuItem(ActionType, ContextMenuItemTagUnicodeInsertLRMMark, contextMenuItemTagUnicodeInsertLRMMark()); 640 ContextMenuItem rightToLeftMarkMenuItem(ActionType, ContextMenuItemTagUnicodeInsertRLMMark, contextMenuItemTagUnicodeInsertRLMMark()); 641 ContextMenuItem leftToRightEmbedMenuItem(ActionType, ContextMenuItemTagUnicodeInsertLREMark, contextMenuItemTagUnicodeInsertLREMark()); 642 ContextMenuItem rightToLeftEmbedMenuItem(ActionType, ContextMenuItemTagUnicodeInsertRLEMark, contextMenuItemTagUnicodeInsertRLEMark()); 643 ContextMenuItem leftToRightOverrideMenuItem(ActionType, ContextMenuItemTagUnicodeInsertLROMark, contextMenuItemTagUnicodeInsertLROMark()); 644 ContextMenuItem rightToLeftOverrideMenuItem(ActionType, ContextMenuItemTagUnicodeInsertRLOMark, contextMenuItemTagUnicodeInsertRLOMark()); 645 ContextMenuItem popDirectionalFormattingMenuItem(ActionType, ContextMenuItemTagUnicodeInsertPDFMark, contextMenuItemTagUnicodeInsertPDFMark()); 646 ContextMenuItem zeroWidthSpaceMenuItem(ActionType, ContextMenuItemTagUnicodeInsertZWSMark, contextMenuItemTagUnicodeInsertZWSMark()); 647 ContextMenuItem zeroWidthJoinerMenuItem(ActionType, ContextMenuItemTagUnicodeInsertZWJMark, contextMenuItemTagUnicodeInsertZWJMark()); 648 ContextMenuItem zeroWidthNonJoinerMenuItem(ActionType, ContextMenuItemTagUnicodeInsertZWNJMark, contextMenuItemTagUnicodeInsertZWNJMark()); 649 650 appendItem(leftToRightMarkMenuItem, &unicodeMenu); 651 appendItem(rightToLeftMarkMenuItem, &unicodeMenu); 652 appendItem(leftToRightEmbedMenuItem, &unicodeMenu); 653 appendItem(rightToLeftEmbedMenuItem, &unicodeMenu); 654 appendItem(leftToRightOverrideMenuItem, &unicodeMenu); 655 appendItem(rightToLeftOverrideMenuItem, &unicodeMenu); 656 appendItem(popDirectionalFormattingMenuItem, &unicodeMenu); 657 appendItem(zeroWidthSpaceMenuItem, &unicodeMenu); 658 appendItem(zeroWidthJoinerMenuItem, &unicodeMenu); 659 appendItem(zeroWidthNonJoinerMenuItem, &unicodeMenu); 660 661 unicodeMenuItem.setSubMenu(&unicodeMenu); 662} 663 664#else 665 666void ContextMenuController::createAndAppendWritingDirectionSubMenu(ContextMenuItem& writingDirectionMenuItem) 667{ 668 ContextMenu writingDirectionMenu; 669 670 ContextMenuItem defaultItem(ActionType, ContextMenuItemTagDefaultDirection, 671 contextMenuItemTagDefaultDirection()); 672 ContextMenuItem ltr(CheckableActionType, ContextMenuItemTagLeftToRight, contextMenuItemTagLeftToRight()); 673 ContextMenuItem rtl(CheckableActionType, ContextMenuItemTagRightToLeft, contextMenuItemTagRightToLeft()); 674 675 appendItem(defaultItem, &writingDirectionMenu); 676 appendItem(ltr, &writingDirectionMenu); 677 appendItem(rtl, &writingDirectionMenu); 678 679 writingDirectionMenuItem.setSubMenu(&writingDirectionMenu); 680} 681 682void ContextMenuController::createAndAppendTextDirectionSubMenu(ContextMenuItem& textDirectionMenuItem) 683{ 684 ContextMenu textDirectionMenu; 685 686 ContextMenuItem defaultItem(ActionType, ContextMenuItemTagTextDirectionDefault, contextMenuItemTagDefaultDirection()); 687 ContextMenuItem ltr(CheckableActionType, ContextMenuItemTagTextDirectionLeftToRight, contextMenuItemTagLeftToRight()); 688 ContextMenuItem rtl(CheckableActionType, ContextMenuItemTagTextDirectionRightToLeft, contextMenuItemTagRightToLeft()); 689 690 appendItem(defaultItem, &textDirectionMenu); 691 appendItem(ltr, &textDirectionMenu); 692 appendItem(rtl, &textDirectionMenu); 693 694 textDirectionMenuItem.setSubMenu(&textDirectionMenu); 695} 696 697#endif 698 699#if PLATFORM(COCOA) 700 701void ContextMenuController::createAndAppendSubstitutionsSubMenu(ContextMenuItem& substitutionsMenuItem) 702{ 703 ContextMenu substitutionsMenu; 704 705 ContextMenuItem showSubstitutions(ActionType, ContextMenuItemTagShowSubstitutions, contextMenuItemTagShowSubstitutions(true)); 706 ContextMenuItem smartCopyPaste(CheckableActionType, ContextMenuItemTagSmartCopyPaste, contextMenuItemTagSmartCopyPaste()); 707 ContextMenuItem smartQuotes(CheckableActionType, ContextMenuItemTagSmartQuotes, contextMenuItemTagSmartQuotes()); 708 ContextMenuItem smartDashes(CheckableActionType, ContextMenuItemTagSmartDashes, contextMenuItemTagSmartDashes()); 709 ContextMenuItem smartLinks(CheckableActionType, ContextMenuItemTagSmartLinks, contextMenuItemTagSmartLinks()); 710 ContextMenuItem textReplacement(CheckableActionType, ContextMenuItemTagTextReplacement, contextMenuItemTagTextReplacement()); 711 712 appendItem(showSubstitutions, &substitutionsMenu); 713 appendItem(*separatorItem(), &substitutionsMenu); 714 appendItem(smartCopyPaste, &substitutionsMenu); 715 appendItem(smartQuotes, &substitutionsMenu); 716 appendItem(smartDashes, &substitutionsMenu); 717 appendItem(smartLinks, &substitutionsMenu); 718 appendItem(textReplacement, &substitutionsMenu); 719 720 substitutionsMenuItem.setSubMenu(&substitutionsMenu); 721} 722 723void ContextMenuController::createAndAppendTransformationsSubMenu(ContextMenuItem& transformationsMenuItem) 724{ 725 ContextMenu transformationsMenu; 726 727 ContextMenuItem makeUpperCase(ActionType, ContextMenuItemTagMakeUpperCase, contextMenuItemTagMakeUpperCase()); 728 ContextMenuItem makeLowerCase(ActionType, ContextMenuItemTagMakeLowerCase, contextMenuItemTagMakeLowerCase()); 729 ContextMenuItem capitalize(ActionType, ContextMenuItemTagCapitalize, contextMenuItemTagCapitalize()); 730 731 appendItem(makeUpperCase, &transformationsMenu); 732 appendItem(makeLowerCase, &transformationsMenu); 733 appendItem(capitalize, &transformationsMenu); 734 735 transformationsMenuItem.setSubMenu(&transformationsMenu); 736} 737 738#endif 739 740static bool selectionContainsPossibleWord(Frame* frame) 741{ 742 // Current algorithm: look for a character that's not just a separator. 743 for (TextIterator it(frame->selection().toNormalizedRange().get()); !it.atEnd(); it.advance()) { 744 int length = it.text().length(); 745 for (int i = 0; i < length; ++i) { 746 if (!(U_GET_GC_MASK(it.text()[i]) & U_GC_Z_MASK)) 747 return true; 748 } 749 } 750 return false; 751} 752 753#if PLATFORM(COCOA) 754#define SUPPORTS_TOGGLE_VIDEO_FULLSCREEN 1 755#else 756#define SUPPORTS_TOGGLE_VIDEO_FULLSCREEN 0 757#endif 758 759#if PLATFORM(COCOA) 760#define SUPPORTS_TOGGLE_SHOW_HIDE_MEDIA_CONTROLS 1 761#else 762#define SUPPORTS_TOGGLE_SHOW_HIDE_MEDIA_CONTROLS 0 763#endif 764 765void ContextMenuController::populate() 766{ 767 ContextMenuItem OpenLinkItem(ActionType, ContextMenuItemTagOpenLink, contextMenuItemTagOpenLink()); 768 ContextMenuItem OpenLinkInNewWindowItem(ActionType, ContextMenuItemTagOpenLinkInNewWindow, 769 contextMenuItemTagOpenLinkInNewWindow()); 770 ContextMenuItem DownloadFileItem(ActionType, ContextMenuItemTagDownloadLinkToDisk, 771 contextMenuItemTagDownloadLinkToDisk()); 772 ContextMenuItem CopyLinkItem(ActionType, ContextMenuItemTagCopyLinkToClipboard, 773 contextMenuItemTagCopyLinkToClipboard()); 774 ContextMenuItem OpenImageInNewWindowItem(ActionType, ContextMenuItemTagOpenImageInNewWindow, 775 contextMenuItemTagOpenImageInNewWindow()); 776 ContextMenuItem DownloadImageItem(ActionType, ContextMenuItemTagDownloadImageToDisk, 777 contextMenuItemTagDownloadImageToDisk()); 778 ContextMenuItem CopyImageItem(ActionType, ContextMenuItemTagCopyImageToClipboard, 779 contextMenuItemTagCopyImageToClipboard()); 780#if PLATFORM(GTK) || PLATFORM(EFL) 781 ContextMenuItem CopyImageUrlItem(ActionType, ContextMenuItemTagCopyImageUrlToClipboard, 782 contextMenuItemTagCopyImageUrlToClipboard()); 783#endif 784 ContextMenuItem OpenMediaInNewWindowItem(ActionType, ContextMenuItemTagOpenMediaInNewWindow, String()); 785 ContextMenuItem DownloadMediaItem(ActionType, ContextMenuItemTagDownloadMediaToDisk, String()); 786 ContextMenuItem CopyMediaLinkItem(ActionType, ContextMenuItemTagCopyMediaLinkToClipboard, String()); 787 ContextMenuItem MediaPlayPause(ActionType, ContextMenuItemTagMediaPlayPause, 788 contextMenuItemTagMediaPlay()); 789 ContextMenuItem MediaMute(ActionType, ContextMenuItemTagMediaMute, 790 contextMenuItemTagMediaMute()); 791#if SUPPORTS_TOGGLE_SHOW_HIDE_MEDIA_CONTROLS 792 ContextMenuItem ToggleMediaControls(ActionType, ContextMenuItemTagToggleMediaControls, 793 contextMenuItemTagHideMediaControls()); 794#else 795 ContextMenuItem ToggleMediaControls(CheckableActionType, ContextMenuItemTagToggleMediaControls, 796 contextMenuItemTagToggleMediaControls()); 797#endif 798 ContextMenuItem ToggleMediaLoop(CheckableActionType, ContextMenuItemTagToggleMediaLoop, 799 contextMenuItemTagToggleMediaLoop()); 800 ContextMenuItem EnterVideoFullscreen(ActionType, ContextMenuItemTagEnterVideoFullscreen, 801 contextMenuItemTagEnterVideoFullscreen()); 802 ContextMenuItem ToggleVideoFullscreen(ActionType, ContextMenuItemTagToggleVideoFullscreen, 803 contextMenuItemTagEnterVideoFullscreen()); 804#if PLATFORM(COCOA) 805 ContextMenuItem SearchSpotlightItem(ActionType, ContextMenuItemTagSearchInSpotlight, 806 contextMenuItemTagSearchInSpotlight()); 807#endif 808#if !PLATFORM(GTK) 809 ContextMenuItem SearchWebItem(ActionType, ContextMenuItemTagSearchWeb, contextMenuItemTagSearchWeb()); 810#endif 811 ContextMenuItem CopyItem(ActionType, ContextMenuItemTagCopy, contextMenuItemTagCopy()); 812 ContextMenuItem BackItem(ActionType, ContextMenuItemTagGoBack, contextMenuItemTagGoBack()); 813 ContextMenuItem ForwardItem(ActionType, ContextMenuItemTagGoForward, contextMenuItemTagGoForward()); 814 ContextMenuItem StopItem(ActionType, ContextMenuItemTagStop, contextMenuItemTagStop()); 815 ContextMenuItem ReloadItem(ActionType, ContextMenuItemTagReload, contextMenuItemTagReload()); 816 ContextMenuItem OpenFrameItem(ActionType, ContextMenuItemTagOpenFrameInNewWindow, 817 contextMenuItemTagOpenFrameInNewWindow()); 818 ContextMenuItem NoGuessesItem(ActionType, ContextMenuItemTagNoGuessesFound, 819 contextMenuItemTagNoGuessesFound()); 820 ContextMenuItem IgnoreSpellingItem(ActionType, ContextMenuItemTagIgnoreSpelling, 821 contextMenuItemTagIgnoreSpelling()); 822 ContextMenuItem LearnSpellingItem(ActionType, ContextMenuItemTagLearnSpelling, 823 contextMenuItemTagLearnSpelling()); 824 ContextMenuItem IgnoreGrammarItem(ActionType, ContextMenuItemTagIgnoreGrammar, 825 contextMenuItemTagIgnoreGrammar()); 826 ContextMenuItem CutItem(ActionType, ContextMenuItemTagCut, contextMenuItemTagCut()); 827 ContextMenuItem PasteItem(ActionType, ContextMenuItemTagPaste, contextMenuItemTagPaste()); 828#if PLATFORM(GTK) 829 ContextMenuItem DeleteItem(ActionType, ContextMenuItemTagDelete, contextMenuItemTagDelete()); 830#endif 831#if PLATFORM(GTK) || PLATFORM(EFL) 832 ContextMenuItem SelectAllItem(ActionType, ContextMenuItemTagSelectAll, contextMenuItemTagSelectAll()); 833#endif 834 835 Node* node = m_context.hitTestResult().innerNonSharedNode(); 836 if (!node) 837 return; 838#if PLATFORM(GTK) 839 if (!m_context.hitTestResult().isContentEditable() && (node->isElementNode() && toElement(node)->isFormControlElement())) 840 return; 841#endif 842 Frame* frame = node->document().frame(); 843 if (!frame) 844 return; 845 846#if ENABLE(SERVICE_CONTROLS) 847 // The default image control menu gets populated solely by the platform. 848 if (m_context.controlledImage()) 849 return; 850#endif 851 852 if (!m_context.hitTestResult().isContentEditable()) { 853 FrameLoader& loader = frame->loader(); 854 URL linkURL = m_context.hitTestResult().absoluteLinkURL(); 855 if (!linkURL.isEmpty()) { 856 if (loader.client().canHandleRequest(ResourceRequest(linkURL))) { 857 appendItem(OpenLinkItem, m_contextMenu.get()); 858 appendItem(OpenLinkInNewWindowItem, m_contextMenu.get()); 859 appendItem(DownloadFileItem, m_contextMenu.get()); 860 } 861 appendItem(CopyLinkItem, m_contextMenu.get()); 862 } 863 864 URL imageURL = m_context.hitTestResult().absoluteImageURL(); 865 if (!imageURL.isEmpty()) { 866 if (!linkURL.isEmpty()) 867 appendItem(*separatorItem(), m_contextMenu.get()); 868 869 appendItem(OpenImageInNewWindowItem, m_contextMenu.get()); 870 appendItem(DownloadImageItem, m_contextMenu.get()); 871 if (imageURL.isLocalFile() || m_context.hitTestResult().image()) 872 appendItem(CopyImageItem, m_contextMenu.get()); 873#if PLATFORM(GTK) || PLATFORM(EFL) 874 appendItem(CopyImageUrlItem, m_contextMenu.get()); 875#endif 876 } 877 878 URL mediaURL = m_context.hitTestResult().absoluteMediaURL(); 879 if (!mediaURL.isEmpty()) { 880 if (!linkURL.isEmpty() || !imageURL.isEmpty()) 881 appendItem(*separatorItem(), m_contextMenu.get()); 882 883 appendItem(MediaPlayPause, m_contextMenu.get()); 884 appendItem(MediaMute, m_contextMenu.get()); 885 appendItem(ToggleMediaControls, m_contextMenu.get()); 886 appendItem(ToggleMediaLoop, m_contextMenu.get()); 887#if SUPPORTS_TOGGLE_VIDEO_FULLSCREEN 888 appendItem(ToggleVideoFullscreen, m_contextMenu.get()); 889#else 890 appendItem(EnterVideoFullscreen, m_contextMenu.get()); 891#endif 892 appendItem(*separatorItem(), m_contextMenu.get()); 893 appendItem(CopyMediaLinkItem, m_contextMenu.get()); 894 appendItem(OpenMediaInNewWindowItem, m_contextMenu.get()); 895 if (loader.client().canHandleRequest(ResourceRequest(mediaURL))) 896 appendItem(DownloadMediaItem, m_contextMenu.get()); 897 } 898 899 if (imageURL.isEmpty() && linkURL.isEmpty() && mediaURL.isEmpty()) { 900 if (m_context.hitTestResult().isSelected()) { 901 if (selectionContainsPossibleWord(frame)) { 902#if PLATFORM(COCOA) 903 String selectedString = frame->displayStringModifiedByEncoding(frame->editor().selectedText()); 904 ContextMenuItem LookUpInDictionaryItem(ActionType, ContextMenuItemTagLookUpInDictionary, contextMenuItemTagLookUpInDictionary(selectedString)); 905 906 appendItem(LookUpInDictionaryItem, m_contextMenu.get()); 907#endif 908 909#if !PLATFORM(GTK) 910 appendItem(SearchWebItem, m_contextMenu.get()); 911 appendItem(*separatorItem(), m_contextMenu.get()); 912#endif 913 } 914 915 appendItem(CopyItem, m_contextMenu.get()); 916#if PLATFORM(COCOA) 917 appendItem(*separatorItem(), m_contextMenu.get()); 918 919 ContextMenuItem SpeechMenuItem(SubmenuType, ContextMenuItemTagSpeechMenu, contextMenuItemTagSpeechMenu()); 920 createAndAppendSpeechSubMenu(SpeechMenuItem); 921 appendItem(SpeechMenuItem, m_contextMenu.get()); 922#endif 923 } else { 924#if ENABLE(INSPECTOR) 925 if (!(frame->page() && (frame->page()->inspectorController().hasInspectorFrontendClient() || frame->page()->inspectorController().hasRemoteFrontend()))) { 926#endif 927 928 // In GTK+ unavailable items are not hidden but insensitive. 929#if PLATFORM(GTK) 930 appendItem(BackItem, m_contextMenu.get()); 931 appendItem(ForwardItem, m_contextMenu.get()); 932 appendItem(StopItem, m_contextMenu.get()); 933 appendItem(ReloadItem, m_contextMenu.get()); 934#else 935 if (frame->page() && frame->page()->backForward().canGoBackOrForward(-1)) 936 appendItem(BackItem, m_contextMenu.get()); 937 938 if (frame->page() && frame->page()->backForward().canGoBackOrForward(1)) 939 appendItem(ForwardItem, m_contextMenu.get()); 940 941 // use isLoadingInAPISense rather than isLoading because Stop/Reload are 942 // intended to match WebKit's API, not WebCore's internal notion of loading status 943 if (loader.documentLoader()->isLoadingInAPISense()) 944 appendItem(StopItem, m_contextMenu.get()); 945 else 946 appendItem(ReloadItem, m_contextMenu.get()); 947#endif 948#if ENABLE(INSPECTOR) 949 } 950#endif 951 952 if (frame->page() && !frame->isMainFrame()) 953 appendItem(OpenFrameItem, m_contextMenu.get()); 954 } 955 } 956 } else { // Make an editing context menu 957 bool inPasswordField = frame->selection().selection().isInPasswordField(); 958 if (!inPasswordField) { 959 bool haveContextMenuItemsForMisspellingOrGrammer = false; 960 bool spellCheckingEnabled = frame->editor().isSpellCheckingEnabledFor(node); 961 if (spellCheckingEnabled) { 962 // Consider adding spelling-related or grammar-related context menu items (never both, since a single selected range 963 // is never considered a misspelling and bad grammar at the same time) 964 bool misspelling; 965 bool badGrammar; 966 Vector<String> guesses = frame->editor().guessesForMisspelledOrUngrammatical(misspelling, badGrammar); 967 if (misspelling || badGrammar) { 968 size_t size = guesses.size(); 969 if (!size) { 970 // If there's bad grammar but no suggestions (e.g., repeated word), just leave off the suggestions 971 // list and trailing separator rather than adding a "No Guesses Found" item (matches AppKit) 972 if (misspelling) { 973 appendItem(NoGuessesItem, m_contextMenu.get()); 974 appendItem(*separatorItem(), m_contextMenu.get()); 975 } 976 } else { 977 for (unsigned i = 0; i < size; i++) { 978 const String &guess = guesses[i]; 979 if (!guess.isEmpty()) { 980 ContextMenuItem item(ActionType, ContextMenuItemTagSpellingGuess, guess); 981 appendItem(item, m_contextMenu.get()); 982 } 983 } 984 appendItem(*separatorItem(), m_contextMenu.get()); 985 } 986 if (misspelling) { 987 appendItem(IgnoreSpellingItem, m_contextMenu.get()); 988 appendItem(LearnSpellingItem, m_contextMenu.get()); 989 } else 990 appendItem(IgnoreGrammarItem, m_contextMenu.get()); 991 appendItem(*separatorItem(), m_contextMenu.get()); 992 haveContextMenuItemsForMisspellingOrGrammer = true; 993#if PLATFORM(COCOA) 994 } else { 995 // If the string was autocorrected, generate a contextual menu item allowing it to be changed back. 996 String replacedString = m_context.hitTestResult().replacedString(); 997 if (!replacedString.isEmpty()) { 998 ContextMenuItem item(ActionType, ContextMenuItemTagChangeBack, contextMenuItemTagChangeBack(replacedString)); 999 appendItem(item, m_contextMenu.get()); 1000 appendItem(*separatorItem(), m_contextMenu.get()); 1001 haveContextMenuItemsForMisspellingOrGrammer = true; 1002 } 1003#endif 1004 } 1005 } 1006 1007 if (!haveContextMenuItemsForMisspellingOrGrammer) { 1008 // Spelling and grammar checking is mutually exclusive with dictation alternatives. 1009 Vector<String> dictationAlternatives = m_context.hitTestResult().dictationAlternatives(); 1010 if (!dictationAlternatives.isEmpty()) { 1011 for (size_t i = 0; i < dictationAlternatives.size(); ++i) { 1012 ContextMenuItem item(ActionType, ContextMenuItemTagDictationAlternative, dictationAlternatives[i]); 1013 appendItem(item, m_contextMenu.get()); 1014 } 1015 appendItem(*separatorItem(), m_contextMenu.get()); 1016 } 1017 } 1018 } 1019 1020 FrameLoader& loader = frame->loader(); 1021 URL linkURL = m_context.hitTestResult().absoluteLinkURL(); 1022 if (!linkURL.isEmpty()) { 1023 if (loader.client().canHandleRequest(ResourceRequest(linkURL))) { 1024 appendItem(OpenLinkItem, m_contextMenu.get()); 1025 appendItem(OpenLinkInNewWindowItem, m_contextMenu.get()); 1026 appendItem(DownloadFileItem, m_contextMenu.get()); 1027 } 1028 appendItem(CopyLinkItem, m_contextMenu.get()); 1029 appendItem(*separatorItem(), m_contextMenu.get()); 1030 } 1031 1032 if (m_context.hitTestResult().isSelected() && !inPasswordField && selectionContainsPossibleWord(frame)) { 1033#if PLATFORM(COCOA) 1034 String selectedString = frame->displayStringModifiedByEncoding(frame->editor().selectedText()); 1035 ContextMenuItem LookUpInDictionaryItem(ActionType, ContextMenuItemTagLookUpInDictionary, contextMenuItemTagLookUpInDictionary(selectedString)); 1036 1037 appendItem(LookUpInDictionaryItem, m_contextMenu.get()); 1038#endif 1039 1040#if !PLATFORM(GTK) 1041 appendItem(SearchWebItem, m_contextMenu.get()); 1042 appendItem(*separatorItem(), m_contextMenu.get()); 1043#endif 1044 } 1045 1046 appendItem(CutItem, m_contextMenu.get()); 1047 appendItem(CopyItem, m_contextMenu.get()); 1048 appendItem(PasteItem, m_contextMenu.get()); 1049#if PLATFORM(GTK) 1050 appendItem(DeleteItem, m_contextMenu.get()); 1051 appendItem(*separatorItem(), m_contextMenu.get()); 1052#endif 1053#if PLATFORM(GTK) || PLATFORM(EFL) 1054 appendItem(SelectAllItem, m_contextMenu.get()); 1055#endif 1056 1057 if (!inPasswordField) { 1058#if !PLATFORM(GTK) 1059 appendItem(*separatorItem(), m_contextMenu.get()); 1060 ContextMenuItem SpellingAndGrammarMenuItem(SubmenuType, ContextMenuItemTagSpellingMenu, 1061 contextMenuItemTagSpellingMenu()); 1062 createAndAppendSpellingAndGrammarSubMenu(SpellingAndGrammarMenuItem); 1063 appendItem(SpellingAndGrammarMenuItem, m_contextMenu.get()); 1064#endif 1065#if PLATFORM(COCOA) 1066 ContextMenuItem substitutionsMenuItem(SubmenuType, ContextMenuItemTagSubstitutionsMenu, 1067 contextMenuItemTagSubstitutionsMenu()); 1068 createAndAppendSubstitutionsSubMenu(substitutionsMenuItem); 1069 appendItem(substitutionsMenuItem, m_contextMenu.get()); 1070 ContextMenuItem transformationsMenuItem(SubmenuType, ContextMenuItemTagTransformationsMenu, 1071 contextMenuItemTagTransformationsMenu()); 1072 createAndAppendTransformationsSubMenu(transformationsMenuItem); 1073 appendItem(transformationsMenuItem, m_contextMenu.get()); 1074#endif 1075#if PLATFORM(GTK) 1076 bool shouldShowFontMenu = frame->editor().canEditRichly(); 1077#else 1078 bool shouldShowFontMenu = true; 1079#endif 1080 if (shouldShowFontMenu) { 1081 ContextMenuItem FontMenuItem(SubmenuType, ContextMenuItemTagFontMenu, 1082 contextMenuItemTagFontMenu()); 1083 createAndAppendFontSubMenu(FontMenuItem); 1084 appendItem(FontMenuItem, m_contextMenu.get()); 1085 } 1086#if PLATFORM(COCOA) 1087 ContextMenuItem SpeechMenuItem(SubmenuType, ContextMenuItemTagSpeechMenu, contextMenuItemTagSpeechMenu()); 1088 createAndAppendSpeechSubMenu(SpeechMenuItem); 1089 appendItem(SpeechMenuItem, m_contextMenu.get()); 1090#endif 1091#if PLATFORM(GTK) 1092 EditorClient* client = frame->editor().client(); 1093 if (client && client->shouldShowUnicodeMenu()) { 1094 ContextMenuItem UnicodeMenuItem(SubmenuType, ContextMenuItemTagUnicode, contextMenuItemTagUnicode()); 1095 createAndAppendUnicodeSubMenu(UnicodeMenuItem); 1096 appendItem(*separatorItem(), m_contextMenu.get()); 1097 appendItem(UnicodeMenuItem, m_contextMenu.get()); 1098 } 1099#else 1100 ContextMenuItem WritingDirectionMenuItem(SubmenuType, ContextMenuItemTagWritingDirectionMenu, 1101 contextMenuItemTagWritingDirectionMenu()); 1102 createAndAppendWritingDirectionSubMenu(WritingDirectionMenuItem); 1103 appendItem(WritingDirectionMenuItem, m_contextMenu.get()); 1104 if (Page* page = frame->page()) { 1105 bool includeTextDirectionSubmenu = page->settings().textDirectionSubmenuInclusionBehavior() == TextDirectionSubmenuAlwaysIncluded 1106 || (page->settings().textDirectionSubmenuInclusionBehavior() == TextDirectionSubmenuAutomaticallyIncluded && frame->editor().hasBidiSelection()); 1107 if (includeTextDirectionSubmenu) { 1108 ContextMenuItem TextDirectionMenuItem(SubmenuType, ContextMenuItemTagTextDirectionMenu, contextMenuItemTagTextDirectionMenu()); 1109 createAndAppendTextDirectionSubMenu(TextDirectionMenuItem); 1110 appendItem(TextDirectionMenuItem, m_contextMenu.get()); 1111 } 1112 } 1113#endif 1114 } 1115 } 1116} 1117 1118#if ENABLE(INSPECTOR) 1119void ContextMenuController::addInspectElementItem() 1120{ 1121 Node* node = m_context.hitTestResult().innerNonSharedNode(); 1122 if (!node) 1123 return; 1124 1125 Frame* frame = node->document().frame(); 1126 if (!frame) 1127 return; 1128 1129 Page* page = frame->page(); 1130 if (!page) 1131 return; 1132 1133 ContextMenuItem InspectElementItem(ActionType, ContextMenuItemTagInspectElement, contextMenuItemTagInspectElement()); 1134#if USE(CROSS_PLATFORM_CONTEXT_MENUS) 1135 if (m_contextMenu && !m_contextMenu->items().isEmpty()) 1136#else 1137 if (m_contextMenu && m_contextMenu->itemCount()) 1138#endif 1139 appendItem(*separatorItem(), m_contextMenu.get()); 1140 appendItem(InspectElementItem, m_contextMenu.get()); 1141} 1142#endif // ENABLE(INSPECTOR) 1143 1144void ContextMenuController::checkOrEnableIfNeeded(ContextMenuItem& item) const 1145{ 1146 if (item.type() == SeparatorType) 1147 return; 1148 1149 Frame* frame = m_context.hitTestResult().innerNonSharedNode()->document().frame(); 1150 if (!frame) 1151 return; 1152 1153 // Custom items already have proper checked and enabled values. 1154 if (ContextMenuItemBaseCustomTag <= item.action() && item.action() <= ContextMenuItemLastCustomTag) 1155 return; 1156 1157 bool shouldEnable = true; 1158 bool shouldCheck = false; 1159 1160 switch (item.action()) { 1161 case ContextMenuItemTagCheckSpelling: 1162 shouldEnable = frame->editor().canEdit(); 1163 break; 1164 case ContextMenuItemTagDefaultDirection: 1165 shouldCheck = false; 1166 shouldEnable = false; 1167 break; 1168 case ContextMenuItemTagLeftToRight: 1169 case ContextMenuItemTagRightToLeft: { 1170 String direction = item.action() == ContextMenuItemTagLeftToRight ? "ltr" : "rtl"; 1171 shouldCheck = frame->editor().selectionHasStyle(CSSPropertyDirection, direction) != FalseTriState; 1172 shouldEnable = true; 1173 break; 1174 } 1175 case ContextMenuItemTagTextDirectionDefault: { 1176 Editor::Command command = frame->editor().command("MakeTextWritingDirectionNatural"); 1177 shouldCheck = command.state() == TrueTriState; 1178 shouldEnable = command.isEnabled(); 1179 break; 1180 } 1181 case ContextMenuItemTagTextDirectionLeftToRight: { 1182 Editor::Command command = frame->editor().command("MakeTextWritingDirectionLeftToRight"); 1183 shouldCheck = command.state() == TrueTriState; 1184 shouldEnable = command.isEnabled(); 1185 break; 1186 } 1187 case ContextMenuItemTagTextDirectionRightToLeft: { 1188 Editor::Command command = frame->editor().command("MakeTextWritingDirectionRightToLeft"); 1189 shouldCheck = command.state() == TrueTriState; 1190 shouldEnable = command.isEnabled(); 1191 break; 1192 } 1193 case ContextMenuItemTagCopy: 1194 shouldEnable = frame->editor().canDHTMLCopy() || frame->editor().canCopy(); 1195 break; 1196 case ContextMenuItemTagCut: 1197 shouldEnable = frame->editor().canDHTMLCut() || frame->editor().canCut(); 1198 break; 1199 case ContextMenuItemTagIgnoreSpelling: 1200 case ContextMenuItemTagLearnSpelling: 1201 shouldEnable = frame->selection().isRange(); 1202 break; 1203 case ContextMenuItemTagPaste: 1204 shouldEnable = frame->editor().canDHTMLPaste() || frame->editor().canPaste(); 1205 break; 1206#if PLATFORM(GTK) 1207 case ContextMenuItemTagDelete: 1208 shouldEnable = frame->editor().canDelete(); 1209 break; 1210 case ContextMenuItemTagInputMethods: 1211 case ContextMenuItemTagUnicode: 1212 case ContextMenuItemTagUnicodeInsertLRMMark: 1213 case ContextMenuItemTagUnicodeInsertRLMMark: 1214 case ContextMenuItemTagUnicodeInsertLREMark: 1215 case ContextMenuItemTagUnicodeInsertRLEMark: 1216 case ContextMenuItemTagUnicodeInsertLROMark: 1217 case ContextMenuItemTagUnicodeInsertRLOMark: 1218 case ContextMenuItemTagUnicodeInsertPDFMark: 1219 case ContextMenuItemTagUnicodeInsertZWSMark: 1220 case ContextMenuItemTagUnicodeInsertZWJMark: 1221 case ContextMenuItemTagUnicodeInsertZWNJMark: 1222 shouldEnable = true; 1223 break; 1224#endif 1225#if PLATFORM(GTK) || PLATFORM(EFL) 1226 case ContextMenuItemTagSelectAll: 1227 shouldEnable = true; 1228 break; 1229#endif 1230 case ContextMenuItemTagUnderline: { 1231 shouldCheck = frame->editor().selectionHasStyle(CSSPropertyWebkitTextDecorationsInEffect, "underline") != FalseTriState; 1232 shouldEnable = frame->editor().canEditRichly(); 1233 break; 1234 } 1235 case ContextMenuItemTagLookUpInDictionary: 1236 shouldEnable = frame->selection().isRange(); 1237 break; 1238 case ContextMenuItemTagCheckGrammarWithSpelling: 1239 if (frame->editor().isGrammarCheckingEnabled()) 1240 shouldCheck = true; 1241 shouldEnable = true; 1242 break; 1243 case ContextMenuItemTagItalic: { 1244 shouldCheck = frame->editor().selectionHasStyle(CSSPropertyFontStyle, "italic") != FalseTriState; 1245 shouldEnable = frame->editor().canEditRichly(); 1246 break; 1247 } 1248 case ContextMenuItemTagBold: { 1249 shouldCheck = frame->editor().selectionHasStyle(CSSPropertyFontWeight, "bold") != FalseTriState; 1250 shouldEnable = frame->editor().canEditRichly(); 1251 break; 1252 } 1253 case ContextMenuItemTagOutline: 1254 shouldEnable = false; 1255 break; 1256 case ContextMenuItemTagShowSpellingPanel: 1257 if (frame->editor().spellingPanelIsShowing()) 1258 item.setTitle(contextMenuItemTagShowSpellingPanel(false)); 1259 else 1260 item.setTitle(contextMenuItemTagShowSpellingPanel(true)); 1261 shouldEnable = frame->editor().canEdit(); 1262 break; 1263 case ContextMenuItemTagNoGuessesFound: 1264 shouldEnable = false; 1265 break; 1266 case ContextMenuItemTagCheckSpellingWhileTyping: 1267 shouldCheck = frame->editor().isContinuousSpellCheckingEnabled(); 1268 break; 1269#if PLATFORM(COCOA) 1270 case ContextMenuItemTagSubstitutionsMenu: 1271 case ContextMenuItemTagTransformationsMenu: 1272 break; 1273 case ContextMenuItemTagShowSubstitutions: 1274 if (frame->editor().substitutionsPanelIsShowing()) 1275 item.setTitle(contextMenuItemTagShowSubstitutions(false)); 1276 else 1277 item.setTitle(contextMenuItemTagShowSubstitutions(true)); 1278 shouldEnable = frame->editor().canEdit(); 1279 break; 1280 case ContextMenuItemTagMakeUpperCase: 1281 case ContextMenuItemTagMakeLowerCase: 1282 case ContextMenuItemTagCapitalize: 1283 case ContextMenuItemTagChangeBack: 1284 shouldEnable = frame->editor().canEdit(); 1285 break; 1286 case ContextMenuItemTagCorrectSpellingAutomatically: 1287 shouldCheck = frame->editor().isAutomaticSpellingCorrectionEnabled(); 1288 break; 1289 case ContextMenuItemTagSmartCopyPaste: 1290 shouldCheck = frame->editor().smartInsertDeleteEnabled(); 1291 break; 1292 case ContextMenuItemTagSmartQuotes: 1293 shouldCheck = frame->editor().isAutomaticQuoteSubstitutionEnabled(); 1294 break; 1295 case ContextMenuItemTagSmartDashes: 1296 shouldCheck = frame->editor().isAutomaticDashSubstitutionEnabled(); 1297 break; 1298 case ContextMenuItemTagSmartLinks: 1299 shouldCheck = frame->editor().isAutomaticLinkDetectionEnabled(); 1300 break; 1301 case ContextMenuItemTagTextReplacement: 1302 shouldCheck = frame->editor().isAutomaticTextReplacementEnabled(); 1303 break; 1304 case ContextMenuItemTagStopSpeaking: 1305 shouldEnable = m_client.isSpeaking(); 1306 break; 1307#else // PLATFORM(COCOA) ends here 1308 case ContextMenuItemTagStopSpeaking: 1309 break; 1310#endif 1311#if PLATFORM(GTK) 1312 case ContextMenuItemTagGoBack: 1313 shouldEnable = frame->page() && frame->page()->backForward().canGoBackOrForward(-1); 1314 break; 1315 case ContextMenuItemTagGoForward: 1316 shouldEnable = frame->page() && frame->page()->backForward().canGoBackOrForward(1); 1317 break; 1318 case ContextMenuItemTagStop: 1319 shouldEnable = frame->loader().documentLoader()->isLoadingInAPISense(); 1320 break; 1321 case ContextMenuItemTagReload: 1322 shouldEnable = !frame->loader().documentLoader()->isLoadingInAPISense(); 1323 break; 1324 case ContextMenuItemTagFontMenu: 1325 shouldEnable = frame->editor().canEditRichly(); 1326 break; 1327#else 1328 case ContextMenuItemTagGoBack: 1329 case ContextMenuItemTagGoForward: 1330 case ContextMenuItemTagStop: 1331 case ContextMenuItemTagReload: 1332 case ContextMenuItemTagFontMenu: 1333#endif 1334 case ContextMenuItemTagNoAction: 1335 case ContextMenuItemTagOpenLinkInNewWindow: 1336 case ContextMenuItemTagOpenLinkInThisWindow: 1337 case ContextMenuItemTagDownloadLinkToDisk: 1338 case ContextMenuItemTagCopyLinkToClipboard: 1339 case ContextMenuItemTagOpenImageInNewWindow: 1340 case ContextMenuItemTagDownloadImageToDisk: 1341 case ContextMenuItemTagCopyImageToClipboard: 1342#if PLATFORM(GTK) || PLATFORM(EFL) 1343 case ContextMenuItemTagCopyImageUrlToClipboard: 1344#endif 1345 break; 1346 case ContextMenuItemTagOpenMediaInNewWindow: 1347 if (m_context.hitTestResult().mediaIsVideo()) 1348 item.setTitle(contextMenuItemTagOpenVideoInNewWindow()); 1349 else 1350 item.setTitle(contextMenuItemTagOpenAudioInNewWindow()); 1351 break; 1352 case ContextMenuItemTagDownloadMediaToDisk: 1353 if (m_context.hitTestResult().mediaIsVideo()) 1354 item.setTitle(contextMenuItemTagDownloadVideoToDisk()); 1355 else 1356 item.setTitle(contextMenuItemTagDownloadAudioToDisk()); 1357 break; 1358 case ContextMenuItemTagCopyMediaLinkToClipboard: 1359 if (m_context.hitTestResult().mediaIsVideo()) 1360 item.setTitle(contextMenuItemTagCopyVideoLinkToClipboard()); 1361 else 1362 item.setTitle(contextMenuItemTagCopyAudioLinkToClipboard()); 1363 break; 1364 case ContextMenuItemTagToggleMediaControls: 1365#if SUPPORTS_TOGGLE_SHOW_HIDE_MEDIA_CONTROLS 1366 item.setTitle(m_context.hitTestResult().mediaControlsEnabled() ? contextMenuItemTagHideMediaControls() : contextMenuItemTagShowMediaControls()); 1367#else 1368 shouldCheck = m_context.hitTestResult().mediaControlsEnabled(); 1369#endif 1370 break; 1371 case ContextMenuItemTagToggleMediaLoop: 1372 shouldCheck = m_context.hitTestResult().mediaLoopEnabled(); 1373 break; 1374 case ContextMenuItemTagToggleVideoFullscreen: 1375#if SUPPORTS_TOGGLE_VIDEO_FULLSCREEN 1376 item.setTitle(m_context.hitTestResult().mediaIsInFullscreen() ? contextMenuItemTagExitVideoFullscreen() : contextMenuItemTagEnterVideoFullscreen()); 1377 break; 1378#endif 1379 case ContextMenuItemTagEnterVideoFullscreen: 1380 shouldEnable = m_context.hitTestResult().mediaSupportsFullscreen(); 1381 break; 1382 case ContextMenuItemTagOpenFrameInNewWindow: 1383 case ContextMenuItemTagSpellingGuess: 1384 case ContextMenuItemTagOther: 1385 case ContextMenuItemTagSearchInSpotlight: 1386 case ContextMenuItemTagSearchWeb: 1387 case ContextMenuItemTagOpenWithDefaultApplication: 1388 case ContextMenuItemPDFActualSize: 1389 case ContextMenuItemPDFZoomIn: 1390 case ContextMenuItemPDFZoomOut: 1391 case ContextMenuItemPDFAutoSize: 1392 case ContextMenuItemPDFSinglePage: 1393 case ContextMenuItemPDFFacingPages: 1394 case ContextMenuItemPDFContinuous: 1395 case ContextMenuItemPDFNextPage: 1396 case ContextMenuItemPDFPreviousPage: 1397 case ContextMenuItemTagOpenLink: 1398 case ContextMenuItemTagIgnoreGrammar: 1399 case ContextMenuItemTagSpellingMenu: 1400 case ContextMenuItemTagShowFonts: 1401 case ContextMenuItemTagStyles: 1402 case ContextMenuItemTagShowColors: 1403 case ContextMenuItemTagSpeechMenu: 1404 case ContextMenuItemTagStartSpeaking: 1405 case ContextMenuItemTagWritingDirectionMenu: 1406 case ContextMenuItemTagTextDirectionMenu: 1407 case ContextMenuItemTagPDFSinglePageScrolling: 1408 case ContextMenuItemTagPDFFacingPagesScrolling: 1409#if ENABLE(INSPECTOR) 1410 case ContextMenuItemTagInspectElement: 1411#endif 1412 case ContextMenuItemBaseCustomTag: 1413 case ContextMenuItemCustomTagNoAction: 1414 case ContextMenuItemLastCustomTag: 1415 case ContextMenuItemBaseApplicationTag: 1416 case ContextMenuItemTagDictationAlternative: 1417 break; 1418 case ContextMenuItemTagMediaPlayPause: 1419 if (m_context.hitTestResult().mediaPlaying()) 1420 item.setTitle(contextMenuItemTagMediaPause()); 1421 else 1422 item.setTitle(contextMenuItemTagMediaPlay()); 1423 break; 1424 case ContextMenuItemTagMediaMute: 1425 shouldEnable = m_context.hitTestResult().mediaHasAudio(); 1426 shouldCheck = shouldEnable && m_context.hitTestResult().mediaMuted(); 1427 break; 1428 } 1429 1430 item.setChecked(shouldCheck); 1431 item.setEnabled(shouldEnable); 1432} 1433 1434#if USE(ACCESSIBILITY_CONTEXT_MENUS) 1435void ContextMenuController::showContextMenuAt(Frame* frame, const IntPoint& clickPoint) 1436{ 1437 clearContextMenu(); 1438 1439 // Simulate a click in the middle of the accessibility object. 1440 PlatformMouseEvent mouseEvent(clickPoint, clickPoint, RightButton, PlatformEvent::MousePressed, 1, false, false, false, false, currentTime()); 1441 frame->eventHandler().handleMousePressEvent(mouseEvent); 1442 bool handled = frame->eventHandler().sendContextMenuEvent(mouseEvent); 1443 if (handled) 1444 m_client.showContextMenu(); 1445} 1446#endif 1447 1448#if ENABLE(SERVICE_CONTROLS) 1449void ContextMenuController::showImageControlsMenu(Event* event) 1450{ 1451 clearContextMenu(); 1452 handleContextMenuEvent(event); 1453 m_client.showContextMenu(); 1454} 1455#endif 1456 1457} // namespace WebCore 1458 1459#endif // ENABLE(CONTEXT_MENUS) 1460