2 * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
29 #include "DumpRenderTree.h"
30 #include "LayoutTestController.h"
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>
48 #include <CoreFoundation/CoreFoundation.h>
56 static bool resolveCygwinPath(const wstring& cygwinPath, wstring& windowsPath);
58 LayoutTestController::~LayoutTestController()
60 COMPtr<IWebView> webView;
61 if (FAILED(frame->webView(&webView)))
64 // reset webview-related states back to default values in preparation for next test
66 COMPtr<IWebViewPrivate> viewPrivate;
67 if (SUCCEEDED(webView->QueryInterface(&viewPrivate)))
68 viewPrivate->setTabKeyCyclesThroughElements(TRUE);
70 COMPtr<IWebViewEditing> viewEditing;
71 if (FAILED(webView->QueryInterface(&viewEditing)))
73 COMPtr<IWebEditingDelegate> delegate;
74 if (FAILED(viewEditing->editingDelegate(&delegate)))
76 COMPtr<EditingDelegate> editingDelegate(Query, viewEditing.get());
78 editingDelegate->setAcceptsEditing(TRUE);
81 void LayoutTestController::addDisallowedURL(JSStringRef url)
86 void LayoutTestController::clearBackForwardList()
88 COMPtr<IWebView> webView;
89 if (FAILED(frame->webView(&webView)))
92 COMPtr<IWebBackForwardList> backForwardList;
93 if (FAILED(webView->backForwardList(&backForwardList)))
96 COMPtr<IWebHistoryItem> item;
97 if (FAILED(backForwardList->currentItem(&item)))
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.
103 if (FAILED(backForwardList->capacity(&capacity)))
106 backForwardList->setCapacity(0);
107 backForwardList->setCapacity(capacity);
108 backForwardList->addItem(item.get());
109 backForwardList->goToItem(item.get());
112 JSStringRef LayoutTestController::copyDecodedHostName(JSStringRef name)
118 JSStringRef LayoutTestController::copyEncodedHostName(JSStringRef name)
124 void LayoutTestController::display()
129 void LayoutTestController::keepWebHistory()
131 COMPtr<IWebHistory> history(Create, CLSID_WebHistory);
135 COMPtr<IWebHistory> sharedHistory(Create, CLSID_WebHistory);
139 history->setOptionalSharedHistory(sharedHistory.get());
142 void LayoutTestController::notifyDone()
144 // Same as on mac. This can be shared.
145 if (m_waitToDump && !topLoadingFrame && !WorkQueue::shared()->count())
147 m_waitToDump = false;
150 JSStringRef LayoutTestController::pathToLocalResource(JSContextRef context, JSStringRef url)
153 if (!resolveCygwinPath(wstring(JSStringGetCharactersPtr(url), JSStringGetLength(url)), localPath))
156 return JSStringCreateWithCharacters(localPath.c_str(), localPath.length());
159 void LayoutTestController::queueBackNavigation(int howFarBack)
161 // Same as on mac. This can be shared.
162 WorkQueue::shared()->queue(new BackItem(howFarBack));
165 void LayoutTestController::queueForwardNavigation(int howFarForward)
167 // Same as on mac. This can be shared.
168 WorkQueue::shared()->queue(new ForwardItem(howFarForward));
171 static wstring jsStringRefToWString(JSStringRef jsStr)
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';
178 return buffer.data();
181 void LayoutTestController::queueLoad(JSStringRef url, JSStringRef target)
183 COMPtr<IWebDataSource> dataSource;
184 if (FAILED(frame->dataSource(&dataSource)))
187 COMPtr<IWebURLResponse> response;
188 if (FAILED(dataSource->response(&response)) || !response)
191 BSTR responseURLBSTR;
192 if (FAILED(response->URL(&responseURLBSTR)))
194 wstring responseURL(responseURLBSTR, SysStringLen(responseURLBSTR));
195 SysFreeString(responseURLBSTR);
197 // FIXME: We should do real relative URL resolution here.
198 int lastSlash = responseURL.rfind('/');
200 responseURL = responseURL.substr(0, lastSlash);
202 wstring wURL = jsStringRefToWString(url);
203 wstring wAbsoluteURL = responseURL + TEXT("/") + wURL;
204 JSRetainPtr<JSStringRef> jsAbsoluteURL(Adopt, JSStringCreateWithCharacters(wAbsoluteURL.data(), wAbsoluteURL.length()));
206 WorkQueue::shared()->queue(new LoadItem(jsAbsoluteURL.get(), target));
209 void LayoutTestController::queueReload()
211 WorkQueue::shared()->queue(new ReloadItem);
214 void LayoutTestController::queueScript(JSStringRef script)
216 WorkQueue::shared()->queue(new ScriptItem(script));
219 void LayoutTestController::setAcceptsEditing(bool acceptsEditing)
221 COMPtr<IWebView> webView;
222 if (FAILED(frame->webView(&webView)))
225 COMPtr<IWebViewEditing> viewEditing;
226 if (FAILED(webView->QueryInterface(&viewEditing)))
229 COMPtr<IWebEditingDelegate> delegate;
230 if (FAILED(viewEditing->editingDelegate(&delegate)))
233 EditingDelegate* editingDelegate = (EditingDelegate*)(IWebEditingDelegate*)delegate.get();
234 editingDelegate->setAcceptsEditing(acceptsEditing);
237 void LayoutTestController::setAuthorAndUserStylesEnabled(bool flag)
239 COMPtr<IWebView> webView;
240 if (FAILED(frame->webView(&webView)))
243 COMPtr<IWebPreferences> preferences;
244 if (FAILED(webView->preferences(&preferences)))
247 COMPtr<IWebPreferencesPrivate> prefsPrivate(Query, preferences);
251 prefsPrivate->setAuthorAndUserStylesEnabled(flag);
254 void LayoutTestController::setCustomPolicyDelegate(bool setDelegate)
256 COMPtr<IWebView> webView;
257 if (FAILED(frame->webView(&webView)))
261 webView->setPolicyDelegate(policyDelegate);
263 webView->setPolicyDelegate(NULL);
266 void LayoutTestController::setMainFrameIsFirstResponder(bool flag)
271 void LayoutTestController::setPrivateBrowsingEnabled(bool privateBrowsingEnabled)
273 COMPtr<IWebView> webView;
274 if (FAILED(frame->webView(&webView)))
277 COMPtr<IWebPreferences> preferences;
278 if (FAILED(webView->preferences(&preferences)))
281 preferences->setPrivateBrowsingEnabled(privateBrowsingEnabled);
284 void LayoutTestController::setTabKeyCyclesThroughElements(bool shouldCycle)
286 COMPtr<IWebView> webView;
287 if (FAILED(frame->webView(&webView)))
290 COMPtr<IWebViewPrivate> viewPrivate;
291 if (FAILED(webView->QueryInterface(&viewPrivate)))
294 viewPrivate->setTabKeyCyclesThroughElements(shouldCycle ? TRUE : FALSE);
297 void LayoutTestController::setUseDashboardCompatibilityMode(bool flag)
302 void LayoutTestController::setUserStyleSheetEnabled(bool flag)
304 COMPtr<IWebView> webView;
305 if (FAILED(frame->webView(&webView)))
308 COMPtr<IWebPreferences> preferences;
309 if (FAILED(webView->preferences(&preferences)))
312 preferences->setUserStyleSheetEnabled(flag);
315 bool appendComponentToPath(wstring& path, const wstring& component)
317 WCHAR buffer[MAX_PATH];
319 if (path.size() + 1 > MAX_PATH)
322 memcpy(buffer, path.data(), path.size() * sizeof(WCHAR));
323 buffer[path.size()] = '\0';
325 if (!PathAppendW(buffer, component.c_str()))
328 path = wstring(buffer);
332 static bool followShortcuts(wstring& path)
334 if (PathFileExists(path.c_str()))
337 // Do we have a shortcut?
338 path.append(TEXT(".lnk"));
339 if (!PathFileExists(path.c_str()))
342 // We have a shortcut, find its target.
343 COMPtr<IShellLink> shortcut(Create, CLSID_ShellLink);
346 COMPtr<IPersistFile> persistFile(Query, shortcut);
349 if (FAILED(persistFile->Load(path.c_str(), STGM_READ)))
351 if (FAILED(shortcut->Resolve(0, 0)))
353 WCHAR targetPath[MAX_PATH];
354 DWORD targetPathLen = _countof(targetPath);
355 if (FAILED(shortcut->GetPath(targetPath, targetPathLen, 0, 0)))
357 if (!PathFileExists(targetPath))
359 // Use the target path as the result path instead.
360 path = wstring(targetPath);
365 static bool resolveCygwinPath(const wstring& cygwinPath, wstring& windowsPath)
367 wstring fileProtocol = L"file://";
368 bool isFileProtocol = cygwinPath.find(fileProtocol) != string::npos;
369 if (cygwinPath[isFileProtocol ? 7 : 0] != '/') // ensure path is absolute
372 // Get the Root path.
373 WCHAR rootPath[MAX_PATH];
374 DWORD rootPathSize = _countof(rootPath);
376 DWORD result = ::SHGetValueW(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Cygnus Solutions\\Cygwin\\mounts v2\\/"), TEXT("native"), &keyType, &rootPath, &rootPathSize);
378 if (result != ERROR_SUCCESS || keyType != REG_SZ)
381 windowsPath = wstring(rootPath, rootPathSize);
383 int oldPos = isFileProtocol ? 8 : 1;
385 int newPos = cygwinPath.find('/', oldPos);
388 wstring pathComponent = cygwinPath.substr(oldPos);
390 if (!appendComponentToPath(windowsPath, pathComponent))
393 if (!followShortcuts(windowsPath))
399 wstring pathComponent = cygwinPath.substr(oldPos, newPos - oldPos);
400 if (!appendComponentToPath(windowsPath, pathComponent))
403 if (!followShortcuts(windowsPath))
410 windowsPath = fileProtocol + windowsPath;
415 static wstring cfStringRefToWString(CFStringRef cfStr)
417 Vector<wchar_t> v(CFStringGetLength(cfStr));
418 CFStringGetCharacters(cfStr, CFRangeMake(0, CFStringGetLength(cfStr)), (UniChar *)v.data());
420 return wstring(v.data(), v.size());
423 void LayoutTestController::setUserStyleSheetLocation(JSStringRef jsURL)
425 COMPtr<IWebView> webView;
426 if (FAILED(frame->webView(&webView)))
429 COMPtr<IWebPreferences> preferences;
430 if (FAILED(webView->preferences(&preferences)))
433 RetainPtr<CFStringRef> urlString(AdoptCF, JSStringCopyCFString(0, jsURL));
434 RetainPtr<CFURLRef> url(AdoptCF, CFURLCreateWithString(0, urlString.get(), 0));
438 // Now copy the file system path, POSIX style.
439 RetainPtr<CFStringRef> pathCF(AdoptCF, CFURLCopyFileSystemPath(url.get(), kCFURLPOSIXPathStyle));
443 wstring path = cfStringRefToWString(pathCF.get());
446 if (!resolveCygwinPath(path, resultPath))
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);
456 url = CFURLCreateFromFileSystemRepresentation(0, (const UInt8*)utf8Vector.data(), utf8Vector.size() - 1, false);
460 resultPath = cfStringRefToWString(CFURLGetString(url.get()));
462 BSTR resultPathBSTR = SysAllocStringLen(resultPath.data(), resultPath.size());
463 preferences->setUserStyleSheetLocation(resultPathBSTR);
464 SysFreeString(resultPathBSTR);
467 void LayoutTestController::setWindowIsKey(bool flag)
469 COMPtr<IWebView> webView;
470 if (FAILED(frame->webView(&webView)))
473 COMPtr<IWebViewPrivate> viewPrivate;
474 if (FAILED(webView->QueryInterface(&viewPrivate)))
478 if (FAILED(viewPrivate->viewWindow((OLE_HANDLE*)&webViewWindow)))
481 ::SendMessage(webViewWindow, flag ? WM_SETFOCUS : WM_KILLFOCUS, (WPARAM)::GetDesktopWindow(), 0);
484 static const CFTimeInterval waitToDumpWatchdogInterval = 10.0;
486 static void waitUntilDoneWatchdogFired(CFRunLoopTimerRef timer, void* info)
488 const char* message = "FAIL: Timed out waiting for notifyDone to be called\n";
489 fprintf(stderr, message);
490 fprintf(stdout, message);
494 void LayoutTestController::setWaitToDump(bool waitUntilDone)
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);
502 int LayoutTestController::windowCount()
508 void LayoutTestController::execCommand(JSStringRef name, JSStringRef value)
510 wstring wName = jsStringRefToWString(name);
511 wstring wValue = jsStringRefToWString(value);
513 COMPtr<IWebView> webView;
514 if (FAILED(frame->webView(&webView)))
517 COMPtr<IWebViewPrivate> viewPrivate;
518 if (FAILED(webView->QueryInterface(&viewPrivate)))
521 BSTR nameBSTR = SysAllocStringLen((OLECHAR*)wName.c_str(), wName.length());
522 BSTR valueBSTR = SysAllocStringLen((OLECHAR*)wValue.c_str(), wValue.length());
523 viewPrivate->executeCoreCommandByName(nameBSTR, valueBSTR);
525 SysFreeString(nameBSTR);
526 SysFreeString(valueBSTR);