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