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