blob: 54538924cf42913927e636704cfa287b700c2ed4 [file] [log] [blame]
/*
* Copyright (C) 2015 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. AND ITS CONTRIBUTORS ``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 INC. OR ITS 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
* INTERRUPTION) 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 "RenderTreePosition.h"
#include "ComposedTreeIterator.h"
#include "PseudoElement.h"
#include "RenderInline.h"
#include "RenderObject.h"
#include "ShadowRoot.h"
namespace WebCore {
void RenderTreePosition::computeNextSibling(const Node& node)
{
ASSERT(!node.renderer());
if (m_hasValidNextSibling) {
#if ASSERT_ENABLED
const unsigned oNSquaredAvoidanceLimit = 20;
bool skipAssert = m_parent.isRenderView() || ++m_assertionLimitCounter > oNSquaredAvoidanceLimit;
ASSERT(skipAssert || nextSiblingRenderer(node) == m_nextSibling);
#endif
return;
}
m_nextSibling = makeWeakPtr(nextSiblingRenderer(node));
m_hasValidNextSibling = true;
}
void RenderTreePosition::invalidateNextSibling(const RenderObject& siblingRenderer)
{
if (!m_hasValidNextSibling)
return;
if (m_nextSibling == &siblingRenderer)
m_hasValidNextSibling = false;
}
RenderObject* RenderTreePosition::nextSiblingRenderer(const Node& node) const
{
ASSERT(!node.renderer());
auto* parentElement = m_parent.element();
if (!parentElement)
return nullptr;
// FIXME: PlugingReplacement shadow trees are very wrong.
if (parentElement == &node)
return nullptr;
Vector<Element*, 30> elementStack;
// In the common case ancestor == parentElement immediately and this just pushes parentElement into stack.
auto* ancestor = node.parentElementInComposedTree();
while (true) {
elementStack.append(ancestor);
if (ancestor == parentElement)
break;
ancestor = ancestor->parentElementInComposedTree();
ASSERT(ancestor);
}
elementStack.reverse();
auto composedDescendants = composedTreeDescendants(*parentElement);
auto initializeIteratorConsideringPseudoElements = [&] {
if (is<PseudoElement>(node)) {
auto* host = downcast<PseudoElement>(node).hostElement();
if (node.isBeforePseudoElement()) {
if (host != parentElement)
return composedDescendants.at(*host).traverseNext();
return composedDescendants.begin();
}
ASSERT(node.isAfterPseudoElement());
elementStack.removeLast();
if (host != parentElement)
return composedDescendants.at(*host).traverseNextSkippingChildren();
return composedDescendants.end();
}
return composedDescendants.at(node).traverseNextSkippingChildren();
};
auto pushCheckingForAfterPseudoElementRenderer = [&] (Element& element) -> RenderElement* {
ASSERT(!element.isPseudoElement());
if (auto* before = element.beforePseudoElement()) {
if (auto* renderer = before->renderer())
return renderer;
}
elementStack.append(&element);
return nullptr;
};
auto popCheckingForAfterPseudoElementRenderers = [&] (unsigned iteratorDepthToMatch) -> RenderElement* {
while (elementStack.size() > iteratorDepthToMatch) {
auto& element = *elementStack.takeLast();
if (auto* after = element.afterPseudoElement()) {
if (auto* renderer = after->renderer())
return renderer;
}
}
return nullptr;
};
auto it = initializeIteratorConsideringPseudoElements();
auto end = composedDescendants.end();
while (it != end) {
if (auto* renderer = popCheckingForAfterPseudoElementRenderers(it.depth()))
return renderer;
if (auto* renderer = it->renderer())
return renderer;
if (is<Element>(*it)) {
auto& element = downcast<Element>(*it);
if (element.hasDisplayContents()) {
if (auto* renderer = pushCheckingForAfterPseudoElementRenderer(element))
return renderer;
it.traverseNext();
continue;
}
}
it.traverseNextSkippingChildren();
}
return popCheckingForAfterPseudoElementRenderers(0);
}
}