| /* |
| * 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; |
| } |