ContextMenuController.cpp revision 8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2
1/* 2 * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27#include "ContextMenuController.h" 28 29#include "Chrome.h" 30#include "ContextMenu.h" 31#include "ContextMenuClient.h" 32#include "Document.h" 33#include "DocumentFragment.h" 34#include "DocumentLoader.h" 35#include "Editor.h" 36#include "EditorClient.h" 37#include "Event.h" 38#include "EventHandler.h" 39#include "EventNames.h" 40#include "Frame.h" 41#include "FrameLoader.h" 42#include "FrameLoadRequest.h" 43#include "HitTestRequest.h" 44#include "HitTestResult.h" 45#include "InspectorController.h" 46#include "MouseEvent.h" 47#include "Node.h" 48#include "Page.h" 49#include "RenderLayer.h" 50#include "RenderObject.h" 51#include "ReplaceSelectionCommand.h" 52#include "ResourceRequest.h" 53#include "SelectionController.h" 54#include "Settings.h" 55#include "TextIterator.h" 56#include "WindowFeatures.h" 57#include "markup.h" 58 59namespace WebCore { 60 61ContextMenuController::ContextMenuController(Page* page, ContextMenuClient* client) 62 : m_page(page) 63 , m_client(client) 64 , m_contextMenu(0) 65{ 66 ASSERT_ARG(page, page); 67 ASSERT_ARG(client, client); 68} 69 70ContextMenuController::~ContextMenuController() 71{ 72 m_client->contextMenuDestroyed(); 73} 74 75void ContextMenuController::clearContextMenu() 76{ 77 m_contextMenu.set(0); 78} 79 80void ContextMenuController::handleContextMenuEvent(Event* event) 81{ 82 ASSERT(event->type() == eventNames().contextmenuEvent); 83 if (!event->isMouseEvent()) 84 return; 85 MouseEvent* mouseEvent = static_cast<MouseEvent*>(event); 86 IntPoint point = IntPoint(mouseEvent->pageX(), mouseEvent->pageY()); 87 HitTestResult result(point); 88 89 if (Frame* frame = event->target()->toNode()->document()->frame()) 90 result = frame->eventHandler()->hitTestResultAtPoint(point, false); 91 92 if (!result.innerNonSharedNode()) 93 return; 94 95 m_contextMenu.set(new ContextMenu(result)); 96 m_contextMenu->populate(); 97 if (m_page->inspectorController()->enabled()) 98 m_contextMenu->addInspectElementItem(); 99 100 PlatformMenuDescription customMenu = m_client->getCustomMenuFromDefaultItems(m_contextMenu.get()); 101 m_contextMenu->setPlatformDescription(customMenu); 102 103 event->setDefaultHandled(); 104} 105 106static void openNewWindow(const KURL& urlToLoad, Frame* frame) 107{ 108 if (Page* oldPage = frame->page()) { 109 WindowFeatures features; 110 if (Page* newPage = oldPage->chrome()->createWindow(frame, 111 FrameLoadRequest(ResourceRequest(urlToLoad, frame->loader()->outgoingReferrer())), features)) 112 newPage->chrome()->show(); 113 } 114} 115 116void ContextMenuController::contextMenuItemSelected(ContextMenuItem* item) 117{ 118 ASSERT(item->type() == ActionType || item->type() == CheckableActionType); 119 120 if (item->action() >= ContextMenuItemBaseApplicationTag) { 121 m_client->contextMenuItemSelected(item, m_contextMenu.get()); 122 return; 123 } 124 125 HitTestResult result = m_contextMenu->hitTestResult(); 126 Frame* frame = result.innerNonSharedNode()->document()->frame(); 127 if (!frame) 128 return; 129 130 switch (item->action()) { 131 case ContextMenuItemTagOpenLinkInNewWindow: 132 openNewWindow(result.absoluteLinkURL(), frame); 133 break; 134 case ContextMenuItemTagDownloadLinkToDisk: 135 // FIXME: Some day we should be able to do this from within WebCore. 136 m_client->downloadURL(result.absoluteLinkURL()); 137 break; 138 case ContextMenuItemTagCopyLinkToClipboard: 139 frame->editor()->copyURL(result.absoluteLinkURL(), result.textContent()); 140 break; 141 case ContextMenuItemTagOpenImageInNewWindow: 142 openNewWindow(result.absoluteImageURL(), frame); 143 break; 144 case ContextMenuItemTagDownloadImageToDisk: 145 // FIXME: Some day we should be able to do this from within WebCore. 146 m_client->downloadURL(result.absoluteImageURL()); 147 break; 148 case ContextMenuItemTagCopyImageToClipboard: 149 // FIXME: The Pasteboard class is not written yet 150 // For now, call into the client. This is temporary! 151 frame->editor()->copyImage(result); 152 break; 153 case ContextMenuItemTagOpenFrameInNewWindow: { 154 DocumentLoader* loader = frame->loader()->documentLoader(); 155 if (!loader->unreachableURL().isEmpty()) 156 openNewWindow(loader->unreachableURL(), frame); 157 else 158 openNewWindow(loader->url(), frame); 159 break; 160 } 161 case ContextMenuItemTagCopy: 162 frame->editor()->copy(); 163 break; 164 case ContextMenuItemTagGoBack: 165 frame->loader()->goBackOrForward(-1); 166 break; 167 case ContextMenuItemTagGoForward: 168 frame->loader()->goBackOrForward(1); 169 break; 170 case ContextMenuItemTagStop: 171 frame->loader()->stop(); 172 break; 173 case ContextMenuItemTagReload: 174 frame->loader()->reload(); 175 break; 176 case ContextMenuItemTagCut: 177 frame->editor()->cut(); 178 break; 179 case ContextMenuItemTagPaste: 180 frame->editor()->paste(); 181 break; 182#if PLATFORM(GTK) 183 case ContextMenuItemTagDelete: 184 frame->editor()->performDelete(); 185 break; 186 case ContextMenuItemTagSelectAll: 187 frame->editor()->command("SelectAll").execute(); 188 break; 189#endif 190 case ContextMenuItemTagSpellingGuess: 191 ASSERT(frame->selectedText().length()); 192 if (frame->editor()->shouldInsertText(item->title(), frame->selection()->toRange().get(), 193 EditorInsertActionPasted)) { 194 Document* document = frame->document(); 195 RefPtr<ReplaceSelectionCommand> command = 196 ReplaceSelectionCommand::create(document, createFragmentFromMarkup(document, item->title(), ""), 197 true, false, true); 198 applyCommand(command); 199 frame->revealSelection(RenderLayer::gAlignToEdgeIfNeeded); 200 } 201 break; 202 case ContextMenuItemTagIgnoreSpelling: 203 frame->editor()->ignoreSpelling(); 204 break; 205 case ContextMenuItemTagLearnSpelling: 206 frame->editor()->learnSpelling(); 207 break; 208 case ContextMenuItemTagSearchWeb: 209 m_client->searchWithGoogle(frame); 210 break; 211 case ContextMenuItemTagLookUpInDictionary: 212 // FIXME: Some day we may be able to do this from within WebCore. 213 m_client->lookUpInDictionary(frame); 214 break; 215 case ContextMenuItemTagOpenLink: 216 if (Frame* targetFrame = result.targetFrame()) 217 targetFrame->loader()->loadFrameRequestWithFormAndValues(FrameLoadRequest(ResourceRequest(result.absoluteLinkURL(), 218 frame->loader()->outgoingReferrer())), false, 0, 0, HashMap<String, String>()); 219 else 220 openNewWindow(result.absoluteLinkURL(), frame); 221 break; 222 case ContextMenuItemTagBold: 223 frame->editor()->command("ToggleBold").execute(); 224 break; 225 case ContextMenuItemTagItalic: 226 frame->editor()->command("ToggleItalic").execute(); 227 break; 228 case ContextMenuItemTagUnderline: 229 frame->editor()->toggleUnderline(); 230 break; 231 case ContextMenuItemTagOutline: 232 // We actually never enable this because CSS does not have a way to specify an outline font, 233 // which may make this difficult to implement. Maybe a special case of text-shadow? 234 break; 235 case ContextMenuItemTagStartSpeaking: { 236 ExceptionCode ec; 237 RefPtr<Range> selectedRange = frame->selection()->toRange(); 238 if (!selectedRange || selectedRange->collapsed(ec)) { 239 Document* document = result.innerNonSharedNode()->document(); 240 selectedRange = document->createRange(); 241 selectedRange->selectNode(document->documentElement(), ec); 242 } 243 m_client->speak(plainText(selectedRange.get())); 244 break; 245 } 246 case ContextMenuItemTagStopSpeaking: 247 m_client->stopSpeaking(); 248 break; 249 case ContextMenuItemTagDefaultDirection: 250 frame->editor()->setBaseWritingDirection(NaturalWritingDirection); 251 break; 252 case ContextMenuItemTagLeftToRight: 253 frame->editor()->setBaseWritingDirection(LeftToRightWritingDirection); 254 break; 255 case ContextMenuItemTagRightToLeft: 256 frame->editor()->setBaseWritingDirection(RightToLeftWritingDirection); 257 break; 258#if PLATFORM(MAC) 259 case ContextMenuItemTagSearchInSpotlight: 260 m_client->searchWithSpotlight(); 261 break; 262#endif 263 case ContextMenuItemTagShowSpellingPanel: 264 frame->editor()->showSpellingGuessPanel(); 265 break; 266 case ContextMenuItemTagCheckSpelling: 267 frame->editor()->advanceToNextMisspelling(); 268 break; 269 case ContextMenuItemTagCheckSpellingWhileTyping: 270 frame->editor()->toggleContinuousSpellChecking(); 271 break; 272#ifndef BUILDING_ON_TIGER 273 case ContextMenuItemTagCheckGrammarWithSpelling: 274 frame->editor()->toggleGrammarChecking(); 275 break; 276#endif 277#if PLATFORM(MAC) 278 case ContextMenuItemTagShowFonts: 279 frame->editor()->showFontPanel(); 280 break; 281 case ContextMenuItemTagStyles: 282 frame->editor()->showStylesPanel(); 283 break; 284 case ContextMenuItemTagShowColors: 285 frame->editor()->showColorPanel(); 286 break; 287#endif 288 case ContextMenuItemTagInspectElement: 289 if (Page* page = frame->page()) 290 page->inspectorController()->inspect(result.innerNonSharedNode()); 291 break; 292 default: 293 break; 294 } 295} 296 297} // namespace WebCore 298