• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
EditingDelegate()42 EditingDelegate::EditingDelegate()
43     : m_refCount(1)
44     , m_acceptsEditing(true)
45 {
46 }
47 
48 // IUnknown
QueryInterface(REFIID riid,void ** ppvObject)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 
AddRef(void)63 ULONG STDMETHODCALLTYPE EditingDelegate::AddRef(void)
64 {
65     return ++m_refCount;
66 }
67 
Release(void)68 ULONG STDMETHODCALLTYPE EditingDelegate::Release(void)
69 {
70     ULONG newRef = --m_refCount;
71     if (!newRef)
72         delete this;
73 
74     return newRef;
75 }
76 
dumpPath(IDOMNode * node)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 
dump(IDOMRange * range)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 
shouldBeginEditingInDOMRange(IWebView * webView,IDOMRange * range,BOOL * result)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 
shouldEndEditingInDOMRange(IWebView * webView,IDOMRange * range,BOOL * result)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 
shouldInsertNode(IWebView * webView,IDOMNode * node,IDOMRange * range,WebViewInsertAction action)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 
shouldInsertText(IWebView * webView,BSTR text,IDOMRange * range,WebViewInsertAction action,BOOL * result)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 
shouldDeleteDOMRange(IWebView * webView,IDOMRange * range,BOOL * result)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 
shouldChangeSelectedDOMRange(IWebView * webView,IDOMRange * currentRange,IDOMRange * proposedRange,WebSelectionAffinity selectionAffinity,BOOL stillSelecting,BOOL * result)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 
shouldApplyStyle(IWebView * webView,IDOMCSSStyleDeclaration * style,IDOMRange * range,BOOL * result)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 
shouldChangeTypingStyle(IWebView * webView,IDOMCSSStyleDeclaration * currentStyle,IDOMCSSStyleDeclaration * proposedStyle,BOOL * result)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 
doPlatformCommand(IWebView * webView,BSTR command,BOOL * result)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 
webViewDidBeginEditing(IWebNotification * notification)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 
webViewDidChange(IWebNotification * notification)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 
webViewDidEndEditing(IWebNotification * notification)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 
webViewDidChangeTypingStyle(IWebNotification * notification)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 
webViewDidChangeSelection(IWebNotification * notification)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 
indexOfFirstWordCharacter(const TCHAR * text)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 
wordLength(const TCHAR * text)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 
checkSpellingOfString(IWebView * view,LPCTSTR text,int length,int * misspellingLocation,int * misspellingLength)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