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 <utils/Log.h>
     40 #include <wtf/text/CString.h>
     41 
     42 namespace WebCore {
     43 
     44 static const xmlChar* const archiveTag = BAD_CAST "Archive";
     45 static const xmlChar* const archiveResourceTag = BAD_CAST "ArchiveResource";
     46 static const xmlChar* const mainResourceTag = BAD_CAST "mainResource";
     47 static const xmlChar* const subresourcesTag = BAD_CAST "subresources";
     48 static const xmlChar* const subframesTag = BAD_CAST "subframes";
     49 static const xmlChar* const urlFieldTag = BAD_CAST "url";
     50 static const xmlChar* const mimeFieldTag = BAD_CAST "mimeType";
     51 static const xmlChar* const encodingFieldTag = BAD_CAST "textEncoding";
     52 static const xmlChar* const frameFieldTag = BAD_CAST "frameName";
     53 static const xmlChar* const dataFieldTag = BAD_CAST "data";
     54 
     55 PassRefPtr<WebArchiveAndroid> WebArchiveAndroid::create(PassRefPtr<ArchiveResource> mainResource,
     56         Vector<PassRefPtr<ArchiveResource> >& subresources,
     57         Vector<PassRefPtr<Archive> >& subframeArchives)
     58 {
     59     if (mainResource)
     60         return adoptRef(new WebArchiveAndroid(mainResource, subresources, subframeArchives));
     61     return 0;
     62 }
     63 
     64 PassRefPtr<WebArchiveAndroid> WebArchiveAndroid::create(Frame* frame)
     65 {
     66     PassRefPtr<ArchiveResource> mainResource = frame->loader()->documentLoader()->mainResource();
     67     Vector<PassRefPtr<ArchiveResource> > subresources;
     68     Vector<PassRefPtr<Archive> > subframes;
     69     int children = frame->tree()->childCount();
     70 
     71     frame->loader()->documentLoader()->getSubresources(subresources);
     72 
     73     for (int child = 0; child < children; child++)
     74         subframes.append(create(frame->tree()->child(child)));
     75 
     76     return create(mainResource, subresources, subframes);
     77 }
     78 
     79 WebArchiveAndroid::WebArchiveAndroid(PassRefPtr<ArchiveResource> mainResource,
     80         Vector<PassRefPtr<ArchiveResource> >& subresources,
     81         Vector<PassRefPtr<Archive> >& subframeArchives)
     82 {
     83     setMainResource(mainResource);
     84 
     85     for (Vector<PassRefPtr<ArchiveResource> >::iterator subresourcesIterator = subresources.begin();
     86          subresourcesIterator != subresources.end();
     87          subresourcesIterator++) {
     88         addSubresource(*subresourcesIterator);
     89     }
     90 
     91     for (Vector<PassRefPtr<Archive> >::iterator subframesIterator = subframeArchives.begin();
     92          subframesIterator != subframeArchives.end();
     93          subframesIterator++) {
     94         addSubframeArchive(*subframesIterator);
     95     }
     96 }
     97 
     98 static bool loadArchiveResourceField(xmlNodePtr resourceNode, const xmlChar* fieldName, Vector<char>* outputData)
     99 {
    100     if (!outputData)
    101         return false;
    102 
    103     outputData->clear();
    104 
    105     const char* base64Data = 0;
    106 
    107     for (xmlNodePtr fieldNode = resourceNode->xmlChildrenNode;
    108          fieldNode;
    109          fieldNode = fieldNode->next) {
    110         if (xmlStrEqual(fieldNode->name, fieldName)) {
    111             base64Data = (const char*)xmlNodeGetContent(fieldNode->xmlChildrenNode);
    112             if (!base64Data) {
    113                 /* Empty fields seem to break if they aren't null terminated. */
    114                 outputData->append('\0');
    115                 return true;
    116             }
    117             break;
    118         }
    119     }
    120     if (!base64Data) {
    121         ALOGD("loadArchiveResourceField: Failed to load field.");
    122         return false;
    123     }
    124 
    125     const int base64Size = xmlStrlen(BAD_CAST base64Data);
    126 
    127     const int result = base64Decode(base64Data, base64Size, *outputData);
    128     if (!result) {
    129         ALOGD("loadArchiveResourceField: Failed to decode field.");
    130         return false;
    131     }
    132 
    133     return true;
    134 }
    135 
    136 static PassRefPtr<SharedBuffer> loadArchiveResourceFieldBuffer(xmlNodePtr resourceNode, const xmlChar* fieldName)
    137 {
    138     Vector<char> fieldData;
    139 
    140     if (loadArchiveResourceField(resourceNode, fieldName, &fieldData))
    141         return SharedBuffer::create(fieldData.data(), fieldData.size());
    142 
    143     return 0;
    144 }
    145 
    146 static String loadArchiveResourceFieldString(xmlNodePtr resourceNode, const xmlChar* fieldName)
    147 {
    148     Vector<char> fieldData;
    149 
    150     if (loadArchiveResourceField(resourceNode, fieldName, &fieldData))
    151         return String::fromUTF8(fieldData.data(), fieldData.size());
    152 
    153     return String();
    154 }
    155 
    156 static KURL loadArchiveResourceFieldURL(xmlNodePtr resourceNode, const xmlChar* fieldName)
    157 {
    158     Vector<char> fieldData;
    159 
    160     if (loadArchiveResourceField(resourceNode, fieldName, &fieldData))
    161         return KURL(ParsedURLString, String::fromUTF8(fieldData.data(), fieldData.size()));
    162 
    163     return KURL();
    164 }
    165 
    166 static PassRefPtr<ArchiveResource> loadArchiveResource(xmlNodePtr resourceNode)
    167 {
    168     if (!xmlStrEqual(resourceNode->name, archiveResourceTag)) {
    169         ALOGD("loadArchiveResource: Malformed resource.");
    170         return 0;
    171     }
    172 
    173     KURL url = loadArchiveResourceFieldURL(resourceNode, urlFieldTag);
    174     if (url.isNull()) {
    175         ALOGD("loadArchiveResource: Failed to load resource.");
    176         return 0;
    177     }
    178 
    179     String mimeType = loadArchiveResourceFieldString(resourceNode, mimeFieldTag);
    180     if (mimeType.isNull()) {
    181         ALOGD("loadArchiveResource: Failed to load resource.");
    182         return 0;
    183     }
    184 
    185     String textEncoding = loadArchiveResourceFieldString(resourceNode, encodingFieldTag);
    186     if (textEncoding.isNull()) {
    187         ALOGD("loadArchiveResource: Failed to load resource.");
    188         return 0;
    189     }
    190 
    191     String frameName = loadArchiveResourceFieldString(resourceNode, frameFieldTag);
    192     if (frameName.isNull()) {
    193         ALOGD("loadArchiveResource: Failed to load resource.");
    194         return 0;
    195     }
    196 
    197     PassRefPtr<SharedBuffer> data = loadArchiveResourceFieldBuffer(resourceNode, dataFieldTag);
    198     if (!data) {
    199         ALOGD("loadArchiveResource: Failed to load resource.");
    200         return 0;
    201     }
    202 
    203     return ArchiveResource::create(data, url, mimeType, textEncoding, frameName);
    204 }
    205 
    206 static PassRefPtr<WebArchiveAndroid> loadArchive(xmlNodePtr archiveNode)
    207 {
    208     xmlNodePtr resourceNode = 0;
    209 
    210     PassRefPtr<ArchiveResource> mainResource;
    211     Vector<PassRefPtr<ArchiveResource> > subresources;
    212     Vector<PassRefPtr<Archive> > subframes;
    213 
    214     if (!xmlStrEqual(archiveNode->name, archiveTag)) {
    215         ALOGD("loadArchive: Malformed archive.");
    216         return 0;
    217     }
    218 
    219     for (resourceNode = archiveNode->xmlChildrenNode;
    220          resourceNode;
    221          resourceNode = resourceNode->next) {
    222         if (xmlStrEqual(resourceNode->name, mainResourceTag)) {
    223             resourceNode = resourceNode->xmlChildrenNode;
    224             if (!resourceNode)
    225                 break;
    226             mainResource = loadArchiveResource(resourceNode);
    227             break;
    228         }
    229     }
    230     if (!mainResource) {
    231         ALOGD("loadArchive: Failed to load main resource.");
    232         return 0;
    233     }
    234 
    235     for (resourceNode = archiveNode->xmlChildrenNode;
    236          resourceNode;
    237          resourceNode = resourceNode->next) {
    238         if (xmlStrEqual(resourceNode->name, subresourcesTag)) {
    239             for (resourceNode = resourceNode->xmlChildrenNode;
    240                  resourceNode;
    241                  resourceNode = resourceNode->next) {
    242                 PassRefPtr<ArchiveResource> subresource = loadArchiveResource(resourceNode);
    243                 if (!subresource) {
    244                     ALOGD("loadArchive: Failed to load subresource.");
    245                     break;
    246                 }
    247                 subresources.append(subresource);
    248             }
    249             break;
    250         }
    251     }
    252 
    253     for (resourceNode = archiveNode->xmlChildrenNode;
    254          resourceNode;
    255          resourceNode = resourceNode->next) {
    256         if (xmlStrEqual(resourceNode->name, subframesTag)) {
    257             for (resourceNode = resourceNode->xmlChildrenNode;
    258                  resourceNode;
    259                  resourceNode = resourceNode->next) {
    260                 PassRefPtr<WebArchiveAndroid> subframe = loadArchive(resourceNode);
    261                 if (!subframe) {
    262                     ALOGD("loadArchive: Failed to load subframe.");
    263                     break;
    264                 }
    265                 subframes.append(subframe);
    266             }
    267             break;
    268         }
    269     }
    270 
    271     return WebArchiveAndroid::create(mainResource, subresources, subframes);
    272 }
    273 
    274 static PassRefPtr<WebArchiveAndroid> createArchiveForError()
    275 {
    276     /* When an archive cannot be loaded, we return an empty archive instead. */
    277     PassRefPtr<ArchiveResource> mainResource = ArchiveResource::create(
    278         SharedBuffer::create(), KURL(ParsedURLString, String::fromUTF8("file:///dummy")),
    279         String::fromUTF8("text/plain"), String(""), String(""));
    280     Vector<PassRefPtr<ArchiveResource> > subresources;
    281     Vector<PassRefPtr<Archive> > subframes;
    282 
    283     return WebArchiveAndroid::create(mainResource, subresources, subframes);
    284 }
    285 
    286 PassRefPtr<WebArchiveAndroid> WebArchiveAndroid::create(SharedBuffer* buffer)
    287 {
    288     const char* const noBaseUrl = "";
    289     const char* const defaultEncoding = 0;
    290     const int noParserOptions = 0;
    291 
    292     xmlDocPtr doc = xmlReadMemory(buffer->data(), buffer->size(), noBaseUrl, defaultEncoding, noParserOptions);
    293     if (!doc) {
    294         ALOGD("create: Failed to parse document.");
    295         return createArchiveForError();
    296     }
    297 
    298     xmlNodePtr root = xmlDocGetRootElement(doc);
    299     if (!root) {
    300         ALOGD("create: Empty document.");
    301         xmlFreeDoc(doc);
    302         return createArchiveForError();
    303     }
    304 
    305     RefPtr<WebArchiveAndroid> archive = loadArchive(root);
    306     if (!archive) {
    307         ALOGD("create: Failed to load archive.");
    308         xmlFreeDoc(doc);
    309         return createArchiveForError();
    310     }
    311 
    312     xmlFreeDoc(doc);
    313     return archive.release();
    314 }
    315 
    316 static bool saveArchiveResourceField(xmlTextWriterPtr writer, const xmlChar* tag, const char* data, int size)
    317 {
    318     int result = xmlTextWriterStartElement(writer, tag);
    319     if (result < 0) {
    320         ALOGD("saveArchiveResourceField: Failed to start element.");
    321         return false;
    322     }
    323 
    324     if (size > 0) {
    325         Vector<char> base64Data;
    326         base64Encode(data, size, base64Data, false);
    327         if (base64Data.isEmpty()) {
    328             ALOGD("saveArchiveResourceField: Failed to base64 encode data.");
    329             return false;
    330         }
    331 
    332         result = xmlTextWriterWriteRawLen(writer, BAD_CAST base64Data.data(), base64Data.size());
    333         if (result < 0) {
    334             ALOGD("saveArchiveResourceField: Failed to write data.");
    335             return false;
    336         }
    337     }
    338 
    339     result = xmlTextWriterEndElement(writer);
    340     if (result < 0) {
    341         ALOGD("saveArchiveResourceField: Failed to end element.");
    342         return false;
    343     }
    344 
    345     return true;
    346 }
    347 
    348 static bool saveArchiveResourceField(xmlTextWriterPtr writer, const xmlChar* tag, SharedBuffer* buffer)
    349 {
    350     return saveArchiveResourceField(writer, tag, buffer->data(), buffer->size());
    351 }
    352 
    353 static bool saveArchiveResourceField(xmlTextWriterPtr writer, const xmlChar* tag, const String& string)
    354 {
    355     CString utf8String = string.utf8();
    356 
    357     return saveArchiveResourceField(writer, tag, utf8String.data(), utf8String.length());
    358 }
    359 
    360 static bool saveArchiveResource(xmlTextWriterPtr writer, PassRefPtr<ArchiveResource> resource)
    361 {
    362     int result = xmlTextWriterStartElement(writer, archiveResourceTag);
    363     if (result < 0) {
    364         ALOGD("saveArchiveResource: Failed to start element.");
    365         return false;
    366     }
    367 
    368     if (!saveArchiveResourceField(writer, urlFieldTag, resource->url().string())
    369         || !saveArchiveResourceField(writer, mimeFieldTag, resource->mimeType())
    370         || !saveArchiveResourceField(writer, encodingFieldTag, resource->textEncoding())
    371         || !saveArchiveResourceField(writer, frameFieldTag, resource->frameName())
    372         || !saveArchiveResourceField(writer, dataFieldTag, resource->data()))
    373         return false;
    374 
    375     result = xmlTextWriterEndElement(writer);
    376     if (result < 0) {
    377         ALOGD("saveArchiveResource: Failed to end element.");
    378         return false;
    379     }
    380 
    381     return true;
    382 }
    383 
    384 static bool saveArchive(xmlTextWriterPtr writer, PassRefPtr<Archive> archive)
    385 {
    386     int result = xmlTextWriterStartElement(writer, archiveTag);
    387     if (result < 0) {
    388         ALOGD("saveArchive: Failed to start element.");
    389         return false;
    390     }
    391 
    392     result = xmlTextWriterStartElement(writer, mainResourceTag);
    393     if (result < 0) {
    394         ALOGD("saveArchive: Failed to start element.");
    395         return false;
    396     }
    397 
    398     if (!saveArchiveResource(writer, archive->mainResource()))
    399         return false;
    400 
    401     result = xmlTextWriterEndElement(writer);
    402     if (result < 0) {
    403         ALOGD("saveArchive: Failed to end element.");
    404         return false;
    405     }
    406 
    407     result = xmlTextWriterStartElement(writer, subresourcesTag);
    408     if (result < 0) {
    409         ALOGD("saveArchive: Failed to start element.");
    410         return false;
    411     }
    412 
    413     for (Vector<const RefPtr<ArchiveResource> >::iterator subresource = archive->subresources().begin();
    414          subresource != archive->subresources().end();
    415          subresource++) {
    416         if (!saveArchiveResource(writer, *subresource))
    417             return false;
    418     }
    419 
    420     result = xmlTextWriterEndElement(writer);
    421     if (result < 0) {
    422         ALOGD("saveArchive: Failed to end element.");
    423         return false;
    424     }
    425 
    426     result = xmlTextWriterStartElement(writer, subframesTag);
    427     if (result < 0) {
    428         ALOGD("saveArchive: Failed to start element.");
    429         return false;
    430     }
    431 
    432     for (Vector<const RefPtr<Archive> >::iterator subframe = archive->subframeArchives().begin();
    433             subframe != archive->subframeArchives().end();
    434             subframe++) {
    435         if (!saveArchive(writer, *subframe))
    436             return false;
    437     }
    438 
    439     result = xmlTextWriterEndElement(writer);
    440     if (result < 0) {
    441         ALOGD("saveArchive: Failed to end element.");
    442         return true;
    443     }
    444 
    445     return true;
    446 }
    447 
    448 bool WebArchiveAndroid::saveWebArchive(xmlTextWriterPtr writer)
    449 {
    450     const char* const defaultXmlVersion = 0;
    451     const char* const defaultEncoding = 0;
    452     const char* const defaultStandalone = 0;
    453 
    454     int result = xmlTextWriterStartDocument(writer, defaultXmlVersion, defaultEncoding, defaultStandalone);
    455     if (result < 0) {
    456         ALOGD("saveWebArchive: Failed to start document.");
    457         return false;
    458     }
    459 
    460     if (!saveArchive(writer, this))
    461         return false;
    462 
    463     result = xmlTextWriterEndDocument(writer);
    464     if (result< 0) {
    465         ALOGD("saveWebArchive: Failed to end document.");
    466         return false;
    467     }
    468 
    469     return true;
    470 }
    471 
    472 }
    473 
    474 #endif // ENABLE(WEB_ARCHIVE)
    475