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