| 283 | } |
| 284 | |
| 285 | void Epub::parseCssFiles() const { |
| 286 | // Maximum CSS file size we'll attempt to parse (uncompressed) |
| 287 | // Larger files risk memory exhaustion on ESP32 |
| 288 | constexpr size_t MAX_CSS_FILE_SIZE = 128 * 1024; // 128KB |
| 289 | // Minimum heap required before attempting CSS parsing |
| 290 | constexpr size_t MIN_HEAP_FOR_CSS_PARSING = 64 * 1024; // 64KB |
| 291 | |
| 292 | if (cssFiles.empty()) { |
| 293 | LOG_DBG("EBP", "No CSS files to parse, but CssParser created for inline styles"); |
| 294 | } |
| 295 | |
| 296 | LOG_DBG("EBP", "CSS files to parse: %zu", cssFiles.size()); |
| 297 | |
| 298 | // See if we have a cached version of the CSS rules |
| 299 | if (cssParser->hasCache()) { |
| 300 | LOG_DBG("EBP", "CSS cache exists, skipping parseCssFiles"); |
| 301 | return; |
| 302 | } |
| 303 | |
| 304 | // No cache yet - parse CSS files |
| 305 | for (const auto& cssPath : cssFiles) { |
| 306 | LOG_DBG("EBP", "Parsing CSS file: %s", cssPath.c_str()); |
| 307 | |
| 308 | // Check heap before parsing - CSS parsing allocates heavily |
| 309 | const uint32_t freeHeap = ESP.getFreeHeap(); |
| 310 | if (freeHeap < MIN_HEAP_FOR_CSS_PARSING) { |
| 311 | LOG_ERR("EBP", "Insufficient heap for CSS parsing (%u bytes free, need %zu), skipping: %s", freeHeap, |
| 312 | MIN_HEAP_FOR_CSS_PARSING, cssPath.c_str()); |
| 313 | continue; |
| 314 | } |
| 315 | |
| 316 | // Check CSS file size before decompressing - skip files that are too large |
| 317 | size_t cssFileSize = 0; |
| 318 | if (getItemSize(cssPath, &cssFileSize)) { |
| 319 | if (cssFileSize > MAX_CSS_FILE_SIZE) { |
| 320 | LOG_ERR("EBP", "CSS file too large (%zu bytes > %zu max), skipping: %s", cssFileSize, MAX_CSS_FILE_SIZE, |
| 321 | cssPath.c_str()); |
| 322 | continue; |
| 323 | } |
| 324 | } |
| 325 | |
| 326 | // Extract CSS file to temp location |
| 327 | const auto tmpCssPath = getCachePath() + "/.tmp.css"; |
| 328 | HalFile tempCssFile; |
| 329 | if (!Storage.openFileForWrite("EBP", tmpCssPath, tempCssFile)) { |
| 330 | LOG_ERR("EBP", "Could not create temp CSS file"); |
| 331 | continue; |
| 332 | } |
| 333 | if (!readItemContentsToStream(cssPath, tempCssFile, 1024)) { |
| 334 | LOG_ERR("EBP", "Could not read CSS file: %s", cssPath.c_str()); |
| 335 | // Explicitly close() file before calling Storage.remove() |
| 336 | tempCssFile.close(); |
| 337 | Storage.remove(tmpCssPath.c_str()); |
| 338 | continue; |
| 339 | } |
| 340 | // Explicitly close() file before reopening for reading |
| 341 | tempCssFile.close(); |
| 342 |
nothing calls this directly
no test coverage detected