Move URL from WebCore to WTF
[WebKit-https.git] / Source / WebCore / loader / archive / cf / LegacyWebArchive.cpp
1 /*
2  * Copyright (C) 2008, 2009 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 #include "config.h"
30 #include "LegacyWebArchive.h"
31
32 #include "CachedResource.h"
33 #include "Document.h"
34 #include "DocumentLoader.h"
35 #include "Editor.h"
36 #include "EditorClient.h"
37 #include "Frame.h"
38 #include "FrameLoader.h"
39 #include "FrameSelection.h"
40 #include "FrameTree.h"
41 #include "HTMLAttachmentElement.h"
42 #include "HTMLFrameElement.h"
43 #include "HTMLFrameOwnerElement.h"
44 #include "HTMLIFrameElement.h"
45 #include "HTMLNames.h"
46 #include "HTMLObjectElement.h"
47 #include "Image.h"
48 #include "Logging.h"
49 #include "MemoryCache.h"
50 #include "Page.h"
51 #include "Range.h"
52 #include "RuntimeEnabledFeatures.h"
53 #include "SerializedAttachmentData.h"
54 #include "Settings.h"
55 #include "SharedBuffer.h"
56 #include "markup.h"
57 #include <wtf/ListHashSet.h>
58 #include <wtf/RetainPtr.h>
59 #include <wtf/URLHash.h>
60 #include <wtf/text/StringBuilder.h>
61 #include <wtf/text/CString.h>
62
63 namespace WebCore {
64
65 static const CFStringRef LegacyWebArchiveMainResourceKey = CFSTR("WebMainResource");
66 static const CFStringRef LegacyWebArchiveSubresourcesKey = CFSTR("WebSubresources");
67 static const CFStringRef LegacyWebArchiveSubframeArchivesKey = CFSTR("WebSubframeArchives");
68 static const CFStringRef LegacyWebArchiveResourceDataKey = CFSTR("WebResourceData");
69 static const CFStringRef LegacyWebArchiveResourceFrameNameKey = CFSTR("WebResourceFrameName");
70 static const CFStringRef LegacyWebArchiveResourceMIMETypeKey = CFSTR("WebResourceMIMEType");
71 static const CFStringRef LegacyWebArchiveResourceURLKey = CFSTR("WebResourceURL");
72 static const CFStringRef LegacyWebArchiveResourceTextEncodingNameKey = CFSTR("WebResourceTextEncodingName");
73 static const CFStringRef LegacyWebArchiveResourceResponseKey = CFSTR("WebResourceResponse");
74 static const CFStringRef LegacyWebArchiveResourceResponseVersionKey = CFSTR("WebResourceResponseVersion");
75
76 RetainPtr<CFDictionaryRef> LegacyWebArchive::createPropertyListRepresentation(ArchiveResource* resource, MainResourceStatus isMainResource)
77 {
78     if (!resource) {
79         // The property list representation of a null/empty WebResource has the following 3 objects stored as nil.
80         // FIXME: 0 is not serializable. Presumably we need to use kCFNull here instead for compatibility.
81         // FIXME: But why do we need to support a resource of 0? Who relies on that?
82         RetainPtr<CFMutableDictionaryRef> propertyList = adoptCF(CFDictionaryCreateMutable(0, 3, 0, 0));
83         CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceDataKey, 0);
84         CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceURLKey, 0);
85         CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceMIMETypeKey, 0);
86         return propertyList;
87     }
88
89     auto propertyList = adoptCF(CFDictionaryCreateMutable(0, 6, 0, &kCFTypeDictionaryValueCallBacks));
90
91     // Resource data can be empty, but must be represented by an empty CFDataRef
92     auto& data = resource->data();
93
94     CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceDataKey, data.createCFData().get());
95
96     // Resource URL cannot be null
97     if (auto cfURL = resource->url().string().createCFString())
98         CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceURLKey, cfURL.get());
99     else {
100         LOG(Archives, "LegacyWebArchive - NULL resource URL is invalid - returning null property list");
101         return nullptr;
102     }
103
104     // FrameName should be left out if empty for subresources, but always included for main resources
105     auto& frameName = resource->frameName();
106     if (!frameName.isEmpty() || isMainResource)
107         CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceFrameNameKey, frameName.createCFString().get());
108
109     // Set MIMEType, TextEncodingName, and ResourceResponse only if they actually exist
110     auto& mimeType = resource->mimeType();
111     if (!mimeType.isEmpty())
112         CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceMIMETypeKey, mimeType.createCFString().get());
113
114     auto& textEncoding = resource->textEncoding();
115     if (!textEncoding.isEmpty())
116         CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceTextEncodingNameKey, textEncoding.createCFString().get());
117
118     // Don't include the resource response for the main resource
119     if (!isMainResource) {
120         if (auto resourceResponseData = createPropertyListRepresentation(resource->response()))
121             CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceResponseKey, resourceResponseData.get());    
122     }
123     
124     return propertyList;
125 }
126
127 RetainPtr<CFDictionaryRef> LegacyWebArchive::createPropertyListRepresentation(Archive& archive)
128 {
129     auto propertyList = adoptCF(CFDictionaryCreateMutable(0, 3, 0, &kCFTypeDictionaryValueCallBacks));
130
131     auto mainResourceDict = createPropertyListRepresentation(archive.mainResource(), MainResource);
132     ASSERT(mainResourceDict);
133     if (!mainResourceDict)
134         return nullptr;
135     CFDictionarySetValue(propertyList.get(), LegacyWebArchiveMainResourceKey, mainResourceDict.get());
136
137     auto subresourcesArray = adoptCF(CFArrayCreateMutable(0, archive.subresources().size(), &kCFTypeArrayCallBacks));
138     for (auto& resource : archive.subresources()) {
139         if (auto subresource = createPropertyListRepresentation(resource.ptr(), Subresource))
140             CFArrayAppendValue(subresourcesArray.get(), subresource.get());
141         else
142             LOG(Archives, "LegacyWebArchive - Failed to create property list for subresource");
143     }
144     if (CFArrayGetCount(subresourcesArray.get()))
145         CFDictionarySetValue(propertyList.get(), LegacyWebArchiveSubresourcesKey, subresourcesArray.get());
146
147     auto subframesArray = adoptCF(CFArrayCreateMutable(0, archive.subframeArchives().size(), &kCFTypeArrayCallBacks));
148     for (auto& subframe : archive.subframeArchives()) {
149         if (auto subframeArchive = createPropertyListRepresentation(subframe.get()))
150             CFArrayAppendValue(subframesArray.get(), subframeArchive.get());
151         else
152             LOG(Archives, "LegacyWebArchive - Failed to create property list for subframe archive");
153     }
154     if (CFArrayGetCount(subframesArray.get()))
155         CFDictionarySetValue(propertyList.get(), LegacyWebArchiveSubframeArchivesKey, subframesArray.get());
156
157     return propertyList;
158 }
159
160 ResourceResponse LegacyWebArchive::createResourceResponseFromPropertyListData(CFDataRef data, CFStringRef responseDataType)
161 {
162     ASSERT(data);
163     if (!data)
164         return ResourceResponse();
165
166     // If the ResourceResponseVersion (passed in as responseDataType) exists at all, this is a "new" web archive that we
167     // can parse well in a cross platform manner If it doesn't exist, we will assume this is an "old" web archive with,
168     // NSURLResponse objects in it and parse the ResourceResponse as such.
169     if (!responseDataType)
170         return createResourceResponseFromMacArchivedData(data);
171
172     // FIXME: Parse the "new" format that the above comment references here. This format doesn't exist yet.
173     return ResourceResponse();
174 }
175
176 RefPtr<ArchiveResource> LegacyWebArchive::createResource(CFDictionaryRef dictionary)
177 {
178     ASSERT(dictionary);
179     if (!dictionary)
180         return nullptr;
181
182     auto resourceData = static_cast<CFDataRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveResourceDataKey));
183     if (resourceData && CFGetTypeID(resourceData) != CFDataGetTypeID()) {
184         LOG(Archives, "LegacyWebArchive - Resource data is not of type CFData, cannot create invalid resource");
185         return nullptr;
186     }
187
188     auto frameName = static_cast<CFStringRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveResourceFrameNameKey));
189     if (frameName && CFGetTypeID(frameName) != CFStringGetTypeID()) {
190         LOG(Archives, "LegacyWebArchive - Frame name is not of type CFString, cannot create invalid resource");
191         return nullptr;
192     }
193
194     auto mimeType = static_cast<CFStringRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveResourceMIMETypeKey));
195     if (mimeType && CFGetTypeID(mimeType) != CFStringGetTypeID()) {
196         LOG(Archives, "LegacyWebArchive - MIME type is not of type CFString, cannot create invalid resource");
197         return nullptr;
198     }
199
200     auto url = static_cast<CFStringRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveResourceURLKey));
201     if (url && CFGetTypeID(url) != CFStringGetTypeID()) {
202         LOG(Archives, "LegacyWebArchive - URL is not of type CFString, cannot create invalid resource");
203         return nullptr;
204     }
205
206     auto textEncoding = static_cast<CFStringRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveResourceTextEncodingNameKey));
207     if (textEncoding && CFGetTypeID(textEncoding) != CFStringGetTypeID()) {
208         LOG(Archives, "LegacyWebArchive - Text encoding is not of type CFString, cannot create invalid resource");
209         return nullptr;
210     }
211
212     ResourceResponse response;
213
214     if (auto resourceResponseData = static_cast<CFDataRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveResourceResponseKey))) {
215         if (CFGetTypeID(resourceResponseData) != CFDataGetTypeID()) {
216             LOG(Archives, "LegacyWebArchive - Resource response data is not of type CFData, cannot create invalid resource");
217             return nullptr;
218         }
219         
220         auto resourceResponseVersion = static_cast<CFStringRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveResourceResponseVersionKey));
221         if (resourceResponseVersion && CFGetTypeID(resourceResponseVersion) != CFStringGetTypeID()) {
222             LOG(Archives, "LegacyWebArchive - Resource response version is not of type CFString, cannot create invalid resource");
223             return nullptr;
224         }
225         
226         response = createResourceResponseFromPropertyListData(resourceResponseData, resourceResponseVersion);
227     }
228
229     return ArchiveResource::create(SharedBuffer::create(resourceData), URL(URL(), url), mimeType, textEncoding, frameName, response);
230 }
231
232 Ref<LegacyWebArchive> LegacyWebArchive::create()
233 {
234     return adoptRef(*new LegacyWebArchive);
235 }
236
237 Ref<LegacyWebArchive> LegacyWebArchive::create(Ref<ArchiveResource>&& mainResource, Vector<Ref<ArchiveResource>>&& subresources, Vector<Ref<LegacyWebArchive>>&& subframeArchives)
238 {
239     auto archive = create();
240
241     archive->setMainResource(WTFMove(mainResource));
242
243     for (auto& subresource : subresources)
244         archive->addSubresource(WTFMove(subresource));
245
246     for (auto& subframeArchive : subframeArchives)
247         archive->addSubframeArchive(WTFMove(subframeArchive));
248
249     return archive;
250 }
251
252 RefPtr<LegacyWebArchive> LegacyWebArchive::create(SharedBuffer& data)
253 {
254     return create(URL(), data);
255 }
256
257 RefPtr<LegacyWebArchive> LegacyWebArchive::create(const URL&, SharedBuffer& data)
258 {
259     LOG(Archives, "LegacyWebArchive - Creating from raw data");
260     
261     Ref<LegacyWebArchive> archive = create();
262         
263     RetainPtr<CFDataRef> cfData = data.createCFData();
264     if (!cfData)
265         return nullptr;
266         
267     CFErrorRef error = nullptr;
268     
269     RetainPtr<CFDictionaryRef> plist = adoptCF(static_cast<CFDictionaryRef>(CFPropertyListCreateWithData(0, cfData.get(), kCFPropertyListImmutable, 0, &error)));
270     if (!plist) {
271 #ifndef NDEBUG
272         RetainPtr<CFStringRef> errorString = error ? adoptCF(CFErrorCopyDescription(error)) : 0;
273         const char* cError = errorString ? CFStringGetCStringPtr(errorString.get(), kCFStringEncodingUTF8) : "unknown error";
274         LOG(Archives, "LegacyWebArchive - Error parsing PropertyList from archive data - %s", cError);
275 #endif
276         if (error)
277             CFRelease(error);
278         return nullptr;
279     }
280     
281     if (CFGetTypeID(plist.get()) != CFDictionaryGetTypeID()) {
282         LOG(Archives, "LegacyWebArchive - Archive property list is not the expected CFDictionary, aborting invalid WebArchive");
283         return nullptr;
284     }
285     
286     if (!archive->extract(plist.get()))
287         return nullptr;
288
289     return WTFMove(archive);
290 }
291
292 bool LegacyWebArchive::extract(CFDictionaryRef dictionary)
293 {
294     ASSERT(dictionary);
295     if (!dictionary) {
296         LOG(Archives, "LegacyWebArchive - Null root CFDictionary, aborting invalid WebArchive");
297         return false;
298     }
299     
300     CFDictionaryRef mainResourceDict = static_cast<CFDictionaryRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveMainResourceKey));
301     if (!mainResourceDict) {
302         LOG(Archives, "LegacyWebArchive - No main resource in archive, aborting invalid WebArchive");
303         return false;
304     }
305     if (CFGetTypeID(mainResourceDict) != CFDictionaryGetTypeID()) {
306         LOG(Archives, "LegacyWebArchive - Main resource is not the expected CFDictionary, aborting invalid WebArchive");
307         return false;
308     }
309
310     auto mainResource = createResource(mainResourceDict);
311     if (!mainResource) {
312         LOG(Archives, "LegacyWebArchive - Failed to parse main resource from CFDictionary or main resource does not exist, aborting invalid WebArchive");
313         return false;
314     }
315
316     if (mainResource->mimeType().isNull()) {
317         LOG(Archives, "LegacyWebArchive - Main resource MIME type is required, but was null.");
318         return false;
319     }
320
321     setMainResource(mainResource.releaseNonNull());
322
323     auto subresourceArray = static_cast<CFArrayRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveSubresourcesKey));
324     if (subresourceArray && CFGetTypeID(subresourceArray) != CFArrayGetTypeID()) {
325         LOG(Archives, "LegacyWebArchive - Subresources is not the expected Array, aborting invalid WebArchive");
326         return false;
327     }
328
329     if (subresourceArray) {
330         auto count = CFArrayGetCount(subresourceArray);
331         for (CFIndex i = 0; i < count; ++i) {
332             auto subresourceDict = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(subresourceArray, i));
333             if (CFGetTypeID(subresourceDict) != CFDictionaryGetTypeID()) {
334                 LOG(Archives, "LegacyWebArchive - Subresource is not expected CFDictionary, aborting invalid WebArchive");
335                 return false;
336             }
337             
338             if (auto subresource = createResource(subresourceDict))
339                 addSubresource(subresource.releaseNonNull());
340         }
341     }
342
343     auto subframeArray = static_cast<CFArrayRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveSubframeArchivesKey));
344     if (subframeArray && CFGetTypeID(subframeArray) != CFArrayGetTypeID()) {
345         LOG(Archives, "LegacyWebArchive - Subframe archives is not the expected Array, aborting invalid WebArchive");
346         return false;
347     }
348
349     if (subframeArray) {
350         auto count = CFArrayGetCount(subframeArray);
351         for (CFIndex i = 0; i < count; ++i) {
352             auto subframeDict = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(subframeArray, i));
353             if (CFGetTypeID(subframeDict) != CFDictionaryGetTypeID()) {
354                 LOG(Archives, "LegacyWebArchive - Subframe array is not expected CFDictionary, aborting invalid WebArchive");
355                 return false;
356             }
357             
358             auto subframeArchive = create();
359             if (subframeArchive->extract(subframeDict))
360                 addSubframeArchive(WTFMove(subframeArchive));
361             else
362                 LOG(Archives, "LegacyWebArchive - Invalid subframe archive skipped");
363         }
364     }
365     
366     return true;
367 }
368
369 RetainPtr<CFDataRef> LegacyWebArchive::rawDataRepresentation()
370 {
371     auto propertyList = createPropertyListRepresentation(*this);
372     ASSERT(propertyList);
373     if (!propertyList) {
374         LOG(Archives, "LegacyWebArchive - Failed to create property list for archive, returning no data");
375         return nullptr;
376     }
377
378     auto stream = adoptCF(CFWriteStreamCreateWithAllocatedBuffers(0, 0));
379
380     CFWriteStreamOpen(stream.get());
381     CFPropertyListWrite(propertyList.get(), stream.get(), kCFPropertyListBinaryFormat_v1_0, 0, 0);
382
383     auto plistData = adoptCF(static_cast<CFDataRef>(CFWriteStreamCopyProperty(stream.get(), kCFStreamPropertyDataWritten)));
384     ASSERT(plistData);
385
386     CFWriteStreamClose(stream.get());
387
388     if (!plistData) {
389         LOG(Archives, "LegacyWebArchive - Failed to convert property list into raw data, returning no data");
390         return nullptr;
391     }
392
393     return plistData;
394 }
395
396 #if !PLATFORM(COCOA)
397
398 ResourceResponse LegacyWebArchive::createResourceResponseFromMacArchivedData(CFDataRef responseData)
399 {
400     // FIXME: If is is possible to parse in a serialized NSURLResponse manually, without using
401     // NSKeyedUnarchiver, manipulating plists directly, then we want to do that here.
402     // Until then, this can be done on Mac only.
403     return ResourceResponse();
404 }
405
406 RetainPtr<CFDataRef> LegacyWebArchive::createPropertyListRepresentation(const ResourceResponse& response)
407 {
408     // FIXME: Write out the "new" format described in createResourceResponseFromPropertyListData once we invent it.
409     return nullptr;
410 }
411
412 #endif
413
414 RefPtr<LegacyWebArchive> LegacyWebArchive::create(Node& node, WTF::Function<bool (Frame&)>&& frameFilter)
415 {
416     Frame* frame = node.document().frame();
417     if (!frame)
418         return create();
419
420     // If the page was loaded with javascript enabled, we don't want to archive <noscript> tags
421     // In practice we don't actually know whether scripting was enabled when the page was originally loaded
422     // but we can approximate that by checking if scripting is enabled right now.
423     std::unique_ptr<Vector<QualifiedName>> tagNamesToFilter;
424     if (frame->page() && frame->page()->settings().isScriptEnabled()) {
425         tagNamesToFilter = std::make_unique<Vector<QualifiedName>>();
426         tagNamesToFilter->append(HTMLNames::noscriptTag);
427     }
428
429     Vector<Node*> nodeList;
430     String markupString = serializeFragment(node, SerializedNodes::SubtreeIncludingNode, &nodeList, ResolveURLs::No, tagNamesToFilter.get());
431     auto nodeType = node.nodeType();
432     if (nodeType != Node::DOCUMENT_NODE && nodeType != Node::DOCUMENT_TYPE_NODE)
433         markupString = documentTypeString(node.document()) + markupString;
434
435     return create(markupString, *frame, nodeList, WTFMove(frameFilter));
436 }
437
438 RefPtr<LegacyWebArchive> LegacyWebArchive::create(Frame& frame)
439 {
440     auto* documentLoader = frame.loader().documentLoader();
441     if (!documentLoader)
442         return nullptr;
443
444     auto mainResource = documentLoader->mainResource();
445     if (!mainResource)
446         return nullptr;
447
448     Vector<Ref<LegacyWebArchive>> subframeArchives;
449     for (unsigned i = 0; i < frame.tree().childCount(); ++i) {
450         if (auto childFrameArchive = create(*frame.tree().child(i)))
451             subframeArchives.append(childFrameArchive.releaseNonNull());
452     }
453
454     return create(mainResource.releaseNonNull(), documentLoader->subresources(), WTFMove(subframeArchives));
455 }
456
457 RefPtr<LegacyWebArchive> LegacyWebArchive::create(Range* range)
458 {
459     if (!range)
460         return nullptr;
461         
462     auto& document = range->startContainer().document();
463     auto* frame = document.frame();
464     if (!frame)
465         return nullptr;
466
467     // FIXME: This is always "for interchange". Is that right?
468     Vector<Node*> nodeList;
469     String markupString = documentTypeString(document) + serializePreservingVisualAppearance(*range, &nodeList, AnnotateForInterchange::Yes);
470     return create(markupString, *frame, nodeList, nullptr);
471 }
472
473 #if ENABLE(ATTACHMENT_ELEMENT)
474
475 static void addSubresourcesForAttachmentElementsIfNecessary(Frame& frame, const Vector<Node*>& nodes, Vector<Ref<ArchiveResource>>& subresources)
476 {
477     if (!RuntimeEnabledFeatures::sharedFeatures().attachmentElementEnabled())
478         return;
479
480     Vector<String> identifiers;
481     for (auto* node : nodes) {
482         if (!is<HTMLAttachmentElement>(node))
483             continue;
484
485         auto uniqueIdentifier = downcast<HTMLAttachmentElement>(*node).uniqueIdentifier();
486         if (uniqueIdentifier.isEmpty())
487             continue;
488
489         identifiers.append(WTFMove(uniqueIdentifier));
490     }
491
492     if (identifiers.isEmpty())
493         return;
494
495     auto* editorClient = frame.editor().client();
496     if (!editorClient)
497         return;
498
499     auto frameName = frame.tree().uniqueName();
500     for (auto& data : editorClient->serializedAttachmentDataForIdentifiers(WTFMove(identifiers))) {
501         auto resourceURL = HTMLAttachmentElement::archiveResourceURL(data.identifier);
502         if (auto resource = ArchiveResource::create(data.data.ptr(), WTFMove(resourceURL), data.mimeType, { }, frameName))
503             subresources.append(resource.releaseNonNull());
504     }
505 }
506
507 #endif
508
509 RefPtr<LegacyWebArchive> LegacyWebArchive::create(const String& markupString, Frame& frame, const Vector<Node*>& nodes, WTF::Function<bool (Frame&)>&& frameFilter)
510 {
511     auto& response = frame.loader().documentLoader()->response();
512     URL responseURL = response.url();
513     
514     // it's possible to have a response without a URL here
515     // <rdar://problem/5454935>
516     if (responseURL.isNull())
517         responseURL = URL({ }, emptyString());
518
519     auto mainResource = ArchiveResource::create(utf8Buffer(markupString), responseURL, response.mimeType(), "UTF-8", frame.tree().uniqueName());
520     if (!mainResource)
521         return nullptr;
522
523     Vector<Ref<LegacyWebArchive>> subframeArchives;
524     Vector<Ref<ArchiveResource>> subresources;
525     HashSet<URL> uniqueSubresources;
526
527     for (auto& nodePtr : nodes) {
528         Node& node = *nodePtr;
529         Frame* childFrame;
530         if ((is<HTMLFrameElementBase>(node) || is<HTMLObjectElement>(node))
531             && (childFrame = downcast<HTMLFrameOwnerElement>(node).contentFrame())) {
532             if (frameFilter && !frameFilter(*childFrame))
533                 continue;
534             if (auto subframeArchive = create(*childFrame->document(), WTFMove(frameFilter)))
535                 subframeArchives.append(subframeArchive.releaseNonNull());
536             else
537                 LOG_ERROR("Unabled to archive subframe %s", childFrame->tree().uniqueName().string().utf8().data());
538
539         } else {
540             ListHashSet<URL> subresourceURLs;
541             node.getSubresourceURLs(subresourceURLs);
542
543             ASSERT(frame.loader().documentLoader());
544             auto& documentLoader = *frame.loader().documentLoader();
545
546             for (auto& subresourceURL : subresourceURLs) {
547                 if (uniqueSubresources.contains(subresourceURL))
548                     continue;
549
550                 uniqueSubresources.add(subresourceURL);
551
552                 if (auto resource = documentLoader.subresource(subresourceURL)) {
553                     subresources.append(resource.releaseNonNull());
554                     continue;
555                 }
556
557                 ResourceRequest request(subresourceURL);
558                 request.setDomainForCachePartition(frame.document()->domainForCachePartition());
559
560                 if (auto* cachedResource = MemoryCache::singleton().resourceForRequest(request, frame.page()->sessionID())) {
561                     if (auto resource = ArchiveResource::create(cachedResource->resourceBuffer(), subresourceURL, cachedResource->response())) {
562                         subresources.append(resource.releaseNonNull());
563                         continue;
564                     }
565                 }
566
567                 // FIXME: should do something better than spew to console here
568                 LOG_ERROR("Failed to archive subresource for %s", subresourceURL.string().utf8().data());
569             }
570         }
571     }
572
573 #if ENABLE(ATTACHMENT_ELEMENT)
574     addSubresourcesForAttachmentElementsIfNecessary(frame, nodes, subresources);
575 #endif
576
577     // If we are archiving the entire page, add any link icons that we have data for.
578     if (!nodes.isEmpty() && nodes[0]->isDocumentNode()) {
579         auto* documentLoader = frame.loader().documentLoader();
580         ASSERT(documentLoader);
581         for (auto& icon : documentLoader->linkIcons()) {
582             if (auto resource = documentLoader->subresource(icon.url))
583                 subresources.append(resource.releaseNonNull());
584         }
585     }
586
587     return create(mainResource.releaseNonNull(), WTFMove(subresources), WTFMove(subframeArchives));
588 }
589
590 RefPtr<LegacyWebArchive> LegacyWebArchive::createFromSelection(Frame* frame)
591 {
592     if (!frame)
593         return nullptr;
594
595     auto* document = frame->document();
596     if (!document)
597         return nullptr;
598
599     StringBuilder builder;
600     builder.append(documentTypeString(*document));
601
602     Vector<Node*> nodeList;
603     auto serializeComposedTree = frame->settings().selectionAcrossShadowBoundariesEnabled() ? SerializeComposedTree::Yes : SerializeComposedTree::No;
604     builder.append(serializePreservingVisualAppearance(frame->selection().selection(), ResolveURLs::No, serializeComposedTree, &nodeList));
605
606     auto archive = create(builder.toString(), *frame, nodeList, nullptr);
607     
608     if (!document->isFrameSet())
609         return archive;
610         
611     // Wrap the frameset document in an iframe so it can be pasted into
612     // another document (which will have a body or frameset of its own). 
613     String iframeMarkup = "<iframe frameborder=\"no\" marginwidth=\"0\" marginheight=\"0\" width=\"98%%\" height=\"98%%\" src=\"" + frame->loader().documentLoader()->response().url().string() + "\"></iframe>";
614     auto iframeResource = ArchiveResource::create(utf8Buffer(iframeMarkup), WTF::blankURL(), "text/html", "UTF-8", String());
615
616     Vector<Ref<LegacyWebArchive>> subframeArchives;
617     subframeArchives.reserveInitialCapacity(1);
618     subframeArchives.uncheckedAppend(archive.releaseNonNull());
619
620     return create(iframeResource.releaseNonNull(), { }, WTFMove(subframeArchives));
621 }
622
623 }