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 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 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 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 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 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 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 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 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 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 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 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 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 347 static bool saveArchiveResourceField(xmlTextWriterPtr writer, const xmlChar* tag, SharedBuffer* buffer) 348 { 349 return saveArchiveResourceField(writer, tag, buffer->data(), buffer->size()); 350 } 351 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 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 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 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