• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
create(PassRefPtr<ArchiveResource> mainResource,Vector<PassRefPtr<ArchiveResource>> & subresources,Vector<PassRefPtr<Archive>> & subframeArchives)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 
create(Frame * frame)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 
WebArchiveAndroid(PassRefPtr<ArchiveResource> mainResource,Vector<PassRefPtr<ArchiveResource>> & subresources,Vector<PassRefPtr<Archive>> & subframeArchives)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 
loadArchiveResourceField(xmlNodePtr resourceNode,const xmlChar * fieldName,Vector<char> * outputData)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 
loadArchiveResourceFieldBuffer(xmlNodePtr resourceNode,const xmlChar * fieldName)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 
loadArchiveResourceFieldString(xmlNodePtr resourceNode,const xmlChar * fieldName)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 
loadArchiveResourceFieldURL(xmlNodePtr resourceNode,const xmlChar * fieldName)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 
loadArchiveResource(xmlNodePtr resourceNode)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 
loadArchive(xmlNodePtr archiveNode)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 
createArchiveForError()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 
create(SharedBuffer * buffer)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 
saveArchiveResourceField(xmlTextWriterPtr writer,const xmlChar * tag,const char * data,int size)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 
saveArchiveResourceField(xmlTextWriterPtr writer,const xmlChar * tag,SharedBuffer * buffer)348 static bool saveArchiveResourceField(xmlTextWriterPtr writer, const xmlChar* tag, SharedBuffer* buffer)
349 {
350     return saveArchiveResourceField(writer, tag, buffer->data(), buffer->size());
351 }
352 
saveArchiveResourceField(xmlTextWriterPtr writer,const xmlChar * tag,const String & string)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 
saveArchiveResource(xmlTextWriterPtr writer,PassRefPtr<ArchiveResource> resource)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 
saveArchive(xmlTextWriterPtr writer,PassRefPtr<Archive> archive)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 
saveWebArchive(xmlTextWriterPtr writer)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