blob: 75455499b0b64f20a75d83bfb0965b819fd4de2a [file] [log] [blame]
/*
* 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