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