[Win] Various DumpRenderTree Fixes.
[WebKit-https.git] / Tools / DumpRenderTree / win / EditingDelegate.cpp
1 /*
2  * Copyright (C) 2007, 2014 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 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 <comutil.h>
33 #include "DumpRenderTree.h"
34 #include "TestRunner.h"
35 #include <WebCore/COMPtr.h>
36 #include <wtf/Assertions.h>
37 #include <wtf/Platform.h>
38 #include <string>
39 #include <tchar.h>
40
41 using std::wstring;
42
43 EditingDelegate::EditingDelegate()
44     : m_refCount(1)
45     , m_acceptsEditing(true)
46 {
47 }
48
49 // IUnknown
50 HRESULT EditingDelegate::QueryInterface(REFIID riid, void** ppvObject)
51 {
52     *ppvObject = 0;
53     if (IsEqualGUID(riid, IID_IUnknown))
54         *ppvObject = static_cast<IWebEditingDelegate*>(this);
55     else if (IsEqualGUID(riid, IID_IWebEditingDelegate))
56         *ppvObject = static_cast<IWebEditingDelegate*>(this);
57     else
58         return E_NOINTERFACE;
59
60     AddRef();
61     return S_OK;
62 }
63
64 ULONG EditingDelegate::AddRef(void)
65 {
66     return ++m_refCount;
67 }
68
69 ULONG EditingDelegate::Release(void)
70 {
71     ULONG newRef = --m_refCount;
72     if (!newRef)
73         delete this;
74
75     return newRef;
76 }
77
78 static wstring dumpPath(IDOMNode* node)
79 {
80     ASSERT(node);
81
82     wstring result;
83
84     _bstr_t name;
85     if (FAILED(node->nodeName(&name.GetBSTR())))
86         return result;
87     result.assign(static_cast<wchar_t*>(name), name.length());
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 EditingDelegate::shouldBeginEditingInDOMRange(IWebView* /*webView*/, IDOMRange* range, BOOL* result)
122 {
123     if (!result) {
124         ASSERT_NOT_REACHED();
125         return E_POINTER;
126     }
127
128     if (::gTestRunner->dumpEditingCallbacks() && !done)
129         _tprintf(TEXT("EDITING DELEGATE: shouldBeginEditingInDOMRange:%s\n"), dump(range));
130
131     *result = m_acceptsEditing;
132     return S_OK;
133 }
134
135 HRESULT EditingDelegate::shouldEndEditingInDOMRange(IWebView* /*webView*/, IDOMRange* range, BOOL* result)
136 {
137     if (!result) {
138         ASSERT_NOT_REACHED();
139         return E_POINTER;
140     }
141
142     if (::gTestRunner->dumpEditingCallbacks() && !done)
143         _tprintf(TEXT("EDITING DELEGATE: shouldEndEditingInDOMRange:%s\n"), dump(range));
144
145     *result = m_acceptsEditing;
146     return S_OK;
147 }
148
149 HRESULT EditingDelegate::shouldInsertNode(IWebView* /*webView*/, IDOMNode* node, IDOMRange* range, WebViewInsertAction action)
150 {
151     static LPCTSTR insertactionstring[] = {
152         TEXT("WebViewInsertActionTyped"),
153         TEXT("WebViewInsertActionPasted"),
154         TEXT("WebViewInsertActionDropped"),
155     };
156
157     if (::gTestRunner->dumpEditingCallbacks() && !done)
158         _tprintf(TEXT("EDITING DELEGATE: shouldInsertNode:%s replacingDOMRange:%s givenAction:%s\n"), dumpPath(node), dump(range), insertactionstring[action]);
159
160     return S_OK;
161 }
162
163 HRESULT EditingDelegate::shouldInsertText(IWebView* /*webView*/, BSTR text, IDOMRange* range, WebViewInsertAction action, BOOL* result)
164 {
165     if (!result) {
166         ASSERT_NOT_REACHED();
167         return E_POINTER;
168     }
169
170     static LPCTSTR insertactionstring[] = {
171         TEXT("WebViewInsertActionTyped"),
172         TEXT("WebViewInsertActionPasted"),
173         TEXT("WebViewInsertActionDropped"),
174     };
175
176     if (::gTestRunner->dumpEditingCallbacks() && !done)
177         _tprintf(TEXT("EDITING DELEGATE: shouldInsertText:%s replacingDOMRange:%s givenAction:%s\n"), text ? text : TEXT(""), dump(range), insertactionstring[action]);
178
179     *result = m_acceptsEditing;
180     return S_OK;
181 }
182
183 HRESULT EditingDelegate::shouldDeleteDOMRange(IWebView* /*webView*/, IDOMRange* range, BOOL* result)
184 {
185     if (!result) {
186         ASSERT_NOT_REACHED();
187         return E_POINTER;
188     }
189
190     if (::gTestRunner->dumpEditingCallbacks() && !done)
191         _tprintf(TEXT("EDITING DELEGATE: shouldDeleteDOMRange:%s\n"), dump(range));
192
193     *result = m_acceptsEditing;
194     return S_OK;
195 }
196
197 HRESULT EditingDelegate::shouldChangeSelectedDOMRange(IWebView* /*webView*/, IDOMRange* currentRange, IDOMRange* proposedRange,
198     WebSelectionAffinity selectionAffinity, BOOL stillSelecting, BOOL* result)
199 {
200     if (!result) {
201         ASSERT_NOT_REACHED();
202         return E_POINTER;
203     }
204
205     static LPCTSTR affinitystring[] = {
206         TEXT("NSSelectionAffinityUpstream"),
207         TEXT("NSSelectionAffinityDownstream")
208     };
209     static LPCTSTR boolstring[] = {
210         TEXT("FALSE"),
211         TEXT("TRUE")
212     };
213
214     if (::gTestRunner->dumpEditingCallbacks() && !done)
215         _tprintf(TEXT("EDITING DELEGATE: shouldChangeSelectedDOMRange:%s toDOMRange:%s affinity:%s stillSelecting:%s\n"), dump(currentRange), dump(proposedRange), affinitystring[selectionAffinity], boolstring[stillSelecting]);
216
217     *result = m_acceptsEditing;
218     return S_OK;
219 }
220
221 HRESULT EditingDelegate::shouldApplyStyle(IWebView* /*webView*/, IDOMCSSStyleDeclaration* style, IDOMRange* range, BOOL* result)
222 {
223     if (!result) {
224         ASSERT_NOT_REACHED();
225         return E_POINTER;
226     }
227
228     if (::gTestRunner->dumpEditingCallbacks() && !done)
229         _tprintf(TEXT("EDITING DELEGATE: shouldApplyStyle:%s toElementsInDOMRange:%s\n"), TEXT("'style description'")/*[[style description] UTF8String]*/, dump(range));
230
231     *result = m_acceptsEditing;
232     return S_OK;
233 }
234
235 HRESULT EditingDelegate::shouldChangeTypingStyle(IWebView* /*webView*/, IDOMCSSStyleDeclaration* currentStyle, IDOMCSSStyleDeclaration* proposedStyle, BOOL* result)
236 {
237     if (!result) {
238         ASSERT_NOT_REACHED();
239         return E_POINTER;
240     }
241
242     if (::gTestRunner->dumpEditingCallbacks() && !done)
243         _tprintf(TEXT("EDITING DELEGATE: shouldChangeTypingStyle:%s toStyle:%s\n"), TEXT("'currentStyle description'"), TEXT("'proposedStyle description'"));
244
245     *result = m_acceptsEditing;
246     return S_OK;
247 }
248
249 HRESULT EditingDelegate::doPlatformCommand(IWebView* /*webView*/, BSTR command, BOOL* result)
250 {
251     if (!result) {
252         ASSERT_NOT_REACHED();
253         return E_POINTER;
254     }
255
256     if (::gTestRunner->dumpEditingCallbacks() && !done)
257         _tprintf(TEXT("EDITING DELEGATE: doPlatformCommand:%s\n"), command ? command : TEXT(""));
258
259     *result = m_acceptsEditing;
260     return S_OK;
261 }
262
263 HRESULT EditingDelegate::webViewDidBeginEditing(IWebNotification* notification)
264 {
265     if (::gTestRunner->dumpEditingCallbacks() && !done) {
266         _bstr_t name;
267         notification->name(&name.GetBSTR());
268         _tprintf(TEXT("EDITING DELEGATE: webViewDidBeginEditing:%s\n"), static_cast<TCHAR*>(name));
269     }
270     return S_OK;
271 }
272
273 HRESULT EditingDelegate::webViewDidChange(IWebNotification* notification)
274 {
275     if (::gTestRunner->dumpEditingCallbacks() && !done) {
276         _bstr_t name;
277         notification->name(&name.GetBSTR());
278         _tprintf(TEXT("EDITING DELEGATE: webViewDidBeginEditing:%s\n"), static_cast<TCHAR*>(name));
279     }
280     return S_OK;
281 }
282
283 HRESULT EditingDelegate::webViewDidEndEditing(IWebNotification* notification)
284 {
285     if (::gTestRunner->dumpEditingCallbacks() && !done) {
286         _bstr_t name;
287         notification->name(&name.GetBSTR());
288         _tprintf(TEXT("EDITING DELEGATE: webViewDidEndEditing:%s\n"), static_cast<TCHAR*>(name));
289     }
290     return S_OK;
291 }
292
293 HRESULT EditingDelegate::webViewDidChangeTypingStyle(IWebNotification* notification)
294 {
295     if (::gTestRunner->dumpEditingCallbacks() && !done) {
296         _bstr_t name;
297         notification->name(&name.GetBSTR());
298         _tprintf(TEXT("EDITING DELEGATE: webViewDidChangeTypingStyle:%s\n"), static_cast<TCHAR*>(name));
299     }
300     return S_OK;
301 }
302
303 HRESULT EditingDelegate::webViewDidChangeSelection(IWebNotification* notification)
304 {
305     if (::gTestRunner->dumpEditingCallbacks() && !done) {
306         _bstr_t name;
307         notification->name(&name.GetBSTR());
308         _tprintf(TEXT("EDITING DELEGATE: webViewDidChangeSelection:%s\n"), static_cast<TCHAR*>(name));
309     }
310     return S_OK;
311 }
312
313 static int indexOfFirstWordCharacter(const TCHAR* text)
314 {
315     const TCHAR* cursor = text;
316     while (*cursor && !iswalpha(*cursor))
317         ++cursor;
318     return *cursor ? (cursor - text) : -1;
319 };
320
321 static int wordLength(const TCHAR* text)
322 {
323     const TCHAR* cursor = text;
324     while (*cursor && iswalpha(*cursor))
325         ++cursor;
326     return cursor - text;
327 };
328
329 HRESULT EditingDelegate::checkSpellingOfString(
330             /* [in] */ IWebView* view,
331             /* [in] */ LPCTSTR text,
332             /* [in] */ int length,
333             /* [out] */ int* misspellingLocation,
334             /* [out] */ int* misspellingLength)
335 {
336     static const TCHAR* misspelledWords[] = {
337         // These words are known misspelled words in webkit tests.
338         // If there are other misspelled words in webkit tests, please add them in
339         // this array.
340         TEXT("foo"),
341         TEXT("Foo"),
342         TEXT("baz"),
343         TEXT("fo"),
344         TEXT("LibertyF"),
345         TEXT("chello"),
346         TEXT("xxxtestxxx"),
347         TEXT("XXxxx"),
348         TEXT("Textx"),
349         TEXT("blockquoted"),
350         TEXT("asd"),
351         TEXT("Lorem"),
352         TEXT("Nunc"),
353         TEXT("Curabitur"),
354         TEXT("eu"),
355         TEXT("adlj"),
356         TEXT("adaasj"),
357         TEXT("sdklj"),
358         TEXT("jlkds"),
359         TEXT("jsaada"),
360         TEXT("jlda"),
361         TEXT("zz"),
362         TEXT("contentEditable"),
363         0,
364     };
365
366     wstring textString(text, length);
367     int wordStart = indexOfFirstWordCharacter(textString.c_str());
368     if (-1 == wordStart)
369         return S_OK;
370     wstring word = textString.substr(wordStart, wordLength(textString.c_str() + wordStart));
371     for (size_t i = 0; misspelledWords[i]; ++i) {
372         if (word == misspelledWords[i]) {
373             *misspellingLocation = wordStart;
374             *misspellingLength = word.size();
375             break;
376         }
377     }
378
379     return S_OK;
380 }