Reviewed by Brady.
[WebKit-https.git] / WebKit / Loader / WebDocumentLoader.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 "WebDocumentLoader.h"
30
31 #import <JavaScriptCore/Assertions.h>
32 #import "WebFrameLoader.h"
33 #import "WebDataProtocol.h"
34 #import "WebFrameBridge.h"
35 #import <WebCore/WebCoreSystemInterface.h>
36
37 @implementation WebDocumentLoader
38
39 - (id)initWithRequest:(NSURLRequest *)req
40 {
41     self = [super init];
42     if (!self)
43         return nil;
44     
45     originalRequest = [req retain];
46     originalRequestCopy = [originalRequest copy];
47     request = [originalRequest mutableCopy];
48     wkSupportsMultipartXMixedReplace(request);
49
50     return self;
51 }
52
53 - (void)dealloc
54 {
55     ASSERT([frameLoader activeDocumentLoader] != self || ![frameLoader isLoading]);
56     
57
58     [mainResourceData release];
59     [originalRequest release];
60     [originalRequestCopy release];
61     [request release];
62     [response release];
63     [mainDocumentError release];
64     [pageTitle release];
65     [triggeringAction release];
66     [lastCheckedRequest release];
67     [responses release];    
68     
69     [super dealloc];
70 }    
71
72
73 - (void)setFrameLoader:(WebFrameLoader *)fl
74 {
75     ASSERT(fl);
76     ASSERT(!frameLoader);
77     
78     frameLoader = fl;
79 }
80
81 - (WebFrameLoader *)frameLoader
82 {
83     return frameLoader;
84 }
85
86 - (void)setMainResourceData:(NSData *)data
87 {
88     [data retain];
89     [mainResourceData release];
90     mainResourceData = data;
91 }
92
93 - (NSData *)mainResourceData
94 {
95     return mainResourceData != nil ? mainResourceData : [frameLoader mainResourceData];
96 }
97
98 - (NSURLRequest *)originalRequest
99 {
100     return originalRequest;
101 }
102
103 - (NSURLRequest *)originalRequestCopy
104 {
105     return originalRequestCopy;
106 }
107
108 - (NSMutableURLRequest *)request
109 {
110     NSMutableURLRequest *clientRequest = [request _webDataRequestExternalRequest];
111     if (!clientRequest)
112         clientRequest = request;
113     return clientRequest;
114 }
115
116 - (NSURLRequest *)initialRequest
117 {
118     NSURLRequest *clientRequest = [[self originalRequest] _webDataRequestExternalRequest];
119     if (!clientRequest)
120         clientRequest = [self originalRequest];
121     return clientRequest;
122 }
123
124 - (NSMutableURLRequest *)actualRequest
125 {
126     return request;
127 }
128
129 - (NSURL *)URL
130 {
131     return [[self request] URL];
132 }
133
134 - (NSURL *)unreachableURL
135 {
136     return [[self originalRequest] _webDataRequestUnreachableURL];
137 }
138
139 - (void)replaceRequestURLForAnchorScrollWithURL:(NSURL *)URL
140 {
141     // assert that URLs differ only by fragment ID
142     
143     NSMutableURLRequest *newOriginalRequest = [originalRequestCopy mutableCopy];
144     [originalRequestCopy release];
145     [newOriginalRequest setURL:URL];
146     originalRequestCopy = newOriginalRequest;
147     
148     NSMutableURLRequest *newRequest = [request mutableCopy];
149     [request release];
150     [newRequest setURL:URL];
151     request = newRequest;
152 }
153
154 - (void)setRequest:(NSURLRequest *)req
155 {
156     ASSERT_ARG(req, req != request);
157     
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)
162         committed = NO;
163     
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.
167     ASSERT(!committed);
168     
169     NSURLRequest *oldRequest = request;
170     
171     request = [req mutableCopy];
172     
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];
177     
178     [oldRequest release];
179 }
180
181 - (void)setResponse:(NSURLResponse *)resp
182 {
183     [resp retain];
184     [response release];
185     response = resp;
186 }
187
188 - (BOOL)isStopping
189 {
190     return stopping;
191 }
192
193 - (WebCoreFrameBridge *)bridge
194 {
195     return [frameLoader bridge];
196 }
197
198 - (void)setMainDocumentError:(NSError *)error
199 {
200     [error retain];
201     [mainDocumentError release];
202     mainDocumentError = error;
203     
204     [frameLoader documentLoader:self setMainDocumentError:error];
205  }
206
207 - (NSError *)mainDocumentError
208 {
209     return mainDocumentError;
210 }
211
212 - (void)clearErrors
213 {
214     [mainDocumentError release];
215     mainDocumentError = nil;
216 }
217
218 - (void)mainReceivedError:(NSError *)error complete:(BOOL)isComplete
219 {
220     if (!frameLoader)
221         return;
222     
223     [self setMainDocumentError:error];
224     
225     if (isComplete) {
226         [frameLoader documentLoader:self mainReceivedCompleteError:error];
227     }
228 }
229
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.
234 - (void)stopLoading
235 {
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.
238     if (committed)
239         [[self bridge] stopLoading];
240     
241     if (!loading)
242         return;
243     
244     [self retain];
245     
246     stopping = YES;
247     
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]];
255     } else {
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];
259     }
260     
261     [frameLoader stopLoadingSubresources];
262     [frameLoader stopLoadingPlugIns];
263     
264     stopping = NO;
265     
266     [self release];
267 }
268
269 - (void)setupForReplace
270 {
271     [frameLoader setupForReplace];
272     committed = NO;
273 }
274
275 - (void)commitIfReady
276 {
277     if (gotFirstByte && !committed) {
278         committed = YES;
279         [frameLoader commitProvisionalLoad:nil];
280     }
281 }
282
283 - (void)finishedLoading
284 {
285     gotFirstByte = YES;   
286     [self commitIfReady];
287     [frameLoader finishedLoadingDocument:self];
288     [[self bridge] end];
289 }
290
291 - (void)setCommitted:(BOOL)f
292 {
293     committed = f;
294 }
295
296 - (BOOL)isCommitted
297 {
298     return committed;
299 }
300
301 - (void)setLoading:(BOOL)f
302 {
303     loading = f;
304 }
305
306 - (BOOL)isLoading
307 {
308     return loading;
309 }
310
311 - (void)commitLoadWithData:(NSData *)data
312 {
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.
315     [self retain];
316     [self commitIfReady];
317     
318     [frameLoader committedLoadWithDocumentLoader:self data:data];
319
320     [self release];
321 }
322
323 - (BOOL)doesProgressiveLoadWithMIMEType:(NSString *)MIMEType
324 {
325     return ![frameLoader isReplacing] || [MIMEType isEqualToString:@"text/html"];
326 }
327
328 - (void)receivedData:(NSData *)data
329 {    
330     gotFirstByte = YES;
331     
332     if ([self doesProgressiveLoadWithMIMEType:[response MIMEType]])
333         [self commitLoadWithData:data];
334 }
335
336 - (void)setupForReplaceByMIMEType:(NSString *)newMIMEType
337 {
338     if (!gotFirstByte)
339         return;
340     
341     NSString *oldMIMEType = [response MIMEType];
342     
343     if (![self doesProgressiveLoadWithMIMEType:oldMIMEType]) {
344         [frameLoader revertToProvisionalWithDocumentLoader:self];
345         [self setupForReplace];
346         [self commitLoadWithData:[self mainResourceData]];
347     }
348     
349     [frameLoader finishedLoadingDocument:self];
350     [[self bridge] end];
351     
352     [frameLoader setReplacing];
353     gotFirstByte = NO;
354     
355     if ([self doesProgressiveLoadWithMIMEType:newMIMEType]) {
356         [frameLoader revertToProvisionalWithDocumentLoader:self];
357         [self setupForReplace];
358     }
359     
360     [frameLoader stopLoadingSubresources];
361     [frameLoader stopLoadingPlugIns];
362
363     [frameLoader finalSetupForReplaceWithDocumentLoader:self];
364 }
365
366 - (void)updateLoading
367 {
368     ASSERT(self == [frameLoader activeDocumentLoader]);
369     
370     [self setLoading:[frameLoader isLoading]];
371 }
372
373 - (NSURLResponse *)response
374 {
375     return response;
376 }
377
378 - (void)detachFromFrameLoader
379 {
380     frameLoader = nil;
381 }
382
383 - (void)prepareForLoadStart
384 {
385     ASSERT(!stopping);
386     [self setPrimaryLoadComplete:NO];
387     ASSERT(frameLoader != nil);
388     [self clearErrors];
389     
390     // Mark the start loading time.
391     loadingStartedTime = CFAbsoluteTimeGetCurrent();
392     
393     [self setLoading:YES];
394     
395     [frameLoader prepareForLoadStart];
396 }
397
398 - (double)loadingStartedTime
399 {
400     return loadingStartedTime;
401 }
402
403 - (void)setIsClientRedirect:(BOOL)flag
404 {
405     isClientRedirect = flag;
406 }
407
408 - (BOOL)isClientRedirect
409 {
410     return isClientRedirect;
411 }
412
413 - (void)setPrimaryLoadComplete:(BOOL)flag
414 {
415     primaryLoadComplete = flag;
416     
417     if (flag) {
418         if ([frameLoader isLoadingMainResource]) {
419             [self setMainResourceData:[frameLoader mainResourceData]];
420             [frameLoader releaseMainResourceLoader];
421         }
422         
423         [self updateLoading];
424     }
425 }
426
427 - (BOOL)isLoadingInAPISense
428 {
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])
433             return YES;
434         if ([frameLoader isLoadingSubresources])
435             return YES;
436         if (![[frameLoader bridge] doneProcessingData])
437             return YES;
438     }
439     
440     return [frameLoader subframeIsLoading];
441 }
442
443 - (void)addResponse:(NSURLResponse *)r
444 {
445     if (!stopRecordingResponses) {
446         if (!responses)
447             responses = [[NSMutableArray alloc] init];
448         [responses addObject: r];
449     }
450 }
451
452 - (void)stopRecordingResponses
453 {
454     stopRecordingResponses = YES;
455 }
456
457 - (NSString *)title
458 {
459     return pageTitle;
460 }
461
462 - (void)setLastCheckedRequest:(NSURLRequest *)req
463 {
464     NSURLRequest *oldRequest = lastCheckedRequest;
465     lastCheckedRequest = [req copy];
466     [oldRequest release];
467 }
468
469 - (NSURLRequest *)lastCheckedRequest
470 {
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];
474 }
475
476 - (NSDictionary *)triggeringAction
477 {
478     return [[triggeringAction retain] autorelease];
479 }
480
481 - (void)setTriggeringAction:(NSDictionary *)action
482 {
483     [action retain];
484     [triggeringAction release];
485     triggeringAction = action;
486 }
487
488 - (NSArray *)responses
489 {
490     return responses;
491 }
492
493 - (void)setOverrideEncoding:(NSString *)enc
494 {
495     NSString *copy = [enc copy];
496     [overrideEncoding release];
497     overrideEncoding = copy;
498 }
499
500 - (NSString *)overrideEncoding
501 {
502     return [[overrideEncoding copy] autorelease];
503 }
504
505 - (void)setTitle:(NSString *)title
506 {
507     if (!title)
508         return;
509
510     NSString *trimmed = [title mutableCopy];
511     CFStringTrimWhitespace((CFMutableStringRef)trimmed);
512
513     if ([trimmed length] != 0 && ![pageTitle isEqualToString:trimmed]) {
514         [frameLoader willChangeTitleForDocument:self];
515         [pageTitle release];
516         pageTitle = [trimmed copy];
517         [frameLoader didChangeTitleForDocument:self];
518     }
519
520     [trimmed release];
521 }
522
523 @end