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