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