• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "PolicyDelegate.h"
35#import "WorkQueue.h"
36#import "WorkQueueItem.h"
37#import <Foundation/Foundation.h>
38#import <JavaScriptCore/JSRetainPtr.h>
39#import <JavaScriptCore/JSStringRef.h>
40#import <JavaScriptCore/JSStringRefCF.h>
41#import <WebKit/DOMDocument.h>
42#import <WebKit/DOMElement.h>
43#import <WebKit/WebApplicationCache.h>
44#import <WebKit/WebBackForwardList.h>
45#import <WebKit/WebDatabaseManagerPrivate.h>
46#import <WebKit/WebDataSource.h>
47#import <WebKit/WebFrame.h>
48#import <WebKit/WebFrameViewPrivate.h>
49#import <WebKit/WebIconDatabasePrivate.h>
50#import <WebKit/WebHTMLRepresentation.h>
51#import <WebKit/WebHTMLViewPrivate.h>
52#import <WebKit/WebHistory.h>
53#import <WebKit/WebHistoryPrivate.h>
54#import <WebKit/WebInspector.h>
55#import <WebKit/WebNSURLExtras.h>
56#import <WebKit/WebPreferences.h>
57#import <WebKit/WebPreferencesPrivate.h>
58#import <WebKit/WebSecurityOriginPrivate.h>
59#import <WebKit/WebTypesInternal.h>
60#import <WebKit/WebView.h>
61#import <WebKit/WebViewPrivate.h>
62#import <wtf/RetainPtr.h>
63
64@interface CommandValidationTarget : NSObject <NSValidatedUserInterfaceItem>
65{
66    SEL _action;
67}
68- (id)initWithAction:(SEL)action;
69@end
70
71@implementation CommandValidationTarget
72
73- (id)initWithAction:(SEL)action
74{
75    self = [super init];
76    if (!self)
77        return nil;
78
79    _action = action;
80    return self;
81}
82
83- (SEL)action
84{
85    return _action;
86}
87
88- (NSInteger)tag
89{
90    return 0;
91}
92
93@end
94
95LayoutTestController::~LayoutTestController()
96{
97}
98
99void LayoutTestController::addDisallowedURL(JSStringRef url)
100{
101    RetainPtr<CFStringRef> urlCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, url));
102
103    if (!disallowedURLs)
104        disallowedURLs = CFSetCreateMutable(kCFAllocatorDefault, 0, NULL);
105
106    // Canonicalize the URL
107    NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:(NSString *)urlCF.get()]];
108    request = [NSURLProtocol canonicalRequestForRequest:request];
109
110    CFSetAddValue(disallowedURLs, [request URL]);
111}
112
113void LayoutTestController::clearAllDatabases()
114{
115    [[WebDatabaseManager sharedWebDatabaseManager] deleteAllDatabases];
116}
117
118void LayoutTestController::clearBackForwardList()
119{
120    WebBackForwardList *backForwardList = [[mainFrame webView] backForwardList];
121    WebHistoryItem *item = [[backForwardList currentItem] retain];
122
123    // We clear the history by setting the back/forward list's capacity to 0
124    // then restoring it back and adding back the current item.
125    int capacity = [backForwardList capacity];
126    [backForwardList setCapacity:0];
127    [backForwardList setCapacity:capacity];
128    [backForwardList addItem:item];
129    [backForwardList goToItem:item];
130    [item release];
131}
132
133JSStringRef LayoutTestController::copyDecodedHostName(JSStringRef name)
134{
135    RetainPtr<CFStringRef> nameCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, name));
136    NSString *nameNS = (NSString *)nameCF.get();
137    return JSStringCreateWithCFString((CFStringRef)[nameNS _web_decodeHostName]);
138}
139
140JSStringRef LayoutTestController::copyEncodedHostName(JSStringRef name)
141{
142    RetainPtr<CFStringRef> nameCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, name));
143    NSString *nameNS = (NSString *)nameCF.get();
144    return JSStringCreateWithCFString((CFStringRef)[nameNS _web_encodeHostName]);
145}
146
147void LayoutTestController::display()
148{
149    displayWebView();
150}
151
152void LayoutTestController::keepWebHistory()
153{
154    if (![WebHistory optionalSharedHistory]) {
155        WebHistory *history = [[WebHistory alloc] init];
156        [WebHistory setOptionalSharedHistory:history];
157        [history release];
158    }
159}
160
161size_t LayoutTestController::webHistoryItemCount()
162{
163    return [[[WebHistory optionalSharedHistory] allItems] count];
164}
165
166void LayoutTestController::notifyDone()
167{
168    if (m_waitToDump && !topLoadingFrame && !WorkQueue::shared()->count())
169        dump();
170    m_waitToDump = false;
171}
172
173JSStringRef LayoutTestController::pathToLocalResource(JSContextRef context, JSStringRef url)
174{
175    return JSStringRetain(url); // Do nothing on mac.
176}
177
178void LayoutTestController::queueLoad(JSStringRef url, JSStringRef target)
179{
180    RetainPtr<CFStringRef> urlCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, url));
181    NSString *urlNS = (NSString *)urlCF.get();
182
183    NSURL *nsurl = [NSURL URLWithString:urlNS relativeToURL:[[[mainFrame dataSource] response] URL]];
184    NSString* nsurlString = [nsurl absoluteString];
185
186    JSRetainPtr<JSStringRef> absoluteURL(Adopt, JSStringCreateWithUTF8CString([nsurlString UTF8String]));
187    WorkQueue::shared()->queue(new LoadItem(absoluteURL.get(), target));
188}
189
190void LayoutTestController::setAcceptsEditing(bool newAcceptsEditing)
191{
192    [(EditingDelegate *)[[mainFrame webView] editingDelegate] setAcceptsEditing:newAcceptsEditing];
193}
194
195void LayoutTestController::setAppCacheMaximumSize(unsigned long long size)
196{
197    [WebApplicationCache setMaximumSize:size];
198}
199
200void LayoutTestController::setAuthorAndUserStylesEnabled(bool flag)
201{
202    [[[mainFrame webView] preferences] setAuthorAndUserStylesEnabled:flag];
203}
204
205void LayoutTestController::setCustomPolicyDelegate(bool setDelegate, bool permissive)
206{
207    if (setDelegate) {
208        [policyDelegate setPermissive:permissive];
209        [[mainFrame webView] setPolicyDelegate:policyDelegate];
210    } else
211        [[mainFrame webView] setPolicyDelegate:nil];
212}
213
214void LayoutTestController::setDatabaseQuota(unsigned long long quota)
215{
216    WebSecurityOrigin *origin = [[WebSecurityOrigin alloc] initWithURL:[NSURL URLWithString:@"file:///"]];
217    [origin setQuota:quota];
218    [origin release];
219}
220
221void LayoutTestController::setIconDatabaseEnabled(bool iconDatabaseEnabled)
222{
223    // FIXME: Workaround <rdar://problem/6480108>
224    static WebIconDatabase* sharedWebIconDatabase = NULL;
225    if (!sharedWebIconDatabase) {
226        if (!iconDatabaseEnabled)
227            return;
228        sharedWebIconDatabase = [WebIconDatabase sharedIconDatabase];
229        if ([sharedWebIconDatabase isEnabled] == iconDatabaseEnabled)
230            return;
231    }
232    [sharedWebIconDatabase setEnabled:iconDatabaseEnabled];
233}
234
235void LayoutTestController::setJavaScriptProfilingEnabled(bool profilingEnabled)
236{
237    [[[mainFrame webView] preferences] setDeveloperExtrasEnabled:profilingEnabled];
238    [[[mainFrame webView] inspector] setJavaScriptProfilingEnabled:profilingEnabled];
239}
240
241void LayoutTestController::setMainFrameIsFirstResponder(bool flag)
242{
243    NSView *documentView = [[mainFrame frameView] documentView];
244
245    NSResponder *firstResponder = flag ? documentView : nil;
246    [[[mainFrame webView] window] makeFirstResponder:firstResponder];
247}
248
249void LayoutTestController::setPrivateBrowsingEnabled(bool privateBrowsingEnabled)
250{
251    [[[mainFrame webView] preferences] setPrivateBrowsingEnabled:privateBrowsingEnabled];
252}
253
254void LayoutTestController::setXSSAuditorEnabled(bool enabled)
255{
256    [[[mainFrame webView] preferences] setXSSAuditorEnabled:enabled];
257}
258
259void LayoutTestController::setPopupBlockingEnabled(bool popupBlockingEnabled)
260{
261    [[[mainFrame webView] preferences] setJavaScriptCanOpenWindowsAutomatically:!popupBlockingEnabled];
262}
263
264void LayoutTestController::setTabKeyCyclesThroughElements(bool cycles)
265{
266    [[mainFrame webView] setTabKeyCyclesThroughElements:cycles];
267}
268
269void LayoutTestController::setUseDashboardCompatibilityMode(bool flag)
270{
271    [[mainFrame webView] _setDashboardBehavior:WebDashboardBehaviorUseBackwardCompatibilityMode to:flag];
272}
273
274void LayoutTestController::setUserStyleSheetEnabled(bool flag)
275{
276    [[WebPreferences standardPreferences] setUserStyleSheetEnabled:flag];
277}
278
279void LayoutTestController::setUserStyleSheetLocation(JSStringRef path)
280{
281    RetainPtr<CFStringRef> pathCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, path));
282    NSURL *url = [NSURL URLWithString:(NSString *)pathCF.get()];
283    [[WebPreferences standardPreferences] setUserStyleSheetLocation:url];
284}
285
286void LayoutTestController::disableImageLoading()
287{
288    [[WebPreferences standardPreferences] setLoadsImagesAutomatically:NO];
289}
290
291void LayoutTestController::dispatchPendingLoadRequests()
292{
293    [[mainFrame webView] _dispatchPendingLoadRequests];
294}
295
296void LayoutTestController::setPersistentUserStyleSheetLocation(JSStringRef jsURL)
297{
298    RetainPtr<CFStringRef> urlString(AdoptCF, JSStringCopyCFString(0, jsURL));
299    ::setPersistentUserStyleSheetLocation(urlString.get());
300}
301
302void LayoutTestController::clearPersistentUserStyleSheet()
303{
304    ::setPersistentUserStyleSheetLocation(0);
305}
306
307void LayoutTestController::setWindowIsKey(bool windowIsKey)
308{
309    m_windowIsKey = windowIsKey;
310    [[mainFrame webView] _updateActiveState];
311}
312
313void LayoutTestController::setSmartInsertDeleteEnabled(bool flag)
314{
315    [[mainFrame webView] setSmartInsertDeleteEnabled:flag];
316}
317
318void LayoutTestController::setSelectTrailingWhitespaceEnabled(bool flag)
319{
320    [[mainFrame webView] setSelectTrailingWhitespaceEnabled:flag];
321}
322
323static const CFTimeInterval waitToDumpWatchdogInterval = 10.0;
324
325static void waitUntilDoneWatchdogFired(CFRunLoopTimerRef timer, void* info)
326{
327    const char* message = "FAIL: Timed out waiting for notifyDone to be called\n";
328    fprintf(stderr, "%s", message);
329    fprintf(stdout, "%s", message);
330    dump();
331}
332
333void LayoutTestController::setWaitToDump(bool waitUntilDone)
334{
335    m_waitToDump = waitUntilDone;
336    if (m_waitToDump && !waitToDumpWatchdog) {
337        waitToDumpWatchdog = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + waitToDumpWatchdogInterval, 0, 0, 0, waitUntilDoneWatchdogFired, NULL);
338        CFRunLoopAddTimer(CFRunLoopGetCurrent(), waitToDumpWatchdog, kCFRunLoopCommonModes);
339    }
340}
341
342int LayoutTestController::windowCount()
343{
344    return CFArrayGetCount(openWindowsRef);
345}
346
347bool LayoutTestController::elementDoesAutoCompleteForElementWithId(JSStringRef id)
348{
349    RetainPtr<CFStringRef> idCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, id));
350    NSString *idNS = (NSString *)idCF.get();
351
352    DOMElement *element = [[mainFrame DOMDocument] getElementById:idNS];
353    id rep = [[mainFrame dataSource] representation];
354
355    if ([rep class] == [WebHTMLRepresentation class])
356        return [(WebHTMLRepresentation *)rep elementDoesAutoComplete:element];
357
358    return false;
359}
360
361void LayoutTestController::execCommand(JSStringRef name, JSStringRef value)
362{
363    RetainPtr<CFStringRef> nameCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, name));
364    NSString *nameNS = (NSString *)nameCF.get();
365
366    RetainPtr<CFStringRef> valueCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, value));
367    NSString *valueNS = (NSString *)valueCF.get();
368
369    [[mainFrame webView] _executeCoreCommandByName:nameNS value:valueNS];
370}
371
372void LayoutTestController::setCacheModel(int cacheModel)
373{
374    [[WebPreferences standardPreferences] setCacheModel:cacheModel];
375}
376
377bool LayoutTestController::isCommandEnabled(JSStringRef name)
378{
379    RetainPtr<CFStringRef> nameCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, name));
380    NSString *nameNS = reinterpret_cast<const NSString *>(nameCF.get());
381
382    // Accept command strings with capital letters for first letter without trailing colon.
383    if (![nameNS hasSuffix:@":"] && [nameNS length]) {
384        nameNS = [[[[nameNS substringToIndex:1] lowercaseString]
385            stringByAppendingString:[nameNS substringFromIndex:1]]
386            stringByAppendingString:@":"];
387    }
388
389    SEL selector = NSSelectorFromString(nameNS);
390    RetainPtr<CommandValidationTarget> target(AdoptNS, [[CommandValidationTarget alloc] initWithAction:selector]);
391    id validator = [NSApp targetForAction:selector to:[mainFrame webView] from:target.get()];
392    if (!validator)
393        return false;
394    if (![validator respondsToSelector:selector])
395        return false;
396    if (![validator respondsToSelector:@selector(validateUserInterfaceItem:)])
397        return true;
398    return [validator validateUserInterfaceItem:target.get()];
399}
400
401bool LayoutTestController::pauseAnimationAtTimeOnElementWithId(JSStringRef animationName, double time, JSStringRef elementId)
402{
403    RetainPtr<CFStringRef> idCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, elementId));
404    NSString *idNS = (NSString *)idCF.get();
405    RetainPtr<CFStringRef> nameCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, animationName));
406    NSString *nameNS = (NSString *)nameCF.get();
407
408    return [mainFrame _pauseAnimation:nameNS onNode:[[mainFrame DOMDocument] getElementById:idNS] atTime:time];
409}
410
411bool LayoutTestController::pauseTransitionAtTimeOnElementWithId(JSStringRef propertyName, double time, JSStringRef elementId)
412{
413    RetainPtr<CFStringRef> idCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, elementId));
414    NSString *idNS = (NSString *)idCF.get();
415    RetainPtr<CFStringRef> nameCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, propertyName));
416    NSString *nameNS = (NSString *)nameCF.get();
417
418    return [mainFrame _pauseTransitionOfProperty:nameNS onNode:[[mainFrame DOMDocument] getElementById:idNS] atTime:time];
419}
420
421unsigned LayoutTestController::numberOfActiveAnimations() const
422{
423    return [mainFrame _numberOfActiveAnimations];
424}
425
426void LayoutTestController::waitForPolicyDelegate()
427{
428    setWaitToDump(true);
429    [policyDelegate setControllerToNotifyDone:this];
430    [[mainFrame webView] setPolicyDelegate:policyDelegate];
431}
432