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