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