ContextMenuController.cpp 60.5 KB
Newer Older
aroben's avatar
aroben committed
1
/*
darin's avatar
darin committed
2
 * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
3
 * Copyright (C) 2010 Igalia S.L
aroben's avatar
aroben committed
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 */

#include "config.h"
#include "ContextMenuController.h"

bolsinga@apple.com's avatar
bolsinga@apple.com committed
30 31
#if ENABLE(CONTEXT_MENUS)

darin@apple.com's avatar
darin@apple.com committed
32
#include "BackForwardController.h"
aroben's avatar
aroben committed
33
#include "Chrome.h"
aroben's avatar
aroben committed
34 35
#include "ContextMenu.h"
#include "ContextMenuClient.h"
36
#include "ContextMenuItem.h"
37
#include "ContextMenuProvider.h"
bdakin's avatar
bdakin committed
38 39 40 41 42
#include "Document.h"
#include "DocumentFragment.h"
#include "DocumentLoader.h"
#include "Editor.h"
#include "EditorClient.h"
aroben's avatar
aroben committed
43
#include "Event.h"
bdakin's avatar
bdakin committed
44
#include "EventHandler.h"
aroben's avatar
aroben committed
45
#include "EventNames.h"
46
#include "ExceptionCodePlaceholder.h"
darin@apple.com's avatar
darin@apple.com committed
47
#include "FormState.h"
bdakin's avatar
bdakin committed
48 49
#include "Frame.h"
#include "FrameLoadRequest.h"
50
#include "FrameLoader.h"
51
#include "FrameLoaderClient.h"
52
#include "FrameSelection.h"
53
#include "HTMLFormElement.h"
aroben's avatar
aroben committed
54 55
#include "HitTestRequest.h"
#include "HitTestResult.h"
56
#include "InspectorController.h"
57
#include "LocalizedStrings.h"
aroben's avatar
aroben committed
58
#include "MouseEvent.h"
59
#include "NavigationAction.h"
aroben's avatar
aroben committed
60
#include "Node.h"
bdakin's avatar
bdakin committed
61
#include "Page.h"
62
#include "PlatformEvent.h"
aroben's avatar
aroben committed
63
#include "RenderObject.h"
bdakin's avatar
bdakin committed
64 65
#include "ReplaceSelectionCommand.h"
#include "ResourceRequest.h"
66
#include "Settings.h"
darin's avatar
darin committed
67
#include "TextIterator.h"
68
#include "TypingCommand.h"
69
#include "UserTypingGestureIndicator.h"
tristan's avatar
qt:  
tristan committed
70
#include "WindowFeatures.h"
bdakin's avatar
bdakin committed
71
#include "markup.h"
72
#include <wtf/unicode/CharacterNames.h>
73 74
#include <wtf/unicode/Unicode.h>

75 76 77 78
#if PLATFORM(GTK)
#include <wtf/gobject/GOwnPtr.h>
#endif

79 80
using namespace WTF;
using namespace Unicode;
aroben's avatar
aroben committed
81 82 83

namespace WebCore {

84 85 86
ContextMenuController::ContextMenuController(Page* page, ContextMenuClient* client)
    : m_page(page)
    , m_client(client)
aroben's avatar
aroben committed
87
{
88 89
    ASSERT_ARG(page, page);
    ASSERT_ARG(client, client);
aroben's avatar
aroben committed
90 91 92 93
}

ContextMenuController::~ContextMenuController()
{
ggaren's avatar
ggaren committed
94
    m_client->contextMenuDestroyed();
aroben's avatar
aroben committed
95 96
}

97 98 99 100 101
PassOwnPtr<ContextMenuController> ContextMenuController::create(Page* page, ContextMenuClient* client)
{
    return adoptPtr(new ContextMenuController(page, client));
}

bdakin's avatar
bdakin committed
102 103
void ContextMenuController::clearContextMenu()
{
104
    m_contextMenu.clear();
105 106 107
    if (m_menuProvider)
        m_menuProvider->contextMenuCleared();
    m_menuProvider = 0;
bdakin's avatar
bdakin committed
108 109
}

aroben's avatar
aroben committed
110 111
void ContextMenuController::handleContextMenuEvent(Event* event)
{
112
    m_contextMenu = createContextMenu(event);
113
    if (!m_contextMenu)
adele's avatar
adele committed
114
        return;
115 116 117

    populate();

118 119 120
    showContextMenu(event);
}

121 122 123 124 125
static PassOwnPtr<ContextMenuItem> separatorItem()
{
    return adoptPtr(new ContextMenuItem(SeparatorType, ContextMenuItemTagNoAction, String()));
}

126
void ContextMenuController::showContextMenu(Event* event, PassRefPtr<ContextMenuProvider> menuProvider)
127
{
128
    m_menuProvider = menuProvider;
129

130
    m_contextMenu = createContextMenu(event);
131 132 133 134
    if (!m_contextMenu) {
        clearContextMenu();
        return;
    }
135 136

    m_menuProvider->populateContextMenu(m_contextMenu.get());
137 138 139 140
    if (m_hitTestResult.isSelected()) {
        appendItem(*separatorItem(), m_contextMenu.get());
        populate();
    }
141 142 143
    showContextMenu(event);
}

144
PassOwnPtr<ContextMenu> ContextMenuController::createContextMenu(Event* event)
145
{
146 147
    ASSERT(event);
    
148
    if (!event->isMouseEvent())
149
        return nullptr;
150

aroben's avatar
aroben committed
151
    MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
152
    HitTestResult result(mouseEvent->absoluteLocation());
aroben's avatar
aroben committed
153

154
    if (Frame* frame = event->target()->toNode()->document()->frame())
155
        result = frame->eventHandler().hitTestResultAtPoint(mouseEvent->absoluteLocation());
156

bdakin's avatar
bdakin committed
157
    if (!result.innerNonSharedNode())
158
        return nullptr;
159 160 161

    m_hitTestResult = result;

162
    return adoptPtr(new ContextMenu);
163
}
aroben's avatar
aroben committed
164

165 166
void ContextMenuController::showContextMenu(Event* event)
{
bolsinga@apple.com's avatar
bolsinga@apple.com committed
167
#if ENABLE(INSPECTOR)
thatcher's avatar
thatcher committed
168
    if (m_page->inspectorController()->enabled())
169
        addInspectElementItem();
bolsinga@apple.com's avatar
bolsinga@apple.com committed
170
#endif
171 172 173 174

#if USE(CROSS_PLATFORM_CONTEXT_MENUS)
    m_contextMenu = m_client->customizeMenu(m_contextMenu.release());
#else
bdakin's avatar
bdakin committed
175 176
    PlatformMenuDescription customMenu = m_client->getCustomMenuFromDefaultItems(m_contextMenu.get());
    m_contextMenu->setPlatformDescription(customMenu);
177
#endif
aroben's avatar
aroben committed
178
    event->setDefaultHandled();
aroben's avatar
aroben committed
179 180
}

181
static void openNewWindow(const KURL& urlToLoad, Frame* frame)
bdakin's avatar
bdakin committed
182
{
tristan's avatar
qt:  
tristan committed
183
    if (Page* oldPage = frame->page()) {
184
        FrameLoadRequest request(frame->document()->securityOrigin(), ResourceRequest(urlToLoad, frame->loader().outgoingReferrer()));
185
        Page* newPage = oldPage;
186
        if (frame->settings().supportsMultipleWindows()) {
187
            newPage = oldPage->chrome().createWindow(frame, request, WindowFeatures(), NavigationAction(request.resourceRequest()));
188 189
            if (!newPage)
                return;
190
            newPage->chrome().show();
191
        }
192
        newPage->mainFrame()->loader().loadFrameRequest(request, false, false, 0, 0, MaybeSendReferrer);
tristan's avatar
qt:  
tristan committed
193
    }
bdakin's avatar
bdakin committed
194 195
}

196 197 198 199
#if PLATFORM(GTK)
static void insertUnicodeCharacter(UChar character, Frame* frame)
{
    String text(&character, 1);
200
    if (!frame->editor().shouldInsertText(text, frame->selection()->toNormalizedRange().get(), EditorInsertActionTyped))
201 202 203 204 205 206
        return;

    TypingCommand::insertText(frame->document(), text, 0, TypingCommand::TextCompositionNone);
}
#endif

aroben's avatar
aroben committed
207
void ContextMenuController::contextMenuItemSelected(ContextMenuItem* item)
bdakin's avatar
bdakin committed
208
{
209
    ASSERT(item->type() == ActionType || item->type() == CheckableActionType);
aroben's avatar
aroben committed
210 211

    if (item->action() >= ContextMenuItemBaseApplicationTag) {
bdakin's avatar
bdakin committed
212
        m_client->contextMenuItemSelected(item, m_contextMenu.get());
aroben's avatar
aroben committed
213 214 215
        return;
    }

216
    if (item->action() >= ContextMenuItemBaseCustomTag) {
217 218
        ASSERT(m_menuProvider);
        m_menuProvider->contextMenuItemSelected(item);
219 220 221
        return;
    }

222
    Frame* frame = m_hitTestResult.innerNonSharedNode()->document()->frame();
bdakin's avatar
bdakin committed
223 224
    if (!frame)
        return;
225

aroben's avatar
aroben committed
226
    switch (item->action()) {
227
    case ContextMenuItemTagOpenLinkInNewWindow:
228
        openNewWindow(m_hitTestResult.absoluteLinkURL(), frame);
229 230
        break;
    case ContextMenuItemTagDownloadLinkToDisk:
231
        // FIXME: Some day we should be able to do this from within WebCore. (Bug 117709)
232
        m_client->downloadURL(m_hitTestResult.absoluteLinkURL());
233 234
        break;
    case ContextMenuItemTagCopyLinkToClipboard:
235
        frame->editor().copyURL(m_hitTestResult.absoluteLinkURL(), m_hitTestResult.textContent());
236 237
        break;
    case ContextMenuItemTagOpenImageInNewWindow:
238
        openNewWindow(m_hitTestResult.absoluteImageURL(), frame);
239 240
        break;
    case ContextMenuItemTagDownloadImageToDisk:
241
        // FIXME: Some day we should be able to do this from within WebCore. (Bug 117709)
242
        m_client->downloadURL(m_hitTestResult.absoluteImageURL());
243 244 245 246
        break;
    case ContextMenuItemTagCopyImageToClipboard:
        // FIXME: The Pasteboard class is not written yet
        // For now, call into the client. This is temporary!
247
        frame->editor().copyImage(m_hitTestResult);
248
        break;
249
#if PLATFORM(QT) || PLATFORM(GTK) || PLATFORM(EFL)
250
    case ContextMenuItemTagCopyImageUrlToClipboard:
251
        frame->editor().copyURL(m_hitTestResult.absoluteImageURL(), m_hitTestResult.textContent());
252 253
        break;
#endif
254
    case ContextMenuItemTagOpenMediaInNewWindow:
255
        openNewWindow(m_hitTestResult.absoluteMediaURL(), frame);
256
        break;
257 258 259 260
    case ContextMenuItemTagDownloadMediaToDisk:
        // FIXME: Some day we should be able to do this from within WebCore. (Bug 117709)
        m_client->downloadURL(m_hitTestResult.absoluteMediaURL());
        break;
261
    case ContextMenuItemTagCopyMediaLinkToClipboard:
262
        frame->editor().copyURL(m_hitTestResult.absoluteMediaURL(), m_hitTestResult.textContent());
263 264
        break;
    case ContextMenuItemTagToggleMediaControls:
265
        m_hitTestResult.toggleMediaControlsDisplay();
266 267
        break;
    case ContextMenuItemTagToggleMediaLoop:
268
        m_hitTestResult.toggleMediaLoopPlayback();
269
        break;
270 271 272
    case ContextMenuItemTagToggleVideoFullscreen:
        m_hitTestResult.toggleMediaFullscreenState();
        break;
273
    case ContextMenuItemTagEnterVideoFullscreen:
274
        m_hitTestResult.enterFullscreenForVideo();
275 276
        break;
    case ContextMenuItemTagMediaPlayPause:
277
        m_hitTestResult.toggleMediaPlayState();
278 279
        break;
    case ContextMenuItemTagMediaMute:
280
        m_hitTestResult.toggleMediaMuteState();
281
        break;
282
    case ContextMenuItemTagOpenFrameInNewWindow: {
283
        DocumentLoader* loader = frame->loader().documentLoader();
284 285 286 287 288 289 290
        if (!loader->unreachableURL().isEmpty())
            openNewWindow(loader->unreachableURL(), frame);
        else
            openNewWindow(loader->url(), frame);
        break;
    }
    case ContextMenuItemTagCopy:
291
        frame->editor().copy();
292 293
        break;
    case ContextMenuItemTagGoBack:
294
        if (Page* page = frame->page())
295
            page->backForward()->goBackOrForward(-1);
296 297
        break;
    case ContextMenuItemTagGoForward:
298
        if (Page* page = frame->page())
299
            page->backForward()->goBackOrForward(1);
300 301
        break;
    case ContextMenuItemTagStop:
302
        frame->loader().stop();
303 304
        break;
    case ContextMenuItemTagReload:
305
        frame->loader().reload();
306 307
        break;
    case ContextMenuItemTagCut:
308
        frame->editor().command("Cut").execute();
309 310
        break;
    case ContextMenuItemTagPaste:
311
        frame->editor().command("Paste").execute();
312
        break;
313
#if PLATFORM(GTK)
314
    case ContextMenuItemTagDelete:
315
        frame->editor().performDelete();
316
        break;
317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346
    case ContextMenuItemTagUnicodeInsertLRMMark:
        insertUnicodeCharacter(leftToRightMark, frame);
        break;
    case ContextMenuItemTagUnicodeInsertRLMMark:
        insertUnicodeCharacter(rightToLeftMark, frame);
        break;
    case ContextMenuItemTagUnicodeInsertLREMark:
        insertUnicodeCharacter(leftToRightEmbed, frame);
        break;
    case ContextMenuItemTagUnicodeInsertRLEMark:
        insertUnicodeCharacter(rightToLeftEmbed, frame);
        break;
    case ContextMenuItemTagUnicodeInsertLROMark:
        insertUnicodeCharacter(leftToRightOverride, frame);
        break;
    case ContextMenuItemTagUnicodeInsertRLOMark:
        insertUnicodeCharacter(rightToLeftOverride, frame);
        break;
    case ContextMenuItemTagUnicodeInsertPDFMark:
        insertUnicodeCharacter(popDirectionalFormatting, frame);
        break;
    case ContextMenuItemTagUnicodeInsertZWSMark:
        insertUnicodeCharacter(zeroWidthSpace, frame);
        break;
    case ContextMenuItemTagUnicodeInsertZWJMark:
        insertUnicodeCharacter(zeroWidthJoiner, frame);
        break;
    case ContextMenuItemTagUnicodeInsertZWNJMark:
        insertUnicodeCharacter(zeroWidthNonJoiner, frame);
        break;
347
#endif
348
#if PLATFORM(GTK) || PLATFORM(QT) || PLATFORM(EFL)
349
    case ContextMenuItemTagSelectAll:
350
        frame->editor().command("SelectAll").execute();
351
        break;
352
#endif
353 354
    case ContextMenuItemTagSpellingGuess: {
        FrameSelection* frameSelection = frame->selection();
355
        if (frame->editor().shouldInsertText(item->title(), frameSelection->toNormalizedRange().get(), EditorInsertActionPasted)) {
356
            Document* document = frame->document();
357 358
            ReplaceSelectionCommand::CommandOptions replaceOptions = ReplaceSelectionCommand::MatchStyle | ReplaceSelectionCommand::PreventNesting;

359
            if (frame->editor().behavior().shouldAllowSpellingSuggestionsWithoutSelection()) {
360 361 362 363 364
                ASSERT(frameSelection->isCaretOrRange());
                VisibleSelection wordSelection(frameSelection->base());
                wordSelection.expandUsingGranularity(WordGranularity);
                frameSelection->setSelection(wordSelection);
            } else {
365
                ASSERT(frame->editor().selectedText().length());
366 367 368 369
                replaceOptions |= ReplaceSelectionCommand::SelectReplacement;
            }

            RefPtr<ReplaceSelectionCommand> command = ReplaceSelectionCommand::create(document, createFragmentFromMarkup(document, item->title(), ""), replaceOptions);
370
            applyCommand(command);
371
            frameSelection->revealSelection(ScrollAlignment::alignToEdgeIfNeeded);
bdakin's avatar
bdakin committed
372
        }
373
        break;
374
    }
375
    case ContextMenuItemTagIgnoreSpelling:
376
        frame->editor().ignoreSpelling();
377 378
        break;
    case ContextMenuItemTagLearnSpelling:
379
        frame->editor().learnSpelling();
380 381 382 383 384 385 386 387 388
        break;
    case ContextMenuItemTagSearchWeb:
        m_client->searchWithGoogle(frame);
        break;
    case ContextMenuItemTagLookUpInDictionary:
        // FIXME: Some day we may be able to do this from within WebCore.
        m_client->lookUpInDictionary(frame);
        break;
    case ContextMenuItemTagOpenLink:
389
        if (Frame* targetFrame = m_hitTestResult.targetFrame())
390
            targetFrame->loader().loadFrameRequest(FrameLoadRequest(frame->document()->securityOrigin(), ResourceRequest(m_hitTestResult.absoluteLinkURL(), frame->loader().outgoingReferrer())), false, false, 0, 0, MaybeSendReferrer);
391
        else
392
            openNewWindow(m_hitTestResult.absoluteLinkURL(), frame);
393
        break;
394
    case ContextMenuItemTagOpenLinkInThisWindow:
395
        frame->loader().loadFrameRequest(FrameLoadRequest(frame->document()->securityOrigin(), ResourceRequest(m_hitTestResult.absoluteLinkURL(), frame->loader().outgoingReferrer())), false, false, 0, 0, MaybeSendReferrer);
396
        break;
397
    case ContextMenuItemTagBold:
398
        frame->editor().command("ToggleBold").execute();
399 400
        break;
    case ContextMenuItemTagItalic:
401
        frame->editor().command("ToggleItalic").execute();
402 403
        break;
    case ContextMenuItemTagUnderline:
404
        frame->editor().toggleUnderline();
405 406 407 408 409 410 411
        break;
    case ContextMenuItemTagOutline:
        // We actually never enable this because CSS does not have a way to specify an outline font,
        // which may make this difficult to implement. Maybe a special case of text-shadow?
        break;
    case ContextMenuItemTagStartSpeaking: {
        RefPtr<Range> selectedRange = frame->selection()->toNormalizedRange();
412
        if (!selectedRange || selectedRange->collapsed(IGNORE_EXCEPTION)) {
413
            Document* document = m_hitTestResult.innerNonSharedNode()->document();
414
            selectedRange = document->createRange();
415
            selectedRange->selectNode(document->documentElement(), IGNORE_EXCEPTION);
416 417 418 419 420 421 422 423
        }
        m_client->speak(plainText(selectedRange.get()));
        break;
    }
    case ContextMenuItemTagStopSpeaking:
        m_client->stopSpeaking();
        break;
    case ContextMenuItemTagDefaultDirection:
424
        frame->editor().setBaseWritingDirection(NaturalWritingDirection);
425 426
        break;
    case ContextMenuItemTagLeftToRight:
427
        frame->editor().setBaseWritingDirection(LeftToRightWritingDirection);
428 429
        break;
    case ContextMenuItemTagRightToLeft:
430
        frame->editor().setBaseWritingDirection(RightToLeftWritingDirection);
431 432
        break;
    case ContextMenuItemTagTextDirectionDefault:
433
        frame->editor().command("MakeTextWritingDirectionNatural").execute();
434 435
        break;
    case ContextMenuItemTagTextDirectionLeftToRight:
436
        frame->editor().command("MakeTextWritingDirectionLeftToRight").execute();
437 438
        break;
    case ContextMenuItemTagTextDirectionRightToLeft:
439
        frame->editor().command("MakeTextWritingDirectionRightToLeft").execute();
440
        break;
bdakin's avatar
bdakin committed
441
#if PLATFORM(MAC)
442 443 444
    case ContextMenuItemTagSearchInSpotlight:
        m_client->searchWithSpotlight();
        break;
445
#endif
446
    case ContextMenuItemTagShowSpellingPanel:
447
        frame->editor().showSpellingGuessPanel();
448 449
        break;
    case ContextMenuItemTagCheckSpelling:
450
        frame->editor().advanceToNextMisspelling();
451 452
        break;
    case ContextMenuItemTagCheckSpellingWhileTyping:
453
        frame->editor().toggleContinuousSpellChecking();
454 455
        break;
    case ContextMenuItemTagCheckGrammarWithSpelling:
456
        frame->editor().toggleGrammarChecking();
457
        break;
458
#if PLATFORM(MAC)
459
    case ContextMenuItemTagShowFonts:
460
        frame->editor().showFontPanel();
461 462
        break;
    case ContextMenuItemTagStyles:
463
        frame->editor().showStylesPanel();
464 465
        break;
    case ContextMenuItemTagShowColors:
466
        frame->editor().showColorPanel();
467
        break;
adele@apple.com's avatar
adele@apple.com committed
468
#endif
469
#if USE(APPKIT)
470
    case ContextMenuItemTagMakeUpperCase:
471
        frame->editor().uppercaseWord();
472 473
        break;
    case ContextMenuItemTagMakeLowerCase:
474
        frame->editor().lowercaseWord();
475 476
        break;
    case ContextMenuItemTagCapitalize:
477
        frame->editor().capitalizeWord();
478
        break;
479 480
#endif
#if PLATFORM(MAC)
481
    case ContextMenuItemTagChangeBack:
482
        frame->editor().changeBackToReplacedString(m_hitTestResult.replacedString());
483 484 485
        break;
#endif
#if USE(AUTOMATIC_TEXT_REPLACEMENT)
486
    case ContextMenuItemTagShowSubstitutions:
487
        frame->editor().showSubstitutionsPanel();
488 489
        break;
    case ContextMenuItemTagSmartCopyPaste:
490
        frame->editor().toggleSmartInsertDelete();
491 492
        break;
    case ContextMenuItemTagSmartQuotes:
493
        frame->editor().toggleAutomaticQuoteSubstitution();
494 495
        break;
    case ContextMenuItemTagSmartDashes:
496
        frame->editor().toggleAutomaticDashSubstitution();
497 498
        break;
    case ContextMenuItemTagSmartLinks:
499
        frame->editor().toggleAutomaticLinkDetection();
500 501
        break;
    case ContextMenuItemTagTextReplacement:
502
        frame->editor().toggleAutomaticTextReplacement();
503 504
        break;
    case ContextMenuItemTagCorrectSpellingAutomatically:
505
        frame->editor().toggleAutomaticSpellingCorrection();
506
        break;
bdakin's avatar
bdakin committed
507
#endif
bolsinga@apple.com's avatar
bolsinga@apple.com committed
508
#if ENABLE(INSPECTOR)
509 510
    case ContextMenuItemTagInspectElement:
        if (Page* page = frame->page())
511
            page->inspectorController()->inspect(m_hitTestResult.innerNonSharedNode());
512
        break;
bolsinga@apple.com's avatar
bolsinga@apple.com committed
513
#endif
514
    case ContextMenuItemTagDictationAlternative:
515
        frame->editor().applyDictationAlternativelternative(item->title());
516
        break;
517 518
    default:
        break;
bdakin's avatar
bdakin committed
519
    }
aroben's avatar
aroben committed
520 521
}

522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575
void ContextMenuController::appendItem(ContextMenuItem& menuItem, ContextMenu* parentMenu)
{
    checkOrEnableIfNeeded(menuItem);
    if (parentMenu)
        parentMenu->appendItem(menuItem);
}

void ContextMenuController::createAndAppendFontSubMenu(ContextMenuItem& fontMenuItem)
{
    ContextMenu fontMenu;

#if PLATFORM(MAC)
    ContextMenuItem showFonts(ActionType, ContextMenuItemTagShowFonts, contextMenuItemTagShowFonts());
#endif
    ContextMenuItem bold(CheckableActionType, ContextMenuItemTagBold, contextMenuItemTagBold());
    ContextMenuItem italic(CheckableActionType, ContextMenuItemTagItalic, contextMenuItemTagItalic());
    ContextMenuItem underline(CheckableActionType, ContextMenuItemTagUnderline, contextMenuItemTagUnderline());
    ContextMenuItem outline(ActionType, ContextMenuItemTagOutline, contextMenuItemTagOutline());
#if PLATFORM(MAC)
    ContextMenuItem styles(ActionType, ContextMenuItemTagStyles, contextMenuItemTagStyles());
    ContextMenuItem showColors(ActionType, ContextMenuItemTagShowColors, contextMenuItemTagShowColors());
#endif

#if PLATFORM(MAC)
    appendItem(showFonts, &fontMenu);
#endif
    appendItem(bold, &fontMenu);
    appendItem(italic, &fontMenu);
    appendItem(underline, &fontMenu);
    appendItem(outline, &fontMenu);
#if PLATFORM(MAC)
    appendItem(styles, &fontMenu);
    appendItem(*separatorItem(), &fontMenu);
    appendItem(showColors, &fontMenu);
#endif

    fontMenuItem.setSubMenu(&fontMenu);
}


#if !PLATFORM(GTK)

void ContextMenuController::createAndAppendSpellingAndGrammarSubMenu(ContextMenuItem& spellingAndGrammarMenuItem)
{
    ContextMenu spellingAndGrammarMenu;

    ContextMenuItem showSpellingPanel(ActionType, ContextMenuItemTagShowSpellingPanel, 
        contextMenuItemTagShowSpellingPanel(true));
    ContextMenuItem checkSpelling(ActionType, ContextMenuItemTagCheckSpelling, 
        contextMenuItemTagCheckSpelling());
    ContextMenuItem checkAsYouType(CheckableActionType, ContextMenuItemTagCheckSpellingWhileTyping, 
        contextMenuItemTagCheckSpellingWhileTyping());
    ContextMenuItem grammarWithSpelling(CheckableActionType, ContextMenuItemTagCheckGrammarWithSpelling, 
        contextMenuItemTagCheckGrammarWithSpelling());
576
#if PLATFORM(MAC)
577 578 579 580 581 582
    ContextMenuItem correctSpelling(CheckableActionType, ContextMenuItemTagCorrectSpellingAutomatically, 
        contextMenuItemTagCorrectSpellingAutomatically());
#endif

    appendItem(showSpellingPanel, &spellingAndGrammarMenu);
    appendItem(checkSpelling, &spellingAndGrammarMenu);
583
#if PLATFORM(MAC)
584 585 586 587
    appendItem(*separatorItem(), &spellingAndGrammarMenu);
#endif
    appendItem(checkAsYouType, &spellingAndGrammarMenu);
    appendItem(grammarWithSpelling, &spellingAndGrammarMenu);
588
#if PLATFORM(MAC)
589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614
    appendItem(correctSpelling, &spellingAndGrammarMenu);
#endif

    spellingAndGrammarMenuItem.setSubMenu(&spellingAndGrammarMenu);
}

#endif // !PLATFORM(GTK)


#if PLATFORM(MAC)

void ContextMenuController::createAndAppendSpeechSubMenu(ContextMenuItem& speechMenuItem)
{
    ContextMenu speechMenu;

    ContextMenuItem start(ActionType, ContextMenuItemTagStartSpeaking, contextMenuItemTagStartSpeaking());
    ContextMenuItem stop(ActionType, ContextMenuItemTagStopSpeaking, contextMenuItemTagStopSpeaking());

    appendItem(start, &speechMenu);
    appendItem(stop, &speechMenu);

    speechMenuItem.setSubMenu(&speechMenu);
}

#endif
 
615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646
#if PLATFORM(GTK)

void ContextMenuController::createAndAppendUnicodeSubMenu(ContextMenuItem& unicodeMenuItem)
{
    ContextMenu unicodeMenu;

    ContextMenuItem leftToRightMarkMenuItem(ActionType, ContextMenuItemTagUnicodeInsertLRMMark, contextMenuItemTagUnicodeInsertLRMMark());
    ContextMenuItem rightToLeftMarkMenuItem(ActionType, ContextMenuItemTagUnicodeInsertRLMMark, contextMenuItemTagUnicodeInsertRLMMark());
    ContextMenuItem leftToRightEmbedMenuItem(ActionType, ContextMenuItemTagUnicodeInsertLREMark, contextMenuItemTagUnicodeInsertLREMark());
    ContextMenuItem rightToLeftEmbedMenuItem(ActionType, ContextMenuItemTagUnicodeInsertRLEMark, contextMenuItemTagUnicodeInsertRLEMark());
    ContextMenuItem leftToRightOverrideMenuItem(ActionType, ContextMenuItemTagUnicodeInsertLROMark, contextMenuItemTagUnicodeInsertLROMark());
    ContextMenuItem rightToLeftOverrideMenuItem(ActionType, ContextMenuItemTagUnicodeInsertRLOMark, contextMenuItemTagUnicodeInsertRLOMark());
    ContextMenuItem popDirectionalFormattingMenuItem(ActionType, ContextMenuItemTagUnicodeInsertPDFMark, contextMenuItemTagUnicodeInsertPDFMark());
    ContextMenuItem zeroWidthSpaceMenuItem(ActionType, ContextMenuItemTagUnicodeInsertZWSMark, contextMenuItemTagUnicodeInsertZWSMark());
    ContextMenuItem zeroWidthJoinerMenuItem(ActionType, ContextMenuItemTagUnicodeInsertZWJMark, contextMenuItemTagUnicodeInsertZWJMark());
    ContextMenuItem zeroWidthNonJoinerMenuItem(ActionType, ContextMenuItemTagUnicodeInsertZWNJMark, contextMenuItemTagUnicodeInsertZWNJMark());

    appendItem(leftToRightMarkMenuItem, &unicodeMenu);
    appendItem(rightToLeftMarkMenuItem, &unicodeMenu);
    appendItem(leftToRightEmbedMenuItem, &unicodeMenu);
    appendItem(rightToLeftEmbedMenuItem, &unicodeMenu);
    appendItem(leftToRightOverrideMenuItem, &unicodeMenu);
    appendItem(rightToLeftOverrideMenuItem, &unicodeMenu);
    appendItem(popDirectionalFormattingMenuItem, &unicodeMenu);
    appendItem(zeroWidthSpaceMenuItem, &unicodeMenu);
    appendItem(zeroWidthJoinerMenuItem, &unicodeMenu);
    appendItem(zeroWidthNonJoinerMenuItem, &unicodeMenu);

    unicodeMenuItem.setSubMenu(&unicodeMenu);
}

#else
647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680

void ContextMenuController::createAndAppendWritingDirectionSubMenu(ContextMenuItem& writingDirectionMenuItem)
{
    ContextMenu writingDirectionMenu;

    ContextMenuItem defaultItem(ActionType, ContextMenuItemTagDefaultDirection, 
        contextMenuItemTagDefaultDirection());
    ContextMenuItem ltr(CheckableActionType, ContextMenuItemTagLeftToRight, contextMenuItemTagLeftToRight());
    ContextMenuItem rtl(CheckableActionType, ContextMenuItemTagRightToLeft, contextMenuItemTagRightToLeft());

    appendItem(defaultItem, &writingDirectionMenu);
    appendItem(ltr, &writingDirectionMenu);
    appendItem(rtl, &writingDirectionMenu);

    writingDirectionMenuItem.setSubMenu(&writingDirectionMenu);
}

void ContextMenuController::createAndAppendTextDirectionSubMenu(ContextMenuItem& textDirectionMenuItem)
{
    ContextMenu textDirectionMenu;

    ContextMenuItem defaultItem(ActionType, ContextMenuItemTagTextDirectionDefault, contextMenuItemTagDefaultDirection());
    ContextMenuItem ltr(CheckableActionType, ContextMenuItemTagTextDirectionLeftToRight, contextMenuItemTagLeftToRight());
    ContextMenuItem rtl(CheckableActionType, ContextMenuItemTagTextDirectionRightToLeft, contextMenuItemTagRightToLeft());

    appendItem(defaultItem, &textDirectionMenu);
    appendItem(ltr, &textDirectionMenu);
    appendItem(rtl, &textDirectionMenu);

    textDirectionMenuItem.setSubMenu(&textDirectionMenu);
}

#endif

681
#if PLATFORM(MAC)
682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727

void ContextMenuController::createAndAppendSubstitutionsSubMenu(ContextMenuItem& substitutionsMenuItem)
{
    ContextMenu substitutionsMenu;

    ContextMenuItem showSubstitutions(ActionType, ContextMenuItemTagShowSubstitutions, contextMenuItemTagShowSubstitutions(true));
    ContextMenuItem smartCopyPaste(CheckableActionType, ContextMenuItemTagSmartCopyPaste, contextMenuItemTagSmartCopyPaste());
    ContextMenuItem smartQuotes(CheckableActionType, ContextMenuItemTagSmartQuotes, contextMenuItemTagSmartQuotes());
    ContextMenuItem smartDashes(CheckableActionType, ContextMenuItemTagSmartDashes, contextMenuItemTagSmartDashes());
    ContextMenuItem smartLinks(CheckableActionType, ContextMenuItemTagSmartLinks, contextMenuItemTagSmartLinks());
    ContextMenuItem textReplacement(CheckableActionType, ContextMenuItemTagTextReplacement, contextMenuItemTagTextReplacement());

    appendItem(showSubstitutions, &substitutionsMenu);
    appendItem(*separatorItem(), &substitutionsMenu);
    appendItem(smartCopyPaste, &substitutionsMenu);
    appendItem(smartQuotes, &substitutionsMenu);
    appendItem(smartDashes, &substitutionsMenu);
    appendItem(smartLinks, &substitutionsMenu);
    appendItem(textReplacement, &substitutionsMenu);

    substitutionsMenuItem.setSubMenu(&substitutionsMenu);
}

void ContextMenuController::createAndAppendTransformationsSubMenu(ContextMenuItem& transformationsMenuItem)
{
    ContextMenu transformationsMenu;

    ContextMenuItem makeUpperCase(ActionType, ContextMenuItemTagMakeUpperCase, contextMenuItemTagMakeUpperCase());
    ContextMenuItem makeLowerCase(ActionType, ContextMenuItemTagMakeLowerCase, contextMenuItemTagMakeLowerCase());
    ContextMenuItem capitalize(ActionType, ContextMenuItemTagCapitalize, contextMenuItemTagCapitalize());

    appendItem(makeUpperCase, &transformationsMenu);
    appendItem(makeLowerCase, &transformationsMenu);
    appendItem(capitalize, &transformationsMenu);

    transformationsMenuItem.setSubMenu(&transformationsMenu);
}

#endif

static bool selectionContainsPossibleWord(Frame* frame)
{
    // Current algorithm: look for a character that's not just a separator.
    for (TextIterator it(frame->selection()->toNormalizedRange().get()); !it.atEnd(); it.advance()) {
        int length = it.length();
        for (int i = 0; i < length; ++i)
728
            if (!(category(it.characterAt(i)) & (Separator_Space | Separator_Line | Separator_Paragraph)))
729 730 731 732 733
                return true;
    }
    return false;
}

734
#if PLATFORM(MAC)
735
#if __MAC_OS_X_VERSION_MIN_REQUIRED == 1060
736 737 738 739 740 741
#define INCLUDE_SPOTLIGHT_CONTEXT_MENU_ITEM 1
#else
#define INCLUDE_SPOTLIGHT_CONTEXT_MENU_ITEM 0
#endif
#endif

742 743 744 745 746 747
#if PLATFORM(MAC)
#define SUPPORTS_TOGGLE_VIDEO_FULLSCREEN 1
#else
#define SUPPORTS_TOGGLE_VIDEO_FULLSCREEN 0
#endif

748 749 750 751 752 753
#if PLATFORM(MAC)
#define SUPPORTS_TOGGLE_SHOW_HIDE_MEDIA_CONTROLS 1
#else
#define SUPPORTS_TOGGLE_SHOW_HIDE_MEDIA_CONTROLS 0
#endif

754 755 756 757 758 759 760 761 762 763 764 765 766 767 768
void ContextMenuController::populate()
{
    ContextMenuItem OpenLinkItem(ActionType, ContextMenuItemTagOpenLink, contextMenuItemTagOpenLink());
    ContextMenuItem OpenLinkInNewWindowItem(ActionType, ContextMenuItemTagOpenLinkInNewWindow, 
        contextMenuItemTagOpenLinkInNewWindow());
    ContextMenuItem DownloadFileItem(ActionType, ContextMenuItemTagDownloadLinkToDisk, 
        contextMenuItemTagDownloadLinkToDisk());
    ContextMenuItem CopyLinkItem(ActionType, ContextMenuItemTagCopyLinkToClipboard, 
        contextMenuItemTagCopyLinkToClipboard());
    ContextMenuItem OpenImageInNewWindowItem(ActionType, ContextMenuItemTagOpenImageInNewWindow, 
        contextMenuItemTagOpenImageInNewWindow());
    ContextMenuItem DownloadImageItem(ActionType, ContextMenuItemTagDownloadImageToDisk, 
        contextMenuItemTagDownloadImageToDisk());
    ContextMenuItem CopyImageItem(ActionType, ContextMenuItemTagCopyImageToClipboard, 
        contextMenuItemTagCopyImageToClipboard());
769
#if PLATFORM(QT) || PLATFORM(GTK) || PLATFORM(EFL)
770 771 772
    ContextMenuItem CopyImageUrlItem(ActionType, ContextMenuItemTagCopyImageUrlToClipboard, 
        contextMenuItemTagCopyImageUrlToClipboard());
#endif
773
    ContextMenuItem OpenMediaInNewWindowItem(ActionType, ContextMenuItemTagOpenMediaInNewWindow, String());
774 775
    ContextMenuItem DownloadMediaItem(ActionType, ContextMenuItemTagDownloadMediaToDisk, String());
    ContextMenuItem CopyMediaLinkItem(ActionType, ContextMenuItemTagCopyMediaLinkToClipboard, String());
776 777 778 779
    ContextMenuItem MediaPlayPause(ActionType, ContextMenuItemTagMediaPlayPause, 
        contextMenuItemTagMediaPlay());
    ContextMenuItem MediaMute(ActionType, ContextMenuItemTagMediaMute, 
        contextMenuItemTagMediaMute());
780 781 782 783
#if SUPPORTS_TOGGLE_SHOW_HIDE_MEDIA_CONTROLS
    ContextMenuItem ToggleMediaControls(ActionType, ContextMenuItemTagToggleMediaControls,
        contextMenuItemTagHideMediaControls());
#else
784 785
    ContextMenuItem ToggleMediaControls(CheckableActionType, ContextMenuItemTagToggleMediaControls, 
        contextMenuItemTagToggleMediaControls());
786
#endif
787 788
    ContextMenuItem ToggleMediaLoop(CheckableActionType, ContextMenuItemTagToggleMediaLoop, 
        contextMenuItemTagToggleMediaLoop());
789 790 791
    ContextMenuItem EnterVideoFullscreen(ActionType, ContextMenuItemTagEnterVideoFullscreen,
        contextMenuItemTagEnterVideoFullscreen());
    ContextMenuItem ToggleVideoFullscreen(ActionType, ContextMenuItemTagToggleVideoFullscreen,
792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818
        contextMenuItemTagEnterVideoFullscreen());
#if PLATFORM(MAC)
    ContextMenuItem SearchSpotlightItem(ActionType, ContextMenuItemTagSearchInSpotlight, 
        contextMenuItemTagSearchInSpotlight());
#endif
#if !PLATFORM(GTK)
    ContextMenuItem SearchWebItem(ActionType, ContextMenuItemTagSearchWeb, contextMenuItemTagSearchWeb());
#endif
    ContextMenuItem CopyItem(ActionType, ContextMenuItemTagCopy, contextMenuItemTagCopy());
    ContextMenuItem BackItem(ActionType, ContextMenuItemTagGoBack, contextMenuItemTagGoBack());
    ContextMenuItem ForwardItem(ActionType, ContextMenuItemTagGoForward,  contextMenuItemTagGoForward());
    ContextMenuItem StopItem(ActionType, ContextMenuItemTagStop, contextMenuItemTagStop());
    ContextMenuItem ReloadItem(ActionType, ContextMenuItemTagReload, contextMenuItemTagReload());
    ContextMenuItem OpenFrameItem(ActionType, ContextMenuItemTagOpenFrameInNewWindow, 
        contextMenuItemTagOpenFrameInNewWindow());
    ContextMenuItem NoGuessesItem(ActionType, ContextMenuItemTagNoGuessesFound, 
        contextMenuItemTagNoGuessesFound());
    ContextMenuItem IgnoreSpellingItem(ActionType, ContextMenuItemTagIgnoreSpelling, 
        contextMenuItemTagIgnoreSpelling());
    ContextMenuItem LearnSpellingItem(ActionType, ContextMenuItemTagLearnSpelling, 
        contextMenuItemTagLearnSpelling());
    ContextMenuItem IgnoreGrammarItem(ActionType, ContextMenuItemTagIgnoreGrammar, 
        contextMenuItemTagIgnoreGrammar());
    ContextMenuItem CutItem(ActionType, ContextMenuItemTagCut, contextMenuItemTagCut());
    ContextMenuItem PasteItem(ActionType, ContextMenuItemTagPaste, contextMenuItemTagPaste());
#if PLATFORM(GTK)
    ContextMenuItem DeleteItem(ActionType, ContextMenuItemTagDelete, contextMenuItemTagDelete());
819
#endif
820
#if PLATFORM(GTK) || PLATFORM(QT) || PLATFORM(EFL)
821 822 823 824 825 826 827
    ContextMenuItem SelectAllItem(ActionType, ContextMenuItemTagSelectAll, contextMenuItemTagSelectAll());
#endif

    Node* node = m_hitTestResult.innerNonSharedNode();
    if (!node)
        return;
#if PLATFORM(GTK)
828
    if (!m_hitTestResult.isContentEditable() && (node->isElementNode() && toElement(node)->isFormControlElement()))
829 830 831 832 833 834 835
        return;
#endif
    Frame* frame = node->document()->frame();
    if (!frame)
        return;

    if (!m_hitTestResult.isContentEditable()) {
836
        FrameLoader& loader = frame->loader();
837 838
        KURL linkURL = m_hitTestResult.absoluteLinkURL();
        if (!linkURL.isEmpty()) {
839
            if (loader.client()->canHandleRequest(ResourceRequest(linkURL))) {
840 841 842 843
                appendItem(OpenLinkItem, m_contextMenu.get());
                appendItem(OpenLinkInNewWindowItem, m_contextMenu.get());
                appendItem(DownloadFileItem, m_contextMenu.get());
            }
844 845 846 847
#if PLATFORM(QT)
            if (m_hitTestResult.isSelected()) 
                appendItem(CopyItem, m_contextMenu.get());
#endif
848 849 850 851 852 853 854 855 856 857 858 859
            appendItem(CopyLinkItem, m_contextMenu.get());
        }

        KURL imageURL = m_hitTestResult.absoluteImageURL();
        if (!imageURL.isEmpty()) {
            if (!linkURL.isEmpty())
                appendItem(*separatorItem(), m_contextMenu.get());

            appendItem(OpenImageInNewWindowItem, m_contextMenu.get());
            appendItem(DownloadImageItem, m_contextMenu.get());
            if (imageURL.isLocalFile() || m_hitTestResult.image())
                appendItem(CopyImageItem, m_contextMenu.get());
860
#if PLATFORM(QT) || PLATFORM(GTK) || PLATFORM(EFL)
861 862
            appendItem(CopyImageUrlItem, m_contextMenu.get());
#endif
863 864 865 866 867 868 869 870 871 872 873
        }

        KURL mediaURL = m_hitTestResult.absoluteMediaURL();
        if (!mediaURL.isEmpty()) {
            if (!linkURL.isEmpty() || !imageURL.isEmpty())
                appendItem(*separatorItem(), m_contextMenu.get());

            appendItem(MediaPlayPause, m_contextMenu.get());
            appendItem(MediaMute, m_contextMenu.get());
            appendItem(ToggleMediaControls, m_contextMenu.get());
            appendItem(ToggleMediaLoop, m_contextMenu.get());
874 875 876
#if SUPPORTS_TOGGLE_VIDEO_FULLSCREEN
            appendItem(ToggleVideoFullscreen, m_contextMenu.get());
#else
877
            appendItem(EnterVideoFullscreen, m_contextMenu.get());
878
#endif
879 880 881
            appendItem(*separatorItem(), m_contextMenu.get());
            appendItem(CopyMediaLinkItem, m_contextMenu.get());
            appendItem(OpenMediaInNewWindowItem, m_contextMenu.get());
882
            if (loader.client()->canHandleRequest(ResourceRequest(mediaURL)))
883
                appendItem(DownloadMediaItem, m_contextMenu.get());
884 885 886 887 888 889
        }

        if (imageURL.isEmpty() && linkURL.isEmpty() && mediaURL.isEmpty()) {
            if (m_hitTestResult.isSelected()) {
                if (selectionContainsPossibleWord(frame)) {
#if PLATFORM(MAC)
890
                    String selectedString = frame->displayStringModifiedByEncoding(frame->editor().selectedText());
891 892 893
                    ContextMenuItem LookUpInDictionaryItem(ActionType, ContextMenuItemTagLookUpInDictionary, contextMenuItemTagLookUpInDictionary(selectedString));

#if INCLUDE_SPOTLIGHT_CONTEXT_MENU_ITEM
894
                    appendItem(SearchSpotlightItem, m_contextMenu.get());
895 896 897
#else
                    appendItem(LookUpInDictionaryItem, m_contextMenu.get());
#endif
898
#endif
899

900 901 902 903
#if !PLATFORM(GTK)
                    appendItem(SearchWebItem, m_contextMenu.get());
                    appendItem(*separatorItem(), m_contextMenu.get());
#endif
904 905 906

#if PLATFORM(MAC) && INCLUDE_SPOTLIGHT_CONTEXT_MENU_ITEM
                    appendItem(LookUpInDictionaryItem, m_contextMenu.get());
907 908 909
                    appendItem(*separatorItem(), m_contextMenu.get());
#endif
                }
910

911 912 913
                appendItem(CopyItem, m_contextMenu.get());
#if PLATFORM(MAC)
                appendItem(*separatorItem(), m_contextMenu.get());
914

915 916 917 918 919 920 921 922
                ContextMenuItem SpeechMenuItem(SubmenuType, ContextMenuItemTagSpeechMenu, contextMenuItemTagSpeechMenu());
                createAndAppendSpeechSubMenu(SpeechMenuItem);
                appendItem(SpeechMenuItem, m_contextMenu.get());
#endif                
            } else {
#if ENABLE(INSPECTOR)
                if (!(frame->page() && frame->page()->inspectorController()->hasInspectorFrontendClient())) {
#endif
923

924 925
                // In GTK+ unavailable items are not hidden but insensitive.
#if PLATFORM(GTK)
926 927 928 929 930
                appendItem(BackItem, m_contextMenu.get());
                appendItem(ForwardItem, m_contextMenu.get());
                appendItem(StopItem, m_contextMenu.get());
                appendItem(ReloadItem, m_contextMenu.get());
#else
931 932 933 934 935 936 937 938
                if (frame->page() && frame->page()->backForward()->canGoBackOrForward(-1))
                    appendItem(BackItem, m_contextMenu.get());

                if (frame->page() && frame->page()->backForward()->canGoBackOrForward(1))
                    appendItem(ForwardItem, m_contextMenu.get());

                // use isLoadingInAPISense rather than isLoading because Stop/Reload are
                // intended to match WebKit's API, not WebCore's internal notion of loading status
939
                if (loader.documentLoader()->isLoadingInAPISense())
940 941 942
                    appendItem(StopItem, m_contextMenu.get());
                else
                    appendItem(ReloadItem, m_contextMenu.get());
943
#endif
944 945 946 947 948 949 950 951 952
#if ENABLE(INSPECTOR)
                }
#endif

                if (frame->page() && frame != frame->page()->mainFrame())
                    appendItem(OpenFrameItem, m_contextMenu.get());
            }
        }
    } else { // Make an editing context menu
953
        FrameSelection* selection = frame->selection();
954
        bool inPasswordField = selection->isInPasswordField();
955 956
        if (!inPasswordField) {
            bool haveContextMenuItemsForMisspellingOrGrammer = false;
957
            bool spellCheckingEnabled = frame->editor().isSpellCheckingEnabledFor(node);
958 959 960 961 962
            if (spellCheckingEnabled) {
                // Consider adding spelling-related or grammar-related context menu items (never both, since a single selected range
                // is never considered a misspelling and bad grammar at the same time)
                bool misspelling;
                bool badGrammar;
963
                Vector<String> guesses = frame->editor().guessesForMisspelledOrUngrammatical(misspelling, badGrammar);
964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980
                if (misspelling || badGrammar) {
                    size_t size = guesses.size();
                    if (!size) {
                        // If there's bad grammar but no suggestions (e.g., repeated word), just leave off the suggestions
                        // list and trailing separator rather than adding a "No Guesses Found" item (matches AppKit)
                        if (misspelling) {
                            appendItem(NoGuessesItem, m_contextMenu.get());
                            appendItem(*separatorItem(), m_contextMenu.get());
                        }
                    } else {
                        for (unsigned i = 0; i < size; i++) {
                            const String &guess = guesses[i];
                            if (!guess.isEmpty()) {
                                ContextMenuItem item(ActionType, ContextMenuItemTagSpellingGuess, guess);
                                appendItem(item, m_contextMenu.get());
                            }
                        }
981 982
                        appendItem(*separatorItem(), m_contextMenu.get());
                    }
983 984 985
                    if (misspelling) {
                        appendItem(IgnoreSpellingItem, m_contextMenu.get());
                        appendItem(LearnSpellingItem, m_contextMenu.get());
986
                    } else
987 988 989
                        appendItem(IgnoreGrammarItem, m_contextMenu.get());
                    appendItem(*separatorItem(), m_contextMenu.get());
                    haveContextMenuItemsForMisspellingOrGrammer = true;
990
#if PLATFORM(MAC)
991
                } else {
992 993 994 995 996 997 998
                    // If the string was autocorrected, generate a contextual menu item allowing it to be changed back.
                    String replacedString = m_hitTestResult.replacedString();
                    if (!replacedString.isEmpty()) {
                        ContextMenuItem item(ActionType, ContextMenuItemTagChangeBack, contextMenuItemTagChangeBack(replacedString));
                        appendItem(item, m_contextMenu.get());
                        appendItem(*separatorItem(), m_contextMenu.get());
                        haveContextMenuItemsForMisspellingOrGrammer = true;
999
                    }
1000
#endif
1001
                }
1002 1003 1004 1005 1006 1007 1008 1009 1010 1011
            }

            if (!haveContextMenuItemsForMisspellingOrGrammer) {
                // Spelling and grammar checking is mutually exclusive with dictation alternatives.
                Vector<String> dictationAlternatives = m_hitTestResult.dictationAlternatives();
                if (!dictationAlternatives.isEmpty()) {
                    for (size_t i = 0; i < dictationAlternatives.size(); ++i) {
                        ContextMenuItem item(ActionType, ContextMenuItemTagDictationAlternative, dictationAlternatives[i]);
                        appendItem(item, m_contextMenu.get());
                    }
1012 1013 1014 1015 1016
                    appendItem(*separatorItem(), m_contextMenu.get());
                }
            }
        }

1017
        FrameLoader& loader = frame->loader();
1018 1019
        KURL linkURL = m_hitTestResult.absoluteLinkURL();
        if (!linkURL.isEmpty()) {
1020
            if (loader.client()->canHandleRequest(ResourceRequest(linkURL))) {
1021 1022 1023 1024 1025 1026 1027 1028 1029 1030
                appendItem(OpenLinkItem, m_contextMenu.get());
                appendItem(OpenLinkInNewWindowItem, m_contextMenu.get());
                appendItem(DownloadFileItem, m_contextMenu.get());
            }
            appendItem(CopyLinkItem, m_contextMenu.get());
            appendItem(*separatorItem(), m_contextMenu.get());
        }

        if (m_hitTestResult.isSelected() && !inPasswordField && selectionContainsPossibleWord(frame)) {
#if PLATFORM(MAC)
1031
            String selectedString = frame->displayStringModifiedByEncoding(frame->editor().selectedText());
1032 1033 1034
            ContextMenuItem LookUpInDictionaryItem(ActionType, ContextMenuItemTagLookUpInDictionary, contextMenuItemTagLookUpInDictionary(selectedString));

#if INCLUDE_SPOTLIGHT_CONTEXT_MENU_ITEM
1035
            appendItem(SearchSpotlightItem, m_contextMenu.get());
1036 1037 1038
#else
            appendItem(LookUpInDictionaryItem, m_contextMenu.get());
#endif
1039
#endif
1040

1041 1042 1043 1044
#if !PLATFORM(GTK)
            appendItem(SearchWebItem, m_contextMenu.get());
            appendItem(*separatorItem(), m_contextMenu.get());
#endif
1045 1046 1047

#if PLATFORM(MAC) && INCLUDE_SPOTLIGHT_CONTEXT_MENU_ITEM
            appendItem(LookUpInDictionaryItem, m_contextMenu.get());
1048 1049 1050 1051 1052 1053 1054 1055 1056 1057
            appendItem(*separatorItem(), m_contextMenu.get());
#endif
        }

        appendItem(CutItem, m_contextMenu.get());
        appendItem(CopyItem, m_contextMenu.get());
        appendItem(PasteItem, m_contextMenu.get());
#if PLATFORM(GTK)
        appendItem(DeleteItem, m_contextMenu.get());
        appendItem(*separatorItem(), m_contextMenu.get());
1058
#endif
1059
#if PLATFORM(GTK) || PLATFORM(QT) || PLATFORM(EFL)