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