فهرست منبع

Ensure selectors never match text elements

Michael Ragazzon 3 سال پیش
والد
کامیت
4f578db6a3
5فایلهای تغییر یافته به همراه30 افزوده شده و 22 حذف شده
  1. 4 0
      Source/Core/Element.cpp
  2. 4 0
      Source/Core/StyleSheet.cpp
  3. 12 6
      Source/Core/StyleSheetNode.cpp
  4. 2 0
      Source/Core/StyleSheetNode.h
  5. 8 16
      Tests/Source/UnitTests/Selectors.cpp

+ 4 - 0
Source/Core/Element.cpp

@@ -1500,6 +1500,8 @@ static Element* QuerySelectorMatchRecursive(const StyleSheetNodeListRaw& nodes,
 	for (int i = 0; i < num_children; i++)
 	{
 		Element* child = element->GetChild(i);
+		if (child->GetTagName() == "#text")
+			continue;
 
 		for (const StyleSheetNode* node : nodes)
 		{
@@ -1522,6 +1524,8 @@ static void QuerySelectorAllMatchRecursive(ElementList& matching_elements, const
 	for (int i = 0; i < num_children; i++)
 	{
 		Element* child = element->GetChild(i);
+		if (child->GetTagName() == "#text")
+			continue;
 
 		for (const StyleSheetNode* node : nodes)
 		{

+ 4 - 0
Source/Core/StyleSheet.cpp

@@ -195,6 +195,10 @@ SharedPtr<const ElementDefinition> StyleSheet::GetElementDefinition(const Elemen
 	const String& id = element->GetId();
 	const StringList& class_names = element->GetStyle()->GetClassNameList();
 
+	// Text elements are never matched.
+	if (tag == "#text")
+		return nullptr;
+
 	// First, look up the indexed requirements. 
 	if (!id.empty())
 		AddApplicableNodes(styled_node_index.ids, id);

+ 12 - 6
Source/Core/StyleSheetNode.cpp

@@ -36,6 +36,11 @@
 
 namespace Rml {
 
+static inline bool IsTextElement(const Element* element)
+{
+	return element->GetTagName() == "#text";
+}
+
 StyleSheetNode::StyleSheetNode()
 {
 	CalculateAndSetSpecificity();
@@ -224,7 +229,7 @@ const PropertyDictionary& StyleSheetNode::GetProperties() const
 	return properties;
 }
 
-inline bool StyleSheetNode::Match(const Element* element) const
+bool StyleSheetNode::Match(const Element* element) const
 {
 	if (!tag.empty() && tag != element->GetTagName())
 		return false;
@@ -269,12 +274,13 @@ inline bool StyleSheetNode::MatchStructuralSelector(const Element* element) cons
 	return true;
 }
 
-// Returns true if this node is applicable to the given element, given its IDs, classes and heritage.
 bool StyleSheetNode::IsApplicable(const Element* const in_element) const
 {
-	// Determine whether the element matches the current node and its entire lineage. The entire hierarchy of
-	// the element's document will be considered during the match as necessary.
+	// Determine whether the element matches the current node and its entire lineage. The entire hierarchy of the element's document will be
+	// considered during the match as necessary.
 
+	// We could in principle just call Match() here and then go on with the ancestor style nodes. Instead, we test the requirements of this node in a
+	// particular order for performance reasons .
 	for (const String& name : pseudo_class_names)
 	{
 		if (!in_element->IsPseudoClassSet(name))
@@ -320,10 +326,10 @@ bool StyleSheetNode::IsApplicable(const Element* const in_element) const
 			// Try a match on every element ancestor. If it succeeds, we continue on to the next node.
 			for (element = element->GetPreviousSibling(); element; element = element->GetPreviousSibling())
 			{
-				if (node->Match(element))
+				if (node->Match(element) && !IsTextElement(in_element))
 					break;
 				// If the node has a next-sibling combinator we must match this first sibling.
-				else if (node->combinator == SelectorCombinator::NextSibling)
+				else if (node->combinator == SelectorCombinator::NextSibling && !IsTextElement(in_element))
 					return false;
 			}
 		}

+ 2 - 0
Source/Core/StyleSheetNode.h

@@ -102,6 +102,8 @@ public:
 	const PropertyDictionary& GetProperties() const;
 
 	/// Returns true if this node is applicable to the given element, given its IDs, classes and heritage.
+	/// @note For performance reasons this call does not check whether 'element' is a text element. The caller must manually check this condition and
+	/// consider any text element not applicable.
 	bool IsApplicable(const Element* element) const;
 
 	/// Returns the specificity of this node.

+ 8 - 16
Tests/Source/UnitTests/Selectors.cpp

@@ -26,7 +26,7 @@
  *
  */
 
-#include "../Common/TestsInterface.h"
+#include "../Common/TestsShell.h"
 #include <RmlUi/Core/Context.h>
 #include <RmlUi/Core/Core.h>
 #include <RmlUi/Core/Element.h>
@@ -47,6 +47,7 @@ static const String doc_begin = R"(
 			width: 400px;
 			height: 300px;
 			margin: auto;
+			font-family: LatoLatin;
 		}
 )";
 static const String doc_end = R"(
@@ -58,9 +59,10 @@ static const String doc_end = R"(
 	<div  id="Z" class="hello world"/>
 	<div  id="P" class="parent">
 		<h1 id="A"/>
+		Some text
 		<p  id="B"/>
 		<p  id="C"/>
-		<p  id="D"> <span id="D0"/><span id="D1"/> </p>
+		<p  id="D"> <span id="D0">  </span><span id="D1">Text</span> </p>
 		<h3 id="E"/>
 		<p  id="F"> <span id="F0"/> </p>
 		<p  id="G"/>
@@ -121,9 +123,10 @@ static const Vector<QuerySelector> query_selectors =
 	{ ":last-of-type",               "Y P A D1 E F0 H I" },
 	{ ":only-child",                 "F0",              SelectorOp::RemoveElementsByIds,  "D0",    "D1 F0" },
 	{ ":only-of-type",               "Y A E F0 I" },
-	{ "span:empty",                  "Y D0 D1 F0" },
+	{ "span:empty",                  "Y D0 F0" },
 	{ ".hello.world, #P span, #I",   "Z D0 D1 F0 I",    SelectorOp::RemoveClasses,        "world", "D0 D1 F0 I" },
 	{ "body * span",                 "D0 D1 F0" },
+	{ "D1 *",                        "" },
 	{ "#E + #F",                     "F",               SelectorOp::InsertElementBefore,  "F",     "" },
 	{ "#E+#F",                       "F" },
 	{ "#E +#F",                      "F" },
@@ -226,18 +229,7 @@ static void InsertElementBefore(ElementDocument* document, const String& before_
 
 TEST_CASE("Selectors")
 {
-	const Vector2i window_size(1024, 768);
-
-	TestsSystemInterface system_interface;
-	TestsRenderInterface render_interface;
-
-	SetRenderInterface(&render_interface);
-	SetSystemInterface(&system_interface);
-
-	Initialise();
-
-	Context* context = Rml::CreateContext("main", window_size);
-	REQUIRE(context);
+	Context* context = TestsShell::GetContext();
 
 	SUBCASE("RCSS document selectors")
 	{
@@ -334,5 +326,5 @@ TEST_CASE("Selectors")
 		context->UnloadDocument(document);
 	}
 
-	Rml::Shutdown();
+	TestsShell::ShutdownShell();
 }