Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | /* base class #1 for rendering objects that have child lists */ |
michael@0 | 7 | |
michael@0 | 8 | #ifndef nsContainerFrame_h___ |
michael@0 | 9 | #define nsContainerFrame_h___ |
michael@0 | 10 | |
michael@0 | 11 | #include "mozilla/Attributes.h" |
michael@0 | 12 | #include "nsSplittableFrame.h" |
michael@0 | 13 | #include "nsFrameList.h" |
michael@0 | 14 | #include "nsLayoutUtils.h" |
michael@0 | 15 | |
michael@0 | 16 | // Option flags for ReflowChild() and FinishReflowChild() |
michael@0 | 17 | // member functions |
michael@0 | 18 | #define NS_FRAME_NO_MOVE_VIEW 0x0001 |
michael@0 | 19 | #define NS_FRAME_NO_MOVE_FRAME (0x0002 | NS_FRAME_NO_MOVE_VIEW) |
michael@0 | 20 | #define NS_FRAME_NO_SIZE_VIEW 0x0004 |
michael@0 | 21 | #define NS_FRAME_NO_VISIBILITY 0x0008 |
michael@0 | 22 | // Only applies to ReflowChild; if true, don't delete the next-in-flow, even |
michael@0 | 23 | // if the reflow is fully complete. |
michael@0 | 24 | #define NS_FRAME_NO_DELETE_NEXT_IN_FLOW_CHILD 0x0010 |
michael@0 | 25 | |
michael@0 | 26 | class nsOverflowContinuationTracker; |
michael@0 | 27 | namespace mozilla { |
michael@0 | 28 | class FramePropertyTable; |
michael@0 | 29 | } |
michael@0 | 30 | |
michael@0 | 31 | // Some macros for container classes to do sanity checking on |
michael@0 | 32 | // width/height/x/y values computed during reflow. |
michael@0 | 33 | // NOTE: AppUnitsPerCSSPixel value hardwired here to remove the |
michael@0 | 34 | // dependency on nsDeviceContext.h. It doesn't matter if it's a |
michael@0 | 35 | // little off. |
michael@0 | 36 | #ifdef DEBUG |
michael@0 | 37 | #define CRAZY_COORD (1000000*60) |
michael@0 | 38 | #define CRAZY_SIZE(_x) (((_x) < -CRAZY_COORD) || ((_x) > CRAZY_COORD)) |
michael@0 | 39 | #endif |
michael@0 | 40 | |
michael@0 | 41 | /** |
michael@0 | 42 | * Implementation of a container frame. |
michael@0 | 43 | */ |
michael@0 | 44 | class nsContainerFrame : public nsSplittableFrame |
michael@0 | 45 | { |
michael@0 | 46 | public: |
michael@0 | 47 | NS_DECL_FRAMEARENA_HELPERS |
michael@0 | 48 | NS_DECL_QUERYFRAME_TARGET(nsContainerFrame) |
michael@0 | 49 | NS_DECL_QUERYFRAME |
michael@0 | 50 | |
michael@0 | 51 | // nsIFrame overrides |
michael@0 | 52 | virtual void Init(nsIContent* aContent, |
michael@0 | 53 | nsIFrame* aParent, |
michael@0 | 54 | nsIFrame* aPrevInFlow) MOZ_OVERRIDE; |
michael@0 | 55 | virtual nsresult SetInitialChildList(ChildListID aListID, |
michael@0 | 56 | nsFrameList& aChildList) MOZ_OVERRIDE; |
michael@0 | 57 | virtual nsresult AppendFrames(ChildListID aListID, |
michael@0 | 58 | nsFrameList& aFrameList) MOZ_OVERRIDE; |
michael@0 | 59 | virtual nsresult InsertFrames(ChildListID aListID, |
michael@0 | 60 | nsIFrame* aPrevFrame, |
michael@0 | 61 | nsFrameList& aFrameList) MOZ_OVERRIDE; |
michael@0 | 62 | virtual nsresult RemoveFrame(ChildListID aListID, |
michael@0 | 63 | nsIFrame* aOldFrame) MOZ_OVERRIDE; |
michael@0 | 64 | |
michael@0 | 65 | virtual const nsFrameList& GetChildList(ChildListID aList) const MOZ_OVERRIDE; |
michael@0 | 66 | virtual void GetChildLists(nsTArray<ChildList>* aLists) const MOZ_OVERRIDE; |
michael@0 | 67 | virtual void DestroyFrom(nsIFrame* aDestructRoot) MOZ_OVERRIDE; |
michael@0 | 68 | virtual void ChildIsDirty(nsIFrame* aChild) MOZ_OVERRIDE; |
michael@0 | 69 | |
michael@0 | 70 | virtual bool IsLeaf() const MOZ_OVERRIDE; |
michael@0 | 71 | virtual FrameSearchResult PeekOffsetNoAmount(bool aForward, int32_t* aOffset) MOZ_OVERRIDE; |
michael@0 | 72 | virtual FrameSearchResult PeekOffsetCharacter(bool aForward, int32_t* aOffset, |
michael@0 | 73 | bool aRespectClusters = true) MOZ_OVERRIDE; |
michael@0 | 74 | |
michael@0 | 75 | #ifdef DEBUG_FRAME_DUMP |
michael@0 | 76 | void List(FILE* out = stderr, const char* aPrefix = "", uint32_t aFlags = 0) const MOZ_OVERRIDE; |
michael@0 | 77 | #endif |
michael@0 | 78 | |
michael@0 | 79 | // nsContainerFrame methods |
michael@0 | 80 | |
michael@0 | 81 | /** |
michael@0 | 82 | * Helper method to create next-in-flows if necessary. If aFrame |
michael@0 | 83 | * already has a next-in-flow then this method does |
michael@0 | 84 | * nothing. Otherwise, a new continuation frame is created and |
michael@0 | 85 | * linked into the flow. In addition, the new frame is inserted |
michael@0 | 86 | * into the principal child list after aFrame. |
michael@0 | 87 | * @note calling this method on a block frame is illegal. Use |
michael@0 | 88 | * nsBlockFrame::CreateContinuationFor() instead. |
michael@0 | 89 | * @param aNextInFlowResult will contain the next-in-flow |
michael@0 | 90 | * <b>if and only if</b> one is created. If a next-in-flow already |
michael@0 | 91 | * exists aNextInFlowResult is set to nullptr. |
michael@0 | 92 | * @return NS_OK if a next-in-flow already exists or is successfully created. |
michael@0 | 93 | */ |
michael@0 | 94 | nsresult CreateNextInFlow(nsIFrame* aFrame, nsIFrame*& aNextInFlowResult); |
michael@0 | 95 | |
michael@0 | 96 | /** |
michael@0 | 97 | * Delete aNextInFlow and its next-in-flows. |
michael@0 | 98 | * @param aDeletingEmptyFrames if set, then the reflow for aNextInFlow's |
michael@0 | 99 | * content was complete before aNextInFlow, so aNextInFlow and its |
michael@0 | 100 | * next-in-flows no longer map any real content. |
michael@0 | 101 | */ |
michael@0 | 102 | virtual void DeleteNextInFlowChild(nsIFrame* aNextInFlow, |
michael@0 | 103 | bool aDeletingEmptyFrames); |
michael@0 | 104 | |
michael@0 | 105 | /** |
michael@0 | 106 | * Helper method to wrap views around frames. Used by containers |
michael@0 | 107 | * under special circumstances (can be used by leaf frames as well) |
michael@0 | 108 | */ |
michael@0 | 109 | static void CreateViewForFrame(nsIFrame* aFrame, |
michael@0 | 110 | bool aForce); |
michael@0 | 111 | |
michael@0 | 112 | // Positions the frame's view based on the frame's origin |
michael@0 | 113 | static void PositionFrameView(nsIFrame* aKidFrame); |
michael@0 | 114 | |
michael@0 | 115 | static nsresult ReparentFrameView(nsIFrame* aChildFrame, |
michael@0 | 116 | nsIFrame* aOldParentFrame, |
michael@0 | 117 | nsIFrame* aNewParentFrame); |
michael@0 | 118 | |
michael@0 | 119 | static nsresult ReparentFrameViewList(const nsFrameList& aChildFrameList, |
michael@0 | 120 | nsIFrame* aOldParentFrame, |
michael@0 | 121 | nsIFrame* aNewParentFrame); |
michael@0 | 122 | |
michael@0 | 123 | // Set the view's size and position after its frame has been reflowed. |
michael@0 | 124 | // |
michael@0 | 125 | // Flags: |
michael@0 | 126 | // NS_FRAME_NO_MOVE_VIEW - don't position the frame's view. Set this if you |
michael@0 | 127 | // don't want to automatically sync the frame and view |
michael@0 | 128 | // NS_FRAME_NO_SIZE_VIEW - don't size the view |
michael@0 | 129 | static void SyncFrameViewAfterReflow(nsPresContext* aPresContext, |
michael@0 | 130 | nsIFrame* aFrame, |
michael@0 | 131 | nsView* aView, |
michael@0 | 132 | const nsRect& aVisualOverflowArea, |
michael@0 | 133 | uint32_t aFlags = 0); |
michael@0 | 134 | |
michael@0 | 135 | // Syncs properties to the top level view and window, like transparency and |
michael@0 | 136 | // shadow. |
michael@0 | 137 | static void SyncWindowProperties(nsPresContext* aPresContext, |
michael@0 | 138 | nsIFrame* aFrame, |
michael@0 | 139 | nsView* aView, |
michael@0 | 140 | nsRenderingContext* aRC = nullptr); |
michael@0 | 141 | |
michael@0 | 142 | // Sets the view's attributes from the frame style. |
michael@0 | 143 | // - visibility |
michael@0 | 144 | // - clip |
michael@0 | 145 | // Call this when one of these styles changes or when the view has just |
michael@0 | 146 | // been created. |
michael@0 | 147 | // @param aStyleContext can be null, in which case the frame's style context is used |
michael@0 | 148 | static void SyncFrameViewProperties(nsPresContext* aPresContext, |
michael@0 | 149 | nsIFrame* aFrame, |
michael@0 | 150 | nsStyleContext* aStyleContext, |
michael@0 | 151 | nsView* aView, |
michael@0 | 152 | uint32_t aFlags = 0); |
michael@0 | 153 | |
michael@0 | 154 | /** |
michael@0 | 155 | * Converts the minimum and maximum sizes given in inner window app units to |
michael@0 | 156 | * outer window device pixel sizes and assigns these constraints to the widget. |
michael@0 | 157 | * |
michael@0 | 158 | * @param aPresContext pres context |
michael@0 | 159 | * @param aWidget widget for this frame |
michael@0 | 160 | * @param minimum size of the window in app units |
michael@0 | 161 | * @param maxmimum size of the window in app units |
michael@0 | 162 | */ |
michael@0 | 163 | static void SetSizeConstraints(nsPresContext* aPresContext, |
michael@0 | 164 | nsIWidget* aWidget, |
michael@0 | 165 | const nsSize& aMinSize, |
michael@0 | 166 | const nsSize& aMaxSize); |
michael@0 | 167 | |
michael@0 | 168 | // Used by both nsInlineFrame and nsFirstLetterFrame. |
michael@0 | 169 | void DoInlineIntrinsicWidth(nsRenderingContext *aRenderingContext, |
michael@0 | 170 | InlineIntrinsicWidthData *aData, |
michael@0 | 171 | nsLayoutUtils::IntrinsicWidthType aType); |
michael@0 | 172 | |
michael@0 | 173 | /** |
michael@0 | 174 | * This is the CSS block concept of computing 'auto' widths, which most |
michael@0 | 175 | * classes derived from nsContainerFrame want. |
michael@0 | 176 | */ |
michael@0 | 177 | virtual nsSize ComputeAutoSize(nsRenderingContext *aRenderingContext, |
michael@0 | 178 | nsSize aCBSize, nscoord aAvailableWidth, |
michael@0 | 179 | nsSize aMargin, nsSize aBorder, |
michael@0 | 180 | nsSize aPadding, bool aShrinkWrap) MOZ_OVERRIDE; |
michael@0 | 181 | |
michael@0 | 182 | /** |
michael@0 | 183 | * Invokes the WillReflow() function, positions the frame and its view (if |
michael@0 | 184 | * requested), and then calls Reflow(). If the reflow succeeds and the child |
michael@0 | 185 | * frame is complete, deletes any next-in-flows using DeleteNextInFlowChild() |
michael@0 | 186 | * |
michael@0 | 187 | * Flags: |
michael@0 | 188 | * NS_FRAME_NO_MOVE_VIEW - don't position the frame's view. Set this if you |
michael@0 | 189 | * don't want to automatically sync the frame and view |
michael@0 | 190 | * NS_FRAME_NO_MOVE_FRAME - don't move the frame. aX and aY are ignored in this |
michael@0 | 191 | * case. Also implies NS_FRAME_NO_MOVE_VIEW |
michael@0 | 192 | */ |
michael@0 | 193 | nsresult ReflowChild(nsIFrame* aKidFrame, |
michael@0 | 194 | nsPresContext* aPresContext, |
michael@0 | 195 | nsHTMLReflowMetrics& aDesiredSize, |
michael@0 | 196 | const nsHTMLReflowState& aReflowState, |
michael@0 | 197 | nscoord aX, |
michael@0 | 198 | nscoord aY, |
michael@0 | 199 | uint32_t aFlags, |
michael@0 | 200 | nsReflowStatus& aStatus, |
michael@0 | 201 | nsOverflowContinuationTracker* aTracker = nullptr); |
michael@0 | 202 | |
michael@0 | 203 | /** |
michael@0 | 204 | * The second half of frame reflow. Does the following: |
michael@0 | 205 | * - sets the frame's bounds |
michael@0 | 206 | * - sizes and positions (if requested) the frame's view. If the frame's final |
michael@0 | 207 | * position differs from the current position and the frame itself does not |
michael@0 | 208 | * have a view, then any child frames with views are positioned so they stay |
michael@0 | 209 | * in sync |
michael@0 | 210 | * - sets the view's visibility, opacity, content transparency, and clip |
michael@0 | 211 | * - invoked the DidReflow() function |
michael@0 | 212 | * |
michael@0 | 213 | * Flags: |
michael@0 | 214 | * NS_FRAME_NO_MOVE_FRAME - don't move the frame. aX and aY are ignored in this |
michael@0 | 215 | * case. Also implies NS_FRAME_NO_MOVE_VIEW |
michael@0 | 216 | * NS_FRAME_NO_MOVE_VIEW - don't position the frame's view. Set this if you |
michael@0 | 217 | * don't want to automatically sync the frame and view |
michael@0 | 218 | * NS_FRAME_NO_SIZE_VIEW - don't size the frame's view |
michael@0 | 219 | */ |
michael@0 | 220 | static nsresult FinishReflowChild(nsIFrame* aKidFrame, |
michael@0 | 221 | nsPresContext* aPresContext, |
michael@0 | 222 | const nsHTMLReflowMetrics& aDesiredSize, |
michael@0 | 223 | const nsHTMLReflowState* aReflowState, |
michael@0 | 224 | nscoord aX, |
michael@0 | 225 | nscoord aY, |
michael@0 | 226 | uint32_t aFlags); |
michael@0 | 227 | |
michael@0 | 228 | |
michael@0 | 229 | static void PositionChildViews(nsIFrame* aFrame); |
michael@0 | 230 | |
michael@0 | 231 | // ========================================================================== |
michael@0 | 232 | /* Overflow containers are continuation frames that hold overflow. They |
michael@0 | 233 | * are created when the frame runs out of computed height, but still has |
michael@0 | 234 | * too much content to fit in the availableHeight. The parent creates a |
michael@0 | 235 | * continuation as usual, but marks it as NS_FRAME_IS_OVERFLOW_CONTAINER |
michael@0 | 236 | * and adds it to its next-in-flow's overflow container list, either by |
michael@0 | 237 | * adding it directly or by putting it in its own excess overflow containers |
michael@0 | 238 | * list (to be drained by the next-in-flow when it calls |
michael@0 | 239 | * ReflowOverflowContainerChildren). The parent continues reflow as if |
michael@0 | 240 | * the frame was complete once it ran out of computed height, but returns |
michael@0 | 241 | * either an NS_FRAME_NOT_COMPLETE or NS_FRAME_OVERFLOW_INCOMPLETE reflow |
michael@0 | 242 | * status to request a next-in-flow. The parent's next-in-flow is then |
michael@0 | 243 | * responsible for calling ReflowOverflowContainerChildren to (drain and) |
michael@0 | 244 | * reflow these overflow continuations. Overflow containers do not affect |
michael@0 | 245 | * other frames' size or position during reflow (but do affect their |
michael@0 | 246 | * parent's overflow area). |
michael@0 | 247 | * |
michael@0 | 248 | * Overflow container continuations are different from normal continuations |
michael@0 | 249 | * in that |
michael@0 | 250 | * - more than one child of the frame can have its next-in-flow broken |
michael@0 | 251 | * off and pushed into the frame's next-in-flow |
michael@0 | 252 | * - new continuations may need to be spliced into the middle of the list |
michael@0 | 253 | * or deleted continuations slipped out |
michael@0 | 254 | * e.g. A, B, C are all fixed-size containers on one page, all have |
michael@0 | 255 | * overflow beyond availableHeight, and content is dynamically added |
michael@0 | 256 | * and removed from B |
michael@0 | 257 | * As a result, it is not possible to simply prepend the new continuations |
michael@0 | 258 | * to the old list as with the overflowProperty mechanism. To avoid |
michael@0 | 259 | * complicated list splicing, the code assumes only one overflow containers |
michael@0 | 260 | * list exists for a given frame: either its own overflowContainersProperty |
michael@0 | 261 | * or its prev-in-flow's excessOverflowContainersProperty, not both. |
michael@0 | 262 | * |
michael@0 | 263 | * The nsOverflowContinuationTracker helper class should be used for tracking |
michael@0 | 264 | * overflow containers and adding them to the appropriate list. |
michael@0 | 265 | * See nsBlockFrame::Reflow for a sample implementation. |
michael@0 | 266 | */ |
michael@0 | 267 | |
michael@0 | 268 | friend class nsOverflowContinuationTracker; |
michael@0 | 269 | |
michael@0 | 270 | /** |
michael@0 | 271 | * Reflow overflow container children. They are invisible to normal reflow |
michael@0 | 272 | * (i.e. don't affect sizing or placement of other children) and inherit |
michael@0 | 273 | * width and horizontal position from their prev-in-flow. |
michael@0 | 274 | * |
michael@0 | 275 | * This method |
michael@0 | 276 | * 1. Pulls excess overflow containers from the prev-in-flow and adds |
michael@0 | 277 | * them to our overflow container list |
michael@0 | 278 | * 2. Reflows all our overflow container kids |
michael@0 | 279 | * 3. Expands aOverflowRect as necessary to accomodate these children. |
michael@0 | 280 | * 4. Sets aStatus's NS_FRAME_OVERFLOW_IS_INCOMPLETE flag (along with |
michael@0 | 281 | * NS_FRAME_REFLOW_NEXTINFLOW as necessary) if any overflow children |
michael@0 | 282 | * are incomplete and |
michael@0 | 283 | * 5. Prepends a list of their continuations to our excess overflow |
michael@0 | 284 | * container list, to be drained into our next-in-flow when it is |
michael@0 | 285 | * reflowed. |
michael@0 | 286 | * |
michael@0 | 287 | * The caller is responsible for tracking any new overflow container |
michael@0 | 288 | * continuations it makes, removing them from its child list, and |
michael@0 | 289 | * making sure they are stored properly in the overflow container lists. |
michael@0 | 290 | * The nsOverflowContinuationTracker helper class should be used for this. |
michael@0 | 291 | * |
michael@0 | 292 | * (aFlags just gets passed through to ReflowChild) |
michael@0 | 293 | */ |
michael@0 | 294 | nsresult ReflowOverflowContainerChildren(nsPresContext* aPresContext, |
michael@0 | 295 | const nsHTMLReflowState& aReflowState, |
michael@0 | 296 | nsOverflowAreas& aOverflowRects, |
michael@0 | 297 | uint32_t aFlags, |
michael@0 | 298 | nsReflowStatus& aStatus); |
michael@0 | 299 | |
michael@0 | 300 | /** |
michael@0 | 301 | * Move any frames on our overflow list to the end of our principal list. |
michael@0 | 302 | * @return true if there were any overflow frames |
michael@0 | 303 | */ |
michael@0 | 304 | virtual bool DrainSelfOverflowList() MOZ_OVERRIDE; |
michael@0 | 305 | |
michael@0 | 306 | /** |
michael@0 | 307 | * Removes aChild without destroying it and without requesting reflow. |
michael@0 | 308 | * Continuations are not affected. Checks the primary and overflow |
michael@0 | 309 | * or overflow containers and excess overflow containers lists, depending |
michael@0 | 310 | * on whether the NS_FRAME_IS_OVERFLOW_CONTAINER flag is set. Does not |
michael@0 | 311 | * check any other auxiliary lists. |
michael@0 | 312 | * Returns NS_ERROR_UNEXPECTED if we failed to remove aChild. |
michael@0 | 313 | * Returns other error codes if we failed to put back a proptable list. |
michael@0 | 314 | * If aForceNormal is true, only checks the primary and overflow lists |
michael@0 | 315 | * even when the NS_FRAME_IS_OVERFLOW_CONTAINER flag is set. |
michael@0 | 316 | */ |
michael@0 | 317 | virtual nsresult StealFrame(nsIFrame* aChild, |
michael@0 | 318 | bool aForceNormal = false); |
michael@0 | 319 | |
michael@0 | 320 | /** |
michael@0 | 321 | * Removes the next-siblings of aChild without destroying them and without |
michael@0 | 322 | * requesting reflow. Checks the principal and overflow lists (not |
michael@0 | 323 | * overflow containers / excess overflow containers). Does not check any |
michael@0 | 324 | * other auxiliary lists. |
michael@0 | 325 | * @param aChild a child frame or nullptr |
michael@0 | 326 | * @return If aChild is non-null, the next-siblings of aChild, if any. |
michael@0 | 327 | * If aChild is null, all child frames on the principal list, if any. |
michael@0 | 328 | */ |
michael@0 | 329 | nsFrameList StealFramesAfter(nsIFrame* aChild); |
michael@0 | 330 | |
michael@0 | 331 | /** |
michael@0 | 332 | * Add overflow containers to the display list |
michael@0 | 333 | */ |
michael@0 | 334 | void DisplayOverflowContainers(nsDisplayListBuilder* aBuilder, |
michael@0 | 335 | const nsRect& aDirtyRect, |
michael@0 | 336 | const nsDisplayListSet& aLists); |
michael@0 | 337 | |
michael@0 | 338 | /** |
michael@0 | 339 | * Builds display lists for the children. The background |
michael@0 | 340 | * of each child is placed in the Content() list (suitable for inline |
michael@0 | 341 | * children and other elements that behave like inlines, |
michael@0 | 342 | * but not for in-flow block children of blocks). DOES NOT |
michael@0 | 343 | * paint the background/borders/outline of this frame. This should |
michael@0 | 344 | * probably be avoided and eventually removed. It's currently here |
michael@0 | 345 | * to emulate what nsContainerFrame::Paint did. |
michael@0 | 346 | */ |
michael@0 | 347 | virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder, |
michael@0 | 348 | const nsRect& aDirtyRect, |
michael@0 | 349 | const nsDisplayListSet& aLists) MOZ_OVERRIDE; |
michael@0 | 350 | |
michael@0 | 351 | /** |
michael@0 | 352 | * Destructor function for the proptable-stored framelists -- |
michael@0 | 353 | * it should never be called. |
michael@0 | 354 | */ |
michael@0 | 355 | static void DestroyFrameList(void* aPropertyValue) |
michael@0 | 356 | { |
michael@0 | 357 | MOZ_ASSERT(false, "The owning frame should destroy its nsFrameList props"); |
michael@0 | 358 | } |
michael@0 | 359 | |
michael@0 | 360 | static void PlaceFrameView(nsIFrame* aFrame) |
michael@0 | 361 | { |
michael@0 | 362 | if (aFrame->HasView()) |
michael@0 | 363 | nsContainerFrame::PositionFrameView(aFrame); |
michael@0 | 364 | else |
michael@0 | 365 | nsContainerFrame::PositionChildViews(aFrame); |
michael@0 | 366 | } |
michael@0 | 367 | |
michael@0 | 368 | #define NS_DECLARE_FRAME_PROPERTY_FRAMELIST(prop) \ |
michael@0 | 369 | NS_DECLARE_FRAME_PROPERTY(prop, nsContainerFrame::DestroyFrameList) |
michael@0 | 370 | |
michael@0 | 371 | NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OverflowProperty) |
michael@0 | 372 | NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OverflowContainersProperty) |
michael@0 | 373 | NS_DECLARE_FRAME_PROPERTY_FRAMELIST(ExcessOverflowContainersProperty) |
michael@0 | 374 | |
michael@0 | 375 | protected: |
michael@0 | 376 | nsContainerFrame(nsStyleContext* aContext) : nsSplittableFrame(aContext) {} |
michael@0 | 377 | ~nsContainerFrame(); |
michael@0 | 378 | |
michael@0 | 379 | /** |
michael@0 | 380 | * Helper for DestroyFrom. DestroyAbsoluteFrames is called before |
michael@0 | 381 | * destroying frames on lists that can contain placeholders. |
michael@0 | 382 | * Derived classes must do that too, if they destroy such frame lists. |
michael@0 | 383 | * See nsBlockFrame::DestroyFrom for an example. |
michael@0 | 384 | */ |
michael@0 | 385 | void DestroyAbsoluteFrames(nsIFrame* aDestructRoot); |
michael@0 | 386 | |
michael@0 | 387 | /** |
michael@0 | 388 | * Builds a display list for non-block children that behave like |
michael@0 | 389 | * inlines. This puts the background of each child into the |
michael@0 | 390 | * Content() list (suitable for inline children but not for |
michael@0 | 391 | * in-flow block children of blocks). |
michael@0 | 392 | * @param aForcePseudoStack forces each child into a pseudo-stacking-context |
michael@0 | 393 | * so its background and all other display items (except for positioned |
michael@0 | 394 | * display items) go into the Content() list. |
michael@0 | 395 | */ |
michael@0 | 396 | void BuildDisplayListForNonBlockChildren(nsDisplayListBuilder* aBuilder, |
michael@0 | 397 | const nsRect& aDirtyRect, |
michael@0 | 398 | const nsDisplayListSet& aLists, |
michael@0 | 399 | uint32_t aFlags = 0); |
michael@0 | 400 | |
michael@0 | 401 | /** |
michael@0 | 402 | * A version of BuildDisplayList that use DISPLAY_CHILD_INLINE. |
michael@0 | 403 | * Intended as a convenience for derived classes. |
michael@0 | 404 | */ |
michael@0 | 405 | void BuildDisplayListForInline(nsDisplayListBuilder* aBuilder, |
michael@0 | 406 | const nsRect& aDirtyRect, |
michael@0 | 407 | const nsDisplayListSet& aLists) { |
michael@0 | 408 | DisplayBorderBackgroundOutline(aBuilder, aLists); |
michael@0 | 409 | BuildDisplayListForNonBlockChildren(aBuilder, aDirtyRect, aLists, |
michael@0 | 410 | DISPLAY_CHILD_INLINE); |
michael@0 | 411 | } |
michael@0 | 412 | |
michael@0 | 413 | |
michael@0 | 414 | // ========================================================================== |
michael@0 | 415 | /* Overflow Frames are frames that did not fit and must be pulled by |
michael@0 | 416 | * our next-in-flow during its reflow. (The same concept for overflow |
michael@0 | 417 | * containers is called "excess frames". We should probably make the |
michael@0 | 418 | * names match.) |
michael@0 | 419 | */ |
michael@0 | 420 | |
michael@0 | 421 | /** |
michael@0 | 422 | * Get the frames on the overflow list. Can return null if there are no |
michael@0 | 423 | * overflow frames. The caller does NOT take ownership of the list; it's |
michael@0 | 424 | * still owned by this frame. A non-null return value indicates that the |
michael@0 | 425 | * list is nonempty. |
michael@0 | 426 | */ |
michael@0 | 427 | inline nsFrameList* GetOverflowFrames() const; |
michael@0 | 428 | |
michael@0 | 429 | /** |
michael@0 | 430 | * As GetOverflowFrames, but removes the overflow frames property. The |
michael@0 | 431 | * caller is responsible for deleting nsFrameList and either passing |
michael@0 | 432 | * ownership of the frames to someone else or destroying the frames. |
michael@0 | 433 | * A non-null return value indicates that the list is nonempty. The |
michael@0 | 434 | * recommended way to use this function it to assign its return value |
michael@0 | 435 | * into an AutoFrameListPtr. |
michael@0 | 436 | */ |
michael@0 | 437 | inline nsFrameList* StealOverflowFrames(); |
michael@0 | 438 | |
michael@0 | 439 | /** |
michael@0 | 440 | * Set the overflow list. aOverflowFrames must not be an empty list. |
michael@0 | 441 | */ |
michael@0 | 442 | void SetOverflowFrames(const nsFrameList& aOverflowFrames); |
michael@0 | 443 | |
michael@0 | 444 | /** |
michael@0 | 445 | * Destroy the overflow list, which must be empty. |
michael@0 | 446 | */ |
michael@0 | 447 | inline void DestroyOverflowList(); |
michael@0 | 448 | |
michael@0 | 449 | /** |
michael@0 | 450 | * Moves any frames on both the prev-in-flow's overflow list and the |
michael@0 | 451 | * receiver's overflow to the receiver's child list. |
michael@0 | 452 | * |
michael@0 | 453 | * Resets the overlist pointers to nullptr, and updates the receiver's child |
michael@0 | 454 | * count and content mapping. |
michael@0 | 455 | * |
michael@0 | 456 | * @return true if any frames were moved and false otherwise |
michael@0 | 457 | */ |
michael@0 | 458 | bool MoveOverflowToChildList(); |
michael@0 | 459 | |
michael@0 | 460 | /** |
michael@0 | 461 | * Push aFromChild and its next siblings to the next-in-flow. Change |
michael@0 | 462 | * the geometric parent of each frame that's pushed. If there is no |
michael@0 | 463 | * next-in-flow the frames are placed on the overflow list (and the |
michael@0 | 464 | * geometric parent is left unchanged). |
michael@0 | 465 | * |
michael@0 | 466 | * Updates the next-in-flow's child count. Does <b>not</b> update the |
michael@0 | 467 | * pusher's child count. |
michael@0 | 468 | * |
michael@0 | 469 | * @param aFromChild the first child frame to push. It is disconnected from |
michael@0 | 470 | * aPrevSibling |
michael@0 | 471 | * @param aPrevSibling aFromChild's previous sibling. Must not be null. |
michael@0 | 472 | * It's an error to push a parent's first child frame |
michael@0 | 473 | */ |
michael@0 | 474 | void PushChildren(nsIFrame* aFromChild, nsIFrame* aPrevSibling); |
michael@0 | 475 | |
michael@0 | 476 | // ========================================================================== |
michael@0 | 477 | /* |
michael@0 | 478 | * Convenience methods for nsFrameLists stored in the |
michael@0 | 479 | * PresContext's proptable |
michael@0 | 480 | */ |
michael@0 | 481 | |
michael@0 | 482 | /** |
michael@0 | 483 | * Get the PresContext-stored nsFrameList named aPropID for this frame. |
michael@0 | 484 | * May return null. |
michael@0 | 485 | */ |
michael@0 | 486 | nsFrameList* GetPropTableFrames(const FramePropertyDescriptor* aProperty) const; |
michael@0 | 487 | |
michael@0 | 488 | /** |
michael@0 | 489 | * Remove and return the PresContext-stored nsFrameList named aPropID for |
michael@0 | 490 | * this frame. May return null. |
michael@0 | 491 | */ |
michael@0 | 492 | nsFrameList* RemovePropTableFrames(const FramePropertyDescriptor* aProperty); |
michael@0 | 493 | |
michael@0 | 494 | /** |
michael@0 | 495 | * Set the PresContext-stored nsFrameList named aPropID for this frame |
michael@0 | 496 | * to the given aFrameList, which must not be null. |
michael@0 | 497 | */ |
michael@0 | 498 | void SetPropTableFrames(nsFrameList* aFrameList, |
michael@0 | 499 | const FramePropertyDescriptor* aProperty); |
michael@0 | 500 | |
michael@0 | 501 | /** |
michael@0 | 502 | * Safely destroy the frames on the nsFrameList stored on aProp for this |
michael@0 | 503 | * frame then remove the property and delete the frame list. |
michael@0 | 504 | * Nothing happens if the property doesn't exist. |
michael@0 | 505 | */ |
michael@0 | 506 | void SafelyDestroyFrameListProp(nsIFrame* aDestructRoot, |
michael@0 | 507 | nsIPresShell* aPresShell, |
michael@0 | 508 | mozilla::FramePropertyTable* aPropTable, |
michael@0 | 509 | const FramePropertyDescriptor* aProp); |
michael@0 | 510 | |
michael@0 | 511 | // ========================================================================== |
michael@0 | 512 | |
michael@0 | 513 | nsFrameList mFrames; |
michael@0 | 514 | }; |
michael@0 | 515 | |
michael@0 | 516 | // ========================================================================== |
michael@0 | 517 | /* The out-of-flow-related code below is for a hacky way of splitting |
michael@0 | 518 | * absolutely-positioned frames. Basically what we do is split the frame |
michael@0 | 519 | * in nsAbsoluteContainingBlock and pretend the continuation is an overflow |
michael@0 | 520 | * container. This isn't an ideal solution, but it lets us print the content |
michael@0 | 521 | * at least. See bug 154892. |
michael@0 | 522 | */ |
michael@0 | 523 | |
michael@0 | 524 | #define IS_TRUE_OVERFLOW_CONTAINER(frame) \ |
michael@0 | 525 | ( (frame->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) \ |
michael@0 | 526 | && !( (frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) && \ |
michael@0 | 527 | frame->IsAbsolutelyPositioned() ) ) |
michael@0 | 528 | //XXXfr This check isn't quite correct, because it doesn't handle cases |
michael@0 | 529 | // where the out-of-flow has overflow.. but that's rare. |
michael@0 | 530 | // We'll need to revisit the way abspos continuations are handled later |
michael@0 | 531 | // for various reasons, this detail is one of them. See bug 154892 |
michael@0 | 532 | |
michael@0 | 533 | /** |
michael@0 | 534 | * Helper class for tracking overflow container continuations during reflow. |
michael@0 | 535 | * |
michael@0 | 536 | * A frame is related to two sets of overflow containers: those that /are/ |
michael@0 | 537 | * its own children, and those that are /continuations/ of its children. |
michael@0 | 538 | * This tracker walks through those continuations (the frame's NIF's children) |
michael@0 | 539 | * and their prev-in-flows (a subset of the frame's normal and overflow |
michael@0 | 540 | * container children) in parallel. It allows the reflower to synchronously |
michael@0 | 541 | * walk its overflow continuations while it loops through and reflows its |
michael@0 | 542 | * children. This makes it possible to insert new continuations at the correct |
michael@0 | 543 | * place in the overflow containers list. |
michael@0 | 544 | * |
michael@0 | 545 | * The reflower is expected to loop through its children in the same order it |
michael@0 | 546 | * looped through them the last time (if there was a last time). |
michael@0 | 547 | * For each child, the reflower should either |
michael@0 | 548 | * - call Skip for the child if was not reflowed in this pass |
michael@0 | 549 | * - call Insert for the overflow continuation if the child was reflowed |
michael@0 | 550 | * but has incomplete overflow |
michael@0 | 551 | * - call Finished for the child if it was reflowed in this pass but |
michael@0 | 552 | * is either complete or has a normal next-in-flow. This call can |
michael@0 | 553 | * be skipped if the child did not previously have an overflow |
michael@0 | 554 | * continuation. |
michael@0 | 555 | */ |
michael@0 | 556 | class nsOverflowContinuationTracker { |
michael@0 | 557 | public: |
michael@0 | 558 | /** |
michael@0 | 559 | * Initializes an nsOverflowContinuationTracker to help track overflow |
michael@0 | 560 | * continuations of aFrame's children. Typically invoked on 'this'. |
michael@0 | 561 | * |
michael@0 | 562 | * aWalkOOFFrames determines whether the walker skips out-of-flow frames |
michael@0 | 563 | * or skips non-out-of-flow frames. |
michael@0 | 564 | * |
michael@0 | 565 | * Don't set aSkipOverflowContainerChildren to false unless you plan |
michael@0 | 566 | * to walk your own overflow container children. (Usually they are handled |
michael@0 | 567 | * by calling ReflowOverflowContainerChildren.) aWalkOOFFrames is ignored |
michael@0 | 568 | * if aSkipOverflowContainerChildren is false. |
michael@0 | 569 | */ |
michael@0 | 570 | nsOverflowContinuationTracker(nsContainerFrame* aFrame, |
michael@0 | 571 | bool aWalkOOFFrames, |
michael@0 | 572 | bool aSkipOverflowContainerChildren = true); |
michael@0 | 573 | /** |
michael@0 | 574 | * This function adds an overflow continuation to our running list and |
michael@0 | 575 | * sets its NS_FRAME_IS_OVERFLOW_CONTAINER flag. |
michael@0 | 576 | * |
michael@0 | 577 | * aReflowStatus should preferably be specific to the recently-reflowed |
michael@0 | 578 | * child and not influenced by any of its siblings' statuses. This |
michael@0 | 579 | * function sets the NS_FRAME_IS_DIRTY bit on aOverflowCont if it needs |
michael@0 | 580 | * to be reflowed. (Its need for reflow depends on changes to its |
michael@0 | 581 | * prev-in-flow, not to its parent--for whom it is invisible, reflow-wise.) |
michael@0 | 582 | * |
michael@0 | 583 | * The caller MUST disconnect the frame from its parent's child list |
michael@0 | 584 | * if it was not previously an NS_FRAME_IS_OVERFLOW_CONTAINER (because |
michael@0 | 585 | * StealFrame is much more inefficient than disconnecting in place |
michael@0 | 586 | * during Reflow, which the caller is able to do but we are not). |
michael@0 | 587 | * |
michael@0 | 588 | * The caller MUST NOT disconnect the frame from its parent's |
michael@0 | 589 | * child list if it is already an NS_FRAME_IS_OVERFLOW_CONTAINER. |
michael@0 | 590 | * (In this case we will disconnect and reconnect it ourselves.) |
michael@0 | 591 | */ |
michael@0 | 592 | nsresult Insert(nsIFrame* aOverflowCont, |
michael@0 | 593 | nsReflowStatus& aReflowStatus); |
michael@0 | 594 | /** |
michael@0 | 595 | * Begin/EndFinish() must be called for each child that is reflowed |
michael@0 | 596 | * but no longer has an overflow continuation. (It may be called for |
michael@0 | 597 | * other children, but in that case has no effect.) It increments our |
michael@0 | 598 | * walker and makes sure we drop any dangling pointers to its |
michael@0 | 599 | * next-in-flow. This function MUST be called before stealing or |
michael@0 | 600 | * deleting aChild's next-in-flow. |
michael@0 | 601 | * The AutoFinish helper object does that for you. Use it like so: |
michael@0 | 602 | * if (kidNextInFlow) { |
michael@0 | 603 | * nsOverflowContinuationTracker::AutoFinish fini(tracker, kid); |
michael@0 | 604 | * ... DeleteNextInFlowChild/StealFrame(kidNextInFlow) here ... |
michael@0 | 605 | * } |
michael@0 | 606 | */ |
michael@0 | 607 | class MOZ_STACK_CLASS AutoFinish { |
michael@0 | 608 | public: |
michael@0 | 609 | AutoFinish(nsOverflowContinuationTracker* aTracker, nsIFrame* aChild) |
michael@0 | 610 | : mTracker(aTracker), mChild(aChild) |
michael@0 | 611 | { |
michael@0 | 612 | if (mTracker) mTracker->BeginFinish(mChild); |
michael@0 | 613 | } |
michael@0 | 614 | ~AutoFinish() |
michael@0 | 615 | { |
michael@0 | 616 | if (mTracker) mTracker->EndFinish(mChild); |
michael@0 | 617 | } |
michael@0 | 618 | private: |
michael@0 | 619 | nsOverflowContinuationTracker* mTracker; |
michael@0 | 620 | nsIFrame* mChild; |
michael@0 | 621 | }; |
michael@0 | 622 | |
michael@0 | 623 | /** |
michael@0 | 624 | * This function should be called for each child that isn't reflowed. |
michael@0 | 625 | * It increments our walker and sets the NS_FRAME_OVERFLOW_INCOMPLETE |
michael@0 | 626 | * reflow flag if it encounters an overflow continuation so that our |
michael@0 | 627 | * next-in-flow doesn't get prematurely deleted. It MUST be called on |
michael@0 | 628 | * each unreflowed child that has an overflow container continuation; |
michael@0 | 629 | * it MAY be called on other children, but it isn't necessary (doesn't |
michael@0 | 630 | * do anything). |
michael@0 | 631 | */ |
michael@0 | 632 | void Skip(nsIFrame* aChild, nsReflowStatus& aReflowStatus) |
michael@0 | 633 | { |
michael@0 | 634 | NS_PRECONDITION(aChild, "null ptr"); |
michael@0 | 635 | if (aChild == mSentry) { |
michael@0 | 636 | StepForward(); |
michael@0 | 637 | NS_MergeReflowStatusInto(&aReflowStatus, NS_FRAME_OVERFLOW_INCOMPLETE); |
michael@0 | 638 | } |
michael@0 | 639 | } |
michael@0 | 640 | |
michael@0 | 641 | private: |
michael@0 | 642 | |
michael@0 | 643 | /** |
michael@0 | 644 | * @see class AutoFinish |
michael@0 | 645 | */ |
michael@0 | 646 | void BeginFinish(nsIFrame* aChild); |
michael@0 | 647 | void EndFinish(nsIFrame* aChild); |
michael@0 | 648 | |
michael@0 | 649 | void SetupOverflowContList(); |
michael@0 | 650 | void SetUpListWalker(); |
michael@0 | 651 | void StepForward(); |
michael@0 | 652 | |
michael@0 | 653 | /* We hold a pointer to either the next-in-flow's overflow containers list |
michael@0 | 654 | or, if that doesn't exist, our frame's excess overflow containers list. |
michael@0 | 655 | We need to make sure that we drop that pointer if the list becomes |
michael@0 | 656 | empty and is deleted elsewhere. */ |
michael@0 | 657 | nsFrameList* mOverflowContList; |
michael@0 | 658 | /* We hold a pointer to the most recently-reflowed child that has an |
michael@0 | 659 | overflow container next-in-flow. We do this because it's a known |
michael@0 | 660 | good point; this pointer won't be deleted on us. We can use it to |
michael@0 | 661 | recover our place in the list. */ |
michael@0 | 662 | nsIFrame* mPrevOverflowCont; |
michael@0 | 663 | /* This is a pointer to the next overflow container's prev-in-flow, which |
michael@0 | 664 | is (or should be) a child of our frame. When we hit this, we will need |
michael@0 | 665 | to increment this walker to the next overflow container. */ |
michael@0 | 666 | nsIFrame* mSentry; |
michael@0 | 667 | /* Parent of all frames in mOverflowContList. If our mOverflowContList |
michael@0 | 668 | is an excessOverflowContainersProperty, or null, then this is our frame |
michael@0 | 669 | (the frame that was passed in to our constructor). Otherwise this is |
michael@0 | 670 | that frame's next-in-flow, and our mOverflowContList is mParent's |
michael@0 | 671 | overflowContainersProperty */ |
michael@0 | 672 | nsContainerFrame* mParent; |
michael@0 | 673 | /* Tells SetUpListWalker whether or not to walk us past any continuations |
michael@0 | 674 | of overflow containers. aWalkOOFFrames is ignored when this is false. */ |
michael@0 | 675 | bool mSkipOverflowContainerChildren; |
michael@0 | 676 | /* Tells us whether to pay attention to OOF frames or non-OOF frames */ |
michael@0 | 677 | bool mWalkOOFFrames; |
michael@0 | 678 | }; |
michael@0 | 679 | |
michael@0 | 680 | inline |
michael@0 | 681 | nsFrameList* |
michael@0 | 682 | nsContainerFrame::GetOverflowFrames() const |
michael@0 | 683 | { |
michael@0 | 684 | nsFrameList* list = |
michael@0 | 685 | static_cast<nsFrameList*>(Properties().Get(OverflowProperty())); |
michael@0 | 686 | NS_ASSERTION(!list || !list->IsEmpty(), "Unexpected empty overflow list"); |
michael@0 | 687 | return list; |
michael@0 | 688 | } |
michael@0 | 689 | |
michael@0 | 690 | inline |
michael@0 | 691 | nsFrameList* |
michael@0 | 692 | nsContainerFrame::StealOverflowFrames() |
michael@0 | 693 | { |
michael@0 | 694 | nsFrameList* list = |
michael@0 | 695 | static_cast<nsFrameList*>(Properties().Remove(OverflowProperty())); |
michael@0 | 696 | NS_ASSERTION(!list || !list->IsEmpty(), "Unexpected empty overflow list"); |
michael@0 | 697 | return list; |
michael@0 | 698 | } |
michael@0 | 699 | |
michael@0 | 700 | inline void |
michael@0 | 701 | nsContainerFrame::DestroyOverflowList() |
michael@0 | 702 | { |
michael@0 | 703 | nsFrameList* list = RemovePropTableFrames(OverflowProperty()); |
michael@0 | 704 | MOZ_ASSERT(list && list->IsEmpty()); |
michael@0 | 705 | list->Delete(PresContext()->PresShell()); |
michael@0 | 706 | } |
michael@0 | 707 | |
michael@0 | 708 | #endif /* nsContainerFrame_h___ */ |