+2012-04-11 Arko Saha <arko@motorola.com>
+
+ Microdata: Implement cache mechanism for HTMLPropertiesCollection.
+ https://bugs.webkit.org/show_bug.cgi?id=80490
+
+ Reviewed by Ryosuke Niwa.
+
+ Implemented caching mechanism for HTMLPropertiesCollection.
+ propertyCache - contains microdata item properties.
+ itemRefElements - contains sorted microdata item and itemref elements.
+ propertyNames - contains microdata property names of the elements in the collection.
+ itemRefElementPosition - store the current position of itemRefElements.
+ hasItemRefElements - set to ture once we have sorted microdata item and itemref elements list i.e, itemRefElements.
+ Cache is invalidated only when dom tree modified. Whenever any query is made on HTMLPropertiesCollection,
+ result is returned from the cache. Earliar we used to calculate properties node list every time a query is made.
+
+ * html/HTMLPropertiesCollection.cpp:
+ (WebCore):
+ (WebCore::HTMLPropertiesCollection::HTMLPropertiesCollection):
+ (WebCore::HTMLPropertiesCollection::invalidateCacheIfNeeded):
+ (WebCore::HTMLPropertiesCollection::updateRefElements): Appends microdata item element and elements which
+ are added through itemref attribute in itemRefElements (in tree order).
+ (WebCore::nextNodeWithProperty):
+ (WebCore::HTMLPropertiesCollection::itemAfter): Takes parent base and previous item property as argument.
+ Finds the next item property in base.
+ (WebCore::HTMLPropertiesCollection::calcLength): Calculates the length of properties collection if length
+ is not available in the cache.
+ (WebCore::HTMLPropertiesCollection::length):
+ (WebCore::HTMLPropertiesCollection::firstProperty): Returns the first property in the collection.
+ (WebCore::HTMLPropertiesCollection::item):
+ (WebCore::HTMLPropertiesCollection::findProperties): Finds microdata item properties in the base element.
+ Appends the properties in propertyCache and property names in propertyNames.
+ (WebCore::HTMLPropertiesCollection::updateNameCache): It updates the propertyCache and propertyNames if hasNameCache is false.
+ (WebCore::HTMLPropertiesCollection::names):
+ (WebCore::HTMLPropertiesCollection::namedItem):
+ (WebCore::HTMLPropertiesCollection::hasNamedItem):
+ * html/HTMLPropertiesCollection.h:
+ (HTMLPropertiesCollection):
+
2012-04-10 Jocelyn Turcotte <jocelyn.turcotte@nokia.com>
[Qt] Fix issues when using the WebView as ShaderEffectSource
using namespace HTMLNames;
-static inline bool compareTreeOrder(Node* node1, Node* node2)
-{
- return (node2->compareDocumentPosition(node1) & (Node::DOCUMENT_POSITION_PRECEDING | Node::DOCUMENT_POSITION_DISCONNECTED)) == Node::DOCUMENT_POSITION_PRECEDING;
-}
-
PassOwnPtr<HTMLPropertiesCollection> HTMLPropertiesCollection::create(Node* itemNode)
{
return adoptPtr(new HTMLPropertiesCollection(itemNode));
HTMLPropertiesCollection::HTMLPropertiesCollection(Node* itemNode)
: HTMLCollection(itemNode, ItemProperties)
- , m_propertyNames(DOMStringList::create())
{
}
{
}
-void HTMLPropertiesCollection::findPropetiesOfAnItem(Node* root) const
+void HTMLPropertiesCollection::invalidateCacheIfNeeded() const
{
- // 5.2.5 Associating names with items.
- Vector<Node*> memory;
+ uint64_t docversion = base()->document()->domTreeVersion();
- memory.append(root);
+ if (m_cache.version == docversion)
+ return;
- Vector<Node*> pending;
- // Add the child elements of root, if any, to pending.
- for (Node* child = root->firstChild(); child; child = child->nextSibling())
- if (child->isHTMLElement())
- pending.append(child);
+ m_cache.clear();
+ m_cache.version = docversion;
+}
- // If root has an itemref attribute, split the value of that itemref attribute on spaces.
- // For each resulting token ID, if there is an element in the home subtree of root with the ID ID,
- // then add the first such element to pending.
- if (toHTMLElement(root)->fastHasAttribute(itemrefAttr)) {
- DOMSettableTokenList* itemRef = root->itemRef();
+void HTMLPropertiesCollection::updateRefElements() const
+{
+ if (m_cache.hasItemRefElements)
+ return;
- for (size_t i = 0; i < itemRef->length(); ++i) {
- AtomicString id = itemRef->item(i);
+ Vector<Element*> itemRefElements;
+ HTMLElement* baseElement = toHTMLElement(base());
- Element* element = root->document()->getElementById(id);
- if (element && element->isHTMLElement())
- pending.append(element);
- }
+ if (!baseElement->fastHasAttribute(itemrefAttr)) {
+ itemRefElements.append(baseElement);
+ m_cache.setItemRefElements(itemRefElements);
+ return;
}
- // Loop till we have processed all pending elements
- while (!pending.isEmpty()) {
+ DOMSettableTokenList* itemRef = baseElement->itemRef();
+ RefPtr<DOMSettableTokenList> processedItemRef = DOMSettableTokenList::create();
+ Node* rootNode = baseElement->treeScope()->rootNode();
- // Remove first element from pending and let current be that element.
- Node* current = pending[0];
- pending.remove(0);
+ for (Node* current = rootNode->firstChild(); current; current = current->traverseNextNode(rootNode)) {
+ if (!current->isHTMLElement())
+ continue;
+ HTMLElement* element = toHTMLElement(current);
- // If current is already in memory, there is a microdata error;
- if (memory.contains(current)) {
- // microdata error;
+ if (element == baseElement) {
+ itemRefElements.append(element);
continue;
}
- memory.append(current);
+ const AtomicString& id = element->getIdAttribute();
+ if (!processedItemRef->tokens().contains(id) && itemRef->tokens().contains(id)) {
+ processedItemRef->setValue(id);
+ if (!element->isDescendantOf(baseElement))
+ itemRefElements.append(element);
+ }
+ }
+
+ m_cache.setItemRefElements(itemRefElements);
+}
- // If current does not have an itemscope attribute, then: add all the child elements of current to pending.
+static Node* nextNodeWithProperty(Node* base, Node* node)
+{
+ // An Microdata item may contain properties which in turn are items themselves. Properties can
+ // also themselves be groups of name-value pairs, by putting the itemscope attribute on the element
+ // that declares the property. If the property has an itemscope attribute specified then we need
+ // to traverse the next sibling.
+ return node == base || (node->isHTMLElement() && !toHTMLElement(node)->fastHasAttribute(itemscopeAttr))
+ ? node->traverseNextNode(base) : node->traverseNextSibling(base);
+}
+
+Element* HTMLPropertiesCollection::itemAfter(Element* base, Element* previous) const
+{
+ Node* current;
+ current = previous ? nextNodeWithProperty(base, previous) : base;
+
+ for (; current; current = nextNodeWithProperty(base, current)) {
+ if (!current->isHTMLElement())
+ continue;
HTMLElement* element = toHTMLElement(current);
- if (!element->fastHasAttribute(itemscopeAttr)) {
- for (Node* child = current->firstChild(); child; child = child->nextSibling())
- if (child->isHTMLElement())
- pending.append(child);
+ if (element->fastHasAttribute(itempropAttr)) {
+ return element;
}
+ }
- // If current has an itemprop attribute specified, add it to results.
- if (element->fastHasAttribute(itempropAttr))
- m_properties.append(current);
+ return 0;
+}
+
+unsigned HTMLPropertiesCollection::calcLength() const
+{
+ unsigned length = 0;
+ updateRefElements();
+
+ const Vector<Element*>& itemRefElements = m_cache.getItemRefElements();
+ for (unsigned i = 0; i < itemRefElements.size(); ++i) {
+ for (Element* element = itemAfter(itemRefElements[i], 0); element; element = itemAfter(itemRefElements[i], element))
+ ++length;
}
+
+ return length;
}
unsigned HTMLPropertiesCollection::length() const
{
- if (!base()->isHTMLElement() || !toHTMLElement(base())->fastHasAttribute(itemscopeAttr))
+ if (!toHTMLElement(base())->fastHasAttribute(itemscopeAttr))
return 0;
- m_properties.clear();
- findPropetiesOfAnItem(base());
- return m_properties.size();
+ invalidateCacheIfNeeded();
+
+ if (!m_cache.hasLength)
+ m_cache.updateLength(calcLength());
+
+ return m_cache.length;
+}
+
+Element* HTMLPropertiesCollection::firstProperty() const
+{
+ Element* element = 0;
+ m_cache.resetPosition();
+ const Vector<Element*>& itemRefElements = m_cache.getItemRefElements();
+ for (unsigned i = 0; i < itemRefElements.size(); ++i) {
+ element = itemAfter(itemRefElements[i], 0);
+ if (element) {
+ m_cache.itemRefElementPosition = i;
+ break;
+ }
+ }
+
+ return element;
}
Node* HTMLPropertiesCollection::item(unsigned index) const
{
- if (!base()->isHTMLElement() || !toHTMLElement(base())->fastHasAttribute(itemscopeAttr))
+ if (!toHTMLElement(base())->fastHasAttribute(itemscopeAttr))
return 0;
- m_properties.clear();
- findPropetiesOfAnItem(base());
+ invalidateCacheIfNeeded();
+ if (m_cache.current && m_cache.position == index)
+ return m_cache.current;
- if (m_properties.size() <= index)
+ if (m_cache.hasLength && m_cache.length <= index)
return 0;
- std::sort(m_properties.begin(), m_properties.end(), compareTreeOrder);
- return m_properties[index];
+ updateRefElements();
+ if (!m_cache.current || m_cache.position > index) {
+ m_cache.current = firstProperty();
+ if (!m_cache.current)
+ return 0;
+ }
+
+ unsigned currentPosition = m_cache.position;
+ Element* element = m_cache.current;
+ unsigned itemRefElementPos = m_cache.itemRefElementPosition;
+ const Vector<Element*>& itemRefElements = m_cache.getItemRefElements();
+
+ bool found = (m_cache.position == index);
+
+ for (unsigned i = itemRefElementPos; i < itemRefElements.size() && !found; ++i) {
+ while (currentPosition < index) {
+ element = itemAfter(itemRefElements[i], element);
+ if (!element)
+ break;
+ currentPosition++;
+
+ if (currentPosition == index) {
+ found = true;
+ itemRefElementPos = i;
+ break;
+ }
+ }
+ }
+
+ m_cache.updateCurrentItem(element, index, itemRefElementPos);
+ return m_cache.current;
}
-PassRefPtr<DOMStringList> HTMLPropertiesCollection::names() const
+void HTMLPropertiesCollection::findProperties(Element* base) const
{
- m_properties.clear();
- m_propertyNames->clear();
+ for (Element* element = itemAfter(base, 0); element; element = itemAfter(base, element)) {
+ DOMSettableTokenList* itemProperty = element->itemProp();
+ for (unsigned i = 0; i < itemProperty->length(); ++i)
+ m_cache.updatePropertyCache(element, itemProperty->item(i));
+ }
+}
- if (!base()->isHTMLElement() || !toHTMLElement(base())->fastHasAttribute(itemscopeAttr))
- return m_propertyNames;
+void HTMLPropertiesCollection::updateNameCache() const
+{
+ invalidateCacheIfNeeded();
+ if (m_cache.hasNameCache)
+ return;
- findPropetiesOfAnItem(base());
+ updateRefElements();
- std::sort(m_properties.begin(), m_properties.end(), compareTreeOrder);
+ const Vector<Element*>& itemRefElements = m_cache.getItemRefElements();
+ for (unsigned i = 0; i < itemRefElements.size(); ++i)
+ findProperties(itemRefElements[i]);
- for (size_t i = 0; i < m_properties.size(); ++i) {
- // For each item properties, split the value of that itemprop attribute on spaces.
- // Add all tokens to property names, with the order preserved but with duplicates removed.
- DOMSettableTokenList* itemProperty = m_properties[i]->itemProp();
- for (size_t i = 0; i < itemProperty->length(); ++i) {
- AtomicString propertyName = itemProperty->item(i);
- if (m_propertyNames->isEmpty() || !m_propertyNames->contains(propertyName))
- m_propertyNames->append(propertyName);
- }
- }
+ m_cache.hasNameCache = true;
+}
+
+PassRefPtr<DOMStringList> HTMLPropertiesCollection::names() const
+{
+ if (!toHTMLElement(base())->fastHasAttribute(itemscopeAttr))
+ return DOMStringList::create();
+
+ updateNameCache();
- return m_propertyNames;
+ return m_cache.propertyNames;
}
PassRefPtr<NodeList> HTMLPropertiesCollection::namedItem(const String& name) const
{
- if (!base()->isHTMLElement() || !toHTMLElement(base())->fastHasAttribute(itemscopeAttr))
+ if (!toHTMLElement(base())->fastHasAttribute(itemscopeAttr))
return 0;
- m_properties.clear();
Vector<RefPtr<Node> > namedItems;
- findPropetiesOfAnItem(base());
- std::sort(m_properties.begin(), m_properties.end(), compareTreeOrder);
+ updateNameCache();
- // For each item properties, split the value of that itemprop attribute on spaces.
- // Add element to namedItem that contains a property named name, with the order preserved.
- for (size_t i = 0; i < m_properties.size(); ++i) {
- DOMSettableTokenList* itemProperty = m_properties[i]->itemProp();
- if (itemProperty->tokens().contains(name))
- namedItems.append(m_properties[i]);
- }
+ Vector<Element*>* propertyResults = m_cache.propertyCache.get(AtomicString(name).impl());
+ for (unsigned i = 0; propertyResults && i < propertyResults->size(); ++i)
+ namedItems.append(propertyResults->at(i));
// FIXME: HTML5 specifies that this should return PropertyNodeList.
return namedItems.isEmpty() ? 0 : StaticNodeList::adopt(namedItems);
bool HTMLPropertiesCollection::hasNamedItem(const AtomicString& name) const
{
- if (!base()->isHTMLElement() || !toHTMLElement(base())->fastHasAttribute(itemscopeAttr))
+ if (!toHTMLElement(base())->fastHasAttribute(itemscopeAttr))
return false;
- m_properties.clear();
- findPropetiesOfAnItem(base());
+ updateNameCache();
- // For each item properties, split the value of that itemprop attribute on spaces.
- // Return true if element contains a property named name.
- for (size_t i = 0; i < m_properties.size(); ++i) {
- DOMSettableTokenList* itemProperty = m_properties[i]->itemProp();
- if (itemProperty->tokens().contains(name))
+ if (Vector<Element*>* propertyCache = m_cache.propertyCache.get(name.impl())) {
+ if (!propertyCache->isEmpty())
return true;
}
#if ENABLE(MICRODATA)
+#include "DOMStringList.h"
#include "HTMLCollection.h"
namespace WebCore {
private:
HTMLPropertiesCollection(Node*);
- void findPropetiesOfAnItem(Node* current) const;
- void getNamedItems(Vector<RefPtr<Node> >&, const String&) const;
+ unsigned calcLength() const;
+ void findProperties(Element* base) const;
+
+ Node* findRefElements(Node* previous) const;
+
+ Element* firstProperty() const;
+ Element* itemAfter(Element* base, Element* previous) const;
+
+ void updateNameCache() const;
+ void updateRefElements() const;
+
+ void invalidateCacheIfNeeded() const;
+
+ mutable struct {
+ uint64_t version;
+ Element* current;
+ unsigned position;
+ unsigned length;
+ bool hasLength;
+ bool hasNameCache;
+ NodeCacheMap propertyCache;
+ Vector<Element*> itemRefElements;
+ RefPtr<DOMStringList> propertyNames;
+ unsigned itemRefElementPosition;
+ bool hasItemRefElements;
+
+ void clear()
+ {
+ version = 0;
+ current = 0;
+ position = 0;
+ length = 0;
+ hasLength = false;
+ hasNameCache = false;
+ propertyCache.clear();
+ itemRefElements.clear();
+ propertyNames.clear();
+ itemRefElementPosition = 0;
+ hasItemRefElements = false;
+ }
+
+ void setItemRefElements(const Vector<Element*>& elements)
+ {
+ itemRefElements = elements;
+ hasItemRefElements = true;
+ }
+
+ const Vector<Element*>& getItemRefElements()
+ {
+ return itemRefElements;
+ }
+
+ void updateLength(unsigned len)
+ {
+ length = len;
+ hasLength = true;
+ }
+
+ void updatePropertyCache(Element* element, const AtomicString& propertyName)
+ {
+ if (!propertyNames)
+ propertyNames = DOMStringList::create();
+
+ if (!propertyNames->contains(propertyName))
+ propertyNames->append(propertyName);
+
+ Vector<Element*>* propertyResults = propertyCache.get(propertyName.impl());
+ if (!propertyResults || !propertyResults->contains(element))
+ append(propertyCache, propertyName, element);
+ }
+
+ void updateCurrentItem(Element* element, unsigned pos, unsigned itemRefElementPos)
+ {
+ current = element;
+ position = pos;
+ itemRefElementPosition = itemRefElementPos;
+ }
+
+ void resetPosition()
+ {
+ current = 0;
+ position = 0;
+ itemRefElementPosition = 0;
+ }
+
+ } m_cache;
- mutable Vector<Node*> m_properties;
- mutable RefPtr<DOMStringList> m_propertyNames;
};
} // namespace WebCore