ContextMenuController.cpp 59.1 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
#include "FrameLoadRequest.h"
49
#include "FrameLoader.h"
50
#include "FrameLoaderClient.h"
51
#include "FrameSelection.h"
52
#include "HTMLFormElement.h"
aroben's avatar
aroben committed
53 54
#include "HitTestRequest.h"
#include "HitTestResult.h"
55
#include "InspectorController.h"
56
#include "LocalizedStrings.h"
darin@apple.com's avatar
darin@apple.com committed
57
#include "MainFrame.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"
bdakin's avatar
bdakin committed
63 64
#include "ReplaceSelectionCommand.h"
#include "ResourceRequest.h"
65
#include "Settings.h"
darin's avatar
darin committed
66
#include "TextIterator.h"
67
#include "TypingCommand.h"
68
#include "UserTypingGestureIndicator.h"
tristan's avatar
qt:  
tristan committed
69
#include "WindowFeatures.h"
bdakin's avatar
bdakin committed
70
#include "markup.h"
71
#include <wtf/unicode/CharacterNames.h>
72 73
#include <wtf/unicode/Unicode.h>

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

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

namespace WebCore {

83
ContextMenuController::ContextMenuController(Page& page, ContextMenuClient& client)
84 85
    : m_page(page)
    , m_client(client)
aroben's avatar
aroben committed
86 87 88 89 90
{
}

ContextMenuController::~ContextMenuController()
{
91
    m_client.contextMenuDestroyed();
92 93
}

bdakin's avatar
bdakin committed
94 95
void ContextMenuController::clearContextMenu()
{
96
    m_contextMenu.clear();
97 98 99
    if (m_menuProvider)
        m_menuProvider->contextMenuCleared();
    m_menuProvider = 0;
bdakin's avatar
bdakin committed
100 101
}

aroben's avatar
aroben committed
102 103
void ContextMenuController::handleContextMenuEvent(Event* event)
{
104
    m_contextMenu = createContextMenu(event);
105
    if (!m_contextMenu)
adele's avatar
adele committed
106
        return;
107 108 109

    populate();

110 111 112
    showContextMenu(event);
}

113 114 115 116 117
static PassOwnPtr<ContextMenuItem> separatorItem()
{
    return adoptPtr(new ContextMenuItem(SeparatorType, ContextMenuItemTagNoAction, String()));
}

118
void ContextMenuController::showContextMenu(Event* event, PassRefPtr<ContextMenuProvider> menuProvider)
119
{
120
    m_menuProvider = menuProvider;
121

122
    m_contextMenu = createContextMenu(event);
123 124 125 126
    if (!m_contextMenu) {
        clearContextMenu();
        return;
    }
127 128

    m_menuProvider->populateContextMenu(m_contextMenu.get());
129 130 131 132
    if (m_hitTestResult.isSelected()) {
        appendItem(*separatorItem(), m_contextMenu.get());
        populate();
    }
133 134 135
    showContextMenu(event);
}

136
PassOwnPtr<ContextMenu> ContextMenuController::createContextMenu(Event* event)
137
{
138 139
    ASSERT(event);
    
140
    if (!event->isMouseEvent())
141
        return nullptr;
142

aroben's avatar
aroben committed
143
    MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
144
    HitTestResult result(mouseEvent->absoluteLocation());
aroben's avatar
aroben committed
145

146
    if (Frame* frame = event->target()->toNode()->document().frame())
147
        result = frame->eventHandler().hitTestResultAtPoint(mouseEvent->absoluteLocation());
148

bdakin's avatar
bdakin committed
149
    if (!result.innerNonSharedNode())
150
        return nullptr;
151 152 153

    m_hitTestResult = result;

154
    return adoptPtr(new ContextMenu);
155
}
aroben's avatar
aroben committed
156

157 158
void ContextMenuController::showContextMenu(Event* event)
{
bolsinga@apple.com's avatar
bolsinga@apple.com committed
159
#if ENABLE(INSPECTOR)
160
    if (m_page.inspectorController()->enabled())
161
        addInspectElementItem();
bolsinga@apple.com's avatar
bolsinga@apple.com committed
162
#endif
163 164

#if USE(CROSS_PLATFORM_CONTEXT_MENUS)
165
    m_contextMenu = m_client.customizeMenu(m_contextMenu.release());
166
#else
167
    PlatformMenuDescription customMenu = m_client.getCustomMenuFromDefaultItems(m_contextMenu.get());
bdakin's avatar
bdakin committed
168
    m_contextMenu->setPlatformDescription(customMenu);
169
#endif
aroben's avatar
aroben committed
170
    event->setDefaultHandled();
aroben's avatar
aroben committed
171 172
}

darin@apple.com's avatar
darin@apple.com committed
173
static void openNewWindow(const URL& urlToLoad, Frame* frame)
bdakin's avatar
bdakin committed
174
{
tristan's avatar
qt:  
tristan committed
175
    if (Page* oldPage = frame->page()) {
176
        FrameLoadRequest request(frame->document()->securityOrigin(), ResourceRequest(urlToLoad, frame->loader().outgoingReferrer()));
177
        Page* newPage = oldPage;
178
        if (frame->settings().supportsMultipleWindows()) {
179
            newPage = oldPage->chrome().createWindow(frame, request, WindowFeatures(), NavigationAction(request.resourceRequest()));
180 181
            if (!newPage)
                return;
182
            newPage->chrome().show();
183
        }
184
        newPage->mainFrame().loader().loadFrameRequest(request, false, false, 0, 0, MaybeSendReferrer);
tristan's avatar
qt:  
tristan committed
185
    }
bdakin's avatar
bdakin committed
186 187
}

188 189 190 191
#if PLATFORM(GTK)
static void insertUnicodeCharacter(UChar character, Frame* frame)
{
    String text(&character, 1);
192
    if (!frame->editor().shouldInsertText(text, frame->selection().toNormalizedRange().get(), EditorInsertActionTyped))
193 194 195 196 197 198
        return;

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

aroben's avatar
aroben committed
199
void ContextMenuController::contextMenuItemSelected(ContextMenuItem* item)
bdakin's avatar
bdakin committed
200
{
201
    ASSERT(item->type() == ActionType || item->type() == CheckableActionType);
aroben's avatar
aroben committed
202 203

    if (item->action() >= ContextMenuItemBaseApplicationTag) {
204
        m_client.contextMenuItemSelected(item, m_contextMenu.get());
aroben's avatar
aroben committed
205 206 207
        return;
    }

208
    if (item->action() >= ContextMenuItemBaseCustomTag) {
209 210
        ASSERT(m_menuProvider);
        m_menuProvider->contextMenuItemSelected(item);
211 212 213
        return;
    }

214
    Frame* frame = m_hitTestResult.innerNonSharedNode()->document().frame();
bdakin's avatar
bdakin committed
215 216
    if (!frame)
        return;
217

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

350
            if (frame->editor().behavior().shouldAllowSpellingSuggestionsWithoutSelection()) {
351 352
                ASSERT(frameSelection.isCaretOrRange());
                VisibleSelection wordSelection(frameSelection.base());
353
                wordSelection.expandUsingGranularity(WordGranularity);
354
                frameSelection.setSelection(wordSelection);
355
            } else {
356
                ASSERT(frame->editor().selectedText().length());
357 358 359
                replaceOptions |= ReplaceSelectionCommand::SelectReplacement;
            }

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

515 516 517 518 519 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
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());
569
#if PLATFORM(MAC)
570 571 572 573 574 575
    ContextMenuItem correctSpelling(CheckableActionType, ContextMenuItemTagCorrectSpellingAutomatically, 
        contextMenuItemTagCorrectSpellingAutomatically());
#endif

    appendItem(showSpellingPanel, &spellingAndGrammarMenu);
    appendItem(checkSpelling, &spellingAndGrammarMenu);
576
#if PLATFORM(MAC)
577 578 579 580
    appendItem(*separatorItem(), &spellingAndGrammarMenu);
#endif
    appendItem(checkAsYouType, &spellingAndGrammarMenu);
    appendItem(grammarWithSpelling, &spellingAndGrammarMenu);
581
#if PLATFORM(MAC)
582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607
    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
 
608 609 610 611 612 613 614 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
#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
640 641 642 643 644 645 646 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

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

674
#if PLATFORM(MAC)
675 676 677 678 679 680 681 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

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.
718
    for (TextIterator it(frame->selection().toNormalizedRange().get()); !it.atEnd(); it.advance()) {
719 720
        int length = it.length();
        for (int i = 0; i < length; ++i)
721
            if (!(category(it.characterAt(i)) & (Separator_Space | Separator_Line | Separator_Paragraph)))
722 723 724 725 726
                return true;
    }
    return false;
}

727 728 729 730 731 732
#if PLATFORM(MAC)
#define SUPPORTS_TOGGLE_VIDEO_FULLSCREEN 1
#else
#define SUPPORTS_TOGGLE_VIDEO_FULLSCREEN 0
#endif

733 734 735 736 737 738
#if PLATFORM(MAC)
#define SUPPORTS_TOGGLE_SHOW_HIDE_MEDIA_CONTROLS 1
#else
#define SUPPORTS_TOGGLE_SHOW_HIDE_MEDIA_CONTROLS 0
#endif

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

    Node* node = m_hitTestResult.innerNonSharedNode();
    if (!node)
        return;
#if PLATFORM(GTK)
813
    if (!m_hitTestResult.isContentEditable() && (node->isElementNode() && toElement(node)->isFormControlElement()))
814 815
        return;
#endif
816
    Frame* frame = node->document().frame();
817 818 819 820
    if (!frame)
        return;

    if (!m_hitTestResult.isContentEditable()) {
821
        FrameLoader& loader = frame->loader();
darin@apple.com's avatar
darin@apple.com committed
822
        URL linkURL = m_hitTestResult.absoluteLinkURL();
823
        if (!linkURL.isEmpty()) {
824
            if (loader.client().canHandleRequest(ResourceRequest(linkURL))) {
825 826 827 828 829 830 831
                appendItem(OpenLinkItem, m_contextMenu.get());
                appendItem(OpenLinkInNewWindowItem, m_contextMenu.get());
                appendItem(DownloadFileItem, m_contextMenu.get());
            }
            appendItem(CopyLinkItem, m_contextMenu.get());
        }

darin@apple.com's avatar
darin@apple.com committed
832
        URL imageURL = m_hitTestResult.absoluteImageURL();
833 834 835 836 837 838 839 840
        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());
841
#if PLATFORM(GTK) || PLATFORM(EFL)
842 843
            appendItem(CopyImageUrlItem, m_contextMenu.get());
#endif
844 845
        }

darin@apple.com's avatar
darin@apple.com committed
846
        URL mediaURL = m_hitTestResult.absoluteMediaURL();
847 848 849 850 851 852 853 854
        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());
855 856 857
#if SUPPORTS_TOGGLE_VIDEO_FULLSCREEN
            appendItem(ToggleVideoFullscreen, m_contextMenu.get());
#else
858
            appendItem(EnterVideoFullscreen, m_contextMenu.get());
859
#endif
860 861 862
            appendItem(*separatorItem(), m_contextMenu.get());
            appendItem(CopyMediaLinkItem, m_contextMenu.get());
            appendItem(OpenMediaInNewWindowItem, m_contextMenu.get());
863
            if (loader.client().canHandleRequest(ResourceRequest(mediaURL)))
864
                appendItem(DownloadMediaItem, m_contextMenu.get());
865 866 867 868 869 870
        }

        if (imageURL.isEmpty() && linkURL.isEmpty() && mediaURL.isEmpty()) {
            if (m_hitTestResult.isSelected()) {
                if (selectionContainsPossibleWord(frame)) {
#if PLATFORM(MAC)
871
                    String selectedString = frame->displayStringModifiedByEncoding(frame->editor().selectedText());
872 873 874 875 876
                    ContextMenuItem LookUpInDictionaryItem(ActionType, ContextMenuItemTagLookUpInDictionary, contextMenuItemTagLookUpInDictionary(selectedString));

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

877 878 879 880 881
#if !PLATFORM(GTK)
                    appendItem(SearchWebItem, m_contextMenu.get());
                    appendItem(*separatorItem(), m_contextMenu.get());
#endif
                }
882

883 884 885
                appendItem(CopyItem, m_contextMenu.get());
#if PLATFORM(MAC)
                appendItem(*separatorItem(), m_contextMenu.get());
886

887 888 889 890 891 892 893 894
                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
895

896 897
                // In GTK+ unavailable items are not hidden but insensitive.
#if PLATFORM(GTK)