02dbc2c471aaa4aa40420bbb0c551f45509cd53e
[WebKit-https.git] / Source / WebKit / mac / WebView / WebDataSource.mm
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