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