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