2 * Copyright (C) 2008, 2009 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
30 #include "LegacyWebArchive.h"
35 #include "DocumentLoader.h"
37 #include "FrameLoader.h"
38 #include "FrameTree.h"
39 #include "HTMLFrameOwnerElement.h"
40 #include "HTMLNames.h"
41 #include "IconDatabase.h"
48 #include "SelectionController.h"
49 #include "SharedBuffer.h"
50 #include <wtf/ListHashSet.h>
51 #include <wtf/RetainPtr.h>
55 static const CFStringRef LegacyWebArchiveMainResourceKey = CFSTR("WebMainResource");
56 static const CFStringRef LegacyWebArchiveSubresourcesKey = CFSTR("WebSubresources");
57 static const CFStringRef LegacyWebArchiveSubframeArchivesKey = CFSTR("WebSubframeArchives");
58 static const CFStringRef LegacyWebArchiveResourceDataKey = CFSTR("WebResourceData");
59 static const CFStringRef LegacyWebArchiveResourceFrameNameKey = CFSTR("WebResourceFrameName");
60 static const CFStringRef LegacyWebArchiveResourceMIMETypeKey = CFSTR("WebResourceMIMEType");
61 static const CFStringRef LegacyWebArchiveResourceURLKey = CFSTR("WebResourceURL");
62 static const CFStringRef LegacyWebArchiveResourceTextEncodingNameKey = CFSTR("WebResourceTextEncodingName");
63 static const CFStringRef LegacyWebArchiveResourceResponseKey = CFSTR("WebResourceResponse");
64 static const CFStringRef LegacyWebArchiveResourceResponseVersionKey = CFSTR("WebResourceResponseVersion");
66 static RetainPtr<CFDictionaryRef> createPropertyListRepresentationFromResource(ArchiveResource* resource, bool mainResource)
69 // The property list representation of a null/empty WebResource has the following 3 objects stored as nil
70 RetainPtr<CFMutableDictionaryRef> propertyList(AdoptCF, CFDictionaryCreateMutable(0, 3, 0, 0));
71 CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceDataKey, 0);
72 CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceURLKey, 0);
73 CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceMIMETypeKey, 0);
78 RetainPtr<CFMutableDictionaryRef> propertyList(AdoptCF, CFDictionaryCreateMutable(0, 6, 0, &kCFTypeDictionaryValueCallBacks));
80 // Resource data can be empty, but must be represented by an empty CFDataRef
81 SharedBuffer* data = resource->data();
82 RetainPtr<CFDataRef> cfData;
84 cfData.adoptCF(data->createCFData());
86 cfData.adoptCF(CFDataCreate(0, 0, 0));
87 CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceDataKey, cfData.get());
89 // Resource URL cannot be null
90 RetainPtr<CFStringRef> cfURL(AdoptCF, resource->url().string().createCFString());
92 CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceURLKey, cfURL.get());
94 LOG(Archives, "LegacyWebArchive - NULL resource URL is invalid - returning null property list");
98 // FrameName should be left out if empty for subresources, but always included for main resources
99 const String& frameName(resource->frameName());
100 if (!frameName.isEmpty() || mainResource) {
101 RetainPtr<CFStringRef> cfFrameName(AdoptCF, frameName.createCFString());
102 CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceFrameNameKey, cfFrameName.get());
105 // Set MIMEType, TextEncodingName, and ResourceResponse only if they actually exist
106 const String& mimeType(resource->mimeType());
107 if (!mimeType.isEmpty()) {
108 RetainPtr<CFStringRef> cfMIMEType(AdoptCF, mimeType.createCFString());
109 CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceMIMETypeKey, cfMIMEType.get());
112 const String& textEncoding(resource->textEncoding());
113 if (!textEncoding.isEmpty()) {
114 RetainPtr<CFStringRef> cfTextEncoding(AdoptCF, textEncoding.createCFString());
115 CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceTextEncodingNameKey, cfTextEncoding.get());
118 // Don't include the resource response for the main resource
120 RetainPtr<CFDataRef> resourceResponseData = propertyListDataFromResourceResponse(resource->response());
121 if (resourceResponseData)
122 CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceResponseKey, resourceResponseData.get());
128 static RetainPtr<CFDictionaryRef> createPropertyListRep(Archive* archive)
130 RetainPtr<CFMutableDictionaryRef> propertyList(AdoptCF, CFDictionaryCreateMutable(0, 3, 0, &kCFTypeDictionaryValueCallBacks));
132 RetainPtr<CFDictionaryRef> mainResourceDict = createPropertyListRepresentationFromResource(archive->mainResource(), true);
133 if (!mainResourceDict)
135 CFDictionarySetValue(propertyList.get(), LegacyWebArchiveMainResourceKey, mainResourceDict.get());
137 RetainPtr<CFMutableArrayRef> subresourcesArray(AdoptCF, CFArrayCreateMutable(0, archive->subresources().size(), &kCFTypeArrayCallBacks));
138 const Vector<RefPtr<ArchiveResource> >& subresources(archive->subresources());
139 for (unsigned i = 0; i < subresources.size(); ++i) {
140 RetainPtr<CFDictionaryRef> subresource = createPropertyListRepresentationFromResource(subresources[i].get(), false);
142 CFArrayAppendValue(subresourcesArray.get(), subresource.get());
144 LOG(Archives, "LegacyWebArchive - Failed to create property list for subresource");
146 if (CFArrayGetCount(subresourcesArray.get()))
147 CFDictionarySetValue(propertyList.get(), LegacyWebArchiveSubresourcesKey, subresourcesArray.get());
149 RetainPtr<CFMutableArrayRef> subframesArray(AdoptCF, CFArrayCreateMutable(0, archive->subframeArchives().size(), &kCFTypeArrayCallBacks));
150 const Vector<RefPtr<Archive> >& subframeArchives(archive->subframeArchives());
151 for (unsigned i = 0; i < subframeArchives.size(); ++i) {
152 RetainPtr<CFDictionaryRef> subframeArchive = createPropertyListRep(subframeArchives[i].get());
154 CFArrayAppendValue(subframesArray.get(), subframeArchive.get());
156 LOG(Archives, "LegacyWebArchive - Failed to create property list for subframe archive");
158 if (CFArrayGetCount(subframesArray.get()))
159 CFDictionarySetValue(propertyList.get(), LegacyWebArchiveSubframeArchivesKey, subframesArray.get());
164 static ResourceResponse createResourceResponseFromPropertyListData(CFDataRef data, CFStringRef responseDataType)
168 return ResourceResponse();
170 // If the ResourceResponseVersion (passed in as responseDataType) exists at all, this is a "new" webarchive that we can parse well in a cross platform manner
171 // If it doesn't exist, we will assume this is an "old" Cocoa-based WebArchive, and parse the ResourceResponse as such
172 if (!responseDataType)
173 return createResourceResponseFromMacArchivedData(data);
175 // FIXME: Parse the "new" format that the above comment references here
176 return ResourceResponse();
179 static PassRefPtr<ArchiveResource> createResource(CFDictionaryRef dictionary)
185 CFDataRef resourceData = static_cast<CFDataRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveResourceDataKey));
186 if (resourceData && CFGetTypeID(resourceData) != CFDataGetTypeID()) {
187 LOG(Archives, "LegacyWebArchive - Resource data is not of type CFData, cannot create invalid resource");
191 CFStringRef frameName = static_cast<CFStringRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveResourceFrameNameKey));
192 if (frameName && CFGetTypeID(frameName) != CFStringGetTypeID()) {
193 LOG(Archives, "LegacyWebArchive - Frame name is not of type CFString, cannot create invalid resource");
197 CFStringRef mimeType = static_cast<CFStringRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveResourceMIMETypeKey));
198 if (mimeType && CFGetTypeID(mimeType) != CFStringGetTypeID()) {
199 LOG(Archives, "LegacyWebArchive - MIME type is not of type CFString, cannot create invalid resource");
203 CFStringRef url = static_cast<CFStringRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveResourceURLKey));
204 if (url && CFGetTypeID(url) != CFStringGetTypeID()) {
205 LOG(Archives, "LegacyWebArchive - URL is not of type CFString, cannot create invalid resource");
209 CFStringRef textEncoding = static_cast<CFStringRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveResourceTextEncodingNameKey));
210 if (textEncoding && CFGetTypeID(textEncoding) != CFStringGetTypeID()) {
211 LOG(Archives, "LegacyWebArchive - Text encoding is not of type CFString, cannot create invalid resource");
215 ResourceResponse response;
217 CFDataRef resourceResponseData = static_cast<CFDataRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveResourceResponseKey));
218 if (resourceResponseData) {
219 if (CFGetTypeID(resourceResponseData) != CFDataGetTypeID()) {
220 LOG(Archives, "LegacyWebArchive - Resource response data is not of type CFData, cannot create invalid resource");
224 CFStringRef resourceResponseVersion = static_cast<CFStringRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveResourceResponseVersionKey));
225 if (resourceResponseVersion && CFGetTypeID(resourceResponseVersion) != CFStringGetTypeID()) {
226 LOG(Archives, "LegacyWebArchive - Resource response version is not of type CFString, cannot create invalid resource");
230 response = createResourceResponseFromPropertyListData(resourceResponseData, resourceResponseVersion);
233 return ArchiveResource::create(SharedBuffer::create(CFDataGetBytePtr(resourceData), CFDataGetLength(resourceData)), KURL(url), mimeType, textEncoding, frameName, response);
236 PassRefPtr<LegacyWebArchive> LegacyWebArchive::create()
238 return adoptRef(new LegacyWebArchive);
241 PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(SharedBuffer* data)
243 LOG(Archives, "LegacyWebArchive - Creating from raw data");
245 RefPtr<LegacyWebArchive> archive = create();
246 if (!archive->init(data))
249 return archive.release();
252 PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(PassRefPtr<ArchiveResource> mainResource, Vector<PassRefPtr<ArchiveResource> >& subresources, Vector<PassRefPtr<LegacyWebArchive> >& subframeArchives)
254 ASSERT(mainResource);
258 RefPtr<LegacyWebArchive> archive = create();
259 archive->setMainResource(mainResource);
261 for (unsigned i = 0; i < subresources.size(); ++i)
262 archive->addSubresource(subresources[i]);
264 for (unsigned i = 0; i < subframeArchives.size(); ++i)
265 archive->addSubframeArchive(subframeArchives[i]);
267 return archive.release();
270 LegacyWebArchive::LegacyWebArchive()
274 bool LegacyWebArchive::init(SharedBuffer* data)
280 RetainPtr<CFDataRef> cfData(AdoptCF, data->createCFData());
284 CFStringRef errorString = 0;
286 RetainPtr<CFDictionaryRef> plist(AdoptCF, static_cast<CFDictionaryRef>(CFPropertyListCreateFromXMLData(0, cfData.get(), kCFPropertyListImmutable, &errorString)));
289 const char* cError = errorString ? CFStringGetCStringPtr(errorString, kCFStringEncodingUTF8) : "unknown error";
290 LOG(Archives, "LegacyWebArchive - Error parsing PropertyList from archive data - %s", cError);
293 CFRelease(errorString);
297 if (CFGetTypeID(plist.get()) != CFDictionaryGetTypeID()) {
298 LOG(Archives, "LegacyWebArchive - Archive property list is not the expected CFDictionary, aborting invalid WebArchive");
302 return extract(plist.get());
305 bool LegacyWebArchive::extract(CFDictionaryRef dictionary)
309 LOG(Archives, "LegacyWebArchive - Null root CFDictionary, aborting invalid WebArchive");
313 CFDictionaryRef mainResourceDict = static_cast<CFDictionaryRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveMainResourceKey));
314 if (!mainResourceDict) {
315 LOG(Archives, "LegacyWebArchive - No main resource in archive, aborting invalid WebArchive");
318 if (CFGetTypeID(mainResourceDict) != CFDictionaryGetTypeID()) {
319 LOG(Archives, "LegacyWebArchive - Main resource is not the expected CFDictionary, aborting invalid WebArchive");
323 setMainResource(createResource(mainResourceDict));
324 if (!mainResource()) {
325 LOG(Archives, "LegacyWebArchive - Failed to parse main resource from CFDictionary or main resource does not exist, aborting invalid WebArchive");
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");
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");
343 addSubresource(createResource(subresourceDict));
347 CFArrayRef subframeArray = static_cast<CFArrayRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveSubframeArchivesKey));
348 if (subframeArray && CFGetTypeID(subframeArray) != CFArrayGetTypeID()) {
349 LOG(Archives, "LegacyWebArchive - Subframe archives is not the expected Array, aborting invalid WebArchive");
354 CFIndex count = CFArrayGetCount(subframeArray);
355 for (CFIndex i = 0; i < count; ++i) {
356 CFDictionaryRef subframeDict = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(subframeArray, i));
357 if (CFGetTypeID(subframeDict) != CFDictionaryGetTypeID()) {
358 LOG(Archives, "LegacyWebArchive - Subframe array is not expected CFDictionary, aborting invalid WebArchive");
362 RefPtr<LegacyWebArchive> subframeArchive = create();
363 if (subframeArchive->extract(subframeDict))
364 addSubframeArchive(subframeArchive.release());
366 LOG(Archives, "LegacyWebArchive - Invalid subframe archive skipped");
373 RetainPtr<CFDataRef> LegacyWebArchive::rawDataRepresentation()
375 RetainPtr<CFDictionaryRef> propertyList = createPropertyListRep(this);
377 LOG(Archives, "LegacyWebArchive - Failed to create property list for archive, returning no data");
381 RetainPtr<CFWriteStreamRef> stream(AdoptCF, CFWriteStreamCreateWithAllocatedBuffers(0, 0));
383 CFWriteStreamOpen(stream.get());
384 CFPropertyListWriteToStream(propertyList.get(), stream.get(), kCFPropertyListBinaryFormat_v1_0, 0);
386 RetainPtr<CFDataRef> plistData(AdoptCF, static_cast<CFDataRef>(CFWriteStreamCopyProperty(stream.get(), kCFStreamPropertyDataWritten)));
388 CFWriteStreamClose(stream.get());
391 LOG(Archives, "LegacyWebArchive - Failed to convert property list into raw data, returning no data");
399 // FIXME: Is it possible to parse in a Cocoa-style resource response manually,
400 // without NSKeyed(Un)Archiver, manipulating plists directly?
401 // If so, the code that does it will go here.
402 // In the meantime, Mac will continue to NSKeyed(Un)Archive the response as it always has
403 ResourceResponse createResourceResponseFromMacArchivedData(CFDataRef responseData)
405 return ResourceResponse();
408 RetainPtr<CFDataRef> propertyListDataFromResourceResponse(const ResourceResponse& response)
410 // FIXME: Write out the "new" format described in ::createResourceResponseFromPropertyListData() up above
415 PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(Node* node)
421 Document* document = node->document();
422 Frame* frame = document ? document->frame() : 0;
426 Vector<Node*> nodeList;
427 String markupString = createMarkup(node, IncludeNode, &nodeList);
428 Node::NodeType nodeType = node->nodeType();
429 if (nodeType != Node::DOCUMENT_NODE && nodeType != Node::DOCUMENT_TYPE_NODE)
430 markupString = frame->documentTypeString() + markupString;
432 return create(markupString, frame, nodeList);
435 PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(Frame* frame)
439 DocumentLoader* documentLoader = frame->loader()->documentLoader();
444 Vector<PassRefPtr<LegacyWebArchive> > subframeArchives;
446 unsigned children = frame->tree()->childCount();
447 for (unsigned i = 0; i < children; ++i) {
448 RefPtr<LegacyWebArchive> childFrameArchive = create(frame->tree()->child(i));
449 if (childFrameArchive)
450 subframeArchives.append(childFrameArchive.release());
453 Vector<PassRefPtr<ArchiveResource> > subresources;
454 documentLoader->getSubresources(subresources);
456 return LegacyWebArchive::create(documentLoader->mainResource(), subresources, subframeArchives);
459 PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(Range* range)
464 Node* startContainer = range->startContainer();
468 Document* document = startContainer->document();
472 Frame* frame = document->frame();
476 Vector<Node*> nodeList;
478 // FIXME: This is always "for interchange". Is that right? See the previous method.
479 String markupString = frame->documentTypeString() + createMarkup(range, &nodeList, AnnotateForInterchange);
481 return create(markupString, frame, nodeList);
484 PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(const String& markupString, Frame* frame, Vector<Node*>& nodes)
488 const ResourceResponse& response = frame->loader()->documentLoader()->response();
489 KURL responseURL = response.url();
491 // it's possible to have a response without a URL here
492 // <rdar://problem/5454935>
493 if (responseURL.isNull())
494 responseURL = KURL("");
496 PassRefPtr<ArchiveResource> mainResource = ArchiveResource::create(utf8Buffer(markupString), responseURL, response.mimeType(), "UTF-8", frame->tree()->name());
498 Vector<PassRefPtr<LegacyWebArchive> > subframeArchives;
499 Vector<PassRefPtr<ArchiveResource> > subresources;
500 HashSet<KURL> uniqueSubresources;
502 Vector<Node*>::iterator it = nodes.begin();
503 Vector<Node*>::iterator end = nodes.end();
505 for (; it != end; ++it) {
507 if (((*it)->hasTagName(HTMLNames::frameTag) || (*it)->hasTagName(HTMLNames::iframeTag) || (*it)->hasTagName(HTMLNames::objectTag)) &&
508 (childFrame = static_cast<HTMLFrameOwnerElement*>(*it)->contentFrame())) {
509 RefPtr<LegacyWebArchive> subframeArchive;
510 subframeArchive = LegacyWebArchive::create(childFrame->document());
513 subframeArchives.append(subframeArchive);
515 LOG_ERROR("Unabled to archive subframe %s", childFrame->tree()->name().string().utf8().data());
517 ListHashSet<KURL> subresourceURLs;
518 (*it)->getSubresourceURLs(subresourceURLs);
520 DocumentLoader* documentLoader = frame->loader()->documentLoader();
521 ListHashSet<KURL>::iterator iterEnd = subresourceURLs.end();
522 for (ListHashSet<KURL>::iterator iter = subresourceURLs.begin(); iter != iterEnd; ++iter) {
523 const KURL& subresourceURL = *iter;
524 if (uniqueSubresources.contains(subresourceURL))
527 uniqueSubresources.add(subresourceURL);
529 RefPtr<ArchiveResource> resource = documentLoader->subresource(subresourceURL);
531 subresources.append(resource.release());
535 CachedResource *cachedResource = cache()->resourceForURL(subresourceURL);
536 if (cachedResource) {
537 resource = ArchiveResource::create(cachedResource->data(), subresourceURL, cachedResource->response());
539 subresources.append(resource.release());
544 // FIXME: should do something better than spew to console here
545 LOG_ERROR("Failed to archive subresource for %s", subresourceURL.string().utf8().data());
550 // Add favicon if one exists for this page
551 if (iconDatabase() && iconDatabase()->isEnabled()) {
552 const String& iconURL = iconDatabase()->iconURLForPageURL(responseURL);
553 if (!iconURL.isEmpty() && iconDatabase()->iconDataKnownForIconURL(iconURL)) {
554 if (Image* iconImage = iconDatabase()->iconForPageURL(responseURL, IntSize(16, 16))) {
555 if (RefPtr<ArchiveResource> resource = ArchiveResource::create(iconImage->data(), KURL(iconURL), "image/x-icon", "", ""))
556 subresources.append(resource.release());
561 return create(mainResource, subresources, subframeArchives);
564 PassRefPtr<LegacyWebArchive> LegacyWebArchive::createFromSelection(Frame* frame)
569 RefPtr<Range> selectionRange = frame->selection()->toNormalizedRange();
570 Vector<Node*> nodeList;
571 String markupString = frame->documentTypeString() + createMarkup(selectionRange.get(), &nodeList, AnnotateForInterchange);
573 RefPtr<LegacyWebArchive> archive = create(markupString, frame, nodeList);
575 if (!frame->document() || !frame->document()->isFrameSet())
576 return archive.release();
578 // Wrap the frameset document in an iframe so it can be pasted into
579 // another document (which will have a body or frameset of its own).
580 String iframeMarkup = String::format("<iframe frameborder=\"no\" marginwidth=\"0\" marginheight=\"0\" width=\"98%%\" height=\"98%%\" src=\"%s\"></iframe>",
581 frame->loader()->documentLoader()->response().url().string().utf8().data());
582 RefPtr<ArchiveResource> iframeResource = ArchiveResource::create(utf8Buffer(iframeMarkup), blankURL(), "text/html", "UTF-8", String());
584 Vector<PassRefPtr<ArchiveResource> > subresources;
586 Vector<PassRefPtr<LegacyWebArchive> > subframeArchives;
587 subframeArchives.append(archive);
589 archive = LegacyWebArchive::create(iframeResource.release(), subresources, subframeArchives);
591 return archive.release();