// 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->GetNodeParent(); if (!parent) return NS_ERROR_FAILURE; mIndexes.InsertElementAt(0, parent->IndexOf(current)); current = parent; } return NS_OK; }
nsIRDFResource* RDFContentSinkImpl::GetContextElement(PRInt32 ancestor /* = 0 */) { if ((nsnull == mContextStack) || (PRUint32(ancestor) >= mContextStack->Length())) { return nsnull; } return mContextStack->ElementAt( mContextStack->Length()-ancestor-1).mResource; }
nsIRDFResource* RDFContentSinkImpl::GetContextElement(int32_t ancestor /* = 0 */) { if ((nullptr == mContextStack) || (uint32_t(ancestor) >= mContextStack->Length())) { return nullptr; } return mContextStack->ElementAt( mContextStack->Length()-ancestor-1).mResource; }
bool nsXBLStreamListener::HasRequest(nsIURI* aURI, nsIContent* aElt) { // XXX Could be more efficient. uint32_t count = mBindingRequests.Length(); for (uint32_t i = 0; i < count; i++) { nsXBLBindingRequest* req = mBindingRequests.ElementAt(i); bool eq; if (req->mBoundElement == aElt && NS_SUCCEEDED(req->mBindingURI->Equals(aURI, &eq)) && eq) return true; } return false; }
nsresult nsContentIterator::Init(nsINode* aRoot) { if (!aRoot) return NS_ERROR_NULL_POINTER; mIsDone = PR_FALSE; mIndexes.Clear(); if (mPre) { mFirst = aRoot; mLast = GetDeepLastChild(aRoot, nsnull); } else { mFirst = GetDeepFirstChild(aRoot, nsnull); mLast = aRoot; } mCommonParent = aRoot; mCurNode = mFirst; RebuildIndexStack(); return NS_OK; }
void nsContentIterator::MakeEmpty() { mCurNode = nsnull; mFirst = nsnull; mLast = nsnull; mCommonParent = nsnull; mIsDone = PR_TRUE; mIndexes.Clear(); }
void nsContentSubtreeIterator::Next() { if (mIsDone || !mCurNode) return; if (mCurNode == mLast) { mIsDone = PR_TRUE; return; } nsINode *nextNode = GetNextSibling(mCurNode, nsnull); NS_ASSERTION(nextNode, "No next sibling!?! This could mean deadlock!"); /* nextNode = GetDeepFirstChild(nextNode); return GetTopAncestorInRange(nextNode, address_of(mCurNode)); */ PRInt32 i = mEndNodes.IndexOf(nextNode); while (i != -1) { // as long as we are finding ancestors of the endpoint of the range, // dive down into their children nextNode = nextNode->GetChildAt(0); NS_ASSERTION(nextNode, "Iterator error, expected a child node!"); // should be impossible to get a null pointer. If we went all the way // down the child chain to the bottom without finding an interior node, // then the previous node should have been the last, which was // was tested at top of routine. i = mEndNodes.IndexOf(nextNode); } mCurNode = nextNode; // This shouldn't be needed, but since our selection code can put us // in a situation where mLast is in generated content, we need this // to stop the iterator when we've walked past past the last node! mIsDone = mCurNode == nsnull; return; }
nsresult RDFContentSinkImpl::PopContext(nsIRDFResource *&aResource, RDFContentSinkState &aState, RDFContentSinkParseMode &aParseMode) { if ((nsnull == mContextStack) || (mContextStack->IsEmpty())) { return NS_ERROR_NULL_POINTER; } PRUint32 i = mContextStack->Length() - 1; RDFContextStackElement &e = mContextStack->ElementAt(i); aResource = e.mResource; NS_IF_ADDREF(aResource); aState = e.mState; aParseMode = e.mParseMode; mContextStack->RemoveElementAt(i); return NS_OK; }
PRInt32 RDFContentSinkImpl::PushContext(nsIRDFResource *aResource, RDFContentSinkState aState, RDFContentSinkParseMode aParseMode) { if (! mContextStack) { mContextStack = new nsAutoTArray<RDFContextStackElement, 8>(); if (! mContextStack) return 0; } RDFContextStackElement* e = mContextStack->AppendElement(); if (! e) return mContextStack->Length(); e->mResource = aResource; e->mState = aState; e->mParseMode = aParseMode; return mContextStack->Length(); }
HTMLContentSink::~HTMLContentSink() { if (mNotificationTimer) { mNotificationTimer->Cancel(); } int32_t numContexts = mContextStack.Length(); if (mCurrentContext == mHeadContext && numContexts > 0) { // Pop off the second html context if it's not done earlier mContextStack.RemoveElementAt(--numContexts); } int32_t i; for (i = 0; i < numContexts; i++) { SinkContext* sc = mContextStack.ElementAt(i); if (sc) { sc->End(); if (sc == mCurrentContext) { mCurrentContext = nullptr; } delete sc; } } if (mCurrentContext == mHeadContext) { mCurrentContext = nullptr; } delete mCurrentContext; delete mHeadContext; for (i = 0; uint32_t(i) < ArrayLength(mNodeInfoCache); ++i) { NS_IF_RELEASE(mNodeInfoCache[i]); } }
NS_IMETHODIMP RDFContentSinkImpl::HandleEndElement(const PRUnichar *aName) { FlushText(); nsIRDFResource* resource; if (NS_FAILED(PopContext(resource, mState, mParseMode))) { // XXX parser didn't catch unmatched tags? #ifdef PR_LOGGING if (PR_LOG_TEST(gLog, PR_LOG_WARNING)) { nsAutoString tagStr(aName); char* tagCStr = ToNewCString(tagStr); PR_LogPrint ("rdfxml: extra close tag '%s' at line %d", tagCStr, 0/*XXX fix me */); NS_Free(tagCStr); } #endif return NS_ERROR_UNEXPECTED; // XXX } // If we've just popped a member or property element, _now_ is the // time to add that element to the graph. switch (mState) { case eRDFContentSinkState_InMemberElement: { nsCOMPtr<nsIRDFContainer> container; NS_NewRDFContainer(getter_AddRefs(container)); container->Init(mDataSource, GetContextElement(1)); container->AppendElement(resource); } break; case eRDFContentSinkState_InPropertyElement: { mDataSource->Assert(GetContextElement(1), GetContextElement(0), resource, true); } break; default: break; } if (mContextStack->IsEmpty()) mState = eRDFContentSinkState_InEpilog; NS_IF_RELEASE(resource); return NS_OK; }
// Keeping arrays of indexes for the stack of nodes makes PositionAt // interesting... nsresult nsContentIterator::PositionAt(nsINode* aCurNode) { if (!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 == tempNode) { mIsDone = PR_FALSE; // paranoia return NS_OK; } // Check to see if the node falls within the traversal range. nsINode* firstNode = mFirst; nsINode* lastNode = mLast; PRInt32 firstOffset=0, lastOffset=0; if (firstNode && lastNode) { if (mPre) { firstNode = NodeToParentOffset(mFirst, &firstOffset); if (lastNode->GetChildCount()) lastOffset = 0; else { lastNode = NodeToParentOffset(mLast, &lastOffset); ++lastOffset; } } else { PRUint32 numChildren = firstNode->GetChildCount(); if (numChildren) firstOffset = numChildren; else firstNode = NodeToParentOffset(mFirst, &firstOffset); lastNode = NodeToParentOffset(mLast, &lastOffset); ++lastOffset; } } // The end positions are always in the range even if it has no parent. // We need to allow that or 'iter->Init(root)' would assert in Last() // or First() for example, bug 327694. if (mFirst != mCurNode && mLast != mCurNode && (!firstNode || !lastNode || !NodeIsInTraversalRange(mCurNode, mPre, firstNode, firstOffset, lastNode, lastOffset))) { mIsDone = PR_TRUE; 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! nsAutoTArray<nsINode*, 8> oldParentStack; nsAutoTArray<PRInt32, 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). if (!oldParentStack.SetCapacity(mIndexes.Length()+1)) return NS_ERROR_FAILURE; // 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 (PRInt32 i = mIndexes.Length()+1; i > 0 && tempNode; i--) { // Insert at head since we're walking up oldParentStack.InsertElementAt(0, tempNode); nsINode *parent = tempNode->GetNodeParent(); 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 = PR_FALSE; return NS_OK; } tempNode = parent; } // Ok. We have the array of old parents. Look for a match. while (newCurNode) { nsINode *parent = newCurNode->GetNodeParent(); if (!parent) // this node has no parent, and thus no index break; PRInt32 indx = parent->IndexOf(newCurNode); // 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). PRInt32 numToDrop = oldParentStack.Length()-(1+indx); if (numToDrop > 0) mIndexes.RemoveElementsAt(mIndexes.Length() - numToDrop, numToDrop); mIndexes.AppendElements(newIndexes); break; } newCurNode = parent; } // phew! mIsDone = PR_FALSE; return NS_OK; }
nsresult nsContentIterator::Init(nsIRange* aRange) { NS_ENSURE_ARG_POINTER(aRange); mIsDone = PR_FALSE; // get common content parent mCommonParent = aRange->GetCommonAncestor(); NS_ENSURE_TRUE(mCommonParent, NS_ERROR_FAILURE); // get the start node and offset PRInt32 startIndx = aRange->StartOffset(); nsINode* startNode = aRange->GetStartParent(); NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE); // get the end node and offset PRInt32 endIndx = aRange->EndOffset(); nsINode* endNode = aRange->GetEndParent(); NS_ENSURE_TRUE(endNode, NS_ERROR_FAILURE); PRBool startIsData = startNode->IsNodeOfType(nsINode::eDATA_NODE); // short circuit when start node == end node if (startNode == endNode) { // Check to see if we have a collapsed range, if so, // there is nothing to iterate over. // // XXX: CharacterDataNodes (text nodes) are currently an exception, // since we always want to be able to iterate text nodes at // the end points of a range. if (!startIsData && startIndx == endIndx) { MakeEmpty(); return NS_OK; } if (startIsData) { // It's a textnode. NS_ASSERTION(startNode->IsNodeOfType(nsINode::eCONTENT), "Data node that's not content?"); mFirst = static_cast<nsIContent*>(startNode); mLast = mFirst; mCurNode = mFirst; RebuildIndexStack(); return NS_OK; } } // Find first node in range. nsIContent *cChild = nsnull; if (!startIsData && NodeHasChildren(startNode)) cChild = startNode->GetChildAt(startIndx); if (!cChild) // no children, must be a text node { // XXXbz no children might also just mean no children. So I'm not // sure what that comment above is talking about. if (mPre) { // XXX: In the future, if start offset is after the last // character in the cdata node, should we set mFirst to // the next sibling? if (!startIsData) { mFirst = GetNextSibling(startNode, nsnull); // Does mFirst node really intersect the range? // The range could be 'degenerate', ie not collapsed // but still contain no content. if (mFirst && !NodeIsInTraversalRange(mFirst, mPre, startNode, startIndx, endNode, endIndx)) { mFirst = nsnull; } } else { NS_ASSERTION(startNode->IsNodeOfType(nsINode::eCONTENT), "Data node that's not content?"); mFirst = static_cast<nsIContent*>(startNode); } } else { // post-order if (startNode->IsNodeOfType(nsINode::eCONTENT)) { mFirst = static_cast<nsIContent*>(startNode); } else { // What else can we do? mFirst = nsnull; } } } else { if (mPre) mFirst = cChild; else // post-order { mFirst = GetDeepFirstChild(cChild, nsnull); // Does mFirst node really intersect the range? // The range could be 'degenerate', ie not collapsed // but still contain no content. if (mFirst && !NodeIsInTraversalRange(mFirst, mPre, startNode, startIndx, endNode, endIndx)) mFirst = nsnull; } } // Find last node in range. PRBool endIsData = endNode->IsNodeOfType(nsINode::eDATA_NODE); if (endIsData || !NodeHasChildren(endNode) || endIndx == 0) { if (mPre) { if (endNode->IsNodeOfType(nsINode::eCONTENT)) { mLast = static_cast<nsIContent*>(endNode); } else { // Not much else to do here... mLast = nsnull; } } else // post-order { // XXX: In the future, if end offset is before the first // character in the cdata node, should we set mLast to // the prev sibling? if (!endIsData) { mLast = GetPrevSibling(endNode, nsnull); if (!NodeIsInTraversalRange(mLast, mPre, startNode, startIndx, endNode, endIndx)) mLast = nsnull; } else { NS_ASSERTION(endNode->IsNodeOfType(nsINode::eCONTENT), "Data node that's not content?"); mLast = static_cast<nsIContent*>(endNode); } } } else { PRInt32 indx = endIndx; cChild = endNode->GetChildAt(--indx); if (!cChild) // No child at offset! { NS_NOTREACHED("nsContentIterator::nsContentIterator"); return NS_ERROR_FAILURE; } if (mPre) { mLast = GetDeepLastChild(cChild, nsnull); if (!NodeIsInTraversalRange(mLast, mPre, startNode, startIndx, endNode, endIndx)) { mLast = nsnull; } } else { // post-order mLast = cChild; } } // If either first or last is null, they both // have to be null! if (!mFirst || !mLast) { mFirst = nsnull; mLast = nsnull; } mCurNode = mFirst; mIsDone = !mCurNode; if (!mCurNode) mIndexes.Clear(); else RebuildIndexStack(); return NS_OK; }
nsresult nsXBLStreamListener::HandleEvent(nsIDOMEvent* aEvent) { nsresult rv = NS_OK; uint32_t i; uint32_t count = mBindingRequests.Length(); // Get the binding document; note that we don't hold onto it in this object // to avoid creating a cycle Event* event = aEvent->InternalDOMEvent(); EventTarget* target = event->GetCurrentTarget(); nsCOMPtr<nsIDocument> bindingDocument = do_QueryInterface(target); NS_ASSERTION(bindingDocument, "Event not targeted at document?!"); // See if we're still alive. nsCOMPtr<nsIDocument> doc(do_QueryReferent(mBoundDocument)); if (!doc) { NS_WARNING("XBL load did not complete until after document went away! Modal dialog bug?\n"); } else { // We have to do a flush prior to notification of the document load. // This has to happen since the HTML content sink can be holding on // to notifications related to our children (e.g., if you bind to the // <body> tag) that result in duplication of content. // We need to get the sink's notifications flushed and then make the binding // ready. if (count > 0) { nsXBLBindingRequest* req = mBindingRequests.ElementAt(0); nsIDocument* document = req->mBoundElement->GetCurrentDoc(); if (document) document->FlushPendingNotifications(Flush_ContentAndNotify); } // Remove ourselves from the set of pending docs. nsBindingManager *bindingManager = doc->BindingManager(); nsIURI* documentURI = bindingDocument->GetDocumentURI(); bindingManager->RemoveLoadingDocListener(documentURI); if (!bindingDocument->GetRootElement()) { // FIXME: How about an error console warning? NS_WARNING("XBL doc with no root element - this usually shouldn't happen"); return NS_ERROR_FAILURE; } // Put our doc info in the doc table. nsBindingManager *xblDocBindingManager = bindingDocument->BindingManager(); nsRefPtr<nsXBLDocumentInfo> info = xblDocBindingManager->GetXBLDocumentInfo(documentURI); xblDocBindingManager->RemoveXBLDocumentInfo(info); // Break the self-imposed cycle. if (!info) { if (nsXBLService::IsChromeOrResourceURI(documentURI)) { NS_WARNING("An XBL file is malformed. Did you forget the XBL namespace on the bindings tag?"); } nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, NS_LITERAL_CSTRING("XBL"), nullptr, nsContentUtils::eXBL_PROPERTIES, "MalformedXBL", nullptr, 0, documentURI); return NS_ERROR_FAILURE; } // If the doc is a chrome URI, then we put it into the XUL cache. #ifdef MOZ_XUL if (nsXBLService::IsChromeOrResourceURI(documentURI)) { nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance(); if (cache && cache->IsEnabled()) cache->PutXBLDocumentInfo(info); } #endif bindingManager->PutXBLDocumentInfo(info); // Notify all pending requests that their bindings are // ready and can be installed. for (i = 0; i < count; i++) { nsXBLBindingRequest* req = mBindingRequests.ElementAt(i); req->DocumentLoaded(bindingDocument); } } target->RemoveEventListener(NS_LITERAL_STRING("load"), this, false); return rv; }