Home | History | Annotate | Download | only in win
      1 /*
      2  * Copyright (C) 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  *
      8  * 1.  Redistributions of source code must retain the above copyright
      9  *     notice, this list of conditions and the following disclaimer.
     10  * 2.  Redistributions in binary form must reproduce the above copyright
     11  *     notice, this list of conditions and the following disclaimer in the
     12  *     documentation and/or other materials provided with the distribution.
     13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
     14  *     its contributors may be used to endorse or promote products derived
     15  *     from this software without specific prior written permission.
     16  *
     17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
     18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
     21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 #include "config.h"
     30 #include "EditingDelegate.h"
     31 
     32 #include "DumpRenderTree.h"
     33 #include "LayoutTestController.h"
     34 #include <WebCore/COMPtr.h>
     35 #include <wtf/Platform.h>
     36 #include <JavaScriptCore/Assertions.h>
     37 #include <string>
     38 #include <tchar.h>
     39 
     40 using std::wstring;
     41 
     42 EditingDelegate::EditingDelegate()
     43     : m_refCount(1)
     44     , m_acceptsEditing(true)
     45 {
     46 }
     47 
     48 // IUnknown
     49 HRESULT STDMETHODCALLTYPE EditingDelegate::QueryInterface(REFIID riid, void** ppvObject)
     50 {
     51     *ppvObject = 0;
     52     if (IsEqualGUID(riid, IID_IUnknown))
     53         *ppvObject = static_cast<IWebEditingDelegate*>(this);
     54     else if (IsEqualGUID(riid, IID_IWebEditingDelegate))
     55         *ppvObject = static_cast<IWebEditingDelegate*>(this);
     56     else
     57         return E_NOINTERFACE;
     58 
     59     AddRef();
     60     return S_OK;
     61 }
     62 
     63 ULONG STDMETHODCALLTYPE EditingDelegate::AddRef(void)
     64 {
     65     return ++m_refCount;
     66 }
     67 
     68 ULONG STDMETHODCALLTYPE EditingDelegate::Release(void)
     69 {
     70     ULONG newRef = --m_refCount;
     71     if (!newRef)
     72         delete this;
     73 
     74     return newRef;
     75 }
     76 
     77 static wstring dumpPath(IDOMNode* node)
     78 {
     79     ASSERT(node);
     80 
     81     wstring result;
     82 
     83     BSTR name;
     84     if (FAILED(node->nodeName(&name)))
     85         return result;
     86     result.assign(name, SysStringLen(name));
     87     SysFreeString(name);
     88 
     89     COMPtr<IDOMNode> parent;
     90     if (SUCCEEDED(node->parentNode(&parent)))
     91         result += TEXT(" > ") + dumpPath(parent.get());
     92 
     93     return result;
     94 }
     95 
     96 static wstring dump(IDOMRange* range)
     97 {
     98     ASSERT(range);
     99 
    100     int startOffset;
    101     if (FAILED(range->startOffset(&startOffset)))
    102         return 0;
    103 
    104     int endOffset;
    105     if (FAILED(range->endOffset(&endOffset)))
    106         return 0;
    107 
    108     COMPtr<IDOMNode> startContainer;
    109     if (FAILED(range->startContainer(&startContainer)))
    110         return 0;
    111 
    112     COMPtr<IDOMNode> endContainer;
    113     if (FAILED(range->endContainer(&endContainer)))
    114         return 0;
    115 
    116     wchar_t buffer[1024];
    117     _snwprintf(buffer, ARRAYSIZE(buffer), L"range from %ld of %s to %ld of %s", startOffset, dumpPath(startContainer.get()), endOffset, dumpPath(endContainer.get()));
    118     return buffer;
    119 }
    120 
    121 HRESULT STDMETHODCALLTYPE EditingDelegate::shouldBeginEditingInDOMRange(
    122     /* [in] */ IWebView* webView,
    123     /* [in] */ IDOMRange* range,
    124     /* [retval][out] */ BOOL* result)
    125 {
    126     if (!result) {
    127         ASSERT_NOT_REACHED();
    128         return E_POINTER;
    129     }
    130 
    131     if (::gLayoutTestController->dumpEditingCallbacks() && !done)
    132         _tprintf(TEXT("EDITING DELEGATE: shouldBeginEditingInDOMRange:%s\n"), dump(range));
    133 
    134     *result = m_acceptsEditing;
    135     return S_OK;
    136 }
    137 
    138 HRESULT STDMETHODCALLTYPE EditingDelegate::shouldEndEditingInDOMRange(
    139     /* [in] */ IWebView* webView,
    140     /* [in] */ IDOMRange* range,
    141     /* [retval][out] */ BOOL* result)
    142 {
    143     if (!result) {
    144         ASSERT_NOT_REACHED();
    145         return E_POINTER;
    146     }
    147 
    148     if (::gLayoutTestController->dumpEditingCallbacks() && !done)
    149         _tprintf(TEXT("EDITING DELEGATE: shouldEndEditingInDOMRange:%s\n"), dump(range));
    150 
    151     *result = m_acceptsEditing;
    152     return S_OK;
    153 }
    154 
    155 HRESULT STDMETHODCALLTYPE EditingDelegate::shouldInsertNode(
    156     /* [in] */ IWebView* webView,
    157     /* [in] */ IDOMNode* node,
    158     /* [in] */ IDOMRange* range,
    159     /* [in] */ WebViewInsertAction action)
    160 {
    161     static LPCTSTR insertactionstring[] = {
    162         TEXT("WebViewInsertActionTyped"),
    163         TEXT("WebViewInsertActionPasted"),
    164         TEXT("WebViewInsertActionDropped"),
    165     };
    166 
    167     if (::gLayoutTestController->dumpEditingCallbacks() && !done)
    168         _tprintf(TEXT("EDITING DELEGATE: shouldInsertNode:%s replacingDOMRange:%s givenAction:%s\n"), dumpPath(node), dump(range), insertactionstring[action]);
    169 
    170     return S_OK;
    171 }
    172 
    173 HRESULT STDMETHODCALLTYPE EditingDelegate::shouldInsertText(
    174     /* [in] */ IWebView* webView,
    175     /* [in] */ BSTR text,
    176     /* [in] */ IDOMRange* range,
    177     /* [in] */ WebViewInsertAction action,
    178     /* [retval][out] */ BOOL* result)
    179 {
    180     if (!result) {
    181         ASSERT_NOT_REACHED();
    182         return E_POINTER;
    183     }
    184 
    185     static LPCTSTR insertactionstring[] = {
    186         TEXT("WebViewInsertActionTyped"),
    187         TEXT("WebViewInsertActionPasted"),
    188         TEXT("WebViewInsertActionDropped"),
    189     };
    190 
    191     if (::gLayoutTestController->dumpEditingCallbacks() && !done)
    192         _tprintf(TEXT("EDITING DELEGATE: shouldInsertText:%s replacingDOMRange:%s givenAction:%s\n"), text ? text : TEXT(""), dump(range), insertactionstring[action]);
    193 
    194     *result = m_acceptsEditing;
    195     return S_OK;
    196 }
    197 
    198 HRESULT STDMETHODCALLTYPE EditingDelegate::shouldDeleteDOMRange(
    199     /* [in] */ IWebView* webView,
    200     /* [in] */ IDOMRange* range,
    201     /* [retval][out] */ BOOL* result)
    202 {
    203     if (!result) {
    204         ASSERT_NOT_REACHED();
    205         return E_POINTER;
    206     }
    207 
    208     if (::gLayoutTestController->dumpEditingCallbacks() && !done)
    209         _tprintf(TEXT("EDITING DELEGATE: shouldDeleteDOMRange:%s\n"), dump(range));
    210 
    211     *result = m_acceptsEditing;
    212     return S_OK;
    213 }
    214 
    215 HRESULT STDMETHODCALLTYPE EditingDelegate::shouldChangeSelectedDOMRange(
    216     /* [in] */ IWebView* webView,
    217     /* [in] */ IDOMRange* currentRange,
    218     /* [in] */ IDOMRange* proposedRange,
    219     /* [in] */ WebSelectionAffinity selectionAffinity,
    220     /* [in] */ BOOL stillSelecting,
    221     /* [retval][out] */ BOOL* result)
    222 {
    223     if (!result) {
    224         ASSERT_NOT_REACHED();
    225         return E_POINTER;
    226     }
    227 
    228     static LPCTSTR affinitystring[] = {
    229         TEXT("NSSelectionAffinityUpstream"),
    230         TEXT("NSSelectionAffinityDownstream")
    231     };
    232     static LPCTSTR boolstring[] = {
    233         TEXT("FALSE"),
    234         TEXT("TRUE")
    235     };
    236 
    237     if (::gLayoutTestController->dumpEditingCallbacks() && !done)
    238         _tprintf(TEXT("EDITING DELEGATE: shouldChangeSelectedDOMRange:%s toDOMRange:%s affinity:%s stillSelecting:%s\n"), dump(currentRange), dump(proposedRange), affinitystring[selectionAffinity], boolstring[stillSelecting]);
    239 
    240     *result = m_acceptsEditing;
    241     return S_OK;
    242 }
    243 
    244 HRESULT STDMETHODCALLTYPE EditingDelegate::shouldApplyStyle(
    245     /* [in] */ IWebView* webView,
    246     /* [in] */ IDOMCSSStyleDeclaration* style,
    247     /* [in] */ IDOMRange* range,
    248     /* [retval][out] */ BOOL* result)
    249 {
    250     if (!result) {
    251         ASSERT_NOT_REACHED();
    252         return E_POINTER;
    253     }
    254 
    255     if (::gLayoutTestController->dumpEditingCallbacks() && !done)
    256         _tprintf(TEXT("EDITING DELEGATE: shouldApplyStyle:%s toElementsInDOMRange:%s\n"), TEXT("'style description'")/*[[style description] UTF8String]*/, dump(range));
    257 
    258     *result = m_acceptsEditing;
    259     return S_OK;
    260 }
    261 
    262 HRESULT STDMETHODCALLTYPE EditingDelegate::shouldChangeTypingStyle(
    263     /* [in] */ IWebView* webView,
    264     /* [in] */ IDOMCSSStyleDeclaration* currentStyle,
    265     /* [in] */ IDOMCSSStyleDeclaration* proposedStyle,
    266     /* [retval][out] */ BOOL* result)
    267 {
    268     if (!result) {
    269         ASSERT_NOT_REACHED();
    270         return E_POINTER;
    271     }
    272 
    273     if (::gLayoutTestController->dumpEditingCallbacks() && !done)
    274         _tprintf(TEXT("EDITING DELEGATE: shouldChangeTypingStyle:%s toStyle:%s\n"), TEXT("'currentStyle description'"), TEXT("'proposedStyle description'"));
    275 
    276     *result = m_acceptsEditing;
    277     return S_OK;
    278 }
    279 
    280 HRESULT STDMETHODCALLTYPE EditingDelegate::doPlatformCommand(
    281     /* [in] */ IWebView *webView,
    282     /* [in] */ BSTR command,
    283     /* [retval][out] */ BOOL *result)
    284 {
    285     if (!result) {
    286         ASSERT_NOT_REACHED();
    287         return E_POINTER;
    288     }
    289 
    290     if (::gLayoutTestController->dumpEditingCallbacks() && !done)
    291         _tprintf(TEXT("EDITING DELEGATE: doPlatformCommand:%s\n"), command ? command : TEXT(""));
    292 
    293     *result = m_acceptsEditing;
    294     return S_OK;
    295 }
    296 
    297 HRESULT STDMETHODCALLTYPE EditingDelegate::webViewDidBeginEditing(
    298     /* [in] */ IWebNotification* notification)
    299 {
    300     if (::gLayoutTestController->dumpEditingCallbacks() && !done) {
    301         BSTR name;
    302         notification->name(&name);
    303         _tprintf(TEXT("EDITING DELEGATE: webViewDidBeginEditing:%s\n"), name ? name : TEXT(""));
    304         SysFreeString(name);
    305     }
    306     return S_OK;
    307 }
    308 
    309 HRESULT STDMETHODCALLTYPE EditingDelegate::webViewDidChange(
    310     /* [in] */ IWebNotification *notification)
    311 {
    312     if (::gLayoutTestController->dumpEditingCallbacks() && !done) {
    313         BSTR name;
    314         notification->name(&name);
    315         _tprintf(TEXT("EDITING DELEGATE: webViewDidBeginEditing:%s\n"), name ? name : TEXT(""));
    316         SysFreeString(name);
    317     }
    318     return S_OK;
    319 }
    320 
    321 HRESULT STDMETHODCALLTYPE EditingDelegate::webViewDidEndEditing(
    322     /* [in] */ IWebNotification *notification)
    323 {
    324     if (::gLayoutTestController->dumpEditingCallbacks() && !done) {
    325         BSTR name;
    326         notification->name(&name);
    327         _tprintf(TEXT("EDITING DELEGATE: webViewDidEndEditing:%s\n"), name ? name : TEXT(""));
    328         SysFreeString(name);
    329     }
    330     return S_OK;
    331 }
    332 
    333 HRESULT STDMETHODCALLTYPE EditingDelegate::webViewDidChangeTypingStyle(
    334     /* [in] */ IWebNotification *notification)
    335 {
    336     if (::gLayoutTestController->dumpEditingCallbacks() && !done) {
    337         BSTR name;
    338         notification->name(&name);
    339         _tprintf(TEXT("EDITING DELEGATE: webViewDidChangeTypingStyle:%s\n"), name ? name : TEXT(""));
    340         SysFreeString(name);
    341     }
    342     return S_OK;
    343 }
    344 
    345 HRESULT STDMETHODCALLTYPE EditingDelegate::webViewDidChangeSelection(
    346     /* [in] */ IWebNotification *notification)
    347 {
    348     if (::gLayoutTestController->dumpEditingCallbacks() && !done) {
    349         BSTR name;
    350         notification->name(&name);
    351         _tprintf(TEXT("EDITING DELEGATE: webViewDidChangeSelection:%s\n"), name ? name : TEXT(""));
    352         SysFreeString(name);
    353     }
    354     return S_OK;
    355 }
    356 
    357 static int indexOfFirstWordCharacter(const TCHAR* text)
    358 {
    359     const TCHAR* cursor = text;
    360     while (*cursor && !iswalpha(*cursor))
    361         ++cursor;
    362     return *cursor ? (cursor - text) : -1;
    363 };
    364 
    365 static int wordLength(const TCHAR* text)
    366 {
    367     const TCHAR* cursor = text;
    368     while (*cursor && iswalpha(*cursor))
    369         ++cursor;
    370     return cursor - text;
    371 };
    372 
    373 HRESULT STDMETHODCALLTYPE EditingDelegate::checkSpellingOfString(
    374             /* [in] */ IWebView* view,
    375             /* [in] */ LPCTSTR text,
    376             /* [in] */ int length,
    377             /* [out] */ int* misspellingLocation,
    378             /* [out] */ int* misspellingLength)
    379 {
    380     static const TCHAR* misspelledWords[] = {
    381         // These words are known misspelled words in webkit tests.
    382         // If there are other misspelled words in webkit tests, please add them in
    383         // this array.
    384         TEXT("foo"),
    385         TEXT("Foo"),
    386         TEXT("baz"),
    387         TEXT("fo"),
    388         TEXT("LibertyF"),
    389         TEXT("chello"),
    390         TEXT("xxxtestxxx"),
    391         TEXT("XXxxx"),
    392         TEXT("Textx"),
    393         TEXT("blockquoted"),
    394         TEXT("asd"),
    395         TEXT("Lorem"),
    396         TEXT("Nunc"),
    397         TEXT("Curabitur"),
    398         TEXT("eu"),
    399         TEXT("adlj"),
    400         TEXT("adaasj"),
    401         TEXT("sdklj"),
    402         TEXT("jlkds"),
    403         TEXT("jsaada"),
    404         TEXT("jlda"),
    405         TEXT("zz"),
    406         TEXT("contentEditable"),
    407         0,
    408     };
    409 
    410     wstring textString(text, length);
    411     int wordStart = indexOfFirstWordCharacter(textString.c_str());
    412     if (-1 == wordStart)
    413         return S_OK;
    414     wstring word = textString.substr(wordStart, wordLength(textString.c_str() + wordStart));
    415     for (size_t i = 0; misspelledWords[i]; ++i) {
    416         if (word == misspelledWords[i]) {
    417             *misspellingLocation = wordStart;
    418             *misspellingLength = word.size();
    419             break;
    420         }
    421     }
    422 
    423     return S_OK;
    424 }
    425