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