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