| /* |
| * 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 <JavaScriptCore/Assertions.h> |
| #import <WebCore/FrameLoader.h> |
| #import <WebCore/KURL.h> |
| #import <WebCore/MIMETypeRegistry.h> |
| #import <WebCore/ResourceRequest.h> |
| #import <WebCore/SharedBuffer.h> |
| #import <WebCore/WebCoreObjCExtras.h> |
| #import <WebCore/WebCoreURLResponse.h> |
| #import <WebKit/DOMHTML.h> |
| #import <WebKit/DOMPrivate.h> |
| #import <WebKitSystemInterface.h> |
| |
| using namespace WebCore; |
| |
| @interface WebDataSourcePrivate : NSObject { |
| @public |
| WebDocumentLoaderMac* loader; |
| |
| id <WebDocumentRepresentation> 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<WebDocumentRepresentation>)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; |
| } |
| @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 |
| { |
| return [[self response] _webcore_MIMEType]; |
| } |
| |
| @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<WebDataSource*> 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 <WebDocumentRepresentation>)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<SharedBuffer> mainResourceData = _private->loader->mainResourceData(); |
| if (!mainResourceData) |
| return nil; |
| return [mainResourceData->createNSData() autorelease]; |
| } |
| |
| - (id <WebDocumentRepresentation>)representation |
| { |
| return _private->representation; |
| } |
| |
| - (WebFrame *)webFrame |
| { |
| FrameLoader* frameLoader = _private->loader->frameLoader(); |
| if (!frameLoader) |
| return nil; |
| return static_cast<WebFrameLoaderClient*>(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] 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] autorelease]; |
| } |
| |
| - (void)addSubresource:(WebResource *)subresource |
| { |
| if (subresource) { |
| if (!_private->unarchivingState) |
| _private->unarchivingState = [[WebUnarchivingState alloc] init]; |
| [_private->unarchivingState addResource:subresource]; |
| } |
| } |
| |
| @end |