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 allowingPlugins:(BOOL)allowPlugins 144{ 145 Class repClass; 146 return [WebView _viewClass:nil andRepresentationClass:&repClass forMIMEType:MIMEType allowingPlugins:allowPlugins] ? 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#if ENABLE(OFFLINE_WEB_APPLICATIONS) 198 DocumentLoader* loader = [self _documentLoader]; 199 200 if (!loader) 201 return NO; 202 203 NSString *cacheDir = [NSString _webkit_localCacheDirectoryWithBundleIdentifier:destinationBundleIdentifier]; 204 205 return ApplicationCacheStorage::storeCopyOfCache(cacheDir, loader->applicationCacheHost()); 206#else 207 return NO; 208#endif 209} 210 211@end 212 213@implementation WebDataSource (WebInternal) 214 215- (void)_finishedLoading 216{ 217 _private->representationFinishedLoading = YES; 218 [[self representation] finishedLoadingWithDataSource:self]; 219} 220 221- (void)_receivedData:(NSData *)data 222{ 223 // protect self temporarily, as the bridge receivedData call could remove our last ref 224 RetainPtr<WebDataSource*> protect(self); 225 226 [[self representation] receivedData:data withDataSource:self]; 227 228 if ([[self _webView] _usesDocumentViews]) 229 [[[[self webFrame] frameView] documentView] dataSourceUpdated:self]; 230} 231 232- (void)_setMainDocumentError:(NSError *)error 233{ 234 if (!_private->representationFinishedLoading) { 235 _private->representationFinishedLoading = YES; 236 [[self representation] receivedError:error withDataSource:self]; 237 } 238} 239 240- (void)_revertToProvisionalState 241{ 242 [self _setRepresentation:nil]; 243} 244 245+ (NSMutableDictionary *)_repTypesAllowImageTypeOmission:(BOOL)allowImageTypeOmission 246{ 247 static NSMutableDictionary *repTypes = nil; 248 static BOOL addedImageTypes = NO; 249 250 if (!repTypes) { 251 repTypes = [[NSMutableDictionary alloc] init]; 252 addTypesFromClass(repTypes, [WebHTMLRepresentation class], [WebHTMLRepresentation supportedNonImageMIMETypes]); 253 254 // Since this is a "secret default" we don't both registering it. 255 BOOL omitPDFSupport = [[NSUserDefaults standardUserDefaults] boolForKey:@"WebKitOmitPDFSupport"]; 256 if (!omitPDFSupport) 257 addTypesFromClass(repTypes, [WebPDFRepresentation class], [WebPDFRepresentation supportedMIMETypes]); 258 } 259 260 if (!addedImageTypes && !allowImageTypeOmission) { 261 addTypesFromClass(repTypes, [WebHTMLRepresentation class], [WebHTMLRepresentation supportedImageMIMETypes]); 262 addedImageTypes = YES; 263 } 264 265 return repTypes; 266} 267 268- (void)_replaceSelectionWithArchive:(WebArchive *)archive selectReplacement:(BOOL)selectReplacement 269{ 270 DOMDocumentFragment *fragment = [self _documentFragmentWithArchive:archive]; 271 if (fragment) 272 [[self webFrame] _replaceSelectionWithFragment:fragment selectReplacement:selectReplacement smartReplace:NO matchStyle:NO]; 273} 274 275// FIXME: There are few reasons why this method and many of its related methods can't be pushed entirely into WebCore in the future. 276- (DOMDocumentFragment *)_documentFragmentWithArchive:(WebArchive *)archive 277{ 278 ASSERT(archive); 279 WebResource *mainResource = [archive mainResource]; 280 if (mainResource) { 281 NSString *MIMEType = [mainResource MIMEType]; 282 if ([WebView canShowMIMETypeAsHTML:MIMEType]) { 283 NSString *markupString = [[NSString alloc] initWithData:[mainResource data] encoding:NSUTF8StringEncoding]; 284 // FIXME: seems poor form to do this as a side effect of getting a document fragment 285 if (DocumentLoader* loader = [self _documentLoader]) 286 loader->addAllArchiveResources([archive _coreLegacyWebArchive]); 287 288 DOMDocumentFragment *fragment = [[self webFrame] _documentFragmentWithMarkupString:markupString baseURLString:[[mainResource URL] _web_originalDataAsString]]; 289 [markupString release]; 290 return fragment; 291 } else if (MIMETypeRegistry::isSupportedImageMIMEType(MIMEType)) { 292 return [self _documentFragmentWithImageResource:mainResource]; 293 294 } 295 } 296 return nil; 297} 298 299- (DOMDocumentFragment *)_documentFragmentWithImageResource:(WebResource *)resource 300{ 301 DOMElement *imageElement = [self _imageElementWithImageResource:resource]; 302 if (!imageElement) 303 return 0; 304 DOMDocumentFragment *fragment = [[[self webFrame] DOMDocument] createDocumentFragment]; 305 [fragment appendChild:imageElement]; 306 return fragment; 307} 308 309- (DOMElement *)_imageElementWithImageResource:(WebResource *)resource 310{ 311 if (!resource) 312 return 0; 313 314 [self addSubresource:resource]; 315 316 DOMElement *imageElement = [[[self webFrame] DOMDocument] createElement:@"img"]; 317 318 // FIXME: calling _web_originalDataAsString on a file URL returns an absolute path. Workaround this. 319 NSURL *URL = [resource URL]; 320 [imageElement setAttribute:@"src" value:[URL isFileURL] ? [URL absoluteString] : [URL _web_originalDataAsString]]; 321 322 return imageElement; 323} 324 325// May return nil if not initialized with a URL. 326- (NSURL *)_URL 327{ 328 const KURL& url = _private->loader->url(); 329 if (url.isEmpty()) 330 return nil; 331 return url; 332} 333 334- (WebView *)_webView 335{ 336 return [[self webFrame] webView]; 337} 338 339- (BOOL)_isDocumentHTML 340{ 341 NSString *MIMEType = [self _responseMIMEType]; 342 return [WebView canShowMIMETypeAsHTML:MIMEType]; 343} 344 345- (void)_makeRepresentation 346{ 347 Class repClass = [[self class] _representationClassForMIMEType:[self _responseMIMEType] allowingPlugins:[[[self _webView] preferences] arePlugInsEnabled]]; 348 349 // Check if the data source was already bound? 350 if (![[self representation] isKindOfClass:repClass]) { 351 id newRep = repClass != nil ? [[repClass alloc] init] : nil; 352 [self _setRepresentation:(id <WebDocumentRepresentation>)newRep]; 353 [newRep release]; 354 } 355 356 [_private->representation setDataSource:self]; 357} 358 359- (DocumentLoader*)_documentLoader 360{ 361 return _private->loader; 362} 363 364- (id)_initWithDocumentLoader:(PassRefPtr<WebDocumentLoaderMac>)loader 365{ 366 self = [super init]; 367 if (!self) 368 return nil; 369 370 _private = [[WebDataSourcePrivate alloc] init]; 371 372 _private->loader = loader.releaseRef(); 373 374 LOG(Loading, "creating datasource for %@", static_cast<NSURL *>(_private->loader->request().url())); 375 376 ++WebDataSourceCount; 377 378 return self; 379} 380 381@end 382 383@implementation WebDataSource 384 385- (id)initWithRequest:(NSURLRequest *)request 386{ 387 return [self _initWithDocumentLoader:WebDocumentLoaderMac::create(request, SubstituteData())]; 388} 389 390- (void)dealloc 391{ 392 --WebDataSourceCount; 393 394 [_private release]; 395 396 [super dealloc]; 397} 398 399- (void)finalize 400{ 401 --WebDataSourceCount; 402 403 [super finalize]; 404} 405 406- (NSData *)data 407{ 408 RefPtr<SharedBuffer> mainResourceData = _private->loader->mainResourceData(); 409 if (!mainResourceData) 410 return nil; 411 return [mainResourceData->createNSData() autorelease]; 412} 413 414- (id <WebDocumentRepresentation>)representation 415{ 416 return _private->representation; 417} 418 419- (WebFrame *)webFrame 420{ 421 FrameLoader* frameLoader = _private->loader->frameLoader(); 422 if (!frameLoader) 423 return nil; 424 return static_cast<WebFrameLoaderClient*>(frameLoader->client())->webFrame(); 425} 426 427- (NSURLRequest *)initialRequest 428{ 429 return _private->loader->originalRequest().nsURLRequest(); 430} 431 432- (NSMutableURLRequest *)request 433{ 434 FrameLoader* frameLoader = _private->loader->frameLoader(); 435 if (!frameLoader || !frameLoader->frameHasLoaded()) 436 return nil; 437 438 // FIXME: this cast is dubious 439 return (NSMutableURLRequest *)_private->loader->request().nsURLRequest(); 440} 441 442- (NSURLResponse *)response 443{ 444 return _private->loader->response().nsURLResponse(); 445} 446 447- (NSString *)textEncodingName 448{ 449 NSString *textEncodingName = _private->loader->overrideEncoding(); 450 if (!textEncodingName) 451 textEncodingName = [[self response] textEncodingName]; 452 return textEncodingName; 453} 454 455- (BOOL)isLoading 456{ 457 return _private->loader->isLoadingInAPISense(); 458} 459 460// Returns nil or the page title. 461- (NSString *)pageTitle 462{ 463 return [[self representation] title]; 464} 465 466- (NSURL *)unreachableURL 467{ 468 const KURL& unreachableURL = _private->loader->unreachableURL(); 469 if (unreachableURL.isEmpty()) 470 return nil; 471 return unreachableURL; 472} 473 474- (WebArchive *)webArchive 475{ 476 // it makes no sense to grab a WebArchive from an uncommitted document. 477 if (!_private->loader->isCommitted()) 478 return nil; 479 480 return [[[WebArchive alloc] _initWithCoreLegacyWebArchive:LegacyWebArchive::create(core([self webFrame]))] autorelease]; 481} 482 483- (WebResource *)mainResource 484{ 485 RefPtr<ArchiveResource> coreResource = _private->loader->mainResource(); 486 return [[[WebResource alloc] _initWithCoreResource:coreResource.release()] autorelease]; 487} 488 489- (NSArray *)subresources 490{ 491 Vector<PassRefPtr<ArchiveResource> > coreSubresources; 492 _private->loader->getSubresources(coreSubresources); 493 494 NSMutableArray *subresources = [[NSMutableArray alloc] initWithCapacity:coreSubresources.size()]; 495 for (unsigned i = 0; i < coreSubresources.size(); ++i) { 496 WebResource *resource = [[WebResource alloc] _initWithCoreResource:coreSubresources[i]]; 497 if (resource) { 498 [subresources addObject:resource]; 499 [resource release]; 500 } 501 } 502 503 return [subresources autorelease]; 504} 505 506- (WebResource *)subresourceForURL:(NSURL *)URL 507{ 508 RefPtr<ArchiveResource> subresource = _private->loader->subresource(URL); 509 510 return subresource ? [[[WebResource alloc] _initWithCoreResource:subresource.get()] autorelease] : nil; 511} 512 513- (void)addSubresource:(WebResource *)subresource 514{ 515 _private->loader->addArchiveResource([subresource _coreResource]); 516} 517 518@end 519