WebKit/mac/WebView/WebDataSource.mm
changeset 0 4f2f89ce4247
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebKit/mac/WebView/WebDataSource.mm	Fri Sep 17 09:02:29 2010 +0300
@@ -0,0 +1,534 @@
+/*
+ * Copyright (C) 2005, 2006, 2007, 2008 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 "WebArchiveInternal.h"
+#import "WebDataSourceInternal.h"
+#import "WebDocument.h"
+#import "WebDocumentLoaderMac.h"
+#import "WebFrameInternal.h"
+#import "WebFrameLoadDelegate.h"
+#import "WebFrameLoaderClient.h"
+#import "WebHTMLRepresentation.h"
+#import "WebKitErrorsPrivate.h"
+#import "WebKitLogging.h"
+#import "WebKitStatisticsPrivate.h"
+#import "WebKitNSStringExtras.h"
+#import "WebNSURLExtras.h"
+#import "WebNSURLRequestExtras.h"
+#import "WebPDFRepresentation.h"
+#import "WebResourceInternal.h"
+#import "WebResourceLoadDelegate.h"
+#import "WebViewInternal.h"
+#import <WebCore/ApplicationCacheStorage.h>
+#import <WebCore/FrameLoader.h>
+#import <WebCore/KURL.h>
+#import <WebCore/LegacyWebArchive.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 <runtime/InitializeThreading.h>
+#import <wtf/Assertions.h>
+#import <wtf/Threading.h>
+
+using namespace WebCore;
+
+@interface WebDataSourcePrivate : NSObject {
+@public
+    WebDocumentLoaderMac* loader;
+   
+    id <WebDocumentRepresentation> representation;
+    
+    BOOL representationFinishedLoading;
+    BOOL includedInWebKitStatistics;
+}
+@end
+
+@implementation WebDataSourcePrivate 
+
++ (void)initialize
+{
+    JSC::initializeThreading();
+    WTF::initializeMainThreadToProcessMainThread();
+#ifndef BUILDING_ON_TIGER
+    WebCoreObjCFinalizeOnMainThread(self);
+#endif
+}
+
+- (void)dealloc
+{
+    if (WebCoreObjCScheduleDeallocateOnMainThread([WebDataSourcePrivate class], self))
+        return;
+
+    ASSERT(loader);
+    if (loader) {
+        ASSERT(!loader->isLoading());
+        loader->detachDataSource();
+        loader->deref();
+    }
+    
+    [representation release];
+
+    [super dealloc];
+}
+
+- (void)finalize
+{
+    ASSERT_MAIN_THREAD();
+
+    ASSERT(loader);
+    if (loader) {
+        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 allowingPlugins:(BOOL)allowPlugins
+{
+    Class repClass;
+    return [WebView _viewClass:nil andRepresentationClass:&repClass forMIMEType:MIMEType allowingPlugins:allowPlugins] ? repClass : nil;
+}
+@end
+
+@implementation WebDataSource (WebPrivate)
+
+- (NSError *)_mainDocumentError
+{
+    return _private->loader->mainDocumentError();
+}
+
+- (void)_addSubframeArchives:(NSArray *)subframeArchives
+{
+    // FIXME: This SPI is poor, poor design.  Can we come up with another solution for those who need it?
+    DocumentLoader* loader = [self _documentLoader];
+    ASSERT(loader);
+    
+    NSEnumerator *enumerator = [subframeArchives objectEnumerator];
+    WebArchive *archive;
+    while ((archive = [enumerator nextObject]) != nil)
+        loader->addAllArchiveResources([archive _coreLegacyWebArchive]);
+}
+
+- (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] MIMEType];
+}
+
+- (BOOL)_transferApplicationCache:(NSString*)destinationBundleIdentifier
+{
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+    DocumentLoader* loader = [self _documentLoader];
+    
+    if (!loader)
+        return NO;
+        
+    NSString *cacheDir = [NSString _webkit_localCacheDirectoryWithBundleIdentifier:destinationBundleIdentifier];
+    
+    return ApplicationCacheStorage::storeCopyOfCache(cacheDir, loader->applicationCacheHost());
+#else
+    return NO;
+#endif
+}
+
+- (void)_setDeferMainResourceDataLoad:(BOOL)flag
+{
+    DocumentLoader* loader = [self _documentLoader];
+
+    if (!loader)
+        return;
+
+    loader->setDeferMainResourceDataLoad(flag);
+}
+
+@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];
+
+    if ([[self _webView] _usesDocumentViews])
+        [[[[self webFrame] frameView] documentView] dataSourceUpdated:self];
+}
+
+- (void)_setMainDocumentError:(NSError *)error
+{
+    if (!_private->representationFinishedLoading) {
+        _private->representationFinishedLoading = YES;
+        [[self representation] receivedError:error withDataSource:self];
+    }
+}
+
+- (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;
+}
+
+- (void)_replaceSelectionWithArchive:(WebArchive *)archive selectReplacement:(BOOL)selectReplacement
+{
+    DOMDocumentFragment *fragment = [self _documentFragmentWithArchive:archive];
+    if (fragment)
+        [[self webFrame] _replaceSelectionWithFragment:fragment selectReplacement:selectReplacement smartReplace:NO matchStyle:NO];
+}
+
+// FIXME: There are few reasons why this method and many of its related methods can't be pushed entirely into WebCore in the future.
+- (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
+            if (DocumentLoader* loader = [self _documentLoader])
+                loader->addAllArchiveResources([archive _coreLegacyWebArchive]);
+
+            DOMDocumentFragment *fragment = [[self webFrame] _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
+{
+    const KURL& url = _private->loader->url();
+    if (url.isEmpty())
+        return nil;
+    return url;
+}
+
+- (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] allowingPlugins:[[[self _webView] preferences] arePlugInsEnabled]];
+    
+    // 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];
+}
+
+- (DocumentLoader*)_documentLoader
+{
+    return _private->loader;
+}
+
+- (id)_initWithDocumentLoader:(PassRefPtr<WebDocumentLoaderMac>)loader
+{
+    self = [super init];
+    if (!self)
+        return nil;
+    
+    _private = [[WebDataSourcePrivate alloc] init];
+    
+    _private->loader = loader.releaseRef();
+        
+    LOG(Loading, "creating datasource for %@", static_cast<NSURL *>(_private->loader->request().url()));
+
+    if ((_private->includedInWebKitStatistics = [[self webFrame] _isIncludedInWebKitStatistics]))
+        ++WebDataSourceCount;
+
+    return self;
+}
+
+@end
+
+@implementation WebDataSource
+
+- (id)initWithRequest:(NSURLRequest *)request
+{
+    return [self _initWithDocumentLoader:WebDocumentLoaderMac::create(request, SubstituteData())];
+}
+
+- (void)dealloc
+{
+    if (_private && _private->includedInWebKitStatistics)
+        --WebDataSourceCount;
+
+    [_private release];
+
+    [super dealloc];
+}
+
+- (void)finalize
+{
+    if (_private && _private->includedInWebKitStatistics)
+        --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->originalRequest().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
+{
+    const KURL& unreachableURL = _private->loader->unreachableURL();
+    if (unreachableURL.isEmpty())
+        return nil;
+    return unreachableURL;
+}
+
+- (WebArchive *)webArchive
+{
+    // it makes no sense to grab a WebArchive from an uncommitted document.
+    if (!_private->loader->isCommitted())
+        return nil;
+        
+    return [[[WebArchive alloc] _initWithCoreLegacyWebArchive:LegacyWebArchive::create(core([self webFrame]))] autorelease];
+}
+
+- (WebResource *)mainResource
+{
+    RefPtr<ArchiveResource> coreResource = _private->loader->mainResource();
+    return [[[WebResource alloc] _initWithCoreResource:coreResource.release()] autorelease];
+}
+
+- (NSArray *)subresources
+{
+    Vector<PassRefPtr<ArchiveResource> > coreSubresources;
+    _private->loader->getSubresources(coreSubresources);
+
+    NSMutableArray *subresources = [[NSMutableArray alloc] initWithCapacity:coreSubresources.size()];
+    for (unsigned i = 0; i < coreSubresources.size(); ++i) {
+        WebResource *resource = [[WebResource alloc] _initWithCoreResource:coreSubresources[i]];
+        if (resource) {
+            [subresources addObject:resource];
+            [resource release];
+        }
+    }
+
+    return [subresources autorelease];
+}
+
+- (WebResource *)subresourceForURL:(NSURL *)URL
+{
+    RefPtr<ArchiveResource> subresource = _private->loader->subresource(URL);
+    
+    return subresource ? [[[WebResource alloc] _initWithCoreResource:subresource.get()] autorelease] : nil;
+}
+
+- (void)addSubresource:(WebResource *)subresource
+{    
+    _private->loader->addArchiveResource([subresource _coreResource]);
+}
+
+@end