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