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