summaryrefslogtreecommitdiff
path: root/Source/WebCore/rendering/RenderNamedFlowThread.cpp
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@nokia.com>2012-05-07 11:21:11 +0200
committerSimon Hausmann <simon.hausmann@nokia.com>2012-05-07 11:21:11 +0200
commit2cf6c8816a73e0132bd8fa3b509d62d7c51b6e47 (patch)
tree988e8c5b116dd0466244ae2fe5af8ee9be926d76 /Source/WebCore/rendering/RenderNamedFlowThread.cpp
parentdd91e772430dc294e3bf478c119ef8d43c0a3358 (diff)
downloadqtwebkit-2cf6c8816a73e0132bd8fa3b509d62d7c51b6e47.tar.gz
Imported WebKit commit 7e538425aa020340619e927792f3d895061fb54b (http://svn.webkit.org/repository/webkit/trunk@116286)
Diffstat (limited to 'Source/WebCore/rendering/RenderNamedFlowThread.cpp')
-rw-r--r--Source/WebCore/rendering/RenderNamedFlowThread.cpp288
1 files changed, 288 insertions, 0 deletions
diff --git a/Source/WebCore/rendering/RenderNamedFlowThread.cpp b/Source/WebCore/rendering/RenderNamedFlowThread.cpp
new file mode 100644
index 000000000..088b87248
--- /dev/null
+++ b/Source/WebCore/rendering/RenderNamedFlowThread.cpp
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS IN..0TERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "RenderNamedFlowThread.h"
+
+#include "FlowThreadController.h"
+#include "RenderRegion.h"
+#include "RenderView.h"
+#include "WebKitNamedFlow.h"
+
+namespace WebCore {
+
+RenderNamedFlowThread::RenderNamedFlowThread(Node* node, const AtomicString& name)
+ : RenderFlowThread(node)
+ , m_flowThreadName(name)
+{
+}
+
+const char* RenderNamedFlowThread::renderName() const
+{
+ return "RenderNamedFlowThread";
+}
+
+
+RenderObject* RenderNamedFlowThread::nextRendererForNode(Node* node) const
+{
+ FlowThreadChildList::const_iterator it = m_flowThreadChildList.begin();
+ FlowThreadChildList::const_iterator end = m_flowThreadChildList.end();
+
+ for (; it != end; ++it) {
+ RenderObject* child = *it;
+ ASSERT(child->node());
+ unsigned short position = node->compareDocumentPosition(child->node());
+ if (position & Node::DOCUMENT_POSITION_FOLLOWING)
+ return child;
+ }
+
+ return 0;
+}
+
+RenderObject* RenderNamedFlowThread::previousRendererForNode(Node* node) const
+{
+ if (m_flowThreadChildList.isEmpty())
+ return 0;
+
+ FlowThreadChildList::const_iterator begin = m_flowThreadChildList.begin();
+ FlowThreadChildList::const_iterator end = m_flowThreadChildList.end();
+ FlowThreadChildList::const_iterator it = end;
+
+ do {
+ --it;
+ RenderObject* child = *it;
+ ASSERT(child->node());
+ unsigned short position = node->compareDocumentPosition(child->node());
+ if (position & Node::DOCUMENT_POSITION_PRECEDING)
+ return child;
+ } while (it != begin);
+
+ return 0;
+}
+
+void RenderNamedFlowThread::addFlowChild(RenderObject* newChild, RenderObject* beforeChild)
+{
+ // The child list is used to sort the flow thread's children render objects
+ // based on their corresponding nodes DOM order. The list is needed to avoid searching the whole DOM.
+
+ // Do not add anonymous objects.
+ if (!newChild->node())
+ return;
+
+ if (beforeChild)
+ m_flowThreadChildList.insertBefore(beforeChild, newChild);
+ else
+ m_flowThreadChildList.add(newChild);
+}
+
+void RenderNamedFlowThread::removeFlowChild(RenderObject* child)
+{
+ m_flowThreadChildList.remove(child);
+}
+
+bool RenderNamedFlowThread::dependsOn(RenderNamedFlowThread* otherRenderFlowThread) const
+{
+ if (m_layoutBeforeThreadsSet.contains(otherRenderFlowThread))
+ return true;
+
+ // Recursively traverse the m_layoutBeforeThreadsSet.
+ RenderNamedFlowThreadCountedSet::const_iterator iterator = m_layoutBeforeThreadsSet.begin();
+ RenderNamedFlowThreadCountedSet::const_iterator end = m_layoutBeforeThreadsSet.end();
+ for (; iterator != end; ++iterator) {
+ const RenderNamedFlowThread* beforeFlowThread = (*iterator).first;
+ if (beforeFlowThread->dependsOn(otherRenderFlowThread))
+ return true;
+ }
+
+ return false;
+}
+
+// Compare two regions to determine in which one the content should flow first.
+// The function returns true if the first passed region is "less" than the second passed region.
+// If the first region appears before second region in DOM,
+// the first region is "less" than the second region.
+// If the first region is "less" than the second region, the first region receives content before second region.
+static bool compareRenderRegions(const RenderRegion* firstRegion, const RenderRegion* secondRegion)
+{
+ ASSERT(firstRegion);
+ ASSERT(secondRegion);
+
+ // If the regions have the same region-index, compare their position in dom.
+ ASSERT(firstRegion->node());
+ ASSERT(secondRegion->node());
+
+ unsigned short position = firstRegion->node()->compareDocumentPosition(secondRegion->node());
+ return (position & Node::DOCUMENT_POSITION_FOLLOWING);
+}
+
+void RenderNamedFlowThread::addRegionToThread(RenderRegion* renderRegion)
+{
+ ASSERT(renderRegion);
+ if (m_regionList.isEmpty())
+ m_regionList.add(renderRegion);
+ else {
+ // Find the first region "greater" than renderRegion.
+ RenderRegionList::iterator it = m_regionList.begin();
+ while (it != m_regionList.end() && !compareRenderRegions(renderRegion, *it))
+ ++it;
+ m_regionList.insertBefore(it, renderRegion);
+ }
+
+ ASSERT(!renderRegion->isValid());
+ if (renderRegion->parentNamedFlowThread()) {
+ if (renderRegion->parentNamedFlowThread()->dependsOn(this)) {
+ // Register ourself to get a notification when the state changes.
+ renderRegion->parentNamedFlowThread()->m_observerThreadsSet.add(this);
+ return;
+ }
+
+ addDependencyOnFlowThread(renderRegion->parentNamedFlowThread());
+ }
+
+ renderRegion->setIsValid(true);
+
+ invalidateRegions();
+}
+
+void RenderNamedFlowThread::removeRegionFromThread(RenderRegion* renderRegion)
+{
+ ASSERT(renderRegion);
+ m_regionRangeMap.clear();
+ m_regionList.remove(renderRegion);
+
+ if (renderRegion->parentNamedFlowThread()) {
+ if (!renderRegion->isValid()) {
+ renderRegion->parentNamedFlowThread()->m_observerThreadsSet.remove(this);
+ // No need to invalidate the regions rectangles. The removed region
+ // was not taken into account. Just return here.
+ return;
+ }
+ removeDependencyOnFlowThread(renderRegion->parentNamedFlowThread());
+ }
+
+ invalidateRegions();
+}
+
+
+void RenderNamedFlowThread::checkInvalidRegions()
+{
+ for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
+ RenderRegion* region = *iter;
+ // The only reason a region would be invalid is because it has a parent flow thread.
+ ASSERT(region->isValid() || region->parentNamedFlowThread());
+ if (region->isValid() || region->parentNamedFlowThread()->dependsOn(this))
+ continue;
+
+ region->parentNamedFlowThread()->m_observerThreadsSet.remove(this);
+ addDependencyOnFlowThread(region->parentNamedFlowThread());
+ region->setIsValid(true);
+ invalidateRegions();
+ }
+
+ if (m_observerThreadsSet.isEmpty())
+ return;
+
+ // Notify all the flow threads that were dependent on this flow.
+
+ // Create a copy of the list first. That's because observers might change the list when calling checkInvalidRegions.
+ Vector<RenderNamedFlowThread*> observers;
+ copyToVector(m_observerThreadsSet, observers);
+
+ for (size_t i = 0; i < observers.size(); ++i) {
+ RenderNamedFlowThread* flowThread = observers.at(i);
+ flowThread->checkInvalidRegions();
+ }
+}
+
+void RenderNamedFlowThread::addDependencyOnFlowThread(RenderNamedFlowThread* otherFlowThread)
+{
+ RenderNamedFlowThreadCountedSet::AddResult result = m_layoutBeforeThreadsSet.add(otherFlowThread);
+ if (result.isNewEntry) {
+ // This is the first time we see this dependency. Make sure we recalculate all the dependencies.
+ view()->flowThreadController()->setIsRenderNamedFlowThreadOrderDirty(true);
+ }
+}
+
+void RenderNamedFlowThread::removeDependencyOnFlowThread(RenderNamedFlowThread* otherFlowThread)
+{
+ bool removed = m_layoutBeforeThreadsSet.remove(otherFlowThread);
+ if (removed) {
+ checkInvalidRegions();
+ view()->flowThreadController()->setIsRenderNamedFlowThreadOrderDirty(true);
+ }
+}
+
+void RenderNamedFlowThread::pushDependencies(RenderNamedFlowThreadList& list)
+{
+ for (RenderNamedFlowThreadCountedSet::iterator iter = m_layoutBeforeThreadsSet.begin(); iter != m_layoutBeforeThreadsSet.end(); ++iter) {
+ RenderNamedFlowThread* flowThread = (*iter).first;
+ if (list.contains(flowThread))
+ continue;
+ flowThread->pushDependencies(list);
+ list.add(flowThread);
+ }
+}
+
+WebKitNamedFlow* RenderNamedFlowThread::ensureNamedFlow()
+{
+ if (!m_namedFlow)
+ m_namedFlow = WebKitNamedFlow::create(this);
+
+ return m_namedFlow.get();
+}
+
+// The content nodes list contains those nodes with -webkit-flow-into: flow.
+// An element with display:none should also be listed among those nodes.
+// The list of nodes is ordered.
+void RenderNamedFlowThread::registerNamedFlowContentNode(Node* contentNode)
+{
+ ASSERT(contentNode && contentNode->isElementNode());
+
+ contentNode->setInNamedFlow();
+
+ // Find the first content node following the new content node.
+ for (NamedFlowContentNodes::iterator it = m_contentNodes.begin(); it != m_contentNodes.end(); ++it) {
+ Node* node = *it;
+ unsigned short position = contentNode->compareDocumentPosition(node);
+ if (position & Node::DOCUMENT_POSITION_FOLLOWING) {
+ m_contentNodes.insertBefore(node, contentNode);
+ return;
+ }
+ }
+ m_contentNodes.add(contentNode);
+}
+
+void RenderNamedFlowThread::unregisterNamedFlowContentNode(Node* contentNode)
+{
+ ASSERT(contentNode && contentNode->isElementNode());
+ ASSERT(m_contentNodes.contains(contentNode));
+ ASSERT(contentNode->inNamedFlow());
+
+ contentNode->clearInNamedFlow();
+ m_contentNodes.remove(contentNode);
+
+}
+
+}