| /* |
| * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. |
| * |
| * 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" |
| |
| #include "Chrome.h" |
| #include "ContextMenu.h" |
| #include "ContextMenuClient.h" |
| #include "Document.h" |
| #include "DocumentFragment.h" |
| #include "DocumentLoader.h" |
| #include "Editor.h" |
| #include "EditorClient.h" |
| #include "Event.h" |
| #include "EventHandler.h" |
| #include "EventNames.h" |
| #include "FormState.h" |
| #include "Frame.h" |
| #include "FrameLoader.h" |
| #include "FrameLoadRequest.h" |
| #include "HitTestRequest.h" |
| #include "HitTestResult.h" |
| #include "HTMLFormElement.h" |
| #include "InspectorController.h" |
| #include "MouseEvent.h" |
| #include "Node.h" |
| #include "Page.h" |
| #include "RenderLayer.h" |
| #include "RenderObject.h" |
| #include "ReplaceSelectionCommand.h" |
| #include "ResourceRequest.h" |
| #include "SelectionController.h" |
| #include "Settings.h" |
| #include "TextIterator.h" |
| #include "WindowFeatures.h" |
| #include "markup.h" |
| |
| namespace WebCore { |
| |
| ContextMenuController::ContextMenuController(Page* page, ContextMenuClient* client) |
| : m_page(page) |
| , m_client(client) |
| , m_contextMenu(0) |
| { |
| ASSERT_ARG(page, page); |
| ASSERT_ARG(client, client); |
| } |
| |
| ContextMenuController::~ContextMenuController() |
| { |
| m_client->contextMenuDestroyed(); |
| } |
| |
| void ContextMenuController::clearContextMenu() |
| { |
| m_contextMenu.set(0); |
| } |
| |
| void ContextMenuController::handleContextMenuEvent(Event* event) |
| { |
| ASSERT(event->type() == eventNames().contextmenuEvent); |
| if (!event->isMouseEvent()) |
| return; |
| MouseEvent* mouseEvent = static_cast<MouseEvent*>(event); |
| HitTestResult result(mouseEvent->absoluteLocation()); |
| |
| if (Frame* frame = event->target()->toNode()->document()->frame()) |
| result = frame->eventHandler()->hitTestResultAtPoint(mouseEvent->absoluteLocation(), false); |
| |
| if (!result.innerNonSharedNode()) |
| return; |
| |
| m_contextMenu.set(new ContextMenu(result)); |
| m_contextMenu->populate(); |
| if (m_page->inspectorController()->enabled()) |
| m_contextMenu->addInspectElementItem(); |
| |
| PlatformMenuDescription customMenu = m_client->getCustomMenuFromDefaultItems(m_contextMenu.get()); |
| m_contextMenu->setPlatformDescription(customMenu); |
| |
| event->setDefaultHandled(); |
| } |
| |
| static void openNewWindow(const KURL& urlToLoad, Frame* frame) |
| { |
| if (Page* oldPage = frame->page()) { |
| WindowFeatures features; |
| if (Page* newPage = oldPage->chrome()->createWindow(frame, |
| FrameLoadRequest(ResourceRequest(urlToLoad, frame->loader()->outgoingReferrer())), features)) |
| newPage->chrome()->show(); |
| } |
| } |
| |
| void ContextMenuController::contextMenuItemSelected(ContextMenuItem* item) |
| { |
| ASSERT(item->type() == ActionType || item->type() == CheckableActionType); |
| |
| if (item->action() >= ContextMenuItemBaseApplicationTag) { |
| m_client->contextMenuItemSelected(item, m_contextMenu.get()); |
| return; |
| } |
| |
| HitTestResult result = m_contextMenu->hitTestResult(); |
| Frame* frame = result.innerNonSharedNode()->document()->frame(); |
| if (!frame) |
| return; |
| |
| switch (item->action()) { |
| case ContextMenuItemTagOpenLinkInNewWindow: |
| openNewWindow(result.absoluteLinkURL(), frame); |
| break; |
| case ContextMenuItemTagDownloadLinkToDisk: |
| // FIXME: Some day we should be able to do this from within WebCore. |
| m_client->downloadURL(result.absoluteLinkURL()); |
| break; |
| case ContextMenuItemTagCopyLinkToClipboard: |
| frame->editor()->copyURL(result.absoluteLinkURL(), result.textContent()); |
| break; |
| case ContextMenuItemTagOpenImageInNewWindow: |
| openNewWindow(result.absoluteImageURL(), frame); |
| break; |
| case ContextMenuItemTagDownloadImageToDisk: |
| // FIXME: Some day we should be able to do this from within WebCore. |
| m_client->downloadURL(result.absoluteImageURL()); |
| break; |
| case ContextMenuItemTagCopyImageToClipboard: |
| // FIXME: The Pasteboard class is not written yet |
| // For now, call into the client. This is temporary! |
| frame->editor()->copyImage(result); |
| break; |
| case ContextMenuItemTagOpenFrameInNewWindow: { |
| DocumentLoader* loader = frame->loader()->documentLoader(); |
| if (!loader->unreachableURL().isEmpty()) |
| openNewWindow(loader->unreachableURL(), frame); |
| else |
| openNewWindow(loader->url(), frame); |
| break; |
| } |
| case ContextMenuItemTagCopy: |
| frame->editor()->copy(); |
| break; |
| case ContextMenuItemTagGoBack: |
| frame->loader()->goBackOrForward(-1); |
| break; |
| case ContextMenuItemTagGoForward: |
| frame->loader()->goBackOrForward(1); |
| break; |
| case ContextMenuItemTagStop: |
| frame->loader()->stop(); |
| break; |
| case ContextMenuItemTagReload: |
| frame->loader()->reload(); |
| break; |
| case ContextMenuItemTagCut: |
| frame->editor()->cut(); |
| break; |
| case ContextMenuItemTagPaste: |
| frame->editor()->paste(); |
| break; |
| #if PLATFORM(GTK) |
| case ContextMenuItemTagDelete: |
| frame->editor()->performDelete(); |
| break; |
| case ContextMenuItemTagSelectAll: |
| frame->editor()->command("SelectAll").execute(); |
| break; |
| #endif |
| case ContextMenuItemTagSpellingGuess: |
| ASSERT(frame->selectedText().length()); |
| if (frame->editor()->shouldInsertText(item->title(), frame->selection()->toNormalizedRange().get(), |
| EditorInsertActionPasted)) { |
| Document* document = frame->document(); |
| RefPtr<ReplaceSelectionCommand> command = |
| ReplaceSelectionCommand::create(document, createFragmentFromMarkup(document, item->title(), ""), |
| true, false, true); |
| applyCommand(command); |
| frame->revealSelection(ScrollAlignment::alignToEdgeIfNeeded); |
| } |
| break; |
| case ContextMenuItemTagIgnoreSpelling: |
| frame->editor()->ignoreSpelling(); |
| break; |
| case ContextMenuItemTagLearnSpelling: |
| frame->editor()->learnSpelling(); |
| 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: |
| if (Frame* targetFrame = result.targetFrame()) { |
| targetFrame->loader()->loadFrameRequest(FrameLoadRequest(ResourceRequest(result.absoluteLinkURL(), |
| frame->loader()->outgoingReferrer())), false, false, 0, 0); |
| } else |
| openNewWindow(result.absoluteLinkURL(), frame); |
| break; |
| case ContextMenuItemTagBold: |
| frame->editor()->command("ToggleBold").execute(); |
| break; |
| case ContextMenuItemTagItalic: |
| frame->editor()->command("ToggleItalic").execute(); |
| break; |
| case ContextMenuItemTagUnderline: |
| frame->editor()->toggleUnderline(); |
| 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: { |
| ExceptionCode ec; |
| RefPtr<Range> selectedRange = frame->selection()->toNormalizedRange(); |
| if (!selectedRange || selectedRange->collapsed(ec)) { |
| Document* document = result.innerNonSharedNode()->document(); |
| selectedRange = document->createRange(); |
| selectedRange->selectNode(document->documentElement(), ec); |
| } |
| m_client->speak(plainText(selectedRange.get())); |
| break; |
| } |
| case ContextMenuItemTagStopSpeaking: |
| m_client->stopSpeaking(); |
| break; |
| case ContextMenuItemTagDefaultDirection: |
| frame->editor()->setBaseWritingDirection(NaturalWritingDirection); |
| break; |
| case ContextMenuItemTagLeftToRight: |
| frame->editor()->setBaseWritingDirection(LeftToRightWritingDirection); |
| break; |
| case ContextMenuItemTagRightToLeft: |
| frame->editor()->setBaseWritingDirection(RightToLeftWritingDirection); |
| break; |
| case ContextMenuItemTagTextDirectionDefault: |
| frame->editor()->command("MakeTextWritingDirectionNatural").execute(); |
| break; |
| case ContextMenuItemTagTextDirectionLeftToRight: |
| frame->editor()->command("MakeTextWritingDirectionLeftToRight").execute(); |
| break; |
| case ContextMenuItemTagTextDirectionRightToLeft: |
| frame->editor()->command("MakeTextWritingDirectionRightToLeft").execute(); |
| break; |
| #if PLATFORM(MAC) |
| case ContextMenuItemTagSearchInSpotlight: |
| m_client->searchWithSpotlight(); |
| break; |
| #endif |
| case ContextMenuItemTagShowSpellingPanel: |
| frame->editor()->showSpellingGuessPanel(); |
| break; |
| case ContextMenuItemTagCheckSpelling: |
| frame->editor()->advanceToNextMisspelling(); |
| break; |
| case ContextMenuItemTagCheckSpellingWhileTyping: |
| frame->editor()->toggleContinuousSpellChecking(); |
| break; |
| #ifndef BUILDING_ON_TIGER |
| case ContextMenuItemTagCheckGrammarWithSpelling: |
| frame->editor()->toggleGrammarChecking(); |
| break; |
| #endif |
| #if PLATFORM(MAC) |
| case ContextMenuItemTagShowFonts: |
| frame->editor()->showFontPanel(); |
| break; |
| case ContextMenuItemTagStyles: |
| frame->editor()->showStylesPanel(); |
| break; |
| case ContextMenuItemTagShowColors: |
| frame->editor()->showColorPanel(); |
| break; |
| #endif |
| #if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) |
| case ContextMenuItemTagMakeUpperCase: |
| frame->editor()->uppercaseWord(); |
| break; |
| case ContextMenuItemTagMakeLowerCase: |
| frame->editor()->lowercaseWord(); |
| break; |
| case ContextMenuItemTagCapitalize: |
| frame->editor()->capitalizeWord(); |
| break; |
| case ContextMenuItemTagShowSubstitutions: |
| frame->editor()->showSubstitutionsPanel(); |
| break; |
| case ContextMenuItemTagSmartCopyPaste: |
| frame->editor()->toggleSmartInsertDelete(); |
| break; |
| case ContextMenuItemTagSmartQuotes: |
| frame->editor()->toggleAutomaticQuoteSubstitution(); |
| break; |
| case ContextMenuItemTagSmartDashes: |
| frame->editor()->toggleAutomaticDashSubstitution(); |
| break; |
| case ContextMenuItemTagSmartLinks: |
| frame->editor()->toggleAutomaticLinkDetection(); |
| break; |
| case ContextMenuItemTagTextReplacement: |
| frame->editor()->toggleAutomaticTextReplacement(); |
| break; |
| case ContextMenuItemTagCorrectSpellingAutomatically: |
| frame->editor()->toggleAutomaticSpellingCorrection(); |
| break; |
| case ContextMenuItemTagChangeBack: |
| frame->editor()->changeBackToReplacedString(result.replacedString()); |
| break; |
| #endif |
| case ContextMenuItemTagInspectElement: |
| if (Page* page = frame->page()) |
| page->inspectorController()->inspect(result.innerNonSharedNode()); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| } // namespace WebCore |