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