d0599e0f1e0bc0a8cf88cd344cbf634b96ef433f
[WebKit-https.git] / WebKitTools / DumpRenderTree / mac / LayoutTestControllerMac.mm
1 /*
2  * Copyright (C) 2007, 2008, 2009 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 "LayoutTestController.h"
32
33 #import "EditingDelegate.h"
34 #import "MockGeolocationProvider.h"
35 #import "PolicyDelegate.h"
36 #import "WorkQueue.h"
37 #import "WorkQueueItem.h"
38 #import <Foundation/Foundation.h>
39 #import <JavaScriptCore/JSRetainPtr.h>
40 #import <JavaScriptCore/JSStringRef.h>
41 #import <JavaScriptCore/JSStringRefCF.h>
42 #import <WebKit/DOMDocument.h>
43 #import <WebKit/DOMElement.h>
44 #import <WebKit/WebApplicationCache.h>
45 #import <WebKit/WebBackForwardList.h>
46 #import <WebKit/WebCoreStatistics.h>
47 #import <WebKit/WebDataSource.h>
48 #import <WebKit/WebDatabaseManagerPrivate.h>
49 #import <WebKit/WebFrame.h>
50 #import <WebKit/WebFrameViewPrivate.h>
51 #import <WebKit/WebGeolocationPosition.h>
52 #import <WebKit/WebHTMLRepresentation.h>
53 #import <WebKit/WebHTMLViewPrivate.h>
54 #import <WebKit/WebHistory.h>
55 #import <WebKit/WebHistoryPrivate.h>
56 #import <WebKit/WebIconDatabasePrivate.h>
57 #import <WebKit/WebInspectorPrivate.h>
58 #import <WebKit/WebNSURLExtras.h>
59 #import <WebKit/WebKitErrors.h>
60 #import <WebKit/WebPreferences.h>
61 #import <WebKit/WebPreferencesPrivate.h>
62 #import <WebKit/WebScriptWorld.h>
63 #import <WebKit/WebSecurityOriginPrivate.h>
64 #import <WebKit/WebTypesInternal.h>
65 #import <WebKit/WebView.h>
66 #import <WebKit/WebViewPrivate.h>
67 #import <WebKit/WebWorkersPrivate.h>
68 #import <wtf/HashMap.h>
69 #import <wtf/RetainPtr.h>
70
71 @interface CommandValidationTarget : NSObject <NSValidatedUserInterfaceItem>
72 {
73     SEL _action;
74 }
75 - (id)initWithAction:(SEL)action;
76 @end
77
78 @implementation CommandValidationTarget
79
80 - (id)initWithAction:(SEL)action
81 {
82     self = [super init];
83     if (!self)
84         return nil;
85
86     _action = action;
87     return self;
88 }
89
90 - (SEL)action
91 {
92     return _action;
93 }
94
95 - (NSInteger)tag
96 {
97     return 0;
98 }
99
100 @end
101
102 LayoutTestController::~LayoutTestController()
103 {
104 }
105
106 void LayoutTestController::addDisallowedURL(JSStringRef url)
107 {
108     RetainPtr<CFStringRef> urlCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, url));
109
110     if (!disallowedURLs)
111         disallowedURLs = CFSetCreateMutable(kCFAllocatorDefault, 0, NULL);
112
113     // Canonicalize the URL
114     NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:(NSString *)urlCF.get()]];
115     request = [NSURLProtocol canonicalRequestForRequest:request];
116
117     CFSetAddValue(disallowedURLs, [request URL]);
118 }
119
120 bool LayoutTestController::callShouldCloseOnWebView()
121 {
122     return [[mainFrame webView] shouldClose];
123 }
124
125 void LayoutTestController::clearAllDatabases()
126 {
127     [[WebDatabaseManager sharedWebDatabaseManager] deleteAllDatabases];
128 }
129
130 void LayoutTestController::clearBackForwardList()
131 {
132     WebBackForwardList *backForwardList = [[mainFrame webView] backForwardList];
133     WebHistoryItem *item = [[backForwardList currentItem] retain];
134
135     // We clear the history by setting the back/forward list's capacity to 0
136     // then restoring it back and adding back the current item.
137     int capacity = [backForwardList capacity];
138     [backForwardList setCapacity:0];
139     [backForwardList setCapacity:capacity];
140     [backForwardList addItem:item];
141     [backForwardList goToItem:item];
142     [item release];
143 }
144
145 JSStringRef LayoutTestController::copyDecodedHostName(JSStringRef name)
146 {
147     RetainPtr<CFStringRef> nameCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, name));
148     NSString *nameNS = (NSString *)nameCF.get();
149     return JSStringCreateWithCFString((CFStringRef)[nameNS _web_decodeHostName]);
150 }
151
152 JSStringRef LayoutTestController::copyEncodedHostName(JSStringRef name)
153 {
154     RetainPtr<CFStringRef> nameCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, name));
155     NSString *nameNS = (NSString *)nameCF.get();
156     return JSStringCreateWithCFString((CFStringRef)[nameNS _web_encodeHostName]);
157 }
158
159 void LayoutTestController::display()
160 {
161     displayWebView();
162 }
163
164 JSRetainPtr<JSStringRef> LayoutTestController::counterValueForElementById(JSStringRef id)
165 {
166     RetainPtr<CFStringRef> idCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, id));
167     NSString *idNS = (NSString *)idCF.get();
168
169     DOMElement *element = [[mainFrame DOMDocument] getElementById:idNS];
170     if (!element)
171         return 0;
172
173     JSRetainPtr<JSStringRef> counterValue(Adopt, JSStringCreateWithCFString((CFStringRef)[mainFrame counterValueForElement:element]));
174     return counterValue;
175 }
176
177 void LayoutTestController::keepWebHistory()
178 {
179     if (![WebHistory optionalSharedHistory]) {
180         WebHistory *history = [[WebHistory alloc] init];
181         [WebHistory setOptionalSharedHistory:history];
182         [history release];
183     }
184 }
185
186 JSValueRef LayoutTestController::computedStyleIncludingVisitedInfo(JSContextRef context, JSValueRef value)
187 {   
188     return [[mainFrame webView] _computedStyleIncludingVisitedInfo:context forElement:value];
189 }
190
191 JSRetainPtr<JSStringRef> LayoutTestController::layerTreeAsText() const
192 {
193     JSRetainPtr<JSStringRef> string(Adopt, JSStringCreateWithCFString((CFStringRef)[mainFrame _layerTreeAsText]));
194     return string;
195 }
196
197 JSRetainPtr<JSStringRef> LayoutTestController::markerTextForListItem(JSContextRef context, JSValueRef nodeObject) const
198 {
199     // FIXME: Implement me.
200     return JSRetainPtr<JSStringRef>();
201 }
202
203 int LayoutTestController::pageNumberForElementById(JSStringRef id, float pageWidthInPixels, float pageHeightInPixels)
204 {
205     RetainPtr<CFStringRef> idCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, id));
206     NSString *idNS = (NSString *)idCF.get();
207
208     DOMElement *element = [[mainFrame DOMDocument] getElementById:idNS];
209     if (!element)
210         return -1;
211
212     return [mainFrame pageNumberForElement:element:pageWidthInPixels:pageHeightInPixels];
213 }
214
215 JSRetainPtr<JSStringRef> LayoutTestController::pageProperty(const char* propertyName, int pageNumber) const
216 {
217     JSRetainPtr<JSStringRef> propertyValue(Adopt, JSStringCreateWithCFString((CFStringRef)[mainFrame pageProperty:propertyName:pageNumber]));
218     return propertyValue;
219 }
220
221 bool LayoutTestController::isPageBoxVisible(int pageNumber) const
222 {
223     return [mainFrame isPageBoxVisible:pageNumber];
224 }
225
226 JSRetainPtr<JSStringRef> LayoutTestController::pageAreaRectInPixels(int pageNumber) const
227 {
228     JSRetainPtr<JSStringRef> propertyValue(Adopt, JSStringCreateWithCFString((CFStringRef)[mainFrame pageAreaRectInPixels:pageNumber]));
229     return propertyValue;
230 }
231
232 JSRetainPtr<JSStringRef> LayoutTestController::preferredPageSizeInPixels(int pageNumber) const
233 {
234     JSRetainPtr<JSStringRef> propertyValue(Adopt, JSStringCreateWithCFString((CFStringRef)[mainFrame preferredPageSizeInPixels:pageNumber]));
235     return propertyValue;
236 }
237
238 int LayoutTestController::numberOfPages(float pageWidthInPixels, float pageHeightInPixels)
239 {
240     return [mainFrame numberOfPages:pageWidthInPixels:pageHeightInPixels];
241 }
242
243 size_t LayoutTestController::webHistoryItemCount()
244 {
245     return [[[WebHistory optionalSharedHistory] allItems] count];
246 }
247
248 unsigned LayoutTestController::workerThreadCount() const
249 {
250     return [WebWorkersPrivate workerThreadCount];
251 }
252
253 void LayoutTestController::notifyDone()
254 {
255     if (m_waitToDump && !topLoadingFrame && !WorkQueue::shared()->count())
256         dump();
257     m_waitToDump = false;
258 }
259
260 JSStringRef LayoutTestController::pathToLocalResource(JSContextRef context, JSStringRef url)
261 {
262     return JSStringRetain(url); // Do nothing on mac.
263 }
264
265 void LayoutTestController::queueLoad(JSStringRef url, JSStringRef target)
266 {
267     RetainPtr<CFStringRef> urlCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, url));
268     NSString *urlNS = (NSString *)urlCF.get();
269
270     NSURL *nsurl = [NSURL URLWithString:urlNS relativeToURL:[[[mainFrame dataSource] response] URL]];
271     NSString* nsurlString = [nsurl absoluteString];
272
273     JSRetainPtr<JSStringRef> absoluteURL(Adopt, JSStringCreateWithUTF8CString([nsurlString UTF8String]));
274     WorkQueue::shared()->queue(new LoadItem(absoluteURL.get(), target));
275 }
276
277 void LayoutTestController::setAcceptsEditing(bool newAcceptsEditing)
278 {
279     [(EditingDelegate *)[[mainFrame webView] editingDelegate] setAcceptsEditing:newAcceptsEditing];
280 }
281
282 void LayoutTestController::setAlwaysAcceptCookies(bool alwaysAcceptCookies)
283 {
284     if (alwaysAcceptCookies == m_alwaysAcceptCookies)
285         return;
286
287     m_alwaysAcceptCookies = alwaysAcceptCookies;
288     NSHTTPCookieAcceptPolicy cookieAcceptPolicy = alwaysAcceptCookies ? NSHTTPCookieAcceptPolicyAlways : NSHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain;
289     [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookieAcceptPolicy:cookieAcceptPolicy];
290 }
291
292 void LayoutTestController::setAppCacheMaximumSize(unsigned long long size)
293 {
294     [WebApplicationCache setMaximumSize:size];
295 }
296
297 void LayoutTestController::setAuthorAndUserStylesEnabled(bool flag)
298 {
299     [[[mainFrame webView] preferences] setAuthorAndUserStylesEnabled:flag];
300 }
301
302 void LayoutTestController::setCustomPolicyDelegate(bool setDelegate, bool permissive)
303 {
304     if (setDelegate) {
305         [policyDelegate setPermissive:permissive];
306         [[mainFrame webView] setPolicyDelegate:policyDelegate];
307     } else
308         [[mainFrame webView] setPolicyDelegate:nil];
309 }
310
311 void LayoutTestController::setDatabaseQuota(unsigned long long quota)
312 {    
313     WebSecurityOrigin *origin = [[WebSecurityOrigin alloc] initWithURL:[NSURL URLWithString:@"file:///"]];
314     [origin setQuota:quota];
315     [origin release];
316 }
317
318 void LayoutTestController::setDomainRelaxationForbiddenForURLScheme(bool forbidden, JSStringRef scheme)
319 {
320     RetainPtr<CFStringRef> schemeCFString(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, scheme));
321     [WebView _setDomainRelaxationForbidden:forbidden forURLScheme:(NSString *)schemeCFString.get()];
322 }
323
324 void LayoutTestController::setMockGeolocationPosition(double latitude, double longitude, double accuracy)
325 {
326     WebGeolocationPosition *position = [[WebGeolocationPosition alloc] initWithTimestamp:0 latitude:latitude longitude:longitude accuracy:accuracy];
327     [[MockGeolocationProvider shared] setPosition:position];
328     [position release];
329 }
330
331 void LayoutTestController::setMockGeolocationError(int code, JSStringRef message)
332 {
333     RetainPtr<CFStringRef> messageCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, message));
334     NSString *messageNS = (NSString *)messageCF.get();
335     NSError *error = [NSError errorWithDomain:WebKitErrorDomain code:code userInfo:[NSDictionary dictionaryWithObject:messageNS forKey:NSLocalizedDescriptionKey]];
336     [[MockGeolocationProvider shared] setError:error];
337 }
338
339 void LayoutTestController::setIconDatabaseEnabled(bool iconDatabaseEnabled)
340 {
341     // FIXME: Workaround <rdar://problem/6480108>
342     static WebIconDatabase* sharedWebIconDatabase = NULL;
343     if (!sharedWebIconDatabase) {
344         if (!iconDatabaseEnabled)
345             return;
346         sharedWebIconDatabase = [WebIconDatabase sharedIconDatabase];
347         if ([sharedWebIconDatabase isEnabled] == iconDatabaseEnabled)
348             return;
349     }
350     [sharedWebIconDatabase setEnabled:iconDatabaseEnabled];
351 }
352
353 void LayoutTestController::setJavaScriptProfilingEnabled(bool profilingEnabled)
354 {
355     setDeveloperExtrasEnabled(profilingEnabled);
356     [[[mainFrame webView] inspector] setJavaScriptProfilingEnabled:profilingEnabled];
357 }
358
359 void LayoutTestController::setMainFrameIsFirstResponder(bool flag)
360 {
361     NSView *documentView = [[mainFrame frameView] documentView];
362     
363     NSResponder *firstResponder = flag ? documentView : nil;
364     [[[mainFrame webView] window] makeFirstResponder:firstResponder];
365 }
366
367 void LayoutTestController::setPrivateBrowsingEnabled(bool privateBrowsingEnabled)
368 {
369     [[[mainFrame webView] preferences] setPrivateBrowsingEnabled:privateBrowsingEnabled];
370 }
371
372 void LayoutTestController::setXSSAuditorEnabled(bool enabled)
373 {
374     [[[mainFrame webView] preferences] setXSSAuditorEnabled:enabled];
375 }
376
377 void LayoutTestController::setFrameFlatteningEnabled(bool enabled)
378 {
379     [[[mainFrame webView] preferences] setFrameFlatteningEnabled:enabled];
380 }
381
382 void LayoutTestController::setSpatialNavigationEnabled(bool enabled)
383 {
384     // FIXME: Implement for SpatialNavigation layout tests.
385 }
386
387 void LayoutTestController::setAllowUniversalAccessFromFileURLs(bool enabled)
388 {
389     [[[mainFrame webView] preferences] setAllowUniversalAccessFromFileURLs:enabled];
390 }
391
392 void LayoutTestController::setAllowFileAccessFromFileURLs(bool enabled)
393 {
394     [[[mainFrame webView] preferences] setAllowFileAccessFromFileURLs:enabled];
395 }
396
397 void LayoutTestController::setPopupBlockingEnabled(bool popupBlockingEnabled)
398 {
399     [[[mainFrame webView] preferences] setJavaScriptCanOpenWindowsAutomatically:!popupBlockingEnabled];
400 }
401
402 void LayoutTestController::setPluginsEnabled(bool pluginsEnabled)
403 {
404     [[[mainFrame webView] preferences] setPlugInsEnabled:pluginsEnabled];
405 }
406
407 void LayoutTestController::setJavaScriptCanAccessClipboard(bool enabled)
408 {
409     [[[mainFrame webView] preferences] setJavaScriptCanAccessClipboard:enabled];
410 }
411
412 void LayoutTestController::setTabKeyCyclesThroughElements(bool cycles)
413 {
414     [[mainFrame webView] setTabKeyCyclesThroughElements:cycles];
415 }
416
417 void LayoutTestController::setTimelineProfilingEnabled(bool enabled)
418 {
419     [[[mainFrame webView] inspector] setTimelineProfilingEnabled:enabled];
420 }
421
422 void LayoutTestController::setUseDashboardCompatibilityMode(bool flag)
423 {
424     [[mainFrame webView] _setDashboardBehavior:WebDashboardBehaviorUseBackwardCompatibilityMode to:flag];
425 }
426
427 void LayoutTestController::setUserStyleSheetEnabled(bool flag)
428 {
429     [[WebPreferences standardPreferences] setUserStyleSheetEnabled:flag];
430 }
431
432 void LayoutTestController::setUserStyleSheetLocation(JSStringRef path)
433 {
434     RetainPtr<CFStringRef> pathCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, path));
435     NSURL *url = [NSURL URLWithString:(NSString *)pathCF.get()];
436     [[WebPreferences standardPreferences] setUserStyleSheetLocation:url];
437 }
438
439 void LayoutTestController::disableImageLoading()
440 {
441     [[WebPreferences standardPreferences] setLoadsImagesAutomatically:NO];
442 }
443
444 void LayoutTestController::dispatchPendingLoadRequests()
445 {
446     [[mainFrame webView] _dispatchPendingLoadRequests];
447 }
448
449 void LayoutTestController::overridePreference(JSStringRef key, JSStringRef value)
450 {
451     RetainPtr<CFStringRef> keyCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, key));
452     NSString *keyNS = (NSString *)keyCF.get();
453
454     RetainPtr<CFStringRef> valueCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, value));
455     NSString *valueNS = (NSString *)valueCF.get();
456
457     [[WebPreferences standardPreferences] _setPreferenceForTestWithValue:valueNS forKey:keyNS];
458 }
459
460 void LayoutTestController::removeAllVisitedLinks()
461 {
462     [WebHistory _removeAllVisitedLinks];
463 }
464
465 void LayoutTestController::setPersistentUserStyleSheetLocation(JSStringRef jsURL)
466 {
467     RetainPtr<CFStringRef> urlString(AdoptCF, JSStringCopyCFString(0, jsURL));
468     ::setPersistentUserStyleSheetLocation(urlString.get());
469 }
470
471 void LayoutTestController::clearPersistentUserStyleSheet()
472 {
473     ::setPersistentUserStyleSheetLocation(0);
474 }
475
476 void LayoutTestController::setWindowIsKey(bool windowIsKey)
477 {
478     m_windowIsKey = windowIsKey;
479     [[mainFrame webView] _updateActiveState];
480 }
481
482 void LayoutTestController::setSmartInsertDeleteEnabled(bool flag)
483 {
484     [[mainFrame webView] setSmartInsertDeleteEnabled:flag];
485 }
486
487 void LayoutTestController::setSelectTrailingWhitespaceEnabled(bool flag)
488 {
489     [[mainFrame webView] setSelectTrailingWhitespaceEnabled:flag];
490 }
491
492 static const CFTimeInterval waitToDumpWatchdogInterval = 30.0;
493
494 static void waitUntilDoneWatchdogFired(CFRunLoopTimerRef timer, void* info)
495 {
496     gLayoutTestController->waitToDumpWatchdogTimerFired();
497 }
498
499 void LayoutTestController::setWaitToDump(bool waitUntilDone)
500 {
501     m_waitToDump = waitUntilDone;
502     if (m_waitToDump && !waitToDumpWatchdog) {
503         waitToDumpWatchdog = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + waitToDumpWatchdogInterval, 0, 0, 0, waitUntilDoneWatchdogFired, NULL);
504         CFRunLoopAddTimer(CFRunLoopGetCurrent(), waitToDumpWatchdog, kCFRunLoopCommonModes);
505     }
506 }
507
508 int LayoutTestController::windowCount()
509 {
510     return CFArrayGetCount(openWindowsRef);
511 }
512
513 bool LayoutTestController::elementDoesAutoCompleteForElementWithId(JSStringRef jsString)
514 {
515     RetainPtr<CFStringRef> idCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, jsString));
516     NSString *idNS = (NSString *)idCF.get();
517     
518     DOMElement *element = [[mainFrame DOMDocument] getElementById:idNS];
519     id rep = [[mainFrame dataSource] representation];
520     
521     if ([rep class] == [WebHTMLRepresentation class])
522         return [(WebHTMLRepresentation *)rep elementDoesAutoComplete:element];
523
524     return false;
525 }
526
527 void LayoutTestController::execCommand(JSStringRef name, JSStringRef value)
528 {
529     RetainPtr<CFStringRef> nameCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, name));
530     NSString *nameNS = (NSString *)nameCF.get();
531
532     RetainPtr<CFStringRef> valueCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, value));
533     NSString *valueNS = (NSString *)valueCF.get();
534
535     [[mainFrame webView] _executeCoreCommandByName:nameNS value:valueNS];
536 }
537
538 void LayoutTestController::setCacheModel(int cacheModel)
539 {
540     [[WebPreferences standardPreferences] setCacheModel:cacheModel];
541 }
542
543 bool LayoutTestController::isCommandEnabled(JSStringRef name)
544 {
545     RetainPtr<CFStringRef> nameCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, name));
546     NSString *nameNS = (NSString *)nameCF.get();
547
548     // Accept command strings with capital letters for first letter without trailing colon.
549     if (![nameNS hasSuffix:@":"] && [nameNS length]) {
550         nameNS = [[[[nameNS substringToIndex:1] lowercaseString]
551             stringByAppendingString:[nameNS substringFromIndex:1]]
552             stringByAppendingString:@":"];
553     }
554
555     SEL selector = NSSelectorFromString(nameNS);
556     RetainPtr<CommandValidationTarget> target(AdoptNS, [[CommandValidationTarget alloc] initWithAction:selector]);
557     id validator = [NSApp targetForAction:selector to:[mainFrame webView] from:target.get()];
558     if (!validator)
559         return false;
560     if (![validator respondsToSelector:selector])
561         return false;
562     if (![validator respondsToSelector:@selector(validateUserInterfaceItem:)])
563         return true;
564     return [validator validateUserInterfaceItem:target.get()];
565 }
566
567 bool LayoutTestController::pauseAnimationAtTimeOnElementWithId(JSStringRef animationName, double time, JSStringRef elementId)
568 {
569     RetainPtr<CFStringRef> idCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, elementId));
570     NSString *idNS = (NSString *)idCF.get();
571     RetainPtr<CFStringRef> nameCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, animationName));
572     NSString *nameNS = (NSString *)nameCF.get();
573     
574     return [mainFrame _pauseAnimation:nameNS onNode:[[mainFrame DOMDocument] getElementById:idNS] atTime:time];
575 }
576
577 bool LayoutTestController::pauseTransitionAtTimeOnElementWithId(JSStringRef propertyName, double time, JSStringRef elementId)
578 {
579     RetainPtr<CFStringRef> idCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, elementId));
580     NSString *idNS = (NSString *)idCF.get();
581     RetainPtr<CFStringRef> nameCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, propertyName));
582     NSString *nameNS = (NSString *)nameCF.get();
583     
584     return [mainFrame _pauseTransitionOfProperty:nameNS onNode:[[mainFrame DOMDocument] getElementById:idNS] atTime:time];
585 }
586
587 bool LayoutTestController::sampleSVGAnimationForElementAtTime(JSStringRef animationId, double time, JSStringRef elementId)
588 {
589     RetainPtr<CFStringRef> animationIDCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, animationId));
590     NSString *animationIDNS = (NSString *)animationIDCF.get();
591     RetainPtr<CFStringRef> elementIDCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, elementId));
592     NSString *elementIDNS = (NSString *)elementIDCF.get();
593
594     return [mainFrame _pauseSVGAnimation:elementIDNS onSMILNode:[[mainFrame DOMDocument] getElementById:animationIDNS] atTime:time];
595 }
596
597 unsigned LayoutTestController::numberOfActiveAnimations() const
598 {
599     return [mainFrame _numberOfActiveAnimations];
600 }
601
602 void LayoutTestController::waitForPolicyDelegate()
603 {
604     setWaitToDump(true);
605     [policyDelegate setControllerToNotifyDone:this];
606     [[mainFrame webView] setPolicyDelegate:policyDelegate];
607 }
608
609 void LayoutTestController::addOriginAccessWhitelistEntry(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains)
610 {
611     RetainPtr<CFStringRef> sourceOriginCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, sourceOrigin));
612     NSString *sourceOriginNS = (NSString *)sourceOriginCF.get();
613     RetainPtr<CFStringRef> protocolCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, destinationProtocol));
614     NSString *destinationProtocolNS = (NSString *)protocolCF.get();
615     RetainPtr<CFStringRef> hostCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, destinationHost));
616     NSString *destinationHostNS = (NSString *)hostCF.get();
617     [WebView _addOriginAccessWhitelistEntryWithSourceOrigin:sourceOriginNS destinationProtocol:destinationProtocolNS destinationHost:destinationHostNS allowDestinationSubdomains:allowDestinationSubdomains];
618 }
619
620 void LayoutTestController::removeOriginAccessWhitelistEntry(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains)
621 {
622     RetainPtr<CFStringRef> sourceOriginCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, sourceOrigin));
623     NSString *sourceOriginNS = (NSString *)sourceOriginCF.get();
624     RetainPtr<CFStringRef> protocolCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, destinationProtocol));
625     NSString *destinationProtocolNS = (NSString *)protocolCF.get();
626     RetainPtr<CFStringRef> hostCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, destinationHost));
627     NSString *destinationHostNS = (NSString *)hostCF.get();
628     [WebView _removeOriginAccessWhitelistEntryWithSourceOrigin:sourceOriginNS destinationProtocol:destinationProtocolNS destinationHost:destinationHostNS allowDestinationSubdomains:allowDestinationSubdomains];
629 }
630
631 void LayoutTestController::setScrollbarPolicy(JSStringRef orientation, JSStringRef policy)
632 {
633     // FIXME: implement
634 }
635
636 void LayoutTestController::addUserScript(JSStringRef source, bool runAtStart)
637 {
638     RetainPtr<CFStringRef> sourceCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, source));
639     NSString *sourceNS = (NSString *)sourceCF.get();
640     [WebView _addUserScriptToGroup:@"org.webkit.DumpRenderTree" world:[WebScriptWorld world] source:sourceNS url:nil whitelist:nil blacklist:nil injectionTime:(runAtStart ? WebInjectAtDocumentStart : WebInjectAtDocumentEnd)];
641 }
642
643 void LayoutTestController::addUserStyleSheet(JSStringRef source)
644 {
645     RetainPtr<CFStringRef> sourceCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, source));
646     NSString *sourceNS = (NSString *)sourceCF.get();
647     [WebView _addUserStyleSheetToGroup:@"org.webkit.DumpRenderTree" world:[WebScriptWorld world] source:sourceNS url:nil whitelist:nil blacklist:nil];
648 }
649
650 void LayoutTestController::setDeveloperExtrasEnabled(bool enabled)
651 {
652     [[[mainFrame webView] preferences] setDeveloperExtrasEnabled:enabled];
653 }
654
655 void LayoutTestController::showWebInspector()
656 {
657     [[[mainFrame webView] inspector] show:nil];
658 }
659
660 void LayoutTestController::closeWebInspector()
661 {
662     [[[mainFrame webView] inspector] close:nil];
663 }
664
665 void LayoutTestController::evaluateInWebInspector(long callId, JSStringRef script)
666 {
667     RetainPtr<CFStringRef> scriptCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, script));
668     NSString *scriptNS = (NSString *)scriptCF.get();
669     [[[mainFrame webView] inspector] evaluateInFrontend:nil callId:callId script:scriptNS];
670 }
671
672 typedef HashMap<unsigned, RetainPtr<WebScriptWorld> > WorldMap;
673 static WorldMap& worldMap()
674 {
675     static WorldMap& map = *new WorldMap;
676     return map;
677 }
678
679 unsigned worldIDForWorld(WebScriptWorld *world)
680 {
681     WorldMap::const_iterator end = worldMap().end();
682     for (WorldMap::const_iterator it = worldMap().begin(); it != end; ++it) {
683         if (it->second == world)
684             return it->first;
685     }
686
687     return 0;
688 }
689
690 void LayoutTestController::evaluateScriptInIsolatedWorld(unsigned worldID, JSObjectRef globalObject, JSStringRef script)
691 {
692     RetainPtr<CFStringRef> scriptCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, script));
693     NSString *scriptNS = (NSString *)scriptCF.get();
694
695     // A worldID of 0 always corresponds to a new world. Any other worldID corresponds to a world
696     // that is created once and cached forever.
697     WebScriptWorld *world;
698     if (!worldID)
699         world = [WebScriptWorld world];
700     else {
701         RetainPtr<WebScriptWorld>& worldSlot = worldMap().add(worldID, 0).first->second;
702         if (!worldSlot)
703             worldSlot.adoptNS([[WebScriptWorld alloc] init]);
704         world = worldSlot.get();
705     }
706
707     [mainFrame _stringByEvaluatingJavaScriptFromString:scriptNS withGlobalObject:globalObject inScriptWorld:world];
708 }
709
710 @interface APITestDelegate : NSObject
711 {
712     bool* m_condition;
713 }
714 @end
715
716 @implementation APITestDelegate
717
718 - (id)initWithCompletionCondition:(bool*)condition
719 {
720     [super init];
721     ASSERT(condition);
722     m_condition = condition;
723     *m_condition = false;
724     return self;
725 }
726
727 - (void)webView:(WebView *)sender didFailLoadWithError:(NSError *)error forFrame:(WebFrame *)frame
728 {
729     printf("API Test load failed\n");
730     *m_condition = true;
731 }
732
733 - (void)webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame
734 {
735     printf("API Test load failed provisional\n");
736     *m_condition = true;
737 }
738
739 - (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame
740 {
741     printf("API Test load succeeded\n");
742     *m_condition = true;
743 }
744
745 @end
746
747 void LayoutTestController::apiTestNewWindowDataLoadBaseURL(JSStringRef utf8Data, JSStringRef baseURL)
748 {
749     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
750
751     RetainPtr<CFStringRef> utf8DataCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, utf8Data));
752     RetainPtr<CFStringRef> baseURLCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, baseURL));
753     
754     WebView *webView = [[WebView alloc] initWithFrame:NSZeroRect frameName:@"" groupName:@""];
755
756     bool done = false;
757     APITestDelegate *delegate = [[APITestDelegate alloc] initWithCompletionCondition:&done];
758     [webView setFrameLoadDelegate:delegate];
759
760     [[webView mainFrame] loadData:[(NSString *)utf8DataCF.get() dataUsingEncoding:NSUTF8StringEncoding] MIMEType:@"text/html" textEncodingName:@"utf-8" baseURL:[NSURL URLWithString:(NSString *)baseURLCF.get()]];
761     
762     while (!done) {
763         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
764         [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantPast]];
765         [pool release];
766     }
767         
768     [webView close];
769     [webView release];
770     [delegate release];
771     [pool release];
772 }
773
774 void LayoutTestController::apiTestGoToCurrentBackForwardItem()
775 {
776     WebView *view = [mainFrame webView];
777     [view goToBackForwardItem:[[view backForwardList] currentItem]];
778 }
779
780 void LayoutTestController::setWebViewEditable(bool editable)
781 {
782     WebView *view = [mainFrame webView];
783     [view setEditable:editable];
784 }
785
786 #ifndef BUILDING_ON_TIGER
787 static NSString *SynchronousLoaderRunLoopMode = @"DumpRenderTreeSynchronousLoaderRunLoopMode";
788
789 #if defined(BUILDING_ON_LEOPARD) || defined(BUILDING_ON_SNOW_LEOPARD)
790 @protocol NSURLConnectionDelegate <NSObject>
791 @end
792 #endif
793
794 @interface SynchronousLoader : NSObject <NSURLConnectionDelegate>
795 {
796     NSString *m_username;
797     NSString *m_password;
798     BOOL m_isDone;
799 }
800 + (void)makeRequest:(NSURLRequest *)request withUsername:(NSString *)username password:(NSString *)password;
801 @end
802
803 @implementation SynchronousLoader : NSObject
804 - (void)dealloc
805 {
806     [m_username release];
807     [m_password release];
808
809     [super dealloc];
810 }
811
812 - (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection
813 {
814     return YES;
815 }
816
817 - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
818 {
819     if ([challenge previousFailureCount] == 0) {
820         NSURLCredential *credential = [[NSURLCredential alloc]  initWithUser:m_username password:m_password persistence:NSURLCredentialPersistenceForSession];
821         [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
822         return;
823     }
824     [[challenge sender] cancelAuthenticationChallenge:challenge];
825 }
826
827 - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
828 {
829     printf("SynchronousLoader failed: %s\n", [[error description] UTF8String]);
830     m_isDone = YES;
831 }
832
833 - (void)connectionDidFinishLoading:(NSURLConnection *)connection
834 {
835     m_isDone = YES;
836 }
837
838 + (void)makeRequest:(NSURLRequest *)request withUsername:(NSString *)username password:(NSString *)password
839 {
840     ASSERT(![[request URL] user]);
841     ASSERT(![[request URL] password]);
842
843     SynchronousLoader *delegate = [[SynchronousLoader alloc] init];
844     delegate->m_username = [username copy];
845     delegate->m_password = [password copy];
846
847     NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:delegate startImmediately:NO];
848     [connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:SynchronousLoaderRunLoopMode];
849     [connection start];
850     
851     while (!delegate->m_isDone)
852         [[NSRunLoop currentRunLoop] runMode:SynchronousLoaderRunLoopMode beforeDate:[NSDate distantFuture]];
853
854     [connection cancel];
855     
856     [connection release];
857     [delegate release];
858 }
859
860 @end
861 #endif
862
863 void LayoutTestController::authenticateSession(JSStringRef url, JSStringRef username, JSStringRef password)
864 {
865     // See <rdar://problem/7880699>.
866 #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
867     RetainPtr<CFStringRef> urlStringCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, url));
868     RetainPtr<CFStringRef> usernameCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, username));
869     RetainPtr<CFStringRef> passwordCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, password));
870
871     NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:(NSString *)urlStringCF.get()]];
872
873     [SynchronousLoader makeRequest:request withUsername:(NSString *)usernameCF.get() password:(NSString *)passwordCF.get()];
874 #endif
875 }
876
877 void LayoutTestController::setEditingBehavior(const char* editingBehavior)
878 {
879     NSString* editingBehaviorNS = [[NSString alloc] initWithUTF8String:editingBehavior];
880     if ([editingBehaviorNS isEqualToString:@"mac"])
881         [[WebPreferences standardPreferences] setEditingBehavior:WebKitEditingMacBehavior];
882     if ([editingBehaviorNS isEqualToString:@"win"])
883         [[WebPreferences standardPreferences] setEditingBehavior:WebKitEditingWinBehavior];
884     [editingBehaviorNS release];
885 }
886
887 void LayoutTestController::abortModal()
888 {
889     [NSApp abortModal];
890 }