Home | History | Annotate | Download | only in android
      1 /*
      2  * Copyright 2010, The Android Open Source Project
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  *  * Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  *  * Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
     14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #define LOG_TAG "webarchive"
     27 
     28 #include "config.h"
     29 #include "WebArchiveAndroid.h"
     30 
     31 #if ENABLE(WEB_ARCHIVE)
     32 
     33 #include "Base64.h"
     34 #include <libxml/encoding.h>
     35 #include <libxml/parser.h>
     36 #include <libxml/tree.h>
     37 #include <libxml/xmlstring.h>
     38 #include <libxml/xmlwriter.h>
     39 #include <wtf/text/CString.h>
     40 
     41 namespace WebCore {
     42 
     43 static const xmlChar* const archiveTag = BAD_CAST "Archive";
     44 static const xmlChar* const archiveResourceTag = BAD_CAST "ArchiveResource";
     45 static const xmlChar* const mainResourceTag = BAD_CAST "mainResource";
     46 static const xmlChar* const subresourcesTag = BAD_CAST "subresources";
     47 static const xmlChar* const subframesTag = BAD_CAST "subframes";
     48 static const xmlChar* const urlFieldTag = BAD_CAST "url";
     49 static const xmlChar* const mimeFieldTag = BAD_CAST "mimeType";
     50 static const xmlChar* const encodingFieldTag = BAD_CAST "textEncoding";
     51 static const xmlChar* const frameFieldTag = BAD_CAST "frameName";
     52 static const xmlChar* const dataFieldTag = BAD_CAST "data";
     53 
     54 PassRefPtr<WebArchiveAndroid> WebArchiveAndroid::create(PassRefPtr<ArchiveResource> mainResource,
     55         Vector<PassRefPtr<ArchiveResource> >& subresources,
     56         Vector<PassRefPtr<Archive> >& subframeArchives)
     57 {
     58     if (mainResource)
     59         return adoptRef(new WebArchiveAndroid(mainResource, subresources, subframeArchives));
     60     return 0;
     61 }
     62 
     63 PassRefPtr<WebArchiveAndroid> WebArchiveAndroid::create(Frame* frame)
     64 {
     65     PassRefPtr<ArchiveResource> mainResource = frame->loader()->documentLoader()->mainResource();
     66     Vector<PassRefPtr<ArchiveResource> > subresources;
     67     Vector<PassRefPtr<Archive> > subframes;
     68     int children = frame->tree()->childCount();
     69 
     70     frame->loader()->documentLoader()->getSubresources(subresources);
     71 
     72     for (int child = 0; child < children; child++)
     73         subframes.append(create(frame->tree()->child(child)));
     74 
     75     return create(mainResource, subresources, subframes);
     76 }
     77 
     78 WebArchiveAndroid::WebArchiveAndroid(PassRefPtr<ArchiveResource> mainResource,
     79         Vector<PassRefPtr<ArchiveResource> >& subresources,
     80         Vector<PassRefPtr<Archive> >& subframeArchives)
     81 {
     82     setMainResource(mainResource);
     83 
     84     for (Vector<PassRefPtr<ArchiveResource> >::iterator subresourcesIterator = subresources.begin();
     85          subresourcesIterator != subresources.end();
     86          subresourcesIterator++) {
     87         addSubresource(*subresourcesIterator);
     88     }
     89 
     90     for (Vector<PassRefPtr<Archive> >::iterator subframesIterator = subframeArchives.begin();
     91          subframesIterator != subframeArchives.end();
     92          subframesIterator++) {
     93         addSubframeArchive(*subframesIterator);
     94     }
     95 }
     96 
     97 static bool loadArchiveResourceField(xmlNodePtr resourceNode, const xmlChar* fieldName, Vector<char>* outputData)
     98 {
     99     if (!outputData)
    100         return false;
    101 
    102     outputData->clear();
    103 
    104     const char* base64Data = 0;
    105 
    106     for (xmlNodePtr fieldNode = resourceNode->xmlChildrenNode;
    107          fieldNode;
    108          fieldNode = fieldNode->next) {
    109         if (xmlStrEqual(fieldNode->name, fieldName)) {
    110             base64Data = (const char*)xmlNodeGetContent(fieldNode->xmlChildrenNode);
    111             if (!base64Data) {
    112                 /* Empty fields seem to break if they aren't null terminated. */
    113                 outputData->append('\0');
    114                 return true;
    115             }
    116             break;
    117         }
    118     }
    119     if (!base64Data) {
    120         LOGD("loadArchiveResourceField: Failed to load field.");
    121         return false;
    122     }
    123 
    124     const int base64Size = xmlStrlen(BAD_CAST base64Data);
    125 
    126     const int result = base64Decode(base64Data, base64Size, *outputData);
    127     if (!result) {
    128         LOGD("loadArchiveResourceField: Failed to decode field.");
    129         return false;
    130     }
    131 
    132     return true;
    133 }
    134 
    135 static PassRefPtr<SharedBuffer> loadArchiveResourceFieldBuffer(xmlNodePtr resourceNode, const xmlChar* fieldName)
    136 {
    137     Vector<char> fieldData;
    138 
    139     if (loadArchiveResourceField(resourceNode, fieldName, &fieldData))
    140         return SharedBuffer::create(fieldData.data(), fieldData.size());
    141 
    142     return 0;
    143 }
    144 
    145 static String loadArchiveResourceFieldString(xmlNodePtr resourceNode, const xmlChar* fieldName)
    146 {
    147     Vector<char> fieldData;
    148 
    149     if (loadArchiveResourceField(resourceNode, fieldName, &fieldData))
    150         return String::fromUTF8(fieldData.data(), fieldData.size());
    151 
    152     return String();
    153 }
    154 
    155 static KURL loadArchiveResourceFieldURL(xmlNodePtr resourceNode, const xmlChar* fieldName)
    156 {
    157     Vector<char> fieldData;
    158 
    159     if (loadArchiveResourceField(resourceNode, fieldName, &fieldData))
    160         return KURL(ParsedURLString, String::fromUTF8(fieldData.data(), fieldData.size()));
    161 
    162     return KURL();
    163 }
    164 
    165 static PassRefPtr<ArchiveResource> loadArchiveResource(xmlNodePtr resourceNode)
    166 {
    167     if (!xmlStrEqual(resourceNode->name, archiveResourceTag)) {
    168         LOGD("loadArchiveResource: Malformed resource.");
    169         return 0;
    170     }
    171 
    172     KURL url = loadArchiveResourceFieldURL(resourceNode, urlFieldTag);
    173     if (url.isNull()) {
    174         LOGD("loadArchiveResource: Failed to load resource.");
    175         return 0;
    176     }
    177 
    178     String mimeType = loadArchiveResourceFieldString(resourceNode, mimeFieldTag);
    179     if (mimeType.isNull()) {
    180         LOGD("loadArchiveResource: Failed to load resource.");
    181         return 0;
    182     }
    183 
    184     String textEncoding = loadArchiveResourceFieldString(resourceNode, encodingFieldTag);
    185     if (textEncoding.isNull()) {
    186         LOGD("loadArchiveResource: Failed to load resource.");
    187         return 0;
    188     }
    189 
    190     String frameName = loadArchiveResourceFieldString(resourceNode, frameFieldTag);
    191     if (frameName.isNull()) {
    192         LOGD("loadArchiveResource: Failed to load resource.");
    193         return 0;
    194     }
    195 
    196     PassRefPtr<SharedBuffer> data = loadArchiveResourceFieldBuffer(resourceNode, dataFieldTag);
    197     if (!data) {
    198         LOGD("loadArchiveResource: Failed to load resource.");
    199         return 0;
    200     }
    201 
    202     return ArchiveResource::create(data, url, mimeType, textEncoding, frameName);
    203 }
    204 
    205 static PassRefPtr<WebArchiveAndroid> loadArchive(xmlNodePtr archiveNode)
    206 {
    207     xmlNodePtr resourceNode = 0;
    208 
    209     PassRefPtr<ArchiveResource> mainResource;
    210     Vector<PassRefPtr<ArchiveResource> > subresources;
    211     Vector<PassRefPtr<Archive> > subframes;
    212 
    213     if (!xmlStrEqual(archiveNode->name, archiveTag)) {
    214         LOGD("loadArchive: Malformed archive.");
    215         return 0;
    216     }
    217 
    218     for (resourceNode = archiveNode->xmlChildrenNode;
    219          resourceNode;
    220          resourceNode = resourceNode->next) {
    221         if (xmlStrEqual(resourceNode->name, mainResourceTag)) {
    222             resourceNode = resourceNode->xmlChildrenNode;
    223             if (!resourceNode)
    224                 break;
    225             mainResource = loadArchiveResource(resourceNode);
    226             break;
    227         }
    228     }
    229     if (!mainResource) {
    230         LOGD("loadArchive: Failed to load main resource.");
    231         return 0;
    232     }
    233 
    234     for (resourceNode = archiveNode->xmlChildrenNode;
    235          resourceNode;
    236          resourceNode = resourceNode->next) {
    237         if (xmlStrEqual(resourceNode->name, subresourcesTag)) {
    238             for (resourceNode = resourceNode->xmlChildrenNode;
    239                  resourceNode;
    240                  resourceNode = resourceNode->next) {
    241                 PassRefPtr<ArchiveResource> subresource = loadArchiveResource(resourceNode);
    242                 if (!subresource) {
    243                     LOGD("loadArchive: Failed to load subresource.");
    244                     break;
    245                 }
    246                 subresources.append(subresource);
    247             }
    248             break;
    249         }
    250     }
    251 
    252     for (resourceNode = archiveNode->xmlChildrenNode;
    253          resourceNode;
    254          resourceNode = resourceNode->next) {
    255         if (xmlStrEqual(resourceNode->name, subframesTag)) {
    256             for (resourceNode = resourceNode->xmlChildrenNode;
    257                  resourceNode;
    258                  resourceNode = resourceNode->next) {
    259                 PassRefPtr<WebArchiveAndroid> subframe = loadArchive(resourceNode);
    260                 if (!subframe) {
    261                     LOGD("loadArchive: Failed to load subframe.");
    262                     break;
    263                 }
    264                 subframes.append(subframe);
    265             }
    266             break;
    267         }
    268     }
    269 
    270     return WebArchiveAndroid::create(mainResource, subresources, subframes);
    271 }
    272 
    273 static PassRefPtr<WebArchiveAndroid> createArchiveForError()
    274 {
    275     /* When an archive cannot be loaded, we return an empty archive instead. */
    276     PassRefPtr<ArchiveResource> mainResource = ArchiveResource::create(
    277         SharedBuffer::create(), KURL(ParsedURLString, String::fromUTF8("file:///dummy")),
    278         String::fromUTF8("text/plain"), String(""), String(""));
    279     Vector<PassRefPtr<ArchiveResource> > subresources;
    280     Vector<PassRefPtr<Archive> > subframes;
    281 
    282     return WebArchiveAndroid::create(mainResource, subresources, subframes);
    283 }
    284 
    285 PassRefPtr<WebArchiveAndroid> WebArchiveAndroid::create(SharedBuffer* buffer)
    286 {
    287     const char* const noBaseUrl = "";
    288     const char* const defaultEncoding = 0;
    289     const int noParserOptions = 0;
    290 
    291     xmlDocPtr doc = xmlReadMemory(buffer->data(), buffer->size(), noBaseUrl, defaultEncoding, noParserOptions);
    292     if (!doc) {
    293         LOGD("create: Failed to parse document.");
    294         return createArchiveForError();
    295     }
    296 
    297     xmlNodePtr root = xmlDocGetRootElement(doc);
    298     if (!root) {
    299         LOGD("create: Empty document.");
    300         xmlFreeDoc(doc);
    301         return createArchiveForError();
    302     }
    303 
    304     RefPtr<WebArchiveAndroid> archive = loadArchive(root);
    305     if (!archive) {
    306         LOGD("create: Failed to load archive.");
    307         xmlFreeDoc(doc);
    308         return createArchiveForError();
    309     }
    310 
    311     xmlFreeDoc(doc);
    312     return archive.release();
    313 }
    314 
    315 static bool saveArchiveResourceField(xmlTextWriterPtr writer, const xmlChar* tag, const char* data, int size)
    316 {
    317     int result = xmlTextWriterStartElement(writer, tag);
    318     if (result < 0) {
    319         LOGD("saveArchiveResourceField: Failed to start element.");
    320         return false;
    321     }
    322 
    323     if (size > 0) {
    324         Vector<char> base64Data;
    325         base64Encode(data, size, base64Data, false);
    326         if (base64Data.isEmpty()) {
    327             LOGD("saveArchiveResourceField: Failed to base64 encode data.");
    328             return false;
    329         }
    330 
    331         result = xmlTextWriterWriteRawLen(writer, BAD_CAST base64Data.data(), base64Data.size());
    332         if (result < 0) {
    333             LOGD("saveArchiveResourceField: Failed to write data.");
    334             return false;
    335         }
    336     }
    337 
    338     result = xmlTextWriterEndElement(writer);
    339     if (result < 0) {
    340         LOGD("saveArchiveResourceField: Failed to end element.");
    341         return false;
    342     }
    343 
    344     return true;
    345 }
    346 
    347 static bool saveArchiveResourceField(xmlTextWriterPtr writer, const xmlChar* tag, SharedBuffer* buffer)
    348 {
    349     return saveArchiveResourceField(writer, tag, buffer->data(), buffer->size());
    350 }
    351 
    352 static bool saveArchiveResourceField(xmlTextWriterPtr writer, const xmlChar* tag, const String& string)
    353 {
    354     CString utf8String = string.utf8();
    355 
    356     return saveArchiveResourceField(writer, tag, utf8String.data(), utf8String.length());
    357 }
    358 
    359 static bool saveArchiveResource(xmlTextWriterPtr writer, PassRefPtr<ArchiveResource> resource)
    360 {
    361     int result = xmlTextWriterStartElement(writer, archiveResourceTag);
    362     if (result < 0) {
    363         LOGD("saveArchiveResource: Failed to start element.");
    364         return false;
    365     }
    366 
    367     if (!saveArchiveResourceField(writer, urlFieldTag, resource->url().string())
    368         || !saveArchiveResourceField(writer, mimeFieldTag, resource->mimeType())
    369         || !saveArchiveResourceField(writer, encodingFieldTag, resource->textEncoding())
    370         || !saveArchiveResourceField(writer, frameFieldTag, resource->frameName())
    371         || !saveArchiveResourceField(writer, dataFieldTag, resource->data()))
    372         return false;
    373 
    374     result = xmlTextWriterEndElement(writer);
    375     if (result < 0) {
    376         LOGD("saveArchiveResource: Failed to end element.");
    377         return false;
    378     }
    379 
    380     return true;
    381 }
    382 
    383 static bool saveArchive(xmlTextWriterPtr writer, PassRefPtr<Archive> archive)
    384 {
    385     int result = xmlTextWriterStartElement(writer, archiveTag);
    386     if (result < 0) {
    387         LOGD("saveArchive: Failed to start element.");
    388         return false;
    389     }
    390 
    391     result = xmlTextWriterStartElement(writer, mainResourceTag);
    392     if (result < 0) {
    393         LOGD("saveArchive: Failed to start element.");
    394         return false;
    395     }
    396 
    397     if (!saveArchiveResource(writer, archive->mainResource()))
    398         return false;
    399 
    400     result = xmlTextWriterEndElement(writer);
    401     if (result < 0) {
    402         LOGD("saveArchive: Failed to end element.");
    403         return false;
    404     }
    405 
    406     result = xmlTextWriterStartElement(writer, subresourcesTag);
    407     if (result < 0) {
    408         LOGD("saveArchive: Failed to start element.");
    409         return false;
    410     }
    411 
    412     for (Vector<const RefPtr<ArchiveResource> >::iterator subresource = archive->subresources().begin();
    413          subresource != archive->subresources().end();
    414          subresource++) {
    415         if (!saveArchiveResource(writer, *subresource))
    416             return false;
    417     }
    418 
    419     result = xmlTextWriterEndElement(writer);
    420     if (result < 0) {
    421         LOGD("saveArchive: Failed to end element.");
    422         return false;
    423     }
    424 
    425     result = xmlTextWriterStartElement(writer, subframesTag);
    426     if (result < 0) {
    427         LOGD("saveArchive: Failed to start element.");
    428         return false;
    429     }
    430 
    431     for (Vector<const RefPtr<Archive> >::iterator subframe = archive->subframeArchives().begin();
    432             subframe != archive->subframeArchives().end();
    433             subframe++) {
    434         if (!saveArchive(writer, *subframe))
    435             return false;
    436     }
    437 
    438     result = xmlTextWriterEndElement(writer);
    439     if (result < 0) {
    440         LOGD("saveArchive: Failed to end element.");
    441         return true;
    442     }
    443 
    444     return true;
    445 }
    446 
    447 bool WebArchiveAndroid::saveWebArchive(xmlTextWriterPtr writer)
    448 {
    449     const char* const defaultXmlVersion = 0;
    450     const char* const defaultEncoding = 0;
    451     const char* const defaultStandalone = 0;
    452 
    453     int result = xmlTextWriterStartDocument(writer, defaultXmlVersion, defaultEncoding, defaultStandalone);
    454     if (result < 0) {
    455         LOGD("saveWebArchive: Failed to start document.");
    456         return false;
    457     }
    458 
    459     if (!saveArchive(writer, this))
    460         return false;
    461 
    462     result = xmlTextWriterEndDocument(writer);
    463     if (result< 0) {
    464         LOGD("saveWebArchive: Failed to end document.");
    465         return false;
    466     }
    467 
    468     return true;
    469 }
    470 
    471 }
    472 
    473 #endif // ENABLE(WEB_ARCHIVE)
    474