Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Some CSS attribute selectors and pseudoclasses fixes #303

Merged
merged 3 commits into from
Aug 3, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions crengine/include/lvstsheet.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,16 @@ enum LVCssSelectorPseudoClass
{
csspc_first_child, // :first-child
csspc_first_of_type, // :first-of-type
csspc_last_child, // :last-child
csspc_last_of_type, // :last-of-type
csspc_nth_child, // :nth-child(even), :nth-child(3n+4)
csspc_nth_of_type, // :nth-of-type()
// Those after this won't be valid when checked in the initial
// document loading phase when the XML is being parsed, as at
// this point, the checked node is always the last node as we
// haven't yet parsed its following siblings. When meeting one,
// we'll need to re-render and re-check styles after load with
// a fully built DOM.
csspc_last_child, // :last-child
csspc_last_of_type, // :last-of-type
csspc_nth_last_child, // :nth-last-child()
csspc_nth_last_of_type, // :nth-last-of-type()
csspc_only_child, // :only-child
Expand All @@ -107,10 +113,10 @@ static const char * css_pseudo_classes[] =
{
"first-child",
"first-of-type",
"last-child",
"last-of-type",
"nth-child",
"nth-of-type",
"last-child",
"last-of-type",
"nth-last-child",
"nth-last-of-type",
"only-child",
Expand Down
4 changes: 4 additions & 0 deletions crengine/include/lvtinydom.h
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,7 @@ class tinyNodeCollection
lUInt32 _nodeStyleHash;
lUInt32 _nodeDisplayStyleHash;
lUInt32 _nodeDisplayStyleHashInitial;
bool _nodeStylesInvalidIfLoading;

int calcFinalBlocks();
void dropStyles();
Expand Down Expand Up @@ -565,6 +566,9 @@ class tinyNodeCollection
return _nodeDisplayStyleHashInitial != NODE_DISPLAY_STYLE_HASH_UNITIALIZED &&
_nodeDisplayStyleHash != _nodeDisplayStyleHashInitial;
}
void setNodeStylesInvalidIfLoading() {
_nodeStylesInvalidIfLoading = true;
}

/// if a cache file is in use
bool hasCacheFile() { return _cacheFile != NULL; }
Expand Down
1 change: 1 addition & 0 deletions crengine/src/epubfmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1039,6 +1039,7 @@ bool ImportEpubDocument( LVStreamRef stream, ldomDocument * m_doc, LVDocViewCall
// set document font list, and register fonts
m_doc->getEmbeddedFontList().set(fontList);
m_doc->registerEmbeddedFonts();
printf("CRE: document loaded, but styles re-init needed (cause: embedded fonts)\n");
m_doc->forceReinitStyles();
}

Expand Down
62 changes: 44 additions & 18 deletions crengine/src/lvstsheet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2437,17 +2437,17 @@ lUInt32 LVCssSelectorRule::getWeight() {
break;
case cssrt_attrset: // E[foo]
case cssrt_attreq: // E[foo="value"]
case cssrt_attreq_i: // E[foo="value i"]
case cssrt_attreq_i: // E[foo="value" i]
case cssrt_attrhas: // E[foo~="value"]
case cssrt_attrhas_i: // E[foo~="value i"]
case cssrt_attrhas_i: // E[foo~="value" i]
case cssrt_attrstarts_word: // E[foo|="value"]
case cssrt_attrstarts_word_i: // E[foo|="value i"]
case cssrt_attrstarts_word_i: // E[foo|="value" i]
case cssrt_attrstarts: // E[foo^="value"]
case cssrt_attrstarts_i: // E[foo^="value i"]
case cssrt_attrstarts_i: // E[foo^="value" i]
case cssrt_attrends: // E[foo$="value"]
case cssrt_attrends_i: // E[foo$="value i"]
case cssrt_attrends_i: // E[foo$="value" i]
case cssrt_attrcontains: // E[foo*="value"]
case cssrt_attrcontains_i: // E[foo*="value i"]
case cssrt_attrcontains_i: // E[foo*="value" i]
case cssrt_class: // E.class
case cssrt_pseudoclass: // E:pseudo-class
return 1 << 8;
Expand Down Expand Up @@ -2578,18 +2578,22 @@ bool LVCssSelectorRule::check( const ldomNode * & node )
}
break;
case cssrt_attreq: // E[foo="value"]
case cssrt_attreq_i: // E[foo="value i"]
case cssrt_attreq_i: // E[foo="value" i]
{
if ( !node->hasAttribute(_attrid) )
return false;
lString16 val = node->getAttributeValue(_attrid);
if (_type == cssrt_attreq_i)
val.lowercase();
return val == _value;
}
break;
case cssrt_attrhas: // E[foo~="value"]
case cssrt_attrhas_i: // E[foo~="value i"]
case cssrt_attrhas_i: // E[foo~="value" i]
// one of space separated values
{
if ( !node->hasAttribute(_attrid) )
return false;
lString16 val = node->getAttributeValue(_attrid);
if (_type == cssrt_attrhas_i)
val.lowercase();
Expand All @@ -2603,8 +2607,10 @@ bool LVCssSelectorRule::check( const ldomNode * & node )
}
break;
case cssrt_attrstarts_word: // E[foo|="value"]
case cssrt_attrstarts_word_i: // E[foo|="value i"]
case cssrt_attrstarts_word_i: // E[foo|="value" i]
{
if ( !node->hasAttribute(_attrid) )
return false;
// value can be exactly value or can begin with value
// immediately followed by a hyphen
lString16 val = node->getAttributeValue(_attrid);
Expand All @@ -2624,8 +2630,10 @@ bool LVCssSelectorRule::check( const ldomNode * & node )
}
break;
case cssrt_attrstarts: // E[foo^="value"]
case cssrt_attrstarts_i: // E[foo^="value i"]
case cssrt_attrstarts_i: // E[foo^="value" i]
{
if ( !node->hasAttribute(_attrid) )
return false;
lString16 val = node->getAttributeValue(_attrid);
int val_len = val.length();
int value_len = _value.length();
Expand All @@ -2638,8 +2646,10 @@ bool LVCssSelectorRule::check( const ldomNode * & node )
}
break;
case cssrt_attrends: // E[foo$="value"]
case cssrt_attrends_i: // E[foo$="value i"]
case cssrt_attrends_i: // E[foo$="value" i]
{
if ( !node->hasAttribute(_attrid) )
return false;
lString16 val = node->getAttributeValue(_attrid);
int val_len = val.length();
int value_len = _value.length();
Expand All @@ -2652,8 +2662,10 @@ bool LVCssSelectorRule::check( const ldomNode * & node )
}
break;
case cssrt_attrcontains: // E[foo*="value"]
case cssrt_attrcontains_i: // E[foo*="value i"]
case cssrt_attrcontains_i: // E[foo*="value" i]
{
if ( !node->hasAttribute(_attrid) )
return false;
lString16 val = node->getAttributeValue(_attrid);
if (_value.length()>val.length())
return false;
Expand Down Expand Up @@ -2859,16 +2871,18 @@ bool parse_attr_value( const char * &str, char * buf, bool &parse_trailing_i, ch
buf[pos] = 0;
str += pos+1;
skip_spaces( str );
if (*str != stop_char)
return false;
str++;
if (parse_trailing_i && pos >=2) {
// The trailing ' i' must be outside the quotes
if (parse_trailing_i) {
parse_trailing_i = false;
if ( (buf[pos-2]==' ') && (buf[pos-1]=='i' || buf[pos-1]=='I') ) {
if (*str == 'i' || *str == 'I') {
parse_trailing_i = true;
buf[pos-2] = 0;
str++;
skip_spaces( str );
}
}
if (*str != stop_char)
return false;
str++;
return true;
}
else
Expand All @@ -2881,6 +2895,8 @@ bool parse_attr_value( const char * &str, char * buf, bool &parse_trailing_i, ch
int end_pos = pos;
if (parse_trailing_i) {
parse_trailing_i = false;
if (end_pos == 0) // Empty value, or some leading space: this is invalid
return false;
if (str[pos] && str[pos]==' ' && str[pos+1] && (str[pos+1]=='i' || str[pos+1]=='I')) {
parse_trailing_i = true;
pos+=2;
Expand Down Expand Up @@ -2951,6 +2967,16 @@ LVCssSelectorRule * parse_attr( const char * &str, lxmlDocBase * doc )
lString16 s( attrvalue );
rule->setAttr(n, s);
// printf("made pseudo class rule %d with %s\n", n, UnicodeToLocal(s).c_str());
if ( n >= csspc_last_child ) {
// Pseudoclasses after csspc_last_child can't be accurately checked
// in the initial loading phase: a re-render will be needed.
doc->setNodeStylesInvalidIfLoading();
// There might still be some issues if CSS would set some display: property
// as, when re-rendering, a cache might be present and prevent modifying
// the DOM for some needed autoBoxing - or the invalid styles set now
// while loading would have created some autoBoxing that we won't be
// able to remove...
}
return rule;
} else if (*str != '[')
return NULL;
Expand Down
15 changes: 12 additions & 3 deletions crengine/src/lvtinydom.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ int gDOMVersionRequested = DOM_VERSION_CURRENT;
// increment to force complete reload/reparsing of old file
#define CACHE_FILE_FORMAT_VERSION "3.05.25k"
/// increment following value to force re-formatting of old book after load
#define FORMATTING_VERSION_ID 0x001A
#define FORMATTING_VERSION_ID 0x001B

#ifndef DOC_DATA_COMPRESSION_LEVEL
/// data compression level (0=no compression, 1=fast compressions, 3=normal compression)
Expand Down Expand Up @@ -1855,6 +1855,7 @@ tinyNodeCollection::tinyNodeCollection()
, _nodeStyleHash(0)
, _nodeDisplayStyleHash(NODE_DISPLAY_STYLE_HASH_UNITIALIZED)
, _nodeDisplayStyleHashInitial(NODE_DISPLAY_STYLE_HASH_UNITIALIZED)
, _nodeStylesInvalidIfLoading(false)
#endif
, _textStorage(this, 't', (int)(TEXT_CACHE_UNPACKED_SPACE*_storageMaxUncompressedSizeFactor), TEXT_CACHE_CHUNK_SIZE ) // persistent text node data storage
, _elemStorage(this, 'e', (int)(ELEM_CACHE_UNPACKED_SPACE*_storageMaxUncompressedSizeFactor), ELEM_CACHE_CHUNK_SIZE ) // persistent element data storage
Expand Down Expand Up @@ -1890,6 +1891,7 @@ tinyNodeCollection::tinyNodeCollection( tinyNodeCollection & v )
, _nodeStyleHash(0)
, _nodeDisplayStyleHash(NODE_DISPLAY_STYLE_HASH_UNITIALIZED)
, _nodeDisplayStyleHashInitial(NODE_DISPLAY_STYLE_HASH_UNITIALIZED)
, _nodeStylesInvalidIfLoading(false)
#endif
, _textStorage(this, 't', (int)(TEXT_CACHE_UNPACKED_SPACE*_storageMaxUncompressedSizeFactor), TEXT_CACHE_CHUNK_SIZE ) // persistent text node data storage
, _elemStorage(this, 'e', (int)(ELEM_CACHE_UNPACKED_SPACE*_storageMaxUncompressedSizeFactor), ELEM_CACHE_CHUNK_SIZE ) // persistent element data storage
Expand Down Expand Up @@ -4194,8 +4196,9 @@ int ldomDocument::render( LVRendPageList * pages, LVDocViewCallback * callback,
if ( !checkRenderContext() ) {
if ( _nodeDisplayStyleHashInitial == NODE_DISPLAY_STYLE_HASH_UNITIALIZED ) { // happen when just loaded
// For knowing/debugging cases when node styles set up during loading
// is invalid (should happen now only when EPUB has embedded fonts)
printf("CRE: document loaded, but styles re-init needed (possible epub with embedded fonts)\n");
// is invalid (should happen now only when EPUB has embedded fonts
// or some pseudoclass like :last-child has been met).
printf("CRE: styles re-init needed after load, re-rendering\n");
}
CRLog::info("rendering context is changed - full render required...");
// Clear LFormattedTextRef cache
Expand Down Expand Up @@ -5756,7 +5759,13 @@ ldomDocumentWriter::~ldomDocumentWriter()
// CRLog::error("*** document style validation failed!!!");
_document->updateRenderContext();
_document->dumpStatistics();
if ( _document->_nodeStylesInvalidIfLoading ) {
// Some pseudoclass like :last-child has been met which has set this flag
printf("CRE: document loaded, but styles re-init needed (cause: peculiar CSS pseudoclasses met)\n");
_document->forceReinitStyles();
}
}

#endif
}

Expand Down