15c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)/* 25c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * Copyright (C) 2011 Google Inc. All rights reserved. 35c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * 45c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * Redistribution and use in source and binary forms, with or without 55c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * modification, are permitted provided that the following conditions are 65c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * met: 75c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * 85c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * * Redistributions of source code must retain the above copyright 95c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * notice, this list of conditions and the following disclaimer. 105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * * Redistributions in binary form must reproduce the above 115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * copyright notice, this list of conditions and the following disclaimer 125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * in the documentation and/or other materials provided with the 135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * distribution. 145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * * Neither the name of Google Inc. nor the names of its 155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * contributors may be used to endorse or promote products derived from 165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * this software without specific prior written permission. 175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * 185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) */ 305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "config.h" 3251b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)#include "platform/mhtml/MHTMLParser.h" 335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 3451b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)#include "platform/MIMETypeRegistry.h" 35197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch#include "platform/mhtml/ArchiveResource.h" 3651b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)#include "platform/mhtml/MHTMLArchive.h" 371e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles)#include "platform/network/ParsedContentType.h" 381e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles)#include "platform/text/QuotedPrintable.h" 391e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles)#include "wtf/HashMap.h" 401e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles)#include "wtf/RefCounted.h" 41591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch#include "wtf/text/Base64.h" 421e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles)#include "wtf/text/StringBuilder.h" 431e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles)#include "wtf/text/StringConcatenate.h" 441e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles)#include "wtf/text/StringHash.h" 451e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles)#include "wtf/text/WTFString.h" 465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 47c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)namespace blink { 485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 491e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles)// This class is a limited MIME parser used to parse the MIME headers of MHTML files. 50197021e6b966cfb06891637935ef33fff06433d1Ben Murdochclass MIMEHeader : public RefCountedWillBeGarbageCollectedFinalized<MIMEHeader> { 511e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles)public: 52197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch static PassRefPtrWillBeRawPtr<MIMEHeader> create() 53197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch { 54197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch return adoptRefWillBeNoop(new MIMEHeader()); 55197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch } 56197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch 571e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) enum Encoding { 581e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) QuotedPrintable, 591e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) Base64, 601e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) EightBit, 611e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) SevenBit, 621e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) Binary, 631e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) Unknown 641e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) }; 651e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) 66197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch static PassRefPtrWillBeRawPtr<MIMEHeader> parseHeader(SharedBufferChunkReader* crLFLineReader); 671e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) 681e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) bool isMultipart() const { return m_contentType.startsWith("multipart/"); } 691e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) 701e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) String contentType() const { return m_contentType; } 711e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) String charset() const { return m_charset; } 721e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) Encoding contentTransferEncoding() const { return m_contentTransferEncoding; } 731e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) String contentLocation() const { return m_contentLocation; } 741e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) 751e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) // Multi-part type and boundaries are only valid for multipart MIME headers. 761e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) String multiPartType() const { return m_multipartType; } 771e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) String endOfPartBoundary() const { return m_endOfPartBoundary; } 781e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) String endOfDocumentBoundary() const { return m_endOfDocumentBoundary; } 791e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) 80197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch void trace(Visitor*) { } 81197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch 821e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles)private: 831e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) MIMEHeader(); 841e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) 851e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) static Encoding parseContentTransferEncoding(const String&); 861e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) 871e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) String m_contentType; 881e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) String m_charset; 891e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) Encoding m_contentTransferEncoding; 901e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) String m_contentLocation; 911e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) String m_multipartType; 921e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) String m_endOfPartBoundary; 931e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) String m_endOfDocumentBoundary; 941e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles)}; 951e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) 961e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles)typedef HashMap<String, String> KeyValueMap; 971e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) 98c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)static KeyValueMap retrieveKeyValuePairs(blink::SharedBufferChunkReader* buffer) 991e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles){ 1001e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) KeyValueMap keyValuePairs; 1011e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) String line; 1021e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) String key; 1031e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) StringBuilder value; 1041e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) while (!(line = buffer->nextChunkAsUTF8StringWithLatin1Fallback()).isNull()) { 1051e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) if (line.isEmpty()) 1061e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) break; // Empty line means end of key/value section. 1071e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) if (line[0] == '\t') { 1081e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) ASSERT(!key.isEmpty()); 1091e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) value.append(line.substring(1)); 1101e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) continue; 1111e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) } 1121e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) // New key/value, store the previous one if any. 1131e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) if (!key.isEmpty()) { 1141e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) if (keyValuePairs.find(key) != keyValuePairs.end()) 115a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles) WTF_LOG_ERROR("Key duplicate found in MIME header. Key is '%s', previous value replaced.", key.ascii().data()); 1161e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) keyValuePairs.add(key, value.toString().stripWhiteSpace()); 1171e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) key = String(); 1181e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) value.clear(); 1191e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) } 1201e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) size_t semiColonIndex = line.find(':'); 1211e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) if (semiColonIndex == kNotFound) { 1221e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) // This is not a key value pair, ignore. 1231e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) continue; 1241e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) } 1251e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) key = line.substring(0, semiColonIndex).lower().stripWhiteSpace(); 1261e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) value.append(line.substring(semiColonIndex + 1)); 1271e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) } 1281e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) // Store the last property if there is one. 1291e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) if (!key.isEmpty()) 1301e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) keyValuePairs.set(key, value.toString().stripWhiteSpace()); 1311e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) return keyValuePairs; 1321e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles)} 1331e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) 134197021e6b966cfb06891637935ef33fff06433d1Ben MurdochPassRefPtrWillBeRawPtr<MIMEHeader> MIMEHeader::parseHeader(SharedBufferChunkReader* buffer) 1351e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles){ 136197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch RefPtrWillBeRawPtr<MIMEHeader> mimeHeader = MIMEHeader::create(); 1371e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) KeyValueMap keyValuePairs = retrieveKeyValuePairs(buffer); 1381e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) KeyValueMap::iterator mimeParametersIterator = keyValuePairs.find("content-type"); 1391e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) if (mimeParametersIterator != keyValuePairs.end()) { 1401e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) ParsedContentType parsedContentType(mimeParametersIterator->value); 1411e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) mimeHeader->m_contentType = parsedContentType.mimeType(); 1421e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) if (!mimeHeader->isMultipart()) { 1431e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) mimeHeader->m_charset = parsedContentType.charset().stripWhiteSpace(); 1441e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) } else { 1451e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) mimeHeader->m_multipartType = parsedContentType.parameterValueForName("type"); 1461e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) mimeHeader->m_endOfPartBoundary = parsedContentType.parameterValueForName("boundary"); 1471e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) if (mimeHeader->m_endOfPartBoundary.isNull()) { 148a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles) WTF_LOG_ERROR("No boundary found in multipart MIME header."); 149d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) return nullptr; 1501e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) } 1511e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) mimeHeader->m_endOfPartBoundary.insert("--", 0); 1521e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) mimeHeader->m_endOfDocumentBoundary = mimeHeader->m_endOfPartBoundary; 1531e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) mimeHeader->m_endOfDocumentBoundary.append("--"); 1541e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) } 1551e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) } 1561e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) 1571e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) mimeParametersIterator = keyValuePairs.find("content-transfer-encoding"); 1581e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) if (mimeParametersIterator != keyValuePairs.end()) 1591e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) mimeHeader->m_contentTransferEncoding = parseContentTransferEncoding(mimeParametersIterator->value); 1601e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) 1611e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) mimeParametersIterator = keyValuePairs.find("content-location"); 1621e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) if (mimeParametersIterator != keyValuePairs.end()) 1631e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) mimeHeader->m_contentLocation = mimeParametersIterator->value; 1641e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) 1651e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) return mimeHeader.release(); 1661e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles)} 1671e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) 1681e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles)MIMEHeader::Encoding MIMEHeader::parseContentTransferEncoding(const String& text) 1691e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles){ 1701e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) String encoding = text.stripWhiteSpace().lower(); 1711e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) if (encoding == "base64") 1721e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) return Base64; 1731e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) if (encoding == "quoted-printable") 1741e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) return QuotedPrintable; 1751e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) if (encoding == "8bit") 1761e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) return EightBit; 1771e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) if (encoding == "7bit") 1781e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) return SevenBit; 1791e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) if (encoding == "binary") 1801e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) return Binary; 181a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles) WTF_LOG_ERROR("Unknown encoding '%s' found in MIME header.", text.ascii().data()); 1821e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) return Unknown; 1831e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles)} 1841e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) 1851e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles)MIMEHeader::MIMEHeader() 1861e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) : m_contentTransferEncoding(Unknown) 1871e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles){ 1881e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles)} 1891e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles) 1905c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)static bool skipLinesUntilBoundaryFound(SharedBufferChunkReader& lineReader, const String& boundary) 1915c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 1925c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) String line; 1935c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) while (!(line = lineReader.nextChunkAsUTF8StringWithLatin1Fallback()).isNull()) { 1945c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (line == boundary) 1955c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return true; 1965c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 1975c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return false; 1985c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 1995c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 2005c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)MHTMLParser::MHTMLParser(SharedBuffer* data) 2015c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) : m_lineReader(data, "\r\n") 2025c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 2035c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 2045c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 205197021e6b966cfb06891637935ef33fff06433d1Ben MurdochPassRefPtrWillBeRawPtr<MHTMLArchive> MHTMLParser::parseArchive() 2065c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 207197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch RefPtrWillBeRawPtr<MIMEHeader> header = MIMEHeader::parseHeader(&m_lineReader); 2085c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return parseArchiveWithHeader(header.get()); 2095c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 2105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 211197021e6b966cfb06891637935ef33fff06433d1Ben MurdochPassRefPtrWillBeRawPtr<MHTMLArchive> MHTMLParser::parseArchiveWithHeader(MIMEHeader* header) 2125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 2135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (!header) { 214a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles) WTF_LOG_ERROR("Failed to parse MHTML part: no header."); 215d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) return nullptr; 2165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 2175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 218197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch RefPtrWillBeRawPtr<MHTMLArchive> archive = MHTMLArchive::create(); 2195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (!header->isMultipart()) { 2205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // With IE a page with no resource is not multi-part. 2215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) bool endOfArchiveReached = false; 222197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch RefPtrWillBeRawPtr<ArchiveResource> resource = parseNextPart(*header, String(), String(), endOfArchiveReached); 2235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (!resource) 224d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) return nullptr; 2255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) archive->setMainResource(resource); 2265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return archive; 2275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 2285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 2295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // Skip the message content (it's a generic browser specific message). 2305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) skipLinesUntilBoundaryFound(m_lineReader, header->endOfPartBoundary()); 2315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 2325c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) bool endOfArchive = false; 2335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) while (!endOfArchive) { 234197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch RefPtrWillBeRawPtr<MIMEHeader> resourceHeader = MIMEHeader::parseHeader(&m_lineReader); 2355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (!resourceHeader) { 236a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles) WTF_LOG_ERROR("Failed to parse MHTML, invalid MIME header."); 237d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) return nullptr; 2385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 2395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (resourceHeader->contentType() == "multipart/alternative") { 2405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // Ignore IE nesting which makes little sense (IE seems to nest only some of the frames). 241197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch RefPtrWillBeRawPtr<MHTMLArchive> subframeArchive = parseArchiveWithHeader(resourceHeader.get()); 2425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (!subframeArchive) { 243a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles) WTF_LOG_ERROR("Failed to parse MHTML subframe."); 244d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) return nullptr; 2455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 2465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) bool endOfPartReached = skipLinesUntilBoundaryFound(m_lineReader, header->endOfPartBoundary()); 2475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) ASSERT_UNUSED(endOfPartReached, endOfPartReached); 2485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // The top-frame is the first frame found, regardless of the nesting level. 2495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (subframeArchive->mainResource()) 2505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) addResourceToArchive(subframeArchive->mainResource(), archive.get()); 2515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) archive->addSubframeArchive(subframeArchive); 2525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) continue; 2535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 2545c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 255197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch RefPtrWillBeRawPtr<ArchiveResource> resource = parseNextPart(*resourceHeader, header->endOfPartBoundary(), header->endOfDocumentBoundary(), endOfArchive); 2565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (!resource) { 257a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles) WTF_LOG_ERROR("Failed to parse MHTML part."); 258d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) return nullptr; 2595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 2605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) addResourceToArchive(resource.get(), archive.get()); 2615c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 2625c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 2635c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return archive.release(); 2645c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 2655c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 2665c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)void MHTMLParser::addResourceToArchive(ArchiveResource* resource, MHTMLArchive* archive) 2675c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 268a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles) const AtomicString& mimeType = resource->mimeType(); 2695c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (!MIMETypeRegistry::isSupportedNonImageMIMEType(mimeType) || MIMETypeRegistry::isSupportedJavaScriptMIMEType(mimeType) || mimeType == "text/css") { 2705c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) m_resources.append(resource); 2715c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return; 2725c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 2735c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 2745c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // The first document suitable resource is the main frame. 2755c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (!archive->mainResource()) { 2765c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) archive->setMainResource(resource); 2775c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) m_frames.append(archive); 2785c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return; 2795c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 2805c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 281197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch RefPtrWillBeRawPtr<MHTMLArchive> subframe = MHTMLArchive::create(); 2825c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) subframe->setMainResource(resource); 2835c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) m_frames.append(subframe); 2845c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 2855c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 286197021e6b966cfb06891637935ef33fff06433d1Ben MurdochPassRefPtrWillBeRawPtr<ArchiveResource> MHTMLParser::parseNextPart(const MIMEHeader& mimeHeader, const String& endOfPartBoundary, const String& endOfDocumentBoundary, bool& endOfArchiveReached) 2875c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 2885c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) ASSERT(endOfPartBoundary.isEmpty() == endOfDocumentBoundary.isEmpty()); 2895c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 29006f816c7c76bc45a15e452ade8a34e8af077693eTorne (Richard Coles) // If no content transfer encoding is specified, default to binary encoding. 29106f816c7c76bc45a15e452ade8a34e8af077693eTorne (Richard Coles) MIMEHeader::Encoding contentTransferEncoding = mimeHeader.contentTransferEncoding(); 29206f816c7c76bc45a15e452ade8a34e8af077693eTorne (Richard Coles) if (contentTransferEncoding == MIMEHeader::Unknown) 29306f816c7c76bc45a15e452ade8a34e8af077693eTorne (Richard Coles) contentTransferEncoding = MIMEHeader::Binary; 29406f816c7c76bc45a15e452ade8a34e8af077693eTorne (Richard Coles) 2955c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) RefPtr<SharedBuffer> content = SharedBuffer::create(); 2965c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) const bool checkBoundary = !endOfPartBoundary.isEmpty(); 2975c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) bool endOfPartReached = false; 29806f816c7c76bc45a15e452ade8a34e8af077693eTorne (Richard Coles) if (contentTransferEncoding == MIMEHeader::Binary) { 2995c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (!checkBoundary) { 300a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles) WTF_LOG_ERROR("Binary contents requires end of part"); 301d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) return nullptr; 3025c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 3035c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) m_lineReader.setSeparator(endOfPartBoundary.utf8().data()); 3045c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) Vector<char> part; 3055c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (!m_lineReader.nextChunk(part)) { 306a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles) WTF_LOG_ERROR("Binary contents requires end of part"); 307d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) return nullptr; 30851b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles) } 30951b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles) content->append(part); 31051b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles) m_lineReader.setSeparator("\r\n"); 31151b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles) Vector<char> nextChars; 31251b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles) if (m_lineReader.peek(nextChars, 2) != 2) { 313a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles) WTF_LOG_ERROR("Invalid seperator."); 314d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) return nullptr; 31551b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles) } 31651b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles) endOfPartReached = true; 31751b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles) ASSERT(nextChars.size() == 2); 31851b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles) endOfArchiveReached = (nextChars[0] == '-' && nextChars[1] == '-'); 31951b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles) if (!endOfArchiveReached) { 32051b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles) String line = m_lineReader.nextChunkAsUTF8StringWithLatin1Fallback(); 32151b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles) if (!line.isEmpty()) { 322a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles) WTF_LOG_ERROR("No CRLF at end of binary section."); 323d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) return nullptr; 32451b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles) } 32551b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles) } 3265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } else { 3275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) String line; 3285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) while (!(line = m_lineReader.nextChunkAsUTF8StringWithLatin1Fallback()).isNull()) { 3295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) endOfArchiveReached = (line == endOfDocumentBoundary); 3305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (checkBoundary && (line == endOfPartBoundary || endOfArchiveReached)) { 3315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) endOfPartReached = true; 3325c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) break; 3335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 3345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // Note that we use line.utf8() and not line.ascii() as ascii turns special characters (such as tab, line-feed...) into '?'. 3355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) content->append(line.utf8().data(), line.length()); 33606f816c7c76bc45a15e452ade8a34e8af077693eTorne (Richard Coles) if (contentTransferEncoding == MIMEHeader::QuotedPrintable) { 3375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // The line reader removes the \r\n, but we need them for the content in this case as the QuotedPrintable decoder expects CR-LF terminated lines. 3385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) content->append("\r\n", 2); 3395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 3405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 3415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 3425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (!endOfPartReached && checkBoundary) { 343a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles) WTF_LOG_ERROR("No bounday found for MHTML part."); 344d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) return nullptr; 3455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 3465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 3475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) Vector<char> data; 34806f816c7c76bc45a15e452ade8a34e8af077693eTorne (Richard Coles) switch (contentTransferEncoding) { 3495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) case MIMEHeader::Base64: 3505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (!base64Decode(content->data(), content->size(), data)) { 351a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles) WTF_LOG_ERROR("Invalid base64 content for MHTML part."); 352d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) return nullptr; 3535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 3545c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) break; 3555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) case MIMEHeader::QuotedPrintable: 3565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) quotedPrintableDecode(content->data(), content->size(), data); 3575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) break; 358c0e19a689c8ac22cdc96b291a8d33a5d3b0b34a4Torne (Richard Coles) case MIMEHeader::EightBit: 3595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) case MIMEHeader::SevenBit: 3605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) case MIMEHeader::Binary: 3615c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) data.append(content->data(), content->size()); 3625c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) break; 3635c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) default: 364a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles) WTF_LOG_ERROR("Invalid encoding for MHTML part."); 365d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles) return nullptr; 3665c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 3675c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) RefPtr<SharedBuffer> contentBuffer = SharedBuffer::adoptVector(data); 3685c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // FIXME: the URL in the MIME header could be relative, we should resolve it if it is. 3695c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // The specs mentions 5 ways to resolve a URL: http://tools.ietf.org/html/rfc2557#section-5 3705c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // IE and Firefox (UNMht) seem to generate only absolute URLs. 3715c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) KURL location = KURL(KURL(), mimeHeader.contentLocation()); 372a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles) return ArchiveResource::create(contentBuffer, location, AtomicString(mimeHeader.contentType()), AtomicString(mimeHeader.charset()), String()); 3735c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 3745c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 3755c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)size_t MHTMLParser::frameCount() const 3765c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 3775c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return m_frames.size(); 3785c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 3795c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 3805c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)MHTMLArchive* MHTMLParser::frameAt(size_t index) const 3815c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 3825c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return m_frames[index].get(); 3835c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 3845c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 3855c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)size_t MHTMLParser::subResourceCount() const 3865c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 3875c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return m_resources.size(); 3885c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 3895c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 3905c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)ArchiveResource* MHTMLParser::subResourceAt(size_t index) const 3915c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 3925c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return m_resources[index].get(); 3935c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 3945c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 3955c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 396