• 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 allowingPlugins:(BOOL)allowPlugins
144{
145    Class repClass;
146    return [WebView _viewClass:nil andRepresentationClass:&repClass forMIMEType:MIMEType allowingPlugins:allowPlugins] ? 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#if ENABLE(OFFLINE_WEB_APPLICATIONS)
198    DocumentLoader* loader = [self _documentLoader];
199
200    if (!loader)
201        return NO;
202
203    NSString *cacheDir = [NSString _webkit_localCacheDirectoryWithBundleIdentifier:destinationBundleIdentifier];
204
205    return ApplicationCacheStorage::storeCopyOfCache(cacheDir, loader->applicationCacheHost());
206#else
207    return NO;
208#endif
209}
210
211@end
212
213@implementation WebDataSource (WebInternal)
214
215- (void)_finishedLoading
216{
217    _private->representationFinishedLoading = YES;
218    [[self representation] finishedLoadingWithDataSource:self];
219}
220
221- (void)_receivedData:(NSData *)data
222{
223    // protect self temporarily, as the bridge receivedData call could remove our last ref
224    RetainPtr<WebDataSource*> protect(self);
225
226    [[self representation] receivedData:data withDataSource:self];
227
228    if ([[self _webView] _usesDocumentViews])
229        [[[[self webFrame] frameView] documentView] dataSourceUpdated:self];
230}
231
232- (void)_setMainDocumentError:(NSError *)error
233{
234    if (!_private->representationFinishedLoading) {
235        _private->representationFinishedLoading = YES;
236        [[self representation] receivedError:error withDataSource:self];
237    }
238}
239
240- (void)_revertToProvisionalState
241{
242    [self _setRepresentation:nil];
243}
244
245+ (NSMutableDictionary *)_repTypesAllowImageTypeOmission:(BOOL)allowImageTypeOmission
246{
247    static NSMutableDictionary *repTypes = nil;
248    static BOOL addedImageTypes = NO;
249
250    if (!repTypes) {
251        repTypes = [[NSMutableDictionary alloc] init];
252        addTypesFromClass(repTypes, [WebHTMLRepresentation class], [WebHTMLRepresentation supportedNonImageMIMETypes]);
253
254        // Since this is a "secret default" we don't both registering it.
255        BOOL omitPDFSupport = [[NSUserDefaults standardUserDefaults] boolForKey:@"WebKitOmitPDFSupport"];
256        if (!omitPDFSupport)
257            addTypesFromClass(repTypes, [WebPDFRepresentation class], [WebPDFRepresentation supportedMIMETypes]);
258    }
259
260    if (!addedImageTypes && !allowImageTypeOmission) {
261        addTypesFromClass(repTypes, [WebHTMLRepresentation class], [WebHTMLRepresentation supportedImageMIMETypes]);
262        addedImageTypes = YES;
263    }
264
265    return repTypes;
266}
267
268- (void)_replaceSelectionWithArchive:(WebArchive *)archive selectReplacement:(BOOL)selectReplacement
269{
270    DOMDocumentFragment *fragment = [self _documentFragmentWithArchive:archive];
271    if (fragment)
272        [[self webFrame] _replaceSelectionWithFragment:fragment selectReplacement:selectReplacement smartReplace:NO matchStyle:NO];
273}
274
275// FIXME: There are few reasons why this method and many of its related methods can't be pushed entirely into WebCore in the future.
276- (DOMDocumentFragment *)_documentFragmentWithArchive:(WebArchive *)archive
277{
278    ASSERT(archive);
279    WebResource *mainResource = [archive mainResource];
280    if (mainResource) {
281        NSString *MIMEType = [mainResource MIMEType];
282        if ([WebView canShowMIMETypeAsHTML:MIMEType]) {
283            NSString *markupString = [[NSString alloc] initWithData:[mainResource data] encoding:NSUTF8StringEncoding];
284            // FIXME: seems poor form to do this as a side effect of getting a document fragment
285            if (DocumentLoader* loader = [self _documentLoader])
286                loader->addAllArchiveResources([archive _coreLegacyWebArchive]);
287
288            DOMDocumentFragment *fragment = [[self webFrame] _documentFragmentWithMarkupString:markupString baseURLString:[[mainResource URL] _web_originalDataAsString]];
289            [markupString release];
290            return fragment;
291        } else if (MIMETypeRegistry::isSupportedImageMIMEType(MIMEType)) {
292            return [self _documentFragmentWithImageResource:mainResource];
293
294        }
295    }
296    return nil;
297}
298
299- (DOMDocumentFragment *)_documentFragmentWithImageResource:(WebResource *)resource
300{
301    DOMElement *imageElement = [self _imageElementWithImageResource:resource];
302    if (!imageElement)
303        return 0;
304    DOMDocumentFragment *fragment = [[[self webFrame] DOMDocument] createDocumentFragment];
305    [fragment appendChild:imageElement];
306    return fragment;
307}
308
309- (DOMElement *)_imageElementWithImageResource:(WebResource *)resource
310{
311    if (!resource)
312        return 0;
313
314    [self addSubresource:resource];
315
316    DOMElement *imageElement = [[[self webFrame] DOMDocument] createElement:@"img"];
317
318    // FIXME: calling _web_originalDataAsString on a file URL returns an absolute path. Workaround this.
319    NSURL *URL = [resource URL];
320    [imageElement setAttribute:@"src" value:[URL isFileURL] ? [URL absoluteString] : [URL _web_originalDataAsString]];
321
322    return imageElement;
323}
324
325// May return nil if not initialized with a URL.
326- (NSURL *)_URL
327{
328    const KURL& url = _private->loader->url();
329    if (url.isEmpty())
330        return nil;
331    return url;
332}
333
334- (WebView *)_webView
335{
336    return [[self webFrame] webView];
337}
338
339- (BOOL)_isDocumentHTML
340{
341    NSString *MIMEType = [self _responseMIMEType];
342    return [WebView canShowMIMETypeAsHTML:MIMEType];
343}
344
345- (void)_makeRepresentation
346{
347    Class repClass = [[self class] _representationClassForMIMEType:[self _responseMIMEType] allowingPlugins:[[[self _webView] preferences] arePlugInsEnabled]];
348
349    // Check if the data source was already bound?
350    if (![[self representation] isKindOfClass:repClass]) {
351        id newRep = repClass != nil ? [[repClass alloc] init] : nil;
352        [self _setRepresentation:(id <WebDocumentRepresentation>)newRep];
353        [newRep release];
354    }
355
356    [_private->representation setDataSource:self];
357}
358
359- (DocumentLoader*)_documentLoader
360{
361    return _private->loader;
362}
363
364- (id)_initWithDocumentLoader:(PassRefPtr<WebDocumentLoaderMac>)loader
365{
366    self = [super init];
367    if (!self)
368        return nil;
369
370    _private = [[WebDataSourcePrivate alloc] init];
371
372    _private->loader = loader.releaseRef();
373
374    LOG(Loading, "creating datasource for %@", static_cast<NSURL *>(_private->loader->request().url()));
375
376    ++WebDataSourceCount;
377
378    return self;
379}
380
381@end
382
383@implementation WebDataSource
384
385- (id)initWithRequest:(NSURLRequest *)request
386{
387    return [self _initWithDocumentLoader:WebDocumentLoaderMac::create(request, SubstituteData())];
388}
389
390- (void)dealloc
391{
392    --WebDataSourceCount;
393
394    [_private release];
395
396    [super dealloc];
397}
398
399- (void)finalize
400{
401    --WebDataSourceCount;
402
403    [super finalize];
404}
405
406- (NSData *)data
407{
408    RefPtr<SharedBuffer> mainResourceData = _private->loader->mainResourceData();
409    if (!mainResourceData)
410        return nil;
411    return [mainResourceData->createNSData() autorelease];
412}
413
414- (id <WebDocumentRepresentation>)representation
415{
416    return _private->representation;
417}
418
419- (WebFrame *)webFrame
420{
421    FrameLoader* frameLoader = _private->loader->frameLoader();
422    if (!frameLoader)
423        return nil;
424    return static_cast<WebFrameLoaderClient*>(frameLoader->client())->webFrame();
425}
426
427- (NSURLRequest *)initialRequest
428{
429    return _private->loader->originalRequest().nsURLRequest();
430}
431
432- (NSMutableURLRequest *)request
433{
434    FrameLoader* frameLoader = _private->loader->frameLoader();
435    if (!frameLoader || !frameLoader->frameHasLoaded())
436        return nil;
437
438    // FIXME: this cast is dubious
439    return (NSMutableURLRequest *)_private->loader->request().nsURLRequest();
440}
441
442- (NSURLResponse *)response
443{
444    return _private->loader->response().nsURLResponse();
445}
446
447- (NSString *)textEncodingName
448{
449    NSString *textEncodingName = _private->loader->overrideEncoding();
450    if (!textEncodingName)
451        textEncodingName = [[self response] textEncodingName];
452    return textEncodingName;
453}
454
455- (BOOL)isLoading
456{
457    return _private->loader->isLoadingInAPISense();
458}
459
460// Returns nil or the page title.
461- (NSString *)pageTitle
462{
463    return [[self representation] title];
464}
465
466- (NSURL *)unreachableURL
467{
468    const KURL& unreachableURL = _private->loader->unreachableURL();
469    if (unreachableURL.isEmpty())
470        return nil;
471    return unreachableURL;
472}
473
474- (WebArchive *)webArchive
475{
476    // it makes no sense to grab a WebArchive from an uncommitted document.
477    if (!_private->loader->isCommitted())
478        return nil;
479
480    return [[[WebArchive alloc] _initWithCoreLegacyWebArchive:LegacyWebArchive::create(core([self webFrame]))] autorelease];
481}
482
483- (WebResource *)mainResource
484{
485    RefPtr<ArchiveResource> coreResource = _private->loader->mainResource();
486    return [[[WebResource alloc] _initWithCoreResource:coreResource.release()] autorelease];
487}
488
489- (NSArray *)subresources
490{
491    Vector<PassRefPtr<ArchiveResource> > coreSubresources;
492    _private->loader->getSubresources(coreSubresources);
493
494    NSMutableArray *subresources = [[NSMutableArray alloc] initWithCapacity:coreSubresources.size()];
495    for (unsigned i = 0; i < coreSubresources.size(); ++i) {
496        WebResource *resource = [[WebResource alloc] _initWithCoreResource:coreSubresources[i]];
497        if (resource) {
498            [subresources addObject:resource];
499            [resource release];
500        }
501    }
502
503    return [subresources autorelease];
504}
505
506- (WebResource *)subresourceForURL:(NSURL *)URL
507{
508    RefPtr<ArchiveResource> subresource = _private->loader->subresource(URL);
509
510    return subresource ? [[[WebResource alloc] _initWithCoreResource:subresource.get()] autorelease] : nil;
511}
512
513- (void)addSubresource:(WebResource *)subresource
514{
515    _private->loader->addArchiveResource([subresource _coreResource]);
516}
517
518@end
519