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