Move URL from WebCore to WTF
[WebKit-https.git] / Source / WebKitLegacy / mac / WebView / WebDataSource.mm
1 /*
2  * Copyright (C) 2005, 2006, 2007, 2008, 2012 Apple 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 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 "WebDataSource.h"
30
31 #import "WebArchive.h"
32 #import "WebArchiveInternal.h"
33 #import "WebDataSourceInternal.h"
34 #import "WebDocument.h"
35 #import "WebDocumentLoaderMac.h"
36 #import "WebFrameInternal.h"
37 #import "WebFrameLoadDelegate.h"
38 #import "WebFrameLoaderClient.h"
39 #import "WebFrameViewInternal.h"
40 #import "WebHTMLRepresentation.h"
41 #import "WebKitErrorsPrivate.h"
42 #import "WebKitLogging.h"
43 #import "WebKitNSStringExtras.h"
44 #import "WebKitStatisticsPrivate.h"
45 #import "WebNSURLExtras.h"
46 #import "WebNSURLRequestExtras.h"
47 #import "WebPDFRepresentation.h"
48 #import "WebResourceInternal.h"
49 #import "WebResourceLoadDelegate.h"
50 #import "WebViewInternal.h"
51 #import <JavaScriptCore/InitializeThreading.h>
52 #import <WebCore/ApplicationCacheStorage.h>
53 #import <WebCore/FrameLoader.h>
54 #import <WebCore/LegacyWebArchive.h>
55 #import <WebCore/MIMETypeRegistry.h>
56 #import <WebCore/ResourceRequest.h>
57 #import <WebCore/SharedBuffer.h>
58 #import <WebCore/WebCoreObjCExtras.h>
59 #import <WebCore/WebCoreURLResponse.h>
60 #import <WebKitLegacy/DOMHTML.h>
61 #import <WebKitLegacy/DOMPrivate.h>
62 #import <wtf/Assertions.h>
63 #import <wtf/MainThread.h>
64 #import <wtf/RefPtr.h>
65 #import <wtf/RetainPtr.h>
66 #import <wtf/RunLoop.h>
67 #import <wtf/URL.h>
68
69 #if PLATFORM(IOS_FAMILY)
70 #import "WebPDFViewIOS.h"
71 #endif
72
73 #if USE(QUICK_LOOK)
74 #import <WebCore/QuickLook.h>
75 #endif
76
77 using namespace WebCore;
78
79 class WebDataSourcePrivate
80 {
81 public:
82     WebDataSourcePrivate(Ref<WebDocumentLoaderMac>&& loader)
83         : loader(WTFMove(loader))
84         , representationFinishedLoading(NO)
85         , includedInWebKitStatistics(NO)
86 #if PLATFORM(IOS_FAMILY)
87         , _dataSourceDelegate(nil)
88 #endif
89     {
90     }
91     ~WebDataSourcePrivate()
92     {
93         // We might run in to infinite recursion if we're stopping loading as the result of detaching from the frame.
94         // Therefore, DocumentLoader::detachFromFrame() did some smart things to stop the recursion.
95         // As a result of breaking the resursion, DocumentLoader::m_subresourceLoader
96         // and DocumentLoader::m_plugInStreamLoaders might not be empty at this time.
97         // See <rdar://problem/9673866> for more details.
98         ASSERT(!loader->isLoading() || loader->isStopping());
99         loader->detachDataSource();
100     }
101
102     Ref<WebDocumentLoaderMac> loader;
103     RetainPtr<id<WebDocumentRepresentation> > representation;
104     BOOL representationFinishedLoading;
105     BOOL includedInWebKitStatistics;
106 #if PLATFORM(IOS_FAMILY)
107     NSObject<WebDataSourcePrivateDelegate> *_dataSourceDelegate;
108 #endif
109 #if USE(QUICK_LOOK)
110     RetainPtr<NSDictionary> _quickLookContent;
111 #endif
112 };
113
114 static inline WebDataSourcePrivate* toPrivate(void* privateAttribute)
115 {
116     return reinterpret_cast<WebDataSourcePrivate*>(privateAttribute);
117 }
118
119 @interface WebDataSource (WebFileInternal)
120 @end
121
122 @implementation WebDataSource (WebFileInternal)
123
124 - (void)_setRepresentation:(id<WebDocumentRepresentation>)representation
125 {
126     toPrivate(_private)->representation = representation;
127     toPrivate(_private)->representationFinishedLoading = NO;
128 }
129
130 static inline void addTypesFromClass(NSMutableDictionary *allTypes, Class objCClass, NSArray *supportTypes)
131 {
132     NSEnumerator *enumerator = [supportTypes objectEnumerator];
133     ASSERT(enumerator != nil);
134     NSString *mime = nil;
135     while ((mime = [enumerator nextObject]) != nil) {
136         // Don't clobber previously-registered classes.
137         if ([allTypes objectForKey:mime] == nil)
138             [allTypes setObject:objCClass forKey:mime];
139     }
140 }
141
142 + (Class)_representationClassForMIMEType:(NSString *)MIMEType allowingPlugins:(BOOL)allowPlugins
143 {
144     Class repClass;
145     return [WebView _viewClass:nil andRepresentationClass:&repClass forMIMEType:MIMEType allowingPlugins:allowPlugins] ? repClass : nil;
146 }
147 @end
148
149 @implementation WebDataSource (WebPrivate)
150
151 + (void)initialize
152 {
153     if (self == [WebDataSource class]) {
154 #if !PLATFORM(IOS_FAMILY)
155         JSC::initializeThreading();
156         WTF::initializeMainThreadToProcessMainThread();
157         RunLoop::initializeMainRunLoop();
158 #endif
159     }
160 }
161
162 - (NSError *)_mainDocumentError
163 {
164     return toPrivate(_private)->loader->mainDocumentError();
165 }
166
167 - (void)_addSubframeArchives:(NSArray *)subframeArchives
168 {
169     // FIXME: This SPI is poor, poor design. Can we come up with another solution for those who need it?
170     for (WebArchive *archive in subframeArchives)
171         toPrivate(_private)->loader->addAllArchiveResources(*[archive _coreLegacyWebArchive]);
172 }
173
174 #if !PLATFORM(IOS_FAMILY)
175
176 - (NSFileWrapper *)_fileWrapperForURL:(NSURL *)URL
177 {
178     if ([URL isFileURL])
179         return [[[NSFileWrapper alloc] initWithURL:[URL URLByResolvingSymlinksInPath] options:0 error:nullptr] autorelease];
180
181     if (auto resource = [self subresourceForURL:URL])
182         return [resource _fileWrapperRepresentation];
183
184     if (auto cachedResponse = [[self _webView] _cachedResponseForURL:URL]) {
185         NSFileWrapper *wrapper = [[[NSFileWrapper alloc] initRegularFileWithContents:[cachedResponse data]] autorelease];
186         [wrapper setPreferredFilename:[[cachedResponse response] suggestedFilename]];
187         return wrapper;
188     }
189     
190     return nil;
191 }
192
193 #endif
194
195 - (NSString *)_responseMIMEType
196 {
197     return [[self response] MIMEType];
198 }
199
200 - (void)_setDeferMainResourceDataLoad:(BOOL)flag
201 {
202     toPrivate(_private)->loader->setDeferMainResourceDataLoad(flag);
203 }
204
205 #if PLATFORM(IOS_FAMILY)
206 - (void)_setOverrideTextEncodingName:(NSString *)encoding
207 {
208     toPrivate(_private)->loader->setOverrideEncoding([encoding UTF8String]);
209 }
210 #endif
211
212 - (void)_setAllowToBeMemoryMapped
213 {
214 }
215
216 - (void)setDataSourceDelegate:(NSObject<WebDataSourcePrivateDelegate> *)delegate
217 {
218 }
219
220 - (NSObject<WebDataSourcePrivateDelegate> *)dataSourceDelegate
221 {
222     return nullptr;
223 }
224
225 #if PLATFORM(IOS_FAMILY)
226 - (NSDictionary *)_quickLookContent
227 {
228 #if USE(QUICK_LOOK)
229     return toPrivate(_private)->_quickLookContent.get();
230 #else
231     return nil;
232 #endif
233 }
234 #endif
235
236 @end
237
238 @implementation WebDataSource (WebInternal)
239
240 - (void)_finishedLoading
241 {
242     toPrivate(_private)->representationFinishedLoading = YES;
243     [[self representation] finishedLoadingWithDataSource:self];
244 }
245
246 - (void)_receivedData:(NSData *)data
247 {
248     // protect self temporarily, as the bridge receivedData call could remove our last ref
249     RetainPtr<WebDataSource*> protect(self);
250     
251     [[self representation] receivedData:data withDataSource:self];
252     [[[[self webFrame] frameView] documentView] dataSourceUpdated:self];
253 }
254
255 - (void)_setMainDocumentError:(NSError *)error
256 {
257     if (!toPrivate(_private)->representationFinishedLoading) {
258         toPrivate(_private)->representationFinishedLoading = YES;
259         [[self representation] receivedError:error withDataSource:self];
260     }
261 }
262
263 - (void)_revertToProvisionalState
264 {
265     [self _setRepresentation:nil];
266 }
267
268 + (NSMutableDictionary *)_repTypesAllowImageTypeOmission:(BOOL)allowImageTypeOmission
269 {
270     static NSMutableDictionary *repTypes = nil;
271     static BOOL addedImageTypes = NO;
272     
273     if (!repTypes) {
274         repTypes = [[NSMutableDictionary alloc] init];
275         addTypesFromClass(repTypes, [WebHTMLRepresentation class], [WebHTMLRepresentation supportedNonImageMIMETypes]);
276         addTypesFromClass(repTypes, [WebHTMLRepresentation class], [WebHTMLRepresentation supportedMediaMIMETypes]);
277         
278         // Since this is a "secret default" we don't both registering it.
279         BOOL omitPDFSupport = [[NSUserDefaults standardUserDefaults] boolForKey:@"WebKitOmitPDFSupport"];
280         if (!omitPDFSupport)
281 #if PLATFORM(IOS_FAMILY)
282 #define WebPDFRepresentation ([WebView _getPDFRepresentationClass])
283 #endif
284             addTypesFromClass(repTypes, [WebPDFRepresentation class], [WebPDFRepresentation supportedMIMETypes]);
285 #if PLATFORM(IOS_FAMILY)
286 #undef WebPDFRepresentation
287 #endif
288     }
289     
290     if (!addedImageTypes && !allowImageTypeOmission) {
291         addTypesFromClass(repTypes, [WebHTMLRepresentation class], [WebHTMLRepresentation supportedImageMIMETypes]);
292         addedImageTypes = YES;
293     }
294     
295     return repTypes;
296 }
297
298 - (void)_replaceSelectionWithArchive:(WebArchive *)archive selectReplacement:(BOOL)selectReplacement
299 {
300     DOMDocumentFragment *fragment = [self _documentFragmentWithArchive:archive];
301     if (fragment)
302         [[self webFrame] _replaceSelectionWithFragment:fragment selectReplacement:selectReplacement smartReplace:NO matchStyle:NO];
303 }
304
305 // FIXME: There are few reasons why this method and many of its related methods can't be pushed entirely into WebCore in the future.
306 - (DOMDocumentFragment *)_documentFragmentWithArchive:(WebArchive *)archive
307 {
308     ASSERT(archive);
309     WebResource *mainResource = [archive mainResource];
310     if (mainResource) {
311         NSString *MIMEType = [mainResource MIMEType];
312         if ([WebView canShowMIMETypeAsHTML:MIMEType]) {
313             NSString *markupString = [[NSString alloc] initWithData:[mainResource data] encoding:NSUTF8StringEncoding];
314
315             // FIXME: seems poor form to do this as a side effect of getting a document fragment
316             toPrivate(_private)->loader->addAllArchiveResources(*[archive _coreLegacyWebArchive]);
317
318             DOMDocumentFragment *fragment = [[self webFrame] _documentFragmentWithMarkupString:markupString baseURLString:[[mainResource URL] _web_originalDataAsString]];
319             [markupString release];
320             return fragment;
321         } else if (MIMETypeRegistry::isSupportedImageMIMEType(MIMEType)) {
322             return [self _documentFragmentWithImageResource:mainResource];
323             
324         }
325     }
326     return nil;
327 }
328
329 - (DOMDocumentFragment *)_documentFragmentWithImageResource:(WebResource *)resource
330 {
331     DOMElement *imageElement = [self _imageElementWithImageResource:resource];
332     if (!imageElement)
333         return nil;
334     DOMDocumentFragment *fragment = [[[self webFrame] DOMDocument] createDocumentFragment];
335     [fragment appendChild:imageElement];
336     return fragment;
337 }
338
339 - (DOMElement *)_imageElementWithImageResource:(WebResource *)resource
340 {
341     if (!resource)
342         return 0;
343     
344     [self addSubresource:resource];
345     
346     DOMElement *imageElement = [[[self webFrame] DOMDocument] createElement:@"img"];
347     
348     // FIXME: calling _web_originalDataAsString on a file URL returns an absolute path. Workaround this.
349     NSURL *URL = [resource URL];
350     [imageElement setAttribute:@"src" value:[URL isFileURL] ? [URL absoluteString] : [URL _web_originalDataAsString]];
351     
352     return imageElement;
353 }
354
355 // May return nil if not initialized with a URL.
356 - (NSURL *)_URL
357 {
358     const URL& url = toPrivate(_private)->loader->url();
359     if (url.isEmpty())
360         return nil;
361     return url;
362 }
363
364 - (WebView *)_webView
365 {
366     return [[self webFrame] webView];
367 }
368
369 - (BOOL)_isDocumentHTML
370 {
371     NSString *MIMEType = [self _responseMIMEType];
372     return [WebView canShowMIMETypeAsHTML:MIMEType];
373 }
374
375 - (void)_makeRepresentation
376 {
377     Class repClass = [[self class] _representationClassForMIMEType:[self _responseMIMEType] allowingPlugins:[[[self _webView] preferences] arePlugInsEnabled]];
378
379 #if PLATFORM(IOS_FAMILY)
380     if ([repClass respondsToSelector:@selector(_representationClassForWebFrame:)])
381         repClass = [repClass performSelector:@selector(_representationClassForWebFrame:) withObject:[self webFrame]];
382 #endif
383
384     // Check if the data source was already bound?
385     if (![[self representation] isKindOfClass:repClass]) {
386         id newRep = repClass != nil ? [(NSObject *)[repClass alloc] init] : nil;
387         [self _setRepresentation:(id <WebDocumentRepresentation>)newRep];
388         [newRep release];
389     }
390
391     id<WebDocumentRepresentation> representation = toPrivate(_private)->representation.get();
392     [representation setDataSource:self];
393 #if PLATFORM(IOS_FAMILY)
394     toPrivate(_private)->loader->setResponseMIMEType([self _responseMIMEType]);
395 #endif
396 }
397
398 - (DocumentLoader*)_documentLoader
399 {
400     return toPrivate(_private)->loader.ptr();
401 }
402
403 - (id)_initWithDocumentLoader:(Ref<WebDocumentLoaderMac>&&)loader
404 {
405     self = [super init];
406     if (!self)
407         return nil;
408
409     _private = static_cast<void*>(new WebDataSourcePrivate(WTFMove(loader)));
410         
411     LOG(Loading, "creating datasource for %@", static_cast<NSURL *>(toPrivate(_private)->loader->request().url()));
412
413     if ((toPrivate(_private)->includedInWebKitStatistics = [[self webFrame] _isIncludedInWebKitStatistics]))
414         ++WebDataSourceCount;
415
416     return self;
417 }
418
419 #if USE(QUICK_LOOK)
420 - (void)_setQuickLookContent:(NSDictionary *)quickLookContent
421 {
422     toPrivate(_private)->_quickLookContent = adoptNS([quickLookContent copy]);
423 }
424 #endif
425
426 @end
427
428 @implementation WebDataSource
429
430 - (instancetype)initWithRequest:(NSURLRequest *)request
431 {
432     return [self _initWithDocumentLoader:WebDocumentLoaderMac::create(request, SubstituteData())];
433 }
434
435 - (void)dealloc
436 {
437     if (WebCoreObjCScheduleDeallocateOnMainThread([WebDataSource class], self))
438         return;
439
440     if (toPrivate(_private) && toPrivate(_private)->includedInWebKitStatistics)
441         --WebDataSourceCount;
442
443 #if USE(QUICK_LOOK)
444     // Added in -[WebCoreResourceHandleAsDelegate connection:didReceiveResponse:].
445     if (NSURL *url = [[self response] URL])
446         removeQLPreviewConverterForURL(url);
447 #endif
448
449     delete toPrivate(_private);
450
451     [super dealloc];
452 }
453
454 - (NSData *)data
455 {
456     RefPtr<SharedBuffer> mainResourceData = toPrivate(_private)->loader->mainResourceData();
457     if (!mainResourceData)
458         return nil;
459     return mainResourceData->createNSData().autorelease();
460 }
461
462 - (id <WebDocumentRepresentation>)representation
463 {
464     return toPrivate(_private)->representation.get();
465 }
466
467 - (WebFrame *)webFrame
468 {
469     if (Frame* frame = toPrivate(_private)->loader->frame())
470         return kit(frame);
471
472     return nil;
473 }
474
475 - (NSURLRequest *)initialRequest
476 {
477     return toPrivate(_private)->loader->originalRequest().nsURLRequest(HTTPBodyUpdatePolicy::UpdateHTTPBody);
478 }
479
480 - (NSMutableURLRequest *)request
481 {
482     FrameLoader* frameLoader = toPrivate(_private)->loader->frameLoader();
483     if (!frameLoader || !frameLoader->frameHasLoaded())
484         return nil;
485
486     // FIXME: this cast is dubious
487     return (NSMutableURLRequest *)toPrivate(_private)->loader->request().nsURLRequest(HTTPBodyUpdatePolicy::UpdateHTTPBody);
488 }
489
490 - (NSURLResponse *)response
491 {
492     return toPrivate(_private)->loader->response().nsURLResponse();
493 }
494
495 - (NSString *)textEncodingName
496 {
497     NSString *textEncodingName = toPrivate(_private)->loader->overrideEncoding();
498     if (!textEncodingName)
499         textEncodingName = [[self response] textEncodingName];
500     return textEncodingName;
501 }
502
503 - (BOOL)isLoading
504 {
505     return toPrivate(_private)->loader->isLoadingInAPISense();
506 }
507
508 // Returns nil or the page title.
509 - (NSString *)pageTitle
510 {
511     return [[self representation] title];
512 }
513
514 - (NSURL *)unreachableURL
515 {
516     const URL& unreachableURL = toPrivate(_private)->loader->unreachableURL();
517     if (unreachableURL.isEmpty())
518         return nil;
519     return unreachableURL;
520 }
521
522 - (WebArchive *)webArchive
523 {
524     // it makes no sense to grab a WebArchive from an uncommitted document.
525     if (!toPrivate(_private)->loader->isCommitted())
526         return nil;
527         
528     return [[[WebArchive alloc] _initWithCoreLegacyWebArchive:LegacyWebArchive::create(*core([self webFrame]))] autorelease];
529 }
530
531 - (WebResource *)mainResource
532 {
533     auto coreResource = toPrivate(_private)->loader->mainResource();
534     if (!coreResource)
535         return nil;
536     return [[[WebResource alloc] _initWithCoreResource:coreResource.releaseNonNull()] autorelease];
537 }
538
539 - (NSArray *)subresources
540 {
541     auto coreSubresources = toPrivate(_private)->loader->subresources();
542     auto subresources = adoptNS([[NSMutableArray alloc] initWithCapacity:coreSubresources.size()]);
543     for (auto& coreSubresource : coreSubresources) {
544         if (auto resource = adoptNS([[WebResource alloc] _initWithCoreResource:coreSubresource.copyRef()]))
545             [subresources addObject:resource.get()];
546     }
547     return subresources.autorelease();
548 }
549
550 - (WebResource *)subresourceForURL:(NSURL *)URL
551 {
552     auto subresource = toPrivate(_private)->loader->subresource(URL);
553     return subresource ? [[[WebResource alloc] _initWithCoreResource:subresource.releaseNonNull()] autorelease] : nil;
554 }
555
556 - (void)addSubresource:(WebResource *)subresource
557 {    
558     toPrivate(_private)->loader->addArchiveResource([subresource _coreResource]);
559 }
560
561 @end