[Mac, iOS] Crash log application information contains latest main frame URL instead...
[WebKit-https.git] / Tools / DumpRenderTree / mac / TestRunnerMac.mm
1 /*
2  * Copyright (C) 2007, 2008, 2009, 2012 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 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 #import "config.h"
30 #import "DumpRenderTree.h"
31 #import "TestRunner.h"
32
33 #import "DefaultPolicyDelegate.h"
34 #import "EditingDelegate.h"
35 #import "MockGeolocationProvider.h"
36 #import "MockWebNotificationProvider.h"
37 #import "PolicyDelegate.h"
38 #import "StorageTrackerDelegate.h"
39 #import "UIDelegate.h"
40 #import "WorkQueue.h"
41 #import "WorkQueueItem.h"
42 #import <Foundation/Foundation.h>
43 #import <JavaScriptCore/JSRetainPtr.h>
44 #import <JavaScriptCore/JSStringRef.h>
45 #import <JavaScriptCore/JSStringRefCF.h>
46 #import <WebCore/GeolocationPosition.h>
47 #import <WebKit/DOMDocument.h>
48 #import <WebKit/DOMElement.h>
49 #import <WebKit/DOMHTMLInputElementPrivate.h>
50 #import <WebKit/WebApplicationCache.h>
51 #import <WebKit/WebBackForwardList.h>
52 #import <WebKit/WebCoreStatistics.h>
53 #import <WebKit/WebDOMOperationsPrivate.h>
54 #import <WebKit/WebDataSource.h>
55 #import <WebKit/WebDatabaseManagerPrivate.h>
56 #import <WebKit/WebDeviceOrientation.h>
57 #import <WebKit/WebDeviceOrientationProviderMock.h>
58 #import <WebKit/WebFrame.h>
59 #import <WebKit/WebFrameViewPrivate.h>
60 #import <WebKit/WebGeolocationPosition.h>
61 #import <WebKit/WebHTMLRepresentation.h>
62 #import <WebKit/WebHTMLViewPrivate.h>
63 #import <WebKit/WebHistory.h>
64 #import <WebKit/WebHistoryPrivate.h>
65 #import <WebKit/WebInspectorPrivate.h>
66 #import <WebKit/WebNSURLExtras.h>
67 #import <WebKit/WebKitErrors.h>
68 #import <WebKit/WebPreferences.h>
69 #import <WebKit/WebPreferencesPrivate.h>
70 #import <WebKit/WebQuotaManager.h>
71 #import <WebKit/WebScriptWorld.h>
72 #import <WebKit/WebSecurityOriginPrivate.h>
73 #import <WebKit/WebStorageManagerPrivate.h>
74 #import <WebKit/WebView.h>
75 #import <WebKit/WebViewPrivate.h>
76 #import <wtf/CurrentTime.h>
77 #import <wtf/HashMap.h>
78 #import <wtf/RetainPtr.h>
79
80 #if !PLATFORM(IOS)
81 #import <WebKit/WebIconDatabasePrivate.h>
82 #endif
83
84 #if PLATFORM(IOS)
85 #import <UIKit/UIWebBrowserView.h>
86 #import <WebKit/WebCoreThread.h>
87 #import <WebKit/WebCoreThreadMessage.h>
88 #import <WebKit/WebDOMOperationsPrivate.h>
89 #endif
90
91 #if !PLATFORM(IOS)
92 @interface CommandValidationTarget : NSObject <NSValidatedUserInterfaceItem>
93 {
94     SEL _action;
95 }
96 - (id)initWithAction:(SEL)action;
97 @end
98
99 @implementation CommandValidationTarget
100
101 - (id)initWithAction:(SEL)action
102 {
103     self = [super init];
104     if (!self)
105         return nil;
106
107     _action = action;
108     return self;
109 }
110
111 - (SEL)action
112 {
113     return _action;
114 }
115
116 - (NSInteger)tag
117 {
118     return 0;
119 }
120
121 @end
122 #endif
123
124 @interface WebGeolocationPosition (Internal)
125 - (id)initWithGeolocationPosition:(PassRefPtr<WebCore::GeolocationPosition>)coreGeolocationPosition;
126 @end
127
128 TestRunner::~TestRunner()
129 {
130 }
131
132 void TestRunner::addDisallowedURL(JSStringRef url)
133 {
134     RetainPtr<CFStringRef> urlCF = adoptCF(JSStringCopyCFString(kCFAllocatorDefault, url));
135
136     if (!disallowedURLs)
137         disallowedURLs = CFSetCreateMutable(kCFAllocatorDefault, 0, NULL);
138
139     // Canonicalize the URL
140     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:(NSString *)urlCF.get()]];
141     request = [NSURLProtocol canonicalRequestForRequest:request];
142
143     CFSetAddValue(disallowedURLs, [request URL]);
144 }
145
146 bool TestRunner::callShouldCloseOnWebView()
147 {
148     return [[mainFrame webView] shouldClose];
149 }
150
151 void TestRunner::clearAllApplicationCaches()
152 {
153     [WebApplicationCache deleteAllApplicationCaches];
154 }
155
156 long long TestRunner::applicationCacheDiskUsageForOrigin(JSStringRef url)
157 {
158     RetainPtr<CFStringRef> urlCF = adoptCF(JSStringCopyCFString(kCFAllocatorDefault, url));
159     WebSecurityOrigin *origin = [[WebSecurityOrigin alloc] initWithURL:[NSURL URLWithString:(NSString *)urlCF.get()]];
160     long long usage = [WebApplicationCache diskUsageForOrigin:origin];
161     [origin release];
162     return usage;
163 }
164
165 void TestRunner::syncLocalStorage()
166 {
167     [[WebStorageManager sharedWebStorageManager] syncLocalStorage];
168 }
169
170 long long TestRunner::localStorageDiskUsageForOrigin(JSStringRef url)
171 {
172     RetainPtr<CFStringRef> urlCF = adoptCF(JSStringCopyCFString(kCFAllocatorDefault, url));
173     WebSecurityOrigin *origin = [[WebSecurityOrigin alloc] initWithURL:[NSURL URLWithString:(NSString *)urlCF.get()]];
174     long long usage = [[WebStorageManager sharedWebStorageManager] diskUsageForOrigin:origin];
175     [origin release];
176     return usage;
177 }
178
179 void TestRunner::observeStorageTrackerNotifications(unsigned number)
180 {
181     [storageDelegate logNotifications:number controller:this];
182 }
183
184 void TestRunner::clearApplicationCacheForOrigin(JSStringRef url)
185 {
186     RetainPtr<CFStringRef> urlCF = adoptCF(JSStringCopyCFString(kCFAllocatorDefault, url));
187
188     WebSecurityOrigin *origin = [[WebSecurityOrigin alloc] initWithURL:[NSURL URLWithString:(NSString *)urlCF.get()]];
189     [WebApplicationCache deleteCacheForOrigin:origin];
190     [origin release];
191 }
192
193 JSValueRef originsArrayToJS(JSContextRef context, NSArray *origins)
194 {
195     NSUInteger count = [origins count];
196
197     JSValueRef arrayResult = JSObjectMakeArray(context, 0, 0, 0);
198     JSObjectRef arrayObj = JSValueToObject(context, arrayResult, 0);
199     for (NSUInteger i = 0; i < count; i++) {
200         NSString *origin = [[origins objectAtIndex:i] databaseIdentifier];
201         JSRetainPtr<JSStringRef> originJS(Adopt, JSStringCreateWithCFString((CFStringRef)origin));
202         JSObjectSetPropertyAtIndex(context, arrayObj, i, JSValueMakeString(context, originJS.get()), 0);
203     }
204
205     return arrayResult;
206 }
207
208 JSValueRef TestRunner::originsWithApplicationCache(JSContextRef context)
209 {
210     return originsArrayToJS(context, [WebApplicationCache originsWithCache]);
211 }
212
213 void TestRunner::clearAllDatabases()
214 {
215     [[WebDatabaseManager sharedWebDatabaseManager] deleteAllDatabases];
216 }
217
218 void TestRunner::deleteAllLocalStorage()
219 {
220     [[WebStorageManager sharedWebStorageManager] deleteAllOrigins];
221 }
222
223 void TestRunner::setStorageDatabaseIdleInterval(double interval)
224 {
225     [WebStorageManager setStorageDatabaseIdleInterval:interval];
226 }
227
228 void TestRunner::closeIdleLocalStorageDatabases()
229 {
230     [WebStorageManager closeIdleLocalStorageDatabases];
231 }
232
233 JSValueRef TestRunner::originsWithLocalStorage(JSContextRef context)
234 {
235     return originsArrayToJS(context, [[WebStorageManager sharedWebStorageManager] origins]);
236 }
237
238 void TestRunner::deleteLocalStorageForOrigin(JSStringRef URL)
239 {
240     RetainPtr<CFStringRef> urlCF = adoptCF(JSStringCopyCFString(kCFAllocatorDefault, URL));
241     
242     WebSecurityOrigin *origin = [[WebSecurityOrigin alloc] initWithURL:[NSURL URLWithString:(NSString *)urlCF.get()]];
243     [[WebStorageManager sharedWebStorageManager] deleteOrigin:origin];
244     [origin release];
245 }
246
247 void TestRunner::clearBackForwardList()
248 {
249     WebBackForwardList *backForwardList = [[mainFrame webView] backForwardList];
250     WebHistoryItem *item = [[backForwardList currentItem] retain];
251
252     // We clear the history by setting the back/forward list's capacity to 0
253     // then restoring it back and adding back the current item.
254     int capacity = [backForwardList capacity];
255     [backForwardList setCapacity:0];
256     [backForwardList setCapacity:capacity];
257     [backForwardList addItem:item];
258     [backForwardList goToItem:item];
259     [item release];
260 }
261
262 JSStringRef TestRunner::copyDecodedHostName(JSStringRef name)
263 {
264     RetainPtr<CFStringRef> nameCF = adoptCF(JSStringCopyCFString(kCFAllocatorDefault, name));
265     NSString *nameNS = (NSString *)nameCF.get();
266     return JSStringCreateWithCFString((CFStringRef)[nameNS _web_decodeHostName]);
267 }
268
269 JSStringRef TestRunner::copyEncodedHostName(JSStringRef name)
270 {
271     RetainPtr<CFStringRef> nameCF = adoptCF(JSStringCopyCFString(kCFAllocatorDefault, name));
272     NSString *nameNS = (NSString *)nameCF.get();
273     return JSStringCreateWithCFString((CFStringRef)[nameNS _web_encodeHostName]);
274 }
275
276 void TestRunner::display()
277 {
278     displayWebView();
279 }
280
281 void TestRunner::keepWebHistory()
282 {
283     if (![WebHistory optionalSharedHistory]) {
284         WebHistory *history = [[WebHistory alloc] init];
285         [WebHistory setOptionalSharedHistory:history];
286         [history release];
287     }
288 }
289
290 int TestRunner::numberOfPendingGeolocationPermissionRequests()
291 {
292     return [[[mainFrame webView] UIDelegate] numberOfPendingGeolocationPermissionRequests];
293 }
294
295 size_t TestRunner::webHistoryItemCount()
296 {
297     return [[[WebHistory optionalSharedHistory] allItems] count];
298 }
299
300 void TestRunner::notifyDone()
301 {
302     if (m_waitToDump && !topLoadingFrame && !WorkQueue::shared()->count())
303         dump();
304     m_waitToDump = false;
305 }
306
307 static inline std::string stringFromJSString(JSStringRef jsString)
308 {
309     size_t maxBufferSize = JSStringGetMaximumUTF8CStringSize(jsString);
310     char* utf8Buffer = new char[maxBufferSize];
311     size_t bytesWrittenToUTF8Buffer = JSStringGetUTF8CString(jsString, utf8Buffer, maxBufferSize);
312     std::string stdString(utf8Buffer, bytesWrittenToUTF8Buffer - 1); // bytesWrittenToUTF8Buffer includes a trailing \0 which std::string doesn't need.
313     delete[] utf8Buffer;
314     return stdString;
315 }
316
317 static inline size_t indexOfSeparatorAfterDirectoryName(const std::string& directoryName, const std::string& fullPath)
318 {
319     std::string searchKey = "/" + directoryName + "/";
320     size_t indexOfSearchKeyStart = fullPath.rfind(searchKey);
321     if (indexOfSearchKeyStart == std::string::npos) {
322         ASSERT_NOT_REACHED();
323         return 0;
324     }
325     // Callers expect the return value not to end in "/", so searchKey.length() - 1.
326     return indexOfSearchKeyStart + searchKey.length() - 1;
327 }
328
329 static inline std::string resourceRootAbsolutePath(const std::string& testURL, const std::string& expectedRootName)
330 {
331     char* localResourceRootEnv = getenv("LOCAL_RESOURCE_ROOT");
332     if (localResourceRootEnv)
333         return std::string(localResourceRootEnv);
334
335     // This fallback approach works for non-http tests and is useful
336     // in the case when we're running DRT directly from the command line.
337     return testURL.substr(0, indexOfSeparatorAfterDirectoryName(expectedRootName, testURL));
338 }
339
340 JSStringRef TestRunner::pathToLocalResource(JSContextRef context, JSStringRef localResourceJSString)
341 {
342     // The passed in path will be an absolute path to the resource starting
343     // with "/tmp" or "/tmp/LayoutTests", optionally starting with the explicit file:// protocol.
344     // /tmp maps to DUMPRENDERTREE_TEMP, and /tmp/LayoutTests maps to LOCAL_RESOURCE_ROOT.
345     // FIXME: This code should work on all *nix platforms and can be moved into TestRunner.cpp.
346     std::string expectedRootName;
347     std::string absolutePathToResourceRoot;
348     std::string localResourceString = stringFromJSString(localResourceJSString);
349
350     if (localResourceString.find("LayoutTests") != std::string::npos) {
351         expectedRootName = "LayoutTests";
352         absolutePathToResourceRoot = resourceRootAbsolutePath(m_testURL, expectedRootName);
353     } else if (localResourceString.find("tmp") != std::string::npos) {
354         expectedRootName = "tmp";
355         absolutePathToResourceRoot = getenv("DUMPRENDERTREE_TEMP");
356     } else {
357         ASSERT_NOT_REACHED(); // pathToLocalResource was passed a path it doesn't know how to map.
358     }
359     ASSERT(!absolutePathToResourceRoot.empty());
360     size_t indexOfSeparatorAfterRootName = indexOfSeparatorAfterDirectoryName(expectedRootName, localResourceString);
361     std::string absolutePathToLocalResource = absolutePathToResourceRoot + localResourceString.substr(indexOfSeparatorAfterRootName);
362
363     // Note: It's important that we keep the file:// or http tests will get confused.
364     if (localResourceString.find("file://") != std::string::npos) {
365         ASSERT(absolutePathToLocalResource[0] == '/');
366         absolutePathToLocalResource = std::string("file://") + absolutePathToLocalResource;
367     }
368     return JSStringCreateWithUTF8CString(absolutePathToLocalResource.c_str());
369 }
370
371 void TestRunner::queueLoad(JSStringRef url, JSStringRef target)
372 {
373     RetainPtr<CFStringRef> urlCF = adoptCF(JSStringCopyCFString(kCFAllocatorDefault, url));
374     NSString *urlNS = (NSString *)urlCF.get();
375
376     NSURL *nsurl = [NSURL URLWithString:urlNS relativeToURL:[[[mainFrame dataSource] response] URL]];
377     NSString *nsurlString = [nsurl absoluteString];
378
379     JSRetainPtr<JSStringRef> absoluteURL(Adopt, JSStringCreateWithUTF8CString([nsurlString UTF8String]));
380     WorkQueue::shared()->queue(new LoadItem(absoluteURL.get(), target));
381 }
382
383 void TestRunner::setAcceptsEditing(bool newAcceptsEditing)
384 {
385     [(EditingDelegate *)[[mainFrame webView] editingDelegate] setAcceptsEditing:newAcceptsEditing];
386 }
387
388 void TestRunner::setAlwaysAcceptCookies(bool alwaysAcceptCookies)
389 {
390     if (alwaysAcceptCookies == m_alwaysAcceptCookies)
391         return;
392
393     m_alwaysAcceptCookies = alwaysAcceptCookies;
394     NSHTTPCookieAcceptPolicy cookieAcceptPolicy = alwaysAcceptCookies ? NSHTTPCookieAcceptPolicyAlways : NSHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain;
395     [WebPreferences _setCurrentNetworkLoaderSessionCookieAcceptPolicy:cookieAcceptPolicy];
396 }
397
398 void TestRunner::setAppCacheMaximumSize(unsigned long long size)
399 {
400     [WebApplicationCache setMaximumSize:size];
401 }
402
403 void TestRunner::setAuthorAndUserStylesEnabled(bool flag)
404 {
405     [[[mainFrame webView] preferences] setAuthorAndUserStylesEnabled:flag];
406 }
407
408 void TestRunner::setCustomPolicyDelegate(bool setDelegate, bool permissive)
409 {
410     if (!setDelegate) {
411         [[mainFrame webView] setPolicyDelegate:defaultPolicyDelegate];
412         return;
413     }
414
415     [policyDelegate setPermissive:permissive];
416     [[mainFrame webView] setPolicyDelegate:policyDelegate];
417 }
418
419 void TestRunner::setDatabaseQuota(unsigned long long quota)
420 {    
421     WebSecurityOrigin *origin = [[WebSecurityOrigin alloc] initWithURL:[NSURL URLWithString:@"file:///"]];
422     [[origin databaseQuotaManager] setQuota:quota];
423     [origin release];
424 }
425
426 void TestRunner::goBack()
427 {
428     [[mainFrame webView] goBack];
429 }
430
431 void TestRunner::setDefersLoading(bool defers)
432 {
433     [[mainFrame webView] setDefersCallbacks:defers];
434 }
435
436 void TestRunner::setDomainRelaxationForbiddenForURLScheme(bool forbidden, JSStringRef scheme)
437 {
438     RetainPtr<CFStringRef> schemeCFString = adoptCF(JSStringCopyCFString(kCFAllocatorDefault, scheme));
439     [WebView _setDomainRelaxationForbidden:forbidden forURLScheme:(NSString *)schemeCFString.get()];
440 }
441
442 void TestRunner::setMockDeviceOrientation(bool canProvideAlpha, double alpha, bool canProvideBeta, double beta, bool canProvideGamma, double gamma)
443 {
444     // DumpRenderTree configured the WebView to use WebDeviceOrientationProviderMock.
445     id<WebDeviceOrientationProvider> provider = [[mainFrame webView] _deviceOrientationProvider];
446     WebDeviceOrientationProviderMock *mockProvider = static_cast<WebDeviceOrientationProviderMock*>(provider);
447     WebDeviceOrientation *orientation = [[WebDeviceOrientation alloc] initWithCanProvideAlpha:canProvideAlpha alpha:alpha canProvideBeta:canProvideBeta beta:beta canProvideGamma:canProvideGamma gamma:gamma];
448     [mockProvider setOrientation:orientation];
449     [orientation release];
450 }
451
452 void TestRunner::setMockGeolocationPosition(double latitude, double longitude, double accuracy, bool providesAltitude, double altitude, bool providesAltitudeAccuracy, double altitudeAccuracy, bool providesHeading, double heading, bool providesSpeed, double speed)
453 {
454     WebGeolocationPosition *position = nil;
455     if (!providesAltitude && !providesAltitudeAccuracy && !providesHeading && !providesSpeed) {
456         // Test the exposed API.
457         position = [[WebGeolocationPosition alloc] initWithTimestamp:currentTime() latitude:latitude longitude:longitude accuracy:accuracy];
458     } else {
459         RefPtr<WebCore::GeolocationPosition> coreGeolocationPosition = WebCore::GeolocationPosition::create(currentTime(), latitude, longitude, accuracy, providesAltitude, altitude, providesAltitudeAccuracy, altitudeAccuracy, providesHeading, heading, providesSpeed, speed);
460         position = [[WebGeolocationPosition alloc] initWithGeolocationPosition:(coreGeolocationPosition.release())];
461     }
462     [[MockGeolocationProvider shared] setPosition:position];
463     [position release];
464 }
465
466 void TestRunner::setMockGeolocationPositionUnavailableError(JSStringRef message)
467 {
468     RetainPtr<CFStringRef> messageCF = adoptCF(JSStringCopyCFString(kCFAllocatorDefault, message));
469     NSString *messageNS = (NSString *)messageCF.get();
470     [[MockGeolocationProvider shared] setPositionUnavailableErrorWithMessage:messageNS];
471 }
472
473 void TestRunner::setGeolocationPermission(bool allow)
474 {
475     setGeolocationPermissionCommon(allow);
476     [[[mainFrame webView] UIDelegate] didSetMockGeolocationPermission];
477 }
478
479 void TestRunner::setIconDatabaseEnabled(bool iconDatabaseEnabled)
480 {
481 #if ENABLE(ICONDATABASE)
482     // FIXME: Workaround <rdar://problem/6480108>
483     static WebIconDatabase *sharedWebIconDatabase = NULL;
484     if (!sharedWebIconDatabase) {
485         if (!iconDatabaseEnabled)
486             return;
487         sharedWebIconDatabase = [WebIconDatabase sharedIconDatabase];
488         if ([sharedWebIconDatabase isEnabled] == iconDatabaseEnabled)
489             return;
490     }
491     [sharedWebIconDatabase setEnabled:iconDatabaseEnabled];
492 #endif
493 }
494
495 void TestRunner::setMainFrameIsFirstResponder(bool flag)
496 {
497 #if !PLATFORM(IOS)
498     NSView *documentView = [[mainFrame frameView] documentView];
499     
500     NSResponder *firstResponder = flag ? documentView : nil;
501     [[[mainFrame webView] window] makeFirstResponder:firstResponder];
502 #endif
503 }
504
505 void TestRunner::setPrivateBrowsingEnabled(bool privateBrowsingEnabled)
506 {
507     [[[mainFrame webView] preferences] setPrivateBrowsingEnabled:privateBrowsingEnabled];
508 }
509
510 void TestRunner::setXSSAuditorEnabled(bool enabled)
511 {
512     [[[mainFrame webView] preferences] setXSSAuditorEnabled:enabled];
513 }
514
515 void TestRunner::setSpatialNavigationEnabled(bool enabled)
516 {
517     [[[mainFrame webView] preferences] setSpatialNavigationEnabled:enabled];
518 }
519
520 void TestRunner::setAllowUniversalAccessFromFileURLs(bool enabled)
521 {
522     [[[mainFrame webView] preferences] setAllowUniversalAccessFromFileURLs:enabled];
523 }
524
525 void TestRunner::setAllowFileAccessFromFileURLs(bool enabled)
526 {
527     [[[mainFrame webView] preferences] setAllowFileAccessFromFileURLs:enabled];
528 }
529
530 void TestRunner::setPopupBlockingEnabled(bool popupBlockingEnabled)
531 {
532     [[[mainFrame webView] preferences] setJavaScriptCanOpenWindowsAutomatically:!popupBlockingEnabled];
533 }
534
535 void TestRunner::setPluginsEnabled(bool pluginsEnabled)
536 {
537     [[[mainFrame webView] preferences] setPlugInsEnabled:pluginsEnabled];
538 }
539
540 void TestRunner::setJavaScriptCanAccessClipboard(bool enabled)
541 {
542     [[[mainFrame webView] preferences] setJavaScriptCanAccessClipboard:enabled];
543 }
544
545 void TestRunner::setAutomaticLinkDetectionEnabled(bool enabled)
546 {
547 #if !PLATFORM(IOS)
548     [[mainFrame webView] setAutomaticLinkDetectionEnabled:enabled];
549 #endif
550 }
551
552 void TestRunner::setTabKeyCyclesThroughElements(bool cycles)
553 {
554     [[mainFrame webView] setTabKeyCyclesThroughElements:cycles];
555 }
556
557 #if PLATFORM(IOS)
558 void TestRunner::setTelephoneNumberParsingEnabled(bool enabled)
559 {
560     [[[mainFrame webView] preferences] _setTelephoneNumberParsingEnabled:enabled];
561 }
562
563 void TestRunner::setPagePaused(bool paused)
564 {
565     [gWebBrowserView setPaused:paused];
566 }
567 #endif
568
569 #if ENABLE(IOS_TEXT_AUTOSIZING)
570 void TestRunner::setTextAutosizingEnabled(bool enabled)
571 {
572     const float phoneMinimumZoomFontSize = 15;
573     [[[mainFrame webView] preferences] _setMinimumZoomFontSize:(enabled ? phoneMinimumZoomFontSize : 0)];
574 }
575 #endif
576
577 void TestRunner::setUseDashboardCompatibilityMode(bool flag)
578 {
579 #if !PLATFORM(IOS)
580     [[mainFrame webView] _setDashboardBehavior:WebDashboardBehaviorUseBackwardCompatibilityMode to:flag];
581 #endif
582 }
583
584 void TestRunner::setUserStyleSheetEnabled(bool flag)
585 {
586     [[WebPreferences standardPreferences] setUserStyleSheetEnabled:flag];
587 }
588
589 void TestRunner::setUserStyleSheetLocation(JSStringRef path)
590 {
591     RetainPtr<CFStringRef> pathCF = adoptCF(JSStringCopyCFString(kCFAllocatorDefault, path));
592     NSURL *url = [NSURL URLWithString:(NSString *)pathCF.get()];
593     [[WebPreferences standardPreferences] setUserStyleSheetLocation:url];
594 }
595
596 void TestRunner::setValueForUser(JSContextRef context, JSValueRef nodeObject, JSStringRef value)
597 {
598     DOMElement *element = [DOMElement _DOMElementFromJSContext:context value:nodeObject];
599     if (!element || ![element isKindOfClass:[DOMHTMLInputElement class]])
600         return;
601
602     RetainPtr<CFStringRef> valueCF = adoptCF(JSStringCopyCFString(kCFAllocatorDefault, value));
603     [(DOMHTMLInputElement *)element setValueForUser:(NSString *)valueCF.get()];
604 }
605
606 void TestRunner::setViewModeMediaFeature(JSStringRef mode)
607 {
608     // FIXME: implement
609 }
610
611 void TestRunner::dispatchPendingLoadRequests()
612 {
613     [[mainFrame webView] _dispatchPendingLoadRequests];
614 }
615
616 void TestRunner::overridePreference(JSStringRef key, JSStringRef value)
617 {
618     RetainPtr<CFStringRef> keyCF = adoptCF(JSStringCopyCFString(kCFAllocatorDefault, key));
619     NSString *keyNS = (NSString *)keyCF.get();
620
621     RetainPtr<CFStringRef> valueCF = adoptCF(JSStringCopyCFString(kCFAllocatorDefault, value));
622     NSString *valueNS = (NSString *)valueCF.get();
623
624     [[WebPreferences standardPreferences] _setPreferenceForTestWithValue:valueNS forKey:keyNS];
625 }
626
627 void TestRunner::removeAllVisitedLinks()
628 {
629     [WebHistory _removeAllVisitedLinks];
630 }
631
632 void TestRunner::setPersistentUserStyleSheetLocation(JSStringRef jsURL)
633 {
634     RetainPtr<CFStringRef> urlString = adoptCF(JSStringCopyCFString(0, jsURL));
635     ::setPersistentUserStyleSheetLocation(urlString.get());
636 }
637
638 void TestRunner::clearPersistentUserStyleSheet()
639 {
640     ::setPersistentUserStyleSheetLocation(0);
641 }
642
643 void TestRunner::setWindowIsKey(bool windowIsKey)
644 {
645     m_windowIsKey = windowIsKey;
646     [[mainFrame webView] _updateActiveState];
647 }
648
649 static const CFTimeInterval waitToDumpWatchdogInterval = 30.0;
650
651 static void waitUntilDoneWatchdogFired(CFRunLoopTimerRef timer, void* info)
652 {
653     gTestRunner->waitToDumpWatchdogTimerFired();
654 }
655
656 void TestRunner::setWaitToDump(bool waitUntilDone)
657 {
658     m_waitToDump = waitUntilDone;
659     if (m_waitToDump && shouldSetWaitToDumpWatchdog())
660         setWaitToDumpWatchdog(CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + waitToDumpWatchdogInterval, 0, 0, 0, waitUntilDoneWatchdogFired, NULL));
661 }
662
663 int TestRunner::windowCount()
664 {
665     return CFArrayGetCount(openWindowsRef);
666 }
667
668 void TestRunner::execCommand(JSStringRef name, JSStringRef value)
669 {
670     RetainPtr<CFStringRef> nameCF = adoptCF(JSStringCopyCFString(kCFAllocatorDefault, name));
671     NSString *nameNS = (NSString *)nameCF.get();
672
673     RetainPtr<CFStringRef> valueCF = adoptCF(JSStringCopyCFString(kCFAllocatorDefault, value));
674     NSString *valueNS = (NSString *)valueCF.get();
675
676     [[mainFrame webView] _executeCoreCommandByName:nameNS value:valueNS];
677 }
678
679 bool TestRunner::findString(JSContextRef context, JSStringRef target, JSObjectRef optionsArray)
680 {
681     WebFindOptions options = 0;
682
683     JSRetainPtr<JSStringRef> lengthPropertyName(Adopt, JSStringCreateWithUTF8CString("length"));
684     JSValueRef lengthValue = JSObjectGetProperty(context, optionsArray, lengthPropertyName.get(), 0);
685     if (!JSValueIsNumber(context, lengthValue))
686         return false;
687
688     RetainPtr<CFStringRef> targetCFString = adoptCF(JSStringCopyCFString(kCFAllocatorDefault, target));
689
690     size_t length = static_cast<size_t>(JSValueToNumber(context, lengthValue, 0));
691     for (size_t i = 0; i < length; ++i) {
692         JSValueRef value = JSObjectGetPropertyAtIndex(context, optionsArray, i, 0);
693         if (!JSValueIsString(context, value))
694             continue;
695
696         JSRetainPtr<JSStringRef> optionName(Adopt, JSValueToStringCopy(context, value, 0));
697
698         if (JSStringIsEqualToUTF8CString(optionName.get(), "CaseInsensitive"))
699             options |= WebFindOptionsCaseInsensitive;
700         else if (JSStringIsEqualToUTF8CString(optionName.get(), "AtWordStarts"))
701             options |= WebFindOptionsAtWordStarts;
702         else if (JSStringIsEqualToUTF8CString(optionName.get(), "TreatMedialCapitalAsWordStart"))
703             options |= WebFindOptionsTreatMedialCapitalAsWordStart;
704         else if (JSStringIsEqualToUTF8CString(optionName.get(), "Backwards"))
705             options |= WebFindOptionsBackwards;
706         else if (JSStringIsEqualToUTF8CString(optionName.get(), "WrapAround"))
707             options |= WebFindOptionsWrapAround;
708         else if (JSStringIsEqualToUTF8CString(optionName.get(), "StartInSelection"))
709             options |= WebFindOptionsStartInSelection;
710     }
711
712     return [[mainFrame webView] findString:(NSString *)targetCFString.get() options:options];
713 }
714
715 void TestRunner::setCacheModel(int cacheModel)
716 {
717     [[WebPreferences standardPreferences] setCacheModel:(WebCacheModel)cacheModel];
718 }
719
720 bool TestRunner::isCommandEnabled(JSStringRef name)
721 {
722 #if !PLATFORM(IOS)
723     RetainPtr<CFStringRef> nameCF = adoptCF(JSStringCopyCFString(kCFAllocatorDefault, name));
724     NSString *nameNS = (NSString *)nameCF.get();
725
726     // Accept command strings with capital letters for first letter without trailing colon.
727     if (![nameNS hasSuffix:@":"] && [nameNS length]) {
728         nameNS = [[[[nameNS substringToIndex:1] lowercaseString]
729             stringByAppendingString:[nameNS substringFromIndex:1]]
730             stringByAppendingString:@":"];
731     }
732
733     SEL selector = NSSelectorFromString(nameNS);
734     RetainPtr<CommandValidationTarget> target = adoptNS([[CommandValidationTarget alloc] initWithAction:selector]);
735     id validator = [NSApp targetForAction:selector to:[mainFrame webView] from:target.get()];
736     if (!validator)
737         return false;
738     if (![validator respondsToSelector:selector])
739         return false;
740     if (![validator respondsToSelector:@selector(validateUserInterfaceItem:)])
741         return true;
742     return [validator validateUserInterfaceItem:target.get()];
743 #else
744     return false;
745 #endif
746 }
747
748 void TestRunner::waitForPolicyDelegate()
749 {
750     setWaitToDump(true);
751     [policyDelegate setControllerToNotifyDone:this];
752     [[mainFrame webView] setPolicyDelegate:policyDelegate];
753 }
754
755 void TestRunner::addOriginAccessWhitelistEntry(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains)
756 {
757     RetainPtr<CFStringRef> sourceOriginCF = adoptCF(JSStringCopyCFString(kCFAllocatorDefault, sourceOrigin));
758     NSString *sourceOriginNS = (NSString *)sourceOriginCF.get();
759     RetainPtr<CFStringRef> protocolCF = adoptCF(JSStringCopyCFString(kCFAllocatorDefault, destinationProtocol));
760     NSString *destinationProtocolNS = (NSString *)protocolCF.get();
761     RetainPtr<CFStringRef> hostCF = adoptCF(JSStringCopyCFString(kCFAllocatorDefault, destinationHost));
762     NSString *destinationHostNS = (NSString *)hostCF.get();
763     [WebView _addOriginAccessWhitelistEntryWithSourceOrigin:sourceOriginNS destinationProtocol:destinationProtocolNS destinationHost:destinationHostNS allowDestinationSubdomains:allowDestinationSubdomains];
764 }
765
766 void TestRunner::removeOriginAccessWhitelistEntry(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains)
767 {
768     RetainPtr<CFStringRef> sourceOriginCF = adoptCF(JSStringCopyCFString(kCFAllocatorDefault, sourceOrigin));
769     NSString *sourceOriginNS = (NSString *)sourceOriginCF.get();
770     RetainPtr<CFStringRef> protocolCF = adoptCF(JSStringCopyCFString(kCFAllocatorDefault, destinationProtocol));
771     NSString *destinationProtocolNS = (NSString *)protocolCF.get();
772     RetainPtr<CFStringRef> hostCF = adoptCF(JSStringCopyCFString(kCFAllocatorDefault, destinationHost));
773     NSString *destinationHostNS = (NSString *)hostCF.get();
774     [WebView _removeOriginAccessWhitelistEntryWithSourceOrigin:sourceOriginNS destinationProtocol:destinationProtocolNS destinationHost:destinationHostNS allowDestinationSubdomains:allowDestinationSubdomains];
775 }
776
777 void TestRunner::setScrollbarPolicy(JSStringRef orientation, JSStringRef policy)
778 {
779     // FIXME: implement
780 }
781
782 void TestRunner::addUserScript(JSStringRef source, bool runAtStart, bool allFrames)
783 {
784     RetainPtr<CFStringRef> sourceCF = adoptCF(JSStringCopyCFString(kCFAllocatorDefault, source));
785     NSString *sourceNS = (NSString *)sourceCF.get();
786     [WebView _addUserScriptToGroup:@"org.webkit.DumpRenderTree" world:[WebScriptWorld world] source:sourceNS url:nil whitelist:nil blacklist:nil injectionTime:(runAtStart ? WebInjectAtDocumentStart : WebInjectAtDocumentEnd) injectedFrames:(allFrames ? WebInjectInAllFrames : WebInjectInTopFrameOnly)];
787 }
788
789 void TestRunner::addUserStyleSheet(JSStringRef source, bool allFrames)
790 {
791     RetainPtr<CFStringRef> sourceCF = adoptCF(JSStringCopyCFString(kCFAllocatorDefault, source));
792     NSString *sourceNS = (NSString *)sourceCF.get();
793     [WebView _addUserStyleSheetToGroup:@"org.webkit.DumpRenderTree" world:[WebScriptWorld world] source:sourceNS url:nil whitelist:nil blacklist:nil injectedFrames:(allFrames ? WebInjectInAllFrames : WebInjectInTopFrameOnly)];
794 }
795
796 void TestRunner::setDeveloperExtrasEnabled(bool enabled)
797 {
798     [[[mainFrame webView] preferences] setDeveloperExtrasEnabled:enabled];
799 }
800
801 void TestRunner::showWebInspector()
802 {
803 #if ENABLE(INSPECTOR)
804     [[[mainFrame webView] inspector] show:nil];
805 #endif
806 }
807
808 void TestRunner::closeWebInspector()
809 {
810 #if ENABLE(INSPECTOR)
811     [[[mainFrame webView] inspector] close:nil];
812 #endif
813 }
814
815 void TestRunner::evaluateInWebInspector(JSStringRef script)
816 {
817 #if ENABLE(INSPECTOR)
818     RetainPtr<CFStringRef> scriptCF = adoptCF(JSStringCopyCFString(kCFAllocatorDefault, script));
819     NSString *scriptNS = (NSString *)scriptCF.get();
820     [[[mainFrame webView] inspector] evaluateInFrontend:nil script:scriptNS];
821 #endif
822 }
823
824 typedef HashMap<unsigned, RetainPtr<WebScriptWorld> > WorldMap;
825 static WorldMap& worldMap()
826 {
827     static WorldMap& map = *new WorldMap;
828     return map;
829 }
830
831 unsigned worldIDForWorld(WebScriptWorld *world)
832 {
833     WorldMap::const_iterator end = worldMap().end();
834     for (WorldMap::const_iterator it = worldMap().begin(); it != end; ++it) {
835         if (it->value == world)
836             return it->key;
837     }
838
839     return 0;
840 }
841
842 void TestRunner::evaluateScriptInIsolatedWorldAndReturnValue(unsigned worldID, JSObjectRef globalObject, JSStringRef script)
843 {
844     // FIXME: Implement this.
845 }
846
847 void TestRunner::evaluateScriptInIsolatedWorld(unsigned worldID, JSObjectRef globalObject, JSStringRef script)
848 {
849     RetainPtr<CFStringRef> scriptCF = adoptCF(JSStringCopyCFString(kCFAllocatorDefault, script));
850     NSString *scriptNS = (NSString *)scriptCF.get();
851
852     // A worldID of 0 always corresponds to a new world. Any other worldID corresponds to a world
853     // that is created once and cached forever.
854     WebScriptWorld *world;
855     if (!worldID)
856         world = [WebScriptWorld world];
857     else {
858         RetainPtr<WebScriptWorld>& worldSlot = worldMap().add(worldID, nullptr).iterator->value;
859         if (!worldSlot)
860             worldSlot = adoptNS([[WebScriptWorld alloc] init]);
861         world = worldSlot.get();
862     }
863
864     [mainFrame _stringByEvaluatingJavaScriptFromString:scriptNS withGlobalObject:globalObject inScriptWorld:world];
865 }
866
867 @interface APITestDelegate : NSObject
868 {
869     bool* m_condition;
870 }
871 @end
872
873 @implementation APITestDelegate
874
875 - (id)initWithCompletionCondition:(bool*)condition
876 {
877     self = [super init];
878     if (!self)
879         return nil;
880     ASSERT(condition);
881     m_condition = condition;
882     *m_condition = false;
883     return self;
884 }
885
886 - (void)webView:(WebView *)sender didFailLoadWithError:(NSError *)error forFrame:(WebFrame *)frame
887 {
888     printf("API Test load failed\n");
889     *m_condition = true;
890 }
891
892 - (void)webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame
893 {
894     printf("API Test load failed provisional\n");
895     *m_condition = true;
896 }
897
898 - (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame
899 {
900     printf("API Test load succeeded\n");
901     *m_condition = true;
902 }
903
904 @end
905
906 #if PLATFORM(IOS)
907
908 @interface APITestDelegateIPhone : NSObject
909 {
910     TestRunner* testRunner;
911     NSData *data;
912     NSURL *baseURL;
913     WebView *webView;
914 }
915 - (id)initWithTestRunner:(TestRunner*)testRunner utf8Data:(JSStringRef)data baseURL:(JSStringRef)baseURL;
916 - (void)run;
917 @end
918
919 @implementation APITestDelegateIPhone
920
921 - (id)initWithTestRunner:(TestRunner*)runner utf8Data:(JSStringRef)dataString baseURL:(JSStringRef)baseURLString
922 {
923     self = [super init];
924     if (!self)
925         return nil;
926
927     testRunner = runner;
928     data = [[(NSString *)adoptCF(JSStringCopyCFString(kCFAllocatorDefault, dataString)).get() dataUsingEncoding:NSUTF8StringEncoding] retain];
929     baseURL = [[NSURL URLWithString:(NSString *)adoptCF(JSStringCopyCFString(kCFAllocatorDefault, baseURLString)).get()] retain];
930     return self;
931 }
932
933 - (void)dealloc
934 {
935     [data release];
936     [baseURL release];
937     [super dealloc];
938 }
939
940 - (void)run
941 {
942     if (webView)
943         return;
944
945     testRunner->setWaitToDump(true);
946
947     WebThreadLock();
948
949     webView = [[WebView alloc] initWithFrame:NSZeroRect frameName:@"" groupName:@""];
950     [webView setFrameLoadDelegate:self];
951     [[webView mainFrame] loadData:data MIMEType:@"text/html" textEncodingName:@"utf-8" baseURL:baseURL];
952 }
953
954 - (void)_cleanUp
955 {
956     if (!webView)
957         return;
958
959     WebThreadLock();
960
961     [webView _clearDelegates];
962     [webView close];
963     [webView release];
964     webView = nil;
965
966     testRunner->notifyDone();
967 }
968
969 - (void)webView:(WebView *)sender didFailLoadWithError:(NSError *)error forFrame:(WebFrame *)frame
970 {
971     printf("API Test load failed\n");
972     [self _cleanUp];
973 }
974
975 - (void)webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame
976 {
977     printf("API Test load failed provisional\n");
978     [self _cleanUp];
979 }
980
981 - (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame
982 {
983     printf("API Test load succeeded\n");
984     [self _cleanUp];
985 }
986
987 @end
988
989 #endif
990
991 void TestRunner::apiTestNewWindowDataLoadBaseURL(JSStringRef utf8Data, JSStringRef baseURL)
992 {
993 #if PLATFORM(IOS)
994     // On iOS this gets called via JavaScript on the WebThread. But since it creates
995     // and closes a WebView, it should be run on the main thread. Make the switch
996     // from the web thread to the main thread and make the test asynchronous.
997     if (WebThreadIsCurrent()) {
998         APITestDelegateIPhone *dispatcher = [[APITestDelegateIPhone alloc] initWithTestRunner:this utf8Data:utf8Data baseURL:baseURL];
999         NSInvocation *invocation = WebThreadMakeNSInvocation(dispatcher, @selector(run));
1000         WebThreadCallDelegate(invocation);
1001         return;
1002     }
1003 #endif
1004
1005     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1006
1007     RetainPtr<CFStringRef> utf8DataCF = adoptCF(JSStringCopyCFString(kCFAllocatorDefault, utf8Data));
1008     RetainPtr<CFStringRef> baseURLCF = adoptCF(JSStringCopyCFString(kCFAllocatorDefault, baseURL));
1009     
1010     WebView *webView = [[WebView alloc] initWithFrame:NSZeroRect frameName:@"" groupName:@""];
1011
1012     bool done = false;
1013     APITestDelegate *delegate = [[APITestDelegate alloc] initWithCompletionCondition:&done];
1014     [webView setFrameLoadDelegate:delegate];
1015
1016     [[webView mainFrame] loadData:[(NSString *)utf8DataCF.get() dataUsingEncoding:NSUTF8StringEncoding] MIMEType:@"text/html" textEncodingName:@"utf-8" baseURL:[NSURL URLWithString:(NSString *)baseURLCF.get()]];
1017     
1018     while (!done) {
1019         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1020         [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantPast]];
1021         [pool release];
1022     }
1023
1024 #if PLATFORM(IOS)
1025     [(DumpRenderTree *)[UIApplication sharedApplication] _waitForWebThread];
1026 #endif
1027
1028     [webView close];
1029     [webView release];
1030     [delegate release];
1031     [pool release];
1032 }
1033
1034 void TestRunner::apiTestGoToCurrentBackForwardItem()
1035 {
1036     WebView *view = [mainFrame webView];
1037     [view goToBackForwardItem:[[view backForwardList] currentItem]];
1038 }
1039
1040 void TestRunner::setWebViewEditable(bool editable)
1041 {
1042     WebView *view = [mainFrame webView];
1043     [view setEditable:editable];
1044 }
1045
1046 static NSString *SynchronousLoaderRunLoopMode = @"DumpRenderTreeSynchronousLoaderRunLoopMode";
1047
1048 @interface SynchronousLoader : NSObject <NSURLConnectionDelegate>
1049 {
1050     NSString *m_username;
1051     NSString *m_password;
1052     BOOL m_isDone;
1053 }
1054 + (void)makeRequest:(NSURLRequest *)request withUsername:(NSString *)username password:(NSString *)password;
1055 @end
1056
1057 @implementation SynchronousLoader : NSObject
1058 - (void)dealloc
1059 {
1060     [m_username release];
1061     [m_password release];
1062
1063     [super dealloc];
1064 }
1065
1066 - (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection
1067 {
1068     return YES;
1069 }
1070
1071 - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
1072 {
1073     if ([challenge previousFailureCount] == 0) {
1074         RetainPtr<NSURLCredential> credential = adoptNS([[NSURLCredential alloc]  initWithUser:m_username password:m_password persistence:NSURLCredentialPersistenceForSession]);
1075         [[challenge sender] useCredential:credential.get() forAuthenticationChallenge:challenge];
1076         return;
1077     }
1078     [[challenge sender] cancelAuthenticationChallenge:challenge];
1079 }
1080
1081 - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
1082 {
1083     printf("SynchronousLoader failed: %s\n", [[error description] UTF8String]);
1084     m_isDone = YES;
1085 }
1086
1087 - (void)connectionDidFinishLoading:(NSURLConnection *)connection
1088 {
1089     m_isDone = YES;
1090 }
1091
1092 + (void)makeRequest:(NSURLRequest *)request withUsername:(NSString *)username password:(NSString *)password
1093 {
1094     ASSERT(![[request URL] user]);
1095     ASSERT(![[request URL] password]);
1096
1097     SynchronousLoader *delegate = [[SynchronousLoader alloc] init];
1098     delegate->m_username = [username copy];
1099     delegate->m_password = [password copy];
1100
1101     NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:delegate startImmediately:NO];
1102     [connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:SynchronousLoaderRunLoopMode];
1103     [connection start];
1104     
1105     while (!delegate->m_isDone)
1106         [[NSRunLoop currentRunLoop] runMode:SynchronousLoaderRunLoopMode beforeDate:[NSDate distantFuture]];
1107
1108     [connection cancel];
1109     
1110     [connection release];
1111     [delegate release];
1112 }
1113
1114 @end
1115
1116 void TestRunner::authenticateSession(JSStringRef url, JSStringRef username, JSStringRef password)
1117 {
1118     // See <rdar://problem/7880699>.
1119     RetainPtr<CFStringRef> urlStringCF = adoptCF(JSStringCopyCFString(kCFAllocatorDefault, url));
1120     RetainPtr<CFStringRef> usernameCF = adoptCF(JSStringCopyCFString(kCFAllocatorDefault, username));
1121     RetainPtr<CFStringRef> passwordCF = adoptCF(JSStringCopyCFString(kCFAllocatorDefault, password));
1122
1123     RetainPtr<NSURLRequest> request = adoptNS([[NSURLRequest alloc] initWithURL:[NSURL URLWithString:(NSString *)urlStringCF.get()]]);
1124
1125     [SynchronousLoader makeRequest:request.get() withUsername:(NSString *)usernameCF.get() password:(NSString *)passwordCF.get()];
1126 }
1127
1128 void TestRunner::abortModal()
1129 {
1130 #if !PLATFORM(IOS)
1131     [NSApp abortModal];
1132 #endif
1133 }
1134
1135 void TestRunner::setSerializeHTTPLoads(bool serialize)
1136 {
1137     [WebView _setLoadResourcesSerially:serialize];
1138 }
1139
1140 void TestRunner::setTextDirection(JSStringRef directionName)
1141 {
1142 #if !PLATFORM(IOS)
1143     if (JSStringIsEqualToUTF8CString(directionName, "ltr"))
1144         [[mainFrame webView] makeBaseWritingDirectionLeftToRight:0];
1145     else if (JSStringIsEqualToUTF8CString(directionName, "rtl"))
1146         [[mainFrame webView] makeBaseWritingDirectionRightToLeft:0];
1147     else
1148         ASSERT_NOT_REACHED();
1149 #endif
1150 }
1151
1152 void TestRunner::addChromeInputField()
1153 {
1154 #if !PLATFORM(IOS)
1155     NSTextField *textField = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 100, 20)];
1156     textField.tag = 1;
1157     [[[[mainFrame webView] window] contentView] addSubview:textField];
1158     [textField release];
1159     
1160     [textField setNextKeyView:[mainFrame webView]];
1161     [[mainFrame webView] setNextKeyView:textField];
1162 #endif
1163 }
1164
1165 void TestRunner::removeChromeInputField()
1166 {
1167 #if !PLATFORM(IOS)
1168     NSView* textField = [[[[mainFrame webView] window] contentView] viewWithTag:1];
1169     if (textField) {
1170         [textField removeFromSuperview];
1171         focusWebView();
1172     }
1173 #endif
1174 }
1175
1176 void TestRunner::focusWebView()
1177 {
1178 #if !PLATFORM(IOS)
1179     [[[mainFrame webView] window] makeFirstResponder:[mainFrame webView]];
1180 #endif
1181 }
1182
1183 void TestRunner::setBackingScaleFactor(double backingScaleFactor)
1184 {
1185 #if !PLATFORM(IOS)
1186     [[mainFrame webView] _setCustomBackingScaleFactor:backingScaleFactor];
1187 #endif
1188 }
1189
1190 void TestRunner::resetPageVisibility()
1191 {
1192     WebView *webView = [mainFrame webView];
1193     if ([webView respondsToSelector:@selector(_setVisibilityState:isInitialState:)])
1194         [webView _setVisibilityState:WebPageVisibilityStateVisible isInitialState:YES];
1195 }
1196
1197 void TestRunner::setPageVisibility(const char* newVisibility)
1198 {
1199     if (!newVisibility)
1200         return;
1201
1202     WebView *webView = [mainFrame webView];
1203     if (!strcmp(newVisibility, "visible"))
1204         [webView _setVisibilityState:WebPageVisibilityStateVisible isInitialState:NO];
1205     else if (!strcmp(newVisibility, "hidden"))
1206         [webView _setVisibilityState:WebPageVisibilityStateHidden isInitialState:NO];
1207     else if (!strcmp(newVisibility, "prerender"))
1208         [webView _setVisibilityState:WebPageVisibilityStatePrerender isInitialState:NO];
1209 }
1210
1211 void TestRunner::grantWebNotificationPermission(JSStringRef jsOrigin)
1212 {
1213     RetainPtr<CFStringRef> cfOrigin = adoptCF(JSStringCopyCFString(kCFAllocatorDefault, jsOrigin));
1214     ASSERT([[mainFrame webView] _notificationProvider] == [MockWebNotificationProvider shared]);
1215     [[MockWebNotificationProvider shared] setWebNotificationOrigin:(NSString *)cfOrigin.get() permission:TRUE];
1216 }
1217
1218 void TestRunner::denyWebNotificationPermission(JSStringRef jsOrigin)
1219 {
1220     RetainPtr<CFStringRef> cfOrigin = adoptCF(JSStringCopyCFString(kCFAllocatorDefault, jsOrigin));
1221     ASSERT([[mainFrame webView] _notificationProvider] == [MockWebNotificationProvider shared]);
1222     [[MockWebNotificationProvider shared] setWebNotificationOrigin:(NSString *)cfOrigin.get() permission:FALSE];
1223 }
1224
1225 void TestRunner::removeAllWebNotificationPermissions()
1226 {
1227     [[MockWebNotificationProvider shared] removeAllWebNotificationPermissions];
1228 }
1229
1230 void TestRunner::simulateWebNotificationClick(JSValueRef jsNotification)
1231 {
1232     uint64_t notificationID = [[mainFrame webView] _notificationIDForTesting:jsNotification];
1233     m_hasPendingWebNotificationClick = true;
1234     dispatch_async(dispatch_get_main_queue(), ^{
1235         if (!m_hasPendingWebNotificationClick)
1236             return;
1237
1238         [[MockWebNotificationProvider shared] simulateWebNotificationClick:notificationID];
1239         m_hasPendingWebNotificationClick = false;
1240     });
1241 }
1242
1243 void TestRunner::simulateLegacyWebNotificationClick(JSStringRef jsTitle)
1244 {
1245 }
1246