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 "WebDocumentLoader.h"
31 #import <JavaScriptCore/Assertions.h>
32 #import "WebFrameLoader.h"
33 #import "WebDataProtocol.h"
34 #import "WebFrameBridge.h"
35 #import <WebCore/WebCoreSystemInterface.h>
37 @implementation WebDocumentLoader
39 - (id)initWithRequest:(NSURLRequest *)req
45 originalRequest = [req retain];
46 originalRequestCopy = [originalRequest copy];
47 request = [originalRequest mutableCopy];
48 wkSupportsMultipartXMixedReplace(request);
55 ASSERT([frameLoader activeDocumentLoader] != self || ![frameLoader isLoading]);
58 [mainResourceData release];
59 [originalRequest release];
60 [originalRequestCopy release];
63 [mainDocumentError release];
65 [triggeringAction release];
66 [lastCheckedRequest release];
73 - (void)setFrameLoader:(WebFrameLoader *)fl
81 - (WebFrameLoader *)frameLoader
86 - (void)setMainResourceData:(NSData *)data
89 [mainResourceData release];
90 mainResourceData = data;
93 - (NSData *)mainResourceData
95 return mainResourceData != nil ? mainResourceData : [frameLoader mainResourceData];
98 - (NSURLRequest *)originalRequest
100 return originalRequest;
103 - (NSURLRequest *)originalRequestCopy
105 return originalRequestCopy;
108 - (NSMutableURLRequest *)request
110 NSMutableURLRequest *clientRequest = [request _webDataRequestExternalRequest];
112 clientRequest = request;
113 return clientRequest;
116 - (NSURLRequest *)initialRequest
118 NSURLRequest *clientRequest = [[self originalRequest] _webDataRequestExternalRequest];
120 clientRequest = [self originalRequest];
121 return clientRequest;
124 - (NSMutableURLRequest *)actualRequest
131 return [[self request] URL];
134 - (NSURL *)unreachableURL
136 return [[self originalRequest] _webDataRequestUnreachableURL];
139 - (void)replaceRequestURLForAnchorScrollWithURL:(NSURL *)URL
141 // assert that URLs differ only by fragment ID
143 NSMutableURLRequest *newOriginalRequest = [originalRequestCopy mutableCopy];
144 [originalRequestCopy release];
145 [newOriginalRequest setURL:URL];
146 originalRequestCopy = newOriginalRequest;
148 NSMutableURLRequest *newRequest = [request mutableCopy];
150 [newRequest setURL:URL];
151 request = newRequest;
154 - (void)setRequest:(NSURLRequest *)req
156 ASSERT_ARG(req, req != request);
158 // Replacing an unreachable URL with alternate content looks like a server-side
159 // redirect at this point, but we can replace a committed dataSource.
160 BOOL handlingUnreachableURL = [req _webDataRequestUnreachableURL] != nil;
161 if (handlingUnreachableURL)
164 // We should never be getting a redirect callback after the data
165 // source is committed, except in the unreachable URL case. It
166 // would be a WebFoundation bug if it sent a redirect callback after commit.
169 NSURLRequest *oldRequest = request;
171 request = [req mutableCopy];
173 // Only send webView:didReceiveServerRedirectForProvisionalLoadForFrame: if URL changed.
174 // Also, don't send it when replacing unreachable URLs with alternate content.
175 if (!handlingUnreachableURL && ![[oldRequest URL] isEqual:[req URL]])
176 [frameLoader didReceiveServerRedirectForProvisionalLoadForFrame];
178 [oldRequest release];
181 - (void)setResponse:(NSURLResponse *)resp
193 - (WebCoreFrameBridge *)bridge
195 return [frameLoader bridge];
198 - (void)setMainDocumentError:(NSError *)error
201 [mainDocumentError release];
202 mainDocumentError = error;
204 [frameLoader documentLoader:self setMainDocumentError:error];
207 - (NSError *)mainDocumentError
209 return mainDocumentError;
214 [mainDocumentError release];
215 mainDocumentError = nil;
218 - (void)mainReceivedError:(NSError *)error complete:(BOOL)isComplete
223 [self setMainDocumentError:error];
226 [frameLoader documentLoader:self mainReceivedCompleteError:error];
230 // Cancels the data source's pending loads. Conceptually, a data source only loads
231 // one document at a time, but one document may have many related resources.
232 // stopLoading will stop all loads initiated by the data source,
233 // but not loads initiated by child frames' data sources -- that's the WebFrame's job.
236 // Always attempt to stop the bridge/part because it may still be loading/parsing after the data source
237 // is done loading and not stopping it can cause a world leak.
239 [[self bridge] stopLoading];
248 if ([frameLoader isLoadingMainResource]) {
249 // Stop the main resource loader and let it send the cancelled message.
250 [frameLoader cancelMainResourceLoad];
251 } else if ([frameLoader isLoadingSubresources]) {
252 // The main resource loader already finished loading. Set the cancelled error on the
253 // document and let the subresourceLoaders send individual cancelled messages below.
254 [self setMainDocumentError:[frameLoader cancelledErrorWithRequest:request]];
256 // If there are no resource loaders, we need to manufacture a cancelled message.
257 // (A back/forward navigation has no resource loaders because its resources are cached.)
258 [self mainReceivedError:[frameLoader cancelledErrorWithRequest:request] complete:YES];
261 [frameLoader stopLoadingSubresources];
262 [frameLoader stopLoadingPlugIns];
269 - (void)setupForReplace
271 [frameLoader setupForReplace];
275 - (void)commitIfReady
277 if (gotFirstByte && !committed) {
279 [frameLoader commitProvisionalLoad:nil];
283 - (void)finishedLoading
286 [self commitIfReady];
287 [frameLoader finishedLoadingDocument:self];
291 - (void)setCommitted:(BOOL)f
301 - (void)setLoading:(BOOL)f
311 - (void)commitLoadWithData:(NSData *)data
313 // Both unloading the old page and parsing the new page may execute JavaScript which destroys the datasource
314 // by starting a new load, so retain temporarily.
316 [self commitIfReady];
318 [frameLoader committedLoadWithDocumentLoader:self data:data];
323 - (BOOL)doesProgressiveLoadWithMIMEType:(NSString *)MIMEType
325 return ![frameLoader isReplacing] || [MIMEType isEqualToString:@"text/html"];
328 - (void)receivedData:(NSData *)data
332 if ([self doesProgressiveLoadWithMIMEType:[response MIMEType]])
333 [self commitLoadWithData:data];
336 - (void)setupForReplaceByMIMEType:(NSString *)newMIMEType
341 NSString *oldMIMEType = [response MIMEType];
343 if (![self doesProgressiveLoadWithMIMEType:oldMIMEType]) {
344 [frameLoader revertToProvisionalWithDocumentLoader:self];
345 [self setupForReplace];
346 [self commitLoadWithData:[self mainResourceData]];
349 [frameLoader finishedLoadingDocument:self];
352 [frameLoader setReplacing];
355 if ([self doesProgressiveLoadWithMIMEType:newMIMEType]) {
356 [frameLoader revertToProvisionalWithDocumentLoader:self];
357 [self setupForReplace];
360 [frameLoader stopLoadingSubresources];
361 [frameLoader stopLoadingPlugIns];
363 [frameLoader finalSetupForReplaceWithDocumentLoader:self];
366 - (void)updateLoading
368 ASSERT(self == [frameLoader activeDocumentLoader]);
370 [self setLoading:[frameLoader isLoading]];
373 - (NSURLResponse *)response
378 - (void)detachFromFrameLoader
383 - (void)prepareForLoadStart
386 [self setPrimaryLoadComplete:NO];
387 ASSERT(frameLoader != nil);
390 // Mark the start loading time.
391 loadingStartedTime = CFAbsoluteTimeGetCurrent();
393 [self setLoading:YES];
395 [frameLoader prepareForLoadStart];
398 - (double)loadingStartedTime
400 return loadingStartedTime;
403 - (void)setIsClientRedirect:(BOOL)flag
405 isClientRedirect = flag;
408 - (BOOL)isClientRedirect
410 return isClientRedirect;
413 - (void)setPrimaryLoadComplete:(BOOL)flag
415 primaryLoadComplete = flag;
418 if ([frameLoader isLoadingMainResource]) {
419 [self setMainResourceData:[frameLoader mainResourceData]];
420 [frameLoader releaseMainResourceLoader];
423 [self updateLoading];
427 - (BOOL)isLoadingInAPISense
429 // Once a frame has loaded, we no longer need to consider subresources,
430 // but we still need to consider subframes.
431 if ([frameLoader state] != WebFrameStateComplete) {
432 if (!primaryLoadComplete && [self isLoading])
434 if ([frameLoader isLoadingSubresources])
436 if (![[frameLoader bridge] doneProcessingData])
440 return [frameLoader subframeIsLoading];
443 - (void)addResponse:(NSURLResponse *)r
445 if (!stopRecordingResponses) {
447 responses = [[NSMutableArray alloc] init];
448 [responses addObject: r];
452 - (void)stopRecordingResponses
454 stopRecordingResponses = YES;
462 - (void)setLastCheckedRequest:(NSURLRequest *)req
464 NSURLRequest *oldRequest = lastCheckedRequest;
465 lastCheckedRequest = [req copy];
466 [oldRequest release];
469 - (NSURLRequest *)lastCheckedRequest
471 // It's OK not to make a copy here because we know the caller
472 // isn't going to modify this request
473 return [[lastCheckedRequest retain] autorelease];
476 - (NSDictionary *)triggeringAction
478 return [[triggeringAction retain] autorelease];
481 - (void)setTriggeringAction:(NSDictionary *)action
484 [triggeringAction release];
485 triggeringAction = action;
488 - (NSArray *)responses
493 - (void)setOverrideEncoding:(NSString *)enc
495 NSString *copy = [enc copy];
496 [overrideEncoding release];
497 overrideEncoding = copy;
500 - (NSString *)overrideEncoding
502 return [[overrideEncoding copy] autorelease];
505 - (void)setTitle:(NSString *)title
510 NSString *trimmed = [title mutableCopy];
511 CFStringTrimWhitespace((CFMutableStringRef)trimmed);
513 if ([trimmed length] != 0 && ![pageTitle isEqualToString:trimmed]) {
514 [frameLoader willChangeTitleForDocument:self];
516 pageTitle = [trimmed copy];
517 [frameLoader didChangeTitleForDocument:self];
523 - (NSURL *)URLForHistory
525 // Return the URL to be used for history and B/F list.
526 // Returns nil for WebDataProtocol URLs that aren't alternates
527 // for unreachable URLs, because these can't be stored in history.
528 NSURL *URL = [originalRequestCopy URL];
529 if ([WebDataProtocol _webIsDataProtocolURL:URL])
530 URL = [originalRequestCopy _webDataRequestUnreachableURL];