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 65using 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 134static 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