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