1fc943185c38e5942f3ad4b094ed2e77d9fbf5c3
[WebKit-https.git] / WebKitTools / DumpRenderTree / win / LayoutTestControllerWin.cpp
1 /*
2  * Copyright (C) 2006, 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 "DumpRenderTree.h"
30 #include "LayoutTestController.h"
31
32 #include "EditingDelegate.h"
33 #include "PolicyDelegate.h"
34 #include "WorkQueue.h"
35 #include "WorkQueueItem.h"
36 #include <WebCore/COMPtr.h>
37 #include <wtf/Platform.h>
38 #include <wtf/RetainPtr.h>
39 #include <wtf/Vector.h>
40 #include <JavaScriptCore/Assertions.h>
41 #include <JavaScriptCore/JavaScriptCore.h>
42 #include <JavaScriptCore/JSRetainPtr.h>
43 #include <WebKit/IWebHistory.h>
44 #include <WebKit/IWebPreferencesPrivate.h>
45 #include <WebKit/IWebViewPrivate.h>
46 #include <WebKit/WebKit.h>
47 #include <string>
48 #include <CoreFoundation/CoreFoundation.h>
49 #include <shlwapi.h>
50 #include <shlguid.h>
51 #include <shobjidl.h>
52
53 using std::string;
54 using std::wstring;
55
56 LayoutTestController::~LayoutTestController()
57 {
58     COMPtr<IWebView> webView;
59     if (FAILED(frame->webView(&webView)))
60         return;
61
62     // reset webview-related states back to default values in preparation for next test
63
64     COMPtr<IWebViewPrivate> viewPrivate;
65     if (SUCCEEDED(webView->QueryInterface(&viewPrivate)))
66         viewPrivate->setTabKeyCyclesThroughElements(TRUE);
67
68     COMPtr<IWebViewEditing> viewEditing;
69     if (FAILED(webView->QueryInterface(&viewEditing)))
70         return;
71     COMPtr<IWebEditingDelegate> delegate;
72     if (FAILED(viewEditing->editingDelegate(&delegate)))
73         return;
74     COMPtr<EditingDelegate> editingDelegate(Query, viewEditing.get());
75     if (editingDelegate)
76         editingDelegate->setAcceptsEditing(TRUE);
77 }
78
79 void LayoutTestController::addDisallowedURL(JSStringRef url)
80 {
81     // FIXME: Implement!
82 }
83
84 void LayoutTestController::clearBackForwardList()
85 {
86     COMPtr<IWebView> webView;
87     if (FAILED(frame->webView(&webView)))
88         return;
89
90     COMPtr<IWebBackForwardList> backForwardList;
91     if (FAILED(webView->backForwardList(&backForwardList)))
92         return;
93
94     COMPtr<IWebHistoryItem> item;
95     if (FAILED(backForwardList->currentItem(&item)))
96         return;
97
98     // We clear the history by setting the back/forward list's capacity to 0
99     // then restoring it back and adding back the current item.
100     int capacity;
101     if (FAILED(backForwardList->capacity(&capacity)))
102         return;
103
104     backForwardList->setCapacity(0);
105     backForwardList->setCapacity(capacity);
106     backForwardList->addItem(item.get());
107     backForwardList->goToItem(item.get());
108 }
109
110 JSStringRef LayoutTestController::copyDecodedHostName(JSStringRef name)
111 {
112     // FIXME: Implement!
113     return 0;
114 }
115
116 JSStringRef LayoutTestController::copyEncodedHostName(JSStringRef name)
117 {
118     // FIXME: Implement!
119     return 0;
120 }
121
122 void LayoutTestController::display()
123 {
124     displayWebView();
125 }
126
127 void LayoutTestController::keepWebHistory()
128 {
129     COMPtr<IWebHistory> history(Create, CLSID_WebHistory);
130     if (!history)
131         return;
132
133     COMPtr<IWebHistory> sharedHistory(Create, CLSID_WebHistory);
134     if (!sharedHistory)
135         return;
136
137     history->setOptionalSharedHistory(sharedHistory.get());
138 }
139
140 void LayoutTestController::notifyDone()
141 {
142     // Same as on mac.  This can be shared.
143     if (m_waitToDump && !topLoadingFrame && !WorkQueue::shared()->count())
144         dump();
145     m_waitToDump = false;
146 }
147
148 void LayoutTestController::queueBackNavigation(int howFarBack)
149 {
150     // Same as on mac.  This can be shared.
151     WorkQueue::shared()->queue(new BackItem(howFarBack));
152 }
153
154 void LayoutTestController::queueForwardNavigation(int howFarForward)
155 {
156     // Same as on mac.  This can be shared.
157     WorkQueue::shared()->queue(new ForwardItem(howFarForward));
158 }
159
160 static wstring jsStringRefToWString(JSStringRef jsStr)
161 {
162     size_t length = JSStringGetLength(jsStr);
163     Vector<WCHAR> buffer(length + 1);
164     memcpy(buffer.data(), JSStringGetCharactersPtr(jsStr), length * sizeof(WCHAR));
165     buffer[length] = '\0';
166
167     return buffer.data();
168 }
169
170 void LayoutTestController::queueLoad(JSStringRef url, JSStringRef target)
171 {
172     COMPtr<IWebDataSource> dataSource;
173     if (FAILED(frame->dataSource(&dataSource)))
174         return;
175
176     COMPtr<IWebURLResponse> response;
177     if (FAILED(dataSource->response(&response)) || !response)
178         return;
179
180     BSTR responseURLBSTR;
181     if (FAILED(response->URL(&responseURLBSTR)))
182         return;
183     wstring responseURL(responseURLBSTR, SysStringLen(responseURLBSTR));
184     SysFreeString(responseURLBSTR);
185
186     // FIXME: We should do real relative URL resolution here.
187     int lastSlash = responseURL.rfind('/');
188     if (lastSlash != -1)
189         responseURL = responseURL.substr(0, lastSlash);
190
191     wstring wURL = jsStringRefToWString(url);
192     wstring wAbosuluteURL = responseURL + TEXT("/") + wURL;
193     JSRetainPtr<JSStringRef> jsAbsoluteURL(Adopt, JSStringCreateWithCharacters(wAbosuluteURL.data(), wAbosuluteURL.length()));
194
195     WorkQueue::shared()->queue(new LoadItem(jsAbsoluteURL.get(), target));
196 }
197
198 void LayoutTestController::queueReload()
199 {
200     WorkQueue::shared()->queue(new ReloadItem);
201 }
202
203 void LayoutTestController::queueScript(JSStringRef script)
204 {
205     WorkQueue::shared()->queue(new ScriptItem(script));
206 }
207
208 void LayoutTestController::setAcceptsEditing(bool acceptsEditing)
209 {
210     COMPtr<IWebView> webView;
211     if (FAILED(frame->webView(&webView)))
212         return;
213
214     COMPtr<IWebViewEditing> viewEditing;
215     if (FAILED(webView->QueryInterface(&viewEditing)))
216         return;
217
218     COMPtr<IWebEditingDelegate> delegate;
219     if (FAILED(viewEditing->editingDelegate(&delegate)))
220         return;
221
222     EditingDelegate* editingDelegate = (EditingDelegate*)(IWebEditingDelegate*)delegate.get();
223     editingDelegate->setAcceptsEditing(acceptsEditing);
224 }
225
226 void LayoutTestController::setAuthorAndUserStylesEnabled(bool flag)
227 {
228     COMPtr<IWebView> webView;
229     if (FAILED(frame->webView(&webView)))
230         return;
231
232     COMPtr<IWebPreferences> preferences;
233     if (FAILED(webView->preferences(&preferences)))
234         return;
235
236     COMPtr<IWebPreferencesPrivate> prefsPrivate(Query, preferences);
237     if (!prefsPrivate)
238         return;
239
240     prefsPrivate->setAuthorAndUserStylesEnabled(flag);
241 }
242
243 void LayoutTestController::setCustomPolicyDelegate(bool setDelegate)
244 {
245     COMPtr<IWebView> webView;
246     if (FAILED(frame->webView(&webView)))
247         return;
248
249     if (setDelegate)
250         webView->setPolicyDelegate(policyDelegate);
251     else
252         webView->setPolicyDelegate(NULL);
253 }
254
255 void LayoutTestController::setMainFrameIsFirstResponder(bool flag)
256 {
257     // FIXME: Implement!
258 }
259
260 void LayoutTestController::setPrivateBrowsingEnabled(bool privateBrowsingEnabled)
261 {
262     COMPtr<IWebView> webView;
263     if (FAILED(frame->webView(&webView)))
264         return;
265
266     COMPtr<IWebPreferences> preferences;
267     if (FAILED(webView->preferences(&preferences)))
268         return;
269
270     preferences->setPrivateBrowsingEnabled(privateBrowsingEnabled);
271 }
272
273 void LayoutTestController::setTabKeyCyclesThroughElements(bool shouldCycle)
274 {
275     COMPtr<IWebView> webView;
276     if (FAILED(frame->webView(&webView)))
277         return;
278
279     COMPtr<IWebViewPrivate> viewPrivate;
280     if (FAILED(webView->QueryInterface(&viewPrivate)))
281         return;
282
283     viewPrivate->setTabKeyCyclesThroughElements(shouldCycle ? TRUE : FALSE);
284 }
285
286 void LayoutTestController::setUseDashboardCompatibilityMode(bool flag)
287 {
288     // FIXME: Implement!
289 }
290
291 void LayoutTestController::setUserStyleSheetEnabled(bool flag)
292 {
293     COMPtr<IWebView> webView;
294     if (FAILED(frame->webView(&webView)))
295         return;
296
297     COMPtr<IWebPreferences> preferences;
298     if (FAILED(webView->preferences(&preferences)))
299         return;
300
301    preferences->setUserStyleSheetEnabled(flag);
302 }
303
304 bool appendComponentToPath(wstring& path, const wstring& component)
305 {
306     WCHAR buffer[MAX_PATH];
307
308     if (path.size() + 1 > MAX_PATH)
309         return false;
310
311     memcpy(buffer, path.data(), path.size() * sizeof(WCHAR));
312     buffer[path.size()] = '\0';
313
314     if (!PathAppendW(buffer, component.c_str()))
315         return false;
316
317     path = wstring(buffer);
318     return true;
319 }
320
321 static bool followShortcuts(wstring& path)
322 {
323     if (PathFileExists(path.c_str()))
324         return true;
325
326     // Do we have a shortcut?
327     path.append(TEXT(".lnk"));
328     if (!PathFileExists(path.c_str()))
329        return false;
330
331     // We have a shortcut, find its target.
332     COMPtr<IShellLink> shortcut(Create, CLSID_ShellLink);
333     if (!shortcut)
334        return false;
335     COMPtr<IPersistFile> persistFile(Query, shortcut);
336     if (!shortcut)
337         return false;
338     if (FAILED(persistFile->Load(path.c_str(), STGM_READ)))
339         return false;
340     if (FAILED(shortcut->Resolve(0, 0)))
341         return false;
342     WCHAR targetPath[MAX_PATH];
343     DWORD targetPathLen = _countof(targetPath);
344     if (FAILED(shortcut->GetPath(targetPath, targetPathLen, 0, 0)))
345         return false;
346     if (!PathFileExists(targetPath))
347         return false;
348     // Use the target path as the result path instead.
349     path = wstring(targetPath);
350
351     return true;
352 }
353
354 static bool resolveCygwinPath(const wstring& cygwinPath, wstring& windowsPath)
355 {
356     if (cygwinPath[0] != '/')
357         return false;
358
359     // Get the Root path.
360     WCHAR rootPath[MAX_PATH];
361     DWORD rootPathSize = _countof(rootPath);
362     DWORD keyType;
363     DWORD result = ::SHGetValueW(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Cygnus Solutions\\Cygwin\\mounts v2\\/"), TEXT("native"), &keyType, &rootPath, &rootPathSize);
364     
365     if (result != ERROR_SUCCESS || keyType != REG_SZ)
366         return false;
367
368     windowsPath = wstring(rootPath, rootPathSize);
369
370     int oldPos = 1;
371     while (1) {
372         int newPos = cygwinPath.find('/', oldPos);
373
374         if (newPos == -1) {
375             wstring pathComponent = cygwinPath.substr(oldPos);
376
377             if (!appendComponentToPath(windowsPath, pathComponent))
378                return false;
379
380             if (!followShortcuts(windowsPath))
381                 return false;
382
383             break;
384         }
385
386         wstring pathComponent = cygwinPath.substr(oldPos, newPos - oldPos);
387         if (!appendComponentToPath(windowsPath, pathComponent))
388             return false;
389
390         if (!followShortcuts(windowsPath))
391             return false;
392
393         oldPos = newPos + 1;
394     }
395     return true;
396 }
397
398 static wstring cfStringRefToWString(CFStringRef cfStr)
399 {
400     Vector<wchar_t> v(CFStringGetLength(cfStr));
401     CFStringGetCharacters(cfStr, CFRangeMake(0, CFStringGetLength(cfStr)), (UniChar *)v.data());
402
403     return wstring(v.data(), v.size());
404 }
405
406 void LayoutTestController::setUserStyleSheetLocation(JSStringRef jsURL)
407 {
408     COMPtr<IWebView> webView;
409     if (FAILED(frame->webView(&webView)))
410         return;
411
412     COMPtr<IWebPreferences> preferences;
413     if (FAILED(webView->preferences(&preferences)))
414         return;
415
416     RetainPtr<CFStringRef> urlString(AdoptCF, JSStringCopyCFString(0, jsURL));
417     RetainPtr<CFURLRef> url(AdoptCF, CFURLCreateWithString(0, urlString.get(), 0));
418     if (!url)
419         return;
420
421     // Now copy the file system path, POSIX style.
422     RetainPtr<CFStringRef> pathCF(AdoptCF, CFURLCopyFileSystemPath(url.get(), kCFURLPOSIXPathStyle));
423     if (!pathCF)
424         return;
425
426     wstring path = cfStringRefToWString(pathCF.get());
427
428     wstring resultPath;
429     if (!resolveCygwinPath(path, resultPath))
430         return;
431
432     // The path has been resolved, now convert it back to a CFURL.
433     int result = WideCharToMultiByte(CP_UTF8, 0, resultPath.c_str(), resultPath.size() + 1, 0, 0, 0, 0);
434     Vector<char> utf8Vector(result);
435     result = WideCharToMultiByte(CP_UTF8, 0, resultPath.c_str(), resultPath.size() + 1, utf8Vector.data(), result, 0, 0);
436     if (!result)
437         return;
438
439     url = CFURLCreateFromFileSystemRepresentation(0, (const UInt8*)utf8Vector.data(), utf8Vector.size() - 1, false);
440     if (!url)
441         return;
442
443     resultPath = cfStringRefToWString(CFURLGetString(url.get()));
444
445     BSTR resultPathBSTR = SysAllocStringLen(resultPath.data(), resultPath.size());
446     preferences->setUserStyleSheetLocation(resultPathBSTR);
447     SysFreeString(resultPathBSTR);
448 }
449
450 void LayoutTestController::setWindowIsKey(bool flag)
451 {
452     COMPtr<IWebView> webView;
453     if (FAILED(frame->webView(&webView)))
454         return;
455
456     COMPtr<IWebViewPrivate> viewPrivate;
457     if (FAILED(webView->QueryInterface(&viewPrivate)))
458         return;
459
460     HWND webViewWindow;
461     if (FAILED(viewPrivate->viewWindow((OLE_HANDLE*)&webViewWindow)))
462         return;
463
464     ::SendMessage(webViewWindow, flag ? WM_SETFOCUS : WM_KILLFOCUS, (WPARAM)::GetDesktopWindow(), 0);
465 }
466
467 static const CFTimeInterval waitToDumpWatchdogInterval = 10.0;
468
469 static void waitUntilDoneWatchdogFired(CFRunLoopTimerRef timer, void* info)
470 {
471     const char* message = "FAIL: Timed out waiting for notifyDone to be called\n";
472     fprintf(stderr, message);
473     fprintf(stdout, message);
474     dump();
475 }
476
477 void LayoutTestController::setWaitToDump(bool waitUntilDone)
478 {
479     // Same as on mac.  This can be shared.
480     m_waitToDump = waitUntilDone;
481     if (m_waitToDump && !waitToDumpWatchdog)
482         ::waitToDumpWatchdog = CFRunLoopTimerCreate(0, 0, waitToDumpWatchdogInterval, 0, 0, waitUntilDoneWatchdogFired, NULL);
483 }
484
485 int LayoutTestController::windowCount()
486 {
487     // FIXME: Implement!
488     return 1;
489 }