1/* 2 * Copyright (C) 2006, 2007, 2011 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 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 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 "WebKitDLL.h" 28#include "WebEditorClient.h" 29 30#include "WebKit.h" 31#include "WebNotification.h" 32#include "WebNotificationCenter.h" 33#include "WebView.h" 34#include "DOMCoreClasses.h" 35#include <WebCore/BString.h> 36#include <WebCore/Document.h> 37#include <WebCore/HTMLElement.h> 38#include <WebCore/HTMLInputElement.h> 39#include <WebCore/HTMLNames.h> 40#include <WebCore/KeyboardEvent.h> 41#include <WebCore/LocalizedStrings.h> 42#include <WebCore/NotImplemented.h> 43#include <WebCore/Page.h> 44#include <WebCore/PlatformKeyboardEvent.h> 45#include <WebCore/Range.h> 46#include <WebCore/Settings.h> 47#include <WebCore/UndoStep.h> 48#include <WebCore/UserTypingGestureIndicator.h> 49#include <WebCore/VisibleSelection.h> 50#include <wtf/text/StringView.h> 51 52using namespace WebCore; 53using namespace HTMLNames; 54 55// {09A11D2B-FAFB-4ca0-A6F7-791EE8932C88} 56static const GUID IID_IWebUndoCommand = 57{ 0x9a11d2b, 0xfafb, 0x4ca0, { 0xa6, 0xf7, 0x79, 0x1e, 0xe8, 0x93, 0x2c, 0x88 } }; 58 59class IWebUndoCommand : public IUnknown { 60public: 61 virtual void execute() = 0; 62}; 63 64// WebEditorUndoTarget ------------------------------------------------------------- 65 66class WebEditorUndoTarget : public IWebUndoTarget 67{ 68public: 69 WebEditorUndoTarget(); 70 71 // IUnknown 72 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject); 73 virtual ULONG STDMETHODCALLTYPE AddRef(void); 74 virtual ULONG STDMETHODCALLTYPE Release(void); 75 76 // IWebUndoTarget 77 virtual HRESULT STDMETHODCALLTYPE invoke( 78 /* [in] */ BSTR actionName, 79 /* [in] */ IUnknown *obj); 80 81private: 82 ULONG m_refCount; 83}; 84 85WebEditorUndoTarget::WebEditorUndoTarget() 86: m_refCount(1) 87{ 88} 89 90HRESULT STDMETHODCALLTYPE WebEditorUndoTarget::QueryInterface(REFIID riid, void** ppvObject) 91{ 92 *ppvObject = 0; 93 if (IsEqualGUID(riid, IID_IUnknown)) 94 *ppvObject = static_cast<IWebUndoTarget*>(this); 95 else if (IsEqualGUID(riid, IID_IWebUndoTarget)) 96 *ppvObject = static_cast<IWebUndoTarget*>(this); 97 else 98 return E_NOINTERFACE; 99 100 AddRef(); 101 return S_OK; 102} 103 104ULONG STDMETHODCALLTYPE WebEditorUndoTarget::AddRef(void) 105{ 106 return ++m_refCount; 107} 108 109ULONG STDMETHODCALLTYPE WebEditorUndoTarget::Release(void) 110{ 111 ULONG newRef = --m_refCount; 112 if (!newRef) 113 delete(this); 114 115 return newRef; 116} 117 118HRESULT STDMETHODCALLTYPE WebEditorUndoTarget::invoke( 119 /* [in] */ BSTR /*actionName*/, 120 /* [in] */ IUnknown *obj) 121{ 122 IWebUndoCommand* undoCommand = 0; 123 if (SUCCEEDED(obj->QueryInterface(IID_IWebUndoCommand, (void**)&undoCommand))) { 124 undoCommand->execute(); 125 undoCommand->Release(); 126 } 127 return S_OK; 128} 129 130// WebEditorClient ------------------------------------------------------------------ 131 132WebEditorClient::WebEditorClient(WebView* webView) 133 : m_webView(webView) 134 , m_undoTarget(0) 135{ 136 m_undoTarget = new WebEditorUndoTarget(); 137} 138 139WebEditorClient::~WebEditorClient() 140{ 141 if (m_undoTarget) 142 m_undoTarget->Release(); 143} 144 145void WebEditorClient::pageDestroyed() 146{ 147 delete this; 148} 149 150bool WebEditorClient::isContinuousSpellCheckingEnabled() 151{ 152 BOOL enabled; 153 if (FAILED(m_webView->isContinuousSpellCheckingEnabled(&enabled))) 154 return false; 155 return !!enabled; 156} 157 158void WebEditorClient::toggleContinuousSpellChecking() 159{ 160 m_webView->toggleContinuousSpellChecking(0); 161} 162 163bool WebEditorClient::isGrammarCheckingEnabled() 164{ 165 BOOL enabled; 166 if (FAILED(m_webView->isGrammarCheckingEnabled(&enabled))) 167 return false; 168 return !!enabled; 169} 170 171void WebEditorClient::toggleGrammarChecking() 172{ 173 m_webView->toggleGrammarChecking(0); 174} 175 176static void initViewSpecificSpelling(IWebViewEditing* viewEditing) 177{ 178 // we just use this as a flag to indicate that we've spell checked the document 179 // and need to close the spell checker out when the view closes. 180 int tag; 181 viewEditing->spellCheckerDocumentTag(&tag); 182} 183 184int WebEditorClient::spellCheckerDocumentTag() 185{ 186 // we don't use the concept of spelling tags 187 notImplemented(); 188 ASSERT_NOT_REACHED(); 189 return 0; 190} 191 192bool WebEditorClient::shouldBeginEditing(Range*) 193{ 194 notImplemented(); 195 return true; 196} 197 198bool WebEditorClient::shouldEndEditing(Range*) 199{ 200 notImplemented(); 201 return true; 202} 203 204void WebEditorClient::didBeginEditing() 205{ 206 notImplemented(); 207} 208 209void WebEditorClient::respondToChangedContents() 210{ 211 notImplemented(); 212} 213 214void WebEditorClient::respondToChangedSelection(Frame*) 215{ 216 m_webView->selectionChanged(); 217 218 static BSTR webViewDidChangeSelectionNotificationName = SysAllocString(WebViewDidChangeSelectionNotification); 219 IWebNotificationCenter* notifyCenter = WebNotificationCenter::defaultCenterInternal(); 220 notifyCenter->postNotificationName(webViewDidChangeSelectionNotificationName, static_cast<IWebView*>(m_webView), 0); 221} 222 223void WebEditorClient::didEndEditing() 224{ 225 notImplemented(); 226} 227 228void WebEditorClient::didWriteSelectionToPasteboard() 229{ 230 notImplemented(); 231} 232 233void WebEditorClient::willWriteSelectionToPasteboard(WebCore::Range*) 234{ 235 notImplemented(); 236} 237 238void WebEditorClient::getClientPasteboardDataForRange(WebCore::Range*, Vector<String>&, Vector<RefPtr<WebCore::SharedBuffer> >&) 239{ 240 notImplemented(); 241} 242 243bool WebEditorClient::shouldDeleteRange(Range* /*range*/) 244{ 245 notImplemented(); 246 return true; 247 248 // FIXME: calling m_webView->editingDelegate() will cause an assertion failure so we don't want to enable this code until that's implemented. 249 //BOOL result = false; 250 //IWebViewEditingDelegate* editingDelegate; 251 //// FIXME: DOMRange needs to be implemented before anything meaningful can be done here 252 //IDOMRange* domRange(0); 253 //if (SUCCEEDED(m_webView->editingDelegate(&editingDelegate))) { 254 // editingDelegate->shouldDeleteDOMRange(m_webView, domRange, &result); 255 // editingDelegate->Release(); 256 //} 257 //return !!result; 258} 259 260bool WebEditorClient::shouldInsertNode(Node* /*node*/, Range* /*replacingRange*/, EditorInsertAction /*givenAction*/) 261{ 262 notImplemented(); 263 return true; 264} 265 266bool WebEditorClient::shouldInsertText(const String& /*str*/, Range* /* replacingRange */, EditorInsertAction /*givenAction*/) 267{ 268 notImplemented(); 269 return true; 270 271 // FIXME: calling m_webView->editingDelegate() will cause an assertion failure so we don't want to enable this code until that's implemented. 272 //BOOL result = false; 273 //IWebViewEditingDelegate* editingDelegate; 274 //// FIXME: DOMRange needs to be implemented before anything meaningful can be done here 275 //IDOMRange* domRange(0); // make a DOMRange from replacingRange 276 //BString text(str); 277 //if (SUCCEEDED(m_webView->editingDelegate(&editingDelegate))) { 278 // editingDelegate->shouldInsertText(m_webView, text, domRange, (WebViewInsertAction) givenAction, &result); 279 // editingDelegate->Release(); 280 //} 281 //return !!result; 282} 283 284//bool WebEditorClient::shouldChangeSelectedRange(Range *currentRange, Range *toProposedRange, SelectionAffinity selectionAffinity, bool stillSelecting) 285//{ notImplemented(); return false; } 286 287bool WebEditorClient::shouldApplyStyle(StyleProperties* /*style*/, Range* /*toElementsInDOMRange*/) 288{ notImplemented(); return true; } 289 290bool WebEditorClient::shouldMoveRangeAfterDelete(Range* /*range*/, Range* /*rangeToBeReplaced*/) 291{ notImplemented(); return true; } 292 293bool WebEditorClient::shouldChangeTypingStyle(StyleProperties* /*currentStyle*/, StyleProperties* /*toProposedStyle*/) 294{ notImplemented(); return false; } 295 296void WebEditorClient::webViewDidChangeTypingStyle(WebNotification* /*notification*/) 297{ notImplemented(); } 298 299void WebEditorClient::webViewDidChangeSelection(WebNotification* /*notification*/) 300{ notImplemented(); } 301 302bool WebEditorClient::smartInsertDeleteEnabled(void) 303{ 304 Page* page = m_webView->page(); 305 if (!page) 306 return false; 307 return page->settings().smartInsertDeleteEnabled(); 308} 309 310bool WebEditorClient::isSelectTrailingWhitespaceEnabled(void) 311{ 312 Page* page = m_webView->page(); 313 if (!page) 314 return false; 315 return page->settings().selectTrailingWhitespaceEnabled(); 316} 317 318bool WebEditorClient::shouldChangeSelectedRange(WebCore::Range*, WebCore::Range*, WebCore::EAffinity, bool) 319{ notImplemented(); return true; } 320 321void WebEditorClient::textFieldDidBeginEditing(Element* e) 322{ 323 IWebFormDelegate* formDelegate; 324 if (SUCCEEDED(m_webView->formDelegate(&formDelegate)) && formDelegate) { 325 IDOMElement* domElement = DOMElement::createInstance(e); 326 if (domElement) { 327 IDOMHTMLInputElement* domInputElement; 328 if (SUCCEEDED(domElement->QueryInterface(IID_IDOMHTMLInputElement, (void**)&domInputElement))) { 329 formDelegate->textFieldDidBeginEditing(domInputElement, kit(e->document().frame())); 330 domInputElement->Release(); 331 } 332 domElement->Release(); 333 } 334 formDelegate->Release(); 335 } 336} 337 338void WebEditorClient::textFieldDidEndEditing(Element* e) 339{ 340 IWebFormDelegate* formDelegate; 341 if (SUCCEEDED(m_webView->formDelegate(&formDelegate)) && formDelegate) { 342 IDOMElement* domElement = DOMElement::createInstance(e); 343 if (domElement) { 344 IDOMHTMLInputElement* domInputElement; 345 if (SUCCEEDED(domElement->QueryInterface(IID_IDOMHTMLInputElement, (void**)&domInputElement))) { 346 formDelegate->textFieldDidEndEditing(domInputElement, kit(e->document().frame())); 347 domInputElement->Release(); 348 } 349 domElement->Release(); 350 } 351 formDelegate->Release(); 352 } 353} 354 355void WebEditorClient::textDidChangeInTextField(Element* e) 356{ 357 if (!UserTypingGestureIndicator::processingUserTypingGesture() || UserTypingGestureIndicator::focusedElementAtGestureStart() != e) 358 return; 359 360 IWebFormDelegate* formDelegate; 361 if (SUCCEEDED(m_webView->formDelegate(&formDelegate)) && formDelegate) { 362 IDOMElement* domElement = DOMElement::createInstance(e); 363 if (domElement) { 364 IDOMHTMLInputElement* domInputElement; 365 if (SUCCEEDED(domElement->QueryInterface(IID_IDOMHTMLInputElement, (void**)&domInputElement))) { 366 formDelegate->textDidChangeInTextField(domInputElement, kit(e->document().frame())); 367 domInputElement->Release(); 368 } 369 domElement->Release(); 370 } 371 formDelegate->Release(); 372 } 373} 374 375bool WebEditorClient::doTextFieldCommandFromEvent(Element* e, KeyboardEvent* ke) 376{ 377 BOOL result = FALSE; 378 IWebFormDelegate* formDelegate; 379 if (SUCCEEDED(m_webView->formDelegate(&formDelegate)) && formDelegate) { 380 IDOMElement* domElement = DOMElement::createInstance(e); 381 if (domElement) { 382 IDOMHTMLInputElement* domInputElement; 383 if (SUCCEEDED(domElement->QueryInterface(IID_IDOMHTMLInputElement, (void**)&domInputElement))) { 384 String command = m_webView->interpretKeyEvent(ke); 385 // We allow empty commands here because the app code actually depends on this being called for all key presses. 386 // We may want to revisit this later because it doesn't really make sense to send an empty command. 387 formDelegate->doPlatformCommand(domInputElement, BString(command), kit(e->document().frame()), &result); 388 domInputElement->Release(); 389 } 390 domElement->Release(); 391 } 392 formDelegate->Release(); 393 } 394 return !!result; 395} 396 397void WebEditorClient::textWillBeDeletedInTextField(Element* e) 398{ 399 // We're using the deleteBackward command for all deletion operations since the autofill code treats all deletions the same way. 400 IWebFormDelegate* formDelegate; 401 if (SUCCEEDED(m_webView->formDelegate(&formDelegate)) && formDelegate) { 402 IDOMElement* domElement = DOMElement::createInstance(e); 403 if (domElement) { 404 IDOMHTMLInputElement* domInputElement; 405 if (SUCCEEDED(domElement->QueryInterface(IID_IDOMHTMLInputElement, (void**)&domInputElement))) { 406 BOOL result; 407 formDelegate->doPlatformCommand(domInputElement, BString(L"DeleteBackward"), kit(e->document().frame()), &result); 408 domInputElement->Release(); 409 } 410 domElement->Release(); 411 } 412 formDelegate->Release(); 413 } 414} 415 416void WebEditorClient::textDidChangeInTextArea(Element* e) 417{ 418 IWebFormDelegate* formDelegate; 419 if (SUCCEEDED(m_webView->formDelegate(&formDelegate)) && formDelegate) { 420 IDOMElement* domElement = DOMElement::createInstance(e); 421 if (domElement) { 422 IDOMHTMLTextAreaElement* domTextAreaElement; 423 if (SUCCEEDED(domElement->QueryInterface(IID_IDOMHTMLTextAreaElement, (void**)&domTextAreaElement))) { 424 formDelegate->textDidChangeInTextArea(domTextAreaElement, kit(e->document().frame())); 425 domTextAreaElement->Release(); 426 } 427 domElement->Release(); 428 } 429 formDelegate->Release(); 430 } 431} 432 433class WebEditorUndoCommand : public IWebUndoCommand 434{ 435public: 436 WebEditorUndoCommand(PassRefPtr<UndoStep>, bool isUndo); 437 void execute(); 438 439 // IUnknown 440 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject); 441 virtual ULONG STDMETHODCALLTYPE AddRef(void); 442 virtual ULONG STDMETHODCALLTYPE Release(void); 443 444private: 445 ULONG m_refCount; 446 RefPtr<UndoStep> m_step; 447 bool m_isUndo; 448}; 449 450WebEditorUndoCommand::WebEditorUndoCommand(PassRefPtr<UndoStep> step, bool isUndo) 451 : m_step(step) 452 , m_isUndo(isUndo) 453 , m_refCount(1) 454{ 455} 456 457void WebEditorUndoCommand::execute() 458{ 459 if (m_isUndo) 460 m_step->unapply(); 461 else 462 m_step->reapply(); 463} 464 465HRESULT STDMETHODCALLTYPE WebEditorUndoCommand::QueryInterface(REFIID riid, void** ppvObject) 466{ 467 *ppvObject = 0; 468 if (IsEqualGUID(riid, IID_IUnknown)) 469 *ppvObject = static_cast<IWebUndoCommand*>(this); 470 else if (IsEqualGUID(riid, IID_IWebUndoCommand)) 471 *ppvObject = static_cast<IWebUndoCommand*>(this); 472 else 473 return E_NOINTERFACE; 474 475 AddRef(); 476 return S_OK; 477} 478 479ULONG STDMETHODCALLTYPE WebEditorUndoCommand::AddRef(void) 480{ 481 return ++m_refCount; 482} 483 484ULONG STDMETHODCALLTYPE WebEditorUndoCommand::Release(void) 485{ 486 ULONG newRef = --m_refCount; 487 if (!newRef) 488 delete(this); 489 490 return newRef; 491} 492 493static String undoNameForEditAction(EditAction editAction) 494{ 495 switch (editAction) { 496 case EditActionUnspecified: return String(); 497 case EditActionSetColor: return WEB_UI_STRING_KEY("Set Color", "Set Color (Undo action name)", "Undo action name"); 498 case EditActionSetBackgroundColor: return WEB_UI_STRING_KEY("Set Background Color", "Set Background Color (Undo action name)", "Undo action name"); 499 case EditActionTurnOffKerning: return WEB_UI_STRING_KEY("Turn Off Kerning", "Turn Off Kerning (Undo action name)", "Undo action name"); 500 case EditActionTightenKerning: return WEB_UI_STRING_KEY("Tighten Kerning", "Tighten Kerning (Undo action name)", "Undo action name"); 501 case EditActionLoosenKerning: return WEB_UI_STRING_KEY("Loosen Kerning", "Loosen Kerning (Undo action name)", "Undo action name"); 502 case EditActionUseStandardKerning: return WEB_UI_STRING_KEY("Use Standard Kerning", "Use Standard Kerning (Undo action name)", "Undo action name"); 503 case EditActionTurnOffLigatures: return WEB_UI_STRING_KEY("Turn Off Ligatures", "Turn Off Ligatures (Undo action name)", "Undo action name"); 504 case EditActionUseStandardLigatures: return WEB_UI_STRING_KEY("Use Standard Ligatures", "Use Standard Ligatures (Undo action name)", "Undo action name"); 505 case EditActionUseAllLigatures: return WEB_UI_STRING_KEY("Use All Ligatures", "Use All Ligatures (Undo action name)", "Undo action name"); 506 case EditActionRaiseBaseline: return WEB_UI_STRING_KEY("Raise Baseline", "Raise Baseline (Undo action name)", "Undo action name"); 507 case EditActionLowerBaseline: return WEB_UI_STRING_KEY("Lower Baseline", "Lower Baseline (Undo action name)", "Undo action name"); 508 case EditActionSetTraditionalCharacterShape: return WEB_UI_STRING_KEY("Set Traditional Character Shape", "Set Traditional Character Shape (Undo action name)", "Undo action name"); 509 case EditActionSetFont: return WEB_UI_STRING_KEY("Set Font", "Set Font (Undo action name)", "Undo action name"); 510 case EditActionChangeAttributes: return WEB_UI_STRING_KEY("Change Attributes", "Change Attributes (Undo action name)", "Undo action name"); 511 case EditActionAlignLeft: return WEB_UI_STRING_KEY("Align Left", "Align Left (Undo action name)", "Undo action name"); 512 case EditActionAlignRight: return WEB_UI_STRING_KEY("Align Right", "Align Right (Undo action name)", "Undo action name"); 513 case EditActionCenter: return WEB_UI_STRING_KEY("Center", "Center (Undo action name)", "Undo action name"); 514 case EditActionJustify: return WEB_UI_STRING_KEY("Justify", "Justify (Undo action name)", "Undo action name"); 515 case EditActionSetWritingDirection: return WEB_UI_STRING_KEY("Set Writing Direction", "Set Writing Direction (Undo action name)", "Undo action name"); 516 case EditActionSubscript: return WEB_UI_STRING_KEY("Subscript", "Subscript (Undo action name)", "Undo action name"); 517 case EditActionSuperscript: return WEB_UI_STRING_KEY("Superscript", "Superscript (Undo action name)", "Undo action name"); 518 case EditActionBold: return WEB_UI_STRING_KEY("Bold", "Bold (Undo action name)", "Undo action name"); 519 case EditActionItalics: return WEB_UI_STRING_KEY("Italics", "Italics (Undo action name)", "Undo action name"); 520 case EditActionUnderline: return WEB_UI_STRING_KEY("Underline", "Underline (Undo action name)", "Undo action name"); 521 case EditActionOutline: return WEB_UI_STRING_KEY("Outline", "Outline (Undo action name)", "Undo action name"); 522 case EditActionUnscript: return WEB_UI_STRING_KEY("Unscript", "Unscript (Undo action name)", "Undo action name"); 523 case EditActionDrag: return WEB_UI_STRING_KEY("Drag", "Drag (Undo action name)", "Undo action name"); 524 case EditActionCut: return WEB_UI_STRING_KEY("Cut", "Cut (Undo action name)", "Undo action name"); 525 case EditActionPaste: return WEB_UI_STRING_KEY("Paste", "Paste (Undo action name)", "Undo action name"); 526 case EditActionPasteFont: return WEB_UI_STRING_KEY("Paste Font", "Paste Font (Undo action name)", "Undo action name"); 527 case EditActionPasteRuler: return WEB_UI_STRING_KEY("Paste Ruler", "Paste Ruler (Undo action name)", "Undo action name"); 528 case EditActionTyping: return WEB_UI_STRING_KEY("Typing", "Typing (Undo action name)", "Undo action name"); 529 case EditActionCreateLink: return WEB_UI_STRING_KEY("Create Link", "Create Link (Undo action name)", "Undo action name"); 530 case EditActionUnlink: return WEB_UI_STRING_KEY("Unlink", "Unlink (Undo action name)", "Undo action name"); 531 case EditActionInsertList: return WEB_UI_STRING_KEY("Insert List", "Insert List (Undo action name)", "Undo action name"); 532 case EditActionFormatBlock: return WEB_UI_STRING_KEY("Formatting", "Format Block (Undo action name)", "Undo action name"); 533 case EditActionIndent: return WEB_UI_STRING_KEY("Indent", "Indent (Undo action name)", "Undo action name"); 534 case EditActionOutdent: return WEB_UI_STRING_KEY("Outdent", "Outdent (Undo action name)", "Undo action name"); 535 } 536 return String(); 537} 538 539void WebEditorClient::registerUndoStep(PassRefPtr<UndoStep> step) 540{ 541 IWebUIDelegate* uiDelegate = 0; 542 if (SUCCEEDED(m_webView->uiDelegate(&uiDelegate))) { 543 String actionName = undoNameForEditAction(step->editingAction()); 544 WebEditorUndoCommand* undoCommand = new WebEditorUndoCommand(step, true); 545 if (!undoCommand) 546 return; 547 uiDelegate->registerUndoWithTarget(m_undoTarget, 0, undoCommand); 548 undoCommand->Release(); // the undo manager owns the reference 549 if (!actionName.isEmpty()) 550 uiDelegate->setActionTitle(BString(actionName)); 551 uiDelegate->Release(); 552 } 553} 554 555void WebEditorClient::registerRedoStep(PassRefPtr<UndoStep> step) 556{ 557 IWebUIDelegate* uiDelegate = 0; 558 if (SUCCEEDED(m_webView->uiDelegate(&uiDelegate))) { 559 WebEditorUndoCommand* undoCommand = new WebEditorUndoCommand(step, false); 560 if (!undoCommand) 561 return; 562 uiDelegate->registerUndoWithTarget(m_undoTarget, 0, undoCommand); 563 undoCommand->Release(); // the undo manager owns the reference 564 uiDelegate->Release(); 565 } 566} 567 568void WebEditorClient::clearUndoRedoOperations() 569{ 570 IWebUIDelegate* uiDelegate = 0; 571 if (SUCCEEDED(m_webView->uiDelegate(&uiDelegate))) { 572 uiDelegate->removeAllActionsWithTarget(m_undoTarget); 573 uiDelegate->Release(); 574 } 575} 576 577bool WebEditorClient::canCopyCut(Frame*, bool defaultValue) const 578{ 579 return defaultValue; 580} 581 582bool WebEditorClient::canPaste(Frame*, bool defaultValue) const 583{ 584 return defaultValue; 585} 586 587bool WebEditorClient::canUndo() const 588{ 589 BOOL result = FALSE; 590 IWebUIDelegate* uiDelegate = 0; 591 if (SUCCEEDED(m_webView->uiDelegate(&uiDelegate))) { 592 uiDelegate->canUndo(&result); 593 uiDelegate->Release(); 594 } 595 return !!result; 596} 597 598bool WebEditorClient::canRedo() const 599{ 600 BOOL result = FALSE; 601 IWebUIDelegate* uiDelegate = 0; 602 if (SUCCEEDED(m_webView->uiDelegate(&uiDelegate))) { 603 uiDelegate->canRedo(&result); 604 uiDelegate->Release(); 605 } 606 return !!result; 607} 608 609void WebEditorClient::undo() 610{ 611 IWebUIDelegate* uiDelegate = 0; 612 if (SUCCEEDED(m_webView->uiDelegate(&uiDelegate))) { 613 uiDelegate->undo(); 614 uiDelegate->Release(); 615 } 616} 617 618void WebEditorClient::redo() 619{ 620 IWebUIDelegate* uiDelegate = 0; 621 if (SUCCEEDED(m_webView->uiDelegate(&uiDelegate))) { 622 uiDelegate->redo(); 623 uiDelegate->Release(); 624 } 625} 626 627void WebEditorClient::handleKeyboardEvent(KeyboardEvent* evt) 628{ 629 if (m_webView->handleEditingKeyboardEvent(evt)) 630 evt->setDefaultHandled(); 631} 632 633void WebEditorClient::handleInputMethodKeydown(KeyboardEvent* ) 634{ 635} 636 637bool WebEditorClient::shouldEraseMarkersAfterChangeSelection(TextCheckingType) const 638{ 639 return true; 640} 641 642void WebEditorClient::ignoreWordInSpellDocument(const String& word) 643{ 644 COMPtr<IWebEditingDelegate> ed; 645 if (FAILED(m_webView->editingDelegate(&ed)) || !ed.get()) 646 return; 647 648 initViewSpecificSpelling(m_webView); 649 ed->ignoreWordInSpellDocument(m_webView, BString(word)); 650} 651 652void WebEditorClient::learnWord(const String& word) 653{ 654 COMPtr<IWebEditingDelegate> ed; 655 if (FAILED(m_webView->editingDelegate(&ed)) || !ed.get()) 656 return; 657 658 ed->learnWord(BString(word)); 659} 660 661void WebEditorClient::checkSpellingOfString(StringView text, int* misspellingLocation, int* misspellingLength) 662{ 663 *misspellingLocation = -1; 664 *misspellingLength = 0; 665 666 COMPtr<IWebEditingDelegate> ed; 667 if (FAILED(m_webView->editingDelegate(&ed)) || !ed.get()) 668 return; 669 670 initViewSpecificSpelling(m_webView); 671 ed->checkSpellingOfString(m_webView, text.upconvertedCharacters(), text.length(), misspellingLocation, misspellingLength); 672} 673 674String WebEditorClient::getAutoCorrectSuggestionForMisspelledWord(const String& inputWord) 675{ 676 // This method can be implemented using customized algorithms for the particular browser. 677 // Currently, it computes an empty string. 678 return String(); 679} 680 681void WebEditorClient::checkGrammarOfString(StringView text, Vector<GrammarDetail>& details, int* badGrammarLocation, int* badGrammarLength) 682{ 683 details.clear(); 684 *badGrammarLocation = -1; 685 *badGrammarLength = 0; 686 687 COMPtr<IWebEditingDelegate> ed; 688 if (FAILED(m_webView->editingDelegate(&ed)) || !ed.get()) 689 return; 690 691 initViewSpecificSpelling(m_webView); 692 COMPtr<IEnumWebGrammarDetails> enumDetailsObj; 693 if (FAILED(ed->checkGrammarOfString(m_webView, text.upconvertedCharacters(), text.length(), &enumDetailsObj, badGrammarLocation, badGrammarLength))) 694 return; 695 696 while (true) { 697 ULONG fetched; 698 COMPtr<IWebGrammarDetail> detailObj; 699 if (enumDetailsObj->Next(1, &detailObj, &fetched) != S_OK) 700 break; 701 702 GrammarDetail detail; 703 if (FAILED(detailObj->length(&detail.length))) 704 continue; 705 if (FAILED(detailObj->location(&detail.location))) 706 continue; 707 BString userDesc; 708 if (FAILED(detailObj->userDescription(&userDesc))) 709 continue; 710 detail.userDescription = String(userDesc, SysStringLen(userDesc)); 711 712 COMPtr<IEnumSpellingGuesses> enumGuessesObj; 713 if (FAILED(detailObj->guesses(&enumGuessesObj))) 714 continue; 715 while (true) { 716 BString guess; 717 if (enumGuessesObj->Next(1, &guess, &fetched) != S_OK) 718 break; 719 detail.guesses.append(String(guess, SysStringLen(guess))); 720 } 721 722 details.append(detail); 723 } 724} 725 726void WebEditorClient::updateSpellingUIWithGrammarString(const String& string, const WebCore::GrammarDetail& detail) 727{ 728 COMPtr<IWebEditingDelegate> ed; 729 if (FAILED(m_webView->editingDelegate(&ed)) || !ed.get()) 730 return; 731 732 Vector<BSTR> guessesBSTRs; 733 for (unsigned i = 0; i < detail.guesses.size(); i++) { 734 BString guess(detail.guesses[i]); 735 guessesBSTRs.append(guess.release()); 736 } 737 BString userDescriptionBSTR(detail.userDescription); 738 ed->updateSpellingUIWithGrammarString(BString(string), detail.location, detail.length, userDescriptionBSTR, guessesBSTRs.data(), (int)guessesBSTRs.size()); 739 for (unsigned i = 0; i < guessesBSTRs.size(); i++) 740 SysFreeString(guessesBSTRs[i]); 741} 742 743void WebEditorClient::updateSpellingUIWithMisspelledWord(const String& word) 744{ 745 COMPtr<IWebEditingDelegate> ed; 746 if (FAILED(m_webView->editingDelegate(&ed)) || !ed.get()) 747 return; 748 749 ed->updateSpellingUIWithMisspelledWord(BString(word)); 750} 751 752void WebEditorClient::showSpellingUI(bool show) 753{ 754 COMPtr<IWebEditingDelegate> ed; 755 if (FAILED(m_webView->editingDelegate(&ed)) || !ed.get()) 756 return; 757 758 ed->showSpellingUI(show); 759} 760 761bool WebEditorClient::spellingUIIsShowing() 762{ 763 COMPtr<IWebEditingDelegate> ed; 764 if (FAILED(m_webView->editingDelegate(&ed)) || !ed.get()) 765 return false; 766 767 BOOL showing; 768 if (FAILED(ed->spellingUIIsShowing(&showing))) 769 return false; 770 771 return !!showing; 772} 773 774void WebEditorClient::getGuessesForWord(const String& word, const String& context, Vector<String>& guesses) 775{ 776 guesses.clear(); 777 778 COMPtr<IWebEditingDelegate> ed; 779 if (FAILED(m_webView->editingDelegate(&ed)) || !ed.get()) 780 return; 781 782 COMPtr<IEnumSpellingGuesses> enumGuessesObj; 783 if (FAILED(ed->guessesForWord(BString(word), &enumGuessesObj))) 784 return; 785 786 while (true) { 787 ULONG fetched; 788 BString guess; 789 if (enumGuessesObj->Next(1, &guess, &fetched) != S_OK) 790 break; 791 guesses.append(String(guess, SysStringLen(guess))); 792 } 793} 794 795void WebEditorClient::willSetInputMethodState() 796{ 797} 798 799void WebEditorClient::setInputMethodState(bool enabled) 800{ 801 m_webView->setInputMethodState(enabled); 802} 803