d2fdc554a9c99d78cd01ebb33e1cd9c09b492011
[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     [pageTitle release];
62     [triggeringAction release];
63     [lastCheckedRequest release];
64     [responses release];    
65     
66     [super dealloc];
67 }    
68
69
70 - (void)setFrameLoader:(WebFrameLoader *)fl
71 {
72     ASSERT(fl);
73     ASSERT(!frameLoader);
74     
75     frameLoader = fl;
76 }
77
78 - (WebFrameLoader *)frameLoader
79 {
80     return frameLoader;
81 }
82
83 - (void)setMainResourceData:(NSData *)data
84 {
85     [data retain];
86     [mainResourceData release];
87     mainResourceData = data;
88 }
89
90 - (NSData *)mainResourceData
91 {
92     return mainResourceData != nil ? mainResourceData : [frameLoader mainResourceData];
93 }
94
95 - (NSURLRequest *)originalRequest
96 {
97     return originalRequest;
98 }
99
100 - (NSURLRequest *)originalRequestCopy
101 {
102     return originalRequestCopy;
103 }
104
105 - (NSMutableURLRequest *)request
106 {
107     NSMutableURLRequest *clientRequest = [request _webDataRequestExternalRequest];
108     if (!clientRequest)
109         clientRequest = request;
110     return clientRequest;
111 }
112
113 - (NSMutableURLRequest *)actualRequest
114 {
115     return request;
116 }
117
118 - (void)replaceRequestURLForAnchorScrollWithURL:(NSURL *)URL
119 {
120     // assert that URLs differ only by fragment ID
121     
122     NSMutableURLRequest *newOriginalRequest = [originalRequestCopy mutableCopy];
123     [originalRequestCopy release];
124     [newOriginalRequest setURL:URL];
125     originalRequestCopy = newOriginalRequest;
126     
127     NSMutableURLRequest *newRequest = [request mutableCopy];
128     [request release];
129     [newRequest setURL:URL];
130     request = newRequest;
131 }
132
133 - (void)setRequest:(NSURLRequest *)req
134 {
135     ASSERT_ARG(req, req != request);
136     
137     // Replacing an unreachable URL with alternate content looks like a server-side
138     // redirect at this point, but we can replace a committed dataSource.
139     BOOL handlingUnreachableURL = [req _webDataRequestUnreachableURL] != nil;
140     if (handlingUnreachableURL)
141         committed = NO;
142     
143     // We should never be getting a redirect callback after the data
144     // source is committed, except in the unreachable URL case. It 
145     // would be a WebFoundation bug if it sent a redirect callback after commit.
146     ASSERT(!committed);
147     
148     NSURLRequest *oldRequest = request;
149     
150     request = [req mutableCopy];
151     
152     // Only send webView:didReceiveServerRedirectForProvisionalLoadForFrame: if URL changed.
153     // Also, don't send it when replacing unreachable URLs with alternate content.
154     if (!handlingUnreachableURL && ![[oldRequest URL] isEqual:[req URL]])
155         [frameLoader didReceiveServerRedirectForProvisionalLoadForFrame];
156     
157     [oldRequest release];
158 }
159
160 - (void)setResponse:(NSURLResponse *)resp
161 {
162     [resp retain];
163     [response release];
164     response = resp;
165 }
166
167 - (BOOL)isStopping
168 {
169     return stopping;
170 }
171
172 - (WebFrameBridge *)bridge
173 {
174     return [frameLoader bridge];
175 }
176
177 - (void)setMainDocumentError:(NSError *)error
178 {
179     [error retain];
180     [mainDocumentError release];
181     mainDocumentError = error;
182     
183     [frameLoader documentLoadState:self setMainDocumentError:error];
184  }
185
186 - (NSError *)mainDocumentError
187 {
188     return mainDocumentError;
189 }
190
191 - (void)clearErrors
192 {
193     [mainDocumentError release];
194     mainDocumentError = nil;
195 }
196
197 - (void)mainReceivedError:(NSError *)error complete:(BOOL)isComplete
198 {
199     if (!frameLoader)
200         return;
201     
202     [self setMainDocumentError:error];
203     
204     if (isComplete) {
205         [frameLoader documentLoadState:self mainReceivedCompleteError:error];
206     }
207 }
208
209 // Cancels the data source's pending loads.  Conceptually, a data source only loads
210 // one document at a time, but one document may have many related resources. 
211 // stopLoading will stop all loads initiated by the data source, 
212 // but not loads initiated by child frames' data sources -- that's the WebFrame's job.
213 - (void)stopLoading
214 {
215     // Always attempt to stop the bridge/part because it may still be loading/parsing after the data source
216     // is done loading and not stopping it can cause a world leak.
217     if (committed)
218         [[self bridge] stopLoading];
219     
220     if (!loading)
221         return;
222     
223     [self retain];
224     
225     stopping = YES;
226     
227     if ([frameLoader isLoadingMainResource]) {
228         // Stop the main resource loader and let it send the cancelled message.
229         [frameLoader cancelMainResourceLoad];
230     } else if ([frameLoader isLoadingSubresources]) {
231         // The main resource loader already finished loading. Set the cancelled error on the 
232         // document and let the subresourceLoaders send individual cancelled messages below.
233         [self setMainDocumentError:[frameLoader cancelledErrorWithRequest:request]];
234     } else {
235         // If there are no resource loaders, we need to manufacture a cancelled message.
236         // (A back/forward navigation has no resource loaders because its resources are cached.)
237         [self mainReceivedError:[frameLoader cancelledErrorWithRequest:request] complete:YES];
238     }
239     
240     [frameLoader stopLoadingSubresources];
241     [frameLoader stopLoadingPlugIns];
242     
243     stopping = NO;
244     
245     [self release];
246 }
247
248 - (void)setupForReplace
249 {
250     [frameLoader setupForReplace];
251     committed = NO;
252 }
253
254 - (void)commitIfReady
255 {
256     if (gotFirstByte && !committed) {
257         committed = YES;
258         [frameLoader commitProvisitionalLoad];
259     }
260 }
261
262 - (void)finishedLoading
263 {
264     gotFirstByte = YES;   
265     [self commitIfReady];
266     [frameLoader finishedLoadingDocumentLoadState:self];
267     [[self bridge] end];
268 }
269
270 - (void)setCommitted:(BOOL)f
271 {
272     committed = f;
273 }
274
275 - (BOOL)isCommitted
276 {
277     return committed;
278 }
279
280 - (void)setLoading:(BOOL)f
281 {
282     loading = f;
283 }
284
285 - (BOOL)isLoading
286 {
287     return loading;
288 }
289
290 - (void)commitLoadWithData:(NSData *)data
291 {
292     // Both unloading the old page and parsing the new page may execute JavaScript which destroys the datasource
293     // by starting a new load, so retain temporarily.
294     [self retain];
295     [self commitIfReady];
296     
297     [frameLoader committedLoadWithDocumentLoadState:self data:data];
298
299     [self release];
300 }
301
302 - (BOOL)doesProgressiveLoadWithMIMEType:(NSString *)MIMEType
303 {
304     return ![frameLoader isReplacing] || [MIMEType isEqualToString:@"text/html"];
305 }
306
307 - (void)receivedData:(NSData *)data
308 {    
309     gotFirstByte = YES;
310     
311     if ([self doesProgressiveLoadWithMIMEType:[response MIMEType]])
312         [self commitLoadWithData:data];
313 }
314
315 - (void)setupForReplaceByMIMEType:(NSString *)newMIMEType
316 {
317     if (!gotFirstByte)
318         return;
319     
320     NSString *oldMIMEType = [response MIMEType];
321     
322     if (![self doesProgressiveLoadWithMIMEType:oldMIMEType]) {
323         [frameLoader revertToProvisionalWithDocumentLoadState:self];
324         [self setupForReplace];
325         [self commitLoadWithData:[self mainResourceData]];
326     }
327     
328     [frameLoader finishedLoadingDocumentLoadState:self];
329     [[self bridge] end];
330     
331     [frameLoader setReplacing];
332     gotFirstByte = NO;
333     
334     if ([self doesProgressiveLoadWithMIMEType:newMIMEType]) {
335         [frameLoader revertToProvisionalWithDocumentLoadState:self];
336         [self setupForReplace];
337     }
338     
339     [frameLoader stopLoadingSubresources];
340     [frameLoader stopLoadingPlugIns];
341
342     [frameLoader finalSetupForReplaceWithDocumentLoadState:self];
343 }
344
345 - (void)updateLoading
346 {
347     ASSERT(self == [frameLoader activeDocumentLoadState]);
348     
349     [self setLoading:[frameLoader isLoading]];
350 }
351
352 - (NSURLResponse *)response
353 {
354     return response;
355 }
356
357 - (void)detachFromFrameLoader
358 {
359     frameLoader = nil;
360 }
361
362 - (void)prepareForLoadStart
363 {
364     ASSERT(!stopping);
365     [self setPrimaryLoadComplete:NO];
366     ASSERT(frameLoader != nil);
367     [self clearErrors];
368     
369     // Mark the start loading time.
370     loadingStartedTime = CFAbsoluteTimeGetCurrent();
371     
372     [self setLoading:YES];
373     
374     [frameLoader prepareForLoadStart];
375 }
376
377 - (double)loadingStartedTime
378 {
379     return loadingStartedTime;
380 }
381
382 - (void)setIsClientRedirect:(BOOL)flag
383 {
384     isClientRedirect = flag;
385 }
386
387 - (BOOL)isClientRedirect
388 {
389     return isClientRedirect;
390 }
391
392 - (void)setPrimaryLoadComplete:(BOOL)flag
393 {
394     primaryLoadComplete = flag;
395     
396     if (flag) {
397         if ([frameLoader isLoadingMainResource]) {
398             [self setMainResourceData:[frameLoader mainResourceData]];
399             [frameLoader releaseMainResourceLoader];
400         }
401         
402         [self updateLoading];
403     }
404 }
405
406 - (BOOL)isLoadingInAPISense
407 {
408     // Once a frame has loaded, we no longer need to consider subresources,
409     // but we still need to consider subframes.
410     if ([frameLoader state] != WebFrameStateComplete) {
411         if (!primaryLoadComplete && [self isLoading])
412             return YES;
413         if ([frameLoader isLoadingSubresources])
414             return YES;
415         if (![[frameLoader bridge] doneProcessingData])
416             return YES;
417     }
418     
419     return [frameLoader subframeIsLoading];
420 }
421
422 - (void)addResponse:(NSURLResponse *)r
423 {
424     if (!stopRecordingResponses) {
425         if (!responses)
426             responses = [[NSMutableArray alloc] init];
427         [responses addObject: r];
428     }
429 }
430
431 - (void)stopRecordingResponses
432 {
433     stopRecordingResponses = YES;
434 }
435
436 - (NSString *)title
437 {
438     return pageTitle;
439 }
440
441 - (void)setLastCheckedRequest:(NSURLRequest *)req
442 {
443     NSURLRequest *oldRequest = lastCheckedRequest;
444     lastCheckedRequest = [req copy];
445     [oldRequest release];
446 }
447
448 - (NSURLRequest *)lastCheckedRequest
449 {
450     // It's OK not to make a copy here because we know the caller
451     // isn't going to modify this request
452     return [[lastCheckedRequest retain] autorelease];
453 }
454
455 - (NSDictionary *)triggeringAction
456 {
457     return [[triggeringAction retain] autorelease];
458 }
459
460 - (void)setTriggeringAction:(NSDictionary *)action
461 {
462     [action retain];
463     [triggeringAction release];
464     triggeringAction = action;
465 }
466
467 - (NSArray *)responses
468 {
469     return responses;
470 }
471
472 - (void)setOverrideEncoding:(NSString *)enc
473 {
474     NSString *copy = [enc copy];
475     [overrideEncoding release];
476     overrideEncoding = copy;
477 }
478
479 - (NSString *)overrideEncoding
480 {
481     return [[overrideEncoding copy] autorelease];
482 }
483
484 - (void)setTitle:(NSString *)title
485 {
486     if (!title)
487         return;
488
489     NSString *trimmed = [title mutableCopy];
490     CFStringTrimWhitespace((CFMutableStringRef)trimmed);
491
492     if ([trimmed length] != 0 && ![pageTitle isEqualToString:trimmed]) {
493         [frameLoader willChangeTitleForDocumentLoadState:self];
494         [pageTitle release];
495         pageTitle = [trimmed copy];
496         [frameLoader didChangeTitleForDocumentLoadState:self];
497     }
498
499     [trimmed release];
500 }
501
502 @end