Back to index

lightning-sunbird  0.9+nobinonly
nsBoxLayoutState.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla Communicator client code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 //
00039 // Eric Vaughan
00040 // Netscape Communications
00041 //
00042 // See documentation in associated header file
00043 //
00044 
00045 #include "nsBoxLayoutState.h"
00046 #include "nsReflowPath.h"
00047 #include "nsBoxFrame.h"
00048 #include "nsHTMLAtoms.h"
00049 #include "nsXULAtoms.h"
00050 #include "nsIContent.h"
00051 #include "nsPresContext.h"
00052 
00053 nsBoxLayoutState::nsBoxLayoutState(nsPresContext* aPresContext):mPresContext(aPresContext), 
00054                                                                  mReflowState(nsnull), 
00055                                                                  mType(Dirty),
00056                                                                  mMaxElementWidth(nsnull),
00057                                                                  mLayoutFlags(0),
00058                                                                  mPaintingDisabled(PR_FALSE)
00059 {
00060   NS_ASSERTION(mPresContext, "PresContext must be non-null");
00061 }
00062 
00063 nsBoxLayoutState::nsBoxLayoutState(const nsBoxLayoutState& aState)
00064 {
00065   mPresContext = aState.mPresContext;
00066   mType        = aState.mType;
00067   mReflowState = aState.mReflowState;
00068   mMaxElementWidth = aState.mMaxElementWidth;
00069   mLayoutFlags = aState.mLayoutFlags;
00070   mPaintingDisabled = aState.mPaintingDisabled;
00071 
00072   NS_ASSERTION(mPresContext, "PresContext must be non-null");
00073 }
00074 
00075 nsBoxLayoutState::nsBoxLayoutState(nsIPresShell* aShell):mReflowState(nsnull), 
00076                                                          mType(Dirty),
00077                                                          mMaxElementWidth(nsnull),
00078                                                          mLayoutFlags(0),
00079                                                          mPaintingDisabled(PR_FALSE)
00080 {
00081   mPresContext = aShell->GetPresContext();
00082   NS_ASSERTION(mPresContext, "PresContext must be non-null");
00083 }
00084 
00085 nsBoxLayoutState::nsBoxLayoutState(nsPresContext* aPresContext, 
00086                                    const nsHTMLReflowState& aReflowState, 
00087                                    nsHTMLReflowMetrics& aDesiredSize):mPresContext(aPresContext),
00088                                                                       mReflowState(&aReflowState),                                                                    
00089                                                                       mType(Dirty),
00090                                                                       mMaxElementWidth(nsnull),
00091                                                                       mLayoutFlags(0),
00092                                                                       mPaintingDisabled(PR_FALSE)
00093 
00094                                                                                         
00095 
00096 {
00097   if (aDesiredSize.mComputeMEW)
00098     mMaxElementWidth = &aDesiredSize.mMaxElementWidth;
00099   NS_ASSERTION(mPresContext, "PresContext must be non-null");
00100 }
00101 
00102 void
00103 nsBoxLayoutState::HandleReflow(nsIBox* aRootBox)
00104 {
00105       switch(mReflowState->reason)
00106       {
00107          case eReflowReason_Incremental: 
00108          {
00109            // XXXwaterson according to evaughan's prior comment here,
00110            // the target ought not to be a box frame. This makes some
00111            // of the logic in `Unwind' confusing.
00112            Unwind(mReflowState->path, aRootBox);
00113            mType = Dirty;
00114            break;  
00115          }
00116 
00117          case eReflowReason_Dirty: 
00118             mType = Dirty;
00119          break;
00120 
00121          // if the a resize reflow then it doesn't need to be reflowed. Only if the size is different
00122          // from the new size would we actually do a reflow
00123          case eReflowReason_Resize:
00124             mType = Resize;
00125          break;
00126 
00127          // if its an initial reflow we must place the child.
00128          // otherwise we might think it was already placed when it wasn't
00129          case eReflowReason_Initial:
00130             mType = Initial;
00131          break;
00132 
00133          case eReflowReason_StyleChange:
00134            // printf("STYLE CHANGE REFLOW. Blowing away all box caches!!\n");
00135             aRootBox->MarkChildrenStyleChange();
00136             // fall through to dirty
00137 
00138          default:
00139             mType = Dirty;
00140       }
00141 }
00142 
00143 
00144 void
00145 nsBoxLayoutState::Unwind(nsReflowPath* aReflowPath, nsIBox* aRootBox)
00146 {
00147   // If incremental, unwind the reflow path, updating dirty bits
00148   // appropriately. We recursively descend through the reflow tree,
00149   // clearing the NS_FRAME_HAS_DIRTY_CHILDREN bit on each frame until
00150   // we reach a target frame. When we reach a target frame, we set the
00151   // NS_FRAME_HAS_DIRTY_CHILDREN bit on the root box's frame, and then
00152   // call the target box's MarkDirty method. This will reset the
00153   // NS_FRAME_HAS_DIRTY_CHILDREN bit on each box on the path back to
00154   // root, as well as initialize each box correctly for a dirty
00155   // layout.
00156   nsReflowPath::iterator iter = aReflowPath->FirstChild();
00157   nsReflowPath::iterator end = aReflowPath->EndChildren();
00158 
00159   for ( ; iter != end; ++iter) {
00160     // Get the box for the given frame.
00161     PRBool isAdaptor = PR_FALSE;
00162     nsIBox* ibox = GetBoxForFrame(*iter, isAdaptor);
00163     if (! ibox) {
00164       NS_ERROR("This should not happen! We should always get a box");
00165       continue;
00166     }
00167 
00168     // Clear the dirty-children bit. This will be re-set by MarkDirty
00169     // once we reach a target.
00170     (*iter)->RemoveStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
00171 
00172     if (isAdaptor) {
00173       // It's nested HTML. Mark the root box's frame with
00174       // NS_FRAME_HAS_DIRTY_CHILDREN so MarkDirty won't walk off the
00175       // top of the box hierarchy and schedule another reflow command.
00176       aRootBox->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
00177 
00178       // Clear the frame's dirty bit so that MarkDirty doesn't
00179       // optimize the layout away.
00180       (*iter)->RemoveStateBits(NS_FRAME_IS_DIRTY);
00181 
00182       // Mark the adaptor dirty.
00183       ibox->MarkDirty(*this);      
00184         
00185       // We are done and we did not coelesce.
00186       continue;
00187     }
00188 
00189     // Is the box frame the target?
00190     // XXXwaterson according to the evaughan's previous comments in
00191     // HandleReflow, it ought to never be. Yet here we are.
00192     nsHTMLReflowCommand *command = iter.get()->mReflowCommand;
00193     if (command) {
00194       // Mark the root box's frame with NS_FRAME_HAS_DIRTY_CHILDREN so
00195       // that MarkDirty won't walk off the top of the box hierarchy
00196       // and schedule another reflow command.
00197       aRootBox->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
00198 
00199       // The target is a box. Mark it dirty, generating a new reflow
00200       // command targeted at us and coelesce out this one.
00201       nsReflowType type;
00202       command->GetType(type);
00203 
00204       if (type == eReflowType_StyleChanged) {
00205         ibox->MarkStyleChange(*this);
00206 
00207         // could be a visiblity change. Like collapse so we need to dirty
00208         // parent so it gets redrawn. But be carefull we
00209         // don't want to just mark dirty that would notify the
00210         // box and it would notify its layout manager. This would 
00211         // be really bad for grid because it would blow away
00212         // all is cached infomation for is colums and rows. Because the
00213         // our parent is most likely a rows or columns and it will think
00214         // its child is getting bigger or something.
00215         nsIBox* parent;
00216         ibox->GetParentBox(&parent);
00217         if (parent) {
00218           parent->AddStateBits(NS_FRAME_IS_DIRTY);
00219         }
00220 
00221       }
00222       else
00223         ibox->MarkDirty(*this);      
00224     }
00225 
00226     // Recursively unwind the reflow path.
00227     Unwind(iter.get(), aRootBox);
00228   }
00229 }
00230 
00231 nsIBox*
00232 nsBoxLayoutState::GetBoxForFrame(nsIFrame* aFrame, PRBool& aIsAdaptor)
00233 {
00234   if (aFrame && !aFrame->IsBoxFrame())
00235     aIsAdaptor = PR_TRUE;
00236 
00237   return aFrame;
00238 }
00239 
00240 /*
00241 void
00242 nsBoxLayoutState::DirtyAllChildren(nsBoxLayoutState& aState, nsIBox* aBox)
00243 {
00244     aBox->MarkDirty(aState);
00245 
00246     nsIBox* first = nsnull;
00247     aBox->GetChildBox(&first);
00248     if (first)
00249        aBox->MarkDirtyChildren(aState);
00250 
00251     while(first)
00252     {
00253       DirtyAllChildren(aState, first);
00254       first->GetNextBox(&first);
00255     }
00256 }
00257 */
00258 
00259 void* 
00260 nsBoxLayoutState::Allocate(size_t sz, nsIPresShell* aPresShell)
00261 {
00262   // Check the recycle list first.
00263   void* result = aPresShell->AllocateFrame(sz);
00264   
00265   if (result) {
00266     memset(result, 0, sz);
00267   }
00268 
00269   return result;
00270 }
00271 
00272 // Overridden to prevent the global delete from being called, since the memory
00273 // came out of an nsIArena instead of the global delete operator's heap.
00274 void 
00275 nsBoxLayoutState::Free(void* aPtr, size_t sz)
00276 {
00277   // Don't let the memory be freed, since it will be recycled
00278   // instead. Don't call the global operator delete.
00279 
00280   // Stash the size of the object in the first four bytes of the
00281   // freed up memory.  The Destroy method can then use this information
00282   // to recycle the object.
00283   size_t* szPtr = (size_t*)aPtr;
00284   *szPtr = sz;
00285 }
00286 
00287 void
00288 nsBoxLayoutState::RecycleFreedMemory(nsIPresShell* aShell, void* aMem)
00289 {
00290   size_t* sz = (size_t*)aMem;
00291   aShell->FreeFrame(*sz, aMem);
00292 }