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>
40 #import <WebKit/DOMHTML.h>
42 @implementation WebFrameLoader
44 - (id)initWithWebFrame:(WebFrame *)wf
49 state = WebFrameStateCommittedPage;
56 // FIXME: should these even exist?
57 [mainResourceLoader release];
58 [subresourceLoaders release];
59 [plugInStreamLoaders release];
62 [provisionalDataSource release];
69 return iconLoader != nil;
72 - (void)loadIconWithRequest:(NSURLRequest *)request
75 iconLoader = [[WebIconLoader alloc] initWithRequest:request];
76 [iconLoader setFrameLoader:self];
77 [iconLoader loadWithRequest:request];
80 - (void)stopLoadingIcon
82 [iconLoader stopLoading];
85 - (void)addPlugInStreamLoader:(WebLoader *)loader
87 if (!plugInStreamLoaders)
88 plugInStreamLoaders = [[NSMutableArray alloc] init];
89 [plugInStreamLoaders addObject:loader];
92 - (void)removePlugInStreamLoader:(WebLoader *)loader
94 [plugInStreamLoaders removeObject:loader];
97 - (void)setDefersCallbacks:(BOOL)defers
99 [mainResourceLoader setDefersCallbacks:defers];
101 NSEnumerator *e = [subresourceLoaders objectEnumerator];
103 while ((loader = [e nextObject]))
104 [loader setDefersCallbacks:defers];
106 e = [plugInStreamLoaders objectEnumerator];
107 while ((loader = [e nextObject]))
108 [loader setDefersCallbacks:defers];
110 [self deliverArchivedResourcesAfterDelay];
113 - (void)stopLoadingPlugIns
115 [plugInStreamLoaders makeObjectsPerformSelector:@selector(cancel)];
116 [plugInStreamLoaders removeAllObjects];
119 - (BOOL)isLoadingMainResource
121 return mainResourceLoader != nil;
124 - (BOOL)isLoadingSubresources
126 return [subresourceLoaders count];
129 - (BOOL)isLoadingPlugIns
131 return [plugInStreamLoaders count];
136 return [self isLoadingMainResource] || [self isLoadingSubresources] || [self isLoadingPlugIns];
139 - (void)stopLoadingSubresources
141 NSArray *loaders = [subresourceLoaders copy];
142 [loaders makeObjectsPerformSelector:@selector(cancel)];
144 [subresourceLoaders removeAllObjects];
147 - (void)addSubresourceLoader:(WebLoader *)loader
149 if (subresourceLoaders == nil)
150 subresourceLoaders = [[NSMutableArray alloc] init];
151 [subresourceLoaders addObject:loader];
154 - (void)removeSubresourceLoader:(WebLoader *)loader
156 [subresourceLoaders removeObject:loader];
159 - (NSData *)mainResourceData
161 return [mainResourceLoader resourceData];
164 - (void)releaseMainResourceLoader
166 [mainResourceLoader release];
167 mainResourceLoader = nil;
170 - (void)cancelMainResourceLoad
172 [mainResourceLoader cancel];
175 - (BOOL)startLoadingMainResourceWithRequest:(NSMutableURLRequest *)request identifier:(id)identifier
177 mainResourceLoader = [[WebMainResourceLoader alloc] initWithFrameLoader:self];
179 [mainResourceLoader setIdentifier:identifier];
180 [[provisionalDataSource webFrame] _addExtraFieldsToRequest:request mainResource:YES alwaysFromRequest:NO];
181 if (![mainResourceLoader loadWithRequest:request]) {
182 // FIXME: if this should really be caught, we should just ASSERT this doesn't happen;
183 // should it be caught by other parts of WebKit or other parts of the app?
184 LOG_ERROR("could not create WebResourceHandle for URL %@ -- should be caught by policy handler level", [request URL]);
185 [mainResourceLoader release];
186 mainResourceLoader = nil;
193 - (void)stopLoadingWithError:(NSError *)error
195 [mainResourceLoader cancelWithError:error];
198 - (WebDataSource *)dataSource
203 - (void)_setDataSource:(WebDataSource *)ds
205 if (ds == nil && dataSource == nil)
208 ASSERT(ds != dataSource);
210 [webFrame _prepareForDataSourceReplacement];
211 [dataSource _setWebFrame:nil];
214 [dataSource release];
217 [ds _setWebFrame:webFrame];
220 - (void)clearDataSource
222 [self _setDataSource:nil];
225 - (WebDataSource *)provisionalDataSource
227 return provisionalDataSource;
230 - (void)_setProvisionalDataSource: (WebDataSource *)d
232 ASSERT(!d || !provisionalDataSource);
234 if (provisionalDataSource != dataSource)
235 [provisionalDataSource _setWebFrame:nil];
238 [provisionalDataSource release];
239 provisionalDataSource = d;
241 [d _setWebFrame:webFrame];
244 - (void)_clearProvisionalDataSource
246 [self _setProvisionalDataSource:nil];
249 - (WebFrameState)state
255 static const char * const stateNames[] = {
256 "WebFrameStateProvisional",
257 "WebFrameStateCommittedPage",
258 "WebFrameStateComplete"
262 static CFAbsoluteTime _timeOfLastCompletedLoad;
264 + (CFAbsoluteTime)timeOfLastCompletedLoad
266 return _timeOfLastCompletedLoad;
269 - (void)_setState:(WebFrameState)newState
271 LOG(Loading, "%@: transition from %s to %s", [webFrame name], stateNames[state], stateNames[newState]);
272 if ([webFrame webView])
273 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]);
275 if (newState == WebFrameStateComplete && webFrame == [[webFrame webView] mainFrame])
276 LOG(DocumentLoad, "completed %@ (%f seconds)", [[[self dataSource] request] URL], CFAbsoluteTimeGetCurrent() - [[self dataSource] _loadingStartedTime]);
280 if (state == WebFrameStateProvisional)
281 [webFrame _provisionalLoadStarted];
282 else if (state == WebFrameStateComplete) {
283 [webFrame _frameLoadCompleted];
284 _timeOfLastCompletedLoad = CFAbsoluteTimeGetCurrent();
285 [dataSource _stopRecordingResponses];
289 - (void)clearProvisionalLoad
291 [self _setProvisionalDataSource:nil];
292 [[webFrame webView] _progressCompleted:webFrame];
293 [self _setState:WebFrameStateComplete];
296 - (void)markLoadComplete
298 [self _setState:WebFrameStateComplete];
301 - (void)clearIconLoader
303 [iconLoader release];
307 - (void)commitProvisionalLoad
309 [self stopLoadingSubresources];
310 [self stopLoadingPlugIns];
311 [self clearIconLoader];
313 [self _setDataSource:provisionalDataSource];
314 [self _setProvisionalDataSource:nil];
315 [self _setState:WebFrameStateCommittedPage];
320 [provisionalDataSource _stopLoading];
321 [dataSource _stopLoading];
322 [self _clearProvisionalDataSource];
323 [self clearArchivedResources];
326 // FIXME: poor method name; also why is this not part of startProvisionalLoad:?
329 [provisionalDataSource _startLoading];
332 - (void)startProvisionalLoad:(WebDataSource *)ds
334 [self _setProvisionalDataSource:ds];
335 [self _setState:WebFrameStateProvisional];
338 - (void)setupForReplace
340 [self _setState:WebFrameStateProvisional];
341 WebDataSource *old = provisionalDataSource;
342 provisionalDataSource = dataSource;
346 [webFrame _detachChildren];
349 - (WebDataSource *)activeDataSource
351 if (state == WebFrameStateProvisional)
352 return provisionalDataSource;
357 - (WebResource *)_archivedSubresourceForURL:(NSURL *)URL
359 return [[self activeDataSource] _archivedSubresourceForURL:URL];
362 - (BOOL)_defersCallbacks
364 return [[self activeDataSource] _defersCallbacks];
367 - (id)_identifierForInitialRequest:(NSURLRequest *)clientRequest
369 return [[self activeDataSource] _identifierForInitialRequest:clientRequest];
372 - (NSURLRequest *)_willSendRequest:(NSMutableURLRequest *)clientRequest forResource:(id)identifier redirectResponse:(NSURLResponse *)redirectResponse
374 return [[self activeDataSource] _willSendRequest:clientRequest forResource:identifier redirectResponse:redirectResponse];
377 - (void)_didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)currentWebChallenge forResource:(id)identifier
379 return [[self activeDataSource] _didReceiveAuthenticationChallenge:currentWebChallenge forResource:identifier];
382 - (void)_didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)currentWebChallenge forResource:(id)identifier
384 return [[self activeDataSource] _didCancelAuthenticationChallenge:currentWebChallenge forResource:identifier];
387 - (void)_didReceiveResponse:(NSURLResponse *)r forResource:(id)identifier
389 return [[self activeDataSource] _didReceiveResponse:r forResource:identifier];
392 - (void)_didReceiveData:(NSData *)data contentLength:(int)lengthReceived forResource:(id)identifier
394 return [[self activeDataSource] _didReceiveData:data contentLength:lengthReceived forResource:identifier];
397 - (void)_didFinishLoadingForResource:(id)identifier
399 return [[self activeDataSource] _didFinishLoadingForResource:identifier];
402 - (void)_didFailLoadingWithError:(NSError *)error forResource:(id)identifier
404 return [[self activeDataSource] _didFailLoadingWithError:error forResource:identifier];
407 - (BOOL)_privateBrowsingEnabled
409 return [[self activeDataSource] _privateBrowsingEnabled];
412 - (void)_addPlugInStreamLoader:(WebLoader *)loader
414 return [[self activeDataSource] _addPlugInStreamLoader:loader];
417 - (void)_removePlugInStreamLoader:(WebLoader *)loader
419 return [[self activeDataSource] _removePlugInStreamLoader:loader];
422 - (void)_finishedLoadingResource
424 return [[self activeDataSource] _finishedLoadingResource];
427 - (void)_receivedError:(NSError *)error
429 return [[self activeDataSource] _receivedError:error];
432 - (void)_addSubresourceLoader:(WebLoader *)loader
434 return [[self activeDataSource] _addSubresourceLoader:loader];
437 - (void)_removeSubresourceLoader:(WebLoader *)loader
439 return [[self activeDataSource] _removeSubresourceLoader:loader];
442 - (NSURLRequest *)_originalRequest
444 return [[self activeDataSource] _originalRequest];
447 - (WebFrame *)webFrame
449 return [[self activeDataSource] webFrame];
452 - (void)_receivedMainResourceError:(NSError *)error complete:(BOOL)isComplete
454 WebDataSource *ds = [self activeDataSource];
456 [ds _receivedMainResourceError:error complete:isComplete];
460 - (NSURLRequest *)initialRequest
462 return [[self activeDataSource] initialRequest];
465 - (void)_receivedData:(NSData *)data
467 [[self activeDataSource] _receivedData:data];
470 - (void)_setRequest:(NSURLRequest *)request
472 [[self activeDataSource] _setRequest:request];
475 - (void)_downloadWithLoadingConnection:(NSURLConnection *)connection request:(NSURLRequest *)request response:(NSURLResponse *)r proxy:(WKNSURLConnectionDelegateProxyPtr)proxy
477 [[self activeDataSource] _downloadWithLoadingConnection:connection request:request response:r proxy:proxy];
480 - (void)_handleFallbackContent
482 [[self activeDataSource] _handleFallbackContent];
487 return [[self activeDataSource] _isStopping];
490 - (void)_decidePolicyForMIMEType:(NSString *)MIMEType decisionListener:(WebPolicyDecisionListener *)listener
492 [[self activeDataSource] _decidePolicyForMIMEType:MIMEType decisionListener:listener];
495 - (void)_setupForReplaceByMIMEType:(NSString *)newMIMEType
497 [[self activeDataSource] _setupForReplaceByMIMEType:newMIMEType];
500 - (void)_setResponse:(NSURLResponse *)response
502 [[self activeDataSource] _setResponse:response];
505 - (void)_mainReceivedError:(NSError *)error complete:(BOOL)isComplete
507 [[self activeDataSource] _mainReceivedError:error complete:isComplete];
510 - (void)_finishedLoading
512 [[self activeDataSource] _finishedLoading];
515 - (void)_mainReceivedBytesSoFar:(unsigned)bytesSoFar complete:(BOOL)isComplete
517 [[self activeDataSource] _mainReceivedBytesSoFar:bytesSoFar complete:isComplete];
520 - (void)_iconLoaderReceivedPageIcon:(WebIconLoader *)iLoader
522 ASSERT(iLoader == iconLoader);
523 [[self activeDataSource] _iconLoaderReceivedPageIcon:iLoader];
528 return [[self activeDataSource] _URL];
531 - (NSError *)cancelledErrorWithRequest:(NSURLRequest *)request
533 return [NSError _webKitErrorWithDomain:NSURLErrorDomain
534 code:NSURLErrorCancelled
538 - (NSError *)fileDoesNotExistErrorWithResponse:(NSURLResponse *)response
540 return [NSError _webKitErrorWithDomain:NSURLErrorDomain
541 code:NSURLErrorFileDoesNotExist
545 - (void)clearArchivedResources
547 [pendingArchivedResources removeAllObjects];
548 [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(deliverArchivedResources) object:nil];
551 - (void)deliverArchivedResources
553 if (![pendingArchivedResources count] || [self _defersCallbacks])
556 NSEnumerator *keyEnum = [pendingArchivedResources keyEnumerator];
558 while ((loader = [keyEnum nextObject])) {
559 WebResource *resource = [pendingArchivedResources objectForKey:loader];
560 [loader didReceiveResponse:[resource _response]];
561 NSData *data = [resource data];
562 [loader didReceiveData:data lengthReceived:[data length] allAtOnce:YES];
563 [loader didFinishLoading];
566 [pendingArchivedResources removeAllObjects];
569 - (void)deliverArchivedResourcesAfterDelay
571 if (![pendingArchivedResources count] || [self _defersCallbacks])
574 [self performSelector:@selector(deliverArchivedResources) withObject:nil afterDelay:0];
577 static BOOL isCaseInsensitiveEqual(NSString *a, NSString *b)
579 return [a caseInsensitiveCompare:b] == NSOrderedSame;
582 // The following 2 methods are copied from [NSHTTPURLProtocol _cachedResponsePassesValidityChecks] and modified for our needs.
583 // FIXME: It would be nice to eventually to share this code somehow.
584 - (BOOL)_canUseResourceForRequest:(NSURLRequest *)theRequest
586 NSURLRequestCachePolicy policy = [theRequest cachePolicy];
588 if (policy == NSURLRequestReturnCacheDataElseLoad) {
590 } else if (policy == NSURLRequestReturnCacheDataDontLoad) {
592 } else if (policy == NSURLRequestReloadIgnoringCacheData) {
594 } else if ([theRequest valueForHTTPHeaderField:@"must-revalidate"] != nil) {
596 } else if ([theRequest valueForHTTPHeaderField:@"proxy-revalidate"] != nil) {
598 } else if ([theRequest valueForHTTPHeaderField:@"If-Modified-Since"] != nil) {
600 } else if ([theRequest valueForHTTPHeaderField:@"Cache-Control"] != nil) {
602 } else if (isCaseInsensitiveEqual(@"POST", [theRequest HTTPMethod])) {
609 - (BOOL)_canUseResourceWithResponse:(NSURLResponse *)theResponse
611 if (WKGetNSURLResponseMustRevalidate(theResponse)) {
613 } else if (WKGetNSURLResponseCalculatedExpiration(theResponse) - CFAbsoluteTimeGetCurrent() < 1) {
620 - (NSMutableDictionary *)pendingArchivedResources
622 if (!pendingArchivedResources)
623 pendingArchivedResources = [[NSMutableDictionary alloc] init];
625 return pendingArchivedResources;
628 - (BOOL)willUseArchiveForRequest:(NSURLRequest *)r originalURL:(NSURL *)originalURL loader:(WebLoader *)loader
630 if ([[r URL] isEqual:originalURL] && [self _canUseResourceForRequest:r]) {
631 WebResource *resource = [self _archivedSubresourceForURL:originalURL];
632 if (resource && [self _canUseResourceWithResponse:[resource _response]]) {
633 [[self pendingArchivedResources] setObject:resource forKey:loader];
634 // Deliver the resource after a delay because callers don't expect to receive callbacks while calling this method.
635 [self deliverArchivedResourcesAfterDelay];
642 - (BOOL)archiveLoadPendingForLoader:(WebLoader *)loader
644 return [pendingArchivedResources objectForKey:loader] != nil;
647 - (void)cancelPendingArchiveLoadForLoader:(WebLoader *)loader
649 [pendingArchivedResources removeObjectForKey:loader];
651 if (![pendingArchivedResources count])
652 [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(deliverArchivedResources) object:nil];
655 - (void)_addExtraFieldsToRequest:(NSMutableURLRequest *)request mainResource:(BOOL)mainResource alwaysFromRequest:(BOOL)f
657 [webFrame _addExtraFieldsToRequest:request mainResource:mainResource alwaysFromRequest:f];
660 - (void)cannotShowMIMETypeForURL:(NSURL *)URL
662 [webFrame _handleUnimplementablePolicyWithErrorCode:WebKitErrorCannotShowMIMEType forURL:URL];
665 - (NSError *)interruptForPolicyChangeErrorWithRequest:(NSURLRequest *)request
667 return [NSError _webKitErrorWithDomain:WebKitErrorDomain code:WebKitErrorFrameLoadInterruptedByPolicyChange URL:[request URL]];
670 - (BOOL)isHostedByObjectElement
672 // Handle <object> fallback for error cases.
673 DOMHTMLElement *hostElement = [webFrame frameElement];
674 return hostElement && [hostElement isKindOfClass:[DOMHTMLObjectElement class]];
677 - (BOOL)isLoadingMainFrame
679 return [webFrame _isMainFrame];
682 + (BOOL)_canShowMIMEType:(NSString *)MIMEType
684 return [WebDataSource _canShowMIMEType:MIMEType];
687 + (BOOL)_representationExistsForURLScheme:(NSString *)URLScheme
689 return [WebDataSource _representationExistsForURLScheme:URLScheme];
692 + (NSString *)_generatedMIMETypeForURLScheme:(NSString *)URLScheme
694 return [WebDataSource _generatedMIMETypeForURLScheme:URLScheme];