2 * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
29 #import <WebKit/WebFrameLoader.h>
31 #import <JavaScriptCore/Assertions.h>
32 #import <WebKit/WebDataSourceInternal.h>
33 #import <WebKit/WebFrameInternal.h>
34 #import <WebKit/WebIconLoader.h>
35 #import <WebKit/WebMainResourceLoader.h>
36 #import <WebKit/WebKitLogging.h>
37 #import <WebKit/WebViewInternal.h>
38 #import <WebKit/WebKitErrorsPrivate.h>
39 #import <WebKit/WebResourcePrivate.h>
41 @implementation WebFrameLoader
43 - (id)initWithWebFrame:(WebFrame *)wf
48 state = WebFrameStateCommittedPage;
55 // FIXME: should these even exist?
56 [mainResourceLoader release];
57 [subresourceLoaders release];
58 [plugInStreamLoaders release];
61 [provisionalDataSource release];
68 return iconLoader != nil;
71 - (void)loadIconWithRequest:(NSURLRequest *)request
74 iconLoader = [[WebIconLoader alloc] initWithRequest:request];
75 [iconLoader setFrameLoader:self];
76 [iconLoader loadWithRequest:request];
79 - (void)stopLoadingIcon
81 [iconLoader stopLoading];
84 - (void)addPlugInStreamLoader:(WebLoader *)loader
86 if (!plugInStreamLoaders)
87 plugInStreamLoaders = [[NSMutableArray alloc] init];
88 [plugInStreamLoaders addObject:loader];
91 - (void)removePlugInStreamLoader:(WebLoader *)loader
93 [plugInStreamLoaders removeObject:loader];
96 - (void)setDefersCallbacks:(BOOL)defers
98 [mainResourceLoader setDefersCallbacks:defers];
100 NSEnumerator *e = [subresourceLoaders objectEnumerator];
102 while ((loader = [e nextObject]))
103 [loader setDefersCallbacks:defers];
105 e = [plugInStreamLoaders objectEnumerator];
106 while ((loader = [e nextObject]))
107 [loader setDefersCallbacks:defers];
109 [self deliverArchivedResourcesAfterDelay];
112 - (void)stopLoadingPlugIns
114 [plugInStreamLoaders makeObjectsPerformSelector:@selector(cancel)];
115 [plugInStreamLoaders removeAllObjects];
118 - (BOOL)isLoadingMainResource
120 return mainResourceLoader != nil;
123 - (BOOL)isLoadingSubresources
125 return [subresourceLoaders count];
128 - (BOOL)isLoadingPlugIns
130 return [plugInStreamLoaders count];
135 return [self isLoadingMainResource] || [self isLoadingSubresources] || [self isLoadingPlugIns];
138 - (void)stopLoadingSubresources
140 NSArray *loaders = [subresourceLoaders copy];
141 [loaders makeObjectsPerformSelector:@selector(cancel)];
143 [subresourceLoaders removeAllObjects];
146 - (void)addSubresourceLoader:(WebLoader *)loader
148 if (subresourceLoaders == nil)
149 subresourceLoaders = [[NSMutableArray alloc] init];
150 [subresourceLoaders addObject:loader];
153 - (void)removeSubresourceLoader:(WebLoader *)loader
155 [subresourceLoaders removeObject:loader];
158 - (NSData *)mainResourceData
160 return [mainResourceLoader resourceData];
163 - (void)releaseMainResourceLoader
165 [mainResourceLoader release];
166 mainResourceLoader = nil;
169 - (void)cancelMainResourceLoad
171 [mainResourceLoader cancel];
174 - (BOOL)startLoadingMainResourceWithRequest:(NSMutableURLRequest *)request identifier:(id)identifier
176 mainResourceLoader = [[WebMainResourceLoader alloc] initWithFrameLoader:self];
178 [mainResourceLoader setIdentifier:identifier];
179 [[provisionalDataSource webFrame] _addExtraFieldsToRequest:request mainResource:YES alwaysFromRequest:NO];
180 if (![mainResourceLoader loadWithRequest:request]) {
181 // FIXME: if this should really be caught, we should just ASSERT this doesn't happen;
182 // should it be caught by other parts of WebKit or other parts of the app?
183 LOG_ERROR("could not create WebResourceHandle for URL %@ -- should be caught by policy handler level", [request URL]);
184 [mainResourceLoader release];
185 mainResourceLoader = nil;
192 - (void)stopLoadingWithError:(NSError *)error
194 [mainResourceLoader cancelWithError:error];
197 - (WebDataSource *)dataSource
202 - (void)_setDataSource:(WebDataSource *)ds
204 if (ds == nil && dataSource == nil)
207 ASSERT(ds != dataSource);
209 [webFrame _prepareForDataSourceReplacement];
210 [dataSource _setWebFrame:nil];
213 [dataSource release];
216 [ds _setWebFrame:webFrame];
219 - (void)clearDataSource
221 [self _setDataSource:nil];
224 - (WebDataSource *)provisionalDataSource
226 return provisionalDataSource;
229 - (void)_setProvisionalDataSource: (WebDataSource *)d
231 ASSERT(!d || !provisionalDataSource);
233 if (provisionalDataSource != dataSource)
234 [provisionalDataSource _setWebFrame:nil];
237 [provisionalDataSource release];
238 provisionalDataSource = d;
240 [d _setWebFrame:webFrame];
243 - (void)_clearProvisionalDataSource
245 [self _setProvisionalDataSource:nil];
248 - (WebFrameState)state
254 static const char * const stateNames[] = {
255 "WebFrameStateProvisional",
256 "WebFrameStateCommittedPage",
257 "WebFrameStateComplete"
261 static CFAbsoluteTime _timeOfLastCompletedLoad;
263 + (CFAbsoluteTime)timeOfLastCompletedLoad
265 return _timeOfLastCompletedLoad;
268 - (void)_setState:(WebFrameState)newState
270 LOG(Loading, "%@: transition from %s to %s", [webFrame name], stateNames[state], stateNames[newState]);
271 if ([webFrame webView])
272 LOG(Timing, "%@: transition from %s to %s, %f seconds since start of document load", [webFrame name], stateNames[state], stateNames[newState], CFAbsoluteTimeGetCurrent() - [[[[webFrame webView] mainFrame] dataSource] _loadingStartedTime]);
274 if (newState == WebFrameStateComplete && webFrame == [[webFrame webView] mainFrame])
275 LOG(DocumentLoad, "completed %@ (%f seconds)", [[[self dataSource] request] URL], CFAbsoluteTimeGetCurrent() - [[self dataSource] _loadingStartedTime]);
279 if (state == WebFrameStateProvisional)
280 [webFrame _provisionalLoadStarted];
281 else if (state == WebFrameStateComplete) {
282 [webFrame _frameLoadCompleted];
283 _timeOfLastCompletedLoad = CFAbsoluteTimeGetCurrent();
284 [dataSource _stopRecordingResponses];
288 - (void)clearProvisionalLoad
290 [self _setProvisionalDataSource:nil];
291 [[webFrame webView] _progressCompleted:webFrame];
292 [self _setState:WebFrameStateComplete];
295 - (void)markLoadComplete
297 [self _setState:WebFrameStateComplete];
300 - (void)clearIconLoader
302 [iconLoader release];
306 - (void)commitProvisionalLoad
308 [self stopLoadingSubresources];
309 [self stopLoadingPlugIns];
310 [self clearIconLoader];
312 [self _setDataSource:provisionalDataSource];
313 [self _setProvisionalDataSource:nil];
314 [self _setState:WebFrameStateCommittedPage];
319 [provisionalDataSource _stopLoading];
320 [dataSource _stopLoading];
321 [self _clearProvisionalDataSource];
322 [self clearArchivedResources];
325 // FIXME: poor method name; also why is this not part of startProvisionalLoad:?
328 [provisionalDataSource _startLoading];
331 - (void)startProvisionalLoad:(WebDataSource *)ds
333 [self _setProvisionalDataSource:ds];
334 [self _setState:WebFrameStateProvisional];
337 - (void)setupForReplace
339 [self _setState:WebFrameStateProvisional];
340 WebDataSource *old = provisionalDataSource;
341 provisionalDataSource = dataSource;
345 [webFrame _detachChildren];
348 - (WebDataSource *)activeDataSource
350 if (state == WebFrameStateProvisional)
351 return provisionalDataSource;
356 - (WebResource *)_archivedSubresourceForURL:(NSURL *)URL
358 return [[self activeDataSource] _archivedSubresourceForURL:URL];
361 - (BOOL)_defersCallbacks
363 return [[self activeDataSource] _defersCallbacks];
366 - (id)_identifierForInitialRequest:(NSURLRequest *)clientRequest
368 return [[self activeDataSource] _identifierForInitialRequest:clientRequest];
371 - (NSURLRequest *)_willSendRequest:(NSMutableURLRequest *)clientRequest forResource:(id)identifier redirectResponse:(NSURLResponse *)redirectResponse
373 return [[self activeDataSource] _willSendRequest:clientRequest forResource:identifier redirectResponse:redirectResponse];
376 - (void)_didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)currentWebChallenge forResource:(id)identifier
378 return [[self activeDataSource] _didReceiveAuthenticationChallenge:currentWebChallenge forResource:identifier];
381 - (void)_didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)currentWebChallenge forResource:(id)identifier
383 return [[self activeDataSource] _didCancelAuthenticationChallenge:currentWebChallenge forResource:identifier];
386 - (void)_didReceiveResponse:(NSURLResponse *)r forResource:(id)identifier
388 return [[self activeDataSource] _didReceiveResponse:r forResource:identifier];
391 - (void)_didReceiveData:(NSData *)data contentLength:(int)lengthReceived forResource:(id)identifier
393 return [[self activeDataSource] _didReceiveData:data contentLength:lengthReceived forResource:identifier];
396 - (void)_didFinishLoadingForResource:(id)identifier
398 return [[self activeDataSource] _didFinishLoadingForResource:identifier];
401 - (void)_didFailLoadingWithError:(NSError *)error forResource:(id)identifier
403 return [[self activeDataSource] _didFailLoadingWithError:error forResource:identifier];
406 - (BOOL)_privateBrowsingEnabled
408 return [[self activeDataSource] _privateBrowsingEnabled];
411 - (void)_addPlugInStreamLoader:(WebLoader *)loader
413 return [[self activeDataSource] _addPlugInStreamLoader:loader];
416 - (void)_removePlugInStreamLoader:(WebLoader *)loader
418 return [[self activeDataSource] _removePlugInStreamLoader:loader];
421 - (void)_finishedLoadingResource
423 return [[self activeDataSource] _finishedLoadingResource];
426 - (void)_receivedError:(NSError *)error
428 return [[self activeDataSource] _receivedError:error];
431 - (void)_addSubresourceLoader:(WebLoader *)loader
433 return [[self activeDataSource] _addSubresourceLoader:loader];
436 - (void)_removeSubresourceLoader:(WebLoader *)loader
438 return [[self activeDataSource] _removeSubresourceLoader:loader];
441 - (NSURLRequest *)_originalRequest
443 return [[self activeDataSource] _originalRequest];
446 - (WebFrame *)webFrame
448 return [[self activeDataSource] webFrame];
451 - (void)_receivedMainResourceError:(NSError *)error complete:(BOOL)isComplete
453 WebDataSource *ds = [self activeDataSource];
455 [ds _receivedMainResourceError:error complete:isComplete];
459 - (NSURLRequest *)initialRequest
461 return [[self activeDataSource] initialRequest];
464 - (void)_receivedData:(NSData *)data
466 [[self activeDataSource] _receivedData:data];
469 - (void)_setRequest:(NSURLRequest *)request
471 [[self activeDataSource] _setRequest:request];
474 - (void)_downloadWithLoadingConnection:(NSURLConnection *)connection request:(NSURLRequest *)request response:(NSURLResponse *)r proxy:(WKNSURLConnectionDelegateProxyPtr)proxy
476 [[self activeDataSource] _downloadWithLoadingConnection:connection request:request response:r proxy:proxy];
479 - (void)_handleFallbackContent
481 [[self activeDataSource] _handleFallbackContent];
486 return [[self activeDataSource] _isStopping];
489 - (void)_decidePolicyForMIMEType:(NSString *)MIMEType decisionListener:(WebPolicyDecisionListener *)listener
491 [[self activeDataSource] _decidePolicyForMIMEType:MIMEType decisionListener:listener];
494 - (void)_setupForReplaceByMIMEType:(NSString *)newMIMEType
496 [[self activeDataSource] _setupForReplaceByMIMEType:newMIMEType];
499 - (void)_setResponse:(NSURLResponse *)response
501 [[self activeDataSource] _setResponse:response];
504 - (void)_mainReceivedError:(NSError *)error complete:(BOOL)isComplete
506 [[self activeDataSource] _mainReceivedError:error complete:isComplete];
509 - (void)_finishedLoading
511 [[self activeDataSource] _finishedLoading];
514 - (void)_mainReceivedBytesSoFar:(unsigned)bytesSoFar complete:(BOOL)isComplete
516 [[self activeDataSource] _mainReceivedBytesSoFar:bytesSoFar complete:isComplete];
519 - (void)_iconLoaderReceivedPageIcon:(WebIconLoader *)iLoader
521 ASSERT(iLoader == iconLoader);
522 [[self activeDataSource] _iconLoaderReceivedPageIcon:iLoader];
527 return [[self activeDataSource] _URL];
530 - (NSError *)cancelledErrorWithRequest:(NSURLRequest *)request
532 return [NSError _webKitErrorWithDomain:NSURLErrorDomain
533 code:NSURLErrorCancelled
537 - (NSError *)fileDoesNotExistErrorWithResponse:(NSURLResponse *)response
539 return [NSError _webKitErrorWithDomain:NSURLErrorDomain
540 code:NSURLErrorFileDoesNotExist
544 - (void)clearArchivedResources
546 [pendingArchivedResources removeAllObjects];
547 [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(deliverArchivedResources) object:nil];
550 - (void)deliverArchivedResources
552 if (![pendingArchivedResources count] || [self _defersCallbacks])
555 NSEnumerator *keyEnum = [pendingArchivedResources keyEnumerator];
557 while ((loader = [keyEnum nextObject])) {
558 WebResource *resource = [pendingArchivedResources objectForKey:loader];
559 [loader didReceiveResponse:[resource _response]];
560 NSData *data = [resource data];
561 [loader didReceiveData:data lengthReceived:[data length] allAtOnce:YES];
562 [loader didFinishLoading];
565 [pendingArchivedResources removeAllObjects];
568 - (void)deliverArchivedResourcesAfterDelay
570 if (![pendingArchivedResources count] || [self _defersCallbacks])
573 [self performSelector:@selector(deliverArchivedResources) withObject:nil afterDelay:0];
576 static BOOL isCaseInsensitiveEqual(NSString *a, NSString *b)
578 return [a caseInsensitiveCompare:b] == NSOrderedSame;
581 // The following 2 methods are copied from [NSHTTPURLProtocol _cachedResponsePassesValidityChecks] and modified for our needs.
582 // FIXME: It would be nice to eventually to share this code somehow.
583 - (BOOL)_canUseResourceForRequest:(NSURLRequest *)theRequest
585 NSURLRequestCachePolicy policy = [theRequest cachePolicy];
587 if (policy == NSURLRequestReturnCacheDataElseLoad) {
589 } else if (policy == NSURLRequestReturnCacheDataDontLoad) {
591 } else if (policy == NSURLRequestReloadIgnoringCacheData) {
593 } else if ([theRequest valueForHTTPHeaderField:@"must-revalidate"] != nil) {
595 } else if ([theRequest valueForHTTPHeaderField:@"proxy-revalidate"] != nil) {
597 } else if ([theRequest valueForHTTPHeaderField:@"If-Modified-Since"] != nil) {
599 } else if ([theRequest valueForHTTPHeaderField:@"Cache-Control"] != nil) {
601 } else if (isCaseInsensitiveEqual(@"POST", [theRequest HTTPMethod])) {
608 - (BOOL)_canUseResourceWithResponse:(NSURLResponse *)theResponse
610 if (WKGetNSURLResponseMustRevalidate(theResponse)) {
612 } else if (WKGetNSURLResponseCalculatedExpiration(theResponse) - CFAbsoluteTimeGetCurrent() < 1) {
619 - (NSMutableDictionary *)pendingArchivedResources
621 if (!pendingArchivedResources)
622 pendingArchivedResources = [[NSMutableDictionary alloc] init];
624 return pendingArchivedResources;
627 - (BOOL)willUseArchiveForRequest:(NSURLRequest *)r originalURL:(NSURL *)originalURL loader:(WebLoader *)loader
629 if ([[r URL] isEqual:originalURL] && [self _canUseResourceForRequest:r]) {
630 WebResource *resource = [self _archivedSubresourceForURL:originalURL];
631 if (resource && [self _canUseResourceWithResponse:[resource _response]]) {
632 [[self pendingArchivedResources] setObject:resource forKey:loader];
633 // Deliver the resource after a delay because callers don't expect to receive callbacks while calling this method.
634 [self deliverArchivedResourcesAfterDelay];
641 - (BOOL)archiveLoadPendingForLoader:(WebLoader *)loader
643 return [pendingArchivedResources objectForKey:loader] != nil;
646 - (void)cancelPendingArchiveLoadForLoader:(WebLoader *)loader
648 [pendingArchivedResources removeObjectForKey:loader];
650 if (![pendingArchivedResources count])
651 [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(deliverArchivedResources) object:nil];
654 - (void)_addExtraFieldsToRequest:(NSMutableURLRequest *)request mainResource:(BOOL)mainResource alwaysFromRequest:(BOOL)f
656 [webFrame _addExtraFieldsToRequest:request mainResource:mainResource alwaysFromRequest:f];