Back to index

lightning-sunbird  0.9+nobinonly
nsFrameManager.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
00002  * vim:cindent:ts=2:et:sw=2:
00003  *
00004  * ***** BEGIN LICENSE BLOCK *****
00005  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00006  *
00007  * The contents of this file are subject to the Mozilla Public License Version
00008  * 1.1 (the "License"); you may not use this file except in compliance with
00009  * the License. You may obtain a copy of the License at
00010  * http://www.mozilla.org/MPL/
00011  *
00012  * Software distributed under the License is distributed on an "AS IS" basis,
00013  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00014  * for the specific language governing rights and limitations under the
00015  * License.
00016  *
00017  * The Original Code is mozilla.org code.
00018  *
00019  * The Initial Developer of the Original Code is
00020  * Netscape Communications Corporation.
00021  * Portions created by the Initial Developer are Copyright (C) 1998
00022  * the Initial Developer. All Rights Reserved.
00023  *
00024  * Contributor(s):
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK *****
00039  *
00040  * This Original Code has been modified by IBM Corporation. Modifications made by IBM 
00041  * described herein are Copyright (c) International Business Machines Corporation, 2000.
00042  * Modifications to Mozilla code or documentation identified per MPL Section 3.3
00043  *
00044  * Date             Modified by     Description of modification
00045  * 04/20/2000       IBM Corp.      OS/2 VisualAge build.
00046  */
00047 #include "nscore.h"
00048 #include "nsPresContext.h"
00049 #include "nsIPresShell.h"
00050 #include "nsStyleSet.h"
00051 #include "nsCSSFrameConstructor.h"
00052 #include "nsStyleContext.h"
00053 #include "nsStyleChangeList.h"
00054 #include "nsIEventQueueService.h"
00055 #include "nsIServiceManager.h"
00056 #include "nsCOMPtr.h"
00057 #include "prthread.h"
00058 #include "plhash.h"
00059 #include "nsPlaceholderFrame.h"
00060 #include "nsLayoutAtoms.h"
00061 #include "nsCSSAnonBoxes.h"
00062 #include "nsCSSPseudoElements.h"
00063 #include "nsHTMLAtoms.h"
00064 #ifdef NS_DEBUG
00065 #include "nsISupportsArray.h"
00066 #include "nsIStyleRule.h"
00067 #endif
00068 #include "nsILayoutHistoryState.h"
00069 #include "nsPresState.h"
00070 #include "nsIContent.h"
00071 #include "nsINameSpaceManager.h"
00072 #include "nsIDocument.h"
00073 #include "nsIBindingManager.h"
00074 #include "nsIScrollableFrame.h"
00075 
00076 #include "nsIHTMLDocument.h"
00077 #include "nsIDOMHTMLDocument.h"
00078 #include "nsIDOMNodeList.h"
00079 #include "nsIDOMHTMLCollection.h"
00080 #include "nsIFormControl.h"
00081 #include "nsIDOMElement.h"
00082 #include "nsIDOMHTMLFormElement.h"
00083 #include "nsIForm.h"
00084 #include "nsContentUtils.h"
00085 #include "nsReadableUtils.h"
00086 #include "nsUnicharUtils.h"
00087 #include "nsPrintfCString.h"
00088 #include "nsDummyLayoutRequest.h"
00089 #include "nsLayoutErrors.h"
00090 #include "nsLayoutUtils.h"
00091 #include "nsAutoPtr.h"
00092 #include "imgIRequest.h"
00093 
00094 #include "nsFrameManager.h"
00095 
00096   #ifdef DEBUG
00097     //#define NOISY_DEBUG
00098     //#define DEBUG_UNDISPLAYED_MAP
00099   #else
00100     #undef NOISY_DEBUG
00101     #undef DEBUG_UNDISPLAYED_MAP
00102   #endif
00103 
00104   #ifdef NOISY_DEBUG
00105     #define NOISY_TRACE(_msg) \
00106       printf("%s",_msg);
00107     #define NOISY_TRACE_FRAME(_msg,_frame) \
00108       printf("%s ",_msg); nsFrame::ListTag(stdout,_frame); printf("\n");
00109   #else
00110     #define NOISY_TRACE(_msg);
00111     #define NOISY_TRACE_FRAME(_msg,_frame);
00112   #endif
00113 
00114 // Class IID's
00115 static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID);
00116 
00117 // IID's
00118 
00119 //----------------------------------------------------------------------
00120 
00121 struct PlaceholderMapEntry : public PLDHashEntryHdr {
00122   // key (the out of flow frame) can be obtained through placeholder frame
00123   nsPlaceholderFrame *placeholderFrame;
00124 };
00125 
00126 PR_STATIC_CALLBACK(const void *)
00127 PlaceholderMapGetKey(PLDHashTable *table, PLDHashEntryHdr *hdr)
00128 {
00129   PlaceholderMapEntry *entry = NS_STATIC_CAST(PlaceholderMapEntry*, hdr);
00130   return entry->placeholderFrame->GetOutOfFlowFrame();
00131 }
00132 
00133 PR_STATIC_CALLBACK(PRBool)
00134 PlaceholderMapMatchEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr,
00135                          const void *key)
00136 {
00137   const PlaceholderMapEntry *entry =
00138     NS_STATIC_CAST(const PlaceholderMapEntry*, hdr);
00139   return entry->placeholderFrame->GetOutOfFlowFrame() == key;
00140 }
00141 
00142 static PLDHashTableOps PlaceholderMapOps = {
00143   PL_DHashAllocTable,
00144   PL_DHashFreeTable,
00145   PlaceholderMapGetKey,
00146   PL_DHashVoidPtrKeyStub,
00147   PlaceholderMapMatchEntry,
00148   PL_DHashMoveEntryStub,
00149   PL_DHashClearEntryStub,
00150   PL_DHashFinalizeStub,
00151   NULL
00152 };
00153 
00154 //----------------------------------------------------------------------
00155 
00156 struct PrimaryFrameMapEntry : public PLDHashEntryHdr {
00157   // key (the content node) can almost always be obtained through the
00158   // frame.  If it weren't for the way image maps (mis)used the primary
00159   // frame map, we'd be able to have a 2 word entry instead of a 3 word
00160   // entry.
00161   nsIContent *content;
00162   nsIFrame *frame;
00163 };
00164 
00165   // These ops should be used if/when we switch back to a 2-word entry.
00166   // See comment in |PrimaryFrameMapEntry| above.
00167 #if 0
00168 PR_STATIC_CALLBACK(const void *)
00169 PrimaryFrameMapGetKey(PLDHashTable *table, PLDHashEntryHdr *hdr)
00170 {
00171   PrimaryFrameMapEntry *entry = NS_STATIC_CAST(PrimaryFrameMapEntry*, hdr);
00172   return entry->frame->GetContent();
00173 }
00174 
00175 PR_STATIC_CALLBACK(PRBool)
00176 PrimaryFrameMapMatchEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr,
00177                          const void *key)
00178 {
00179   const PrimaryFrameMapEntry *entry =
00180     NS_STATIC_CAST(const PrimaryFrameMapEntry*, hdr);
00181   return entry->frame->GetContent() == key;
00182 }
00183 
00184 static PLDHashTableOps PrimaryFrameMapOps = {
00185   PL_DHashAllocTable,
00186   PL_DHashFreeTable,
00187   PrimaryFrameMapGetKey,
00188   PL_DHashVoidPtrKeyStub,
00189   PrimaryFrameMapMatchEntry,
00190   PL_DHashMoveEntryStub,
00191   PL_DHashClearEntryStub,
00192   PL_DHashFinalizeStub,
00193   NULL
00194 };
00195 #endif /* 0 */
00196 
00197 //----------------------------------------------------------------------
00198 
00199 // XXXldb This seems too complicated for what I think it's doing, and it
00200 // should also be using pldhash rather than plhash to use less memory.
00201 MOZ_DECL_CTOR_COUNTER(UndisplayedNode)
00202 
00203 class UndisplayedNode {
00204 public:
00205   UndisplayedNode(nsIContent* aContent, nsStyleContext* aStyle)
00206     : mContent(aContent),
00207       mStyle(aStyle),
00208       mNext(nsnull)
00209   {
00210     MOZ_COUNT_CTOR(UndisplayedNode);
00211   }
00212 
00213   NS_HIDDEN ~UndisplayedNode()
00214   {
00215     MOZ_COUNT_DTOR(UndisplayedNode);
00216     delete mNext;
00217   }
00218 
00219   nsCOMPtr<nsIContent>      mContent;
00220   nsRefPtr<nsStyleContext>  mStyle;
00221   UndisplayedNode*          mNext;
00222 };
00223 
00224 class nsFrameManagerBase::UndisplayedMap {
00225 public:
00226   UndisplayedMap(PRUint32 aNumBuckets = 16) NS_HIDDEN;
00227   ~UndisplayedMap(void) NS_HIDDEN;
00228 
00229   NS_HIDDEN_(UndisplayedNode*) GetFirstNode(nsIContent* aParentContent);
00230 
00231   NS_HIDDEN_(nsresult) AddNodeFor(nsIContent* aParentContent,
00232                                   nsIContent* aChild, nsStyleContext* aStyle);
00233 
00234   NS_HIDDEN_(void) RemoveNodeFor(nsIContent* aParentContent,
00235                                  UndisplayedNode* aNode);
00236 
00237   NS_HIDDEN_(void) RemoveNodesFor(nsIContent* aParentContent);
00238 
00239   // Removes all entries from the hash table
00240   NS_HIDDEN_(void)  Clear(void);
00241 
00242 protected:
00243   NS_HIDDEN_(PLHashEntry**) GetEntryFor(nsIContent* aParentContent);
00244   NS_HIDDEN_(void)          AppendNodeFor(UndisplayedNode* aNode,
00245                                           nsIContent* aParentContent);
00246 
00247   PLHashTable*  mTable;
00248   PLHashEntry** mLastLookup;
00249 };
00250 
00251 //----------------------------------------------------------------------
00252 
00253 nsFrameManager::nsFrameManager()
00254 {
00255 }
00256 
00257 nsFrameManager::~nsFrameManager()
00258 {
00259   NS_ASSERTION(!mPresShell, "nsFrameManager::Destroy never called");
00260 }
00261 
00262 nsresult
00263 nsFrameManager::Init(nsIPresShell* aPresShell,
00264                      nsStyleSet*  aStyleSet)
00265 {
00266   if (!aPresShell) {
00267     NS_ERROR("null pres shell");
00268     return NS_ERROR_FAILURE;
00269   }
00270 
00271   if (!aStyleSet) {
00272     NS_ERROR("null style set");
00273     return NS_ERROR_FAILURE;
00274   }
00275 
00276   mPresShell = aPresShell;
00277   mStyleSet = aStyleSet;
00278   return NS_OK;
00279 }
00280 
00281 void
00282 nsFrameManager::Destroy()
00283 {
00284   NS_ASSERTION(mPresShell, "Frame manager already shut down.");
00285 
00286   nsPresContext *presContext = mPresShell->GetPresContext();
00287   
00288   // Destroy the frame hierarchy.
00289   mPresShell->SetIgnoreFrameDestruction(PR_TRUE);
00290 
00291   mIsDestroyingFrames = PR_TRUE;  // This flag prevents GetPrimaryFrameFor from returning pointers to destroyed frames
00292 
00293   // Unregister all placeholders before tearing down the frame tree
00294   nsFrameManager::ClearPlaceholderFrameMap();
00295 
00296   if (mRootFrame) {
00297     mRootFrame->Destroy(presContext);
00298     mRootFrame = nsnull;
00299   }
00300   
00301   nsFrameManager::ClearPrimaryFrameMap();
00302   delete mUndisplayedMap;
00303   mUndisplayedMap = nsnull;
00304 
00305   mPresShell = nsnull;
00306 }
00307 
00308 nsIFrame*
00309 nsFrameManager::GetCanvasFrame()
00310 {
00311   if (mRootFrame) {
00312     // walk the children of the root frame looking for a frame with type==canvas
00313     // start at the root
00314     nsIFrame* childFrame = mRootFrame;
00315     while (childFrame) {
00316       // get each sibling of the child and check them, startig at the child
00317       nsIFrame *siblingFrame = childFrame;
00318       while (siblingFrame) {
00319         if (siblingFrame->GetType() == nsLayoutAtoms::canvasFrame) {
00320           // this is it
00321           return siblingFrame;
00322         } else {
00323           siblingFrame = siblingFrame->GetNextSibling();
00324         }
00325       }
00326       // move on to the child's child
00327       childFrame = childFrame->GetFirstChild(nsnull);
00328     }
00329   }
00330   return nsnull;
00331 }
00332 
00333 //----------------------------------------------------------------------
00334 
00335 // Primary frame functions
00336 nsIFrame*
00337 nsFrameManager::GetPrimaryFrameFor(nsIContent* aContent)
00338 {
00339   NS_ENSURE_TRUE(aContent, nsnull);
00340 
00341   if (mIsDestroyingFrames) {
00342 #ifdef DEBUG
00343     printf("GetPrimaryFrameFor() called while nsFrameManager is being destroyed!\n");
00344 #endif
00345     return nsnull;
00346   }
00347 
00348   if (!aContent->MayHaveFrame()) {
00349     return nsnull;
00350   }
00351 
00352   if (mPrimaryFrameMap.ops) {
00353     PrimaryFrameMapEntry *entry = NS_STATIC_CAST(PrimaryFrameMapEntry*,
00354         PL_DHashTableOperate(&mPrimaryFrameMap, aContent, PL_DHASH_LOOKUP));
00355     if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
00356       return entry->frame;
00357     }
00358 
00359     // XXX: todo:  Add a lookup into the undisplay map to skip searches 
00360     //             if we already know the content has no frame.
00361     //             nsCSSFrameConstructor calls SetUndisplayedContent() for every
00362     //             content node that has display: none.
00363     //             Today, the undisplay map doesn't quite support what we need.
00364     //             We need to see if we can add a method to make a search for aContent 
00365     //             very fast in the embedded hash table.
00366     //             This would almost completely remove the lookup penalty for things
00367     //             like <SCRIPT> and comments in very large documents.
00368     // XXX with the nsIContent::MayHaveFrame bit, is that really necessary now?
00369 
00370     // Give the frame construction code the opportunity to return the
00371     // frame that maps the content object
00372 
00373     // if the prev sibling of aContent has a cached primary frame,
00374     // pass that data in to the style set to speed things up
00375     // if any methods in here fail, don't report that failure
00376     // we're just trying to enhance performance here, not test for correctness
00377     nsFindFrameHint hint;
00378     nsIContent* parent = aContent->GetParent();
00379     if (parent)
00380     {
00381       PRInt32 index = parent->IndexOf(aContent);
00382       if (index > 0)  // no use looking if it's the first child
00383       {
00384         nsIContent *prevSibling;
00385         do {
00386           prevSibling = parent->GetChildAt(--index);
00387         } while (index &&
00388                  (prevSibling->IsContentOfType(nsIContent::eTEXT) ||
00389                   prevSibling->IsContentOfType(nsIContent::eCOMMENT) ||
00390                   prevSibling->IsContentOfType(nsIContent::ePROCESSING_INSTRUCTION)));
00391         if (prevSibling) {
00392           entry = NS_STATIC_CAST(PrimaryFrameMapEntry*,
00393                           PL_DHashTableOperate(&mPrimaryFrameMap, prevSibling,
00394                                                PL_DHASH_LOOKUP));
00395           if (PL_DHASH_ENTRY_IS_BUSY(entry))
00396             hint.mPrimaryFrameForPrevSibling = entry->frame;
00397         }
00398       }
00399     }
00400 
00401     // walk the frame tree to find the frame that maps aContent.  
00402     // Use the hint if we have it.
00403     nsIFrame *result;
00404 
00405     mPresShell->FrameConstructor()->
00406       FindPrimaryFrameFor(this, aContent, &result, 
00407                           hint.mPrimaryFrameForPrevSibling ? &hint : nsnull);
00408 
00409     return result;
00410   }
00411 
00412   return nsnull;
00413 }
00414 
00415 nsresult
00416 nsFrameManager::SetPrimaryFrameFor(nsIContent* aContent,
00417                                    nsIFrame*   aPrimaryFrame)
00418 {
00419   NS_ENSURE_ARG_POINTER(aContent);
00420   NS_ASSERTION(aPrimaryFrame && aPrimaryFrame->GetParent(),
00421                "BOGUS!");
00422 
00423   // This code should be used if/when we switch back to a 2-word entry
00424   // in the primary frame map.
00425 #if 0
00426   NS_PRECONDITION(aPrimaryFrame->GetContent() == aContent, "wrong content");
00427 #endif
00428 
00429   // Create a new hashtable if necessary
00430   if (!mPrimaryFrameMap.ops) {
00431     if (!PL_DHashTableInit(&mPrimaryFrameMap, PL_DHashGetStubOps(), nsnull,
00432                            sizeof(PrimaryFrameMapEntry), 16)) {
00433       mPrimaryFrameMap.ops = nsnull;
00434       return NS_ERROR_OUT_OF_MEMORY;
00435     }
00436   }
00437 
00438   // Add a mapping to the hash table
00439   PrimaryFrameMapEntry *entry = NS_STATIC_CAST(PrimaryFrameMapEntry*,
00440       PL_DHashTableOperate(&mPrimaryFrameMap, aContent, PL_DHASH_ADD));
00441 #ifdef DEBUG_dbaron
00442   if (entry->frame) {
00443     NS_WARNING("already have primary frame for content");
00444   }
00445 #endif
00446   entry->frame = aPrimaryFrame;
00447   entry->content = aContent;
00448     
00449   return NS_OK;
00450 }
00451 
00452 void
00453 nsFrameManager::RemoveAsPrimaryFrame(nsIContent* aContent,
00454                                      nsIFrame* aPrimaryFrame)
00455 {
00456   NS_PRECONDITION(aPrimaryFrame, "Must have a frame");
00457   if (aContent && mPrimaryFrameMap.ops) {
00458     PrimaryFrameMapEntry *entry = NS_STATIC_CAST(PrimaryFrameMapEntry*,
00459         PL_DHashTableOperate(&mPrimaryFrameMap, aContent, PL_DHASH_LOOKUP));
00460     if (PL_DHASH_ENTRY_IS_BUSY(entry) && entry->frame == aPrimaryFrame) {
00461       // Don't use PL_DHashTableRawRemove, since we want the table to
00462       // shrink as needed.
00463       PL_DHashTableOperate(&mPrimaryFrameMap, aContent, PL_DHASH_REMOVE);
00464     }
00465   }
00466 
00467   aPrimaryFrame->RemovedAsPrimaryFrame(GetPresContext());
00468 }
00469 
00470 void
00471 nsFrameManager::ClearPrimaryFrameMap()
00472 {
00473   if (mPrimaryFrameMap.ops) {
00474     PL_DHashTableFinish(&mPrimaryFrameMap);
00475     mPrimaryFrameMap.ops = nsnull;
00476   }
00477 }
00478 
00479 // Placeholder frame functions
00480 nsPlaceholderFrame*
00481 nsFrameManager::GetPlaceholderFrameFor(nsIFrame*  aFrame)
00482 {
00483   NS_PRECONDITION(aFrame, "null param unexpected");
00484 
00485   if (mPlaceholderMap.ops) {
00486     PlaceholderMapEntry *entry = NS_STATIC_CAST(PlaceholderMapEntry*,
00487            PL_DHashTableOperate(NS_CONST_CAST(PLDHashTable*, &mPlaceholderMap),
00488                                 aFrame, PL_DHASH_LOOKUP));
00489     if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
00490       return entry->placeholderFrame;
00491     }
00492   }
00493 
00494   return nsnull;
00495 }
00496 
00497 nsresult
00498 nsFrameManager::RegisterPlaceholderFrame(nsPlaceholderFrame* aPlaceholderFrame)
00499 {
00500   NS_PRECONDITION(aPlaceholderFrame, "null param unexpected");
00501   NS_PRECONDITION(nsLayoutAtoms::placeholderFrame == aPlaceholderFrame->GetType(),
00502                   "unexpected frame type");
00503   if (!mPlaceholderMap.ops) {
00504     if (!PL_DHashTableInit(&mPlaceholderMap, &PlaceholderMapOps, nsnull,
00505                            sizeof(PlaceholderMapEntry), 16)) {
00506       mPlaceholderMap.ops = nsnull;
00507       return NS_ERROR_OUT_OF_MEMORY;
00508     }
00509   }
00510   PlaceholderMapEntry *entry = NS_STATIC_CAST(PlaceholderMapEntry*, 
00511          PL_DHashTableOperate(&mPlaceholderMap,
00512                               aPlaceholderFrame->GetOutOfFlowFrame(),
00513                               PL_DHASH_ADD));
00514   if (!entry)
00515     return NS_ERROR_OUT_OF_MEMORY;
00516 
00517   NS_ASSERTION(!entry->placeholderFrame, "Registering a placeholder for a frame that already has a placeholder!");
00518   entry->placeholderFrame = aPlaceholderFrame;
00519 
00520   return NS_OK;
00521 }
00522 
00523 void
00524 nsFrameManager::UnregisterPlaceholderFrame(nsPlaceholderFrame* aPlaceholderFrame)
00525 {
00526   NS_PRECONDITION(aPlaceholderFrame, "null param unexpected");
00527   NS_PRECONDITION(nsLayoutAtoms::placeholderFrame == aPlaceholderFrame->GetType(),
00528                   "unexpected frame type");
00529 
00530   if (mPlaceholderMap.ops) {
00531     PL_DHashTableOperate(&mPlaceholderMap,
00532                          aPlaceholderFrame->GetOutOfFlowFrame(),
00533                          PL_DHASH_REMOVE);
00534   }
00535 }
00536 
00537 PR_STATIC_CALLBACK(PLDHashOperator)
00538 UnregisterPlaceholders(PLDHashTable* table, PLDHashEntryHdr* hdr,
00539                        PRUint32 number, void* arg)
00540 {
00541   PlaceholderMapEntry* entry = NS_STATIC_CAST(PlaceholderMapEntry*, hdr);
00542   entry->placeholderFrame->SetOutOfFlowFrame(nsnull);
00543   return PL_DHASH_NEXT;
00544 }
00545 
00546 void
00547 nsFrameManager::ClearPlaceholderFrameMap()
00548 {
00549   if (mPlaceholderMap.ops) {
00550     PL_DHashTableEnumerate(&mPlaceholderMap, UnregisterPlaceholders, nsnull);
00551     PL_DHashTableFinish(&mPlaceholderMap);
00552     mPlaceholderMap.ops = nsnull;
00553   }
00554 }
00555 
00556 //----------------------------------------------------------------------
00557 
00558 nsStyleContext*
00559 nsFrameManager::GetUndisplayedContent(nsIContent* aContent)
00560 {
00561   if (!aContent || !mUndisplayedMap)
00562     return nsnull;
00563 
00564   nsIContent* parent = aContent->GetParent();
00565   if (!parent)
00566     return nsnull;
00567 
00568   for (UndisplayedNode* node = mUndisplayedMap->GetFirstNode(parent);
00569          node; node = node->mNext) {
00570     if (node->mContent == aContent)
00571       return node->mStyle;
00572   }
00573 
00574   return nsnull;
00575 }
00576   
00577 void
00578 nsFrameManager::SetUndisplayedContent(nsIContent* aContent, 
00579                                       nsStyleContext* aStyleContext)
00580 {
00581 #ifdef DEBUG_UNDISPLAYED_MAP
00582    static int i = 0;
00583    printf("SetUndisplayedContent(%d): p=%p \n", i++, (void *)aContent);
00584 #endif
00585 
00586   if (! mUndisplayedMap) {
00587     mUndisplayedMap = new UndisplayedMap;
00588   }
00589   if (mUndisplayedMap) {
00590     nsIContent* parent = aContent->GetParent();
00591     NS_ASSERTION(parent, "undisplayed content must have a parent");
00592     if (parent) {
00593       mUndisplayedMap->AddNodeFor(parent, aContent, aStyleContext);
00594     }
00595   }
00596 }
00597 
00598 void
00599 nsFrameManager::ChangeUndisplayedContent(nsIContent* aContent, 
00600                                          nsStyleContext* aStyleContext)
00601 {
00602   NS_ASSERTION(mUndisplayedMap, "no existing undisplayed content");
00603   
00604 #ifdef DEBUG_UNDISPLAYED_MAP
00605    static int i = 0;
00606    printf("ChangeUndisplayedContent(%d): p=%p \n", i++, (void *)aContent);
00607 #endif
00608 
00609   for (UndisplayedNode* node = mUndisplayedMap->GetFirstNode(aContent->GetParent());
00610          node; node = node->mNext) {
00611     if (node->mContent == aContent) {
00612       node->mStyle = aStyleContext;
00613       return;
00614     }
00615   }
00616 
00617   NS_NOTREACHED("no existing undisplayed content");
00618 }
00619 
00620 void
00621 nsFrameManager::ClearUndisplayedContentIn(nsIContent* aContent,
00622                                           nsIContent* aParentContent)
00623 {
00624 #ifdef DEBUG_UNDISPLAYED_MAP
00625   static int i = 0;
00626   printf("ClearUndisplayedContent(%d): content=%p parent=%p --> ", i++, (void *)aContent, (void*)aParentContent);
00627 #endif
00628   
00629   if (mUndisplayedMap) {
00630     UndisplayedNode* node = mUndisplayedMap->GetFirstNode(aParentContent);
00631     while (node) {
00632       if (node->mContent == aContent) {
00633         mUndisplayedMap->RemoveNodeFor(aParentContent, node);
00634 
00635 #ifdef DEBUG_UNDISPLAYED_MAP
00636         printf( "REMOVED!\n");
00637 #endif
00638 #ifdef DEBUG
00639         // make sure that there are no more entries for the same content
00640         nsStyleContext *context = GetUndisplayedContent(aContent);
00641         NS_ASSERTION(context == nsnull, "Found more undisplayed content data after removal");
00642 #endif
00643         return;
00644       }
00645       node = node->mNext;
00646     }
00647   }
00648 }
00649 
00650 void
00651 nsFrameManager::ClearAllUndisplayedContentIn(nsIContent* aParentContent)
00652 {
00653 #ifdef DEBUG_UNDISPLAYED_MAP
00654   static int i = 0;
00655   printf("ClearAllUndisplayedContentIn(%d): parent=%p \n", i++, (void*)aParentContent);
00656 #endif
00657 
00658   if (mUndisplayedMap) {
00659     mUndisplayedMap->RemoveNodesFor(aParentContent);
00660   }
00661 }
00662 
00663 void
00664 nsFrameManager::ClearUndisplayedContentMap()
00665 {
00666 #ifdef DEBUG_UNDISPLAYED_MAP
00667   static int i = 0;
00668   printf("ClearUndisplayedContentMap(%d)\n", i++);
00669 #endif
00670 
00671   if (mUndisplayedMap) {
00672     mUndisplayedMap->Clear();
00673   }
00674 }
00675 
00676 //----------------------------------------------------------------------
00677 
00678 nsresult
00679 nsFrameManager::InsertFrames(nsIFrame*       aParentFrame,
00680                              nsIAtom*        aListName,
00681                              nsIFrame*       aPrevFrame,
00682                              nsIFrame*       aFrameList)
00683 {
00684 #ifdef IBMBIDI
00685   if (aPrevFrame) {
00686     // Insert aFrameList after the last bidi continuation of aPrevFrame.
00687     nsPropertyTable *propTable = GetPresContext()->PropertyTable();
00688     nsIFrame* nextBidi;
00689     for (; ;) {
00690       nextBidi = NS_STATIC_CAST(nsIFrame*,
00691                   propTable->GetProperty(aPrevFrame, nsLayoutAtoms::nextBidi));
00692       if (!nextBidi) {
00693         break;
00694       }
00695       aPrevFrame = nextBidi;
00696     }
00697   }
00698 #endif // IBMBIDI
00699 
00700   return aParentFrame->InsertFrames(aListName, aPrevFrame, aFrameList);
00701 }
00702 
00703 nsresult
00704 nsFrameManager::RemoveFrame(nsIFrame*       aParentFrame,
00705                             nsIAtom*        aListName,
00706                             nsIFrame*       aOldFrame)
00707 {
00708 #ifdef IBMBIDI
00709   // Don't let the parent remove next bidi. In the other cases the it should NOT be removed.
00710   nsIFrame* nextBidi =
00711     NS_STATIC_CAST(nsIFrame*, aOldFrame->GetProperty(nsLayoutAtoms::nextBidi));
00712   if (nextBidi) {
00713     RemoveFrame(aParentFrame, aListName, nextBidi);
00714   }
00715 #endif // IBMBIDI
00716 
00717   return aParentFrame->RemoveFrame(aListName, aOldFrame);
00718 }
00719 
00720 //----------------------------------------------------------------------
00721 
00722 void
00723 nsFrameManager::NotifyDestroyingFrame(nsIFrame* aFrame)
00724 {
00725   // We've already removed from the primary frame map once, but we're
00726   // going to try to do it again here to fix callers of GetPrimaryFrameFor
00727   // during frame destruction, since this problem keeps coming back to
00728   // bite us.  We may want to remove the previous caller.
00729   if (mPrimaryFrameMap.ops) {
00730     PrimaryFrameMapEntry *entry = NS_STATIC_CAST(PrimaryFrameMapEntry*,
00731         PL_DHashTableOperate(&mPrimaryFrameMap, aFrame->GetContent(), PL_DHASH_LOOKUP));
00732     if (PL_DHASH_ENTRY_IS_BUSY(entry) && entry->frame == aFrame) {
00733       NS_NOTREACHED("frame was not removed from primary frame map before "
00734                     "destruction or was readded to map after being removed");
00735       PL_DHashTableRawRemove(&mPrimaryFrameMap, entry);
00736     }
00737   }
00738 }
00739 
00740 #ifdef NS_DEBUG
00741 static void
00742 DumpContext(nsIFrame* aFrame, nsStyleContext* aContext)
00743 {
00744   if (aFrame) {
00745     fputs("frame: ", stdout);
00746     nsAutoString  name;
00747     nsIFrameDebug*  frameDebug;
00748 
00749     if (NS_SUCCEEDED(aFrame->QueryInterface(NS_GET_IID(nsIFrameDebug), (void**)&frameDebug))) {
00750       frameDebug->GetFrameName(name);
00751       fputs(NS_LossyConvertUCS2toASCII(name).get(), stdout);
00752     }
00753     fprintf(stdout, " (%p)", NS_STATIC_CAST(void*, aFrame));
00754   }
00755   if (aContext) {
00756     fprintf(stdout, " style: %p ", NS_STATIC_CAST(void*, aContext));
00757 
00758     nsIAtom* pseudoTag = aContext->GetPseudoType();
00759     if (pseudoTag) {
00760       nsAutoString  buffer;
00761       pseudoTag->ToString(buffer);
00762       fputs(NS_LossyConvertUCS2toASCII(buffer).get(), stdout);
00763       fputs(" ", stdout);
00764     }
00765 
00766 /* XXXdwh fix debugging here.  Need to add a List method to nsRuleNode
00767    and have the context call list on its rule node.
00768     PRInt32 count = aContext->GetStyleRuleCount();
00769     if (0 < count) {
00770       fputs("{\n", stdout);
00771       nsISupportsArray* rules = aContext->GetStyleRules();
00772       PRInt32 ix;
00773       for (ix = 0; ix < count; ix++) {
00774         nsIStyleRule* rule = (nsIStyleRule*)rules->ElementAt(ix);
00775         rule->List(stdout, 1);
00776         NS_RELEASE(rule);
00777       }
00778       NS_RELEASE(rules);
00779       fputs("}\n", stdout);
00780     }
00781     else 
00782     */
00783     {
00784       fputs("{}\n", stdout);
00785     }
00786   }
00787 }
00788 
00789 static void
00790 VerifySameTree(nsStyleContext* aContext1, nsStyleContext* aContext2)
00791 {
00792   nsStyleContext* top1 = aContext1;
00793   nsStyleContext* top2 = aContext2;
00794   nsStyleContext* parent;
00795   for (;;) {
00796     parent = top1->GetParent();
00797     if (!parent)
00798       break;
00799     top1 = parent;
00800   }
00801   for (;;) {
00802     parent = top2->GetParent();
00803     if (!parent)
00804       break;
00805     top2 = parent;
00806   }
00807   if (top1 != top2)
00808     printf("Style contexts are not in the same style context tree.\n");
00809 }
00810 
00811 static void
00812 VerifyContextParent(nsPresContext* aPresContext, nsIFrame* aFrame, 
00813                     nsStyleContext* aContext, nsStyleContext* aParentContext)
00814 {
00815   // get the contexts not provided
00816   if (!aContext) {
00817     aContext = aFrame->GetStyleContext();
00818   }
00819 
00820   if (!aParentContext) {
00821     // Get the correct parent context from the frame
00822     //  - if the frame is a placeholder, we get the out of flow frame's context 
00823     //    as the parent context instead of asking the frame
00824 
00825     // get the parent context from the frame (indirectly)
00826     nsIFrame* providerFrame = nsnull;
00827     PRBool providerIsChild;
00828     aFrame->GetParentStyleContextFrame(aPresContext,
00829                                        &providerFrame, &providerIsChild);
00830     if (providerFrame)
00831       aParentContext = providerFrame->GetStyleContext();
00832     // aParentContext could still be null
00833   }
00834 
00835   NS_ASSERTION(aContext, "Failure to get required contexts");
00836   nsStyleContext* actualParentContext = aContext->GetParent();
00837 
00838   if (aParentContext) {
00839     if (aParentContext != actualParentContext) {
00840       DumpContext(aFrame, aContext);
00841       if (aContext == aParentContext) {
00842         fputs("Using parent's style context\n\n", stdout);
00843       }
00844       else {
00845         fputs("Wrong parent style context: ", stdout);
00846         DumpContext(nsnull, actualParentContext);
00847         fputs("should be using: ", stdout);
00848         DumpContext(nsnull, aParentContext);
00849         VerifySameTree(actualParentContext, aParentContext);
00850         fputs("\n", stdout);
00851       }
00852     }
00853   }
00854   else {
00855     if (actualParentContext) {
00856       DumpContext(aFrame, aContext);
00857       fputs("Has parent context: ", stdout);
00858       DumpContext(nsnull, actualParentContext);
00859       fputs("Should be null\n\n", stdout);
00860     }
00861   }
00862 }
00863 
00864 static void
00865 VerifyStyleTree(nsPresContext* aPresContext, nsIFrame* aFrame,
00866                 nsStyleContext* aParentContext)
00867 {
00868   nsStyleContext*  context = aFrame->GetStyleContext();
00869   VerifyContextParent(aPresContext, aFrame, context, nsnull);
00870 
00871   PRInt32 listIndex = 0;
00872   nsIAtom* childList = nsnull;
00873   nsIFrame* child;
00874 
00875   do {
00876     child = aFrame->GetFirstChild(childList);
00877     while (child) {
00878       if (NS_FRAME_OUT_OF_FLOW != (child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
00879         // only do frames that are in flow
00880         if (nsLayoutAtoms::placeholderFrame == child->GetType()) { 
00881           // placeholder: first recurse and verify the out of flow frame,
00882           // then verify the placeholder's context
00883           nsIFrame* outOfFlowFrame =
00884             nsPlaceholderFrame::GetRealFrameForPlaceholder(child);
00885 
00886           // recurse to out of flow frame, letting the parent context get resolved
00887           VerifyStyleTree(aPresContext, outOfFlowFrame, nsnull);
00888 
00889           // verify placeholder using the parent frame's context as
00890           // parent context
00891           VerifyContextParent(aPresContext, child, nsnull, nsnull);
00892         }
00893         else { // regular frame
00894           VerifyStyleTree(aPresContext, child, nsnull);
00895         }
00896       }
00897       child = child->GetNextSibling();
00898     }
00899 
00900     childList = aFrame->GetAdditionalChildListName(listIndex++);
00901   } while (childList);
00902   
00903   // do additional contexts 
00904   PRInt32 contextIndex = -1;
00905   while (1) {
00906     nsStyleContext* extraContext = aFrame->GetAdditionalStyleContext(++contextIndex);
00907     if (extraContext) {
00908       VerifyContextParent(aPresContext, aFrame, extraContext, context);
00909     }
00910     else {
00911       break;
00912     }
00913   }
00914 }
00915 
00916 void
00917 nsFrameManager::DebugVerifyStyleTree(nsIFrame* aFrame)
00918 {
00919   if (aFrame) {
00920     nsStyleContext* context = aFrame->GetStyleContext();
00921     nsStyleContext* parentContext = context->GetParent();
00922     VerifyStyleTree(GetPresContext(), aFrame, parentContext);
00923   }
00924 }
00925 
00926 #endif // DEBUG
00927 
00928 nsresult
00929 nsFrameManager::ReParentStyleContext(nsIFrame* aFrame)
00930 {
00931   // DO NOT verify the style tree before reparenting.  The frame
00932   // tree has already been changed, so this check would just fail.
00933   nsStyleContext* oldContext = aFrame->GetStyleContext();
00934   // XXXbz can oldContext really ever be null?
00935   if (oldContext) {
00936     nsPresContext *presContext = GetPresContext();
00937     nsRefPtr<nsStyleContext> newContext;
00938     nsIFrame* providerFrame = nsnull;
00939     PRBool providerIsChild = PR_FALSE;
00940     nsIFrame* providerChild = nsnull;
00941     aFrame->GetParentStyleContextFrame(presContext, &providerFrame,
00942                                        &providerIsChild);
00943     nsStyleContext* newParentContext = nsnull;
00944     if (providerIsChild) {
00945       ReParentStyleContext(providerFrame);
00946       newParentContext = providerFrame->GetStyleContext();
00947       providerChild = providerFrame;
00948     } else if (providerFrame) {
00949       newParentContext = providerFrame->GetStyleContext();
00950     } else {
00951       NS_NOTREACHED("Reparenting something that has no usable parent? "
00952                     "Shouldn't happen!");
00953     }
00954 
00955     newContext = mStyleSet->ReParentStyleContext(presContext, oldContext,
00956                                                  newParentContext);
00957     if (newContext) {
00958       if (newContext != oldContext) {
00959         PRInt32 listIndex = 0;
00960         nsIAtom* childList = nsnull;
00961         nsIFrame* child;
00962           
00963         aFrame->SetStyleContext(presContext, newContext);
00964 
00965         do {
00966           child = aFrame->GetFirstChild(childList);
00967           while (child) {
00968             // only do frames that are in flow
00969             if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
00970               if (nsLayoutAtoms::placeholderFrame == child->GetType()) {
00971                 // get out of flow frame and recurse there
00972                 nsIFrame* outOfFlowFrame =
00973                   nsPlaceholderFrame::GetRealFrameForPlaceholder(child);
00974                 NS_ASSERTION(outOfFlowFrame, "no out-of-flow frame");
00975 
00976                 NS_ASSERTION(outOfFlowFrame != providerChild,
00977                              "Out of flow provider?");
00978 
00979                 ReParentStyleContext(outOfFlowFrame);
00980 
00981                 // reparent placeholder too
00982                 ReParentStyleContext(child);
00983               }
00984               else if (child != providerChild) {
00985                 // regular frame, not reparented yet
00986                 ReParentStyleContext(child);
00987               }
00988             }
00989 
00990             child = child->GetNextSibling();
00991           }
00992 
00993           childList = aFrame->GetAdditionalChildListName(listIndex++);
00994         } while (childList);
00995 
00996         // do additional contexts 
00997         PRInt32 contextIndex = -1;
00998         while (1) {
00999           nsStyleContext* oldExtraContext =
01000             aFrame->GetAdditionalStyleContext(++contextIndex);
01001           if (oldExtraContext) {
01002             nsRefPtr<nsStyleContext> newExtraContext;
01003             newExtraContext = mStyleSet->ReParentStyleContext(presContext,
01004                                                               oldExtraContext,
01005                                                               newContext);
01006             if (newExtraContext) {
01007               aFrame->SetAdditionalStyleContext(contextIndex, newExtraContext);
01008             }
01009           }
01010           else {
01011             break;
01012           }
01013         }
01014 #ifdef DEBUG
01015         VerifyStyleTree(GetPresContext(), aFrame, newParentContext);
01016 #endif
01017       }
01018     }
01019   }
01020   return NS_OK;
01021 }
01022 
01023 static nsChangeHint
01024 CaptureChange(nsStyleContext* aOldContext, nsStyleContext* aNewContext,
01025               nsIFrame* aFrame, nsIContent* aContent,
01026               nsStyleChangeList* aChangeList, nsChangeHint aMinChange,
01027               nsChangeHint aChangeToAssume)
01028 {
01029   nsChangeHint ourChange = aOldContext->CalcStyleDifference(aNewContext);
01030   NS_UpdateHint(ourChange, aChangeToAssume);
01031   if (NS_UpdateHint(aMinChange, ourChange)) {
01032     aChangeList->AppendChange(aFrame, aContent, ourChange);
01033   }
01034   return aMinChange;
01035 }
01036 
01037 nsChangeHint
01038 nsFrameManager::ReResolveStyleContext(nsPresContext    *aPresContext,
01039                                       nsIFrame          *aFrame,
01040                                       nsIContent        *aParentContent,
01041                                       nsStyleChangeList *aChangeList, 
01042                                       nsChangeHint       aMinChange)
01043 {
01044   // XXXldb get new context from prev-in-flow if possible, to avoid
01045   // duplication.  (Or should we just let |GetContext| handle that?)
01046   // Getting the hint would be nice too, but that's harder.
01047 
01048   // XXXbryner we may be able to avoid some of the refcounting goop here.
01049   // We do need a reference to oldContext for the lifetime of this function, and it's possible
01050   // that the frame has the last reference to it, so AddRef it here.
01051 
01052   nsChangeHint assumeDifferenceHint = NS_STYLE_HINT_NONE;
01053   nsStyleContext* oldContext = aFrame->GetStyleContext();
01054   nsStyleSet* styleSet = aPresContext->StyleSet();
01055 
01056   if (oldContext) {
01057     oldContext->AddRef();
01058     nsIAtom* const pseudoTag = oldContext->GetPseudoType();
01059     nsIContent* localContent = aFrame->GetContent();
01060     nsIContent* content = localContent ? localContent : aParentContent;
01061 
01062     nsStyleContext* parentContext;
01063     nsIFrame* resolvedChild = nsnull;
01064     // Get the frame providing the parent style context.  If it is a
01065     // child, then resolve the provider first.
01066     nsIFrame* providerFrame = nsnull;
01067     PRBool providerIsChild = PR_FALSE;
01068     aFrame->GetParentStyleContextFrame(aPresContext,
01069                                        &providerFrame, &providerIsChild); 
01070     if (!providerIsChild) {
01071       if (providerFrame)
01072         parentContext = providerFrame->GetStyleContext();
01073       else
01074         parentContext = nsnull;
01075     }
01076     else {
01077       // resolve the provider here (before aFrame below).
01078 
01079       // assumeDifferenceHint forces the parent's change to be also
01080       // applied to this frame, no matter what
01081       // nsStyleStruct::CalcStyleDifference says. CalcStyleDifference
01082       // can't be trusted because it assumes any changes to the parent
01083       // style context provider will be automatically propagated to
01084       // the frame(s) with child style contexts.
01085       assumeDifferenceHint = ReResolveStyleContext(aPresContext, providerFrame,
01086                                                    content, aChangeList, aMinChange);
01087 
01088       // The provider's new context becomes the parent context of
01089       // aFrame's context.
01090       parentContext = providerFrame->GetStyleContext();
01091       // Set |resolvedChild| so we don't bother resolving the
01092       // provider again.
01093       resolvedChild = providerFrame;
01094     }
01095     
01096     // do primary context
01097     nsStyleContext* newContext = nsnull;
01098     if (pseudoTag == nsCSSAnonBoxes::mozNonElement) {
01099       NS_ASSERTION(localContent,
01100                    "non pseudo-element frame without content node");
01101       newContext = styleSet->ResolveStyleForNonElement(parentContext).get();
01102     }
01103     else if (pseudoTag) {
01104       nsIContent* pseudoContent =
01105           aParentContent ? aParentContent : localContent;
01106       if (pseudoTag == nsCSSPseudoElements::before ||
01107           pseudoTag == nsCSSPseudoElements::after) {
01108         // XXX what other pseudos do we need to treat like this?
01109         newContext = styleSet->ProbePseudoStyleFor(pseudoContent,
01110                                                    pseudoTag,
01111                                                    parentContext).get();
01112         if (!newContext) {
01113           // This pseudo should no longer exist; gotta reframe
01114           NS_UpdateHint(aMinChange, nsChangeHint_ReconstructFrame);
01115           aChangeList->AppendChange(aFrame, pseudoContent,
01116                                     nsChangeHint_ReconstructFrame);
01117           // We're reframing anyway; just keep the same context
01118           newContext = oldContext;
01119           newContext->AddRef();
01120         }
01121       } else {
01122         newContext = styleSet->ResolvePseudoStyleFor(pseudoContent,
01123                                                      pseudoTag,
01124                                                      parentContext).get();
01125       }
01126     }
01127     else {
01128       NS_ASSERTION(localContent,
01129                    "non pseudo-element frame without content node");
01130       newContext = styleSet->ResolveStyleFor(content, parentContext).get();
01131     }
01132     NS_ASSERTION(newContext, "failed to get new style context");
01133     if (newContext) {
01134       if (!parentContext) {
01135         if (oldContext->GetRuleNode() == newContext->GetRuleNode()) {
01136           // We're the root of the style context tree and the new style
01137           // context returned has the same rule node.  This means that
01138           // we can use FindChildWithRules to keep a lot of the old
01139           // style contexts around.  However, we need to start from the
01140           // same root.
01141           newContext->Release();
01142           newContext = oldContext;
01143           newContext->AddRef();
01144         }
01145       }
01146 
01147       if (newContext != oldContext) {
01148         aMinChange = CaptureChange(oldContext, newContext, aFrame,
01149                                    content, aChangeList, aMinChange,
01150                                    assumeDifferenceHint);
01151         if (!(aMinChange & nsChangeHint_ReconstructFrame)) {
01152           // if frame gets regenerated, let it keep old context
01153           aFrame->SetStyleContext(aPresContext, newContext);
01154         }
01155         // if old context had image and new context does not have the same image, 
01156         // stop the image load for the frame
01157         const nsStyleBackground* oldColor = oldContext->GetStyleBackground();
01158         const nsStyleBackground* newColor = newContext->GetStyleBackground();
01159 
01160         if (oldColor->mBackgroundImage) {
01161           PRBool stopImages = !newColor->mBackgroundImage;
01162           if (!stopImages) {
01163             nsCOMPtr<nsIURI> oldURI, newURI;
01164             oldColor->mBackgroundImage->GetURI(getter_AddRefs(oldURI));
01165             newColor->mBackgroundImage->GetURI(getter_AddRefs(newURI));
01166             PRBool equal;
01167             stopImages =
01168               NS_FAILED(oldURI->Equals(newURI, &equal)) || !equal;
01169           }
01170           if (stopImages) {
01171             // stop the image loading for the frame, the image has changed
01172             aPresContext->StopImagesFor(aFrame);
01173           }
01174         }
01175       }
01176       oldContext->Release();
01177     }
01178     else {
01179       NS_ERROR("resolve style context failed");
01180       newContext = oldContext;  // new context failed, recover... (take ref)
01181       oldContext = nsnull;
01182     }
01183 
01184     // do additional contexts 
01185     PRInt32 contextIndex = -1;
01186     while (1 == 1) {
01187       nsStyleContext* oldExtraContext = nsnull;
01188       oldExtraContext = aFrame->GetAdditionalStyleContext(++contextIndex);
01189       if (oldExtraContext) {
01190         nsStyleContext* newExtraContext = nsnull;
01191         nsIAtom* const extraPseudoTag = oldExtraContext->GetPseudoType();
01192         NS_ASSERTION(extraPseudoTag &&
01193                      extraPseudoTag != nsCSSAnonBoxes::mozNonElement,
01194                      "extra style context is not pseudo element");
01195         newExtraContext = styleSet->ResolvePseudoStyleFor(content,
01196                                                           extraPseudoTag,
01197                                                           newContext).get();
01198         if (newExtraContext) {
01199           if (oldExtraContext != newExtraContext) {
01200             aMinChange = CaptureChange(oldExtraContext, newExtraContext,
01201                                        aFrame, content, aChangeList,
01202                                        aMinChange, assumeDifferenceHint);
01203             if (!(aMinChange & nsChangeHint_ReconstructFrame)) {
01204               aFrame->SetAdditionalStyleContext(contextIndex, newExtraContext);
01205             }
01206           }
01207           newExtraContext->Release();
01208         }
01209       }
01210       else {
01211         break;
01212       }
01213     }
01214 
01215     // now look for undisplayed child content and pseudos
01216     if (localContent && mUndisplayedMap) {
01217       for (UndisplayedNode* undisplayed =
01218                                    mUndisplayedMap->GetFirstNode(localContent);
01219            undisplayed; undisplayed = undisplayed->mNext) {
01220         nsRefPtr<nsStyleContext> undisplayedContext;
01221         nsIAtom* const undisplayedPseudoTag = undisplayed->mStyle->GetPseudoType();
01222         if (!undisplayedPseudoTag) {  // child content
01223           undisplayedContext = styleSet->ResolveStyleFor(undisplayed->mContent,
01224                                                          newContext);
01225         }
01226         else if (undisplayedPseudoTag == nsCSSAnonBoxes::mozNonElement) {
01227           undisplayedContext = styleSet->ResolveStyleForNonElement(newContext);
01228         }
01229         else {  // pseudo element
01230           NS_NOTREACHED("no pseudo elements in undisplayed map");
01231           NS_ASSERTION(undisplayedPseudoTag, "pseudo element without tag");
01232           undisplayedContext = styleSet->ResolvePseudoStyleFor(localContent,
01233                                                                undisplayedPseudoTag,
01234                                                                newContext);
01235         }
01236         if (undisplayedContext) {
01237           const nsStyleDisplay* display = undisplayedContext->GetStyleDisplay();
01238           if (display->mDisplay != NS_STYLE_DISPLAY_NONE) {
01239             aChangeList->AppendChange(nsnull,
01240                                       undisplayed->mContent
01241                                       ? NS_STATIC_CAST(nsIContent*,
01242                                                        undisplayed->mContent)
01243                                       : localContent, 
01244                                       NS_STYLE_HINT_FRAMECHANGE);
01245             // The node should be removed from the undisplayed map when
01246             // we reframe it.
01247           } else {
01248             // update the undisplayed node with the new context
01249             undisplayed->mStyle = undisplayedContext;
01250           }
01251         }
01252       }
01253     }
01254 
01255     if (!(aMinChange & nsChangeHint_ReconstructFrame)) {
01256       // Make sure not to do this for pseudo-frames -- those can't have :before
01257       // or :after content.
01258       if (!pseudoTag && localContent &&
01259           localContent->IsContentOfType(nsIContent::eELEMENT)) {
01260         // Check for a new :before pseudo and an existing :before
01261         // frame, but only if the frame is the first-in-flow.
01262         nsIFrame* prevInFlow = aFrame->GetPrevInFlow();
01263         if (!prevInFlow) {
01264           // Checking for a :before frame is cheaper than getting the
01265           // :before style context.
01266           if (!nsLayoutUtils::GetBeforeFrame(aFrame) &&
01267               nsLayoutUtils::HasPseudoStyle(localContent, newContext,
01268                                             nsCSSPseudoElements::before,
01269                                             aPresContext)) {
01270             // Have to create the new :before frame
01271             NS_UpdateHint(aMinChange, nsChangeHint_ReconstructFrame);
01272             aChangeList->AppendChange(aFrame, content,
01273                                       nsChangeHint_ReconstructFrame);
01274           }
01275         }
01276       }
01277     }
01278 
01279     
01280     if (!(aMinChange & nsChangeHint_ReconstructFrame)) {
01281       // Make sure not to do this for pseudo-frames -- those can't have :before
01282       // or :after content.
01283       if (!pseudoTag && localContent &&
01284           localContent->IsContentOfType(nsIContent::eELEMENT)) {
01285         // Check for new :after content, but only if the frame is the
01286         // first-in-flow.
01287         nsIFrame* nextInFlow = aFrame->GetNextInFlow();
01288 
01289         if (!nextInFlow) {
01290           // Getting the :after frame is more expensive than getting the pseudo
01291           // context, so get the pseudo context first.
01292           if (nsLayoutUtils::HasPseudoStyle(localContent, newContext,
01293                                             nsCSSPseudoElements::after,
01294                                             aPresContext) &&
01295               !nsLayoutUtils::GetAfterFrame(aFrame)) {
01296             // have to create the new :after frame
01297             NS_UpdateHint(aMinChange, nsChangeHint_ReconstructFrame);
01298             aChangeList->AppendChange(aFrame, content,
01299                                       nsChangeHint_ReconstructFrame);
01300           }
01301         }      
01302       }
01303     }
01304     
01305     if (!(aMinChange & nsChangeHint_ReconstructFrame)) {
01306       
01307       // There is no need to waste time crawling into a frame's children on a frame change.
01308       // The act of reconstructing frames will force new style contexts to be resolved on all
01309       // of this frame's descendants anyway, so we want to avoid wasting time processing
01310       // style contexts that we're just going to throw away anyway. - dwh
01311 
01312       // now do children
01313       PRInt32 listIndex = 0;
01314       nsIAtom* childList = nsnull;
01315 
01316       do {
01317         nsIFrame* child = aFrame->GetFirstChild(childList);
01318         while (child) {
01319           if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
01320             // only do frames that are in flow
01321             if (nsLayoutAtoms::placeholderFrame == child->GetType()) { // placeholder
01322               // get out of flow frame and recur there
01323               nsIFrame* outOfFlowFrame =
01324                 nsPlaceholderFrame::GetRealFrameForPlaceholder(child);
01325               NS_ASSERTION(outOfFlowFrame, "no out-of-flow frame");
01326               NS_ASSERTION(outOfFlowFrame != resolvedChild,
01327                            "out-of-flow frame not a true descendant");
01328 
01329               // Note that the out-of-flow may not be a geometric descendant of
01330               // the frame where we started the reresolve.  Therefore, even if
01331               // aMinChange already includes nsChangeHint_ReflowFrame we don't
01332               // want to pass that on to the out-of-flow reresolve, since that
01333               // can lead to the out-of-flow not getting reflown when it should
01334               // be (eg a reresolve starting at <body> that involves reflowing
01335               // the <body> would miss reflowing fixed-pos nodes that also need
01336               // reflow).  In the cases when the out-of-flow _is_ a geometric
01337               // descendant of a frame we already have a reflow hint for,
01338               // reflow coalescing should keep us from doing the work twice.
01339 
01340               // |nsFrame::GetParentStyleContextFrame| checks being out
01341               // of flow so that this works correctly.
01342               ReResolveStyleContext(aPresContext, outOfFlowFrame,
01343                                     content, aChangeList,
01344                                     NS_SubtractHint(aMinChange,
01345                                                     nsChangeHint_ReflowFrame));
01346 
01347               // reresolve placeholder's context under the same parent
01348               // as the out-of-flow frame
01349               ReResolveStyleContext(aPresContext, child, content,
01350                                     aChangeList, aMinChange);
01351             }
01352             else {  // regular child frame
01353               if (child != resolvedChild) {
01354                 ReResolveStyleContext(aPresContext, child, content,
01355                                       aChangeList, aMinChange);
01356               } else {
01357                 NOISY_TRACE_FRAME("child frame already resolved as descendent, skipping",aFrame);
01358               }
01359             }
01360           }
01361           child = child->GetNextSibling();
01362         }
01363 
01364         childList = aFrame->GetAdditionalChildListName(listIndex++);
01365       } while (childList);
01366       // XXX need to do overflow frames???
01367     }
01368 
01369     newContext->Release();
01370   }
01371 
01372   return aMinChange;
01373 }
01374 
01375 nsChangeHint
01376 nsFrameManager::ComputeStyleChangeFor(nsIFrame          *aFrame, 
01377                                       nsStyleChangeList *aChangeList,
01378                                       nsChangeHint       aMinChange)
01379 {
01380   nsChangeHint topLevelChange = aMinChange;
01381 
01382   nsIFrame* frame = aFrame;
01383   nsIFrame* frame2 = aFrame;
01384 
01385   NS_ASSERTION(!frame->GetPrevInFlow(), "must start with the first in flow");
01386 
01387   // We want to start with this frame and walk all its next-in-flows,
01388   // as well as all its special siblings and their next-in-flows,
01389   // reresolving style on all the frames we encounter in this walk.
01390 
01391   nsPropertyTable *propTable = GetPresContext()->PropertyTable();
01392 
01393   do {
01394     // Outer loop over special siblings
01395     do {
01396       // Inner loop over next-in-flows of the current frame
01397       nsChangeHint frameChange =
01398         ReResolveStyleContext(GetPresContext(), frame, nsnull,
01399                               aChangeList, topLevelChange);
01400       NS_UpdateHint(topLevelChange, frameChange);
01401 
01402       if (topLevelChange & nsChangeHint_ReconstructFrame) {
01403         // If it's going to cause a framechange, then don't bother
01404         // with the continuations or special siblings since they'll be
01405         // clobbered by the frame reconstruct anyway.
01406         NS_ASSERTION(!frame->GetPrevInFlow(),
01407                      "continuing frame had more severe impact than first-in-flow");
01408         return topLevelChange;
01409       }
01410 
01411       frame = frame->GetNextInFlow();
01412     } while (frame);
01413 
01414     // Might we have special siblings?
01415     if (!(frame2->GetStateBits() & NS_FRAME_IS_SPECIAL)) {
01416       // nothing more to do here
01417       return topLevelChange;
01418     }
01419     
01420     frame2 = NS_STATIC_CAST(nsIFrame*,
01421          propTable->GetProperty(frame2, nsLayoutAtoms::IBSplitSpecialSibling));
01422     frame = frame2;
01423   } while (frame2);
01424   return topLevelChange;
01425 }
01426 
01427 
01428 nsReStyleHint
01429 nsFrameManager::HasAttributeDependentStyle(nsIContent *aContent,
01430                                            nsIAtom *aAttribute,
01431                                            PRInt32 aModType)
01432 {
01433   nsReStyleHint hint = mStyleSet->HasAttributeDependentStyle(GetPresContext(),
01434                                                              aContent,
01435                                                              aAttribute,
01436                                                              aModType);
01437 
01438   if (aAttribute == nsHTMLAtoms::style) {
01439     // Perhaps should check that it's XUL, SVG, (or HTML) namespace, but
01440     // it doesn't really matter.  Or we could even let
01441     // HTMLCSSStyleSheetImpl::HasAttributeDependentStyle handle it.
01442     hint = nsReStyleHint(hint | eReStyle_Self);
01443   }
01444 
01445   return hint;
01446 }
01447 
01448 // Capture state for a given frame.
01449 // Accept a content id here, in some cases we may not have content (scroll position)
01450 void
01451 nsFrameManager::CaptureFrameStateFor(nsIFrame* aFrame,
01452                                      nsILayoutHistoryState* aState,
01453                                      nsIStatefulFrame::SpecialStateID aID)
01454 {
01455   if (!aFrame || !aState) {
01456     NS_WARNING("null frame, or state");
01457     return;
01458   }
01459 
01460   // Only capture state for stateful frames
01461   nsIStatefulFrame* statefulFrame;
01462   CallQueryInterface(aFrame, &statefulFrame);
01463 
01464   if (!statefulFrame) {
01465     return;
01466   }
01467 
01468   // Capture the state, exit early if we get null (nothing to save)
01469   nsAutoPtr<nsPresState> frameState;
01470   nsresult rv = statefulFrame->SaveState(GetPresContext(),
01471                                          getter_Transfers(frameState));
01472   if (!frameState) {
01473     return;
01474   }
01475 
01476   // Generate the hash key to store the state under
01477   // Exit early if we get empty key
01478   nsCAutoString stateKey;
01479   nsIContent* content = aFrame->GetContent();
01480   nsIDocument* doc = content ? content->GetCurrentDoc() : nsnull;
01481   rv = nsContentUtils::GenerateStateKey(content, doc, aID, stateKey);
01482   if(NS_FAILED(rv) || stateKey.IsEmpty()) {
01483     return;
01484   }
01485 
01486   // Store the state
01487   rv = aState->AddState(stateKey, frameState);
01488   if (NS_SUCCEEDED(rv)) {
01489     // aState owns frameState now.
01490     frameState.forget();
01491   }
01492 }
01493 
01494 void
01495 nsFrameManager::CaptureFrameState(nsIFrame* aFrame,
01496                                   nsILayoutHistoryState* aState)
01497 {
01498   NS_PRECONDITION(nsnull != aFrame && nsnull != aState, "null parameters passed in");
01499 
01500   CaptureFrameStateFor(aFrame, aState);
01501 
01502   // Now capture state recursively for the frame hierarchy rooted at aFrame
01503   nsIAtom*  childListName = nsnull;
01504   PRInt32   childListIndex = 0;
01505   do {    
01506     nsIFrame* childFrame = aFrame->GetFirstChild(childListName);
01507     while (childFrame) {             
01508       CaptureFrameState(childFrame, aState);
01509       // Get the next sibling child frame
01510       childFrame = childFrame->GetNextSibling();
01511     }
01512     childListName = aFrame->GetAdditionalChildListName(childListIndex++);
01513   } while (childListName);
01514 }
01515 
01516 // Restore state for a given frame.
01517 // Accept a content id here, in some cases we may not have content (scroll position)
01518 void
01519 nsFrameManager::RestoreFrameStateFor(nsIFrame* aFrame,
01520                                      nsILayoutHistoryState* aState,
01521                                      nsIStatefulFrame::SpecialStateID aID)
01522 {
01523   if (!aFrame || !aState) {
01524     NS_WARNING("null frame or state");
01525     return;
01526   }
01527 
01528   // Only capture state for stateful frames
01529   nsIStatefulFrame* statefulFrame;
01530   CallQueryInterface(aFrame, &statefulFrame);
01531   if (!statefulFrame) {
01532     return;
01533   }
01534 
01535   // Generate the hash key the state was stored under
01536   // Exit early if we get empty key
01537   nsIContent* content = aFrame->GetContent();
01538   // If we don't have content, we can't generate a hash
01539   // key and there's probably no state information for us.
01540   if (!content) {
01541     return;
01542   }
01543 
01544   nsCAutoString stateKey;
01545   nsIDocument* doc = content->GetCurrentDoc();
01546   nsresult rv = nsContentUtils::GenerateStateKey(content, doc, aID, stateKey);
01547   if (NS_FAILED(rv) || stateKey.IsEmpty()) {
01548     return;
01549   }
01550 
01551   // Get the state from the hash
01552   nsPresState *frameState;
01553   rv = aState->GetState(stateKey, &frameState);
01554   if (!frameState) {
01555     return;
01556   }
01557 
01558   // Restore it
01559   rv = statefulFrame->RestoreState(GetPresContext(), frameState);
01560   if (NS_FAILED(rv)) {
01561     return;
01562   }
01563 
01564   // If we restore ok, remove the state from the state table
01565   aState->RemoveState(stateKey);
01566 }
01567 
01568 void
01569 nsFrameManager::RestoreFrameState(nsIFrame* aFrame,
01570                                   nsILayoutHistoryState* aState)
01571 {
01572   NS_PRECONDITION(nsnull != aFrame && nsnull != aState, "null parameters passed in");
01573   
01574   RestoreFrameStateFor(aFrame, aState);
01575 
01576   // Now restore state recursively for the frame hierarchy rooted at aFrame
01577   nsIAtom*  childListName = nsnull;
01578   PRInt32   childListIndex = 0;
01579   do {    
01580     nsIFrame* childFrame = aFrame->GetFirstChild(childListName);
01581     while (childFrame) {
01582       RestoreFrameState(childFrame, aState);
01583       // Get the next sibling child frame
01584       childFrame = childFrame->GetNextSibling();
01585     }
01586     childListName = aFrame->GetAdditionalChildListName(childListIndex++);
01587   } while (childListName);
01588 }
01589 
01590 //----------------------------------------------------------------------
01591 
01592 static PLHashNumber
01593 HashKey(void* key)
01594 {
01595   return NS_PTR_TO_INT32(key);
01596 }
01597 
01598 static PRIntn
01599 CompareKeys(void* key1, void* key2)
01600 {
01601   return key1 == key2;
01602 }
01603 
01604 //----------------------------------------------------------------------
01605 
01606 MOZ_DECL_CTOR_COUNTER(UndisplayedMap)
01607 
01608 nsFrameManagerBase::UndisplayedMap::UndisplayedMap(PRUint32 aNumBuckets)
01609 {
01610   MOZ_COUNT_CTOR(nsFrameManagerBase::UndisplayedMap);
01611   mTable = PL_NewHashTable(aNumBuckets, (PLHashFunction)HashKey,
01612                            (PLHashComparator)CompareKeys,
01613                            (PLHashComparator)nsnull,
01614                            nsnull, nsnull);
01615   mLastLookup = nsnull;
01616 }
01617 
01618 nsFrameManagerBase::UndisplayedMap::~UndisplayedMap(void)
01619 {
01620   MOZ_COUNT_DTOR(nsFrameManagerBase::UndisplayedMap);
01621   Clear();
01622   PL_HashTableDestroy(mTable);
01623 }
01624 
01625 PLHashEntry**  
01626 nsFrameManagerBase::UndisplayedMap::GetEntryFor(nsIContent* aParentContent)
01627 {
01628   if (mLastLookup && (aParentContent == (*mLastLookup)->key)) {
01629     return mLastLookup;
01630   }
01631   PLHashNumber hashCode = NS_PTR_TO_INT32(aParentContent);
01632   PLHashEntry** entry = PL_HashTableRawLookup(mTable, hashCode, aParentContent);
01633   if (*entry) {
01634     mLastLookup = entry;
01635   }
01636   return entry;
01637 }
01638 
01639 UndisplayedNode* 
01640 nsFrameManagerBase::UndisplayedMap::GetFirstNode(nsIContent* aParentContent)
01641 {
01642   PLHashEntry** entry = GetEntryFor(aParentContent);
01643   if (*entry) {
01644     return (UndisplayedNode*)((*entry)->value);
01645   }
01646   return nsnull;
01647 }
01648 
01649 void
01650 nsFrameManagerBase::UndisplayedMap::AppendNodeFor(UndisplayedNode* aNode,
01651                                                   nsIContent* aParentContent)
01652 {
01653   PLHashEntry** entry = GetEntryFor(aParentContent);
01654   if (*entry) {
01655     UndisplayedNode*  node = (UndisplayedNode*)((*entry)->value);
01656     while (node->mNext) {
01657       if (node->mContent == aNode->mContent) {
01658         // We actually need to check this in optimized builds because
01659         // there are some callers that do this.  See bug 118014, bug
01660         // 136704, etc.
01661         NS_NOTREACHED("node in map twice");
01662         delete aNode;
01663         return;
01664       }
01665       node = node->mNext;
01666     }
01667     node->mNext = aNode;
01668   }
01669   else {
01670     PLHashNumber hashCode = NS_PTR_TO_INT32(aParentContent);
01671     PL_HashTableRawAdd(mTable, entry, hashCode, aParentContent, aNode);
01672     mLastLookup = nsnull; // hashtable may have shifted bucket out from under us
01673   }
01674 }
01675 
01676 nsresult 
01677 nsFrameManagerBase::UndisplayedMap::AddNodeFor(nsIContent* aParentContent,
01678                                                nsIContent* aChild, 
01679                                                nsStyleContext* aStyle)
01680 {
01681   UndisplayedNode*  node = new UndisplayedNode(aChild, aStyle);
01682   if (! node) {
01683     return NS_ERROR_OUT_OF_MEMORY;
01684   }
01685 
01686   AppendNodeFor(node, aParentContent);
01687   return NS_OK;
01688 }
01689 
01690 void
01691 nsFrameManagerBase::UndisplayedMap::RemoveNodeFor(nsIContent* aParentContent,
01692                                                   UndisplayedNode* aNode)
01693 {
01694   PLHashEntry** entry = GetEntryFor(aParentContent);
01695   NS_ASSERTION(*entry, "content not in map");
01696   if (*entry) {
01697     if ((UndisplayedNode*)((*entry)->value) == aNode) {  // first node
01698       if (aNode->mNext) {
01699         (*entry)->value = aNode->mNext;
01700         aNode->mNext = nsnull;
01701       }
01702       else {
01703         PL_HashTableRawRemove(mTable, entry, *entry);
01704         mLastLookup = nsnull; // hashtable may have shifted bucket out from under us
01705       }
01706     }
01707     else {
01708       UndisplayedNode*  node = (UndisplayedNode*)((*entry)->value);
01709       while (node->mNext) {
01710         if (node->mNext == aNode) {
01711           node->mNext = aNode->mNext;
01712           aNode->mNext = nsnull;
01713           break;
01714         }
01715         node = node->mNext;
01716       }
01717     }
01718   }
01719   delete aNode;
01720 }
01721 
01722 void
01723 nsFrameManagerBase::UndisplayedMap::RemoveNodesFor(nsIContent* aParentContent)
01724 {
01725   PLHashEntry** entry = GetEntryFor(aParentContent);
01726   NS_ASSERTION(entry, "content not in map");
01727   if (*entry) {
01728     UndisplayedNode*  node = (UndisplayedNode*)((*entry)->value);
01729     NS_ASSERTION(node, "null node for non-null entry in UndisplayedMap");
01730     delete node;
01731     PL_HashTableRawRemove(mTable, entry, *entry);
01732     mLastLookup = nsnull; // hashtable may have shifted bucket out from under us
01733   }
01734 }
01735 
01736 PR_STATIC_CALLBACK(PRIntn)
01737 RemoveUndisplayedEntry(PLHashEntry* he, PRIntn i, void* arg)
01738 {
01739   UndisplayedNode*  node = (UndisplayedNode*)(he->value);
01740   delete node;
01741   // Remove and free this entry and continue enumerating
01742   return HT_ENUMERATE_REMOVE | HT_ENUMERATE_NEXT;
01743 }
01744 
01745 void
01746 nsFrameManagerBase::UndisplayedMap::Clear(void)
01747 {
01748   mLastLookup = nsnull;
01749   PL_HashTableEnumerateEntries(mTable, RemoveUndisplayedEntry, 0);
01750 }