Loading should be disabled while constructing the fragment in WebContentReader::readW...
[WebKit-https.git] / Source / WebCore / editing / cocoa / WebContentReaderCocoa.mm
1 /*
2  * Copyright (C) 2006-2017 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  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "WebContentReader.h"
28
29 #import "ArchiveResource.h"
30 #import "Blob.h"
31 #import "CachedResourceLoader.h"
32 #import "DOMURL.h"
33 #import "Document.h"
34 #import "DocumentFragment.h"
35 #import "DocumentLoader.h"
36 #import "Frame.h"
37 #import "FrameLoader.h"
38 #import "FrameLoaderClient.h"
39 #import "HTMLBodyElement.h"
40 #import "HTMLImageElement.h"
41 #import "LegacyWebArchive.h"
42 #import "Page.h"
43 #import "Settings.h"
44 #import "WebArchiveResourceFromNSAttributedString.h"
45 #import "WebArchiveResourceWebResourceHandler.h"
46 #import "WebNSAttributedStringExtras.h"
47 #import "markup.h"
48 #import <pal/spi/cocoa/NSAttributedStringSPI.h>
49 #import <wtf/SoftLinking.h>
50
51 #if (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300)
52 @interface NSAttributedString ()
53 - (NSString *)_htmlDocumentFragmentString:(NSRange)range documentAttributes:(NSDictionary *)dict subresources:(NSArray **)subresources;
54 @end
55 #elif PLATFORM(IOS)
56 SOFT_LINK_PRIVATE_FRAMEWORK(WebKitLegacy)
57 #elif PLATFORM(MAC)
58 SOFT_LINK_FRAMEWORK_IN_UMBRELLA(WebKit, WebKitLegacy)
59 #endif
60
61 #if (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED < 110000) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101300)
62 SOFT_LINK(WebKitLegacy, _WebCreateFragment, void, (WebCore::Document& document, NSAttributedString *string, WebCore::FragmentAndResources& result), (document, string, result))
63 #endif
64
65 namespace WebCore {
66
67 #if (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300)
68
69 static NSDictionary *attributesForAttributedStringConversion()
70 {
71     // This function needs to be kept in sync with identically named one in WebKitLegacy, which is used on older OS versions.
72     RetainPtr<NSArray> excludedElements = adoptNS([[NSArray alloc] initWithObjects:
73         // Omit style since we want style to be inline so the fragment can be easily inserted.
74         @"style",
75         // Omit xml so the result is not XHTML.
76         @"xml",
77         // Omit tags that will get stripped when converted to a fragment anyway.
78         @"doctype", @"html", @"head", @"body",
79         // Omit deprecated tags.
80         @"applet", @"basefont", @"center", @"dir", @"font", @"menu", @"s", @"strike", @"u",
81         // Omit object so no file attachments are part of the fragment.
82         @"object", nil]);
83
84 #if PLATFORM(IOS)
85     static NSString * const NSExcludedElementsDocumentAttribute = @"ExcludedElements";
86 #endif
87
88     return @{
89         NSExcludedElementsDocumentAttribute: excludedElements.get(),
90         @"InterchangeNewline": @YES,
91         @"CoalesceTabSpans": @YES,
92         @"OutputBaseURL": [(NSURL *)URL::fakeURLWithRelativePart(emptyString()) retain], // The value needs +1 refcount, as NSAttributedString over-releases it.
93         @"WebResourceHandler": [WebArchiveResourceWebResourceHandler new],
94     };
95 }
96
97 static FragmentAndResources createFragment(Frame& frame, NSAttributedString *string)
98 {
99     FragmentAndResources result;
100     Document& document = *frame.document();
101
102     NSArray *subresources = nil;
103     NSString *fragmentString = [string _htmlDocumentFragmentString:NSMakeRange(0, [string length]) documentAttributes:attributesForAttributedStringConversion() subresources:&subresources];
104     auto fragment = DocumentFragment::create(document);
105     fragment->parseHTML(fragmentString, document.body(), DisallowScriptingAndPluginContent);
106
107     result.fragment = WTFMove(fragment);
108     for (WebArchiveResourceFromNSAttributedString *resource in subresources)
109         result.resources.append(*resource->resource);
110
111     return result;
112 }
113
114 #else
115
116 static FragmentAndResources createFragment(Frame& frame, NSAttributedString *string)
117 {
118     FragmentAndResources result;
119     _WebCreateFragment(*frame.document(), string, result);
120     return result;
121 }
122
123 #endif
124
125 class DeferredLoadingScope {
126 public:
127     DeferredLoadingScope(Frame& frame)
128         : m_frame(frame)
129         , m_cachedResourceLoader(frame.document()->cachedResourceLoader())
130     {
131         if (!frame.page()->defersLoading()) {
132             frame.page()->setDefersLoading(true);
133             m_didEnabledDeferredLoading = true;
134         }
135
136         if (m_cachedResourceLoader->imagesEnabled()) {
137             m_cachedResourceLoader->setImagesEnabled(false);
138             m_didDisableImage = true;
139         }
140     }
141
142     ~DeferredLoadingScope()
143     {
144         if (m_didEnabledDeferredLoading)
145             m_cachedResourceLoader->setImagesEnabled(true);
146         if (m_didDisableImage)
147             m_frame->page()->setDefersLoading(false);
148     }
149
150 private:
151     Ref<Frame> m_frame;
152     Ref<CachedResourceLoader> m_cachedResourceLoader;
153     bool m_didEnabledDeferredLoading { false };
154     bool m_didDisableImage { false };
155 };
156
157 RefPtr<DocumentFragment> createFragmentAndAddResources(Frame& frame, NSAttributedString *string)
158 {
159     if (!frame.page() || !frame.document())
160         return nullptr;
161
162     auto& document = *frame.document();
163     if (!document.isHTMLDocument() || !string)
164         return nullptr;
165
166     DeferredLoadingScope scope(frame);
167     auto fragmentAndResources = createFragment(frame, string);
168     if (!fragmentAndResources.fragment)
169         return nullptr;
170
171     HashMap<AtomicString, AtomicString> blobURLMap;
172     for (const Ref<ArchiveResource>& subresource : fragmentAndResources.resources) {
173         auto blob = Blob::create(subresource->data(), subresource->mimeType());
174         String blobURL = DOMURL::createObjectURL(document, blob);
175         blobURLMap.set(subresource->url().string(), blobURL);
176     }
177     replaceSubresourceURLs(*fragmentAndResources.fragment, WTFMove(blobURLMap));
178
179     return WTFMove(fragmentAndResources.fragment);
180 }
181
182 bool WebContentReader::readWebArchive(SharedBuffer& buffer)
183 {
184     if (frame.settings().preferMIMETypeForImages() || !frame.document())
185         return false;
186
187     auto archive = LegacyWebArchive::create(URL(), buffer);
188     if (!archive)
189         return false;
190
191     RefPtr<ArchiveResource> mainResource = archive->mainResource();
192     if (!mainResource)
193         return false;
194
195     auto type = mainResource->mimeType();
196     if (!frame.loader().client().canShowMIMETypeAsHTML(type))
197         return false;
198
199     DeferredLoadingScope scope(frame);
200     auto markupString = String::fromUTF8(mainResource->data().data(), mainResource->data().size());
201     addFragment(createFragmentFromMarkup(*frame.document(), markupString, mainResource->url(), DisallowScriptingAndPluginContent));
202
203     if (DocumentLoader* loader = frame.loader().documentLoader())
204         loader->addAllArchiveResources(*archive);
205
206     return true;
207 }
208
209 bool WebContentReader::readHTML(const String& string)
210 {
211     String stringOmittingMicrosoftPrefix = string;
212     
213 #if PLATFORM(MAC)
214     // This code was added to make HTML paste from Microsoft Word on Mac work, back in 2004.
215     // It's a simple-minded way to ignore the CF_HTML clipboard format, just skipping over the
216     // description part and parsing the entire context plus fragment.
217     if (string.startsWith("Version:")) {
218         size_t location = string.findIgnoringCase("<html");
219         if (location != notFound)
220             stringOmittingMicrosoftPrefix = string.substring(location);
221     }
222 #endif
223
224     if (stringOmittingMicrosoftPrefix.isEmpty())
225         return false;
226
227     if (!frame.document())
228         return false;
229     Document& document = *frame.document();
230
231     addFragment(createFragmentFromMarkup(document, stringOmittingMicrosoftPrefix, emptyString(), DisallowScriptingAndPluginContent));
232     return true;
233 }
234
235 bool WebContentReader::readRTFD(SharedBuffer& buffer)
236 {
237     if (frame.settings().preferMIMETypeForImages())
238         return false;
239
240     auto fragment = createFragmentAndAddResources(frame, adoptNS([[NSAttributedString alloc] initWithRTFD:buffer.createNSData().get() documentAttributes:nullptr]).get());
241     if (!fragment)
242         return false;
243     addFragment(fragment.releaseNonNull());
244
245     return true;
246 }
247
248 bool WebContentReader::readRTF(SharedBuffer& buffer)
249 {
250     if (frame.settings().preferMIMETypeForImages())
251         return false;
252
253     auto fragment = createFragmentAndAddResources(frame, adoptNS([[NSAttributedString alloc] initWithRTF:buffer.createNSData().get() documentAttributes:nullptr]).get());
254     if (!fragment)
255         return false;
256     addFragment(fragment.releaseNonNull());
257
258     return true;
259 }
260
261 bool WebContentReader::readPlainText(const String& text)
262 {
263     if (!allowPlainText)
264         return false;
265
266     addFragment(createFragmentFromText(context, [text precomposedStringWithCanonicalMapping]));
267
268     madeFragmentFromPlainText = true;
269     return true;
270 }
271
272 bool WebContentReader::readImage(Ref<SharedBuffer>&& buffer, const String& type)
273 {
274     auto blob = Blob::create(buffer.get(), type);
275     ASSERT(frame.document());
276     auto& document = *frame.document();
277     String blobURL = DOMURL::createObjectURL(document, blob);
278     addFragment(createFragmentForImageAndURL(document, blobURL));
279     return true;
280 }
281
282 }