Reviewed by Adele (preliminary version) and later by Kevin.
[WebKit-https.git] / WebKit / Loader / WebFrameLoader.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 <WebKit/WebFrameLoader.h>
30
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>
41
42 @implementation WebFrameLoader
43
44 - (id)initWithWebFrame:(WebFrame *)wf
45 {
46     self = [super init];
47     if (self) {
48         webFrame = wf;
49         state = WebFrameStateCommittedPage;
50     }
51     return self;    
52 }
53
54 - (void)dealloc
55 {
56     // FIXME: should these even exist?
57     [mainResourceLoader release];
58     [subresourceLoaders release];
59     [plugInStreamLoaders release];
60     [iconLoader release];
61     [dataSource release];
62     [provisionalDataSource release];
63     
64     [super dealloc];
65 }
66
67 - (BOOL)hasIconLoader
68 {
69     return iconLoader != nil;
70 }
71
72 - (void)loadIconWithRequest:(NSURLRequest *)request
73 {
74     ASSERT(!iconLoader);
75     iconLoader = [[WebIconLoader alloc] initWithRequest:request];
76     [iconLoader setFrameLoader:self];
77     [iconLoader loadWithRequest:request];
78 }
79
80 - (void)stopLoadingIcon
81 {
82     [iconLoader stopLoading];
83 }
84
85 - (void)addPlugInStreamLoader:(WebLoader *)loader
86 {
87     if (!plugInStreamLoaders)
88         plugInStreamLoaders = [[NSMutableArray alloc] init];
89     [plugInStreamLoaders addObject:loader];
90 }
91
92 - (void)removePlugInStreamLoader:(WebLoader *)loader
93 {
94     [plugInStreamLoaders removeObject:loader];
95 }    
96
97 - (void)setDefersCallbacks:(BOOL)defers
98 {
99     [mainResourceLoader setDefersCallbacks:defers];
100     
101     NSEnumerator *e = [subresourceLoaders objectEnumerator];
102     WebLoader *loader;
103     while ((loader = [e nextObject]))
104         [loader setDefersCallbacks:defers];
105     
106     e = [plugInStreamLoaders objectEnumerator];
107     while ((loader = [e nextObject]))
108         [loader setDefersCallbacks:defers];
109
110     [self deliverArchivedResourcesAfterDelay];
111 }
112
113 - (void)stopLoadingPlugIns
114 {
115     [plugInStreamLoaders makeObjectsPerformSelector:@selector(cancel)];
116     [plugInStreamLoaders removeAllObjects];   
117 }
118
119 - (BOOL)isLoadingMainResource
120 {
121     return mainResourceLoader != nil;
122 }
123
124 - (BOOL)isLoadingSubresources
125 {
126     return [subresourceLoaders count];
127 }
128
129 - (BOOL)isLoadingPlugIns
130 {
131     return [plugInStreamLoaders count];
132 }
133
134 - (BOOL)isLoading
135 {
136     return [self isLoadingMainResource] || [self isLoadingSubresources] || [self isLoadingPlugIns];
137 }
138
139 - (void)stopLoadingSubresources
140 {
141     NSArray *loaders = [subresourceLoaders copy];
142     [loaders makeObjectsPerformSelector:@selector(cancel)];
143     [loaders release];
144     [subresourceLoaders removeAllObjects];
145 }
146
147 - (void)addSubresourceLoader:(WebLoader *)loader
148 {
149     if (subresourceLoaders == nil)
150         subresourceLoaders = [[NSMutableArray alloc] init];
151     [subresourceLoaders addObject:loader];
152 }
153
154 - (void)removeSubresourceLoader:(WebLoader *)loader
155 {
156     [subresourceLoaders removeObject:loader];
157 }
158
159 - (NSData *)mainResourceData
160 {
161     return [mainResourceLoader resourceData];
162 }
163
164 - (void)releaseMainResourceLoader
165 {
166     [mainResourceLoader release];
167     mainResourceLoader = nil;
168 }
169
170 - (void)cancelMainResourceLoad
171 {
172     [mainResourceLoader cancel];
173 }
174
175 - (BOOL)startLoadingMainResourceWithRequest:(NSMutableURLRequest *)request identifier:(id)identifier
176 {
177     mainResourceLoader = [[WebMainResourceLoader alloc] initWithFrameLoader:self];
178     
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;
187         return NO;
188     }
189     
190     return YES;
191 }
192
193 - (void)stopLoadingWithError:(NSError *)error
194 {
195     [mainResourceLoader cancelWithError:error];
196 }
197
198 - (WebDataSource *)dataSource
199 {
200     return dataSource; 
201 }
202
203 - (void)_setDataSource:(WebDataSource *)ds
204 {
205     if (ds == nil && dataSource == nil)
206         return;
207     
208     ASSERT(ds != dataSource);
209     
210     [webFrame _prepareForDataSourceReplacement];
211     [dataSource _setWebFrame:nil];
212     
213     [ds retain];
214     [dataSource release];
215     dataSource = ds;
216
217     [ds _setWebFrame:webFrame];
218 }
219
220 - (void)clearDataSource
221 {
222     [self _setDataSource:nil];
223 }
224
225 - (WebDataSource *)provisionalDataSource 
226 {
227     return provisionalDataSource; 
228 }
229
230 - (void)_setProvisionalDataSource: (WebDataSource *)d
231 {
232     ASSERT(!d || !provisionalDataSource);
233
234     if (provisionalDataSource != dataSource)
235         [provisionalDataSource _setWebFrame:nil];
236
237     [d retain];
238     [provisionalDataSource release];
239     provisionalDataSource = d;
240
241     [d _setWebFrame:webFrame];
242 }
243
244 - (void)_clearProvisionalDataSource
245 {
246     [self _setProvisionalDataSource:nil];
247 }
248
249 - (WebFrameState)state
250 {
251     return state;
252 }
253
254 #ifndef NDEBUG
255 static const char * const stateNames[] = {
256     "WebFrameStateProvisional",
257     "WebFrameStateCommittedPage",
258     "WebFrameStateComplete"
259 };
260 #endif
261
262 static CFAbsoluteTime _timeOfLastCompletedLoad;
263
264 + (CFAbsoluteTime)timeOfLastCompletedLoad
265 {
266     return _timeOfLastCompletedLoad;
267 }
268
269 - (void)_setState:(WebFrameState)newState
270 {
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]);
274     
275     if (newState == WebFrameStateComplete && webFrame == [[webFrame webView] mainFrame])
276         LOG(DocumentLoad, "completed %@ (%f seconds)", [[[self dataSource] request] URL], CFAbsoluteTimeGetCurrent() - [[self dataSource] _loadingStartedTime]);
277     
278     state = newState;
279     
280     if (state == WebFrameStateProvisional)
281         [webFrame _provisionalLoadStarted];
282     else if (state == WebFrameStateComplete) {
283         [webFrame _frameLoadCompleted];
284         _timeOfLastCompletedLoad = CFAbsoluteTimeGetCurrent();
285         [dataSource _stopRecordingResponses];
286     }
287 }
288
289 - (void)clearProvisionalLoad
290 {
291     [self _setProvisionalDataSource:nil];
292     [[webFrame webView] _progressCompleted:webFrame];
293     [self _setState:WebFrameStateComplete];
294 }
295
296 - (void)markLoadComplete
297 {
298     [self _setState:WebFrameStateComplete];
299 }
300
301 - (void)clearIconLoader
302 {
303     [iconLoader release];
304     iconLoader = nil;
305 }
306
307 - (void)commitProvisionalLoad
308 {
309     [self stopLoadingSubresources];
310     [self stopLoadingPlugIns];
311     [self clearIconLoader];
312
313     [self _setDataSource:provisionalDataSource];
314     [self _setProvisionalDataSource:nil];
315     [self _setState:WebFrameStateCommittedPage];
316 }
317
318 - (void)stopLoading
319 {
320     [provisionalDataSource _stopLoading];
321     [dataSource _stopLoading];
322     [self _clearProvisionalDataSource];
323     [self clearArchivedResources];
324 }
325
326 // FIXME: poor method name; also why is this not part of startProvisionalLoad:?
327 - (void)startLoading
328 {
329     [provisionalDataSource _startLoading];
330 }
331
332 - (void)startProvisionalLoad:(WebDataSource *)ds
333 {
334     [self _setProvisionalDataSource:ds];
335     [self _setState:WebFrameStateProvisional];
336 }
337
338 - (void)setupForReplace
339 {
340     [self _setState:WebFrameStateProvisional];
341     WebDataSource *old = provisionalDataSource;
342     provisionalDataSource = dataSource;
343     dataSource = nil;
344     [old release];
345     
346     [webFrame _detachChildren];
347 }
348
349 - (WebDataSource *)activeDataSource
350 {
351     if (state == WebFrameStateProvisional)
352         return provisionalDataSource;
353
354     return dataSource;
355 }
356
357 - (WebResource *)_archivedSubresourceForURL:(NSURL *)URL
358 {
359     return [[self activeDataSource] _archivedSubresourceForURL:URL];
360 }
361
362 - (BOOL)_defersCallbacks
363 {
364     return [[self activeDataSource] _defersCallbacks];
365 }
366
367 - (id)_identifierForInitialRequest:(NSURLRequest *)clientRequest
368 {
369     return [[self activeDataSource] _identifierForInitialRequest:clientRequest];
370 }
371
372 - (NSURLRequest *)_willSendRequest:(NSMutableURLRequest *)clientRequest forResource:(id)identifier redirectResponse:(NSURLResponse *)redirectResponse
373 {
374     return [[self activeDataSource] _willSendRequest:clientRequest forResource:identifier redirectResponse:redirectResponse];
375 }
376
377 - (void)_didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)currentWebChallenge forResource:(id)identifier
378 {
379     return [[self activeDataSource] _didReceiveAuthenticationChallenge:currentWebChallenge forResource:identifier];
380 }
381
382 - (void)_didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)currentWebChallenge forResource:(id)identifier
383 {
384     return [[self activeDataSource] _didCancelAuthenticationChallenge:currentWebChallenge forResource:identifier];
385 }
386
387 - (void)_didReceiveResponse:(NSURLResponse *)r forResource:(id)identifier
388 {
389     return [[self activeDataSource] _didReceiveResponse:r forResource:identifier];
390 }
391
392 - (void)_didReceiveData:(NSData *)data contentLength:(int)lengthReceived forResource:(id)identifier
393 {
394     return [[self activeDataSource] _didReceiveData:data contentLength:lengthReceived forResource:identifier];
395 }
396
397 - (void)_didFinishLoadingForResource:(id)identifier
398 {
399     return [[self activeDataSource] _didFinishLoadingForResource:identifier];
400 }
401
402 - (void)_didFailLoadingWithError:(NSError *)error forResource:(id)identifier
403 {
404     return [[self activeDataSource] _didFailLoadingWithError:error forResource:identifier];
405 }
406
407 - (BOOL)_privateBrowsingEnabled
408 {
409     return [[self activeDataSource] _privateBrowsingEnabled];
410 }
411
412 - (void)_addPlugInStreamLoader:(WebLoader *)loader
413 {
414     return [[self activeDataSource] _addPlugInStreamLoader:loader];
415 }
416
417 - (void)_removePlugInStreamLoader:(WebLoader *)loader
418 {
419     return [[self activeDataSource] _removePlugInStreamLoader:loader];
420 }
421
422 - (void)_finishedLoadingResource
423 {
424     return [[self activeDataSource] _finishedLoadingResource];
425 }
426
427 - (void)_receivedError:(NSError *)error
428 {
429     return [[self activeDataSource] _receivedError:error];
430 }
431
432 - (void)_addSubresourceLoader:(WebLoader *)loader
433 {
434     return [[self activeDataSource] _addSubresourceLoader:loader];
435 }
436
437 - (void)_removeSubresourceLoader:(WebLoader *)loader
438 {
439     return [[self activeDataSource] _removeSubresourceLoader:loader];
440 }
441
442 - (NSURLRequest *)_originalRequest
443 {
444     return [[self activeDataSource] _originalRequest];
445 }
446
447 - (WebFrame *)webFrame
448 {
449     return [[self activeDataSource] webFrame];
450 }
451
452 - (void)_receivedMainResourceError:(NSError *)error complete:(BOOL)isComplete
453 {
454     WebDataSource *ds = [self activeDataSource];
455     [ds retain];
456     [ds _receivedMainResourceError:error complete:isComplete];
457     [ds release];
458 }
459
460 - (NSURLRequest *)initialRequest
461 {
462     return [[self activeDataSource] initialRequest];
463 }
464
465 - (void)_receivedData:(NSData *)data
466 {
467     [[self activeDataSource] _receivedData:data];
468 }
469
470 - (void)_setRequest:(NSURLRequest *)request
471 {
472     [[self activeDataSource] _setRequest:request];
473 }
474
475 - (void)_downloadWithLoadingConnection:(NSURLConnection *)connection request:(NSURLRequest *)request response:(NSURLResponse *)r proxy:(WKNSURLConnectionDelegateProxyPtr)proxy
476 {
477     [[self activeDataSource] _downloadWithLoadingConnection:connection request:request response:r proxy:proxy];
478 }
479
480 - (void)_handleFallbackContent
481 {
482     [[self activeDataSource] _handleFallbackContent];
483 }
484
485 - (BOOL)_isStopping
486 {
487     return [[self activeDataSource] _isStopping];
488 }
489
490 - (void)_decidePolicyForMIMEType:(NSString *)MIMEType decisionListener:(WebPolicyDecisionListener *)listener
491 {
492     [[self activeDataSource] _decidePolicyForMIMEType:MIMEType decisionListener:listener];
493 }
494
495 - (void)_setupForReplaceByMIMEType:(NSString *)newMIMEType
496 {
497     [[self activeDataSource] _setupForReplaceByMIMEType:newMIMEType];
498 }
499
500 - (void)_setResponse:(NSURLResponse *)response
501 {
502     [[self activeDataSource] _setResponse:response];
503 }
504
505 - (void)_mainReceivedError:(NSError *)error complete:(BOOL)isComplete
506 {
507     [[self activeDataSource] _mainReceivedError:error complete:isComplete];
508 }
509
510 - (void)_finishedLoading
511 {
512     [[self activeDataSource] _finishedLoading];
513 }
514
515 - (void)_mainReceivedBytesSoFar:(unsigned)bytesSoFar complete:(BOOL)isComplete
516 {
517     [[self activeDataSource] _mainReceivedBytesSoFar:bytesSoFar complete:isComplete];
518 }
519
520 - (void)_iconLoaderReceivedPageIcon:(WebIconLoader *)iLoader
521 {
522     ASSERT(iLoader == iconLoader);
523     [[self activeDataSource] _iconLoaderReceivedPageIcon:iLoader];
524 }
525
526 - (NSURL *)_URL
527 {
528     return [[self activeDataSource] _URL];
529 }
530
531 - (NSError *)cancelledErrorWithRequest:(NSURLRequest *)request
532 {
533     return [NSError _webKitErrorWithDomain:NSURLErrorDomain
534                                       code:NSURLErrorCancelled
535                                        URL:[request URL]];
536 }
537
538 - (NSError *)fileDoesNotExistErrorWithResponse:(NSURLResponse *)response
539 {
540     return [NSError _webKitErrorWithDomain:NSURLErrorDomain
541                                                 code:NSURLErrorFileDoesNotExist
542                                                  URL:[response URL]];    
543 }
544
545 - (void)clearArchivedResources
546 {
547     [pendingArchivedResources removeAllObjects];
548     [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(deliverArchivedResources) object:nil];
549 }
550
551 - (void)deliverArchivedResources
552 {
553     if (![pendingArchivedResources count] || [self _defersCallbacks])
554         return;
555         
556     NSEnumerator *keyEnum = [pendingArchivedResources keyEnumerator];
557     WebLoader *loader;
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];
564     }
565     
566     [pendingArchivedResources removeAllObjects];
567 }
568
569 - (void)deliverArchivedResourcesAfterDelay
570 {
571     if (![pendingArchivedResources count] || [self _defersCallbacks])
572         return;
573     
574     [self performSelector:@selector(deliverArchivedResources) withObject:nil afterDelay:0];
575 }
576
577 static BOOL isCaseInsensitiveEqual(NSString *a, NSString *b)
578 {
579     return [a caseInsensitiveCompare:b] == NSOrderedSame;
580 }
581
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
585 {
586     NSURLRequestCachePolicy policy = [theRequest cachePolicy];
587     
588     if (policy == NSURLRequestReturnCacheDataElseLoad) {
589         return YES;
590     } else if (policy == NSURLRequestReturnCacheDataDontLoad) {
591         return YES;
592     } else if (policy == NSURLRequestReloadIgnoringCacheData) {
593         return NO;
594     } else if ([theRequest valueForHTTPHeaderField:@"must-revalidate"] != nil) {
595         return NO;
596     } else if ([theRequest valueForHTTPHeaderField:@"proxy-revalidate"] != nil) {
597         return NO;
598     } else if ([theRequest valueForHTTPHeaderField:@"If-Modified-Since"] != nil) {
599         return NO;
600     } else if ([theRequest valueForHTTPHeaderField:@"Cache-Control"] != nil) {
601         return NO;
602     } else if (isCaseInsensitiveEqual(@"POST", [theRequest HTTPMethod])) {
603         return NO;
604     } else {
605         return YES;
606     }
607 }
608
609 - (BOOL)_canUseResourceWithResponse:(NSURLResponse *)theResponse
610 {
611     if (WKGetNSURLResponseMustRevalidate(theResponse)) {
612         return NO;
613     } else if (WKGetNSURLResponseCalculatedExpiration(theResponse) - CFAbsoluteTimeGetCurrent() < 1) {
614         return NO;
615     } else {
616         return YES;
617     }
618 }
619
620 - (NSMutableDictionary *)pendingArchivedResources
621 {
622     if (!pendingArchivedResources)
623         pendingArchivedResources = [[NSMutableDictionary alloc] init];
624     
625     return pendingArchivedResources;
626 }
627
628 - (BOOL)willUseArchiveForRequest:(NSURLRequest *)r originalURL:(NSURL *)originalURL loader:(WebLoader *)loader
629 {
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];
636             return YES;
637         }
638     }
639     return NO;
640 }
641
642 - (BOOL)archiveLoadPendingForLoader:(WebLoader *)loader
643 {
644     return [pendingArchivedResources objectForKey:loader] != nil;
645 }
646
647 - (void)cancelPendingArchiveLoadForLoader:(WebLoader *)loader
648 {
649     [pendingArchivedResources removeObjectForKey:loader];
650     
651     if (![pendingArchivedResources count])
652         [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(deliverArchivedResources) object:nil];
653 }
654
655 - (void)_addExtraFieldsToRequest:(NSMutableURLRequest *)request mainResource:(BOOL)mainResource alwaysFromRequest:(BOOL)f
656 {
657     [webFrame _addExtraFieldsToRequest:request mainResource:mainResource alwaysFromRequest:f];
658 }
659
660 - (void)cannotShowMIMETypeForURL:(NSURL *)URL
661 {
662     [webFrame _handleUnimplementablePolicyWithErrorCode:WebKitErrorCannotShowMIMEType forURL:URL];    
663 }
664
665 - (NSError *)interruptForPolicyChangeErrorWithRequest:(NSURLRequest *)request
666 {
667     return [NSError _webKitErrorWithDomain:WebKitErrorDomain code:WebKitErrorFrameLoadInterruptedByPolicyChange URL:[request URL]];
668 }
669
670 - (BOOL)isHostedByObjectElement
671 {
672     // Handle <object> fallback for error cases.            
673     DOMHTMLElement *hostElement = [webFrame frameElement];
674     return hostElement && [hostElement isKindOfClass:[DOMHTMLObjectElement class]];
675 }
676
677 - (BOOL)isLoadingMainFrame
678 {
679     return [webFrame _isMainFrame];
680 }
681
682 + (BOOL)_canShowMIMEType:(NSString *)MIMEType
683 {
684     return [WebDataSource _canShowMIMEType:MIMEType];
685 }
686
687 + (BOOL)_representationExistsForURLScheme:(NSString *)URLScheme
688 {
689     return [WebDataSource _representationExistsForURLScheme:URLScheme];
690 }
691
692 + (NSString *)_generatedMIMETypeForURLScheme:(NSString *)URLScheme
693 {
694     return [WebDataSource _generatedMIMETypeForURLScheme:URLScheme];
695 }
696
697 @end