|
1 /* |
|
2 * Copyright (C) 2005, 2006, 2007 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 <WebKit/DOMHTML.h> |
|
59 #import <WebKit/DOMPrivate.h> |
|
60 #import <WebKitSystemInterface.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 |
|
139 @end |
|
140 |
|
141 @implementation WebDataSource (WebPrivate) |
|
142 |
|
143 - (NSError *)_mainDocumentError |
|
144 { |
|
145 return _private->loader->mainDocumentError(); |
|
146 } |
|
147 |
|
148 - (void)_addSubframeArchives:(NSArray *)subframeArchives |
|
149 { |
|
150 NSEnumerator *enumerator = [subframeArchives objectEnumerator]; |
|
151 WebArchive *archive; |
|
152 while ((archive = [enumerator nextObject]) != nil) |
|
153 [self _addToUnarchiveState:archive]; |
|
154 } |
|
155 |
|
156 - (NSFileWrapper *)_fileWrapperForURL:(NSURL *)URL |
|
157 { |
|
158 if ([URL isFileURL]) { |
|
159 NSString *path = [[URL path] stringByResolvingSymlinksInPath]; |
|
160 return [[[NSFileWrapper alloc] initWithPath:path] autorelease]; |
|
161 } |
|
162 |
|
163 WebResource *resource = [self subresourceForURL:URL]; |
|
164 if (resource) |
|
165 return [resource _fileWrapperRepresentation]; |
|
166 |
|
167 NSCachedURLResponse *cachedResponse = [[self _webView] _cachedResponseForURL:URL]; |
|
168 if (cachedResponse) { |
|
169 NSFileWrapper *wrapper = [[[NSFileWrapper alloc] initRegularFileWithContents:[cachedResponse data]] autorelease]; |
|
170 [wrapper setPreferredFilename:[[cachedResponse response] suggestedFilename]]; |
|
171 return wrapper; |
|
172 } |
|
173 |
|
174 return nil; |
|
175 } |
|
176 |
|
177 @end |
|
178 |
|
179 @implementation WebDataSource (WebInternal) |
|
180 |
|
181 - (void)_finishedLoading |
|
182 { |
|
183 _private->representationFinishedLoading = YES; |
|
184 [[self representation] finishedLoadingWithDataSource:self]; |
|
185 } |
|
186 |
|
187 - (void)_receivedData:(NSData *)data |
|
188 { |
|
189 // protect self temporarily, as the bridge receivedData call could remove our last ref |
|
190 RetainPtr<WebDataSource*> protect(self); |
|
191 |
|
192 [[self representation] receivedData:data withDataSource:self]; |
|
193 [[[[self webFrame] frameView] documentView] dataSourceUpdated:self]; |
|
194 } |
|
195 |
|
196 - (void)_setMainDocumentError:(NSError *)error |
|
197 { |
|
198 if (!_private->representationFinishedLoading) { |
|
199 _private->representationFinishedLoading = YES; |
|
200 [[self representation] receivedError:error withDataSource:self]; |
|
201 } |
|
202 } |
|
203 |
|
204 - (void)_clearUnarchivingState |
|
205 { |
|
206 [_private->unarchivingState release]; |
|
207 _private->unarchivingState = nil; |
|
208 } |
|
209 |
|
210 - (void)_revertToProvisionalState |
|
211 { |
|
212 [self _setRepresentation:nil]; |
|
213 } |
|
214 |
|
215 + (NSMutableDictionary *)_repTypesAllowImageTypeOmission:(BOOL)allowImageTypeOmission |
|
216 { |
|
217 static NSMutableDictionary *repTypes = nil; |
|
218 static BOOL addedImageTypes = NO; |
|
219 |
|
220 if (!repTypes) { |
|
221 repTypes = [[NSMutableDictionary alloc] init]; |
|
222 addTypesFromClass(repTypes, [WebHTMLRepresentation class], [WebHTMLRepresentation supportedNonImageMIMETypes]); |
|
223 |
|
224 // Since this is a "secret default" we don't both registering it. |
|
225 BOOL omitPDFSupport = [[NSUserDefaults standardUserDefaults] boolForKey:@"WebKitOmitPDFSupport"]; |
|
226 if (!omitPDFSupport) |
|
227 addTypesFromClass(repTypes, [WebPDFRepresentation class], [WebPDFRepresentation supportedMIMETypes]); |
|
228 } |
|
229 |
|
230 if (!addedImageTypes && !allowImageTypeOmission) { |
|
231 addTypesFromClass(repTypes, [WebHTMLRepresentation class], [WebHTMLRepresentation supportedImageMIMETypes]); |
|
232 addedImageTypes = YES; |
|
233 } |
|
234 |
|
235 return repTypes; |
|
236 } |
|
237 |
|
238 - (WebResource *)_archivedSubresourceForURL:(NSURL *)URL |
|
239 { |
|
240 return [_private->unarchivingState archivedResourceForURL:URL]; |
|
241 } |
|
242 |
|
243 - (void)_replaceSelectionWithArchive:(WebArchive *)archive selectReplacement:(BOOL)selectReplacement |
|
244 { |
|
245 DOMDocumentFragment *fragment = [self _documentFragmentWithArchive:archive]; |
|
246 if (fragment) |
|
247 [[self _bridge] replaceSelectionWithFragment:fragment selectReplacement:selectReplacement smartReplace:NO matchStyle:NO]; |
|
248 } |
|
249 |
|
250 - (DOMDocumentFragment *)_documentFragmentWithArchive:(WebArchive *)archive |
|
251 { |
|
252 ASSERT(archive); |
|
253 WebResource *mainResource = [archive mainResource]; |
|
254 if (mainResource) { |
|
255 NSString *MIMEType = [mainResource MIMEType]; |
|
256 if ([WebView canShowMIMETypeAsHTML:MIMEType]) { |
|
257 NSString *markupString = [[NSString alloc] initWithData:[mainResource data] encoding:NSUTF8StringEncoding]; |
|
258 // FIXME: seems poor form to do this as a side effect of getting a document fragment |
|
259 [self _addToUnarchiveState:archive]; |
|
260 DOMDocumentFragment *fragment = [[self _bridge] documentFragmentWithMarkupString:markupString baseURLString:[[mainResource URL] _web_originalDataAsString]]; |
|
261 [markupString release]; |
|
262 return fragment; |
|
263 } else if (MIMETypeRegistry::isSupportedImageMIMEType(MIMEType)) { |
|
264 return [self _documentFragmentWithImageResource:mainResource]; |
|
265 |
|
266 } |
|
267 } |
|
268 return nil; |
|
269 } |
|
270 |
|
271 - (DOMDocumentFragment *)_documentFragmentWithImageResource:(WebResource *)resource |
|
272 { |
|
273 DOMElement *imageElement = [self _imageElementWithImageResource:resource]; |
|
274 if (!imageElement) |
|
275 return 0; |
|
276 DOMDocumentFragment *fragment = [[[self webFrame] DOMDocument] createDocumentFragment]; |
|
277 [fragment appendChild:imageElement]; |
|
278 return fragment; |
|
279 } |
|
280 |
|
281 - (DOMElement *)_imageElementWithImageResource:(WebResource *)resource |
|
282 { |
|
283 if (!resource) |
|
284 return 0; |
|
285 |
|
286 [self addSubresource:resource]; |
|
287 |
|
288 DOMElement *imageElement = [[[self webFrame] DOMDocument] createElement:@"img"]; |
|
289 |
|
290 // FIXME: calling _web_originalDataAsString on a file URL returns an absolute path. Workaround this. |
|
291 NSURL *URL = [resource URL]; |
|
292 [imageElement setAttribute:@"src" value:[URL isFileURL] ? [URL absoluteString] : [URL _web_originalDataAsString]]; |
|
293 |
|
294 return imageElement; |
|
295 } |
|
296 |
|
297 // May return nil if not initialized with a URL. |
|
298 - (NSURL *)_URL |
|
299 { |
|
300 KURL URL = _private->loader->URL(); |
|
301 return URL.isEmpty() ? nil : URL.getNSURL(); |
|
302 } |
|
303 |
|
304 - (WebArchive *)_popSubframeArchiveWithName:(NSString *)frameName |
|
305 { |
|
306 return [_private->unarchivingState popSubframeArchiveWithFrameName:frameName]; |
|
307 } |
|
308 |
|
309 - (WebFrameBridge *)_bridge |
|
310 { |
|
311 ASSERT(_private->loader->isCommitted()); |
|
312 return [[self webFrame] _bridge]; |
|
313 } |
|
314 |
|
315 - (WebView *)_webView |
|
316 { |
|
317 return [[self webFrame] webView]; |
|
318 } |
|
319 |
|
320 - (BOOL)_isDocumentHTML |
|
321 { |
|
322 NSString *MIMEType = [[self response] MIMEType]; |
|
323 return [WebView canShowMIMETypeAsHTML:MIMEType]; |
|
324 } |
|
325 |
|
326 -(void)_makeRepresentation |
|
327 { |
|
328 Class repClass = [[self class] _representationClassForMIMEType:[[self response] MIMEType]]; |
|
329 |
|
330 // Check if the data source was already bound? |
|
331 if (![[self representation] isKindOfClass:repClass]) { |
|
332 id newRep = repClass != nil ? [[repClass alloc] init] : nil; |
|
333 [self _setRepresentation:(id <WebDocumentRepresentation>)newRep]; |
|
334 [newRep release]; |
|
335 } |
|
336 |
|
337 [_private->representation setDataSource:self]; |
|
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 - (DocumentLoader*)_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 _private = [[WebDataSourcePrivate alloc] init]; |
|
359 |
|
360 _private->loader = loader; |
|
361 loader->ref(); |
|
362 |
|
363 LOG(Loading, "creating datasource for %@", _private->loader->request().url().getNSURL()); |
|
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:new WebDocumentLoaderMac(request, SubstituteData())]; |
|
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 RefPtr<SharedBuffer> mainResourceData = _private->loader->mainResourceData(); |
|
398 if (!mainResourceData) |
|
399 return nil; |
|
400 return [mainResourceData->createNSData() autorelease]; |
|
401 } |
|
402 |
|
403 - (id <WebDocumentRepresentation>)representation |
|
404 { |
|
405 return _private->representation; |
|
406 } |
|
407 |
|
408 - (WebFrame *)webFrame |
|
409 { |
|
410 FrameLoader* frameLoader = _private->loader->frameLoader(); |
|
411 if (!frameLoader) |
|
412 return nil; |
|
413 return static_cast<WebFrameLoaderClient*>(frameLoader->client())->webFrame(); |
|
414 } |
|
415 |
|
416 - (NSURLRequest *)initialRequest |
|
417 { |
|
418 return _private->loader->initialRequest().nsURLRequest(); |
|
419 } |
|
420 |
|
421 - (NSMutableURLRequest *)request |
|
422 { |
|
423 FrameLoader* frameLoader = _private->loader->frameLoader(); |
|
424 if (!frameLoader || !frameLoader->frameHasLoaded()) |
|
425 return nil; |
|
426 |
|
427 // FIXME: this cast is dubious |
|
428 return (NSMutableURLRequest *)_private->loader->request().nsURLRequest(); |
|
429 } |
|
430 |
|
431 - (NSURLResponse *)response |
|
432 { |
|
433 return _private->loader->response().nsURLResponse(); |
|
434 } |
|
435 |
|
436 - (NSString *)textEncodingName |
|
437 { |
|
438 NSString *textEncodingName = _private->loader->overrideEncoding(); |
|
439 if (!textEncodingName) |
|
440 textEncodingName = [[self response] textEncodingName]; |
|
441 return textEncodingName; |
|
442 } |
|
443 |
|
444 - (BOOL)isLoading |
|
445 { |
|
446 return _private->loader->isLoadingInAPISense(); |
|
447 } |
|
448 |
|
449 // Returns nil or the page title. |
|
450 - (NSString *)pageTitle |
|
451 { |
|
452 return [[self representation] title]; |
|
453 } |
|
454 |
|
455 - (NSURL *)unreachableURL |
|
456 { |
|
457 KURL URL = _private->loader->unreachableURL(); |
|
458 return URL.isEmpty() ? nil : URL.getNSURL(); |
|
459 } |
|
460 |
|
461 - (WebArchive *)webArchive |
|
462 { |
|
463 // it makes no sense to grab a WebArchive from an uncommitted document. |
|
464 if (!_private->loader->isCommitted()) |
|
465 return nil; |
|
466 return [WebArchiver archiveFrame:[self webFrame]]; |
|
467 } |
|
468 |
|
469 - (WebResource *)mainResource |
|
470 { |
|
471 NSURLResponse *response = [self response]; |
|
472 return [[[WebResource alloc] initWithData:[self data] |
|
473 URL:[response URL] |
|
474 MIMEType:[response MIMEType] |
|
475 textEncodingName:[response textEncodingName] |
|
476 frameName:[[self webFrame] name]] autorelease]; |
|
477 } |
|
478 |
|
479 - (NSArray *)subresources |
|
480 { |
|
481 if (!_private->loader->isCommitted()) |
|
482 return [NSMutableArray array]; |
|
483 |
|
484 NSArray *datas; |
|
485 NSArray *responses; |
|
486 [[self _bridge] getAllResourceDatas:&datas andResponses:&responses]; |
|
487 ASSERT([datas count] == [responses count]); |
|
488 |
|
489 NSMutableArray *subresources = [[NSMutableArray alloc] initWithCapacity:[datas count]]; |
|
490 for (unsigned i = 0; i < [datas count]; ++i) { |
|
491 NSURLResponse *response = [responses objectAtIndex:i]; |
|
492 [subresources addObject:[[[WebResource alloc] _initWithData:[datas objectAtIndex:i] URL:[response URL] response:response] autorelease]]; |
|
493 } |
|
494 |
|
495 return [subresources autorelease]; |
|
496 } |
|
497 |
|
498 - (WebResource *)subresourceForURL:(NSURL *)URL |
|
499 { |
|
500 if (!_private->loader->isCommitted()) |
|
501 return nil; |
|
502 |
|
503 NSData *data; |
|
504 NSURLResponse *response; |
|
505 if (![[self _bridge] getData:&data andResponse:&response forURL:[URL _web_originalDataAsString]]) |
|
506 return [self _archivedSubresourceForURL:URL]; |
|
507 |
|
508 return [[[WebResource alloc] _initWithData:data URL:URL response:response] autorelease]; |
|
509 } |
|
510 |
|
511 - (void)addSubresource:(WebResource *)subresource |
|
512 { |
|
513 if (subresource) { |
|
514 if (!_private->unarchivingState) |
|
515 _private->unarchivingState = [[WebUnarchivingState alloc] init]; |
|
516 [_private->unarchivingState addResource:subresource]; |
|
517 } |
|
518 } |
|
519 |
|
520 @end |