Browse Source

Optimize document re-layout by caching layout-important CSS properties

Add ElementStyleCache class which handles caching of CSS properties that are
important for document layout. The class provides methods for accessing these
properties in constant time after the initial O(log(N)) fetch with cold cache.

This change makes the number of Element::GetProperty (and hence std::map::find)
calls during the re-layout drop in my 100x4 datagrid test from tens of
thousands to merely a hundred, resulting a substantial speed boost.
Victor Luchitz 13 years ago
parent
commit
f4bc0e526a

+ 8 - 0
Build/vc2010/RocketCore.vcproj

@@ -283,10 +283,18 @@
 				RelativePath="..\..\Source\Core\ElementStyle.cpp"
 				>
 			</File>
+			<File
+				RelativePath="..\..\Source\Core\ElementStyleCache.cpp"
+				>
+			</File>
 			<File
 				RelativePath="..\..\Source\Core\ElementStyle.h"
 				>
 			</File>
+			<File
+				RelativePath="..\..\Source\Core\ElementStyleCache.h"
+				>
+			</File>
 			<File
 				RelativePath="..\..\Source\Core\ElementUtilities.cpp"
 				>

+ 2 - 0
Build/vc2010/RocketCore.vcxproj

@@ -90,6 +90,7 @@
     <ClCompile Include="..\..\Source\Core\ElementReference.cpp" />
     <ClCompile Include="..\..\Source\Core\ElementScroll.cpp" />
     <ClCompile Include="..\..\Source\Core\ElementStyle.cpp" />
+	<ClCompile Include="..\..\Source\Core\ElementStyleCache.cpp" />
     <ClCompile Include="..\..\Source\Core\ElementUtilities.cpp" />
     <ClCompile Include="..\..\Source\Core\LayoutBlockBox.cpp" />
     <ClCompile Include="..\..\Source\Core\LayoutBlockBoxSpace.cpp" />
@@ -225,6 +226,7 @@
     <ClInclude Include="..\..\Include\Rocket\Core\ElementReference.h" />
     <ClInclude Include="..\..\Include\Rocket\Core\ElementScroll.h" />
     <ClInclude Include="..\..\Source\Core\ElementStyle.h" />
+	<ClInclude Include="..\..\Source\Core\ElementStyleCache.h" />
     <ClInclude Include="..\..\Include\Rocket\Core\ElementUtilities.h" />
     <ClInclude Include="..\..\Source\Core\LayoutBlockBox.h" />
     <ClInclude Include="..\..\Source\Core\LayoutBlockBoxSpace.h" />

+ 2 - 0
Build/vc2012/RocketCore.vcxproj

@@ -92,6 +92,7 @@
     <ClCompile Include="..\..\Source\Core\ElementReference.cpp" />
     <ClCompile Include="..\..\Source\Core\ElementScroll.cpp" />
     <ClCompile Include="..\..\Source\Core\ElementStyle.cpp" />
+	<ClCompile Include="..\..\Source\Core\ElementStyleCache.cpp" />
     <ClCompile Include="..\..\Source\Core\ElementUtilities.cpp" />
     <ClCompile Include="..\..\Source\Core\LayoutBlockBox.cpp" />
     <ClCompile Include="..\..\Source\Core\LayoutBlockBoxSpace.cpp" />
@@ -227,6 +228,7 @@
     <ClInclude Include="..\..\Include\Rocket\Core\ElementReference.h" />
     <ClInclude Include="..\..\Include\Rocket\Core\ElementScroll.h" />
     <ClInclude Include="..\..\Source\Core\ElementStyle.h" />
+	<ClInclude Include="..\..\Source\Core\ElementStyleCache.h" />
     <ClInclude Include="..\..\Include\Rocket\Core\ElementUtilities.h" />
     <ClInclude Include="..\..\Source\Core\LayoutBlockBox.h" />
     <ClInclude Include="..\..\Source\Core\LayoutBlockBoxSpace.h" />

+ 40 - 0
Include/Rocket/Core/Element.h

@@ -220,6 +220,43 @@ public:
 	/// @param[in] base_value The value that is scaled by the percentage value, if it is a percentage.
 	/// @return The value of this property for this element.
 	float ResolveProperty(const String& name, float base_value);
+	/// Resolves one of this element's non-inherited properties. If the value is a number or px, this is returned. If it's a 
+	/// percentage then it is resolved based on the second argument (the base value).
+	/// @param[in] name The property to resolve the value for.
+	/// @param[in] base_value The value that is scaled by the percentage value, if it is a percentage.
+	/// @return The value of this property for this element.
+	float ResolveProperty(const Property *property, float base_value);
+
+	/// Returns 'border-width' properties from element's style or local cache.
+	void GetBorderWidthProperties(const Property **border_top_width, const Property **border_bottom_width, const Property **border_left_width, const Property **border_right_width);
+	/// Returns 'margin' properties from element's style or local cache.
+	void GetMarginProperties(const Property **margin_top, const Property **margin_bottom, const Property **margin_left, const Property **margin_right);
+	/// Returns 'padding' properties from element's style or local cache.
+	void GetPaddingProperties(const Property **padding_top, const Property **padding_bottom, const Property **padding_left, const Property **padding_right);
+	/// Returns 'width' and 'height' properties from element's style or local cache.
+	void GetDimensionProperties(const Property **width, const Property **height);
+	/// Returns local 'width' and 'height' properties from element's style or local cache,
+	/// ignoring default values.
+	void GetLocalDimensionProperties(const Property **width, const Property **height);
+	/// Returns 'overflow' properties' values from element's style or local cache.
+	void GetOverflow(int *overflow_x, int *overflow_y);
+	/// Returns 'position' property value from element's style or local cache.
+	int GetPosition();
+	/// Returns 'float' property value from element's style or local cache.
+	int GetFloat();
+	/// Returns 'display' property value from element's style or local cache.
+	int GetDisplay();
+	/// Returns 'white-space' property value from element's style or local cache.
+	int GetWhitespace();
+
+	/// Returns 'line-height' property value from element's style or local cache.
+	const Property *GetLineHeightProperty();
+	/// Returns 'text-align' property value from element's style or local cache.
+	int GetTextAlign();
+	/// Returns 'text-transform' property value from element's style or local cache.
+	int GetTextTransform();
+	/// Returns 'vertical-align' property value from element's style or local cache.
+	const Property *GetVerticalAlignProperty();
 
 	/// Iterates over the properties defined on this element.
 	/// @param[inout] index Index of the property to fetch. This is incremented to the next valid index after the fetch. Indices are not necessarily incremental.
@@ -574,6 +611,9 @@ protected:
 	/// Returns true if the element has been marked as needing a re-layout.
 	virtual bool IsLayoutDirty();
 
+	/// Increment/Decrement the layout lock
+	virtual void LockLayout(bool lock);
+
 	/// Forces a reevaluation of applicable font effects.
 	virtual void DirtyFont();
 

+ 107 - 7
Source/Core/Element.cpp

@@ -293,7 +293,7 @@ String Element::GetAddress(bool include_pseudo_classes) const
 // Sets the position of this element, as a two-dimensional offset from another element.
 void Element::SetOffset(const Vector2f& offset, Element* _offset_parent, bool _offset_fixed)
 {
-	_offset_fixed |= GetProperty< int >(POSITION) == POSITION_FIXED;
+	_offset_fixed |= GetPosition() == POSITION_FIXED;
 
 	// If our offset has definitely changed, or any of our parenting has, then these are set and
 	// updated based on our left / right / top / bottom properties.
@@ -529,6 +529,82 @@ float Element::ResolveProperty(const String& name, float base_value)
 	return style->ResolveProperty(name, base_value);
 }
 
+// Resolves one of this element's style.
+float Element::ResolveProperty(const Property *property, float base_value)
+{
+	return style->ResolveProperty(property, base_value);
+}
+
+void Element::GetBorderWidthProperties(const Property **border_top, const Property **border_bottom, const Property **border_left, const Property **bottom_right)
+{
+	style->GetBorderWidthProperties(border_top, border_bottom, border_left, bottom_right);
+}
+
+void Element::GetMarginProperties(const Property **margin_top, const Property **margin_bottom, const Property **margin_left, const Property **margin_right)
+{
+	style->GetMarginProperties(margin_top, margin_bottom, margin_left, margin_right);
+}
+
+void Element::GetPaddingProperties(const Property **padding_top, const Property **padding_bottom, const Property **padding_left, const Property **padding_right)
+{
+	style->GetPaddingProperties(padding_top, padding_bottom, padding_left, padding_right);
+}
+
+void Element::GetDimensionProperties(const Property **width, const Property **height)
+{
+	style->GetDimensionProperties(width, height);
+}
+
+void Element::GetLocalDimensionProperties(const Property **width, const Property **height)
+{
+	style->GetLocalDimensionProperties(width, height);
+}
+
+void Element::GetOverflow(int *overflow_x, int *overflow_y)
+{
+	style->GetOverflow(overflow_x, overflow_y);
+}
+
+int Element::GetPosition()
+{
+	return style->GetPosition();
+}
+
+int Element::GetFloat()
+{
+	return style->GetFloat();
+}
+
+int Element::GetDisplay()
+{
+	return style->GetDisplay();
+}
+
+int Element::GetWhitespace()
+{
+	return style->GetWhitespace();
+}
+
+const Property *Element::GetLineHeightProperty()
+{
+	return style->GetLineHeightProperty();
+}
+
+int Element::GetTextAlign()
+{
+	return style->GetTextAlign();
+}
+
+int Element::GetTextTransform()
+{
+	return style->GetTextTransform();
+}
+
+const Property *Element::GetVerticalAlignProperty()
+{
+	return style->GetVerticalAlignProperty();
+}
+
 // Iterates over the properties defined on this element.
 bool Element::IterateProperties(int& index, PseudoClassList& pseudo_classes, String& name, const Property*& property) const
 {
@@ -1019,6 +1095,8 @@ void Element::ScrollIntoView(bool align_with_top)
 // Appends a child to this element
 void Element::AppendChild(Element* child, bool dom_element)
 {
+	LockLayout(true);
+
 	child->AddReference();
 	child->SetParent(this);
 	if (dom_element)
@@ -1038,6 +1116,8 @@ void Element::AppendChild(Element* child, bool dom_element)
 
 	if (dom_element)
 		DirtyLayout();
+
+	LockLayout(false);
 }
 
 // Adds a child to this element, directly after the adjacent element. Inherits
@@ -1063,6 +1143,8 @@ void Element::InsertBefore(Element* child, Element* adjacent_element)
 
 	if (found_child)
 	{
+		LockLayout(true);
+
 		child->AddReference();
 		child->SetParent(this);
 
@@ -1079,6 +1161,8 @@ void Element::InsertBefore(Element* child, Element* adjacent_element)
 		child->OnChildAdd(child);
 		DirtyStackingContext();
 		DirtyStructure();
+
+		LockLayout(false);
 	}
 	else
 	{
@@ -1104,6 +1188,8 @@ bool Element::ReplaceChild(Element* inserted_element, Element* replaced_element)
 		return false;
 	}
 
+	LockLayout(true);
+
 	children.insert(insertion_point, inserted_element);
 	RemoveChild(replaced_element);
 
@@ -1111,6 +1197,8 @@ bool Element::ReplaceChild(Element* inserted_element, Element* replaced_element)
 	inserted_element->GetStyle()->DirtyProperties();
 	inserted_element->OnChildAdd(inserted_element);
 
+	LockLayout(false);
+
 	return true;
 }
 
@@ -1124,6 +1212,8 @@ bool Element::RemoveChild(Element* child)
 		// Add the element to the delete list
 		if ((*itr) == child)
 		{
+			LockLayout(true);
+
 			// Inform the context of the element's pending removal (if we have a valid context).
 			Context* context = GetContext();
 			if (context)
@@ -1165,6 +1255,8 @@ bool Element::RemoveChild(Element* child)
 			DirtyStackingContext();
 			DirtyStructure();
 
+			LockLayout(false);
+
 			return true;
 		}
 
@@ -1378,8 +1470,8 @@ void Element::OnPropertyChange(const PropertyNameList& changed_properties)
 	if (all_dirty || changed_properties.find(VISIBILITY) != changed_properties.end() ||
 		changed_properties.find(DISPLAY) != changed_properties.end())
 	{
-		bool new_visibility = GetProperty< int >(VISIBILITY) == VISIBILITY_VISIBLE &&
-							  GetProperty< int >(DISPLAY) != DISPLAY_NONE;
+		bool new_visibility = GetDisplay() != DISPLAY_NONE &&
+							  GetProperty< int >(VISIBILITY) == VISIBILITY_VISIBLE;
 
 		if (visible != new_visibility)
 		{
@@ -1560,6 +1652,14 @@ void Element::DirtyLayout()
 		document->DirtyLayout();
 }
 
+/// Increment/Decrement the layout lock
+void Element::LockLayout(bool lock)
+{
+	Element* document = GetOwnerDocument();
+	if (document != NULL)
+		document->LockLayout(lock);
+}
+
 // Forces a re-layout of this element, and any other children required.
 bool Element::IsLayoutDirty()
 {
@@ -1717,7 +1817,7 @@ void Element::DirtyOffset()
 
 void Element::UpdateOffset()
 {
-	int position_property = GetProperty< int >(POSITION);
+	int position_property = GetPosition();
 	if (position_property == POSITION_ABSOLUTE ||
 		position_property == POSITION_FIXED)
 	{
@@ -1810,11 +1910,11 @@ void Element::BuildStackingContext(ElementList* new_stacking_context)
 		std::pair< Element*, float > ordered_child;
 		ordered_child.first = child;
 
-		if (child->GetProperty< int >(POSITION) != POSITION_STATIC)
+		if (child->GetPosition() != POSITION_STATIC)
 			ordered_child.second = 3;
-		else if (child->GetProperty< int >(FLOAT) != FLOAT_NONE)
+		else if (child->GetFloat() != FLOAT_NONE)
 			ordered_child.second = 1;
-		else if (child->GetProperty< int >(DISPLAY) == DISPLAY_BLOCK)
+		else if (child->GetDisplay() == DISPLAY_BLOCK)
 			ordered_child.second = 0;
 		else
 			ordered_child.second = 2;

+ 7 - 4
Source/Core/ElementHandle.cpp

@@ -113,14 +113,17 @@ void ElementHandle::ProcessEvent(Event& event)
 
 			if (size_target)
 			{
+				const Property *margin_top, *margin_bottom, *margin_left, *margin_right;
+				size_target->GetMarginProperties(&margin_top, &margin_bottom, &margin_left, &margin_right);
+
 				// Check if we have auto-margins; if so, they have to be set to the current margins.
-				if (size_target->GetProperty(MARGIN_TOP)->unit == Property::KEYWORD)
+				if (margin_top->unit == Property::KEYWORD)
 					size_target->SetProperty(MARGIN_TOP, Property((float) Math::RealToInteger(size_target->GetBox().GetEdge(Box::MARGIN, Box::TOP)), Property::PX));
-				if (size_target->GetProperty(MARGIN_RIGHT)->unit == Property::KEYWORD)
+				if (margin_right->unit == Property::KEYWORD)
 					size_target->SetProperty(MARGIN_RIGHT, Property((float) Math::RealToInteger(size_target->GetBox().GetEdge(Box::MARGIN, Box::RIGHT)), Property::PX));
-				if (size_target->GetProperty(MARGIN_BOTTOM)->unit == Property::KEYWORD)
+				if (margin_bottom->unit == Property::KEYWORD)
 					size_target->SetProperty(MARGIN_BOTTOM, Property((float) Math::RealToInteger(size_target->GetBox().GetEdge(Box::MARGIN, Box::BOTTOM)), Property::PX));
-				if (size_target->GetProperty(MARGIN_LEFT)->unit == Property::KEYWORD)
+				if (margin_left->unit == Property::KEYWORD)
 					size_target->SetProperty(MARGIN_LEFT, Property((float) Math::RealToInteger(size_target->GetBox().GetEdge(Box::MARGIN, Box::LEFT)), Property::PX));
 
 				int new_x = Math::RealToInteger(size_original_size.x + x);

+ 163 - 32
Source/Core/ElementStyle.cpp

@@ -27,6 +27,7 @@
 
 #include "precompiled.h"
 #include "ElementStyle.h"
+#include "ElementStyleCache.h"
 #include <algorithm>
 #include <Rocket/Core/ElementDocument.h>
 #include <Rocket/Core/ElementUtilities.h>
@@ -47,8 +48,10 @@ namespace Core {
 ElementStyle::ElementStyle(Element* _element)
 {
 	local_properties = NULL;
+	em_properties = NULL;
 	definition = NULL;
 	element = _element;
+	cache = new ElementStyleCache(this);
 
 	definition_dirty = true;
 	child_definition_dirty = true;
@@ -58,9 +61,20 @@ ElementStyle::~ElementStyle()
 {
 	if (local_properties != NULL)
 		delete local_properties;
+	if (em_properties != NULL)
+		delete em_properties;
 
 	if (definition != NULL)
 		definition->RemoveReference();
+
+	delete cache;
+}
+
+static PropCounter prop_counter;
+
+PropCounter &ElementStyle::GetPropCounter()
+{
+	return prop_counter;
 }
 
 // Returns the element's definition, updating if necessary.
@@ -278,6 +292,10 @@ void ElementStyle::RemoveProperty(const String& name)
 // Returns one of this element's properties.
 const Property* ElementStyle::GetProperty(const String& name)
 {
+	if (prop_counter.find(name) == prop_counter.end())
+		prop_counter[name] = 0;
+	prop_counter[name] = prop_counter[name] + 1;
+
 	const Property* local_property = GetLocalProperty(name);
 	if (local_property != NULL)
 		return local_property;
@@ -323,6 +341,32 @@ const Property* ElementStyle::GetLocalProperty(const String& name)
 	return NULL;
 }
 
+// Resolves one of this element's properties.
+float ElementStyle::ResolveProperty(const Property* property, float base_value)
+{
+	if (!property)
+	{
+		ROCKET_ERROR;
+		return 0.0f;
+	}
+
+	if (property->unit & Property::RELATIVE_UNIT)
+	{
+		if (property->unit & Property::PERCENT)
+			return base_value * property->value.Get< float >() * 0.01f;
+		else if (property->unit & Property::EM)
+			return property->value.Get< float >() * ElementUtilities::GetFontSize(element);
+	}
+
+	if (property->unit & Property::NUMBER || property->unit & Property::PX)
+	{
+		return property->value.Get< float >();
+	}
+
+	// We're not a numeric property; return 0.
+	return 0.0f;
+}
+
 // Resolves one of this element's properties.
 float ElementStyle::ResolveProperty(const String& name, float base_value)
 {
@@ -368,23 +412,6 @@ float ElementStyle::ResolveProperty(const String& name, float base_value)
 		return property->value.Get< float >();
 	}
 
-	// Values based on pixels-per-inch.
-	if (property->unit & Property::PPI_UNIT)
-	{
-		float inch = property->value.Get< float >() * element->GetRenderInterface()->GetPixelsPerInch();
-
-		if (property->unit & Property::IN) // inch
-			return inch;
-		if (property->unit & Property::CM) // centimeter
-			return inch / 2.54f;
-		if (property->unit & Property::MM) // millimeter
-			return inch / 25.4f;
-		if (property->unit & Property::PT) // point
-			return inch / 72.0f;
-		if (property->unit & Property::PC) // pica
-			return inch / 6.0f;
-	}
-
 	// We're not a numeric property; return 0.
 	return 0.0f;
 }
@@ -478,22 +505,25 @@ void ElementStyle::DirtyEmProperties()
 {
 	const PropertyNameList &properties = StyleSheetSpecification::GetRegisteredProperties();
 
-	// Check if any of these are currently em-relative. If so, dirty them.
-	PropertyNameList em_properties;
-	for (PropertyNameList::const_iterator list_iterator = properties.begin(); list_iterator != properties.end(); ++list_iterator)
+	if (!em_properties)
 	{
-		// Skip font-size; this is relative to our parent's em, not ours.
-		if (*list_iterator == FONT_SIZE)
-			continue;
-
-		// Get this property from this element. If this is em-relative, then add it to the list to
-		// dirty.
-		if (element->GetProperty(*list_iterator)->unit == Property::EM)
-			em_properties.insert(*list_iterator);
+		// Check if any of these are currently em-relative. If so, dirty them.
+		em_properties = new PropertyNameList;
+		for (PropertyNameList::const_iterator list_iterator = properties.begin(); list_iterator != properties.end(); ++list_iterator)
+		{
+			// Skip font-size; this is relative to our parent's em, not ours.
+			if (*list_iterator == FONT_SIZE)
+				continue;
+
+			// Get this property from this element. If this is em-relative, then add it to the list to
+			// dirty.
+			if (element->GetProperty(*list_iterator)->unit == Property::EM)
+				em_properties->insert(*list_iterator);
+		}
 	}
 
-	if (!em_properties.empty())
-		DirtyProperties(em_properties);
+	if (!em_properties->empty())
+		DirtyProperties(*em_properties, false);
 
 	// Now dirty all of our descendant's font-size properties that are relative to ems.
 	int num_children = element->GetNumChildren(true);
@@ -528,7 +558,7 @@ void ElementStyle::DirtyProperty(const String& property)
 }
 
 // Sets a list of properties as dirty.
-void ElementStyle::DirtyProperties(const PropertyNameList& properties)
+void ElementStyle::DirtyProperties(const PropertyNameList& properties, bool clear_em_properties)
 {
 	if (properties.empty())
 		return;
@@ -542,6 +572,9 @@ void ElementStyle::DirtyProperties(const PropertyNameList& properties)
 		const PropertyNameList &all_inherited_properties = StyleSheetSpecification::GetRegisteredInheritedProperties();
 		for (int i = 0; i < element->GetNumChildren(true); i++)
 			element->GetChild(i)->GetStyle()->DirtyInheritedProperties(all_inherited_properties);
+
+		// Clear all cached properties.
+		cache->Clear();
 	}
 	else
 	{
@@ -562,6 +595,16 @@ void ElementStyle::DirtyProperties(const PropertyNameList& properties)
 			for (int i = 0; i < element->GetNumChildren(true); i++)
 				element->GetChild(i)->GetStyle()->DirtyInheritedProperties(inherited_properties);
 		}
+
+		// Clear cached properties.
+		cache->Clear();
+	}
+
+	// clear the list of EM-properties, we will refill it in DirtyEmProperties
+	if (clear_em_properties && em_properties != NULL)
+	{
+		delete em_properties;
+		em_properties = NULL;
 	}
 
 	// And send the event.
@@ -571,16 +614,34 @@ void ElementStyle::DirtyProperties(const PropertyNameList& properties)
 // Sets a list of our potentially inherited properties as dirtied by an ancestor.
 void ElementStyle::DirtyInheritedProperties(const PropertyNameList& properties)
 {
+	bool clear_em_properties = em_properties != NULL;
+
 	PropertyNameList inherited_properties;
 	for (PropertyNameList::const_iterator i = properties.begin(); i != properties.end(); ++i)
 	{
-		if (GetLocalProperty((*i)) == NULL)
+		const Property *property = GetLocalProperty((*i));
+		if (property == NULL)
+		{
 			inherited_properties.insert(*i);
+			if (!clear_em_properties && em_properties != NULL && em_properties->find((*i)) != em_properties->end()) {
+				clear_em_properties = true;
+			}
+		}
 	}
 
 	if (inherited_properties.empty())
 		return;
 
+	// clear the list of EM-properties, we will refill it in DirtyEmProperties
+	if (clear_em_properties && em_properties != NULL)
+	{
+		delete em_properties;
+		em_properties = NULL;
+	}
+
+	// Clear cached inherited properties.
+	cache->ClearInherited();
+
 	// Pass the list of those properties that this element doesn't override onto our children.
 	for (int i = 0; i < element->GetNumChildren(true); i++)
 		element->GetChild(i)->GetStyle()->DirtyInheritedProperties(inherited_properties);
@@ -588,5 +649,75 @@ void ElementStyle::DirtyInheritedProperties(const PropertyNameList& properties)
 	element->OnPropertyChange(properties);
 }
 
+void ElementStyle::GetBorderWidthProperties(const Property **border_top_width, const Property **border_bottom_width, const Property **border_left_width, const Property **bottom_right_width)
+{
+	cache->GetBorderWidthProperties(border_top_width, border_bottom_width, border_left_width, bottom_right_width);
+}
+
+void ElementStyle::GetMarginProperties(const Property **margin_top, const Property **margin_bottom, const Property **margin_left, const Property **margin_right)
+{
+	cache->GetMarginProperties(margin_top, margin_bottom, margin_left, margin_right);
+}
+
+void ElementStyle::GetPaddingProperties(const Property **padding_top, const Property **padding_bottom, const Property **padding_left, const Property **padding_right)
+{
+	cache->GetPaddingProperties(padding_top, padding_bottom, padding_left, padding_right);
+}
+
+void ElementStyle::GetDimensionProperties(const Property **width, const Property **height)
+{
+	cache->GetDimensionProperties(width, height);
+}
+
+void ElementStyle::GetLocalDimensionProperties(const Property **width, const Property **height)
+{
+	cache->GetLocalDimensionProperties(width, height);
+}
+
+void ElementStyle::GetOverflow(int *overflow_x, int *overflow_y)
+{
+	cache->GetOverflow(overflow_x, overflow_y);
+}
+
+int ElementStyle::GetPosition()
+{
+	return cache->GetPosition();
+}
+
+int ElementStyle::GetFloat()
+{
+	return cache->GetFloat();
+}
+
+int ElementStyle::GetDisplay()
+{
+	return cache->GetDisplay();
+}
+
+int ElementStyle::GetWhitespace()
+{
+	return cache->GetWhitespace();
+}
+
+const Property *ElementStyle::GetLineHeightProperty()
+{
+	return cache->GetLineHeightProperty();
+}
+
+int ElementStyle::GetTextAlign()
+{
+	return cache->GetTextAlign();
+}
+
+int ElementStyle::GetTextTransform()
+{
+	return cache->GetTextTransform();
+}
+
+const Property *ElementStyle::GetVerticalAlignProperty()
+{
+	return cache->GetVerticalAlignProperty();
+}
+
 }
 }

+ 49 - 2
Source/Core/ElementStyle.h

@@ -34,6 +34,10 @@
 namespace Rocket {
 namespace Core {
 
+class ElementStyleCache;
+
+typedef std::map<String, int> PropCounter;
+
 /**
 	Manages an element's style and property information.
 	@author Lloyd Weehuizen
@@ -103,6 +107,12 @@ public:
 	const Property* GetLocalProperty(const String& name);
 	/// Resolves one of this element's properties. If the value is a number or px, this is returned. If it's a 
 	/// percentage then it is resolved based on the second argument (the base value).
+	/// @param[in] property Property to resolve the value for.
+	/// @param[in] base_value The value that is scaled by the percentage value, if it is a percentage.
+	/// @return The value of this property for this element.
+	float ResolveProperty(const Property *property, float base_value);
+	/// Resolves one of this element's properties. If the value is a number or px, this is returned. If it's a 
+	/// percentage then it is resolved based on the second argument (the base value).
 	/// @param[in] name The name of the property to resolve the value for.
 	/// @param[in] base_value The value that is scaled by the percentage value, if it is a percentage.
 	/// @return The value of this property for this element.
@@ -131,11 +141,44 @@ public:
 	// Dirties font-size on child elements if appropriate.
 	void DirtyInheritedEmProperties();
 
+	/// Returns 'border-width' properties from element's style or local cache.
+	void GetBorderWidthProperties(const Property **border_top_width, const Property **border_bottom_width, const Property **border_left_width, const Property **border_right_width);
+	/// Returns 'margin' properties from element's style or local cache.
+	void GetMarginProperties(const Property **margin_top, const Property **margin_bottom, const Property **margin_left, const Property **margin_right);
+	/// Returns 'padding' properties from element's style or local cache.
+	void GetPaddingProperties(const Property **padding_top, const Property **padding_bottom, const Property **padding_left, const Property **padding_right);
+	/// Returns 'width' and 'height' properties from element's style or local cache.
+	void GetDimensionProperties(const Property **width, const Property **height);
+	/// Returns local 'width' and 'height' properties from element's style or local cache,
+	/// ignoring default values.
+	void GetLocalDimensionProperties(const Property **width, const Property **height);
+	/// Returns 'overflow' properties' values from element's style or local cache.
+	void GetOverflow(int *overflow_x, int *overflow_y);
+	/// Returns 'position' property value from element's style or local cache.
+	int GetPosition();
+	/// Returns 'float' property value from element's style or local cache.
+	int GetFloat();
+	/// Returns 'display' property value from element's style or local cache.
+	int GetDisplay();
+	/// Returns 'white-space' property value from element's style or local cache.
+	int GetWhitespace();
+
+	/// Returns 'line-height' property value from element's style or local cache.
+	const Property *GetLineHeightProperty();
+	/// Returns 'text-align' property value from element's style or local cache.
+	int GetTextAlign();
+	/// Returns 'text-transform' property value from element's style or local cache.
+	int GetTextTransform();
+	/// Returns 'vertical-align' property value from element's style or local cache.
+	const Property *GetVerticalAlignProperty();
+
+	static PropCounter &GetPropCounter();
+
 private:
 	// Sets a single property as dirty.
 	void DirtyProperty(const String& property);
 	// Sets a list of properties as dirty.
-	void DirtyProperties(const PropertyNameList& properties);
+	void DirtyProperties(const PropertyNameList& properties, bool clear_em_properties = true);
 	// Sets a list of our potentially inherited properties as dirtied by an ancestor.
 	void DirtyInheritedProperties(const PropertyNameList& properties);
 
@@ -147,14 +190,18 @@ private:
 	// This element's current pseudo-classes.
 	PseudoClassList pseudo_classes;
 
-	// Any properties that have been overridden in this element;
+	// Any properties that have been overridden in this element.
 	PropertyDictionary* local_properties;
+	// All properties (including inherited) that are EM-relative.
+	PropertyNameList* em_properties;
 	// The definition of this element; if this is NULL one will be fetched from the element's style.
 	ElementDefinition* definition;
 	// Set if a new element definition should be fetched from the style.
 	bool definition_dirty;
 	// Set if a child element has a dirty style definition
 	bool child_definition_dirty;
+	// cached non-inherited properties
+	ElementStyleCache *cache;
 };
 
 }

+ 341 - 0
Source/Core/ElementStyleCache.cpp

@@ -0,0 +1,341 @@
+/*
+ * This source file is part of libRocket, the HTML/CSS Interface Middleware
+ *
+ * For the latest information, see http://www.librocket.com
+ *
+ * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#include "precompiled.h"
+#include "ElementStyle.h"
+#include "ElementStyleCache.h"
+
+namespace Rocket {
+namespace Core {
+
+ElementStyleCache::ElementStyleCache(ElementStyle *style) : style(style), 
+	border_top_width(NULL), border_bottom_width(NULL), border_left_width(NULL), border_right_width(NULL),
+	margin_top(NULL), margin_bottom(NULL), margin_left(NULL), margin_right(NULL),
+	padding_top(NULL), padding_bottom(NULL), padding_left(NULL), padding_right(NULL),
+	width(NULL), height(NULL),
+	local_width(NULL), local_height(NULL), have_local_width(false), have_local_height(false),
+	overflow_x(NULL), overflow_y(NULL),
+	position(-1), float_(-1), display(-1), whitespace(-1),
+	line_height(NULL), text_align(-1), text_transform(-1), vertical_align(NULL)
+{
+}
+
+void ElementStyleCache::Clear()
+{
+	ClearBorder();
+	ClearMargin();
+	ClearPadding();
+	ClearDimensions();
+	ClearOverflow();
+	ClearPosition();
+	ClearFloat();
+	ClearDisplay();
+	ClearWhitespace();
+}
+
+void ElementStyleCache::ClearInherited()
+{
+	ClearLineHeight();
+	ClearTextAlign();
+	ClearTextTransform();
+	ClearVerticalAlign();
+}
+
+void ElementStyleCache::ClearBorder()
+{
+	border_top_width = border_bottom_width = border_left_width = border_right_width = NULL;
+}
+
+void ElementStyleCache::ClearMargin()
+{
+	margin_top = margin_bottom = margin_left = margin_right = NULL;
+}
+
+void ElementStyleCache::ClearPadding()
+{
+	padding_top = padding_bottom = padding_left = padding_right = NULL;
+}
+
+void ElementStyleCache::ClearDimensions()
+{
+	width = height = NULL;
+	have_local_width = have_local_height = false;
+}
+
+void ElementStyleCache::ClearOverflow()
+{
+	overflow_x = overflow_y = -1;
+}
+
+void ElementStyleCache::ClearPosition()
+{
+	position = -1;
+}
+
+void ElementStyleCache::ClearFloat()
+{
+	float_ = -1;
+}
+
+void ElementStyleCache::ClearDisplay()
+{
+	display = -1;
+}
+
+void ElementStyleCache::ClearWhitespace()
+{
+	whitespace = -1;
+}
+
+void ElementStyleCache::ClearLineHeight()
+{
+	line_height = NULL;
+}
+
+void ElementStyleCache::ClearTextAlign()
+{
+	text_align = -1;
+}
+
+void ElementStyleCache::ClearTextTransform()
+{
+	text_transform = -1;
+}
+
+void ElementStyleCache::ClearVerticalAlign()
+{
+	vertical_align = NULL;
+}
+
+void ElementStyleCache::GetBorderWidthProperties(const Property **o_border_top_width, const Property **o_border_bottom_width, const Property **o_border_left_width, const Property **o_border_right_width)
+{
+	if (o_border_top_width)
+	{
+		if (!border_top_width)
+			border_top_width = style->GetProperty(BORDER_TOP_WIDTH);
+		*o_border_top_width = border_top_width;
+	}
+
+	if (o_border_bottom_width)
+	{
+		if (!border_bottom_width)
+			border_bottom_width = style->GetProperty(BORDER_BOTTOM_WIDTH);
+		*o_border_bottom_width = border_top_width;
+	}
+
+	if (o_border_left_width)
+	{
+		if (!border_left_width)
+			border_left_width = style->GetProperty(BORDER_LEFT_WIDTH);
+		*o_border_left_width = border_left_width;
+	}
+
+	if (o_border_right_width)
+	{
+		if (!border_right_width)
+			border_right_width = style->GetProperty(BORDER_RIGHT_WIDTH);
+		*o_border_right_width = border_right_width;
+	}
+}
+
+void ElementStyleCache::GetMarginProperties(const Property **o_margin_top, const Property **o_margin_bottom, const Property **o_margin_left, const Property **o_margin_right)
+{
+	if (o_margin_top)
+	{
+		if (!margin_top)
+			margin_top = style->GetProperty(MARGIN_TOP);
+		*o_margin_top = margin_top;
+	}
+
+	if (o_margin_bottom)
+	{
+		if (!margin_bottom)
+			margin_bottom = style->GetProperty(MARGIN_BOTTOM);
+		*o_margin_bottom = margin_bottom;
+	}
+
+	if (o_margin_left)
+	{
+		if (!margin_left)
+			margin_left = style->GetProperty(MARGIN_LEFT);
+		*o_margin_left = margin_left;
+	}
+
+	if (o_margin_right)
+	{
+		if (!margin_right)
+			margin_right = style->GetProperty(MARGIN_RIGHT);
+		*o_margin_right = margin_right;
+	}
+}
+
+void ElementStyleCache::GetPaddingProperties(const Property **o_padding_top, const Property **o_padding_bottom, const Property **o_padding_left, const Property **o_padding_right)
+{
+	if (o_padding_top)
+	{
+		if (!padding_top)
+			padding_top = style->GetProperty(PADDING_TOP);
+		*o_padding_top = padding_top;
+	}
+
+	if (o_padding_bottom)
+	{
+		if (!padding_bottom)
+			padding_bottom = style->GetProperty(PADDING_BOTTOM);
+		*o_padding_bottom = padding_bottom;
+	}
+
+	if (o_padding_left)
+	{
+		if (!padding_left)
+			padding_left = style->GetProperty(PADDING_LEFT);
+		*o_padding_left = padding_left;
+	}
+
+	if (o_padding_right)
+	{
+		if (!padding_right)
+			padding_right = style->GetProperty(PADDING_RIGHT);
+		*o_padding_right = padding_right;
+	}
+}
+
+void ElementStyleCache::GetDimensionProperties(const Property **o_width, const Property **o_height)
+{
+	if (o_width)
+	{
+		if (!width)
+			width = style->GetProperty(WIDTH);
+		*o_width = width;
+	}
+
+	if (o_height)
+	{
+		if (!height)
+			height = style->GetProperty(HEIGHT);
+		*o_height = height;
+	}
+}
+
+void ElementStyleCache::GetLocalDimensionProperties(const Property **o_width, const Property **o_height)
+{
+	if (o_width)
+	{
+		if (!have_local_width)
+		{
+			have_local_width = true;
+			local_width = style->GetLocalProperty(WIDTH);
+		}
+		*o_width = local_width;
+	}
+
+	if (o_height)
+	{
+		if (!have_local_height)
+		{
+			have_local_height = true;
+			local_height = style->GetLocalProperty(HEIGHT);
+		}
+		*o_height = local_height;
+	}
+}
+
+void ElementStyleCache::GetOverflow(int *o_overflow_x, int *o_overflow_y)
+{
+	if (o_overflow_x)
+	{
+		if (overflow_x < 0)
+			overflow_x = style->GetProperty(OVERFLOW_X)->Get< int >();
+		*o_overflow_x = overflow_x;
+	}
+
+	if (o_overflow_y)
+	{
+		if (overflow_y < 0)
+			overflow_y = style->GetProperty(OVERFLOW_Y)->Get< int >();
+		*o_overflow_y = overflow_y;
+	}
+}
+
+int ElementStyleCache::GetPosition()
+{
+	if (position < 0)
+		position = style->GetProperty(POSITION)->Get< int >();
+	return position;
+}
+
+int ElementStyleCache::GetFloat()
+{
+	if (float_ < 0)
+		float_ = style->GetProperty(FLOAT)->Get< int >();
+	return float_;
+}
+
+int ElementStyleCache::GetDisplay()
+{
+	if (display < 0)
+		display = style->GetProperty(DISPLAY)->Get< int >();
+	return display;
+}
+
+int ElementStyleCache::GetWhitespace()
+{
+	if (whitespace < 0)
+		whitespace = style->GetProperty(WHITE_SPACE)->Get< int >();
+	return whitespace;
+}
+
+const Property *ElementStyleCache::GetLineHeightProperty()
+{
+	if (!line_height)
+		line_height = style->GetProperty(LINE_HEIGHT);
+	return line_height;
+}
+
+int ElementStyleCache::GetTextAlign()
+{
+	if (text_align < 0)
+		text_align = style->GetProperty(TEXT_ALIGN)->Get< int >();
+	return text_align;
+}
+
+int ElementStyleCache::GetTextTransform()
+{
+	if (text_transform < 0)
+		text_transform = style->GetProperty(TEXT_TRANSFORM)->Get< int >();
+	return text_transform;
+}
+
+const Property *ElementStyleCache::GetVerticalAlignProperty()
+{
+	if (!vertical_align)
+		vertical_align = style->GetProperty(VERTICAL_ALIGN);
+	return vertical_align;
+}
+
+}
+}

+ 130 - 0
Source/Core/ElementStyleCache.h

@@ -0,0 +1,130 @@
+/*
+ * This source file is part of libRocket, the HTML/CSS Interface Middleware
+ *
+ * For the latest information, see http://www.librocket.com
+ *
+ * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#ifndef ROCKETCOREELEMENTSTYLECACHE_H
+#define ROCKETCOREELEMENTSTYLECACHE_H
+
+#include "ElementDefinition.h"
+#include <Rocket/Core/Types.h>
+
+namespace Rocket {
+namespace Core {
+
+class ElementStyle;
+
+/**
+	Manages caching of layout-important properties and provides
+	O(1) access to them (note that for invalidated cache, the access
+	time is still O(log(N)) as per standard std::map).
+	@author Victor Luchits
+ */
+
+class ElementStyleCache
+{
+public:
+	ElementStyleCache(ElementStyle *style);
+
+	/// Invalidation function for all non-inherited properties
+	void Clear();
+
+	/// Invalidation function for all inherited properties
+	void ClearInherited();
+
+	/// Invalidation functions for individual and grouped non-inherited properties
+	void ClearBorder();
+	void ClearMargin();
+	void ClearPadding();
+	void ClearDimensions();
+	void ClearPosition();
+	void ClearFloat();
+	void ClearDisplay();
+	void ClearWhitespace();
+	void ClearOverflow();
+
+	/// Invalidation functions for individual and grouped inherited properties
+	void ClearLineHeight();
+	void ClearTextAlign();
+	void ClearTextTransform();
+	void ClearVerticalAlign();
+
+	/// Returns 'border-width' properties from element's style or local cache.
+	void GetBorderWidthProperties(const Property **border_top_width, const Property **border_bottom_width, const Property **border_left_width, const Property **border_right_width);
+	/// Returns 'margin' properties from element's style or local cache.
+	void GetMarginProperties(const Property **margin_top, const Property **margin_bottom, const Property **margin_left, const Property **margin_right);
+	/// Returns 'padding' properties from element's style or local cache.
+	void GetPaddingProperties(const Property **padding_top, const Property **padding_bottom, const Property **padding_left, const Property **padding_right);
+	/// Returns 'width' and 'height' properties from element's style or local cache.
+	void GetDimensionProperties(const Property **width, const Property **height);
+	/// Returns local 'width' and 'height' properties from element's style or local cache,
+	/// ignoring default values.
+	void GetLocalDimensionProperties(const Property **width, const Property **height);
+	/// Returns 'overflow' properties' values from element's style or local cache.
+	void GetOverflow(int *overflow_x, int *overflow_y);
+	/// Returns 'position' property value from element's style or local cache.
+	int GetPosition();
+	/// Returns 'float' property value from element's style or local cache.
+	int GetFloat();
+	/// Returns 'display' property value from element's style or local cache.
+	int GetDisplay();
+	/// Returns 'white-space' property value from element's style or local cache.
+	int GetWhitespace();
+
+	/// Returns 'line-height' property value from element's style or local cache.
+	const Property *GetLineHeightProperty();
+	/// Returns 'text-align' property value from element's style or local cache.
+	int GetTextAlign();
+	/// Returns 'text-transform' property value from element's style or local cache.
+	int GetTextTransform();
+	/// Returns 'vertical-align' property value from element's style or local cache.
+	const Property *GetVerticalAlignProperty();
+
+private:
+	/// Element style that owns this cache instance.
+	ElementStyle *style;
+
+	/// Cached properties.
+	const Property *border_top_width, *border_bottom_width, *border_left_width, *border_right_width;
+	const Property *margin_top, *margin_bottom, *margin_left, *margin_right;
+	const Property *padding_top, *padding_bottom, *padding_left, *padding_right;
+	const Property *width, *height;
+	const Property *local_width, *local_height;
+	bool have_local_width, have_local_height;
+	int overflow_x, overflow_y;
+	int position;
+	int float_;
+	int display;
+	int whitespace;
+	const Property *line_height;
+	int text_align;
+	int text_transform;
+	const Property *vertical_align;
+};
+
+}
+}
+
+#endif

+ 4 - 4
Source/Core/ElementTextDefault.cpp

@@ -149,7 +149,7 @@ bool ElementTextDefault::GenerateToken(float& token_width, int line_begin)
 		return 0;
 
 	// Determine how we are processing white-space while formatting the text.
-	int white_space_property = GetProperty< int >(WHITE_SPACE);
+	int white_space_property = GetWhitespace();
 	bool collapse_white_space = white_space_property == WHITE_SPACE_NORMAL ||
 								white_space_property == WHITE_SPACE_NOWRAP ||
 								white_space_property == WHITE_SPACE_PRE_LINE;
@@ -160,7 +160,7 @@ bool ElementTextDefault::GenerateToken(float& token_width, int line_begin)
 	const word* token_begin = text.CString() + line_begin;
 	WString token;
 
-	BuildToken(token, token_begin, text.CString() + text.Length(), true, collapse_white_space, break_at_endline, GetProperty< int >(TEXT_TRANSFORM));
+	BuildToken(token, token_begin, text.CString() + text.Length(), true, collapse_white_space, break_at_endline, GetTextTransform());
 	token_width = (float) font_face_handle->GetStringWidth(token, 0);
 
 	return LastToken(token_begin, text.CString() + text.Length(), collapse_white_space, break_at_endline);
@@ -181,7 +181,7 @@ bool ElementTextDefault::GenerateLine(WString& line, int& line_length, float& li
 		return true;
 
 	// Determine how we are processing white-space while formatting the text.
-	int white_space_property = GetProperty< int >(WHITE_SPACE);
+	int white_space_property = GetWhitespace();
 	bool collapse_white_space = white_space_property == WHITE_SPACE_NORMAL ||
 								white_space_property == WHITE_SPACE_NOWRAP ||
 								white_space_property == WHITE_SPACE_PRE_LINE;
@@ -194,7 +194,7 @@ bool ElementTextDefault::GenerateLine(WString& line, int& line_length, float& li
 							white_space_property == WHITE_SPACE_PRE_LINE;
 
 	// Determine what (if any) text transformation we are putting the characters through.
-	int text_transform_property = GetProperty< int >(TEXT_TRANSFORM);
+	int text_transform_property = GetTextTransform();
 
 	// Starting at the line_begin character, we generate sections of the text (we'll call them tokens) depending on the
 	// white-space parsing parameters. Each section is then appended to the line if it can fit. If not, or if an

+ 10 - 22
Source/Core/ElementUtilities.cpp

@@ -141,7 +141,7 @@ int ElementUtilities::GetLineHeight(Element* element)
 		return 0;
 
 	int line_height = font_face_handle->GetLineHeight();
-	const Property* line_height_property = element->GetProperty(LINE_HEIGHT);
+	const Property* line_height_property = element->GetLineHeightProperty();
 
 	// If the property is a straight number or an em measurement, then it scales the line height.
 	if (line_height_property->unit == Property::NUMBER ||
@@ -210,29 +210,14 @@ bool ElementUtilities::GetClippingRegion(Vector2i& clip_origin, Vector2i& clip_d
 			// Ignore nodes that don't clip.
 			if (clipping_element->GetClientWidth() < clipping_element->GetScrollWidth()
 				|| clipping_element->GetClientHeight() < clipping_element->GetScrollHeight())
-			{
+			{				
 				Vector2f element_origin_f = clipping_element->GetAbsoluteOffset(Box::CONTENT);
 				Vector2f element_dimensions_f = clipping_element->GetBox().GetSize(Box::CONTENT);
-
+				
 				Vector2i element_origin(Math::RealToInteger(element_origin_f.x), Math::RealToInteger(element_origin_f.y));
 				Vector2i element_dimensions(Math::RealToInteger(element_dimensions_f.x), Math::RealToInteger(element_dimensions_f.y));
-
-				bool clip_x = clipping_element->GetProperty(OVERFLOW_X)->Get< int >() != OVERFLOW_VISIBLE;
-				bool clip_y = clipping_element->GetProperty(OVERFLOW_Y)->Get< int >() != OVERFLOW_VISIBLE;
-				ROCKET_ASSERT(clip_x || clip_y);
 				
-				if (!clip_x)
-				{
-					element_origin.x = 0;
-					element_dimensions.x = clip_dimensions.x < 0 ? element->GetContext()->GetDimensions().x : clip_dimensions.x;
-				}
-				else if (!clip_y)
-				{
-					element_origin.y = 0;
-					element_dimensions.y = clip_dimensions.y < 0 ? element->GetContext()->GetDimensions().y : clip_dimensions.y;
-				}
-
-				if (clip_dimensions == Vector2i(-1, -1))
+				if (clip_origin == Vector2i(-1, -1) && clip_dimensions == Vector2i(-1, -1))
 				{
 					clip_origin = element_origin;
 					clip_dimensions = element_dimensions;
@@ -241,10 +226,10 @@ bool ElementUtilities::GetClippingRegion(Vector2i& clip_origin, Vector2i& clip_d
 				{
 					Vector2i top_left(Math::Max(clip_origin.x, element_origin.x),
 									  Math::Max(clip_origin.y, element_origin.y));
-
+					
 					Vector2i bottom_right(Math::Min(clip_origin.x + clip_dimensions.x, element_origin.x + element_dimensions.x),
 										  Math::Min(clip_origin.y + clip_dimensions.y, element_origin.y + element_dimensions.y));
-
+					
 					clip_origin = top_left;
 					clip_dimensions.x = Math::Max(0, bottom_right.x - top_left.x);
 					clip_dimensions.y = Math::Max(0, bottom_right.y - top_left.y);
@@ -434,7 +419,10 @@ static void SetBox(Element* element)
 
 	Box box;
 	LayoutEngine::BuildBox(box, containing_block, element);
-	if (element->GetLocalProperty(HEIGHT) == NULL)
+
+	const Property *local_height;
+	element->GetLocalDimensionProperties(NULL, &local_height);
+	if (local_height == NULL)
 		box.SetContent(Vector2f(box.GetSize().x, containing_block.y));
 
 	element->SetBox(box);

+ 4 - 5
Source/Core/LayoutBlockBox.cpp

@@ -71,7 +71,7 @@ LayoutBlockBox::LayoutBlockBox(LayoutEngine* _layout_engine, LayoutBlockBox* _pa
 	// Determine the offset parent for our children.
 	if (parent != NULL &&
 		parent->offset_parent->GetElement() != NULL &&
-		(element == NULL || element->GetProperty< int >(POSITION) == POSITION_STATIC))
+		(element == NULL || element->GetPosition() == POSITION_STATIC))
 		offset_parent = parent->offset_parent;
 	else
 		offset_parent = this;
@@ -100,11 +100,10 @@ LayoutBlockBox::LayoutBlockBox(LayoutEngine* _layout_engine, LayoutBlockBox* _pa
 
 	if (element != NULL)
 	{
-		wrap_content = element->GetProperty< int >(WHITE_SPACE) != WHITE_SPACE_NOWRAP;
+		wrap_content = element->GetWhitespace() != WHITE_SPACE_NOWRAP;
 
 		// Determine if this element should have scrollbars or not, and create them if so.
-		overflow_x_property = element->GetProperty< int >(OVERFLOW_X);
-		overflow_y_property = element->GetProperty< int >(OVERFLOW_Y);
+		element->GetOverflow(&overflow_x_property, &overflow_y_property);
 
 		if (overflow_x_property == OVERFLOW_SCROLL)
 			element->GetElementScroll()->EnableScrollbar(ElementScroll::HORIZONTAL, box.GetSize(Box::PADDING).x);
@@ -266,7 +265,7 @@ LayoutBlockBox::CloseResult LayoutBlockBox::Close()
 	if (context == BLOCK &&
 		element != NULL)
 	{
-		if (element->GetProperty< int >(POSITION) != POSITION_STATIC)
+		if (element->GetPosition() != POSITION_STATIC)
 			CloseAbsoluteElements();
 	}
 

+ 1 - 1
Source/Core/LayoutBlockBoxSpace.cpp

@@ -66,7 +66,7 @@ void LayoutBlockBoxSpace::PositionBox(Vector2f& box_position, float& box_width,
 float LayoutBlockBoxSpace::PositionBox(float cursor, Element* element)
 {
 	Vector2f element_size = element->GetBox().GetSize(Box::MARGIN);
-	int float_property = element->GetProperty< int >(FLOAT);
+	int float_property = element->GetFloat();
 
 	// Shift the cursor down (if necessary) so it isn't placed any higher than a previously-floated box.
 	for (int i = 0; i < NUM_ANCHOR_EDGES; ++i)

+ 48 - 29
Source/Core/LayoutEngine.cpp

@@ -100,23 +100,29 @@ void LayoutEngine::BuildBox(Box& box, const Vector2f& containing_block, Element*
 	}
 
 	// Calculate the padding area.
-	float padding = element->ResolveProperty(PADDING_TOP, containing_block.x);
+	const Property *padding_top, *padding_bottom, *padding_left, *padding_right;
+	element->GetPaddingProperties (&padding_top, &padding_bottom, &padding_left, &padding_right);
+
+	float padding = element->ResolveProperty(padding_top, containing_block.x);
 	box.SetEdge(Box::PADDING, Box::TOP, Math::Max(0.0f, padding));
-	padding = element->ResolveProperty(PADDING_RIGHT, containing_block.x);
+	padding = element->ResolveProperty(padding_right, containing_block.x);
 	box.SetEdge(Box::PADDING, Box::RIGHT, Math::Max(0.0f, padding));
-	padding = element->ResolveProperty(PADDING_BOTTOM, containing_block.x);
+	padding = element->ResolveProperty(padding_bottom, containing_block.x);
 	box.SetEdge(Box::PADDING, Box::BOTTOM, Math::Max(0.0f, padding));
-	padding = element->ResolveProperty(PADDING_LEFT, containing_block.x);
+	padding = element->ResolveProperty(padding_left, containing_block.x);
 	box.SetEdge(Box::PADDING, Box::LEFT, Math::Max(0.0f, padding));
 
 	// Calculate the border area.
-	float border = element->ResolveProperty(BORDER_TOP_WIDTH, containing_block.x);
+	const Property *border_top_width, *border_bottom_width, *border_left_width, *border_right_width;
+	element->GetBorderWidthProperties (&border_top_width, &border_bottom_width, &border_left_width, &border_right_width);
+
+	float border = element->ResolveProperty(border_top_width, containing_block.x);
 	box.SetEdge(Box::BORDER, Box::TOP, Math::Max(0.0f, border));
-	border = element->ResolveProperty(BORDER_RIGHT_WIDTH, containing_block.x);
+	border = element->ResolveProperty(border_right_width, containing_block.x);
 	box.SetEdge(Box::BORDER, Box::RIGHT, Math::Max(0.0f, border));
-	border = element->ResolveProperty(BORDER_BOTTOM_WIDTH, containing_block.x);
+	border = element->ResolveProperty(border_bottom_width, containing_block.x);
 	box.SetEdge(Box::BORDER, Box::BOTTOM, Math::Max(0.0f, border));
-	border = element->ResolveProperty(BORDER_LEFT_WIDTH, containing_block.x);
+	border = element->ResolveProperty(border_left_width, containing_block.x);
 	box.SetEdge(Box::BORDER, Box::LEFT, Math::Max(0.0f, border));
 
 	// Calculate the size of the content area.
@@ -134,15 +140,17 @@ void LayoutEngine::BuildBox(Box& box, const Vector2f& containing_block, Element*
 		// The element has resized itself, so we only resize it if a RCSS width or height was set explicitly. A value of
 		// 'auto' (or 'auto-fit', ie, both keywords) means keep (or adjust) the intrinsic dimensions.
 		bool auto_width = false, auto_height = false;
-		const Property* width_property = element->GetProperty(WIDTH);
+		const Property* width_property, *height_property;
+
+		element->GetDimensionProperties(&width_property, &height_property);
+
 		if (width_property->unit != Property::KEYWORD)
-			content_area.x = element->ResolveProperty(WIDTH, containing_block.x);
+			content_area.x = element->ResolveProperty(width_property, containing_block.x);
 		else
 			auto_width = true;
 
-		const Property* height_property = element->GetProperty(HEIGHT);
 		if (height_property->unit != Property::KEYWORD)
-			content_area.y = element->ResolveProperty(HEIGHT, containing_block.y);
+			content_area.y = element->ResolveProperty(height_property, containing_block.y);
 		else
 			auto_height = true;
 
@@ -181,10 +189,13 @@ void LayoutEngine::BuildBox(Box& box, const Vector2f& containing_block, Element*
 		box.SetContent(content_area);
 
 		// Evaluate the margins. Any declared as 'auto' will resolve to 0.
-		box.SetEdge(Box::MARGIN, Box::TOP, element->ResolveProperty(MARGIN_TOP, containing_block.x));
-		box.SetEdge(Box::MARGIN, Box::RIGHT, element->ResolveProperty(MARGIN_RIGHT, containing_block.x));
-		box.SetEdge(Box::MARGIN, Box::BOTTOM, element->ResolveProperty(MARGIN_BOTTOM, containing_block.x));
-		box.SetEdge(Box::MARGIN, Box::LEFT, element->ResolveProperty(MARGIN_LEFT, containing_block.x));
+		const Property *margin_top, *margin_bottom, *margin_left, *margin_right;
+		element->GetMarginProperties(&margin_top, &margin_bottom, &margin_left, &margin_right);
+
+		box.SetEdge(Box::MARGIN, Box::TOP, element->ResolveProperty(margin_top, containing_block.x));
+		box.SetEdge(Box::MARGIN, Box::RIGHT, element->ResolveProperty(margin_right, containing_block.x));
+		box.SetEdge(Box::MARGIN, Box::BOTTOM, element->ResolveProperty(margin_bottom, containing_block.x));
+		box.SetEdge(Box::MARGIN, Box::LEFT, element->ResolveProperty(margin_left, containing_block.x));
 	}
 
 	// The element is block, so we need to run the box through the ringer to potentially evaluate auto margins and
@@ -295,13 +306,13 @@ bool LayoutEngine::FormatElement(Element* element)
 		return true;
 
 	// Fetch the display property, and don't lay this element out if it is set to a display type of none.
-	int display_property = element->GetProperty< int >(DISPLAY);
+	int display_property = element->GetDisplay();
 	if (display_property == DISPLAY_NONE)
 		return true;
 
 	// Check for an absolute position; if this has been set, then we remove it from the flow and add it to the current
 	// block box to be laid out and positioned once the block has been closed and sized.
-	int position_property = element->GetProperty< int >(POSITION);
+	int position_property = element->GetPosition();
 	if (position_property == POSITION_ABSOLUTE ||
 		position_property == POSITION_FIXED)
 	{
@@ -311,7 +322,7 @@ bool LayoutEngine::FormatElement(Element* element)
 	}
 
 	// If the element is floating, we remove it from the flow.
-	int float_property = element->GetProperty< int >(FLOAT);
+	int float_property = element->GetFloat();
 	if (float_property != FLOAT_NONE)
 	{
 		// Format the element as a block element.
@@ -469,7 +480,8 @@ void LayoutEngine::BuildBoxWidth(Box& box, Element* element, float containing_bl
 		width_auto = false;
 	else
 	{
-		const Property* width_property = element->GetProperty(WIDTH);
+		const Property* width_property;
+		element->GetDimensionProperties(&width_property, NULL);
 		if (width_property->unit == Property::KEYWORD)
 		{
 			width_auto = true;
@@ -477,17 +489,20 @@ void LayoutEngine::BuildBoxWidth(Box& box, Element* element, float containing_bl
 		else
 		{
 			width_auto = false;
-			content_area.x = element->ResolveProperty(WIDTH, containing_block_width);
+			content_area.x = element->ResolveProperty(width_property, containing_block_width);
 		}
 	}
 
 	// Determine if the element has automatic margins.
 	bool margins_auto[2];
 	int num_auto_margins = 0;
+
+	const Property *margin_left, *margin_right;
+	element->GetMarginProperties(NULL, NULL, &margin_left, &margin_right);
+
 	for (int i = 0; i < 2; ++i)
 	{
-		const String& property_name = i == 0 ? MARGIN_LEFT : MARGIN_RIGHT;
-		const Property* margin_property = element->GetLocalProperty(property_name);
+		const Property* margin_property = i == 0 ? margin_left : margin_right;
 		if (margin_property != NULL &&
 			margin_property->unit == Property::KEYWORD)
 		{
@@ -497,7 +512,7 @@ void LayoutEngine::BuildBoxWidth(Box& box, Element* element, float containing_bl
 		else
 		{
 			margins_auto[i] = false;
-			box.SetEdge(Box::MARGIN, i == 0 ? Box::LEFT : Box::RIGHT, element->ResolveProperty(property_name, containing_block_width));
+			box.SetEdge(Box::MARGIN, i == 0 ? Box::LEFT : Box::RIGHT, element->ResolveProperty(margin_property, containing_block_width));
 		}
 	}
 
@@ -561,7 +576,8 @@ void LayoutEngine::BuildBoxHeight(Box& box, Element* element, float containing_b
 		height_auto = false;
 	else
 	{
-		const Property* height_property = element->GetProperty(HEIGHT);
+		const Property* height_property;
+		element->GetDimensionProperties(NULL, &height_property);
 		if (height_property == NULL)
 		{
 			height_auto = false;		
@@ -573,17 +589,20 @@ void LayoutEngine::BuildBoxHeight(Box& box, Element* element, float containing_b
 		else
 		{
 			height_auto = false;
-			content_area.y = element->ResolveProperty(HEIGHT, containing_block_height);
+			content_area.y = element->ResolveProperty(height_property, containing_block_height);
 		}
 	}
 
 	// Determine if the element has automatic margins.
 	bool margins_auto[2];
 	int num_auto_margins = 0;
+
+	const Property *margin_top, *margin_bottom;
+	element->GetMarginProperties(&margin_top, &margin_bottom, NULL, NULL);
+
 	for (int i = 0; i < 2; ++i)
 	{
-		const String& property_name = i == 0 ? MARGIN_TOP : MARGIN_BOTTOM;
-		const Property* margin_property = element->GetLocalProperty(property_name);
+		const Property* margin_property = i == 0 ? margin_top : margin_bottom;
 		if (margin_property != NULL &&
 			margin_property->unit == Property::KEYWORD)
 		{
@@ -593,7 +612,7 @@ void LayoutEngine::BuildBoxHeight(Box& box, Element* element, float containing_b
 		else
 		{
 			margins_auto[i] = false;
-			box.SetEdge(Box::MARGIN, i == 0 ? Box::TOP : Box::BOTTOM, element->ResolveProperty(property_name, containing_block_height));
+			box.SetEdge(Box::MARGIN, i == 0 ? Box::TOP : Box::BOTTOM, element->ResolveProperty(margin_property, containing_block_height));
 		}
 	}
 

+ 2 - 2
Source/Core/LayoutInlineBox.cpp

@@ -70,7 +70,7 @@ LayoutInlineBox::LayoutInlineBox(Element* _element, const Box& _box) : box(_box)
 		}
 	}
 
-	const Property* property = element->GetProperty(VERTICAL_ALIGN);
+	const Property* property = element->GetVerticalAlignProperty();
 	if (property->unit == Property::KEYWORD)
 		vertical_align_property = property->value.Get< int >();
 	else
@@ -239,7 +239,7 @@ void LayoutInlineBox::CalculateBaseline(float& ascender, float& descender)
 		// The baseline of this box is offset by a fixed amount from its parent's baseline.
 		default:
 		{
-			SetVerticalPosition(-1 * element->ResolveProperty(VERTICAL_ALIGN, GetParentLineHeight()));
+			SetVerticalPosition(-1 * element->ResolveProperty(element->GetVerticalAlignProperty(), GetParentLineHeight()));
 		}
 		break;
 	}

+ 1 - 1
Source/Core/LayoutLineBox.cpp

@@ -140,7 +140,7 @@ LayoutInlineBox* LayoutLineBox::Close(LayoutInlineBox* overflow)
 
 	// Position all the boxes horizontally in the line. We only need to reposition the elements if they're set to
 	// centre or right; the element are already placed left-aligned, and justification occurs at the text level.
-	int text_align_property = parent->GetParent()->GetElement()->GetProperty(TEXT_ALIGN)->value.Get< int >();
+	int text_align_property = parent->GetParent()->GetElement()->GetTextAlign();
 	if (text_align_property == TEXT_ALIGN_CENTER ||
 		text_align_property == TEXT_ALIGN_RIGHT)
 	{

+ 1 - 1
Source/Core/StyleSheetNodeSelectorEmpty.cpp

@@ -46,7 +46,7 @@ bool StyleSheetNodeSelectorEmpty::IsApplicable(const Element* element, int ROCKE
 {
 	for (int i = 0; i < element->GetNumChildren(); ++i)
 	{
-		if (element->GetChild(i)->GetProperty< int >(DISPLAY) != DISPLAY_NONE)
+		if (element->GetChild(i)->GetDisplay() != DISPLAY_NONE)
 			return false;
 	}
 

+ 1 - 1
Source/Core/StyleSheetNodeSelectorFirstChild.cpp

@@ -58,7 +58,7 @@ bool StyleSheetNodeSelectorFirstChild::IsApplicable(const Element* element, int
 
 		// If this child is not a text element, then the selector fails; this element is non-trivial.
 		if (dynamic_cast< ElementText* >(child) == NULL &&
-			child->GetProperty< int >(DISPLAY) != DISPLAY_NONE)
+			child->GetDisplay() != DISPLAY_NONE)
 			return false;
 
 		// Otherwise, skip over the text element to find the last non-trivial element.

+ 1 - 1
Source/Core/StyleSheetNodeSelectorFirstOfType.cpp

@@ -59,7 +59,7 @@ bool StyleSheetNodeSelectorFirstOfType::IsApplicable(const Element* element, int
 		// Otherwise, if this child shares our element's tag, then our element is not the first tagged child; the
 		// selector fails.
 		if (child->GetTagName() == element->GetTagName() &&
-			child->GetProperty< int >(DISPLAY) != DISPLAY_NONE)
+			child->GetDisplay() != DISPLAY_NONE)
 			return false;
 
 		child_index++;

+ 1 - 1
Source/Core/StyleSheetNodeSelectorLastChild.cpp

@@ -58,7 +58,7 @@ bool StyleSheetNodeSelectorLastChild::IsApplicable(const Element* element, int R
 
 		// If this child is not a text element, then the selector fails; this element is non-trivial.
 		if (dynamic_cast< ElementText* >(child) == NULL &&
-			child->GetProperty< int >(DISPLAY) != DISPLAY_NONE)
+			child->GetDisplay() != DISPLAY_NONE)
 			return false;
 
 		// Otherwise, skip over the text element to find the last non-trivial element.

+ 1 - 1
Source/Core/StyleSheetNodeSelectorLastOfType.cpp

@@ -59,7 +59,7 @@ bool StyleSheetNodeSelectorLastOfType::IsApplicable(const Element* element, int
 		// Otherwise, if this child shares our element's tag, then our element is not the first tagged child; the
 		// selector fails.
 		if (child->GetTagName() == element->GetTagName() &&
-			child->GetProperty< int >(DISPLAY) != DISPLAY_NONE)
+			child->GetDisplay() != DISPLAY_NONE)
 			return false;
 
 		child_index--;

+ 1 - 1
Source/Core/StyleSheetNodeSelectorNthChild.cpp

@@ -64,7 +64,7 @@ bool StyleSheetNodeSelectorNthChild::IsApplicable(const Element* element, int a,
 			break;
 
 		// Skip nodes without a display type.
-		if (child->GetProperty< int >(DISPLAY) == DISPLAY_NONE)
+		if (child->GetDisplay() == DISPLAY_NONE)
 			continue;
 
 		element_index++;

+ 1 - 1
Source/Core/StyleSheetNodeSelectorNthLastChild.cpp

@@ -62,7 +62,7 @@ bool StyleSheetNodeSelectorNthLastChild::IsApplicable(const Element* element, in
 		if (child == element)
 			break;
 
-		if (child->GetProperty< int >(DISPLAY) == DISPLAY_NONE)
+		if (child->GetDisplay() == DISPLAY_NONE)
 			continue;
 
 		element_index++;

+ 1 - 1
Source/Core/StyleSheetNodeSelectorNthLastOfType.cpp

@@ -60,7 +60,7 @@ bool StyleSheetNodeSelectorNthLastOfType::IsApplicable(const Element* element, i
 
 		// Skip nodes that don't share our tag.
 		if (child->GetTagName() != element->GetTagName() ||
-			child->GetProperty< int >(DISPLAY) == DISPLAY_NONE)
+			child->GetDisplay() == DISPLAY_NONE)
 			continue;
 
 		element_index++;

+ 1 - 1
Source/Core/StyleSheetNodeSelectorNthOfType.cpp

@@ -60,7 +60,7 @@ bool StyleSheetNodeSelectorNthOfType::IsApplicable(const Element* element, int a
 
 		// Skip nodes that don't share our tag.
 		if (child->GetTagName() != element->GetTagName() ||
-			child->GetProperty< int >(DISPLAY) == DISPLAY_NONE)
+			child->GetDisplay() == DISPLAY_NONE)
 			continue;
 
 		element_index++;

+ 1 - 1
Source/Core/StyleSheetNodeSelectorOnlyChild.cpp

@@ -58,7 +58,7 @@ bool StyleSheetNodeSelectorOnlyChild::IsApplicable(const Element* element, int R
 
 		// Skip the child if it is trivial.
 		if (dynamic_cast< const ElementText* >(element) != NULL ||
-			child->GetProperty< int >(DISPLAY) == DISPLAY_NONE)
+			child->GetDisplay() == DISPLAY_NONE)
 			continue;
 
 		return false;

+ 1 - 1
Source/Core/StyleSheetNodeSelectorOnlyOfType.cpp

@@ -58,7 +58,7 @@ bool StyleSheetNodeSelectorOnlyOfType::IsApplicable(const Element* element, int
 
 		// Skip the child if it does not share our tag.
 		if (child->GetTagName() != element->GetTagName() ||
-			child->GetProperty< int >(DISPLAY) == DISPLAY_NONE)
+			child->GetDisplay() == DISPLAY_NONE)
 			continue;
 
 		// We've found a similarly-tagged child to our element; selector fails.

+ 6 - 3
Source/Core/WidgetSlider.cpp

@@ -308,10 +308,13 @@ void WidgetSlider::FormatBar(float bar_length)
 	Box bar_box;
 	LayoutEngine::BuildBox(bar_box, parent->GetBox().GetSize(), bar);
 
+	const Property *local_width, *local_height;
+	bar->GetLocalDimensionProperties(&local_width, &local_height);
+
 	Vector2f bar_box_content = bar_box.GetSize();
 	if (orientation == HORIZONTAL)
 	{
-		if (bar->GetLocalProperty(HEIGHT) == NULL)
+		if (local_height == NULL)
 			bar_box_content.y = parent->GetBox().GetSize().y;
 	}
 
@@ -323,7 +326,7 @@ void WidgetSlider::FormatBar(float bar_length)
 		{
 			float track_length = track_size.y - (bar_box.GetCumulativeEdge(Box::CONTENT, Box::TOP) + bar_box.GetCumulativeEdge(Box::CONTENT, Box::BOTTOM));
 
-			if (bar->GetLocalProperty(HEIGHT) == NULL)
+			if (local_height == NULL)
 			{
 				bar_box_content.y = track_length * bar_length;
 
@@ -344,7 +347,7 @@ void WidgetSlider::FormatBar(float bar_length)
 		{
 			float track_length = track_size.x - (bar_box.GetCumulativeEdge(Box::CONTENT, Box::LEFT) + bar_box.GetCumulativeEdge(Box::CONTENT, Box::RIGHT));
 
-			if (bar->GetLocalProperty(WIDTH) == NULL)
+			if (local_width == NULL)
 			{
 				bar_box_content.x = track_length * bar_length;