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
65using 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
134static 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