/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim:cindent:ts=2:et:sw=2: * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** * * This Original Code has been modified by IBM Corporation. Modifications made by IBM * described herein are Copyright (c) International Business Machines Corporation, 2000. * Modifications to Mozilla code or documentation identified per MPL Section 3.3 * * Date Modified by Description of modification * 04/20/2000 IBM Corp. OS/2 VisualAge build. */ /* storage of the frame tree and information about it */ #include "nscore.h" #include "nsPresContext.h" #include "nsIPresShell.h" #include "nsStyleSet.h" #include "nsCSSFrameConstructor.h" #include "nsStyleContext.h" #include "nsStyleChangeList.h" #include "nsIServiceManager.h" #include "nsCOMPtr.h" #include "prthread.h" #include "plhash.h" #include "nsPlaceholderFrame.h" #include "nsContainerFrame.h" #include "nsBlockFrame.h" #include "nsGkAtoms.h" #include "nsCSSAnonBoxes.h" #include "nsCSSPseudoElements.h" #ifdef NS_DEBUG #include "nsIStyleRule.h" #endif #include "nsILayoutHistoryState.h" #include "nsPresState.h" #include "nsIContent.h" #include "nsINameSpaceManager.h" #include "nsIDocument.h" #include "nsIScrollableFrame.h" #include "nsIHTMLDocument.h" #include "nsIDOMHTMLDocument.h" #include "nsIDOMNodeList.h" #include "nsIDOMHTMLCollection.h" #include "nsIFormControl.h" #include "nsIDOMElement.h" #include "nsIDOMHTMLFormElement.h" #include "nsIForm.h" #include "nsContentUtils.h" #include "nsReadableUtils.h" #include "nsUnicharUtils.h" #include "nsPrintfCString.h" #include "nsLayoutErrors.h" #include "nsLayoutUtils.h" #include "nsAutoPtr.h" #include "imgIRequest.h" #include "nsTransitionManager.h" #include "nsFrameManager.h" #ifdef ACCESSIBILITY #include "nsIAccessibilityService.h" #include "nsIAccessibleEvent.h" #endif #ifdef DEBUG //#define NOISY_DEBUG //#define DEBUG_UNDISPLAYED_MAP #else #undef NOISY_DEBUG #undef DEBUG_UNDISPLAYED_MAP #endif #ifdef NOISY_DEBUG #define NOISY_TRACE(_msg) \ printf("%s",_msg); #define NOISY_TRACE_FRAME(_msg,_frame) \ printf("%s ",_msg); nsFrame::ListTag(stdout,_frame); printf("\n"); #else #define NOISY_TRACE(_msg); #define NOISY_TRACE_FRAME(_msg,_frame); #endif // IID's //---------------------------------------------------------------------- struct PlaceholderMapEntry : public PLDHashEntryHdr { // key (the out of flow frame) can be obtained through placeholder frame nsPlaceholderFrame *placeholderFrame; }; static PRBool PlaceholderMapMatchEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr, const void *key) { const PlaceholderMapEntry *entry = static_cast(hdr); NS_ASSERTION(entry->placeholderFrame->GetOutOfFlowFrame() != (void*)0xdddddddd, "Dead placeholder in placeholder map"); return entry->placeholderFrame->GetOutOfFlowFrame() == key; } static PLDHashTableOps PlaceholderMapOps = { PL_DHashAllocTable, PL_DHashFreeTable, PL_DHashVoidPtrKeyStub, PlaceholderMapMatchEntry, PL_DHashMoveEntryStub, PL_DHashClearEntryStub, PL_DHashFinalizeStub, NULL }; //---------------------------------------------------------------------- struct PrimaryFrameMapEntry : public PLDHashEntryHdr { // key (the content node) can almost always be obtained through the // frame. If it weren't for the way image maps (mis)used the primary // frame map, we'd be able to have a 2 word entry instead of a 3 word // entry. nsIContent *content; nsIFrame *frame; }; // These ops should be used if/when we switch back to a 2-word entry. // See comment in |PrimaryFrameMapEntry| above. #if 0 static PRBool PrimaryFrameMapMatchEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr, const void *key) { const PrimaryFrameMapEntry *entry = static_cast(hdr); return entry->frame->GetContent() == key; } static PLDHashTableOps PrimaryFrameMapOps = { PL_DHashAllocTable, PL_DHashFreeTable, PL_DHashVoidPtrKeyStub, PrimaryFrameMapMatchEntry, PL_DHashMoveEntryStub, PL_DHashClearEntryStub, PL_DHashFinalizeStub, NULL }; #endif /* 0 */ //---------------------------------------------------------------------- // XXXldb This seems too complicated for what I think it's doing, and it // should also be using pldhash rather than plhash to use less memory. class UndisplayedNode { public: UndisplayedNode(nsIContent* aContent, nsStyleContext* aStyle) : mContent(aContent), mStyle(aStyle), mNext(nsnull) { MOZ_COUNT_CTOR(UndisplayedNode); } NS_HIDDEN ~UndisplayedNode() { MOZ_COUNT_DTOR(UndisplayedNode); // Delete mNext iteratively to avoid blowing up the stack (bug 460461). UndisplayedNode *cur = mNext; while (cur) { UndisplayedNode *next = cur->mNext; cur->mNext = nsnull; delete cur; cur = next; } } nsCOMPtr mContent; nsRefPtr mStyle; UndisplayedNode* mNext; }; class nsFrameManagerBase::UndisplayedMap { public: UndisplayedMap(PRUint32 aNumBuckets = 16) NS_HIDDEN; ~UndisplayedMap(void) NS_HIDDEN; NS_HIDDEN_(UndisplayedNode*) GetFirstNode(nsIContent* aParentContent); NS_HIDDEN_(nsresult) AddNodeFor(nsIContent* aParentContent, nsIContent* aChild, nsStyleContext* aStyle); NS_HIDDEN_(void) RemoveNodeFor(nsIContent* aParentContent, UndisplayedNode* aNode); NS_HIDDEN_(void) RemoveNodesFor(nsIContent* aParentContent); // Removes all entries from the hash table NS_HIDDEN_(void) Clear(void); protected: NS_HIDDEN_(PLHashEntry**) GetEntryFor(nsIContent* aParentContent); NS_HIDDEN_(void) AppendNodeFor(UndisplayedNode* aNode, nsIContent* aParentContent); PLHashTable* mTable; PLHashEntry** mLastLookup; }; //---------------------------------------------------------------------- nsFrameManager::nsFrameManager() { } nsFrameManager::~nsFrameManager() { NS_ASSERTION(!mPresShell, "nsFrameManager::Destroy never called"); } nsresult nsFrameManager::Init(nsIPresShell* aPresShell, nsStyleSet* aStyleSet) { if (!aPresShell) { NS_ERROR("null pres shell"); return NS_ERROR_FAILURE; } if (!aStyleSet) { NS_ERROR("null style set"); return NS_ERROR_FAILURE; } mPresShell = aPresShell; mStyleSet = aStyleSet; return NS_OK; } void nsFrameManager::Destroy() { NS_ASSERTION(mPresShell, "Frame manager already shut down."); // Destroy the frame hierarchy. mPresShell->SetIgnoreFrameDestruction(PR_TRUE); mIsDestroying = PR_TRUE; // This flag prevents GetPrimaryFrameFor from returning pointers to destroyed frames // Unregister all placeholders before tearing down the frame tree nsFrameManager::ClearPlaceholderFrameMap(); if (mRootFrame) { mRootFrame->Destroy(); mRootFrame = nsnull; } nsFrameManager::ClearPrimaryFrameMap(); delete mUndisplayedMap; mUndisplayedMap = nsnull; mPresShell = nsnull; } nsIFrame* nsFrameManager::GetCanvasFrame() { if (mRootFrame) { // walk the children of the root frame looking for a frame with type==canvas // start at the root nsIFrame* childFrame = mRootFrame; while (childFrame) { // get each sibling of the child and check them, startig at the child nsIFrame *siblingFrame = childFrame; while (siblingFrame) { if (siblingFrame->GetType() == nsGkAtoms::canvasFrame) { // this is it return siblingFrame; } else { siblingFrame = siblingFrame->GetNextSibling(); } } // move on to the child's child childFrame = childFrame->GetFirstChild(nsnull); } } return nsnull; } //---------------------------------------------------------------------- // Primary frame functions nsIFrame* nsFrameManager::GetPrimaryFrameFor(nsIContent* aContent, PRInt32 aIndexHint) { NS_ASSERTION(!mIsDestroyingFrames, "GetPrimaryFrameFor() called while frames are being destroyed!"); NS_ENSURE_TRUE(aContent, nsnull); if (mIsDestroying) { NS_ERROR("GetPrimaryFrameFor() called while nsFrameManager is being destroyed!"); return nsnull; } if (!aContent->MayHaveFrame()) { return nsnull; } if (mPrimaryFrameMap.ops) { PrimaryFrameMapEntry *entry = static_cast (PL_DHashTableOperate(&mPrimaryFrameMap, aContent, PL_DHASH_LOOKUP)); if (PL_DHASH_ENTRY_IS_BUSY(entry)) { return entry->frame; } // XXX: todo: Add a lookup into the undisplay map to skip searches // if we already know the content has no frame. // nsCSSFrameConstructor calls SetUndisplayedContent() for every // content node that has display: none. // Today, the undisplay map doesn't quite support what we need. // We need to see if we can add a method to make a search for aContent // very fast in the embedded hash table. // This would almost completely remove the lookup penalty for things // like