7afc67049f7150a3b151837aeb942a81748fe53f
[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 "WebDOMOperationsPrivate.h"
34 #import "WebDataProtocol.h"
35 #import "WebDataSourceInternal.h"
36 #import "WebDefaultResourceLoadDelegate.h"
37 #import "WebDocument.h"
38 #import "WebDownloadInternal.h"
39 #import "WebFrameBridge.h"
40 #import "WebFrameInternal.h"
41 #import "WebFrameLoader.h"
42 #import "WebFrameLoadDelegate.h"
43 #import "WebFrameView.h"
44 #import "WebHTMLRepresentation.h"
45 #import "WebHTMLViewPrivate.h"
46 #import "WebHistory.h"
47 #import "WebHistoryItemPrivate.h"
48 #import "WebIconDatabasePrivate.h"
49 #import "WebKitErrorsPrivate.h"
50 #import "WebKitLogging.h"
51 #import "WebKitNSStringExtras.h"
52 #import "WebKitStatisticsPrivate.h"
53 #import "WebNSObjectExtras.h"
54 #import "WebNSURLExtras.h"
55 #import "WebNSURLRequestExtras.h"
56 #import "WebPDFRepresentation.h"
57 #import "WebPreferences.h"
58 #import "WebResourceLoadDelegate.h"
59 #import "WebResourcePrivate.h"
60 #import "WebScriptDebugServerPrivate.h"
61 #import "WebUnarchivingState.h"
62 #import "WebViewInternal.h"
63 #import <Foundation/NSURLConnection.h>
64 #import <Foundation/NSURLRequest.h>
65 #import <JavaScriptCore/Assertions.h>
66 #import <WebKit/DOMHTML.h>
67 #import <WebKit/DOMPrivate.h>
68 #import <WebKitSystemInterface.h>
69 #import "WebDocumentLoadStateMac.h"
70
71 @interface WebDataSourcePrivate : NSObject
72 {
73     @public
74     
75     WebDocumentLoadStateMac *loadState;
76    
77     id <WebDocumentRepresentation> representation;
78     
79     BOOL loadingFromPageCache;
80     WebUnarchivingState *unarchivingState;
81     NSMutableDictionary *subresources;
82     BOOL representationFinishedLoading;
83 }
84
85 @end
86
87 @implementation WebDataSourcePrivate 
88
89 - (void)dealloc
90 {
91     ASSERT(![loadState isLoading]);
92
93     [loadState release];
94     
95     [representation release];
96     [unarchivingState release];
97
98     [super dealloc];
99 }
100
101 @end
102
103 @interface WebDataSource (WebFileInternal)
104 @end
105
106 @implementation WebDataSource (WebFileInternal)
107
108 - (void)_setRepresentation: (id<WebDocumentRepresentation>)representation
109 {
110     [_private->representation release];
111     _private->representation = [representation retain];
112     _private->representationFinishedLoading = NO;
113 }
114
115 static inline void addTypesFromClass(NSMutableDictionary *allTypes, Class class, NSArray *supportTypes)
116 {
117     NSEnumerator *enumerator = [supportTypes objectEnumerator];
118     ASSERT(enumerator != nil);
119     NSString *mime = nil;
120     while ((mime = [enumerator nextObject]) != nil) {
121         // Don't clobber previously-registered classes.
122         if ([allTypes objectForKey:mime] == nil)
123             [allTypes setObject:class forKey:mime];
124     }
125 }
126
127 + (Class)_representationClassForMIMEType:(NSString *)MIMEType
128 {
129     Class repClass;
130     return [WebView _viewClass:nil andRepresentationClass:&repClass forMIMEType:MIMEType] ? repClass : nil;
131 }
132
133 @end
134
135 @implementation WebDataSource (WebPrivate)
136
137 - (NSError *)_mainDocumentError
138 {
139     return [_private->loadState mainDocumentError];
140 }
141
142 - (void)_addSubframeArchives:(NSArray *)subframeArchives
143 {
144     NSEnumerator *enumerator = [subframeArchives objectEnumerator];
145     WebArchive *archive;
146     while ((archive = [enumerator nextObject]) != nil)
147         [self _addToUnarchiveState:archive];
148 }
149
150 - (NSFileWrapper *)_fileWrapperForURL:(NSURL *)URL
151 {
152     if ([URL isFileURL]) {
153         NSString *path = [[URL path] stringByResolvingSymlinksInPath];
154         return [[[NSFileWrapper alloc] initWithPath:path] autorelease];
155     }
156     
157     WebResource *resource = [self subresourceForURL:URL];
158     if (resource)
159         return [resource _fileWrapperRepresentation];
160     
161     NSCachedURLResponse *cachedResponse = [[self _webView] _cachedResponseForURL:URL];
162     if (cachedResponse) {
163         NSFileWrapper *wrapper = [[[NSFileWrapper alloc] initRegularFileWithContents:[cachedResponse data]] autorelease];
164         [wrapper setPreferredFilename:[[cachedResponse response] suggestedFilename]];
165         return wrapper;
166     }
167     
168     return nil;
169 }
170
171 @end
172
173 @implementation WebDataSource (WebInternal)
174
175 - (void)_finishedLoading
176 {
177     _private->representationFinishedLoading = YES;
178     [[self representation] finishedLoadingWithDataSource:self];
179 }
180
181 - (void)_receivedData:(NSData *)data
182 {
183     [[self representation] receivedData:data withDataSource:self];
184     [[[[self webFrame] frameView] documentView] dataSourceUpdated:self];
185 }
186
187 - (void)_setMainDocumentError:(NSError *)error
188 {
189     if (!_private->representationFinishedLoading) {
190         _private->representationFinishedLoading = YES;
191         [[self representation] receivedError:error withDataSource:self];
192     }
193 }
194
195 - (void)_clearUnarchivingState
196 {
197     [_private->unarchivingState release];
198     _private->unarchivingState = nil;
199 }
200
201 - (void)_revertToProvisionalState
202 {
203     [self _setRepresentation:nil];
204 }
205
206 + (NSMutableDictionary *)_repTypesAllowImageTypeOmission:(BOOL)allowImageTypeOmission
207 {
208     static NSMutableDictionary *repTypes = nil;
209     static BOOL addedImageTypes = NO;
210     
211     if (!repTypes) {
212         repTypes = [[NSMutableDictionary alloc] init];
213         addTypesFromClass(repTypes, [WebHTMLRepresentation class], [WebHTMLRepresentation supportedNonImageMIMETypes]);
214         
215         // Since this is a "secret default" we don't both registering it.
216         BOOL omitPDFSupport = [[NSUserDefaults standardUserDefaults] boolForKey:@"WebKitOmitPDFSupport"];
217         if (!omitPDFSupport)
218             addTypesFromClass(repTypes, [WebPDFRepresentation class], [WebPDFRepresentation supportedMIMETypes]);
219     }
220     
221     if (!addedImageTypes && !allowImageTypeOmission) {
222         addTypesFromClass(repTypes, [WebHTMLRepresentation class], [WebHTMLRepresentation supportedImageMIMETypes]);
223         addedImageTypes = YES;
224     }
225     
226     return repTypes;
227 }
228
229 - (void)_decidePolicyForMIMEType:(NSString *)MIMEType decisionListener:(WebPolicyDecisionListener *)listener
230 {
231     WebView *wv = [self _webView];
232     [[wv _policyDelegateForwarder] webView:wv decidePolicyForMIMEType:MIMEType
233                                    request:[self request]
234                                      frame:[self webFrame]
235                           decisionListener:listener];
236 }
237
238 - (void)_receivedMainResourceError:(NSError *)error complete:(BOOL)isComplete
239 {
240     // MOVABLE
241     WebFrameBridge *bridge = [[self webFrame] _bridge];
242     
243     // Retain the bridge because the stop may release the last reference to it.
244     [bridge retain];
245     
246     if (isComplete) {
247         // FIXME: Don't want to do this if an entirely new load is going, so should check
248         // that both data sources on the frame are either self or nil.
249         // Can't call [self _bridge] because we might not have commited yet
250         [bridge stop];
251         // FIXME: WebKitErrorPlugInWillHandleLoad is a workaround for the cancel we do to prevent loading plugin content twice.  See <rdar://problem/4258008>
252         if ([error code] != NSURLErrorCancelled && [error code] != WebKitErrorPlugInWillHandleLoad)
253             [bridge handleFallbackContent];
254     }
255     
256     [bridge release];
257     
258     [[self webFrame] _receivedMainResourceError:error];
259     [_private->loadState mainReceivedError:error complete:isComplete];
260 }
261
262 - (void)_downloadWithLoadingConnection:(NSURLConnection *)connection request:(NSURLRequest *)request response:(NSURLResponse *)r proxy:(WKNSURLConnectionDelegateProxyPtr) proxy
263 {
264     [WebDownload _downloadWithLoadingConnection:connection
265                                         request:request
266                                        response:r
267                                        delegate:[[self _webView] downloadDelegate]
268                                           proxy:proxy];
269 }
270
271 - (void)_didFailLoadingWithError:(NSError *)error forResource:(id)identifier
272 {
273     WebView *webView = [self _webView];
274     
275     [webView _completeProgressForIdentifier:identifier];
276     
277     if (error)
278         [[webView _resourceLoadDelegateForwarder] webView:webView resource:identifier didFailLoadingWithError:error fromDataSource:self];
279 }
280
281 - (void)_didFinishLoadingForResource:(id)identifier
282 {
283     WebView *webView = [self _webView];
284     
285     [webView _completeProgressForIdentifier:identifier];    
286     
287     if ([webView _resourceLoadDelegateImplementations].delegateImplementsDidFinishLoadingFromDataSource)
288         [[webView resourceLoadDelegate] webView:webView resource:identifier didFinishLoadingFromDataSource:self];
289     else
290         [[WebDefaultResourceLoadDelegate sharedResourceLoadDelegate] webView:webView resource:identifier didFinishLoadingFromDataSource:self];
291 }
292
293 - (void)_didReceiveData:(NSData *)data contentLength:(int)lengthReceived forResource:(id)identifier
294 {
295     WebView *webView = [self _webView];
296     
297     [webView _incrementProgressForIdentifier:identifier data:data];
298     
299     if ([webView _resourceLoadDelegateImplementations].delegateImplementsDidReceiveContentLength)
300         [[webView resourceLoadDelegate] webView:webView resource:identifier didReceiveContentLength:(WebNSUInteger)lengthReceived fromDataSource:self];
301     else
302         [[WebDefaultResourceLoadDelegate sharedResourceLoadDelegate] webView:webView resource:identifier didReceiveContentLength:(WebNSUInteger)lengthReceived fromDataSource:self];
303 }
304
305 - (void)_didReceiveResponse:(NSURLResponse *)r forResource:(id)identifier
306 {
307     WebView *webView = [self _webView];
308     
309     [_private->loadState addResponse:r];
310     
311     [webView _incrementProgressForIdentifier:identifier response:r];
312     
313     if ([webView _resourceLoadDelegateImplementations].delegateImplementsDidReceiveResponse)
314         [[webView resourceLoadDelegate] webView:webView resource:identifier didReceiveResponse:r fromDataSource:self];
315     else
316         [[WebDefaultResourceLoadDelegate sharedResourceLoadDelegate] webView:webView resource:identifier didReceiveResponse:r fromDataSource:self];
317 }
318
319 - (void)_didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)currentWebChallenge forResource:(id)identifier
320 {
321     WebView *webView = [self _webView];
322     
323     if ([webView _resourceLoadDelegateImplementations].delegateImplementsDidCancelAuthenticationChallenge)
324         [[webView resourceLoadDelegate] webView:webView resource:identifier didCancelAuthenticationChallenge:currentWebChallenge fromDataSource:self];
325     else
326         [[WebDefaultResourceLoadDelegate sharedResourceLoadDelegate] webView:webView resource:identifier didCancelAuthenticationChallenge:currentWebChallenge fromDataSource:self];
327 }
328
329 - (void)_didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)currentWebChallenge forResource:(id)identifier
330 {
331     WebView *webView = [self _webView];
332     
333     if ([webView _resourceLoadDelegateImplementations].delegateImplementsDidReceiveAuthenticationChallenge)
334         [[webView resourceLoadDelegate] webView:webView resource:identifier didReceiveAuthenticationChallenge:currentWebChallenge fromDataSource:self];
335     else
336         [[WebDefaultResourceLoadDelegate sharedResourceLoadDelegate] webView:webView resource:identifier didReceiveAuthenticationChallenge:currentWebChallenge fromDataSource:self];
337 }
338
339 - (NSURLRequest *)_willSendRequest:(NSMutableURLRequest *)clientRequest forResource:(id)identifier redirectResponse:(NSURLResponse *)redirectResponse
340 {
341     WebView *webView = [self _webView];
342     
343     [clientRequest _web_setHTTPUserAgent:[webView userAgentForURL:[clientRequest URL]]];
344     
345     if ([webView _resourceLoadDelegateImplementations].delegateImplementsWillSendRequest)
346         return [[webView resourceLoadDelegate] webView:webView resource:identifier willSendRequest:clientRequest redirectResponse:redirectResponse fromDataSource:self];
347     else
348         return [[WebDefaultResourceLoadDelegate sharedResourceLoadDelegate] webView:webView resource:identifier willSendRequest:clientRequest redirectResponse:redirectResponse fromDataSource:self];
349 }        
350
351 - (id)_identifierForInitialRequest:(NSURLRequest *)clientRequest
352 {
353     WebView *webView = [self _webView];
354     
355     // The identifier is released after the last callback, rather than in dealloc
356     // to avoid potential cycles.
357     if ([webView _resourceLoadDelegateImplementations].delegateImplementsIdentifierForRequest)
358         return [[[webView resourceLoadDelegate] webView:webView identifierForInitialRequest:clientRequest fromDataSource:self] retain];
359     else
360         return [[[WebDefaultResourceLoadDelegate sharedResourceLoadDelegate] webView:webView identifierForInitialRequest:clientRequest fromDataSource:self] retain];
361 }
362
363 - (WebResource *)_archivedSubresourceForURL:(NSURL *)URL
364 {
365     return [_private->unarchivingState archivedResourceForURL:URL];
366 }
367
368 - (void)_startLoading
369 {
370     [_private->loadState prepareForLoadStart];
371     
372     if ([[_private->loadState frameLoader] isLoadingMainResource])
373         return;
374     
375     _private->loadingFromPageCache = NO;
376     
377     id identifier;
378     id resourceLoadDelegate = [[self _webView] resourceLoadDelegate];
379     if ([resourceLoadDelegate respondsToSelector:@selector(webView:identifierForInitialRequest:fromDataSource:)])
380         identifier = [resourceLoadDelegate webView:[self _webView] identifierForInitialRequest:[_private->loadState originalRequest] fromDataSource:self];
381     else
382         identifier = [[WebDefaultResourceLoadDelegate sharedResourceLoadDelegate] webView:[self _webView] identifierForInitialRequest:[_private->loadState originalRequest] fromDataSource:self];
383     
384     if (![[_private->loadState frameLoader] startLoadingMainResourceWithRequest:[_private->loadState actualRequest] identifier:identifier])
385         [_private->loadState updateLoading];
386 }
387
388 - (void)_replaceSelectionWithArchive:(WebArchive *)archive selectReplacement:(BOOL)selectReplacement
389 {
390     DOMDocumentFragment *fragment = [self _documentFragmentWithArchive:archive];
391     if (fragment)
392         [[self _bridge] replaceSelectionWithFragment:fragment selectReplacement:selectReplacement smartReplace:NO matchStyle:NO];
393 }
394
395 - (DOMDocumentFragment *)_documentFragmentWithArchive:(WebArchive *)archive
396 {
397     ASSERT(archive);
398     WebResource *mainResource = [archive mainResource];
399     if (mainResource) {
400         NSString *MIMEType = [mainResource MIMEType];
401         if ([WebView canShowMIMETypeAsHTML:MIMEType]) {
402             NSString *markupString = [[NSString alloc] initWithData:[mainResource data] encoding:NSUTF8StringEncoding];
403             // FIXME: seems poor form to do this as a side effect of getting a document fragment
404             [self _addToUnarchiveState:archive];
405             DOMDocumentFragment *fragment = [[self _bridge] documentFragmentWithMarkupString:markupString baseURLString:[[mainResource URL] _web_originalDataAsString]];
406             [markupString release];
407             return fragment;
408         } else if ([[WebFrameBridge supportedImageResourceMIMETypes] containsObject:MIMEType]) {
409             return [self _documentFragmentWithImageResource:mainResource];
410             
411         }
412     }
413     return nil;
414 }
415
416 - (DOMDocumentFragment *)_documentFragmentWithImageResource:(WebResource *)resource
417 {
418     DOMElement *imageElement = [self _imageElementWithImageResource:resource];
419     if (!imageElement)
420         return 0;
421     DOMDocumentFragment *fragment = [[[self _bridge] DOMDocument] createDocumentFragment];
422     [fragment appendChild:imageElement];
423     return fragment;
424 }
425
426 - (DOMElement *)_imageElementWithImageResource:(WebResource *)resource
427 {
428     if (!resource)
429         return 0;
430     
431     [self addSubresource:resource];
432     
433     DOMElement *imageElement = [[[self _bridge] DOMDocument] createElement:@"img"];
434     
435     // FIXME: calling _web_originalDataAsString on a file URL returns an absolute path. Workaround this.
436     NSURL *URL = [resource URL];
437     [imageElement setAttribute:@"src" value:[URL isFileURL] ? [URL absoluteString] : [URL _web_originalDataAsString]];
438     
439     return imageElement;
440 }
441
442 // May return nil if not initialized with a URL.
443 - (NSURL *)_URL
444 {
445     return [[self request] URL];
446 }
447
448 - (void)_loadFromPageCache:(NSDictionary *)pageCache
449 {
450     [_private->loadState prepareForLoadStart];
451     _private->loadingFromPageCache = YES;
452     [_private->loadState setCommitted:YES];
453     [[self webFrame] _commitProvisionalLoad:pageCache];
454 }
455
456 - (WebArchive *)_popSubframeArchiveWithName:(NSString *)frameName
457 {
458     return [_private->unarchivingState popSubframeArchiveWithFrameName:frameName];
459 }
460
461 - (WebFrameBridge *)_bridge
462 {
463     ASSERT([_private->loadState isCommitted]);
464     return [[self webFrame] _bridge];
465 }
466
467 - (WebView *)_webView
468 {
469     return [[[_private->loadState frameLoader] webFrame] webView];
470 }
471
472 - (BOOL)_isDocumentHTML
473 {
474     NSString *MIMEType = [[self response] MIMEType];
475     return [WebView canShowMIMETypeAsHTML:MIMEType];
476 }
477
478 - (void)_stopLoadingWithError:(NSError *)error
479 {
480     [[_private->loadState frameLoader] stopLoadingWithError:error];
481 }
482
483 - (BOOL)_loadingFromPageCache
484 {
485     return _private->loadingFromPageCache;
486 }
487
488 -(void)_makeRepresentation
489 {
490     Class repClass = [[self class] _representationClassForMIMEType:[[self response] MIMEType]];
491     
492     // Check if the data source was already bound?
493     if (![[self representation] isKindOfClass:repClass]) {
494         id newRep = repClass != nil ? [[repClass alloc] init] : nil;
495         [self _setRepresentation:(id <WebDocumentRepresentation>)newRep];
496         [newRep release];
497     }
498     
499     [_private->representation setDataSource:self];
500 }
501
502 - (NSURL *)_URLForHistory
503 {
504     // Return the URL to be used for history and B/F list.
505     // Returns nil for WebDataProtocol URLs that aren't alternates 
506     // for unreachable URLs, because these can't be stored in history.
507     NSURL *URL = [[_private->loadState originalRequestCopy] URL];
508     if ([WebDataProtocol _webIsDataProtocolURL:URL])
509         URL = [[_private->loadState originalRequestCopy] _webDataRequestUnreachableURL];
510     
511     return [URL _webkit_canonicalize];
512 }
513
514 - (void)_addToUnarchiveState:(WebArchive *)archive
515 {
516     if (!_private->unarchivingState)
517         _private->unarchivingState = [[WebUnarchivingState alloc] init];
518     [_private->unarchivingState addArchive:archive];
519 }
520
521 - (WebDocumentLoadState *)_documentLoadState
522 {
523     return _private->loadState;
524 }
525
526 - (id)_initWithDocumentLoadState:(WebDocumentLoadStateMac *)loadState
527 {
528     self = [super init];
529     if (!self) {
530         return nil;
531     }
532     
533     _private = [[WebDataSourcePrivate alloc] init];
534     
535     _private->loadState = [loadState retain];
536         
537     LOG(Loading, "creating datasource for %@", [[_private->loadState request] URL]);
538     WKSupportsMultipartXMixedReplace([_private->loadState request]);
539     
540     ++WebDataSourceCount;
541     
542     return self;
543     
544 }
545
546 @end
547
548 @implementation WebDataSource
549
550 -(id)initWithRequest:(NSURLRequest *)request
551 {
552     return [self _initWithDocumentLoadState:[[WebDocumentLoadState alloc] initWithRequest:request]];
553 }
554
555 - (void)dealloc
556 {
557     ASSERT([[_private->loadState frameLoader] activeDataSource] != self || ![[_private->loadState frameLoader] isLoading]);
558
559     --WebDataSourceCount;
560     
561     [_private release];
562     
563     [super dealloc];
564 }
565
566 - (void)finalize
567 {
568     --WebDataSourceCount;
569
570     [super finalize];
571 }
572
573 - (NSData *)data
574 {
575     return [_private->loadState mainResourceData];
576 }
577
578 - (id <WebDocumentRepresentation>)representation
579 {
580     return _private->representation;
581 }
582
583 - (WebFrame *)webFrame
584 {
585     return [[_private->loadState frameLoader] webFrame];
586 }
587
588 -(NSURLRequest *)initialRequest
589 {
590     NSURLRequest *clientRequest = [[_private->loadState originalRequest] _webDataRequestExternalRequest];
591     if (!clientRequest)
592         clientRequest = [_private->loadState originalRequest];
593     return clientRequest;
594 }
595
596 -(NSMutableURLRequest *)request
597 {
598     return [_private->loadState request];
599 }
600
601 - (NSURLResponse *)response
602 {
603     return [_private->loadState response];
604 }
605
606 - (NSString *)textEncodingName
607 {
608     NSString *textEncodingName = [_private->loadState overrideEncoding];
609
610     if (!textEncodingName)
611         textEncodingName = [[self response] textEncodingName];
612
613     return textEncodingName;
614 }
615
616 - (BOOL)isLoading
617 {
618     return [_private->loadState isLoadingInAPISense];
619 }
620
621 // Returns nil or the page title.
622 - (NSString *)pageTitle
623 {
624     return [[self representation] title];
625 }
626
627 - (NSURL *)unreachableURL
628 {
629     return [[_private->loadState originalRequest] _webDataRequestUnreachableURL];
630 }
631
632 - (WebArchive *)webArchive
633 {
634     // it makes no sense to grab a WebArchive from an uncommitted document.
635     if (![_private->loadState isCommitted])
636         return nil;
637
638     return [WebArchiver archiveFrame:[self webFrame]];
639 }
640
641 - (WebResource *)mainResource
642 {
643     NSURLResponse *response = [self response];
644     return [[[WebResource alloc] initWithData:[self data]
645                                           URL:[response URL] 
646                                      MIMEType:[response MIMEType]
647                              textEncodingName:[response textEncodingName]
648                                     frameName:[[self webFrame] name]] autorelease];
649 }
650
651 - (NSArray *)subresources
652 {
653     NSArray *datas;
654     NSArray *responses;
655     [[self _bridge] getAllResourceDatas:&datas andResponses:&responses];
656     ASSERT([datas count] == [responses count]);
657
658     NSMutableArray *subresources = [[NSMutableArray alloc] initWithCapacity:[datas count]];
659     for (unsigned i = 0; i < [datas count]; ++i) {
660         NSURLResponse *response = [responses objectAtIndex:i];
661         [subresources addObject:[[[WebResource alloc] _initWithData:[datas objectAtIndex:i] URL:[response URL] response:response] autorelease]];
662     }
663
664     return [subresources autorelease];
665 }
666
667 - (WebResource *)subresourceForURL:(NSURL *)URL
668 {
669     NSData *data;
670     NSURLResponse *response;
671     if (![[self _bridge] getData:&data andResponse:&response forURL:URL])
672         return nil;
673
674     return [[[WebResource alloc] _initWithData:data URL:URL response:response] autorelease];
675 }
676
677 - (void)addSubresource:(WebResource *)subresource
678 {
679     if (subresource) {
680         if (!_private->unarchivingState)
681             _private->unarchivingState = [[WebUnarchivingState alloc] init];
682         [_private->unarchivingState addResource:subresource];
683     }
684 }
685
686 @end