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