Backed out changeset bc3d643c4973 (bug 1395973) for failing browser-chrome's toolkit/content/tests/browser/browser_bug982298.js and toolkit/modules/tests/browser/browser_Finder_hidden_textarea.js. r=backout on a CLOSED TREE

This commit is contained in:
Sebastian Hengst 2017-09-14 14:48:50 +02:00
Родитель e089501d36
Коммит 16e918b99e
1 изменённых файлов: 424 добавлений и 49 удалений

Просмотреть файл

@ -144,19 +144,28 @@ protected:
// Recursively get the deepest first/last child of aRoot. This will return
// aRoot itself if it has no children.
nsINode* GetDeepFirstChild(nsINode* aRoot);
nsIContent* GetDeepFirstChild(nsIContent* aRoot);
nsINode* GetDeepLastChild(nsINode* aRoot);
nsIContent* GetDeepLastChild(nsIContent* aRoot);
nsINode* GetDeepFirstChild(nsINode* aRoot,
nsTArray<int32_t>* aIndexes = nullptr);
nsIContent* GetDeepFirstChild(nsIContent* aRoot,
nsTArray<int32_t>* aIndexes = nullptr);
nsINode* GetDeepLastChild(nsINode* aRoot,
nsTArray<int32_t>* aIndexes = nullptr);
nsIContent* GetDeepLastChild(nsIContent* aRoot,
nsTArray<int32_t>* aIndexes = nullptr);
// Get the next/previous sibling of aNode, or its parent's, or grandparent's,
// etc. Returns null if aNode and all its ancestors have no next/previous
// sibling.
nsIContent* GetNextSibling(nsINode* aNode);
nsIContent* GetPrevSibling(nsINode* aNode);
nsIContent* GetNextSibling(nsINode* aNode,
nsTArray<int32_t>* aIndexes = nullptr);
nsIContent* GetPrevSibling(nsINode* aNode,
nsTArray<int32_t>* aIndexes = nullptr);
nsINode* NextNode(nsINode* aNode);
nsINode* PrevNode(nsINode* aNode);
nsINode* NextNode(nsINode* aNode, nsTArray<int32_t>* aIndexes = nullptr);
nsINode* PrevNode(nsINode* aNode, nsTArray<int32_t>* aIndexes = nullptr);
// WARNING: This function is expensive
nsresult RebuildIndexStack();
void MakeEmpty();
@ -167,6 +176,30 @@ protected:
nsCOMPtr<nsINode> mLast;
nsCOMPtr<nsINode> mCommonParent;
// used by nsContentIterator to cache indices
AutoTArray<int32_t, 8> mIndexes;
// used by nsSubtreeIterator to cache indices. Why put them in the base
// class? Because otherwise I have to duplicate the routines GetNextSibling
// etc across both classes, with slight variations for caching. Or
// alternately, create a base class for the cache itself and have all the
// cache manipulation go through a vptr. I think this is the best space and
// speed combo, even though it's ugly.
int32_t mCachedIndex;
// another note about mCachedIndex: why should the subtree iterator use a
// trivial cached index instead of the mre robust array of indicies (which is
// what the basic content iterator uses)? The reason is that subtree
// iterators do not do much transitioning between parents and children. They
// tend to stay at the same level. In fact, you can prove (though I won't
// attempt it here) that they change levels at most n+m times, where n is the
// height of the parent hierarchy from the range start to the common
// ancestor, and m is the the height of the parent hierarchy from the range
// end to the common ancestor. If we used the index array, we would pay the
// price up front for n, and then pay the cost for m on the fly later on.
// With the simple cache, we only "pay as we go". Either way, we call
// IndexOf() once for each change of level in the hierarchy. Since a trivial
// index is much simpler, we use it for the subtree iterator.
bool mIsDone;
bool mPre;
@ -232,9 +265,10 @@ nsContentIterator::LastRelease()
* constructor/destructor
******************************************************/
nsContentIterator::nsContentIterator(bool aPre)
: mIsDone(false)
, mPre(aPre)
nsContentIterator::nsContentIterator(bool aPre) :
// don't need to explicitly initialize |nsCOMPtr|s, they will automatically
// be nullptr
mCachedIndex(0), mIsDone(false), mPre(aPre)
{
}
@ -257,6 +291,7 @@ nsContentIterator::Init(nsINode* aRoot)
}
mIsDone = false;
mIndexes.Clear();
if (mPre) {
mFirst = aRoot;
@ -270,6 +305,7 @@ nsContentIterator::Init(nsINode* aRoot)
mCommonParent = aRoot;
mCurNode = mFirst;
RebuildIndexStack();
return NS_OK;
}
@ -340,6 +376,8 @@ nsContentIterator::InitInternal(nsINode* aStartContainer, uint32_t aStartOffset,
mLast = mFirst;
mCurNode = mFirst;
DebugOnly<nsresult> rv = RebuildIndexStack();
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "RebuildIndexStack failed");
return NS_OK;
}
}
@ -504,6 +542,13 @@ nsContentIterator::InitInternal(nsINode* aStartContainer, uint32_t aStartOffset,
mCurNode = mFirst;
mIsDone = !mCurNode;
if (!mCurNode) {
mIndexes.Clear();
} else {
DebugOnly<nsresult> rv = RebuildIndexStack();
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "RebuildIndexStack failed");
}
return NS_OK;
}
@ -511,6 +556,36 @@ nsContentIterator::InitInternal(nsINode* aStartContainer, uint32_t aStartOffset,
/******************************************************
* Helper routines
******************************************************/
// WARNING: This function is expensive
nsresult
nsContentIterator::RebuildIndexStack()
{
// Make sure we start at the right indexes on the stack! Build array up
// to common parent of start and end. Perhaps it's too many entries, but
// that's far better than too few.
nsINode* parent;
nsINode* current;
mIndexes.Clear();
current = mCurNode;
if (!current) {
return NS_OK;
}
while (current != mCommonParent) {
parent = current->GetParentNode();
if (NS_WARN_IF(!parent)) {
return NS_ERROR_FAILURE;
}
mIndexes.InsertElementAt(0, parent->IndexOf(current));
current = parent;
}
return NS_OK;
}
void
nsContentIterator::MakeEmpty()
@ -520,20 +595,28 @@ nsContentIterator::MakeEmpty()
mLast = nullptr;
mCommonParent = nullptr;
mIsDone = true;
mIndexes.Clear();
}
nsINode*
nsContentIterator::GetDeepFirstChild(nsINode* aRoot)
nsContentIterator::GetDeepFirstChild(nsINode* aRoot,
nsTArray<int32_t>* aIndexes)
{
if (NS_WARN_IF(!aRoot) || !aRoot->HasChildren()) {
return aRoot;
}
return GetDeepFirstChild(aRoot->GetFirstChild());
// We can't pass aRoot itself to the full GetDeepFirstChild, because that
// will only take nsIContent and aRoot might be a document. Pass aRoot's
// child, but be sure to preserve aIndexes.
if (aIndexes) {
aIndexes->AppendElement(0);
}
return GetDeepFirstChild(aRoot->GetFirstChild(), aIndexes);
}
nsIContent*
nsContentIterator::GetDeepFirstChild(nsIContent* aRoot)
nsContentIterator::GetDeepFirstChild(nsIContent* aRoot,
nsTArray<int32_t>* aIndexes)
{
if (NS_WARN_IF(!aRoot)) {
return nullptr;
@ -543,6 +626,10 @@ nsContentIterator::GetDeepFirstChild(nsIContent* aRoot)
nsIContent* child = node->GetFirstChild();
while (child) {
if (aIndexes) {
// Add this node to the stack of indexes
aIndexes->AppendElement(0);
}
node = child;
child = node->GetFirstChild();
}
@ -551,70 +638,164 @@ nsContentIterator::GetDeepFirstChild(nsIContent* aRoot)
}
nsINode*
nsContentIterator::GetDeepLastChild(nsINode* aRoot)
nsContentIterator::GetDeepLastChild(nsINode* aRoot,
nsTArray<int32_t>* aIndexes)
{
if (NS_WARN_IF(!aRoot) || !aRoot->HasChildren()) {
return aRoot;
}
return GetDeepLastChild(aRoot->GetLastChild());
// We can't pass aRoot itself to the full GetDeepLastChild, because that will
// only take nsIContent and aRoot might be a document. Pass aRoot's child,
// but be sure to preserve aIndexes.
if (aIndexes) {
aIndexes->AppendElement(aRoot->GetChildCount() - 1);
}
return GetDeepLastChild(aRoot->GetLastChild(), aIndexes);
}
nsIContent*
nsContentIterator::GetDeepLastChild(nsIContent* aRoot)
nsContentIterator::GetDeepLastChild(nsIContent* aRoot,
nsTArray<int32_t>* aIndexes)
{
if (NS_WARN_IF(!aRoot)) {
return nullptr;
}
nsIContent* node = aRoot;
while (node->HasChildren()) {
nsIContent* child = node->GetLastChild();
int32_t numChildren = node->GetChildCount();
while (numChildren) {
nsIContent* child = node->GetChildAt(--numChildren);
if (aIndexes) {
// Add this node to the stack of indexes
aIndexes->AppendElement(numChildren);
}
numChildren = child->GetChildCount();
node = child;
}
return node;
}
// Get the next sibling, or parent's next sibling, or grandpa's next sibling...
nsIContent*
nsContentIterator::GetNextSibling(nsINode* aNode)
nsContentIterator::GetNextSibling(nsINode* aNode,
nsTArray<int32_t>* aIndexes)
{
if (NS_WARN_IF(!aNode)) {
return nullptr;
}
if (aNode->GetNextSibling()) {
return aNode->GetNextSibling();
}
nsINode* parent = aNode->GetParentNode();
if (NS_WARN_IF(!parent)) {
return nullptr;
}
return GetNextSibling(parent);
int32_t indx = 0;
NS_ASSERTION(!aIndexes || !aIndexes->IsEmpty(),
"ContentIterator stack underflow");
if (aIndexes && !aIndexes->IsEmpty()) {
// use the last entry on the Indexes array for the current index
indx = (*aIndexes)[aIndexes->Length()-1];
} else {
indx = mCachedIndex;
}
NS_WARNING_ASSERTION(indx >= 0, "bad indx");
// reverify that the index of the current node hasn't changed.
// not super cheap, but a lot cheaper than IndexOf(), and still O(1).
// ignore result this time - the index may now be out of range.
nsIContent* sib = parent->GetChildAt(indx);
if (sib != aNode) {
// someone changed our index - find the new index the painful way
indx = parent->IndexOf(aNode);
NS_WARNING_ASSERTION(indx >= 0, "bad indx");
}
// indx is now canonically correct
if ((sib = parent->GetChildAt(++indx))) {
// update index cache
if (aIndexes && !aIndexes->IsEmpty()) {
aIndexes->ElementAt(aIndexes->Length()-1) = indx;
} else {
mCachedIndex = indx;
}
} else {
if (parent != mCommonParent) {
if (aIndexes) {
// pop node off the stack, go up one level and return parent or fail.
// Don't leave the index empty, especially if we're
// returning nullptr. This confuses other parts of the code.
if (aIndexes->Length() > 1) {
aIndexes->RemoveElementAt(aIndexes->Length()-1);
}
}
}
// ok to leave cache out of date here if parent == mCommonParent?
sib = GetNextSibling(parent, aIndexes);
}
return sib;
}
// Get the prev sibling, or parent's prev sibling, or grandpa's prev sibling...
nsIContent*
nsContentIterator::GetPrevSibling(nsINode* aNode)
nsContentIterator::GetPrevSibling(nsINode* aNode,
nsTArray<int32_t>* aIndexes)
{
if (NS_WARN_IF(!aNode)) {
return nullptr;
}
if (aNode->GetPreviousSibling()) {
return aNode->GetPreviousSibling();
}
nsINode* parent = aNode->GetParentNode();
if (NS_WARN_IF(!parent)) {
return nullptr;
}
return GetPrevSibling(parent);
int32_t indx = 0;
NS_ASSERTION(!aIndexes || !aIndexes->IsEmpty(),
"ContentIterator stack underflow");
if (aIndexes && !aIndexes->IsEmpty()) {
// use the last entry on the Indexes array for the current index
indx = (*aIndexes)[aIndexes->Length()-1];
} else {
indx = mCachedIndex;
}
// reverify that the index of the current node hasn't changed
// ignore result this time - the index may now be out of range.
nsIContent* sib = parent->GetChildAt(indx);
if (sib != aNode) {
// someone changed our index - find the new index the painful way
indx = parent->IndexOf(aNode);
NS_WARNING_ASSERTION(indx >= 0, "bad indx");
}
// indx is now canonically correct
if (indx > 0 && (sib = parent->GetChildAt(--indx))) {
// update index cache
if (aIndexes && !aIndexes->IsEmpty()) {
aIndexes->ElementAt(aIndexes->Length()-1) = indx;
} else {
mCachedIndex = indx;
}
} else if (parent != mCommonParent) {
if (aIndexes && !aIndexes->IsEmpty()) {
// pop node off the stack, go up one level and try again.
aIndexes->RemoveElementAt(aIndexes->Length()-1);
}
return GetPrevSibling(parent, aIndexes);
}
return sib;
}
nsINode*
nsContentIterator::NextNode(nsINode* aNode)
nsContentIterator::NextNode(nsINode* aNode, nsTArray<int32_t>* aIndexes)
{
nsINode* node = aNode;
@ -625,11 +806,19 @@ nsContentIterator::NextNode(nsINode* aNode)
nsIContent* firstChild = node->GetFirstChild();
MOZ_ASSERT(firstChild);
// update cache
if (aIndexes) {
// push an entry on the index stack
aIndexes->AppendElement(0);
} else {
mCachedIndex = 0;
}
return firstChild;
}
// else next sibling is next
return GetNextSibling(node);
return GetNextSibling(node, aIndexes);
}
// post-order
@ -639,17 +828,64 @@ nsContentIterator::NextNode(nsINode* aNode)
mIsDone = true;
return node;
}
nsIContent* sibling = node->GetNextSibling();
nsIContent* sibling = nullptr;
int32_t indx = 0;
// get the cached index
NS_ASSERTION(!aIndexes || !aIndexes->IsEmpty(),
"ContentIterator stack underflow");
if (aIndexes && !aIndexes->IsEmpty()) {
// use the last entry on the Indexes array for the current index
indx = (*aIndexes)[aIndexes->Length()-1];
} else {
indx = mCachedIndex;
}
// reverify that the index of the current node hasn't changed. not super
// cheap, but a lot cheaper than IndexOf(), and still O(1). ignore result
// this time - the index may now be out of range.
if (indx >= 0) {
sibling = parent->GetChildAt(indx);
}
if (sibling != node) {
// someone changed our index - find the new index the painful way
indx = parent->IndexOf(node);
NS_WARNING_ASSERTION(indx >= 0, "bad indx");
}
// indx is now canonically correct
sibling = parent->GetChildAt(++indx);
if (sibling) {
// update cache
if (aIndexes && !aIndexes->IsEmpty()) {
// replace an entry on the index stack
aIndexes->ElementAt(aIndexes->Length()-1) = indx;
} else {
mCachedIndex = indx;
}
// next node is sibling's "deep left" child
return GetDeepFirstChild(sibling);
return GetDeepFirstChild(sibling, aIndexes);
}
// else it's the parent, update cache
if (aIndexes) {
// Pop an entry off the index stack. Don't leave the index empty,
// especially if we're returning nullptr. This confuses other parts of the
// code.
if (aIndexes->Length() > 1) {
aIndexes->RemoveElementAt(aIndexes->Length()-1);
}
} else {
// this might be wrong, but we are better off guessing
mCachedIndex = 0;
}
return parent;
}
nsINode*
nsContentIterator::PrevNode(nsINode* aNode)
nsContentIterator::PrevNode(nsINode* aNode, nsTArray<int32_t>* aIndexes)
{
nsINode* node = aNode;
@ -661,22 +897,81 @@ nsContentIterator::PrevNode(nsINode* aNode)
mIsDone = true;
return aNode;
}
nsIContent* sibling = nullptr;
int32_t indx = 0;
nsIContent* sibling = node->GetPreviousSibling();
if (sibling) {
return GetDeepLastChild(sibling);
// get the cached index
NS_ASSERTION(!aIndexes || !aIndexes->IsEmpty(),
"ContentIterator stack underflow");
if (aIndexes && !aIndexes->IsEmpty()) {
// use the last entry on the Indexes array for the current index
indx = (*aIndexes)[aIndexes->Length()-1];
} else {
indx = mCachedIndex;
}
// reverify that the index of the current node hasn't changed. not super
// cheap, but a lot cheaper than IndexOf(), and still O(1). ignore result
// this time - the index may now be out of range.
if (indx >= 0) {
sibling = parent->GetChildAt(indx);
NS_WARNING_ASSERTION(sibling, "GetChildAt returned null");
}
if (sibling != node) {
// someone changed our index - find the new index the painful way
indx = parent->IndexOf(node);
NS_WARNING_ASSERTION(indx >= 0, "bad indx");
}
// indx is now canonically correct
if (indx && (sibling = parent->GetChildAt(--indx))) {
// update cache
if (aIndexes && !aIndexes->IsEmpty()) {
// replace an entry on the index stack
aIndexes->ElementAt(aIndexes->Length()-1) = indx;
} else {
mCachedIndex = indx;
}
// prev node is sibling's "deep right" child
return GetDeepLastChild(sibling, aIndexes);
}
// else it's the parent, update cache
if (aIndexes && !aIndexes->IsEmpty()) {
// pop an entry off the index stack
aIndexes->RemoveElementAt(aIndexes->Length()-1);
} else {
// this might be wrong, but we are better off guessing
mCachedIndex = 0;
}
return parent;
}
// post-order
if (node->HasChildren()) {
return node->GetLastChild();
int32_t numChildren = node->GetChildCount();
NS_WARNING_ASSERTION(numChildren >= 0, "no children");
// if it has children then prev node is last child
if (numChildren) {
nsIContent* lastChild = node->GetLastChild();
NS_WARNING_ASSERTION(lastChild, "GetLastChild returned null");
numChildren--;
// update cache
if (aIndexes) {
// push an entry on the index stack
aIndexes->AppendElement(numChildren);
} else {
mCachedIndex = numChildren;
}
return lastChild;
}
// else prev sibling is previous
return GetPrevSibling(node);
return GetPrevSibling(node, aIndexes);
}
/******************************************************
@ -724,7 +1019,7 @@ nsContentIterator::Next()
return;
}
mCurNode = NextNode(mCurNode);
mCurNode = NextNode(mCurNode, &mIndexes);
}
@ -740,7 +1035,7 @@ nsContentIterator::Prev()
return;
}
mCurNode = PrevNode(mCurNode);
mCurNode = PrevNode(mCurNode, &mIndexes);
}
@ -750,6 +1045,7 @@ nsContentIterator::IsDone()
return mIsDone;
}
// Keeping arrays of indexes for the stack of nodes makes PositionAt
// interesting...
nsresult
@ -759,12 +1055,15 @@ nsContentIterator::PositionAt(nsINode* aCurNode)
return NS_ERROR_NULL_POINTER;
}
nsINode* newCurNode = aCurNode;
nsINode* tempNode = mCurNode;
mCurNode = aCurNode;
// take an early out if this doesn't actually change the position
if (mCurNode == aCurNode) {
mIsDone = false;
if (mCurNode == tempNode) {
mIsDone = false; // paranoia
return NS_OK;
}
mCurNode = aCurNode;
// Check to see if the node falls within the traversal range.
@ -817,6 +1116,82 @@ nsContentIterator::PositionAt(nsINode* aCurNode)
return NS_ERROR_FAILURE;
}
// We can be at ANY node in the sequence. Need to regenerate the array of
// indexes back to the root or common parent!
AutoTArray<nsINode*, 8> oldParentStack;
AutoTArray<int32_t, 8> newIndexes;
// Get a list of the parents up to the root, then compare the new node with
// entries in that array until we find a match (lowest common ancestor). If
// no match, use IndexOf, take the parent, and repeat. This avoids using
// IndexOf() N times on possibly large arrays. We still end up doing it a
// fair bit. It's better to use Clone() if possible.
// we know the depth we're down (though we may not have started at the top).
oldParentStack.SetCapacity(mIndexes.Length() + 1);
// We want to loop mIndexes.Length() + 1 times here, because we want to make
// sure we include mCommonParent in the oldParentStack, for use in the next
// for loop, and mIndexes only has entries for nodes from tempNode up through
// an ancestor of tempNode that's a child of mCommonParent.
for (int32_t i = mIndexes.Length() + 1; i > 0 && tempNode; i--) {
// Insert at head since we're walking up
oldParentStack.InsertElementAt(0, tempNode);
nsINode* parent = tempNode->GetParentNode();
if (NS_WARN_IF(!parent)) {
// this node has no parent, and thus no index
break;
}
if (parent == mCurNode) {
// The position was moved to a parent of the current position. All we
// need to do is drop some indexes. Shortcut here.
mIndexes.RemoveElementsAt(mIndexes.Length() - oldParentStack.Length(),
oldParentStack.Length());
mIsDone = false;
return NS_OK;
}
tempNode = parent;
}
// Ok. We have the array of old parents. Look for a match.
while (newCurNode) {
nsINode* parent = newCurNode->GetParentNode();
if (NS_WARN_IF(!parent)) {
// this node has no parent, and thus no index
break;
}
int32_t indx = parent->IndexOf(newCurNode);
NS_WARNING_ASSERTION(indx >= 0, "bad indx");
// insert at the head!
newIndexes.InsertElementAt(0, indx);
// look to see if the parent is in the stack
indx = oldParentStack.IndexOf(parent);
if (indx >= 0) {
// ok, the parent IS on the old stack! Rework things. We want
// newIndexes to replace all nodes equal to or below the match. Note
// that index oldParentStack.Length() - 1 is the last node, which is one
// BELOW the last index in the mIndexes stack. In other words, we want
// to remove elements starting at index (indx + 1).
int32_t numToDrop = oldParentStack.Length() - (1 + indx);
if (numToDrop > 0) {
mIndexes.RemoveElementsAt(mIndexes.Length() - numToDrop, numToDrop);
}
mIndexes.AppendElements(newIndexes);
break;
}
newCurNode = parent;
}
// phew!
mIsDone = false;
return NS_OK;
}