/* * Copyright (C) 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. * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 "EditingDelegate.h" #include "DumpRenderTree.h" #include "LayoutTestController.h" #include <WebCore/COMPtr.h> #include <wtf/Platform.h> #include <JavaScriptCore/Assertions.h> #include <string> #include <tchar.h> using std::wstring; EditingDelegate::EditingDelegate() : m_refCount(1) , m_acceptsEditing(true) { } // IUnknown HRESULT STDMETHODCALLTYPE EditingDelegate::QueryInterface(REFIID riid, void** ppvObject) { *ppvObject = 0; if (IsEqualGUID(riid, IID_IUnknown)) *ppvObject = static_cast<IWebEditingDelegate*>(this); else if (IsEqualGUID(riid, IID_IWebEditingDelegate)) *ppvObject = static_cast<IWebEditingDelegate*>(this); else return E_NOINTERFACE; AddRef(); return S_OK; } ULONG STDMETHODCALLTYPE EditingDelegate::AddRef(void) { return ++m_refCount; } ULONG STDMETHODCALLTYPE EditingDelegate::Release(void) { ULONG newRef = --m_refCount; if (!newRef) delete this; return newRef; } static wstring dumpPath(IDOMNode* node) { ASSERT(node); wstring result; BSTR name; if (FAILED(node->nodeName(&name))) return result; result.assign(name, SysStringLen(name)); SysFreeString(name); COMPtr<IDOMNode> parent; if (SUCCEEDED(node->parentNode(&parent))) result += TEXT(" > ") + dumpPath(parent.get()); return result; } static wstring dump(IDOMRange* range) { ASSERT(range); int startOffset; if (FAILED(range->startOffset(&startOffset))) return 0; int endOffset; if (FAILED(range->endOffset(&endOffset))) return 0; COMPtr<IDOMNode> startContainer; if (FAILED(range->startContainer(&startContainer))) return 0; COMPtr<IDOMNode> endContainer; if (FAILED(range->endContainer(&endContainer))) return 0; wchar_t buffer[1024]; _snwprintf(buffer, ARRAYSIZE(buffer), L"range from %ld of %s to %ld of %s", startOffset, dumpPath(startContainer.get()), endOffset, dumpPath(endContainer.get())); return buffer; } HRESULT STDMETHODCALLTYPE EditingDelegate::shouldBeginEditingInDOMRange( /* [in] */ IWebView* webView, /* [in] */ IDOMRange* range, /* [retval][out] */ BOOL* result) { if (!result) { ASSERT_NOT_REACHED(); return E_POINTER; } if (::gLayoutTestController->dumpEditingCallbacks() && !done) _tprintf(TEXT("EDITING DELEGATE: shouldBeginEditingInDOMRange:%s\n"), dump(range)); *result = m_acceptsEditing; return S_OK; } HRESULT STDMETHODCALLTYPE EditingDelegate::shouldEndEditingInDOMRange( /* [in] */ IWebView* webView, /* [in] */ IDOMRange* range, /* [retval][out] */ BOOL* result) { if (!result) { ASSERT_NOT_REACHED(); return E_POINTER; } if (::gLayoutTestController->dumpEditingCallbacks() && !done) _tprintf(TEXT("EDITING DELEGATE: shouldEndEditingInDOMRange:%s\n"), dump(range)); *result = m_acceptsEditing; return S_OK; } HRESULT STDMETHODCALLTYPE EditingDelegate::shouldInsertNode( /* [in] */ IWebView* webView, /* [in] */ IDOMNode* node, /* [in] */ IDOMRange* range, /* [in] */ WebViewInsertAction action) { static LPCTSTR insertactionstring[] = { TEXT("WebViewInsertActionTyped"), TEXT("WebViewInsertActionPasted"), TEXT("WebViewInsertActionDropped"), }; if (::gLayoutTestController->dumpEditingCallbacks() && !done) _tprintf(TEXT("EDITING DELEGATE: shouldInsertNode:%s replacingDOMRange:%s givenAction:%s\n"), dumpPath(node), dump(range), insertactionstring[action]); return S_OK; } HRESULT STDMETHODCALLTYPE EditingDelegate::shouldInsertText( /* [in] */ IWebView* webView, /* [in] */ BSTR text, /* [in] */ IDOMRange* range, /* [in] */ WebViewInsertAction action, /* [retval][out] */ BOOL* result) { if (!result) { ASSERT_NOT_REACHED(); return E_POINTER; } static LPCTSTR insertactionstring[] = { TEXT("WebViewInsertActionTyped"), TEXT("WebViewInsertActionPasted"), TEXT("WebViewInsertActionDropped"), }; if (::gLayoutTestController->dumpEditingCallbacks() && !done) _tprintf(TEXT("EDITING DELEGATE: shouldInsertText:%s replacingDOMRange:%s givenAction:%s\n"), text ? text : TEXT(""), dump(range), insertactionstring[action]); *result = m_acceptsEditing; return S_OK; } HRESULT STDMETHODCALLTYPE EditingDelegate::shouldDeleteDOMRange( /* [in] */ IWebView* webView, /* [in] */ IDOMRange* range, /* [retval][out] */ BOOL* result) { if (!result) { ASSERT_NOT_REACHED(); return E_POINTER; } if (::gLayoutTestController->dumpEditingCallbacks() && !done) _tprintf(TEXT("EDITING DELEGATE: shouldDeleteDOMRange:%s\n"), dump(range)); *result = m_acceptsEditing; return S_OK; } HRESULT STDMETHODCALLTYPE EditingDelegate::shouldChangeSelectedDOMRange( /* [in] */ IWebView* webView, /* [in] */ IDOMRange* currentRange, /* [in] */ IDOMRange* proposedRange, /* [in] */ WebSelectionAffinity selectionAffinity, /* [in] */ BOOL stillSelecting, /* [retval][out] */ BOOL* result) { if (!result) { ASSERT_NOT_REACHED(); return E_POINTER; } static LPCTSTR affinitystring[] = { TEXT("NSSelectionAffinityUpstream"), TEXT("NSSelectionAffinityDownstream") }; static LPCTSTR boolstring[] = { TEXT("FALSE"), TEXT("TRUE") }; if (::gLayoutTestController->dumpEditingCallbacks() && !done) _tprintf(TEXT("EDITING DELEGATE: shouldChangeSelectedDOMRange:%s toDOMRange:%s affinity:%s stillSelecting:%s\n"), dump(currentRange), dump(proposedRange), affinitystring[selectionAffinity], boolstring[stillSelecting]); *result = m_acceptsEditing; return S_OK; } HRESULT STDMETHODCALLTYPE EditingDelegate::shouldApplyStyle( /* [in] */ IWebView* webView, /* [in] */ IDOMCSSStyleDeclaration* style, /* [in] */ IDOMRange* range, /* [retval][out] */ BOOL* result) { if (!result) { ASSERT_NOT_REACHED(); return E_POINTER; } if (::gLayoutTestController->dumpEditingCallbacks() && !done) _tprintf(TEXT("EDITING DELEGATE: shouldApplyStyle:%s toElementsInDOMRange:%s\n"), TEXT("'style description'")/*[[style description] UTF8String]*/, dump(range)); *result = m_acceptsEditing; return S_OK; } HRESULT STDMETHODCALLTYPE EditingDelegate::shouldChangeTypingStyle( /* [in] */ IWebView* webView, /* [in] */ IDOMCSSStyleDeclaration* currentStyle, /* [in] */ IDOMCSSStyleDeclaration* proposedStyle, /* [retval][out] */ BOOL* result) { if (!result) { ASSERT_NOT_REACHED(); return E_POINTER; } if (::gLayoutTestController->dumpEditingCallbacks() && !done) _tprintf(TEXT("EDITING DELEGATE: shouldChangeTypingStyle:%s toStyle:%s\n"), TEXT("'currentStyle description'"), TEXT("'proposedStyle description'")); *result = m_acceptsEditing; return S_OK; } HRESULT STDMETHODCALLTYPE EditingDelegate::doPlatformCommand( /* [in] */ IWebView *webView, /* [in] */ BSTR command, /* [retval][out] */ BOOL *result) { if (!result) { ASSERT_NOT_REACHED(); return E_POINTER; } if (::gLayoutTestController->dumpEditingCallbacks() && !done) _tprintf(TEXT("EDITING DELEGATE: doPlatformCommand:%s\n"), command ? command : TEXT("")); *result = m_acceptsEditing; return S_OK; } HRESULT STDMETHODCALLTYPE EditingDelegate::webViewDidBeginEditing( /* [in] */ IWebNotification* notification) { if (::gLayoutTestController->dumpEditingCallbacks() && !done) { BSTR name; notification->name(&name); _tprintf(TEXT("EDITING DELEGATE: webViewDidBeginEditing:%s\n"), name ? name : TEXT("")); SysFreeString(name); } return S_OK; } HRESULT STDMETHODCALLTYPE EditingDelegate::webViewDidChange( /* [in] */ IWebNotification *notification) { if (::gLayoutTestController->dumpEditingCallbacks() && !done) { BSTR name; notification->name(&name); _tprintf(TEXT("EDITING DELEGATE: webViewDidBeginEditing:%s\n"), name ? name : TEXT("")); SysFreeString(name); } return S_OK; } HRESULT STDMETHODCALLTYPE EditingDelegate::webViewDidEndEditing( /* [in] */ IWebNotification *notification) { if (::gLayoutTestController->dumpEditingCallbacks() && !done) { BSTR name; notification->name(&name); _tprintf(TEXT("EDITING DELEGATE: webViewDidEndEditing:%s\n"), name ? name : TEXT("")); SysFreeString(name); } return S_OK; } HRESULT STDMETHODCALLTYPE EditingDelegate::webViewDidChangeTypingStyle( /* [in] */ IWebNotification *notification) { if (::gLayoutTestController->dumpEditingCallbacks() && !done) { BSTR name; notification->name(&name); _tprintf(TEXT("EDITING DELEGATE: webViewDidChangeTypingStyle:%s\n"), name ? name : TEXT("")); SysFreeString(name); } return S_OK; } HRESULT STDMETHODCALLTYPE EditingDelegate::webViewDidChangeSelection( /* [in] */ IWebNotification *notification) { if (::gLayoutTestController->dumpEditingCallbacks() && !done) { BSTR name; notification->name(&name); _tprintf(TEXT("EDITING DELEGATE: webViewDidChangeSelection:%s\n"), name ? name : TEXT("")); SysFreeString(name); } return S_OK; } static int indexOfFirstWordCharacter(const TCHAR* text) { const TCHAR* cursor = text; while (*cursor && !iswalpha(*cursor)) ++cursor; return *cursor ? (cursor - text) : -1; }; static int wordLength(const TCHAR* text) { const TCHAR* cursor = text; while (*cursor && iswalpha(*cursor)) ++cursor; return cursor - text; }; HRESULT STDMETHODCALLTYPE EditingDelegate::checkSpellingOfString( /* [in] */ IWebView* view, /* [in] */ LPCTSTR text, /* [in] */ int length, /* [out] */ int* misspellingLocation, /* [out] */ int* misspellingLength) { static const TCHAR* misspelledWords[] = { // These words are known misspelled words in webkit tests. // If there are other misspelled words in webkit tests, please add them in // this array. TEXT("foo"), TEXT("Foo"), TEXT("baz"), TEXT("fo"), TEXT("LibertyF"), TEXT("chello"), TEXT("xxxtestxxx"), TEXT("XXxxx"), TEXT("Textx"), TEXT("blockquoted"), TEXT("asd"), TEXT("Lorem"), TEXT("Nunc"), TEXT("Curabitur"), TEXT("eu"), TEXT("adlj"), TEXT("adaasj"), TEXT("sdklj"), TEXT("jlkds"), TEXT("jsaada"), TEXT("jlda"), TEXT("zz"), TEXT("contentEditable"), 0, }; wstring textString(text, length); int wordStart = indexOfFirstWordCharacter(textString.c_str()); if (-1 == wordStart) return S_OK; wstring word = textString.substr(wordStart, wordLength(textString.c_str() + wordStart)); for (size_t i = 0; misspelledWords[i]; ++i) { if (word == misspelledWords[i]) { *misspellingLocation = wordStart; *misspellingLength = word.size(); break; } } return S_OK; }