1/*
2 * Copyright (C) 2007 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 "FrameLoadDelegate.h"
32
33#import "AccessibilityController.h"
34#import "AppleScriptController.h"
35#import "EventSendingController.h"
36#import "GCController.h"
37#import "LayoutTestController.h"
38#import "NavigationController.h"
39#import "ObjCController.h"
40#import "ObjCPlugin.h"
41#import "ObjCPluginFunction.h"
42#import "PlainTextController.h"
43#import "TextInputController.h"
44#import "WorkQueue.h"
45#import "WorkQueueItem.h"
46#import <JavaScriptCore/JavaScriptCore.h>
47#import <WebKit/WebFramePrivate.h>
48#import <WebKit/WebHTMLViewPrivate.h>
49#import <WebKit/WebKit.h>
50#import <WebKit/WebNSURLExtras.h>
51#import <WebKit/WebScriptWorld.h>
52#import <WebKit/WebSecurityOriginPrivate.h>
53#import <WebKit/WebViewPrivate.h>
54#import <wtf/Assertions.h>
55
56@interface NSURL (DRTExtras)
57- (NSString *)_drt_descriptionSuitableForTestResult;
58@end
59
60@interface NSError (DRTExtras)
61- (NSString *)_drt_descriptionSuitableForTestResult;
62@end
63
64@interface NSURLResponse (DRTExtras)
65- (NSString *)_drt_descriptionSuitableForTestResult;
66@end
67
68@interface NSURLRequest (DRTExtras)
69- (NSString *)_drt_descriptionSuitableForTestResult;
70@end
71
72@interface WebFrame (DRTExtras)
73- (NSString *)_drt_descriptionSuitableForTestResult;
74@end
75
76@implementation WebFrame (DRTExtras)
77- (NSString *)_drt_descriptionSuitableForTestResult
78{
79    BOOL isMainFrame = (self == [[self webView] mainFrame]);
80    NSString *name = [self name];
81    if (isMainFrame) {
82        if ([name length])
83            return [NSString stringWithFormat:@"main frame \"%@\"", name];
84        else
85            return @"main frame";
86    } else {
87        if (name)
88            return [NSString stringWithFormat:@"frame \"%@\"", name];
89        else
90            return @"frame (anonymous)";
91    }
92}
93
94- (NSString *)_drt_printFrameUserGestureStatus
95{
96    BOOL isUserGesture = [[self webView] _isProcessingUserGesture];
97    return [NSString stringWithFormat:@"Frame with user gesture \"%@\"", isUserGesture ? @"true" : @"false"];
98}
99@end
100
101@implementation FrameLoadDelegate
102
103- (id)init
104{
105    if ((self = [super init])) {
106        gcController = new GCController;
107        accessibilityController = new AccessibilityController;
108    }
109    return self;
110}
111
112- (void)dealloc
113{
114    delete gcController;
115    delete accessibilityController;
116    [super dealloc];
117}
118
119// Exec messages in the work queue until they're all done, or one of them starts a new load
120- (void)processWork:(id)dummy
121{
122    // if another load started, then wait for it to complete.
123    if (topLoadingFrame)
124        return;
125
126    // if we finish all the commands, we're ready to dump state
127    if (WorkQueue::shared()->processWork() && !gLayoutTestController->waitToDump())
128        dump();
129}
130
131- (void)resetToConsistentState
132{
133    accessibilityController->resetToConsistentState();
134}
135
136- (void)webView:(WebView *)c locationChangeDone:(NSError *)error forDataSource:(WebDataSource *)dataSource
137{
138    if ([dataSource webFrame] == topLoadingFrame) {
139        topLoadingFrame = nil;
140        WorkQueue::shared()->setFrozen(true); // first complete load freezes the queue for the rest of this test
141        if (!gLayoutTestController->waitToDump()) {
142            if (WorkQueue::shared()->count())
143                [self performSelector:@selector(processWork:) withObject:nil afterDelay:0];
144            else
145                dump();
146        }
147    }
148}
149
150- (void)webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame
151{
152    if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) {
153        NSString *string = [NSString stringWithFormat:@"%@ - didStartProvisionalLoadForFrame", [frame _drt_descriptionSuitableForTestResult]];
154        printf ("%s\n", [string UTF8String]);
155    }
156
157    if (!done && gLayoutTestController->dumpUserGestureInFrameLoadCallbacks()) {
158        NSString *string = [NSString stringWithFormat:@"%@ - in didStartProvisionalLoadForFrame", [frame _drt_printFrameUserGestureStatus]];
159        printf ("%s\n", [string UTF8String]);
160    }
161
162    ASSERT([frame provisionalDataSource]);
163    // Make sure we only set this once per test.  If it gets cleared, and then set again, we might
164    // end up doing two dumps for one test.
165    if (!topLoadingFrame && !done)
166        topLoadingFrame = frame;
167
168    if (!done && gLayoutTestController->stopProvisionalFrameLoads()) {
169        NSString *string = [NSString stringWithFormat:@"%@ - stopping load in didStartProvisionalLoadForFrame callback", [frame _drt_descriptionSuitableForTestResult]];
170        printf ("%s\n", [string UTF8String]);
171        [frame stopLoading];
172    }
173}
174
175- (void)webView:(WebView *)sender didCommitLoadForFrame:(WebFrame *)frame
176{
177    if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) {
178        NSString *string = [NSString stringWithFormat:@"%@ - didCommitLoadForFrame", [frame _drt_descriptionSuitableForTestResult]];
179        printf ("%s\n", [string UTF8String]);
180    }
181
182    ASSERT(![frame provisionalDataSource]);
183    ASSERT([frame dataSource]);
184
185    gLayoutTestController->setWindowIsKey(true);
186    NSView *documentView = [[mainFrame frameView] documentView];
187    [[[mainFrame webView] window] makeFirstResponder:documentView];
188}
189
190- (void)webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame
191{
192    if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) {
193        NSString *string = [NSString stringWithFormat:@"%@ - didFailProvisionalLoadWithError", [frame _drt_descriptionSuitableForTestResult]];
194        printf("%s\n", [string UTF8String]);
195    }
196
197    if ([error domain] == NSURLErrorDomain && ([error code] == NSURLErrorServerCertificateHasUnknownRoot || [error code] == NSURLErrorServerCertificateUntrusted)) {
198        // <http://webkit.org/b/31200> In order to prevent extra frame load delegate logging being generated if the first test to use SSL
199        // is set to log frame load delegate calls we ignore SSL certificate errors on localhost and 127.0.0.1 from within dumpRenderTree.
200        // Those are the only hosts that we use SSL with at present.  If we hit this code path then we've found another host that we need
201        // to apply the workaround to.
202        ASSERT_NOT_REACHED();
203        return;
204    }
205
206    ASSERT([frame provisionalDataSource]);
207    [self webView:sender locationChangeDone:error forDataSource:[frame provisionalDataSource]];
208}
209
210- (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame
211{
212    ASSERT([frame dataSource]);
213    ASSERT(frame == [[frame dataSource] webFrame]);
214
215    if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) {
216        NSString *string = [NSString stringWithFormat:@"%@ - didFinishLoadForFrame", [frame _drt_descriptionSuitableForTestResult]];
217        printf ("%s\n", [string UTF8String]);
218    }
219
220    // FIXME: This call to displayIfNeeded can be removed when <rdar://problem/5092361> is fixed.
221    // After that is fixed, we will reenable painting after WebCore is done loading the document,
222    // and this call will no longer be needed.
223    if ([[sender mainFrame] isEqual:frame])
224        [sender displayIfNeeded];
225    [self webView:sender locationChangeDone:nil forDataSource:[frame dataSource]];
226    [gNavigationController webView:sender didFinishLoadForFrame:frame];
227}
228
229- (void)webView:(WebView *)sender didFailLoadWithError:(NSError *)error forFrame:(WebFrame *)frame
230{
231    if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) {
232        NSString *string = [NSString stringWithFormat:@"%@ - didFailLoadWithError", [frame _drt_descriptionSuitableForTestResult]];
233        printf ("%s\n", [string UTF8String]);
234    }
235
236    ASSERT(![frame provisionalDataSource]);
237    ASSERT([frame dataSource]);
238
239    [self webView:sender locationChangeDone:error forDataSource:[frame dataSource]];
240}
241
242- (void)webView:(WebView *)webView windowScriptObjectAvailable:(WebScriptObject *)windowScriptObject
243{
244    if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) {
245        NSString *string = [NSString stringWithFormat:@"?? - windowScriptObjectAvailable"];
246        printf ("%s\n", [string UTF8String]);
247    }
248
249    ASSERT_NOT_REACHED();
250}
251
252- (void)didClearWindowObjectInStandardWorldForFrame:(WebFrame *)frame
253{
254    // Make New-Style LayoutTestController
255    JSContextRef context = [frame globalContext];
256    JSObjectRef globalObject = JSContextGetGlobalObject(context);
257    JSValueRef exception = 0;
258
259    ASSERT(gLayoutTestController);
260    gLayoutTestController->makeWindowObject(context, globalObject, &exception);
261    ASSERT(!exception);
262
263    gcController->makeWindowObject(context, globalObject, &exception);
264    ASSERT(!exception);
265
266    accessibilityController->makeWindowObject(context, globalObject, &exception);
267    ASSERT(!exception);
268
269    // Make Old-Style controllers
270
271    WebView *webView = [frame webView];
272    WebScriptObject *obj = [frame windowObject];
273    AppleScriptController *asc = [[AppleScriptController alloc] initWithWebView:webView];
274    [obj setValue:asc forKey:@"appleScriptController"];
275    [asc release];
276
277    EventSendingController *esc = [[EventSendingController alloc] init];
278    [obj setValue:esc forKey:@"eventSender"];
279    [esc release];
280
281    [obj setValue:gNavigationController forKey:@"navigationController"];
282
283    ObjCController *occ = [[ObjCController alloc] init];
284    [obj setValue:occ forKey:@"objCController"];
285    [occ release];
286
287    ObjCPlugin *plugin = [[ObjCPlugin alloc] init];
288    [obj setValue:plugin forKey:@"objCPlugin"];
289    [plugin release];
290
291    ObjCPluginFunction *pluginFunction = [[ObjCPluginFunction alloc] init];
292    [obj setValue:pluginFunction forKey:@"objCPluginFunction"];
293    [pluginFunction release];
294
295    [obj setValue:[PlainTextController sharedPlainTextController] forKey:@"plainText"];
296
297    TextInputController *tic = [[TextInputController alloc] initWithWebView:webView];
298    [obj setValue:tic forKey:@"textInputController"];
299    [tic release];
300}
301
302- (void)didClearWindowObjectForFrame:(WebFrame *)frame inIsolatedWorld:(WebScriptWorld *)world
303{
304    JSGlobalContextRef ctx = [frame _globalContextForScriptWorld:world];
305    if (!ctx)
306        return;
307
308    JSObjectRef globalObject = JSContextGetGlobalObject(ctx);
309    if (!globalObject)
310        return;
311
312    JSObjectSetProperty(ctx, globalObject, JSRetainPtr<JSStringRef>(Adopt, JSStringCreateWithUTF8CString("__worldID")).get(), JSValueMakeNumber(ctx, worldIDForWorld(world)), kJSPropertyAttributeReadOnly, 0);
313}
314
315- (void)webView:(WebView *)sender didClearWindowObjectForFrame:(WebFrame *)frame inScriptWorld:(WebScriptWorld *)world
316{
317    if (world == [WebScriptWorld standardWorld])
318        [self didClearWindowObjectInStandardWorldForFrame:frame];
319    else
320        [self didClearWindowObjectForFrame:frame inIsolatedWorld:world];
321}
322
323- (void)webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame
324{
325    if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) {
326        NSString *string = [NSString stringWithFormat:@"%@ - didReceiveTitle: %@", [frame _drt_descriptionSuitableForTestResult], title];
327        printf ("%s\n", [string UTF8String]);
328    }
329
330    if (gLayoutTestController->dumpTitleChanges())
331        printf("TITLE CHANGED: %s\n", [title UTF8String]);
332}
333
334- (void)webView:(WebView *)sender didReceiveServerRedirectForProvisionalLoadForFrame:(WebFrame *)frame
335{
336    if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) {
337        NSString *string = [NSString stringWithFormat:@"%@ - didReceiveServerRedirectForProvisionalLoadForFrame", [frame _drt_descriptionSuitableForTestResult]];
338        printf ("%s\n", [string UTF8String]);
339    }
340}
341
342- (void)webView:(WebView *)sender didChangeLocationWithinPageForFrame:(WebFrame *)frame
343{
344    if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) {
345        NSString *string = [NSString stringWithFormat:@"%@ - didChangeLocationWithinPageForFrame", [frame _drt_descriptionSuitableForTestResult]];
346        printf ("%s\n", [string UTF8String]);
347    }
348}
349
350- (void)webView:(WebView *)sender willPerformClientRedirectToURL:(NSURL *)URL delay:(NSTimeInterval)seconds fireDate:(NSDate *)date forFrame:(WebFrame *)frame
351{
352    if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) {
353        NSString *string = [NSString stringWithFormat:@"%@ - willPerformClientRedirectToURL: %@ ", [frame _drt_descriptionSuitableForTestResult], [URL _drt_descriptionSuitableForTestResult]];
354        printf ("%s\n", [string UTF8String]);
355    }
356}
357
358- (void)webView:(WebView *)sender didCancelClientRedirectForFrame:(WebFrame *)frame
359{
360    if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) {
361        NSString *string = [NSString stringWithFormat:@"%@ - didCancelClientRedirectForFrame", [frame _drt_descriptionSuitableForTestResult]];
362        printf ("%s\n", [string UTF8String]);
363    }
364}
365
366- (void)webView:(WebView *)sender didFinishDocumentLoadForFrame:(WebFrame *)frame
367{
368    if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) {
369        NSString *string = [NSString stringWithFormat:@"%@ - didFinishDocumentLoadForFrame", [frame _drt_descriptionSuitableForTestResult]];
370        printf ("%s\n", [string UTF8String]);
371    } else if (!done) {
372        unsigned pendingFrameUnloadEvents = [frame _pendingFrameUnloadEventCount];
373        if (pendingFrameUnloadEvents) {
374            NSString *string = [NSString stringWithFormat:@"%@ - has %u onunload handler(s)", [frame _drt_descriptionSuitableForTestResult], pendingFrameUnloadEvents];
375            printf ("%s\n", [string UTF8String]);
376        }
377    }
378}
379
380- (void)webView:(WebView *)sender didHandleOnloadEventsForFrame:(WebFrame *)frame
381{
382    if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) {
383        NSString *string = [NSString stringWithFormat:@"%@ - didHandleOnloadEventsForFrame", [frame _drt_descriptionSuitableForTestResult]];
384        printf ("%s\n", [string UTF8String]);
385    }
386}
387
388- (void)webViewDidDisplayInsecureContent:(WebView *)sender
389{
390    if (!done && gLayoutTestController->dumpFrameLoadCallbacks())
391        printf ("didDisplayInsecureContent\n");
392}
393
394- (void)webView:(WebView *)sender didRunInsecureContent:(WebSecurityOrigin *)origin
395{
396    if (!done && gLayoutTestController->dumpFrameLoadCallbacks())
397        printf ("didRunInsecureContent\n");
398}
399
400@end
401