1 /* 2 * Copyright (C) 2005, 2006, 2007, 2008 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 "WebDataSource.h" 30 31 #import "WebArchive.h" 32 #import "WebArchiveInternal.h" 33 #import "WebDataSourceInternal.h" 34 #import "WebDocument.h" 35 #import "WebDocumentLoaderMac.h" 36 #import "WebFrameInternal.h" 37 #import "WebFrameLoadDelegate.h" 38 #import "WebFrameLoaderClient.h" 39 #import "WebHTMLRepresentation.h" 40 #import "WebKitErrorsPrivate.h" 41 #import "WebKitLogging.h" 42 #import "WebKitStatisticsPrivate.h" 43 #import "WebKitNSStringExtras.h" 44 #import "WebNSURLExtras.h" 45 #import "WebNSURLRequestExtras.h" 46 #import "WebPDFRepresentation.h" 47 #import "WebResourceInternal.h" 48 #import "WebResourceLoadDelegate.h" 49 #import "WebViewInternal.h" 50 #import <WebCore/ApplicationCacheStorage.h> 51 #import <WebCore/FrameLoader.h> 52 #import <WebCore/KURL.h> 53 #import <WebCore/LegacyWebArchive.h> 54 #import <WebCore/MIMETypeRegistry.h> 55 #import <WebCore/ResourceRequest.h> 56 #import <WebCore/SharedBuffer.h> 57 #import <WebCore/WebCoreObjCExtras.h> 58 #import <WebCore/WebCoreURLResponse.h> 59 #import <WebKit/DOMHTML.h> 60 #import <WebKit/DOMPrivate.h> 61 #import <runtime/InitializeThreading.h> 62 #import <wtf/Assertions.h> 63 #import <wtf/Threading.h> 64 65 using namespace WebCore; 66 67 @interface WebDataSourcePrivate : NSObject { 68 @public 69 WebDocumentLoaderMac* loader; 70 71 id <WebDocumentRepresentation> representation; 72 73 BOOL representationFinishedLoading; 74 BOOL includedInWebKitStatistics; 75 } 76 @end 77 78 @implementation WebDataSourcePrivate 79 80 + (void)initialize 81 { 82 JSC::initializeThreading(); 83 WTF::initializeMainThreadToProcessMainThread(); 84 #ifndef BUILDING_ON_TIGER 85 WebCoreObjCFinalizeOnMainThread(self); 86 #endif 87 } 88 89 - (void)dealloc 90 { 91 if (WebCoreObjCScheduleDeallocateOnMainThread([WebDataSourcePrivate class], self)) 92 return; 93 94 ASSERT(loader); 95 if (loader) { 96 ASSERT(!loader->isLoading()); 97 loader->detachDataSource(); 98 loader->deref(); 99 } 100 101 [representation release]; 102 103 [super dealloc]; 104 } 105 106 - (void)finalize 107 { 108 ASSERT_MAIN_THREAD(); 109 110 ASSERT(loader); 111 if (loader) { 112 ASSERT(!loader->isLoading()); 113 loader->detachDataSource(); 114 loader->deref(); 115 } 116 117 [super finalize]; 118 } 119 120 @end 121 122 @interface WebDataSource (WebFileInternal) 123 @end 124 125 @implementation WebDataSource (WebFileInternal) 126 127 - (void)_setRepresentation:(id<WebDocumentRepresentation>)representation 128 { 129 [_private->representation release]; 130 _private->representation = [representation retain]; 131 _private->representationFinishedLoading = NO; 132 } 133 134 static inline void addTypesFromClass(NSMutableDictionary *allTypes, Class objCClass, NSArray *supportTypes) 135 { 136 NSEnumerator *enumerator = [supportTypes objectEnumerator]; 137 ASSERT(enumerator != nil); 138 NSString *mime = nil; 139 while ((mime = [enumerator nextObject]) != nil) { 140 // Don't clobber previously-registered classes. 141 if ([allTypes objectForKey:mime] == nil) 142 [allTypes setObject:objCClass forKey:mime]; 143 } 144 } 145 146 + (Class)_representationClassForMIMEType:(NSString *)MIMEType allowingPlugins:(BOOL)allowPlugins 147 { 148 Class repClass; 149 return [WebView _viewClass:nil andRepresentationClass:&repClass forMIMEType:MIMEType allowingPlugins:allowPlugins] ? repClass : nil; 150 } 151 @end 152 153 @implementation WebDataSource (WebPrivate) 154 155 - (NSError *)_mainDocumentError 156 { 157 return _private->loader->mainDocumentError(); 158 } 159 160 - (void)_addSubframeArchives:(NSArray *)subframeArchives 161 { 162 // FIXME: This SPI is poor, poor design. Can we come up with another solution for those who need it? 163 DocumentLoader* loader = [self _documentLoader]; 164 ASSERT(loader); 165 166 NSEnumerator *enumerator = [subframeArchives objectEnumerator]; 167 WebArchive *archive; 168 while ((archive = [enumerator nextObject]) != nil) 169 loader->addAllArchiveResources([archive _coreLegacyWebArchive]); 170 } 171 172 - (NSFileWrapper *)_fileWrapperForURL:(NSURL *)URL 173 { 174 if ([URL isFileURL]) { 175 NSString *path = [[URL path] stringByResolvingSymlinksInPath]; 176 return [[[NSFileWrapper alloc] initWithPath:path] autorelease]; 177 } 178 179 WebResource *resource = [self subresourceForURL:URL]; 180 if (resource) 181 return [resource _fileWrapperRepresentation]; 182 183 NSCachedURLResponse *cachedResponse = [[self _webView] _cachedResponseForURL:URL]; 184 if (cachedResponse) { 185 NSFileWrapper *wrapper = [[[NSFileWrapper alloc] initRegularFileWithContents:[cachedResponse data]] autorelease]; 186 [wrapper setPreferredFilename:[[cachedResponse response] suggestedFilename]]; 187 return wrapper; 188 } 189 190 return nil; 191 } 192 193 - (NSString *)_responseMIMEType 194 { 195 return [[self response] MIMEType]; 196 } 197 198 - (BOOL)_transferApplicationCache:(NSString*)destinationBundleIdentifier 199 { 200 #if ENABLE(OFFLINE_WEB_APPLICATIONS) 201 DocumentLoader* loader = [self _documentLoader]; 202 203 if (!loader) 204 return NO; 205 206 NSString *cacheDir = [NSString _webkit_localCacheDirectoryWithBundleIdentifier:destinationBundleIdentifier]; 207 208 return ApplicationCacheStorage::storeCopyOfCache(cacheDir, loader->applicationCacheHost()); 209 #else 210 return NO; 211 #endif 212 } 213 214 - (void)_setDeferMainResourceDataLoad:(BOOL)flag 215 { 216 DocumentLoader* loader = [self _documentLoader]; 217 218 if (!loader) 219 return; 220 221 loader->setDeferMainResourceDataLoad(flag); 222 } 223 224 @end 225 226 @implementation WebDataSource (WebInternal) 227 228 - (void)_finishedLoading 229 { 230 _private->representationFinishedLoading = YES; 231 [[self representation] finishedLoadingWithDataSource:self]; 232 } 233 234 - (void)_receivedData:(NSData *)data 235 { 236 // protect self temporarily, as the bridge receivedData call could remove our last ref 237 RetainPtr<WebDataSource*> protect(self); 238 239 [[self representation] receivedData:data withDataSource:self]; 240 241 if ([[self _webView] _usesDocumentViews]) 242 [[[[self webFrame] frameView] documentView] dataSourceUpdated:self]; 243 } 244 245 - (void)_setMainDocumentError:(NSError *)error 246 { 247 if (!_private->representationFinishedLoading) { 248 _private->representationFinishedLoading = YES; 249 [[self representation] receivedError:error withDataSource:self]; 250 } 251 } 252 253 - (void)_revertToProvisionalState 254 { 255 [self _setRepresentation:nil]; 256 } 257 258 + (NSMutableDictionary *)_repTypesAllowImageTypeOmission:(BOOL)allowImageTypeOmission 259 { 260 static NSMutableDictionary *repTypes = nil; 261 static BOOL addedImageTypes = NO; 262 263 if (!repTypes) { 264 repTypes = [[NSMutableDictionary alloc] init]; 265 addTypesFromClass(repTypes, [WebHTMLRepresentation class], [WebHTMLRepresentation supportedNonImageMIMETypes]); 266 267 // Since this is a "secret default" we don't both registering it. 268 BOOL omitPDFSupport = [[NSUserDefaults standardUserDefaults] boolForKey:@"WebKitOmitPDFSupport"]; 269 if (!omitPDFSupport) 270 addTypesFromClass(repTypes, [WebPDFRepresentation class], [WebPDFRepresentation supportedMIMETypes]); 271 } 272 273 if (!addedImageTypes && !allowImageTypeOmission) { 274 addTypesFromClass(repTypes, [WebHTMLRepresentation class], [WebHTMLRepresentation supportedImageMIMETypes]); 275 addedImageTypes = YES; 276 } 277 278 return repTypes; 279 } 280 281 - (void)_replaceSelectionWithArchive:(WebArchive *)archive selectReplacement:(BOOL)selectReplacement 282 { 283 DOMDocumentFragment *fragment = [self _documentFragmentWithArchive:archive]; 284 if (fragment) 285 [[self webFrame] _replaceSelectionWithFragment:fragment selectReplacement:selectReplacement smartReplace:NO matchStyle:NO]; 286 } 287 288 // FIXME: There are few reasons why this method and many of its related methods can't be pushed entirely into WebCore in the future. 289 - (DOMDocumentFragment *)_documentFragmentWithArchive:(WebArchive *)archive 290 { 291 ASSERT(archive); 292 WebResource *mainResource = [archive mainResource]; 293 if (mainResource) { 294 NSString *MIMEType = [mainResource MIMEType]; 295 if ([WebView canShowMIMETypeAsHTML:MIMEType]) { 296 NSString *markupString = [[NSString alloc] initWithData:[mainResource data] encoding:NSUTF8StringEncoding]; 297 // FIXME: seems poor form to do this as a side effect of getting a document fragment 298 if (DocumentLoader* loader = [self _documentLoader]) 299 loader->addAllArchiveResources([archive _coreLegacyWebArchive]); 300 301 DOMDocumentFragment *fragment = [[self webFrame] _documentFragmentWithMarkupString:markupString baseURLString:[[mainResource URL] _web_originalDataAsString]]; 302 [markupString release]; 303 return fragment; 304 } else if (MIMETypeRegistry::isSupportedImageMIMEType(MIMEType)) { 305 return [self _documentFragmentWithImageResource:mainResource]; 306 307 } 308 } 309 return nil; 310 } 311 312 - (DOMDocumentFragment *)_documentFragmentWithImageResource:(WebResource *)resource 313 { 314 DOMElement *imageElement = [self _imageElementWithImageResource:resource]; 315 if (!imageElement) 316 return 0; 317 DOMDocumentFragment *fragment = [[[self webFrame] DOMDocument] createDocumentFragment]; 318 [fragment appendChild:imageElement]; 319 return fragment; 320 } 321 322 - (DOMElement *)_imageElementWithImageResource:(WebResource *)resource 323 { 324 if (!resource) 325 return 0; 326 327 [self addSubresource:resource]; 328 329 DOMElement *imageElement = [[[self webFrame] DOMDocument] createElement:@"img"]; 330 331 // FIXME: calling _web_originalDataAsString on a file URL returns an absolute path. Workaround this. 332 NSURL *URL = [resource URL]; 333 [imageElement setAttribute:@"src" value:[URL isFileURL] ? [URL absoluteString] : [URL _web_originalDataAsString]]; 334 335 return imageElement; 336 } 337 338 // May return nil if not initialized with a URL. 339 - (NSURL *)_URL 340 { 341 const KURL& url = _private->loader->url(); 342 if (url.isEmpty()) 343 return nil; 344 return url; 345 } 346 347 - (WebView *)_webView 348 { 349 return [[self webFrame] webView]; 350 } 351 352 - (BOOL)_isDocumentHTML 353 { 354 NSString *MIMEType = [self _responseMIMEType]; 355 return [WebView canShowMIMETypeAsHTML:MIMEType]; 356 } 357 358 - (void)_makeRepresentation 359 { 360 Class repClass = [[self class] _representationClassForMIMEType:[self _responseMIMEType] allowingPlugins:[[[self _webView] preferences] arePlugInsEnabled]]; 361 362 // Check if the data source was already bound? 363 if (![[self representation] isKindOfClass:repClass]) { 364 id newRep = repClass != nil ? [[repClass alloc] init] : nil; 365 [self _setRepresentation:(id <WebDocumentRepresentation>)newRep]; 366 [newRep release]; 367 } 368 369 [_private->representation setDataSource:self]; 370 } 371 372 - (DocumentLoader*)_documentLoader 373 { 374 return _private->loader; 375 } 376 377 - (id)_initWithDocumentLoader:(PassRefPtr<WebDocumentLoaderMac>)loader 378 { 379 self = [super init]; 380 if (!self) 381 return nil; 382 383 _private = [[WebDataSourcePrivate alloc] init]; 384 385 _private->loader = loader.releaseRef(); 386 387 LOG(Loading, "creating datasource for %@", static_cast<NSURL *>(_private->loader->request().url())); 388 389 if ((_private->includedInWebKitStatistics = [[self webFrame] _isIncludedInWebKitStatistics])) 390 ++WebDataSourceCount; 391 392 return self; 393 } 394 395 @end 396 397 @implementation WebDataSource 398 399 - (id)initWithRequest:(NSURLRequest *)request 400 { 401 return [self _initWithDocumentLoader:WebDocumentLoaderMac::create(request, SubstituteData())]; 402 } 403 404 - (void)dealloc 405 { 406 if (_private && _private->includedInWebKitStatistics) 407 --WebDataSourceCount; 408 409 [_private release]; 410 411 [super dealloc]; 412 } 413 414 - (void)finalize 415 { 416 if (_private && _private->includedInWebKitStatistics) 417 --WebDataSourceCount; 418 419 [super finalize]; 420 } 421 422 - (NSData *)data 423 { 424 RefPtr<SharedBuffer> mainResourceData = _private->loader->mainResourceData(); 425 if (!mainResourceData) 426 return nil; 427 return [mainResourceData->createNSData() autorelease]; 428 } 429 430 - (id <WebDocumentRepresentation>)representation 431 { 432 return _private->representation; 433 } 434 435 - (WebFrame *)webFrame 436 { 437 FrameLoader* frameLoader = _private->loader->frameLoader(); 438 if (!frameLoader) 439 return nil; 440 return static_cast<WebFrameLoaderClient*>(frameLoader->client())->webFrame(); 441 } 442 443 - (NSURLRequest *)initialRequest 444 { 445 return _private->loader->originalRequest().nsURLRequest(); 446 } 447 448 - (NSMutableURLRequest *)request 449 { 450 FrameLoader* frameLoader = _private->loader->frameLoader(); 451 if (!frameLoader || !frameLoader->frameHasLoaded()) 452 return nil; 453 454 // FIXME: this cast is dubious 455 return (NSMutableURLRequest *)_private->loader->request().nsURLRequest(); 456 } 457 458 - (NSURLResponse *)response 459 { 460 return _private->loader->response().nsURLResponse(); 461 } 462 463 - (NSString *)textEncodingName 464 { 465 NSString *textEncodingName = _private->loader->overrideEncoding(); 466 if (!textEncodingName) 467 textEncodingName = [[self response] textEncodingName]; 468 return textEncodingName; 469 } 470 471 - (BOOL)isLoading 472 { 473 return _private->loader->isLoadingInAPISense(); 474 } 475 476 // Returns nil or the page title. 477 - (NSString *)pageTitle 478 { 479 return [[self representation] title]; 480 } 481 482 - (NSURL *)unreachableURL 483 { 484 const KURL& unreachableURL = _private->loader->unreachableURL(); 485 if (unreachableURL.isEmpty()) 486 return nil; 487 return unreachableURL; 488 } 489 490 - (WebArchive *)webArchive 491 { 492 // it makes no sense to grab a WebArchive from an uncommitted document. 493 if (!_private->loader->isCommitted()) 494 return nil; 495 496 return [[[WebArchive alloc] _initWithCoreLegacyWebArchive:LegacyWebArchive::create(core([self webFrame]))] autorelease]; 497 } 498 499 - (WebResource *)mainResource 500 { 501 RefPtr<ArchiveResource> coreResource = _private->loader->mainResource(); 502 return [[[WebResource alloc] _initWithCoreResource:coreResource.release()] autorelease]; 503 } 504 505 - (NSArray *)subresources 506 { 507 Vector<PassRefPtr<ArchiveResource> > coreSubresources; 508 _private->loader->getSubresources(coreSubresources); 509 510 NSMutableArray *subresources = [[NSMutableArray alloc] initWithCapacity:coreSubresources.size()]; 511 for (unsigned i = 0; i < coreSubresources.size(); ++i) { 512 WebResource *resource = [[WebResource alloc] _initWithCoreResource:coreSubresources[i]]; 513 if (resource) { 514 [subresources addObject:resource]; 515 [resource release]; 516 } 517 } 518 519 return [subresources autorelease]; 520 } 521 522 - (WebResource *)subresourceForURL:(NSURL *)URL 523 { 524 RefPtr<ArchiveResource> subresource = _private->loader->subresource(URL); 525 526 return subresource ? [[[WebResource alloc] _initWithCoreResource:subresource.get()] autorelease] : nil; 527 } 528 529 - (void)addSubresource:(WebResource *)subresource 530 { 531 _private->loader->addArchiveResource([subresource _coreResource]); 532 } 533 534 @end 535