A fork of https://github.com/crosspoint-reader/crosspoint-reader
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix: Strip unused CSS rules (#1014)

## Summary

* In a sample book I loaded, it had 900+ CSS rules, and took up 180kB of
memory loading the cache in
* Looking at the rules, a lot of them were completely useless as we only
ever apply look for 3 kinds of CSS rules:
* `tag`
* `tag.class1`
* `.class1`
* Stripping out CSS rules with descendant, nested, attribute matching,
sibling matching, pseudo element selection (as we never actually read
these from the cache) reduced the rule count down to 200

## Additional Context

* I've left in `.class1.class2` rules for now, even though we
technically can never match on them as they're likely to be addressed
soonest out of the all the CSS expansion
* Because we don't ever delete the CSS cache, users will need to delete
the book cache through the menu in order to get this new logic
* A new PR should be done up to address this - tracked here
https://github.com/crosspoint-reader/crosspoint-reader/issues/1015

---

### AI Usage

While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.

Did you use AI tools to help write this code? No

authored by

Dave Allie and committed by
GitHub
356fe9a3 5da23eed

+53 -1
+53 -1
lib/Epub/Epub/css/CssParser.cpp
··· 74 74 } 75 75 76 76 // Remove trailing space 77 - if (!result.empty() && result.back() == ' ') { 77 + while (!result.empty() && (result.back() == ' ' || result.back() == '\n')) { 78 78 result.pop_back(); 79 79 } 80 80 return result; ··· 365 365 std::string key = normalized(sel); 366 366 if (key.empty()) continue; 367 367 368 + // TODO: Consider adding support for sibling css selectors in the future 369 + // Ensure no + in selector as we don't support adjacent CSS selectors for now 370 + if (key.find('+') != std::string_view::npos) { 371 + continue; 372 + } 373 + 374 + // TODO: Consider adding support for direct nested css selectors in the future 375 + // Ensure no > in selector as we don't support nested CSS selectors for now 376 + if (key.find('>') != std::string_view::npos) { 377 + continue; 378 + } 379 + 380 + // TODO: Consider adding support for attribute css selectors in the future 381 + // Ensure no [ in selector as we don't support attribute CSS selectors for now 382 + if (key.find('[') != std::string_view::npos) { 383 + continue; 384 + } 385 + 386 + // TODO: Consider adding support for pseudo selectors in the future 387 + // Ensure no : in selector as we don't support pseudo CSS selectors for now 388 + if (key.find(':') != std::string_view::npos) { 389 + continue; 390 + } 391 + 392 + // TODO: Consider adding support for ID css selectors in the future 393 + // Ensure no # in selector as we don't support ID CSS selectors for now 394 + if (key.find('#') != std::string_view::npos) { 395 + continue; 396 + } 397 + 398 + // TODO: Consider adding support for general sibling combinator selectors in the future 399 + // Ensure no ~ in selector as we don't support general sibling combinator CSS selectors for now 400 + if (key.find('~') != std::string_view::npos) { 401 + continue; 402 + } 403 + 404 + // TODO: Consider adding support for wildcard css selectors in the future 405 + // Ensure no * in selector as we don't support wildcard CSS selectors for now 406 + if (key.find('*') != std::string_view::npos) { 407 + continue; 408 + } 409 + 410 + // TODO: Add support for more complex selectors in the future 411 + // At the moment, we only ever check for `tag`, `tag.class1` or `.class1` 412 + // If the selector has whitespace in it, then it's either a CSS selector for a descendant element (e.g. `tag1 tag2`) 413 + // or some other slightly more advanced CSS selector which we don't support yet 414 + if (key.find(' ') != std::string_view::npos) { 415 + continue; 416 + } 417 + 368 418 // Skip if this would exceed the rule limit 369 419 if (rulesBySelector_.size() >= MAX_RULES) { 370 420 LOG_DBG("CSS", "Reached max rules limit, stopping selector processing"); ··· 550 600 result.applyOver(tagIt->second); 551 601 } 552 602 603 + // TODO: Support combinations of classes (e.g. style on .class1.class2) 553 604 // 2. Apply class styles (medium priority) 554 605 if (!classAttr.empty()) { 555 606 const auto classes = splitWhitespace(classAttr); ··· 563 614 } 564 615 } 565 616 617 + // TODO: Support combinations of classes (e.g. style on p.class1.class2) 566 618 // 3. Apply element.class styles (higher priority) 567 619 for (const auto& cls : classes) { 568 620 std::string combinedKey = tag + "." + normalized(cls);