Unify more WebKitLegacy sources
[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/PreviewLoaderClient.h>
57 #import <WebCore/ResourceRequest.h>
58 #import <WebCore/SharedBuffer.h>
59 #import <WebCore/WebCoreObjCExtras.h>
60 #import <WebCore/WebCoreURLResponse.h>
61 #import <WebKitLegacy/DOMHTML.h>
62 #import <WebKitLegacy/DOMPrivate.h>
63 #import <wtf/Assertions.h>
64 #import <wtf/MainThread.h>
65 #import <wtf/RefPtr.h>
66 #import <wtf/RetainPtr.h>
67 #import <wtf/RunLoop.h>
68 #import <wtf/URL.h>
69
70 #if PLATFORM(IOS_FAMILY)
71 #import "WebPDFViewIOS.h"
72 #endif
73
74 #if USE(QUICK_LOOK)
75 #import <WebCore/QuickLook.h>
76 #endif
77
78 class WebDataSourcePrivate
79 {
80 public:
81     WebDataSourcePrivate(Ref<WebDocumentLoaderMac>&& loader)
82         : loader(WTFMove(loader))
83         , representationFinishedLoading(NO)
84         , includedInWebKitStatistics(NO)
85 #if PLATFORM(IOS_FAMILY)
86         , _dataSourceDelegate(nil)
87 #endif
88     {
89     }
90     ~WebDataSourcePrivate()
91     {
92         // We might run in to infinite recursion if we're stopping loading as the result of detaching from the frame.
93         // Therefore, DocumentLoader::detachFromFrame() did some smart things to stop the recursion.
94         // As a result of breaking the resursion, DocumentLoader::m_subresourceLoader
95         // and DocumentLoader::m_plugInStreamLoaders might not be empty at this time.
96         // See <rdar://problem/9673866> for more details.
97         ASSERT(!loader->isLoading() || loader->isStopping());
98         loader->detachDataSource();
99     }
100
101     Ref<WebDocumentLoaderMac> loader;
102     RetainPtr<id<WebDocumentRepresentation> > representation;
103     BOOL representationFinishedLoading;
104     BOOL includedInWebKitStatistics;
105 #if PLATFORM(IOS_FAMILY)
106     NSObject<WebDataSourcePrivateDelegate> *_dataSourceDelegate;
107 #endif
108 #if USE(QUICK_LOOK)
109     RetainPtr<NSDictionary> _quickLookContent;
110     RefPtr<WebCore::PreviewLoaderClient> _quickLookPreviewLoaderClient;
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 (WebCore::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 - (WebCore::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 - (WebCore::PreviewLoaderClient*)_quickLookPreviewLoaderClient
421 {
422     return toPrivate(_private)->_quickLookPreviewLoaderClient.get();
423 }
424
425 - (void)_setQuickLookContent:(NSDictionary *)quickLookContent
426 {
427     toPrivate(_private)->_quickLookContent = adoptNS([quickLookContent copy]);
428 }
429
430 - (void)_setQuickLookPreviewLoaderClient:(WebCore::PreviewLoaderClient*)quickLookPreviewLoaderClient
431 {
432     toPrivate(_private)->_quickLookPreviewLoaderClient = quickLookPreviewLoaderClient;
433 }
434 #endif
435
436 @end
437
438 @implementation WebDataSource
439
440 - (instancetype)initWithRequest:(NSURLRequest *)request
441 {
442     return [self _initWithDocumentLoader:WebDocumentLoaderMac::create(request, WebCore::SubstituteData())];
443 }
444
445 - (void)dealloc
446 {
447     if (WebCoreObjCScheduleDeallocateOnMainThread([WebDataSource class], self))
448         return;
449
450     if (toPrivate(_private) && toPrivate(_private)->includedInWebKitStatistics)
451         --WebDataSourceCount;
452
453 #if USE(QUICK_LOOK)
454     // Added in -[WebCoreResourceHandleAsDelegate connection:didReceiveResponse:].
455     if (NSURL *url = [[self response] URL])
456         WebCore::removeQLPreviewConverterForURL(url);
457 #endif
458
459     delete toPrivate(_private);
460
461     [super dealloc];
462 }
463
464 - (NSData *)data
465 {
466     RefPtr<WebCore::SharedBuffer> mainResourceData = toPrivate(_private)->loader->mainResourceData();
467     if (!mainResourceData)
468         return nil;
469     return mainResourceData->createNSData().autorelease();
470 }
471
472 - (id <WebDocumentRepresentation>)representation
473 {
474     return toPrivate(_private)->representation.get();
475 }
476
477 - (WebFrame *)webFrame
478 {
479     if (auto* frame = toPrivate(_private)->loader->frame())
480         return kit(frame);
481
482     return nil;
483 }
484
485 - (NSURLRequest *)initialRequest
486 {
487     return toPrivate(_private)->loader->originalRequest().nsURLRequest(WebCore::HTTPBodyUpdatePolicy::UpdateHTTPBody);
488 }
489
490 - (NSMutableURLRequest *)request
491 {
492     auto* frameLoader = toPrivate(_private)->loader->frameLoader();
493     if (!frameLoader || !frameLoader->frameHasLoaded())
494         return nil;
495
496     // FIXME: this cast is dubious
497     return (NSMutableURLRequest *)toPrivate(_private)->loader->request().nsURLRequest(WebCore::HTTPBodyUpdatePolicy::UpdateHTTPBody);
498 }
499
500 - (NSURLResponse *)response
501 {
502     return toPrivate(_private)->loader->response().nsURLResponse();
503 }
504
505 - (NSString *)textEncodingName
506 {
507     NSString *textEncodingName = toPrivate(_private)->loader->overrideEncoding();
508     if (!textEncodingName)
509         textEncodingName = [[self response] textEncodingName];
510     return textEncodingName;
511 }
512
513 - (BOOL)isLoading
514 {
515     return toPrivate(_private)->loader->isLoadingInAPISense();
516 }
517
518 // Returns nil or the page title.
519 - (NSString *)pageTitle
520 {
521     return [[self representation] title];
522 }
523
524 - (NSURL *)unreachableURL
525 {
526     const URL& unreachableURL = toPrivate(_private)->loader->unreachableURL();
527     if (unreachableURL.isEmpty())
528         return nil;
529     return unreachableURL;
530 }
531
532 - (WebArchive *)webArchive
533 {
534     // it makes no sense to grab a WebArchive from an uncommitted document.
535     if (!toPrivate(_private)->loader->isCommitted())
536         return nil;
537         
538     return [[[WebArchive alloc] _initWithCoreLegacyWebArchive:WebCore::LegacyWebArchive::create(*core([self webFrame]))] autorelease];
539 }
540
541 - (WebResource *)mainResource
542 {
543     auto coreResource = toPrivate(_private)->loader->mainResource();
544     if (!coreResource)
545         return nil;
546     return [[[WebResource alloc] _initWithCoreResource:coreResource.releaseNonNull()] autorelease];
547 }
548
549 - (NSArray *)subresources
550 {
551     auto coreSubresources = toPrivate(_private)->loader->subresources();
552     auto subresources = adoptNS([[NSMutableArray alloc] initWithCapacity:coreSubresources.size()]);
553     for (auto& coreSubresource : coreSubresources) {
554         if (auto resource = adoptNS([[WebResource alloc] _initWithCoreResource:coreSubresource.copyRef()]))
555             [subresources addObject:resource.get()];
556     }
557     return subresources.autorelease();
558 }
559
560 - (WebResource *)subresourceForURL:(NSURL *)URL
561 {
562     auto subresource = toPrivate(_private)->loader->subresource(URL);
563     return subresource ? [[[WebResource alloc] _initWithCoreResource:subresource.releaseNonNull()] autorelease] : nil;
564 }
565
566 - (void)addSubresource:(WebResource *)subresource
567 {    
568     toPrivate(_private)->loader->addArchiveResource([subresource _coreResource]);
569 }
570
571 @end