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