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