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