diff --git a/bin/unit_tests/assets/html/ss64_deferred_cascade.css b/bin/unit_tests/assets/html/ss64_deferred_cascade.css
new file mode 100644
index 000000000..8fdbd76a7
--- /dev/null
+++ b/bin/unit_tests/assets/html/ss64_deferred_cascade.css
@@ -0,0 +1,11 @@
+h1 {
+ font-size: 1.5rem;
+}
+
+li a[href="../bash/"] {
+ background-color: #F8F9FA;
+}
+
+#external-css-loaded {
+ height: 37px;
+}
diff --git a/include/eepp/ui/css/stylesheet.hpp b/include/eepp/ui/css/stylesheet.hpp
index 2f17015cc..6ddd978bb 100644
--- a/include/eepp/ui/css/stylesheet.hpp
+++ b/include/eepp/ui/css/stylesheet.hpp
@@ -11,6 +11,8 @@ namespace EE { namespace UI { namespace CSS {
class EE_API StyleSheet {
public:
+ using SourceOrder = Uint32;
+
StyleSheet();
void clear();
@@ -23,6 +25,10 @@ class EE_API StyleSheet {
void combineStyleSheet( const StyleSheet& styleSheet );
+ void combineStyleSheet( const StyleSheet& styleSheet, SourceOrder sourceOrder );
+
+ SourceOrder reserveSourceOrder();
+
std::shared_ptr getElementStyles( UIWidget* element,
const bool& applyPseudo = false ) const;
@@ -78,6 +84,18 @@ class EE_API StyleSheet {
StyleSheet& operator=( const StyleSheet& other );
protected:
+ struct SourcePosition {
+ SourceOrder styleSheet{ 0 };
+ Uint32 rule{ 0 };
+
+ bool operator<( const SourcePosition& other ) const {
+ if ( styleSheet != other.styleSheet )
+ return styleSheet < other.styleSheet;
+ return rule < other.rule;
+ }
+ };
+ static_assert( sizeof( SourcePosition ) == sizeof( Uint64 ) );
+
Uint64 mVersion{ 1 };
Uint32 mMarker{ 0 };
std::vector> mNodes;
@@ -87,8 +105,8 @@ class EE_API StyleSheet {
UnorderedMap mClassNodeIndex;
// Candidate buckets are independent, so retain insertion order explicitly for equal
// specificity.
- UnorderedMap mStyleSourceOrder;
- size_t mNextStyleSourceOrder{ 0 };
+ UnorderedMap mStyleSourceOrder;
+ SourceOrder mNextStyleSourceOrder{ 0 };
MediaQueryList::vector mMediaQueryList;
KeyframesDefinitionMap mKeyframesMap;
using ElementDefinitionCache = UnorderedMap>;
@@ -99,6 +117,9 @@ class EE_API StyleSheet {
void addMediaQueryList( MediaQueryList::ptr list );
bool addStyleToNodeIndex( StyleSheetStyle* style );
+
+ void addStyle( std::shared_ptr node, SourceOrder sourceOrder,
+ Uint32 ruleOrder );
};
}}} // namespace EE::UI::CSS
diff --git a/include/eepp/ui/uiscenenode.hpp b/include/eepp/ui/uiscenenode.hpp
index 0d7454952..65229c3dd 100644
--- a/include/eepp/ui/uiscenenode.hpp
+++ b/include/eepp/ui/uiscenenode.hpp
@@ -407,9 +407,11 @@ class EE_API UISceneNode : public SceneNode {
* @param forceReloadStyle If true, forces immediate style reload (default: true).
* @param baseURI If the resource was loaded from an URI, pass the URI in order to solve
* relative paths in CSS
+ * @param sourceOrder Reserved document order for a stylesheet loaded asynchronously.
*/
void combineStyleSheet( const CSS::StyleSheet& styleSheet, bool forceReloadStyle = true,
- URI baseURI = {} );
+ URI baseURI = {},
+ std::optional sourceOrder = {} );
/**
* @brief Combines an inline stylesheet with the existing one.
@@ -421,9 +423,11 @@ class EE_API UISceneNode : public SceneNode {
* @param marker Marker to associate with the new styles.
* @param baseURI If the resource was loaded from an URI, pass the URI in order to solve
* relative paths in CSS
+ * @param sourceOrder Reserved document order for a stylesheet loaded asynchronously.
*/
void combineStyleSheet( const std::string& inlineStyleSheet, bool forceReloadStyle = true,
- const Uint32& marker = 0, URI baseURI = {} );
+ const Uint32& marker = 0, URI baseURI = {},
+ std::optional sourceOrder = {} );
/**
* @brief Gets the reference to the current stylesheet.
@@ -1161,8 +1165,9 @@ class EE_API UISceneNode : public SceneNode {
*
* @param uri URI to load
* @param defer Defer some specific time the CSS load (0 to just load it asynchronously)
+ * @param sourceOrder Document order reserved when the stylesheet link was encountered.
*/
- void loadCSS( URI uri, std::optional