ContextMenuController.cpp 59.6 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
    case ContextMenuItemTagSpellingGuess: {
354 355
        FrameSelection& frameSelection = frame->selection();
        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
                ASSERT(frameSelection.isCaretOrRange());
                VisibleSelection wordSelection(frameSelection.base());
362
                wordSelection.expandUsingGranularity(WordGranularity);
363
                frameSelection.setSelection(wordSelection);
364
            } 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
        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: {
411
        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

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.
725
    for (TextIterator it(frame->selection().toNormalizedRange().get()); !it.atEnd(); it.advance()) {
726 727
        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 735 736 737 738 739
#if PLATFORM(MAC)
#define SUPPORTS_TOGGLE_VIDEO_FULLSCREEN 1
#else
#define SUPPORTS_TOGGLE_VIDEO_FULLSCREEN 0
#endif

740 741 742 743 744 745
#if PLATFORM(MAC)
#define SUPPORTS_TOGGLE_SHOW_HIDE_MEDIA_CONTROLS 1
#else
#define SUPPORTS_TOGGLE_SHOW_HIDE_MEDIA_CONTROLS 0
#endif

746 747 748 749 750 751 752 753 754 755 756 757 758 759 760
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());
761
#if PLATFORM(QT) || PLATFORM(GTK) || PLATFORM(EFL)
762 763 764
    ContextMenuItem CopyImageUrlItem(ActionType, ContextMenuItemTagCopyImageUrlToClipboard, 
        contextMenuItemTagCopyImageUrlToClipboard());
#endif
765
    ContextMenuItem OpenMediaInNewWindowItem(ActionType, ContextMenuItemTagOpenMediaInNewWindow, String());
766 767
    ContextMenuItem DownloadMediaItem(ActionType, ContextMenuItemTagDownloadMediaToDisk, String());
    ContextMenuItem CopyMediaLinkItem(ActionType, ContextMenuItemTagCopyMediaLinkToClipboard, String());
768 769 770 771
    ContextMenuItem MediaPlayPause(ActionType, ContextMenuItemTagMediaPlayPause, 
        contextMenuItemTagMediaPlay());
    ContextMenuItem MediaMute(ActionType, ContextMenuItemTagMediaMute, 
        contextMenuItemTagMediaMute());
772 773 774 775
#if SUPPORTS_TOGGLE_SHOW_HIDE_MEDIA_CONTROLS
    ContextMenuItem ToggleMediaControls(ActionType, ContextMenuItemTagToggleMediaControls,
        contextMenuItemTagHideMediaControls());
#else
776 777
    ContextMenuItem ToggleMediaControls(CheckableActionType, ContextMenuItemTagToggleMediaControls, 
        contextMenuItemTagToggleMediaControls());
778
#endif
779 780
    ContextMenuItem ToggleMediaLoop(CheckableActionType, ContextMenuItemTagToggleMediaLoop, 
        contextMenuItemTagToggleMediaLoop());
781 782 783
    ContextMenuItem EnterVideoFullscreen(ActionType, ContextMenuItemTagEnterVideoFullscreen,
        contextMenuItemTagEnterVideoFullscreen());
    ContextMenuItem ToggleVideoFullscreen(ActionType, ContextMenuItemTagToggleVideoFullscreen,
784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810
        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());
811
#endif
812
#if PLATFORM(GTK) || PLATFORM(QT) || PLATFORM(EFL)
813 814 815 816 817 818 819
    ContextMenuItem SelectAllItem(ActionType, ContextMenuItemTagSelectAll, contextMenuItemTagSelectAll());
#endif

    Node* node = m_hitTestResult.innerNonSharedNode();
    if (!node)
        return;
#if PLATFORM(GTK)
820
    if (!m_hitTestResult.isContentEditable() && (node->isElementNode() && toElement(node)->isFormControlElement()))
821 822 823 824 825 826 827
        return;
#endif
    Frame* frame = node->document()->frame();
    if (!frame)
        return;

    if (!m_hitTestResult.isContentEditable()) {
828
        FrameLoader& loader = frame->loader();
829 830
        KURL linkURL = m_hitTestResult.absoluteLinkURL();
        if (!linkURL.isEmpty()) {
831
            if (loader.client().canHandleRequest(ResourceRequest(linkURL))) {
832 833 834 835
                appendItem(OpenLinkItem, m_contextMenu.get());
                appendItem(OpenLinkInNewWindowItem, m_contextMenu.get());
                appendItem(DownloadFileItem, m_contextMenu.get());
            }
836 837 838 839
#if PLATFORM(QT)
            if (m_hitTestResult.isSelected()) 
                appendItem(CopyItem, m_contextMenu.get());
#endif
840 841 842 843 844 845 846 847 848 849 850 851
            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());
852
#if PLATFORM(QT) || PLATFORM(GTK) || PLATFORM(EFL)
853 854
            appendItem(CopyImageUrlItem, m_contextMenu.get());
#endif
855 856 857 858 859 860 861 862 863 864 865
        }

        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());
866 867 868
#if SUPPORTS_TOGGLE_VIDEO_FULLSCREEN
            appendItem(ToggleVideoFullscreen, m_contextMenu.get());
#else
869
            appendItem(EnterVideoFullscreen, m_contextMenu.get());
870
#endif
871 872 873
            appendItem(*separatorItem(), m_contextMenu.get());
            appendItem(CopyMediaLinkItem, m_contextMenu.get());
            appendItem(OpenMediaInNewWindowItem, m_contextMenu.get());
874
            if (loader.client().canHandleRequest(ResourceRequest(mediaURL)))
875
                appendItem(DownloadMediaItem, m_contextMenu.get());
876 877 878 879 880 881
        }

        if (imageURL.isEmpty() && linkURL.isEmpty() && mediaURL.isEmpty()) {
            if (m_hitTestResult.isSelected()) {
                if (selectionContainsPossibleWord(frame)) {
#if PLATFORM(MAC)
882
                    String selectedString = frame->displayStringModifiedByEncoding(frame->editor().selectedText());
883 884 885 886 887
                    ContextMenuItem LookUpInDictionaryItem(ActionType, ContextMenuItemTagLookUpInDictionary, contextMenuItemTagLookUpInDictionary(selectedString));

                    appendItem(LookUpInDictionaryItem, m_contextMenu.get());
#endif

888 889 890 891 892
#if !PLATFORM(GTK)
                    appendItem(SearchWebItem, m_contextMenu.get());
                    appendItem(*separatorItem(), m_contextMenu.get());
#endif
                }
893

894 895 896
                appendItem(CopyItem, m_contextMenu.get());
#if PLATFORM(MAC)
                appendItem(*separatorItem(), m_contextMenu.get());
897

898 899 900 901 902 903 904 905
                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
906

907 908
                // In GTK+ unavailable items are not hidden but insensitive.
#if PLATFORM(GTK)
909 910 911 912 913
                appendItem(BackItem, m_contextMenu.get());
                appendItem(ForwardItem, m_contextMenu.get());
                appendItem(StopItem, m_contextMenu.get());
                appendItem(ReloadItem, m_contextMenu.get());
#else
914 915 916 917 918 919 920 921
                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
922
                if (loader.documentLoader()->isLoadingInAPISense())
923 924 925
                    appendItem(StopItem, m_contextMenu.get());
                else
                    appendItem(ReloadItem, m_contextMenu.get());
926
#endif
927 928 929 930
#if ENABLE(INSPECTOR)
                }
#endif

931
                if (frame->page() && frame != &frame->page()->mainFrame())
932 933 934 935
                    appendItem(OpenFrameItem, m_contextMenu.get());
            }
        }
    } else { // Make an editing context menu
936
        bool inPasswordField = frame->selection().isInPasswordField();
937 938
        if (!inPasswordField) {
            bool haveContextMenuItemsForMisspellingOrGrammer = false;
939
            bool spellCheckingEnabled = frame->editor().isSpellCheckingEnabledFor(node);
940 941 942 943 944
            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;
945
                Vector<String> guesses = frame->editor().guessesForMisspelledOrUngrammatical(misspelling, badGrammar);
946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962
                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());
                            }
                        }
963 964
                        appendItem(*separatorItem(), m_contextMenu.get());
                    }
965 966 967
                    if (misspelling) {
                        appendItem(IgnoreSpellingItem, m_contextMenu.get());
                        appendItem(LearnSpellingItem, m_contextMenu.get());
968
                    } else
969 970 971
                        appendItem(IgnoreGrammarItem, m_contextMenu.get());
                    appendItem(*separatorItem(), m_contextMenu.get());
                    haveContextMenuItemsForMisspellingOrGrammer = true;
972
#if PLATFORM(MAC)
973
                } else {
974 975 976 977 978 979 980
                    // 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;
981
                    }
982
#endif
983
                }
984 985 986 987 988 989 990 991 992 993
            }

            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());
                    }
994 995 996 997 998
                    appendItem(*separatorItem(), m_contextMenu.get());
                }
            }
        }

999
        FrameLoader& loader = frame->loader();
1000 1001
        KURL linkURL = m_hitTestResult.absoluteLinkURL();
        if (!linkURL.isEmpty()) {
1002
            if (loader.client().canHandleRequest(ResourceRequest(linkURL))) {
1003 1004 1005 1006 1007 1008 1009 1010 1011 1012
                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)
1013
            String selectedString = frame->displayStringModifiedByEncoding(frame->editor().selectedText());
1014 1015 1016 1017 1018
            ContextMenuItem LookUpInDictionaryItem(ActionType, ContextMenuItemTagLookUpInDictionary, contextMenuItemTagLookUpInDictionary(selectedString));

            appendItem(LookUpInDictionaryItem, m_contextMenu.get());
#endif

1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030
#if !PLATFORM(GTK)
            appendItem(SearchWebItem, m_contextMenu.get());
            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());
1031
#endif
1032
#if PLATFORM(GTK) || PLATFORM(QT) || PLATFORM(EFL)
1033 1034 1035 1036 1037
        appendItem(SelectAllItem, m_contextMenu.get());
#endif

        if (!inPasswordField) {
#if !PLATFORM(GTK)
1038
            appendItem(*separatorItem(), m_contextMenu.get());
1039 1040 1041 1042 1043
            ContextMenuItem SpellingAndGrammarMenuItem(SubmenuType, ContextMenuItemTagSpellingMenu, 
                contextMenuItemTagSpellingMenu());
            createAndAppendSpellingAndGrammarSubMenu(SpellingAndGrammarMenuItem);
            appendItem(SpellingAndGrammarMenuItem, m_contextMenu.get());
#endif
1044
#if PLATFORM(MAC)
1045 1046 1047 1048 1049 1050 1051 1052 1053 1054
            ContextMenuItem substitutionsMenuItem(SubmenuType, ContextMenuItemTagSubstitutionsMenu, 
                contextMenuItemTagSubstitutionsMenu());
            createAndAppendSubstitutionsSubMenu(substitutionsMenuItem);
            appendItem(substitutionsMenuItem, m_contextMenu.get());
            ContextMenuItem transformationsMenuItem(SubmenuType, ContextMenuItemTagTransformationsMenu, 
                contextMenuItemTagTransformationsMenu());
            createAndAppendTransformationsSubMenu(transformationsMenuItem);
            appendItem(transformationsMenuItem, m_contextMenu.get());
#endif
#if PLATFORM(GTK)
1055
            bool shouldShowFontMenu = frame->editor().canEditRichly();
1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066