/* * Copyright (C) 2005, 2006, 2007 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #import "WebDataSource.h" #import "WebArchive.h" #import "WebArchiver.h" #import "WebDataSourceInternal.h" #import "WebDocument.h" #import "WebDocumentLoaderMac.h" #import "WebFrameBridge.h" #import "WebFrameInternal.h" #import "WebFrameLoadDelegate.h" #import "WebFrameLoaderClient.h" #import "WebHTMLRepresentation.h" #import "WebKitErrorsPrivate.h" #import "WebKitLogging.h" #import "WebKitStatisticsPrivate.h" #import "WebNSURLExtras.h" #import "WebNSURLRequestExtras.h" #import "WebPDFRepresentation.h" #import "WebResourceLoadDelegate.h" #import "WebResourcePrivate.h" #import "WebUnarchivingState.h" #import "WebViewInternal.h" #import #import #import #import #import #import #import #import #import #import using namespace WebCore; @interface WebDataSourcePrivate : NSObject { @public WebDocumentLoaderMac* loader; id representation; WebUnarchivingState *unarchivingState; BOOL representationFinishedLoading; } @end @implementation WebDataSourcePrivate #ifndef BUILDING_ON_TIGER + (void)initialize { WebCoreObjCFinalizeOnMainThread(self); } #endif - (void)dealloc { ASSERT(!loader->isLoading()); loader->detachDataSource(); loader->deref(); [representation release]; [unarchivingState release]; [super dealloc]; } - (void)finalize { ASSERT_MAIN_THREAD(); ASSERT(!loader->isLoading()); loader->detachDataSource(); loader->deref(); [super finalize]; } @end @interface WebDataSource (WebFileInternal) @end @implementation WebDataSource (WebFileInternal) - (void)_setRepresentation:(id)representation { [_private->representation release]; _private->representation = [representation retain]; _private->representationFinishedLoading = NO; } static inline void addTypesFromClass(NSMutableDictionary *allTypes, Class objCClass, NSArray *supportTypes) { NSEnumerator *enumerator = [supportTypes objectEnumerator]; ASSERT(enumerator != nil); NSString *mime = nil; while ((mime = [enumerator nextObject]) != nil) { // Don't clobber previously-registered classes. if ([allTypes objectForKey:mime] == nil) [allTypes setObject:objCClass forKey:mime]; } } + (Class)_representationClassForMIMEType:(NSString *)MIMEType { Class repClass; return [WebView _viewClass:nil andRepresentationClass:&repClass forMIMEType:MIMEType] ? repClass : nil; } - (NSString *)_MIMETypeOfResponse:(NSURLResponse *)response { #ifdef BUILDING_ON_TIGER return [response MIMEType]; #else // FIXME: This is part of a workaround for REGRESSION: Plain text document from HTTP server detected // as application/octet-stream NSString *MIMEType = [response MIMEType]; if ([MIMEType isEqualToString:@"application/octet-stream"] && [response isKindOfClass:[NSHTTPURLResponse class]] && [[[(NSHTTPURLResponse *)response allHeaderFields] objectForKey:@"Content-Type"] hasPrefix:@"text/plain"]) return @"text/plain"; return MIMEType; #endif } @end @implementation WebDataSource (WebPrivate) - (NSError *)_mainDocumentError { return _private->loader->mainDocumentError(); } - (void)_addSubframeArchives:(NSArray *)subframeArchives { NSEnumerator *enumerator = [subframeArchives objectEnumerator]; WebArchive *archive; while ((archive = [enumerator nextObject]) != nil) [self _addToUnarchiveState:archive]; } - (NSFileWrapper *)_fileWrapperForURL:(NSURL *)URL { if ([URL isFileURL]) { NSString *path = [[URL path] stringByResolvingSymlinksInPath]; return [[[NSFileWrapper alloc] initWithPath:path] autorelease]; } WebResource *resource = [self subresourceForURL:URL]; if (resource) return [resource _fileWrapperRepresentation]; NSCachedURLResponse *cachedResponse = [[self _webView] _cachedResponseForURL:URL]; if (cachedResponse) { NSFileWrapper *wrapper = [[[NSFileWrapper alloc] initRegularFileWithContents:[cachedResponse data]] autorelease]; [wrapper setPreferredFilename:[[cachedResponse response] suggestedFilename]]; return wrapper; } return nil; } - (NSString *)_responseMIMEType { #ifdef BUILDING_ON_TIGER return [[self response] MIMEType]; #else return [self _MIMETypeOfResponse:[self response]]; #endif } @end @implementation WebDataSource (WebInternal) - (void)_finishedLoading { _private->representationFinishedLoading = YES; [[self representation] finishedLoadingWithDataSource:self]; } - (void)_receivedData:(NSData *)data { // protect self temporarily, as the bridge receivedData call could remove our last ref RetainPtr protect(self); [[self representation] receivedData:data withDataSource:self]; [[[[self webFrame] frameView] documentView] dataSourceUpdated:self]; } - (void)_setMainDocumentError:(NSError *)error { if (!_private->representationFinishedLoading) { _private->representationFinishedLoading = YES; [[self representation] receivedError:error withDataSource:self]; } } - (void)_clearUnarchivingState { [_private->unarchivingState release]; _private->unarchivingState = nil; } - (void)_revertToProvisionalState { [self _setRepresentation:nil]; } + (NSMutableDictionary *)_repTypesAllowImageTypeOmission:(BOOL)allowImageTypeOmission { static NSMutableDictionary *repTypes = nil; static BOOL addedImageTypes = NO; if (!repTypes) { repTypes = [[NSMutableDictionary alloc] init]; addTypesFromClass(repTypes, [WebHTMLRepresentation class], [WebHTMLRepresentation supportedNonImageMIMETypes]); // Since this is a "secret default" we don't both registering it. BOOL omitPDFSupport = [[NSUserDefaults standardUserDefaults] boolForKey:@"WebKitOmitPDFSupport"]; if (!omitPDFSupport) addTypesFromClass(repTypes, [WebPDFRepresentation class], [WebPDFRepresentation supportedMIMETypes]); } if (!addedImageTypes && !allowImageTypeOmission) { addTypesFromClass(repTypes, [WebHTMLRepresentation class], [WebHTMLRepresentation supportedImageMIMETypes]); addedImageTypes = YES; } return repTypes; } - (WebResource *)_archivedSubresourceForURL:(NSURL *)URL { return [_private->unarchivingState archivedResourceForURL:URL]; } - (void)_replaceSelectionWithArchive:(WebArchive *)archive selectReplacement:(BOOL)selectReplacement { DOMDocumentFragment *fragment = [self _documentFragmentWithArchive:archive]; if (fragment) [[self _bridge] replaceSelectionWithFragment:fragment selectReplacement:selectReplacement smartReplace:NO matchStyle:NO]; } - (DOMDocumentFragment *)_documentFragmentWithArchive:(WebArchive *)archive { ASSERT(archive); WebResource *mainResource = [archive mainResource]; if (mainResource) { NSString *MIMEType = [mainResource MIMEType]; if ([WebView canShowMIMETypeAsHTML:MIMEType]) { NSString *markupString = [[NSString alloc] initWithData:[mainResource data] encoding:NSUTF8StringEncoding]; // FIXME: seems poor form to do this as a side effect of getting a document fragment [self _addToUnarchiveState:archive]; DOMDocumentFragment *fragment = [[self _bridge] documentFragmentWithMarkupString:markupString baseURLString:[[mainResource URL] _web_originalDataAsString]]; [markupString release]; return fragment; } else if (MIMETypeRegistry::isSupportedImageMIMEType(MIMEType)) { return [self _documentFragmentWithImageResource:mainResource]; } } return nil; } - (DOMDocumentFragment *)_documentFragmentWithImageResource:(WebResource *)resource { DOMElement *imageElement = [self _imageElementWithImageResource:resource]; if (!imageElement) return 0; DOMDocumentFragment *fragment = [[[self webFrame] DOMDocument] createDocumentFragment]; [fragment appendChild:imageElement]; return fragment; } - (DOMElement *)_imageElementWithImageResource:(WebResource *)resource { if (!resource) return 0; [self addSubresource:resource]; DOMElement *imageElement = [[[self webFrame] DOMDocument] createElement:@"img"]; // FIXME: calling _web_originalDataAsString on a file URL returns an absolute path. Workaround this. NSURL *URL = [resource URL]; [imageElement setAttribute:@"src" value:[URL isFileURL] ? [URL absoluteString] : [URL _web_originalDataAsString]]; return imageElement; } // May return nil if not initialized with a URL. - (NSURL *)_URL { KURL URL = _private->loader->URL(); return URL.isEmpty() ? nil : URL.getNSURL(); } - (WebArchive *)_popSubframeArchiveWithName:(NSString *)frameName { return [_private->unarchivingState popSubframeArchiveWithFrameName:frameName]; } - (WebFrameBridge *)_bridge { ASSERT(_private->loader->isCommitted()); return [[self webFrame] _bridge]; } - (WebView *)_webView { return [[self webFrame] webView]; } - (BOOL)_isDocumentHTML { NSString *MIMEType = [self _responseMIMEType]; return [WebView canShowMIMETypeAsHTML:MIMEType]; } -(void)_makeRepresentation { Class repClass = [[self class] _representationClassForMIMEType:[self _responseMIMEType]]; // Check if the data source was already bound? if (![[self representation] isKindOfClass:repClass]) { id newRep = repClass != nil ? [[repClass alloc] init] : nil; [self _setRepresentation:(id )newRep]; [newRep release]; } [_private->representation setDataSource:self]; } - (void)_addToUnarchiveState:(WebArchive *)archive { if (!_private->unarchivingState) _private->unarchivingState = [[WebUnarchivingState alloc] init]; [_private->unarchivingState addArchive:archive]; } - (DocumentLoader*)_documentLoader { return _private->loader; } - (id)_initWithDocumentLoader:(WebDocumentLoaderMac *)loader { self = [super init]; if (!self) return nil; _private = [[WebDataSourcePrivate alloc] init]; _private->loader = loader; loader->ref(); LOG(Loading, "creating datasource for %@", _private->loader->request().url().getNSURL()); ++WebDataSourceCount; return self; } @end @implementation WebDataSource - (id)initWithRequest:(NSURLRequest *)request { return [self _initWithDocumentLoader:new WebDocumentLoaderMac(request, SubstituteData())]; } - (void)dealloc { --WebDataSourceCount; [_private release]; [super dealloc]; } - (void)finalize { --WebDataSourceCount; [super finalize]; } - (NSData *)data { RefPtr mainResourceData = _private->loader->mainResourceData(); if (!mainResourceData) return nil; return [mainResourceData->createNSData() autorelease]; } - (id )representation { return _private->representation; } - (WebFrame *)webFrame { FrameLoader* frameLoader = _private->loader->frameLoader(); if (!frameLoader) return nil; return static_cast(frameLoader->client())->webFrame(); } - (NSURLRequest *)initialRequest { return _private->loader->initialRequest().nsURLRequest(); } - (NSMutableURLRequest *)request { FrameLoader* frameLoader = _private->loader->frameLoader(); if (!frameLoader || !frameLoader->frameHasLoaded()) return nil; // FIXME: this cast is dubious return (NSMutableURLRequest *)_private->loader->request().nsURLRequest(); } - (NSURLResponse *)response { return _private->loader->response().nsURLResponse(); } - (NSString *)textEncodingName { NSString *textEncodingName = _private->loader->overrideEncoding(); if (!textEncodingName) textEncodingName = [[self response] textEncodingName]; return textEncodingName; } - (BOOL)isLoading { return _private->loader->isLoadingInAPISense(); } // Returns nil or the page title. - (NSString *)pageTitle { return [[self representation] title]; } - (NSURL *)unreachableURL { KURL URL = _private->loader->unreachableURL(); return URL.isEmpty() ? nil : URL.getNSURL(); } - (WebArchive *)webArchive { // it makes no sense to grab a WebArchive from an uncommitted document. if (!_private->loader->isCommitted()) return nil; return [WebArchiver archiveFrame:[self webFrame]]; } - (WebResource *)mainResource { NSURLResponse *response = [self response]; return [[[WebResource alloc] initWithData:[self data] URL:[response URL] MIMEType:[self _responseMIMEType] textEncodingName:[response textEncodingName] frameName:[[self webFrame] name]] autorelease]; } - (NSArray *)subresources { if (!_private->loader->isCommitted()) return [NSMutableArray array]; NSArray *datas; NSArray *responses; [[self _bridge] getAllResourceDatas:&datas andResponses:&responses]; ASSERT([datas count] == [responses count]); NSMutableArray *subresources = [[NSMutableArray alloc] initWithCapacity:[datas count]]; for (unsigned i = 0; i < [datas count]; ++i) { NSURLResponse *response = [responses objectAtIndex:i]; [subresources addObject:[[[WebResource alloc] _initWithData:[datas objectAtIndex:i] URL:[response URL] response:response MIMEType:[self _MIMETypeOfResponse:response]] autorelease]]; } return [subresources autorelease]; } - (WebResource *)subresourceForURL:(NSURL *)URL { if (!_private->loader->isCommitted()) return nil; NSData *data; NSURLResponse *response; if (![[self _bridge] getData:&data andResponse:&response forURL:[URL _web_originalDataAsString]]) return [self _archivedSubresourceForURL:URL]; return [[[WebResource alloc] _initWithData:data URL:URL response:response MIMEType:[self _MIMETypeOfResponse:response]] autorelease]; } - (void)addSubresource:(WebResource *)subresource { if (subresource) { if (!_private->unarchivingState) _private->unarchivingState = [[WebUnarchivingState alloc] init]; [_private->unarchivingState addResource:subresource]; } } @end