Reviewed by Alice.
[WebKit-https.git] / WebKit / Loader / WebDocumentLoadState.m
1 /*
2  * Copyright (C) 2006 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 "WebDocumentLoadState.h"
30
31 #import <JavaScriptCore/Assertions.h>
32 #import "WebFrameLoader.h"
33 #import "WebDataProtocol.h"
34 #import "WebFrameBridge.h"
35
36 #import <WebKitSystemInterface.h>
37
38 @implementation WebDocumentLoadState
39
40 - (id)initWithRequest:(NSURLRequest *)req
41 {
42     self = [super init];
43     if (!self)
44         return nil;
45     
46     originalRequest = [req retain];
47     originalRequestCopy = [originalRequest copy];
48     request = [originalRequest mutableCopy];
49
50     return self;
51 }
52
53 - (void)dealloc
54 {
55     [mainResourceData release];
56     [originalRequest release];
57     [originalRequestCopy release];
58     [request release];
59     [response release];
60     [mainDocumentError release];
61     
62     [super dealloc];
63 }    
64
65
66 - (void)setFrameLoader:(WebFrameLoader *)fl
67 {
68     ASSERT(fl);
69     ASSERT(!frameLoader);
70     
71     frameLoader = fl;
72 }
73
74 - (WebFrameLoader *)frameLoader
75 {
76     return frameLoader;
77 }
78
79 - (void)setMainResourceData:(NSData *)data
80 {
81     [data retain];
82     [mainResourceData release];
83     mainResourceData = data;
84 }
85
86 - (NSData *)mainResourceData
87 {
88     return mainResourceData != nil ? mainResourceData : [frameLoader mainResourceData];
89 }
90
91 - (NSURLRequest *)originalRequest
92 {
93     return originalRequest;
94 }
95
96 - (NSURLRequest *)originalRequestCopy
97 {
98     return originalRequestCopy;
99 }
100
101 - (NSMutableURLRequest *)request
102 {
103     NSMutableURLRequest *clientRequest = [request _webDataRequestExternalRequest];
104     if (!clientRequest)
105         clientRequest = request;
106     return clientRequest;
107 }
108
109 - (void)replaceRequestURLForAnchorScrollWithURL:(NSURL *)URL
110 {
111     // assert that URLs differ only by fragment ID
112     
113     NSMutableURLRequest *newOriginalRequest = [originalRequestCopy mutableCopy];
114     [originalRequestCopy release];
115     [newOriginalRequest setURL:URL];
116     originalRequestCopy = newOriginalRequest;
117     
118     NSMutableURLRequest *newRequest = [request mutableCopy];
119     [request release];
120     [newRequest setURL:URL];
121     request = newRequest;
122 }
123
124 - (void)setRequest:(NSURLRequest *)req
125 {
126     ASSERT_ARG(req, req != request);
127     
128     // Replacing an unreachable URL with alternate content looks like a server-side
129     // redirect at this point, but we can replace a committed dataSource.
130     BOOL handlingUnreachableURL = [req _webDataRequestUnreachableURL] != nil;
131     if (handlingUnreachableURL)
132         committed = NO;
133     
134     // We should never be getting a redirect callback after the data
135     // source is committed, except in the unreachable URL case. It 
136     // would be a WebFoundation bug if it sent a redirect callback after commit.
137     ASSERT(!committed);
138     
139     NSURLRequest *oldRequest = request;
140     
141     request = [req mutableCopy];
142     
143     // Only send webView:didReceiveServerRedirectForProvisionalLoadForFrame: if URL changed.
144     // Also, don't send it when replacing unreachable URLs with alternate content.
145     if (!handlingUnreachableURL && ![[oldRequest URL] isEqual:[req URL]])
146         [frameLoader didReceiveServerRedirectForProvisionalLoadForFrame];
147     
148     [oldRequest release];
149 }
150
151 - (void)setResponse:(NSURLResponse *)resp
152 {
153     [resp retain];
154     [response release];
155     response = resp;
156 }
157
158 - (BOOL)isStopping
159 {
160     return stopping;
161 }
162
163 - (WebFrameBridge *)bridge
164 {
165     return [frameLoader bridge];
166 }
167
168 - (void)setMainDocumentError:(NSError *)error
169 {
170     [error retain];
171     [mainDocumentError release];
172     mainDocumentError = error;
173     
174     [frameLoader documentLoadState:self setMainDocumentError:error];
175  }
176
177 - (NSError *)mainDocumentError
178 {
179     return mainDocumentError;
180 }
181
182 - (void)clearErrors
183 {
184     [mainDocumentError release];
185     mainDocumentError = nil;
186 }
187
188 - (void)mainReceivedError:(NSError *)error complete:(BOOL)isComplete
189 {
190     if (!frameLoader)
191         return;
192     
193     [self setMainDocumentError:error];
194     
195     if (isComplete) {
196         [frameLoader documentLoadState:self mainReceivedCompleteError:error];
197     }
198 }
199
200 // Cancels the data source's pending loads.  Conceptually, a data source only loads
201 // one document at a time, but one document may have many related resources. 
202 // stopLoading will stop all loads initiated by the data source, 
203 // but not loads initiated by child frames' data sources -- that's the WebFrame's job.
204 - (void)stopLoading
205 {
206     // Always attempt to stop the bridge/part because it may still be loading/parsing after the data source
207     // is done loading and not stopping it can cause a world leak.
208     if (committed)
209         [[self bridge] stopLoading];
210     
211     if (!loading)
212         return;
213     
214     [self retain];
215     
216     stopping = YES;
217     
218     if ([frameLoader isLoadingMainResource]) {
219         // Stop the main resource loader and let it send the cancelled message.
220         [frameLoader cancelMainResourceLoad];
221     } else if ([frameLoader isLoadingSubresources]) {
222         // The main resource loader already finished loading. Set the cancelled error on the 
223         // document and let the subresourceLoaders send individual cancelled messages below.
224         [self setMainDocumentError:[frameLoader cancelledErrorWithRequest:request]];
225     } else {
226         // If there are no resource loaders, we need to manufacture a cancelled message.
227         // (A back/forward navigation has no resource loaders because its resources are cached.)
228         [self mainReceivedError:[frameLoader cancelledErrorWithRequest:request] complete:YES];
229     }
230     
231     [frameLoader stopLoadingSubresources];
232     [frameLoader stopLoadingPlugIns];
233     
234     stopping = NO;
235     
236     [self release];
237 }
238
239 - (void)setupForReplace
240 {
241     [frameLoader setupForReplace];
242     committed = NO;
243 }
244
245 - (void)commitIfReady
246 {
247     if (gotFirstByte && !committed) {
248         committed = YES;
249         [frameLoader commitProvisitionalLoad];
250     }
251 }
252
253 - (void)finishedLoading
254 {
255     gotFirstByte = YES;   
256     [self commitIfReady];
257     [frameLoader finishedLoadingDocumentLoadState:self];
258     [[self bridge] end];
259 }
260
261 - (void)setCommitted:(BOOL)f
262 {
263     committed = f;
264 }
265
266 - (BOOL)isCommitted
267 {
268     return committed;
269 }
270
271 - (void)setLoading:(BOOL)f
272 {
273     loading = f;
274 }
275
276 - (BOOL)isLoading
277 {
278     return loading;
279 }
280
281 - (void)commitLoadWithData:(NSData *)data
282 {
283     // Both unloading the old page and parsing the new page may execute JavaScript which destroys the datasource
284     // by starting a new load, so retain temporarily.
285     [self retain];
286     [self commitIfReady];
287     
288     [frameLoader committedLoadWithDocumentLoadState:self data:data];
289
290     [self release];
291 }
292
293 - (BOOL)doesProgressiveLoadWithMIMEType:(NSString *)MIMEType
294 {
295     return ![frameLoader isReplacing] || [MIMEType isEqualToString:@"text/html"];
296 }
297
298 - (void)receivedData:(NSData *)data
299 {    
300     gotFirstByte = YES;
301     
302     if ([self doesProgressiveLoadWithMIMEType:[response MIMEType]])
303         [self commitLoadWithData:data];
304 }
305
306 - (void)setupForReplaceByMIMEType:(NSString *)newMIMEType
307 {
308     if (!gotFirstByte)
309         return;
310     
311     NSString *oldMIMEType = [response MIMEType];
312     
313     if (![self doesProgressiveLoadWithMIMEType:oldMIMEType]) {
314         [frameLoader revertToProvisionalWithDocumentLoadState:self];
315         [self setupForReplace];
316         [self commitLoadWithData:[self mainResourceData]];
317     }
318     
319     [frameLoader finishedLoadingDocumentLoadState:self];
320     [[self bridge] end];
321     
322     [frameLoader setReplacing];
323     gotFirstByte = NO;
324     
325     if ([self doesProgressiveLoadWithMIMEType:newMIMEType]) {
326         [frameLoader revertToProvisionalWithDocumentLoadState:self];
327         [self setupForReplace];
328     }
329     
330     [frameLoader stopLoadingSubresources];
331     [frameLoader stopLoadingPlugIns];
332
333     [frameLoader finalSetupForReplaceWithDocumentLoadState:self];
334 }
335
336 - (void)updateLoading
337 {
338     ASSERT(self == [frameLoader activeDocumentLoadState]);
339     
340     [self setLoading:[frameLoader isLoading]];
341 }
342
343 - (NSURLResponse *)response
344 {
345     return response;
346 }
347
348 - (void)detachFromFrameLoader
349 {
350     frameLoader = nil;
351 }
352
353 @end