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