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