Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/layout/generic/nsContainerFrame.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
/* base class #1 for rendering objects that have child lists */
8
9
#include "nsContainerFrame.h"
10
11
#include "mozilla/ComputedStyle.h"
12
#include "mozilla/dom/HTMLSummaryElement.h"
13
#include "nsAbsoluteContainingBlock.h"
14
#include "nsAttrValue.h"
15
#include "nsAttrValueInlines.h"
16
#include "nsFlexContainerFrame.h"
17
#include "nsIDocument.h"
18
#include "nsPresContext.h"
19
#include "nsRect.h"
20
#include "nsPoint.h"
21
#include "nsStyleConsts.h"
22
#include "nsView.h"
23
#include "nsIPresShell.h"
24
#include "nsCOMPtr.h"
25
#include "nsGkAtoms.h"
26
#include "nsViewManager.h"
27
#include "nsIWidget.h"
28
#include "nsCSSRendering.h"
29
#include "nsError.h"
30
#include "nsDisplayList.h"
31
#include "nsIBaseWindow.h"
32
#include "nsBoxLayoutState.h"
33
#include "nsCSSFrameConstructor.h"
34
#include "nsBlockFrame.h"
35
#include "nsBulletFrame.h"
36
#include "nsPlaceholderFrame.h"
37
#include "mozilla/AutoRestore.h"
38
#include "nsIFrameInlines.h"
39
#include "nsPrintfCString.h"
40
#include <algorithm>
41
42
using namespace mozilla;
43
using namespace mozilla::dom;
44
using namespace mozilla::layout;
45
46
nsContainerFrame::~nsContainerFrame()
47
0
{
48
0
}
49
50
0
NS_QUERYFRAME_HEAD(nsContainerFrame)
51
0
  NS_QUERYFRAME_ENTRY(nsContainerFrame)
52
0
NS_QUERYFRAME_TAIL_INHERITING(nsSplittableFrame)
53
54
void
55
nsContainerFrame::Init(nsIContent*       aContent,
56
                       nsContainerFrame* aParent,
57
                       nsIFrame*         aPrevInFlow)
58
0
{
59
0
  nsSplittableFrame::Init(aContent, aParent, aPrevInFlow);
60
0
  if (aPrevInFlow) {
61
0
    // Make sure we copy bits from our prev-in-flow that will affect
62
0
    // us. A continuation for a container frame needs to know if it
63
0
    // has a child with a view so that we'll properly reposition it.
64
0
    if (aPrevInFlow->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW)
65
0
      AddStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW);
66
0
  }
67
0
}
68
69
void
70
nsContainerFrame::SetInitialChildList(ChildListID  aListID,
71
                                      nsFrameList& aChildList)
72
0
{
73
#ifdef DEBUG
74
  nsFrame::VerifyDirtyBitSet(aChildList);
75
#endif
76
0
  if (aListID == kPrincipalList) {
77
0
    MOZ_ASSERT(mFrames.IsEmpty(),
78
0
               "unexpected second call to SetInitialChildList");
79
0
    mFrames.SetFrames(aChildList);
80
0
  } else if (aListID == kBackdropList) {
81
0
    MOZ_ASSERT(StyleDisplay()->mTopLayer != NS_STYLE_TOP_LAYER_NONE,
82
0
               "Only top layer frames should have backdrop");
83
0
    MOZ_ASSERT(GetStateBits() & NS_FRAME_OUT_OF_FLOW,
84
0
               "Top layer frames should be out-of-flow");
85
0
    MOZ_ASSERT(!GetProperty(BackdropProperty()),
86
0
               "We shouldn't have setup backdrop frame list before");
87
#ifdef DEBUG
88
    {
89
      nsIFrame* placeholder = aChildList.FirstChild();
90
      MOZ_ASSERT(aChildList.OnlyChild(), "Should have only one backdrop");
91
      MOZ_ASSERT(placeholder->IsPlaceholderFrame(),
92
                "The frame to be stored should be a placeholder");
93
      MOZ_ASSERT(static_cast<nsPlaceholderFrame*>(placeholder)->
94
                GetOutOfFlowFrame()->IsBackdropFrame(),
95
                "The placeholder should points to a backdrop frame");
96
    }
97
#endif
98
    nsFrameList* list = new (PresShell()) nsFrameList(aChildList);
99
0
    SetProperty(BackdropProperty(), list);
100
0
  } else {
101
0
    MOZ_ASSERT_UNREACHABLE("Unexpected child list");
102
0
  }
103
0
}
104
105
void
106
nsContainerFrame::AppendFrames(ChildListID  aListID,
107
                               nsFrameList& aFrameList)
108
0
{
109
0
  MOZ_ASSERT(aListID == kPrincipalList || aListID == kNoReflowPrincipalList,
110
0
             "unexpected child list");
111
0
112
0
  if (MOZ_UNLIKELY(aFrameList.IsEmpty())) {
113
0
    return;
114
0
  }
115
0
116
0
  DrainSelfOverflowList(); // ensure the last frame is in mFrames
117
0
  mFrames.AppendFrames(this, aFrameList);
118
0
119
0
  if (aListID != kNoReflowPrincipalList) {
120
0
    PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange,
121
0
                                  NS_FRAME_HAS_DIRTY_CHILDREN);
122
0
  }
123
0
}
124
125
void
126
nsContainerFrame::InsertFrames(ChildListID aListID,
127
                               nsIFrame* aPrevFrame,
128
                               nsFrameList& aFrameList)
129
0
{
130
0
  MOZ_ASSERT(aListID == kPrincipalList || aListID == kNoReflowPrincipalList,
131
0
             "unexpected child list");
132
0
  NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
133
0
               "inserting after sibling frame with different parent");
134
0
135
0
  if (MOZ_UNLIKELY(aFrameList.IsEmpty())) {
136
0
    return;
137
0
  }
138
0
139
0
  DrainSelfOverflowList(); // ensure aPrevFrame is in mFrames
140
0
  mFrames.InsertFrames(this, aPrevFrame, aFrameList);
141
0
142
0
  if (aListID != kNoReflowPrincipalList) {
143
0
    PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange,
144
0
                                  NS_FRAME_HAS_DIRTY_CHILDREN);
145
0
  }
146
0
}
147
148
void
149
nsContainerFrame::RemoveFrame(ChildListID aListID,
150
                              nsIFrame* aOldFrame)
151
0
{
152
0
  MOZ_ASSERT(aListID == kPrincipalList || aListID == kNoReflowPrincipalList,
153
0
             "unexpected child list");
154
0
155
0
  AutoTArray<nsIFrame*, 10> continuations;
156
0
  {
157
0
    nsIFrame* continuation = aOldFrame;
158
0
    while (continuation) {
159
0
      continuations.AppendElement(continuation);
160
0
      continuation = continuation->GetNextContinuation();
161
0
    }
162
0
  }
163
0
164
0
  nsIPresShell* shell = PresShell();
165
0
  nsContainerFrame* lastParent = nullptr;
166
0
167
0
  // Loop and destroy aOldFrame and all of its continuations.
168
0
  //
169
0
  // Request a reflow on the parent frames involved unless we were explicitly
170
0
  // told not to (kNoReflowPrincipalList).
171
0
  const bool generateReflowCommand = (kNoReflowPrincipalList != aListID);
172
0
  for (nsIFrame* continuation : Reversed(continuations)) {
173
0
    nsContainerFrame* parent = continuation->GetParent();
174
0
175
0
    // Please note that 'parent' may not actually be where 'continuation' lives.
176
0
    // We really MUST use StealFrame() and nothing else here.
177
0
    // @see nsInlineFrame::StealFrame for details.
178
0
    parent->StealFrame(continuation);
179
0
    continuation->Destroy();
180
0
    if (generateReflowCommand && parent != lastParent) {
181
0
      shell->FrameNeedsReflow(parent, nsIPresShell::eTreeChange,
182
0
                              NS_FRAME_HAS_DIRTY_CHILDREN);
183
0
      lastParent = parent;
184
0
    }
185
0
  }
186
0
}
187
188
void
189
nsContainerFrame::DestroyAbsoluteFrames(nsIFrame* aDestructRoot,
190
                                        PostDestroyData& aPostDestroyData)
191
0
{
192
0
  if (IsAbsoluteContainer()) {
193
0
    GetAbsoluteContainingBlock()->DestroyFrames(this, aDestructRoot, aPostDestroyData);
194
0
    MarkAsNotAbsoluteContainingBlock();
195
0
  }
196
0
}
197
198
void
199
nsContainerFrame::SafelyDestroyFrameListProp(nsIFrame*        aDestructRoot,
200
                                             PostDestroyData& aPostDestroyData,
201
                                             nsIPresShell*    aPresShell,
202
                                             FrameListPropertyDescriptor aProp)
203
0
{
204
0
  // Note that the last frame can be removed through another route and thus
205
0
  // delete the property -- that's why we fetch the property again before
206
0
  // removing each frame rather than fetching it once and iterating the list.
207
0
  while (nsFrameList* frameList = GetProperty(aProp)) {
208
0
    nsIFrame* frame = frameList->RemoveFirstChild();
209
0
    if (MOZ_LIKELY(frame)) {
210
0
      frame->DestroyFrom(aDestructRoot, aPostDestroyData);
211
0
    } else {
212
0
      RemoveProperty(aProp);
213
0
      frameList->Delete(aPresShell);
214
0
      return;
215
0
    }
216
0
  }
217
0
}
218
219
void
220
nsContainerFrame::DestroyFrom(nsIFrame* aDestructRoot, PostDestroyData& aPostDestroyData)
221
0
{
222
0
  // Prevent event dispatch during destruction.
223
0
  if (HasView()) {
224
0
    GetView()->SetFrame(nullptr);
225
0
  }
226
0
227
0
  DestroyAbsoluteFrames(aDestructRoot, aPostDestroyData);
228
0
229
0
  // Destroy frames on the principal child list.
230
0
  mFrames.DestroyFramesFrom(aDestructRoot, aPostDestroyData);
231
0
232
0
  // If we have any IB split siblings, clear their references to us.
233
0
  if (HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) {
234
0
    // Delete previous sibling's reference to me.
235
0
    nsIFrame* prevSib = GetProperty(nsIFrame::IBSplitPrevSibling());
236
0
    if (prevSib) {
237
0
      NS_WARNING_ASSERTION(
238
0
        this == prevSib->GetProperty(nsIFrame::IBSplitSibling()),
239
0
        "IB sibling chain is inconsistent");
240
0
      prevSib->DeleteProperty(nsIFrame::IBSplitSibling());
241
0
    }
242
0
243
0
    // Delete next sibling's reference to me.
244
0
    nsIFrame* nextSib = GetProperty(nsIFrame::IBSplitSibling());
245
0
    if (nextSib) {
246
0
      NS_WARNING_ASSERTION(
247
0
        this == nextSib->GetProperty(nsIFrame::IBSplitPrevSibling()),
248
0
        "IB sibling chain is inconsistent");
249
0
      nextSib->DeleteProperty(nsIFrame::IBSplitPrevSibling());
250
0
    }
251
0
252
#ifdef DEBUG
253
    // This is just so we can assert it's not set in nsFrame::DestroyFrom.
254
    RemoveStateBits(NS_FRAME_PART_OF_IBSPLIT);
255
#endif
256
  }
257
0
258
0
  if (MOZ_UNLIKELY(!mProperties.IsEmpty())) {
259
0
    using T = mozilla::FrameProperties::UntypedDescriptor;
260
0
    bool hasO = false, hasOC = false, hasEOC = false, hasBackdrop = false;
261
0
    mProperties.ForEach([&] (const T& aProp, void*) {
262
0
      if (aProp == OverflowProperty()) {
263
0
        hasO = true;
264
0
      } else if (aProp == OverflowContainersProperty()) {
265
0
        hasOC = true;
266
0
      } else if (aProp == ExcessOverflowContainersProperty()) {
267
0
        hasEOC = true;
268
0
      } else if (aProp == BackdropProperty()) {
269
0
        hasBackdrop = true;
270
0
      }
271
0
      return true;
272
0
    });
273
0
274
0
    // Destroy frames on the auxiliary frame lists and delete the lists.
275
0
    nsPresContext* pc = PresContext();
276
0
    nsIPresShell* shell = pc->PresShell();
277
0
    if (hasO) {
278
0
      SafelyDestroyFrameListProp(aDestructRoot, aPostDestroyData, shell,
279
0
                                 OverflowProperty());
280
0
    }
281
0
282
0
    MOZ_ASSERT(IsFrameOfType(eCanContainOverflowContainers) ||
283
0
               !(hasOC || hasEOC),
284
0
               "this type of frame shouldn't have overflow containers");
285
0
    if (hasOC) {
286
0
      SafelyDestroyFrameListProp(aDestructRoot, aPostDestroyData, shell,
287
0
                                 OverflowContainersProperty());
288
0
    }
289
0
    if (hasEOC) {
290
0
      SafelyDestroyFrameListProp(aDestructRoot, aPostDestroyData, shell,
291
0
                                 ExcessOverflowContainersProperty());
292
0
    }
293
0
294
0
    MOZ_ASSERT(!GetProperty(BackdropProperty()) ||
295
0
               StyleDisplay()->mTopLayer != NS_STYLE_TOP_LAYER_NONE,
296
0
               "only top layer frame may have backdrop");
297
0
    if (hasBackdrop) {
298
0
      SafelyDestroyFrameListProp(aDestructRoot, aPostDestroyData, shell,
299
0
                                 BackdropProperty());
300
0
    }
301
0
  }
302
0
303
0
  nsSplittableFrame::DestroyFrom(aDestructRoot, aPostDestroyData);
304
0
}
305
306
/////////////////////////////////////////////////////////////////////////////
307
// Child frame enumeration
308
309
const nsFrameList&
310
nsContainerFrame::GetChildList(ChildListID aListID) const
311
0
{
312
0
  // We only know about the principal child list, the overflow lists,
313
0
  // and the backdrop list.
314
0
  switch (aListID) {
315
0
    case kPrincipalList:
316
0
      return mFrames;
317
0
    case kOverflowList: {
318
0
      nsFrameList* list = GetOverflowFrames();
319
0
      return list ? *list : nsFrameList::EmptyList();
320
0
    }
321
0
    case kOverflowContainersList: {
322
0
      nsFrameList* list = GetPropTableFrames(OverflowContainersProperty());
323
0
      return list ? *list : nsFrameList::EmptyList();
324
0
    }
325
0
    case kExcessOverflowContainersList: {
326
0
      nsFrameList* list =
327
0
        GetPropTableFrames(ExcessOverflowContainersProperty());
328
0
      return list ? *list : nsFrameList::EmptyList();
329
0
    }
330
0
    case kBackdropList: {
331
0
      nsFrameList* list = GetPropTableFrames(BackdropProperty());
332
0
      return list ? *list : nsFrameList::EmptyList();
333
0
    }
334
0
    default:
335
0
      return nsSplittableFrame::GetChildList(aListID);
336
0
  }
337
0
}
338
339
void
340
nsContainerFrame::GetChildLists(nsTArray<ChildList>* aLists) const
341
0
{
342
0
  mFrames.AppendIfNonempty(aLists, kPrincipalList);
343
0
344
0
  using T = mozilla::FrameProperties::UntypedDescriptor;
345
0
  mProperties.ForEach([this, aLists] (const T& aProp, void* aValue) {
346
0
    typedef const nsFrameList* L;
347
0
    if (aProp == OverflowProperty()) {
348
0
      L(aValue)->AppendIfNonempty(aLists, kOverflowList);
349
0
    } else if (aProp == OverflowContainersProperty()) {
350
0
      MOZ_ASSERT(IsFrameOfType(nsIFrame::eCanContainOverflowContainers),
351
0
                 "found unexpected OverflowContainersProperty");
352
0
      Unused << this; // silence clang -Wunused-lambda-capture in opt builds
353
0
      L(aValue)->AppendIfNonempty(aLists, kOverflowContainersList);
354
0
    } else if (aProp == ExcessOverflowContainersProperty()) {
355
0
      MOZ_ASSERT(IsFrameOfType(nsIFrame::eCanContainOverflowContainers),
356
0
                 "found unexpected ExcessOverflowContainersProperty");
357
0
      Unused << this; // silence clang -Wunused-lambda-capture in opt builds
358
0
      L(aValue)->AppendIfNonempty(aLists, kExcessOverflowContainersList);
359
0
    } else if (aProp == BackdropProperty()) {
360
0
      L(aValue)->AppendIfNonempty(aLists, kBackdropList);
361
0
    }
362
0
    return true;
363
0
  });
364
0
365
0
  nsSplittableFrame::GetChildLists(aLists);
366
0
}
367
368
/////////////////////////////////////////////////////////////////////////////
369
// Painting/Events
370
371
void
372
nsContainerFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
373
                                   const nsDisplayListSet& aLists)
374
0
{
375
0
  DisplayBorderBackgroundOutline(aBuilder, aLists);
376
0
  BuildDisplayListForNonBlockChildren(aBuilder, aLists);
377
0
}
378
379
void
380
nsContainerFrame::BuildDisplayListForNonBlockChildren(nsDisplayListBuilder*   aBuilder,
381
                                                      const nsDisplayListSet& aLists,
382
                                                      uint32_t                aFlags)
383
0
{
384
0
  nsIFrame* kid = mFrames.FirstChild();
385
0
  // Put each child's background directly onto the content list
386
0
  nsDisplayListSet set(aLists, aLists.Content());
387
0
  // The children should be in content order
388
0
  while (kid) {
389
0
    BuildDisplayListForChild(aBuilder, kid, set, aFlags);
390
0
    kid = kid->GetNextSibling();
391
0
  }
392
0
}
393
394
/* virtual */ void
395
nsContainerFrame::ChildIsDirty(nsIFrame* aChild)
396
0
{
397
0
  NS_ASSERTION(NS_SUBTREE_DIRTY(aChild), "child isn't actually dirty");
398
0
399
0
  AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
400
0
}
401
402
nsIFrame::FrameSearchResult
403
nsContainerFrame::PeekOffsetNoAmount(bool aForward, int32_t* aOffset)
404
0
{
405
0
  NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range");
406
0
  // Don't allow the caret to stay in an empty (leaf) container frame.
407
0
  return CONTINUE_EMPTY;
408
0
}
409
410
nsIFrame::FrameSearchResult
411
nsContainerFrame::PeekOffsetCharacter(
412
                    bool aForward, int32_t* aOffset,
413
                    PeekOffsetCharacterOptions aOptions)
414
0
{
415
0
  NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range");
416
0
  // Don't allow the caret to stay in an empty (leaf) container frame.
417
0
  return CONTINUE_EMPTY;
418
0
}
419
420
/////////////////////////////////////////////////////////////////////////////
421
// Helper member functions
422
423
/**
424
 * Position the view associated with |aKidFrame|, if there is one. A
425
 * container frame should call this method after positioning a frame,
426
 * but before |Reflow|.
427
 */
428
void
429
nsContainerFrame::PositionFrameView(nsIFrame* aKidFrame)
430
0
{
431
0
  nsIFrame* parentFrame = aKidFrame->GetParent();
432
0
  if (!aKidFrame->HasView() || !parentFrame)
433
0
    return;
434
0
435
0
  nsView* view = aKidFrame->GetView();
436
0
  nsViewManager* vm = view->GetViewManager();
437
0
  nsPoint pt;
438
0
  nsView* ancestorView = parentFrame->GetClosestView(&pt);
439
0
440
0
  if (ancestorView != view->GetParent()) {
441
0
    NS_ASSERTION(ancestorView == view->GetParent()->GetParent(),
442
0
                 "Allowed only one anonymous view between frames");
443
0
    // parentFrame is responsible for positioning aKidFrame's view
444
0
    // explicitly
445
0
    return;
446
0
  }
447
0
448
0
  pt += aKidFrame->GetPosition();
449
0
  vm->MoveViewTo(view, pt.x, pt.y);
450
0
}
451
452
nsresult
453
nsContainerFrame::ReparentFrameView(nsIFrame* aChildFrame,
454
                                    nsIFrame* aOldParentFrame,
455
                                    nsIFrame* aNewParentFrame)
456
0
{
457
0
  MOZ_ASSERT(aChildFrame, "null child frame pointer");
458
0
  MOZ_ASSERT(aOldParentFrame, "null old parent frame pointer");
459
0
  MOZ_ASSERT(aNewParentFrame, "null new parent frame pointer");
460
0
  MOZ_ASSERT(aOldParentFrame != aNewParentFrame, "same old and new parent frame");
461
0
462
0
  // See if either the old parent frame or the new parent frame have a view
463
0
  while (!aOldParentFrame->HasView() && !aNewParentFrame->HasView()) {
464
0
    // Walk up both the old parent frame and the new parent frame nodes
465
0
    // stopping when we either find a common parent or views for one
466
0
    // or both of the frames.
467
0
    //
468
0
    // This works well in the common case where we push/pull and the old parent
469
0
    // frame and the new parent frame are part of the same flow. They will
470
0
    // typically be the same distance (height wise) from the
471
0
    aOldParentFrame = aOldParentFrame->GetParent();
472
0
    aNewParentFrame = aNewParentFrame->GetParent();
473
0
474
0
    // We should never walk all the way to the root frame without finding
475
0
    // a view
476
0
    NS_ASSERTION(aOldParentFrame && aNewParentFrame, "didn't find view");
477
0
478
0
    // See if we reached a common ancestor
479
0
    if (aOldParentFrame == aNewParentFrame) {
480
0
      break;
481
0
    }
482
0
  }
483
0
484
0
  // See if we found a common parent frame
485
0
  if (aOldParentFrame == aNewParentFrame) {
486
0
    // We found a common parent and there are no views between the old parent
487
0
    // and the common parent or the new parent frame and the common parent.
488
0
    // Because neither the old parent frame nor the new parent frame have views,
489
0
    // then any child views don't need reparenting
490
0
    return NS_OK;
491
0
  }
492
0
493
0
  // We found views for one or both of the ancestor frames before we
494
0
  // found a common ancestor.
495
0
  nsView* oldParentView = aOldParentFrame->GetClosestView();
496
0
  nsView* newParentView = aNewParentFrame->GetClosestView();
497
0
498
0
  // See if the old parent frame and the new parent frame are in the
499
0
  // same view sub-hierarchy. If they are then we don't have to do
500
0
  // anything
501
0
  if (oldParentView != newParentView) {
502
0
    // They're not so we need to reparent any child views
503
0
    aChildFrame->ReparentFrameViewTo(oldParentView->GetViewManager(),
504
0
                                     newParentView,
505
0
                                     oldParentView);
506
0
  }
507
0
508
0
  return NS_OK;
509
0
}
510
511
nsresult
512
nsContainerFrame::ReparentFrameViewList(const nsFrameList& aChildFrameList,
513
                                        nsIFrame*          aOldParentFrame,
514
                                        nsIFrame*          aNewParentFrame)
515
0
{
516
0
  MOZ_ASSERT(aChildFrameList.NotEmpty(), "empty child frame list");
517
0
  MOZ_ASSERT(aOldParentFrame, "null old parent frame pointer");
518
0
  MOZ_ASSERT(aNewParentFrame, "null new parent frame pointer");
519
0
  MOZ_ASSERT(aOldParentFrame != aNewParentFrame, "same old and new parent frame");
520
0
521
0
  // See if either the old parent frame or the new parent frame have a view
522
0
  while (!aOldParentFrame->HasView() && !aNewParentFrame->HasView()) {
523
0
    // Walk up both the old parent frame and the new parent frame nodes
524
0
    // stopping when we either find a common parent or views for one
525
0
    // or both of the frames.
526
0
    //
527
0
    // This works well in the common case where we push/pull and the old parent
528
0
    // frame and the new parent frame are part of the same flow. They will
529
0
    // typically be the same distance (height wise) from the
530
0
    aOldParentFrame = aOldParentFrame->GetParent();
531
0
    aNewParentFrame = aNewParentFrame->GetParent();
532
0
533
0
    // We should never walk all the way to the root frame without finding
534
0
    // a view
535
0
    NS_ASSERTION(aOldParentFrame && aNewParentFrame, "didn't find view");
536
0
537
0
    // See if we reached a common ancestor
538
0
    if (aOldParentFrame == aNewParentFrame) {
539
0
      break;
540
0
    }
541
0
  }
542
0
543
0
544
0
  // See if we found a common parent frame
545
0
  if (aOldParentFrame == aNewParentFrame) {
546
0
    // We found a common parent and there are no views between the old parent
547
0
    // and the common parent or the new parent frame and the common parent.
548
0
    // Because neither the old parent frame nor the new parent frame have views,
549
0
    // then any child views don't need reparenting
550
0
    return NS_OK;
551
0
  }
552
0
553
0
  // We found views for one or both of the ancestor frames before we
554
0
  // found a common ancestor.
555
0
  nsView* oldParentView = aOldParentFrame->GetClosestView();
556
0
  nsView* newParentView = aNewParentFrame->GetClosestView();
557
0
558
0
  // See if the old parent frame and the new parent frame are in the
559
0
  // same view sub-hierarchy. If they are then we don't have to do
560
0
  // anything
561
0
  if (oldParentView != newParentView) {
562
0
    nsViewManager* viewManager = oldParentView->GetViewManager();
563
0
564
0
    // They're not so we need to reparent any child views
565
0
    for (nsFrameList::Enumerator e(aChildFrameList); !e.AtEnd(); e.Next()) {
566
0
      e.get()->ReparentFrameViewTo(viewManager, newParentView, oldParentView);
567
0
    }
568
0
  }
569
0
570
0
  return NS_OK;
571
0
}
572
573
static nsIWidget*
574
GetPresContextContainerWidget(nsPresContext* aPresContext)
575
0
{
576
0
  nsCOMPtr<nsISupports> container = aPresContext->Document()->GetContainer();
577
0
  nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(container);
578
0
  if (!baseWindow)
579
0
    return nullptr;
580
0
581
0
  nsCOMPtr<nsIWidget> mainWidget;
582
0
  baseWindow->GetMainWidget(getter_AddRefs(mainWidget));
583
0
  return mainWidget;
584
0
}
585
586
static bool
587
IsTopLevelWidget(nsIWidget* aWidget)
588
0
{
589
0
  nsWindowType windowType = aWidget->WindowType();
590
0
  return windowType == eWindowType_toplevel ||
591
0
         windowType == eWindowType_dialog ||
592
0
         windowType == eWindowType_popup ||
593
0
         windowType == eWindowType_sheet;
594
0
}
595
596
void
597
nsContainerFrame::SyncWindowProperties(nsPresContext*       aPresContext,
598
                                       nsIFrame*            aFrame,
599
                                       nsView*              aView,
600
                                       gfxContext*          aRC,
601
                                       uint32_t             aFlags)
602
0
{
603
0
#ifdef MOZ_XUL
604
0
  if (!aView || !nsCSSRendering::IsCanvasFrame(aFrame) || !aView->HasWidget())
605
0
    return;
606
0
607
0
  nsCOMPtr<nsIWidget> windowWidget = GetPresContextContainerWidget(aPresContext);
608
0
  if (!windowWidget || !IsTopLevelWidget(windowWidget))
609
0
    return;
610
0
611
0
  nsViewManager* vm = aView->GetViewManager();
612
0
  nsView* rootView = vm->GetRootView();
613
0
614
0
  if (aView != rootView)
615
0
    return;
616
0
617
0
  Element* rootElement = aPresContext->Document()->GetRootElement();
618
0
  if (!rootElement || !rootElement->IsXULElement()) {
619
0
    // Scrollframes use native widgets which don't work well with
620
0
    // translucent windows, at least in Windows XP. So if the document
621
0
    // has a root scrollrame it's useless to try to make it transparent,
622
0
    // we'll just get something broken.
623
0
    // nsCSSFrameConstructor::ConstructRootFrame constructs root
624
0
    // scrollframes whenever the root element is not a XUL element, so
625
0
    // we test for that here. We can't just call
626
0
    // presShell->GetRootScrollFrame() since that might not have
627
0
    // been constructed yet.
628
0
    // We can change this to allow translucent toplevel HTML documents
629
0
    // (e.g. to do something like Dashboard widgets), once we
630
0
    // have broad support for translucent scrolled documents, but be
631
0
    // careful because apparently some Firefox extensions expect
632
0
    // openDialog("something.html") to produce an opaque window
633
0
    // even if the HTML doesn't have a background-color set.
634
0
    return;
635
0
  }
636
0
637
0
  nsIFrame *rootFrame = aPresContext->PresShell()->FrameConstructor()->GetRootElementStyleFrame();
638
0
  if (!rootFrame)
639
0
    return;
640
0
641
0
  if (aFlags & SET_ASYNC) {
642
0
    aView->SetNeedsWindowPropertiesSync();
643
0
    return;
644
0
  }
645
0
646
0
  RefPtr<nsPresContext> kungFuDeathGrip(aPresContext);
647
0
  AutoWeakFrame weak(rootFrame);
648
0
649
0
  nsTransparencyMode mode = nsLayoutUtils::GetFrameTransparency(aFrame, rootFrame);
650
0
  int32_t shadow = rootFrame->StyleUIReset()->mWindowShadow;
651
0
  nsCOMPtr<nsIWidget> viewWidget = aView->GetWidget();
652
0
  viewWidget->SetTransparencyMode(mode);
653
0
  windowWidget->SetWindowShadowStyle(shadow);
654
0
655
0
  if (!aRC)
656
0
    return;
657
0
658
0
  if (!weak.IsAlive()) {
659
0
    return;
660
0
  }
661
0
662
0
  nsBoxLayoutState aState(aPresContext, aRC);
663
0
  nsSize minSize = rootFrame->GetXULMinSize(aState);
664
0
  nsSize maxSize = rootFrame->GetXULMaxSize(aState);
665
0
666
0
  SetSizeConstraints(aPresContext, windowWidget, minSize, maxSize);
667
0
#endif
668
0
}
669
670
void nsContainerFrame::SetSizeConstraints(nsPresContext* aPresContext,
671
                                          nsIWidget* aWidget,
672
                                          const nsSize& aMinSize,
673
                                          const nsSize& aMaxSize)
674
0
{
675
0
  LayoutDeviceIntSize devMinSize(aPresContext->AppUnitsToDevPixels(aMinSize.width),
676
0
                                 aPresContext->AppUnitsToDevPixels(aMinSize.height));
677
0
  LayoutDeviceIntSize devMaxSize(aMaxSize.width == NS_INTRINSICSIZE ? NS_MAXSIZE :
678
0
                                 aPresContext->AppUnitsToDevPixels(aMaxSize.width),
679
0
                                 aMaxSize.height == NS_INTRINSICSIZE ? NS_MAXSIZE :
680
0
                                 aPresContext->AppUnitsToDevPixels(aMaxSize.height));
681
0
682
0
  // MinSize has a priority over MaxSize
683
0
  if (devMinSize.width > devMaxSize.width)
684
0
    devMaxSize.width = devMinSize.width;
685
0
  if (devMinSize.height > devMaxSize.height)
686
0
    devMaxSize.height = devMinSize.height;
687
0
688
0
  widget::SizeConstraints constraints(devMinSize, devMaxSize);
689
0
690
0
  // The sizes are in inner window sizes, so convert them into outer window sizes.
691
0
  // Use a size of (200, 200) as only the difference between the inner and outer
692
0
  // size is needed.
693
0
  LayoutDeviceIntSize windowSize =
694
0
    aWidget->ClientToWindowSize(LayoutDeviceIntSize(200, 200));
695
0
  if (constraints.mMinSize.width)
696
0
    constraints.mMinSize.width += windowSize.width - 200;
697
0
  if (constraints.mMinSize.height)
698
0
    constraints.mMinSize.height += windowSize.height - 200;
699
0
  if (constraints.mMaxSize.width != NS_MAXSIZE)
700
0
    constraints.mMaxSize.width += windowSize.width - 200;
701
0
  if (constraints.mMaxSize.height != NS_MAXSIZE)
702
0
    constraints.mMaxSize.height += windowSize.height - 200;
703
0
704
0
  aWidget->SetSizeConstraints(constraints);
705
0
}
706
707
void
708
nsContainerFrame::SyncFrameViewAfterReflow(nsPresContext* aPresContext,
709
                                           nsIFrame*       aFrame,
710
                                           nsView*        aView,
711
                                           const nsRect&   aVisualOverflowArea,
712
                                           uint32_t        aFlags)
713
0
{
714
0
  if (!aView) {
715
0
    return;
716
0
  }
717
0
718
0
  // Make sure the view is sized and positioned correctly
719
0
  if (0 == (aFlags & NS_FRAME_NO_MOVE_VIEW)) {
720
0
    PositionFrameView(aFrame);
721
0
  }
722
0
723
0
  if (0 == (aFlags & NS_FRAME_NO_SIZE_VIEW)) {
724
0
    nsViewManager* vm = aView->GetViewManager();
725
0
726
0
    vm->ResizeView(aView, aVisualOverflowArea, true);
727
0
  }
728
0
}
729
730
static nscoord GetCoord(const nsStyleCoord& aCoord, nscoord aIfNotCoord)
731
0
{
732
0
  if (aCoord.ConvertsToLength()) {
733
0
    return aCoord.ComputeCoordPercentCalc(0);
734
0
  }
735
0
  return aIfNotCoord;
736
0
}
737
738
void
739
nsContainerFrame::DoInlineIntrinsicISize(gfxContext *aRenderingContext,
740
                                         InlineIntrinsicISizeData *aData,
741
                                         nsLayoutUtils::IntrinsicISizeType aType)
742
0
{
743
0
  if (GetPrevInFlow())
744
0
    return; // Already added.
745
0
746
0
  MOZ_ASSERT(aType == nsLayoutUtils::MIN_ISIZE ||
747
0
             aType == nsLayoutUtils::PREF_ISIZE, "bad type");
748
0
749
0
  WritingMode wm = GetWritingMode();
750
0
  mozilla::Side startSide =
751
0
    wm.PhysicalSideForInlineAxis(eLogicalEdgeStart);
752
0
  mozilla::Side endSide =
753
0
    wm.PhysicalSideForInlineAxis(eLogicalEdgeEnd);
754
0
755
0
  const nsStylePadding *stylePadding = StylePadding();
756
0
  const nsStyleBorder *styleBorder = StyleBorder();
757
0
  const nsStyleMargin *styleMargin = StyleMargin();
758
0
759
0
  // This goes at the beginning no matter how things are broken and how
760
0
  // messy the bidi situations are, since per CSS2.1 section 8.6
761
0
  // (implemented in bug 328168), the startSide border is always on the
762
0
  // first line.
763
0
  // This frame is a first-in-flow, but it might have a previous bidi
764
0
  // continuation, in which case that continuation should handle the startSide
765
0
  // border.
766
0
  // For box-decoration-break:clone we setup clonePBM = startPBM + endPBM and
767
0
  // add that to each line.  For box-decoration-break:slice clonePBM is zero.
768
0
  nscoord clonePBM = 0; // PBM = PaddingBorderMargin
769
0
  const bool sliceBreak =
770
0
    styleBorder->mBoxDecorationBreak == StyleBoxDecorationBreak::Slice;
771
0
  if (!GetPrevContinuation()) {
772
0
    nscoord startPBM =
773
0
      // clamp negative calc() to 0
774
0
      std::max(GetCoord(stylePadding->mPadding.Get(startSide), 0), 0) +
775
0
      styleBorder->GetComputedBorderWidth(startSide) +
776
0
      GetCoord(styleMargin->mMargin.Get(startSide), 0);
777
0
    if (MOZ_LIKELY(sliceBreak)) {
778
0
      aData->mCurrentLine += startPBM;
779
0
    } else {
780
0
      clonePBM = startPBM;
781
0
    }
782
0
  }
783
0
784
0
  nscoord endPBM =
785
0
    // clamp negative calc() to 0
786
0
    std::max(GetCoord(stylePadding->mPadding.Get(endSide), 0), 0) +
787
0
    styleBorder->GetComputedBorderWidth(endSide) +
788
0
    GetCoord(styleMargin->mMargin.Get(endSide), 0);
789
0
  if (MOZ_UNLIKELY(!sliceBreak)) {
790
0
    clonePBM += endPBM;
791
0
  }
792
0
793
0
  const nsLineList_iterator* savedLine = aData->mLine;
794
0
  nsIFrame* const savedLineContainer = aData->LineContainer();
795
0
796
0
  nsContainerFrame *lastInFlow;
797
0
  for (nsContainerFrame *nif = this; nif;
798
0
       nif = static_cast<nsContainerFrame*>(nif->GetNextInFlow())) {
799
0
    if (aData->mCurrentLine == 0) {
800
0
      aData->mCurrentLine = clonePBM;
801
0
    }
802
0
    for (nsIFrame* kid : nif->mFrames) {
803
0
      if (aType == nsLayoutUtils::MIN_ISIZE)
804
0
        kid->AddInlineMinISize(aRenderingContext,
805
0
                               static_cast<InlineMinISizeData*>(aData));
806
0
      else
807
0
        kid->AddInlinePrefISize(aRenderingContext,
808
0
                                static_cast<InlinePrefISizeData*>(aData));
809
0
    }
810
0
811
0
    // After we advance to our next-in-flow, the stored line and line container
812
0
    // may no longer be correct. Just forget them.
813
0
    aData->mLine = nullptr;
814
0
    aData->SetLineContainer(nullptr);
815
0
816
0
    lastInFlow = nif;
817
0
  }
818
0
819
0
  aData->mLine = savedLine;
820
0
  aData->SetLineContainer(savedLineContainer);
821
0
822
0
  // This goes at the end no matter how things are broken and how
823
0
  // messy the bidi situations are, since per CSS2.1 section 8.6
824
0
  // (implemented in bug 328168), the endSide border is always on the
825
0
  // last line.
826
0
  // We reached the last-in-flow, but it might have a next bidi
827
0
  // continuation, in which case that continuation should handle
828
0
  // the endSide border.
829
0
  if (MOZ_LIKELY(!lastInFlow->GetNextContinuation() && sliceBreak)) {
830
0
    aData->mCurrentLine += endPBM;
831
0
  }
832
0
}
833
834
/* virtual */
835
LogicalSize
836
nsContainerFrame::ComputeAutoSize(gfxContext*         aRenderingContext,
837
                                  WritingMode         aWM,
838
                                  const LogicalSize&  aCBSize,
839
                                  nscoord             aAvailableISize,
840
                                  const LogicalSize&  aMargin,
841
                                  const LogicalSize&  aBorder,
842
                                  const LogicalSize&  aPadding,
843
                                  ComputeSizeFlags    aFlags)
844
0
{
845
0
  LogicalSize result(aWM, 0xdeadbeef, NS_UNCONSTRAINEDSIZE);
846
0
  nscoord availBased = aAvailableISize - aMargin.ISize(aWM) -
847
0
                       aBorder.ISize(aWM) - aPadding.ISize(aWM);
848
0
  // replaced elements always shrink-wrap
849
0
  if ((aFlags & ComputeSizeFlags::eShrinkWrap) || IsFrameOfType(eReplaced)) {
850
0
    // Only bother computing our 'auto' ISize if the result will be used.
851
0
    // It'll be used under two scenarios:
852
0
    // - If our ISize property is itself 'auto'.
853
0
    // - If we're using flex-basis in place of our ISize property (i.e. we're a
854
0
    // flex item with our inline axis being the main axis), AND we have
855
0
    // flex-basis:content.
856
0
    const nsStylePosition* pos = StylePosition();
857
0
    if (pos->ISize(aWM).GetUnit() == eStyleUnit_Auto ||
858
0
        (pos->mFlexBasis.GetUnit() == eStyleUnit_Enumerated &&
859
0
         pos->mFlexBasis.GetIntValue() == NS_STYLE_FLEX_BASIS_CONTENT &&
860
0
         IsFlexItem() &&
861
0
         nsFlexContainerFrame::IsItemInlineAxisMainAxis(this))) {
862
0
      result.ISize(aWM) = ShrinkWidthToFit(aRenderingContext, availBased, aFlags);
863
0
    }
864
0
  } else {
865
0
    result.ISize(aWM) = availBased;
866
0
  }
867
0
868
0
  if (IsTableCaption()) {
869
0
    // If we're a container for font size inflation, then shrink
870
0
    // wrapping inside of us should not apply font size inflation.
871
0
    AutoMaybeDisableFontInflation an(this);
872
0
873
0
    WritingMode tableWM = GetParent()->GetWritingMode();
874
0
    uint8_t captionSide = StyleTableBorder()->mCaptionSide;
875
0
876
0
    if (aWM.IsOrthogonalTo(tableWM)) {
877
0
      if (captionSide == NS_STYLE_CAPTION_SIDE_TOP ||
878
0
          captionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE ||
879
0
          captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM ||
880
0
          captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE) {
881
0
        // For an orthogonal caption on a block-dir side of the table,
882
0
        // shrink-wrap to min-isize.
883
0
        result.ISize(aWM) = GetMinISize(aRenderingContext);
884
0
      } else {
885
0
        // An orthogonal caption on an inline-dir side of the table
886
0
        // is constrained to the containing block.
887
0
        nscoord pref = GetPrefISize(aRenderingContext);
888
0
        if (pref > aCBSize.ISize(aWM)) {
889
0
          pref = aCBSize.ISize(aWM);
890
0
        }
891
0
        if (pref < result.ISize(aWM)) {
892
0
          result.ISize(aWM) = pref;
893
0
        }
894
0
      }
895
0
    } else {
896
0
      if (captionSide == NS_STYLE_CAPTION_SIDE_LEFT ||
897
0
          captionSide == NS_STYLE_CAPTION_SIDE_RIGHT) {
898
0
        result.ISize(aWM) = GetMinISize(aRenderingContext);
899
0
      } else if (captionSide == NS_STYLE_CAPTION_SIDE_TOP ||
900
0
                 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) {
901
0
        // The outer frame constrains our available isize to the isize of
902
0
        // the table.  Grow if our min-isize is bigger than that, but not
903
0
        // larger than the containing block isize.  (It would really be nice
904
0
        // to transmit that information another way, so we could grow up to
905
0
        // the table's available isize, but that's harder.)
906
0
        nscoord min = GetMinISize(aRenderingContext);
907
0
        if (min > aCBSize.ISize(aWM)) {
908
0
          min = aCBSize.ISize(aWM);
909
0
        }
910
0
        if (min > result.ISize(aWM)) {
911
0
          result.ISize(aWM) = min;
912
0
        }
913
0
      }
914
0
    }
915
0
  }
916
0
  return result;
917
0
}
918
919
void
920
nsContainerFrame::ReflowChild(nsIFrame*                aKidFrame,
921
                              nsPresContext*           aPresContext,
922
                              ReflowOutput&            aDesiredSize,
923
                              const ReflowInput&       aReflowInput,
924
                              const WritingMode&       aWM,
925
                              const LogicalPoint&      aPos,
926
                              const nsSize&            aContainerSize,
927
                              uint32_t                 aFlags,
928
                              nsReflowStatus&          aStatus,
929
                              nsOverflowContinuationTracker* aTracker)
930
0
{
931
0
  MOZ_ASSERT(aReflowInput.mFrame == aKidFrame, "bad reflow state");
932
0
  if (aWM.IsVerticalRL() || (!aWM.IsVertical() && !aWM.IsBidiLTR())) {
933
0
    NS_ASSERTION(aContainerSize.width != NS_UNCONSTRAINEDSIZE,
934
0
                 "ReflowChild with unconstrained container width!");
935
0
  }
936
0
  MOZ_ASSERT(aDesiredSize.VisualOverflow() == nsRect(0,0,0,0) &&
937
0
             aDesiredSize.ScrollableOverflow() == nsRect(0,0,0,0),
938
0
             "please reset the overflow areas before calling ReflowChild");
939
0
940
0
  // Position the child frame and its view if requested.
941
0
  if (NS_FRAME_NO_MOVE_FRAME != (aFlags & NS_FRAME_NO_MOVE_FRAME)) {
942
0
    aKidFrame->SetPosition(aWM, aPos, aContainerSize);
943
0
  }
944
0
945
0
  if (0 == (aFlags & NS_FRAME_NO_MOVE_VIEW)) {
946
0
    PositionFrameView(aKidFrame);
947
0
    PositionChildViews(aKidFrame);
948
0
  }
949
0
950
0
  // Reflow the child frame
951
0
  aKidFrame->Reflow(aPresContext, aDesiredSize, aReflowInput, aStatus);
952
0
953
0
  // If the child frame is complete, delete any next-in-flows,
954
0
  // but only if the NO_DELETE_NEXT_IN_FLOW flag isn't set.
955
0
  if (!aStatus.IsInlineBreakBefore() &&
956
0
      aStatus.IsFullyComplete() &&
957
0
      !(aFlags & NS_FRAME_NO_DELETE_NEXT_IN_FLOW_CHILD)) {
958
0
    nsIFrame* kidNextInFlow = aKidFrame->GetNextInFlow();
959
0
    if (kidNextInFlow) {
960
0
      // Remove all of the childs next-in-flows. Make sure that we ask
961
0
      // the right parent to do the removal (it's possible that the
962
0
      // parent is not this because we are executing pullup code)
963
0
      nsOverflowContinuationTracker::AutoFinish fini(aTracker, aKidFrame);
964
0
      kidNextInFlow->GetParent()->DeleteNextInFlowChild(kidNextInFlow, true);
965
0
    }
966
0
  }
967
0
}
968
969
//XXX temporary: hold on to a copy of the old physical version of
970
//    ReflowChild so that we can convert callers incrementally.
971
void
972
nsContainerFrame::ReflowChild(nsIFrame*                aKidFrame,
973
                              nsPresContext*           aPresContext,
974
                              ReflowOutput&     aDesiredSize,
975
                              const ReflowInput& aReflowInput,
976
                              nscoord                  aX,
977
                              nscoord                  aY,
978
                              uint32_t                 aFlags,
979
                              nsReflowStatus&          aStatus,
980
                              nsOverflowContinuationTracker* aTracker)
981
0
{
982
0
  MOZ_ASSERT(aReflowInput.mFrame == aKidFrame, "bad reflow state");
983
0
984
0
  // Position the child frame and its view if requested.
985
0
  if (NS_FRAME_NO_MOVE_FRAME != (aFlags & NS_FRAME_NO_MOVE_FRAME)) {
986
0
    aKidFrame->SetPosition(nsPoint(aX, aY));
987
0
  }
988
0
989
0
  if (0 == (aFlags & NS_FRAME_NO_MOVE_VIEW)) {
990
0
    PositionFrameView(aKidFrame);
991
0
    PositionChildViews(aKidFrame);
992
0
  }
993
0
994
0
  // Reflow the child frame
995
0
  aKidFrame->Reflow(aPresContext, aDesiredSize, aReflowInput, aStatus);
996
0
997
0
  // If the child frame is complete, delete any next-in-flows,
998
0
  // but only if the NO_DELETE_NEXT_IN_FLOW flag isn't set.
999
0
  if (aStatus.IsFullyComplete() &&
1000
0
      !(aFlags & NS_FRAME_NO_DELETE_NEXT_IN_FLOW_CHILD)) {
1001
0
    nsIFrame* kidNextInFlow = aKidFrame->GetNextInFlow();
1002
0
    if (kidNextInFlow) {
1003
0
      // Remove all of the childs next-in-flows. Make sure that we ask
1004
0
      // the right parent to do the removal (it's possible that the
1005
0
      // parent is not this because we are executing pullup code)
1006
0
      nsOverflowContinuationTracker::AutoFinish fini(aTracker, aKidFrame);
1007
0
      kidNextInFlow->GetParent()->DeleteNextInFlowChild(kidNextInFlow, true);
1008
0
    }
1009
0
  }
1010
0
}
1011
1012
1013
/**
1014
 * Position the views of |aFrame|'s descendants. A container frame
1015
 * should call this method if it moves a frame after |Reflow|.
1016
 */
1017
void
1018
nsContainerFrame::PositionChildViews(nsIFrame* aFrame)
1019
0
{
1020
0
  if (!(aFrame->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW)) {
1021
0
    return;
1022
0
  }
1023
0
1024
0
  // Recursively walk aFrame's child frames.
1025
0
  // Process the additional child lists, but skip the popup list as the
1026
0
  // view for popups is managed by the parent. Currently only nsMenuFrame
1027
0
  // and nsPopupSetFrame have a popupList and during layout will adjust the
1028
0
  // view manually to position the popup.
1029
0
  ChildListIterator lists(aFrame);
1030
0
  for (; !lists.IsDone(); lists.Next()) {
1031
0
    if (lists.CurrentID() == kPopupList) {
1032
0
      continue;
1033
0
    }
1034
0
    nsFrameList::Enumerator childFrames(lists.CurrentList());
1035
0
    for (; !childFrames.AtEnd(); childFrames.Next()) {
1036
0
      // Position the frame's view (if it has one) otherwise recursively
1037
0
      // process its children
1038
0
      nsIFrame* childFrame = childFrames.get();
1039
0
      if (childFrame->HasView()) {
1040
0
        PositionFrameView(childFrame);
1041
0
      } else {
1042
0
        PositionChildViews(childFrame);
1043
0
      }
1044
0
    }
1045
0
  }
1046
0
}
1047
1048
/**
1049
 * The second half of frame reflow. Does the following:
1050
 * - sets the frame's bounds
1051
 * - sizes and positions (if requested) the frame's view. If the frame's final
1052
 *   position differs from the current position and the frame itself does not
1053
 *   have a view, then any child frames with views are positioned so they stay
1054
 *   in sync
1055
 * - sets the view's visibility, opacity, content transparency, and clip
1056
 * - invoked the DidReflow() function
1057
 *
1058
 * Flags:
1059
 * NS_FRAME_NO_MOVE_FRAME - don't move the frame. aX and aY are ignored in this
1060
 *    case. Also implies NS_FRAME_NO_MOVE_VIEW
1061
 * NS_FRAME_NO_MOVE_VIEW - don't position the frame's view. Set this if you
1062
 *    don't want to automatically sync the frame and view
1063
 * NS_FRAME_NO_SIZE_VIEW - don't size the frame's view
1064
 */
1065
1066
/**
1067
 * De-optimize function to work around a VC2017 15.5+ compiler bug:
1068
 * https://bugzil.la/1424281#c12
1069
 */
1070
#if defined(_MSC_VER) && !defined(__clang__) && defined(_M_AMD64)
1071
#pragma optimize("g", off)
1072
#endif
1073
void
1074
nsContainerFrame::FinishReflowChild(nsIFrame*                  aKidFrame,
1075
                                    nsPresContext*             aPresContext,
1076
                                    const ReflowOutput& aDesiredSize,
1077
                                    const ReflowInput*   aReflowInput,
1078
                                    const WritingMode&         aWM,
1079
                                    const LogicalPoint&        aPos,
1080
                                    const nsSize&              aContainerSize,
1081
                                    uint32_t                   aFlags)
1082
0
{
1083
0
  if (aWM.IsVerticalRL() || (!aWM.IsVertical() && !aWM.IsBidiLTR())) {
1084
0
    NS_ASSERTION(aContainerSize.width != NS_UNCONSTRAINEDSIZE,
1085
0
                 "FinishReflowChild with unconstrained container width!");
1086
0
  }
1087
0
1088
0
  nsPoint curOrigin = aKidFrame->GetPosition();
1089
0
  WritingMode outerWM = aDesiredSize.GetWritingMode();
1090
0
  LogicalSize convertedSize = aDesiredSize.Size(outerWM).ConvertTo(aWM,
1091
0
                                                                   outerWM);
1092
0
1093
0
  if (NS_FRAME_NO_MOVE_FRAME != (aFlags & NS_FRAME_NO_MOVE_FRAME)) {
1094
0
    aKidFrame->SetRect(aWM, LogicalRect(aWM, aPos, convertedSize),
1095
0
                       aContainerSize);
1096
0
  } else {
1097
0
    aKidFrame->SetSize(aWM, convertedSize);
1098
0
  }
1099
0
1100
0
  if (aKidFrame->HasView()) {
1101
0
    nsView* view = aKidFrame->GetView();
1102
0
    // Make sure the frame's view is properly sized and positioned and has
1103
0
    // things like opacity correct
1104
0
    SyncFrameViewAfterReflow(aPresContext, aKidFrame, view,
1105
0
                             aDesiredSize.VisualOverflow(), aFlags);
1106
0
  }
1107
0
1108
0
  nsPoint newOrigin = aKidFrame->GetPosition();
1109
0
  if (!(aFlags & NS_FRAME_NO_MOVE_VIEW) && curOrigin != newOrigin) {
1110
0
    if (!aKidFrame->HasView()) {
1111
0
      // If the frame has moved, then we need to make sure any child views are
1112
0
      // correctly positioned
1113
0
      PositionChildViews(aKidFrame);
1114
0
    }
1115
0
  }
1116
0
1117
0
  aKidFrame->DidReflow(aPresContext, aReflowInput);
1118
0
}
1119
#if defined(_MSC_VER) && !defined(__clang__) && defined(_M_AMD64)
1120
#pragma optimize("", on)
1121
#endif
1122
1123
//XXX temporary: hold on to a copy of the old physical version of
1124
//    FinishReflowChild so that we can convert callers incrementally.
1125
void
1126
nsContainerFrame::FinishReflowChild(nsIFrame*                  aKidFrame,
1127
                                    nsPresContext*             aPresContext,
1128
                                    const ReflowOutput& aDesiredSize,
1129
                                    const ReflowInput*   aReflowInput,
1130
                                    nscoord                    aX,
1131
                                    nscoord                    aY,
1132
                                    uint32_t                   aFlags)
1133
0
{
1134
0
  nsPoint curOrigin = aKidFrame->GetPosition();
1135
0
1136
0
  if (NS_FRAME_NO_MOVE_FRAME != (aFlags & NS_FRAME_NO_MOVE_FRAME)) {
1137
0
    aKidFrame->SetRect(nsRect(aX, aY, aDesiredSize.Width(), aDesiredSize.Height()));
1138
0
  } else {
1139
0
    aKidFrame->SetSize(nsSize(aDesiredSize.Width(), aDesiredSize.Height()));
1140
0
  }
1141
0
1142
0
  if (aKidFrame->HasView()) {
1143
0
    nsView* view = aKidFrame->GetView();
1144
0
    // Make sure the frame's view is properly sized and positioned and has
1145
0
    // things like opacity correct
1146
0
    SyncFrameViewAfterReflow(aPresContext, aKidFrame, view,
1147
0
                             aDesiredSize.VisualOverflow(), aFlags);
1148
0
  }
1149
0
1150
0
  if (!(aFlags & NS_FRAME_NO_MOVE_VIEW) &&
1151
0
      (curOrigin.x != aX || curOrigin.y != aY)) {
1152
0
    if (!aKidFrame->HasView()) {
1153
0
      // If the frame has moved, then we need to make sure any child views are
1154
0
      // correctly positioned
1155
0
      PositionChildViews(aKidFrame);
1156
0
    }
1157
0
  }
1158
0
1159
0
  aKidFrame->DidReflow(aPresContext, aReflowInput);
1160
0
}
1161
1162
void
1163
nsContainerFrame::ReflowOverflowContainerChildren(nsPresContext*           aPresContext,
1164
                                                  const ReflowInput& aReflowInput,
1165
                                                  nsOverflowAreas&         aOverflowRects,
1166
                                                  uint32_t                 aFlags,
1167
                                                  nsReflowStatus&          aStatus,
1168
                                                  ChildFrameMerger         aMergeFunc)
1169
0
{
1170
0
  MOZ_ASSERT(aPresContext, "null pointer");
1171
0
1172
0
  nsFrameList* overflowContainers = DrainExcessOverflowContainersList(aMergeFunc);
1173
0
  if (!overflowContainers) {
1174
0
    return; // nothing to reflow
1175
0
  }
1176
0
1177
0
  nsOverflowContinuationTracker tracker(this, false, false);
1178
0
  bool shouldReflowAllKids = aReflowInput.ShouldReflowAllKids();
1179
0
1180
0
  for (nsIFrame* frame : *overflowContainers) {
1181
0
    if (frame->GetPrevInFlow()->GetParent() != GetPrevInFlow()) {
1182
0
      // frame's prevInFlow has moved, skip reflowing this frame;
1183
0
      // it will get reflowed once it's been placed
1184
0
      if (GetNextInFlow()) {
1185
0
        // We report OverflowIncomplete status in this case to avoid our parent
1186
0
        // deleting our next-in-flows which might destroy non-empty frames.
1187
0
        nsReflowStatus status;
1188
0
        status.SetOverflowIncomplete();
1189
0
        aStatus.MergeCompletionStatusFrom(status);
1190
0
      }
1191
0
      continue;
1192
0
    }
1193
0
    // If the available vertical height has changed, we need to reflow
1194
0
    // even if the frame isn't dirty.
1195
0
    if (shouldReflowAllKids || NS_SUBTREE_DIRTY(frame)) {
1196
0
      // Get prev-in-flow
1197
0
      nsIFrame* prevInFlow = frame->GetPrevInFlow();
1198
0
      NS_ASSERTION(prevInFlow,
1199
0
                   "overflow container frame must have a prev-in-flow");
1200
0
      NS_ASSERTION(frame->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER,
1201
0
                   "overflow container frame must have overflow container bit set");
1202
0
      WritingMode wm = frame->GetWritingMode();
1203
0
      nsSize containerSize = aReflowInput.AvailableSize(wm).GetPhysicalSize(wm);
1204
0
      LogicalRect prevRect = prevInFlow->GetLogicalRect(wm, containerSize);
1205
0
1206
0
      // Initialize reflow params
1207
0
      LogicalSize availSpace(wm, prevRect.ISize(wm),
1208
0
                             aReflowInput.AvailableSize(wm).BSize(wm));
1209
0
      ReflowOutput desiredSize(aReflowInput);
1210
0
      ReflowInput frameState(aPresContext, aReflowInput,
1211
0
                                   frame, availSpace);
1212
0
      nsReflowStatus frameStatus;
1213
0
1214
0
      // Reflow
1215
0
      LogicalPoint pos(wm, prevRect.IStart(wm), 0);
1216
0
      ReflowChild(frame, aPresContext, desiredSize, frameState,
1217
0
                  wm, pos, containerSize, aFlags, frameStatus, &tracker);
1218
0
      //XXXfr Do we need to override any shrinkwrap effects here?
1219
0
      // e.g. desiredSize.Width() = prevRect.width;
1220
0
      FinishReflowChild(frame, aPresContext, desiredSize, &frameState,
1221
0
                        wm, pos, containerSize, aFlags);
1222
0
1223
0
      // Handle continuations
1224
0
      if (!frameStatus.IsFullyComplete()) {
1225
0
        if (frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
1226
0
          // Abspos frames can't cause their parent to be incomplete,
1227
0
          // only overflow incomplete.
1228
0
          frameStatus.SetOverflowIncomplete();
1229
0
        }
1230
0
        else {
1231
0
          NS_ASSERTION(frameStatus.IsComplete(),
1232
0
                       "overflow container frames can't be incomplete, only overflow-incomplete");
1233
0
        }
1234
0
1235
0
        // Acquire a next-in-flow, creating it if necessary
1236
0
        nsIFrame* nif = frame->GetNextInFlow();
1237
0
        if (!nif) {
1238
0
          NS_ASSERTION(frameStatus.NextInFlowNeedsReflow(),
1239
0
                       "Someone forgot a NextInFlowNeedsReflow flag");
1240
0
          nif = aPresContext->PresShell()->FrameConstructor()->
1241
0
            CreateContinuingFrame(aPresContext, frame, this);
1242
0
        }
1243
0
        else if (!(nif->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) {
1244
0
          // used to be a normal next-in-flow; steal it from the child list
1245
0
          nsresult rv = nif->GetParent()->StealFrame(nif);
1246
0
          if (NS_FAILED(rv)) {
1247
0
            return;
1248
0
          }
1249
0
        }
1250
0
1251
0
        tracker.Insert(nif, frameStatus);
1252
0
      }
1253
0
      aStatus.MergeCompletionStatusFrom(frameStatus);
1254
0
      // At this point it would be nice to assert !frame->GetOverflowRect().IsEmpty(),
1255
0
      // but we have some unsplittable frames that, when taller than
1256
0
      // availableHeight will push zero-height content into a next-in-flow.
1257
0
    }
1258
0
    else {
1259
0
      tracker.Skip(frame, aStatus);
1260
0
      if (aReflowInput.mFloatManager) {
1261
0
        nsBlockFrame::RecoverFloatsFor(frame, *aReflowInput.mFloatManager,
1262
0
                                       aReflowInput.GetWritingMode(),
1263
0
                                       aReflowInput.ComputedPhysicalSize());
1264
0
      }
1265
0
    }
1266
0
    ConsiderChildOverflow(aOverflowRects, frame);
1267
0
  }
1268
0
}
1269
1270
void
1271
nsContainerFrame::DisplayOverflowContainers(nsDisplayListBuilder*   aBuilder,
1272
                                            const nsDisplayListSet& aLists)
1273
0
{
1274
0
  nsFrameList* overflowconts = GetPropTableFrames(OverflowContainersProperty());
1275
0
  if (overflowconts) {
1276
0
    for (nsIFrame* frame : *overflowconts) {
1277
0
      BuildDisplayListForChild(aBuilder, frame, aLists);
1278
0
    }
1279
0
  }
1280
0
}
1281
1282
static bool
1283
TryRemoveFrame(nsIFrame* aFrame,
1284
               nsContainerFrame::FrameListPropertyDescriptor aProp,
1285
               nsIFrame* aChildToRemove)
1286
0
{
1287
0
  nsFrameList* list = aFrame->GetProperty(aProp);
1288
0
  if (list && list->StartRemoveFrame(aChildToRemove)) {
1289
0
    // aChildToRemove *may* have been removed from this list.
1290
0
    if (list->IsEmpty()) {
1291
0
      aFrame->RemoveProperty(aProp);
1292
0
      list->Delete(aFrame->PresShell());
1293
0
    }
1294
0
    return true;
1295
0
  }
1296
0
  return false;
1297
0
}
1298
1299
bool
1300
nsContainerFrame::MaybeStealOverflowContainerFrame(nsIFrame* aChild)
1301
0
{
1302
0
  bool removed = false;
1303
0
  if (MOZ_UNLIKELY(aChild->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) {
1304
0
    // Try removing from the overflow container list.
1305
0
    removed = ::TryRemoveFrame(this, OverflowContainersProperty(),
1306
0
                               aChild);
1307
0
    if (!removed) {
1308
0
      // It might be in the excess overflow container list.
1309
0
      removed = ::TryRemoveFrame(this,
1310
0
                                 ExcessOverflowContainersProperty(),
1311
0
                                 aChild);
1312
0
    }
1313
0
  }
1314
0
  return removed;
1315
0
}
1316
1317
nsresult
1318
nsContainerFrame::StealFrame(nsIFrame* aChild)
1319
0
{
1320
#ifdef DEBUG
1321
  if (!mFrames.ContainsFrame(aChild)) {
1322
    nsFrameList* list = GetOverflowFrames();
1323
    if (!list || !list->ContainsFrame(aChild)) {
1324
      list = GetProperty(OverflowContainersProperty());
1325
      if (!list || !list->ContainsFrame(aChild)) {
1326
        list = GetProperty(ExcessOverflowContainersProperty());
1327
        MOZ_ASSERT(list && list->ContainsFrame(aChild), "aChild isn't our child"
1328
                   " or on a frame list not supported by StealFrame");
1329
      }
1330
    }
1331
  }
1332
#endif
1333
1334
0
  bool removed = MaybeStealOverflowContainerFrame(aChild);
1335
0
  if (!removed) {
1336
0
    // NOTE nsColumnSetFrame and nsCanvasFrame have their overflow containers
1337
0
    // on the normal lists so we might get here also if the frame bit
1338
0
    // NS_FRAME_IS_OVERFLOW_CONTAINER is set.
1339
0
    removed = mFrames.StartRemoveFrame(aChild);
1340
0
    if (!removed) {
1341
0
      // We didn't find the child in our principal child list.
1342
0
      // Maybe it's on the overflow list?
1343
0
      nsFrameList* frameList = GetOverflowFrames();
1344
0
      if (frameList) {
1345
0
        removed = frameList->ContinueRemoveFrame(aChild);
1346
0
        if (frameList->IsEmpty()) {
1347
0
          DestroyOverflowList();
1348
0
        }
1349
0
      }
1350
0
    }
1351
0
  }
1352
0
1353
0
  MOZ_ASSERT(removed, "StealFrame: can't find aChild");
1354
0
  return removed ? NS_OK : NS_ERROR_UNEXPECTED;
1355
0
}
1356
1357
nsFrameList
1358
nsContainerFrame::StealFramesAfter(nsIFrame* aChild)
1359
0
{
1360
0
  NS_ASSERTION(!aChild ||
1361
0
               !(aChild->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER),
1362
0
               "StealFramesAfter doesn't handle overflow containers");
1363
0
  NS_ASSERTION(!IsBlockFrame(), "unexpected call");
1364
0
1365
0
  if (!aChild) {
1366
0
    nsFrameList copy(mFrames);
1367
0
    mFrames.Clear();
1368
0
    return copy;
1369
0
  }
1370
0
1371
0
  for (nsFrameList::FrameLinkEnumerator iter(mFrames); !iter.AtEnd();
1372
0
       iter.Next()) {
1373
0
    if (iter.PrevFrame() == aChild) {
1374
0
      return mFrames.ExtractTail(iter);
1375
0
    }
1376
0
  }
1377
0
1378
0
  // We didn't find the child in the principal child list.
1379
0
  // Maybe it's on the overflow list?
1380
0
  nsFrameList* overflowFrames = GetOverflowFrames();
1381
0
  if (overflowFrames) {
1382
0
    for (nsFrameList::FrameLinkEnumerator iter(*overflowFrames); !iter.AtEnd();
1383
0
         iter.Next()) {
1384
0
      if (iter.PrevFrame() == aChild) {
1385
0
        return overflowFrames->ExtractTail(iter);
1386
0
      }
1387
0
    }
1388
0
  }
1389
0
1390
0
  NS_ERROR("StealFramesAfter: can't find aChild");
1391
0
  return nsFrameList::EmptyList();
1392
0
}
1393
1394
/*
1395
 * Create a next-in-flow for aFrame. Will return the newly created
1396
 * frame <b>if and only if</b> a new frame is created; otherwise
1397
 * nullptr is returned.
1398
 */
1399
nsIFrame*
1400
nsContainerFrame::CreateNextInFlow(nsIFrame* aFrame)
1401
0
{
1402
0
  MOZ_ASSERT(!IsBlockFrame(),
1403
0
             "you should have called nsBlockFrame::CreateContinuationFor instead");
1404
0
  MOZ_ASSERT(mFrames.ContainsFrame(aFrame), "expected an in-flow child frame");
1405
0
1406
0
  nsPresContext* pc = PresContext();
1407
0
  nsIFrame* nextInFlow = aFrame->GetNextInFlow();
1408
0
  if (nullptr == nextInFlow) {
1409
0
    // Create a continuation frame for the child frame and insert it
1410
0
    // into our child list.
1411
0
    nextInFlow = pc->PresShell()->FrameConstructor()->
1412
0
      CreateContinuingFrame(pc, aFrame, this);
1413
0
    mFrames.InsertFrame(nullptr, aFrame, nextInFlow);
1414
0
1415
0
    NS_FRAME_LOG(NS_FRAME_TRACE_NEW_FRAMES,
1416
0
       ("nsContainerFrame::CreateNextInFlow: frame=%p nextInFlow=%p",
1417
0
        aFrame, nextInFlow));
1418
0
1419
0
    return nextInFlow;
1420
0
  }
1421
0
  return nullptr;
1422
0
}
1423
1424
/**
1425
 * Remove and delete aNextInFlow and its next-in-flows. Updates the sibling and flow
1426
 * pointers
1427
 */
1428
void
1429
nsContainerFrame::DeleteNextInFlowChild(nsIFrame* aNextInFlow,
1430
                                        bool      aDeletingEmptyFrames)
1431
0
{
1432
#ifdef DEBUG
1433
  nsIFrame* prevInFlow = aNextInFlow->GetPrevInFlow();
1434
#endif
1435
0
  MOZ_ASSERT(prevInFlow, "bad prev-in-flow");
1436
0
1437
0
  // If the next-in-flow has a next-in-flow then delete it, too (and
1438
0
  // delete it first).
1439
0
  // Do this in a loop so we don't overflow the stack for frames
1440
0
  // with very many next-in-flows
1441
0
  nsIFrame* nextNextInFlow = aNextInFlow->GetNextInFlow();
1442
0
  if (nextNextInFlow) {
1443
0
    AutoTArray<nsIFrame*, 8> frames;
1444
0
    for (nsIFrame* f = nextNextInFlow; f; f = f->GetNextInFlow()) {
1445
0
      frames.AppendElement(f);
1446
0
    }
1447
0
    for (nsIFrame* delFrame : Reversed(frames)) {
1448
0
      delFrame->GetParent()->
1449
0
        DeleteNextInFlowChild(delFrame, aDeletingEmptyFrames);
1450
0
    }
1451
0
  }
1452
0
1453
0
  // Take the next-in-flow out of the parent's child list
1454
0
  DebugOnly<nsresult> rv = StealFrame(aNextInFlow);
1455
0
  NS_ASSERTION(NS_SUCCEEDED(rv), "StealFrame failure");
1456
0
1457
#ifdef DEBUG
1458
  if (aDeletingEmptyFrames) {
1459
    nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(aNextInFlow);
1460
  }
1461
#endif
1462
1463
0
  // Delete the next-in-flow frame and its descendants. This will also
1464
0
  // remove it from its next-in-flow/prev-in-flow chain.
1465
0
  aNextInFlow->Destroy();
1466
0
1467
0
  MOZ_ASSERT(!prevInFlow->GetNextInFlow(), "non null next-in-flow");
1468
0
}
1469
1470
/**
1471
 * Set the frames on the overflow list
1472
 */
1473
void
1474
nsContainerFrame::SetOverflowFrames(const nsFrameList& aOverflowFrames)
1475
0
{
1476
0
  MOZ_ASSERT(aOverflowFrames.NotEmpty(), "Shouldn't be called");
1477
0
1478
0
  nsPresContext* pc = PresContext();
1479
0
  nsFrameList* newList = new (pc->PresShell()) nsFrameList(aOverflowFrames);
1480
0
1481
0
  SetProperty(OverflowProperty(), newList);
1482
0
}
1483
1484
nsFrameList*
1485
nsContainerFrame::GetPropTableFrames(
1486
  FrameListPropertyDescriptor aProperty) const
1487
0
{
1488
0
  return GetProperty(aProperty);
1489
0
}
1490
1491
nsFrameList*
1492
nsContainerFrame::RemovePropTableFrames(FrameListPropertyDescriptor aProperty)
1493
0
{
1494
0
  return RemoveProperty(aProperty);
1495
0
}
1496
1497
void
1498
nsContainerFrame::SetPropTableFrames(nsFrameList* aFrameList,
1499
                                     FrameListPropertyDescriptor aProperty)
1500
0
{
1501
0
  MOZ_ASSERT(aProperty && aFrameList, "null ptr");
1502
0
  MOZ_ASSERT(
1503
0
    (aProperty != nsContainerFrame::OverflowContainersProperty() &&
1504
0
     aProperty != nsContainerFrame::ExcessOverflowContainersProperty()) ||
1505
0
    IsFrameOfType(nsIFrame::eCanContainOverflowContainers),
1506
0
    "this type of frame can't have overflow containers");
1507
0
  MOZ_ASSERT(!GetPropTableFrames(aProperty));
1508
0
  SetProperty(aProperty, aFrameList);
1509
0
}
1510
1511
void
1512
nsContainerFrame::PushChildrenToOverflow(nsIFrame* aFromChild,
1513
                                         nsIFrame* aPrevSibling)
1514
0
{
1515
0
  MOZ_ASSERT(aFromChild, "null pointer");
1516
0
  MOZ_ASSERT(aPrevSibling, "pushing first child");
1517
0
  MOZ_ASSERT(aPrevSibling->GetNextSibling() == aFromChild, "bad prev sibling");
1518
0
1519
0
  // Add the frames to our overflow list (let our next in flow drain
1520
0
  // our overflow list when it is ready)
1521
0
  SetOverflowFrames(mFrames.RemoveFramesAfter(aPrevSibling));
1522
0
}
1523
1524
void
1525
nsContainerFrame::PushChildren(nsIFrame* aFromChild,
1526
                               nsIFrame* aPrevSibling)
1527
0
{
1528
0
  MOZ_ASSERT(aFromChild, "null pointer");
1529
0
  MOZ_ASSERT(aPrevSibling, "pushing first child");
1530
0
  MOZ_ASSERT(aPrevSibling->GetNextSibling() == aFromChild, "bad prev sibling");
1531
0
1532
0
  // Disconnect aFromChild from its previous sibling
1533
0
  nsFrameList tail = mFrames.RemoveFramesAfter(aPrevSibling);
1534
0
1535
0
  nsContainerFrame* nextInFlow =
1536
0
    static_cast<nsContainerFrame*>(GetNextInFlow());
1537
0
  if (nextInFlow) {
1538
0
    // XXX This is not a very good thing to do. If it gets removed
1539
0
    // then remove the copy of this routine that doesn't do this from
1540
0
    // nsInlineFrame.
1541
0
    // When pushing and pulling frames we need to check for whether any
1542
0
    // views need to be reparented.
1543
0
    for (nsIFrame* f = aFromChild; f; f = f->GetNextSibling()) {
1544
0
      nsContainerFrame::ReparentFrameView(f, this, nextInFlow);
1545
0
    }
1546
0
    nextInFlow->mFrames.InsertFrames(nextInFlow, nullptr, tail);
1547
0
  }
1548
0
  else {
1549
0
    // Add the frames to our overflow list
1550
0
    SetOverflowFrames(tail);
1551
0
  }
1552
0
}
1553
1554
bool
1555
nsContainerFrame::MoveOverflowToChildList()
1556
0
{
1557
0
  bool result = false;
1558
0
1559
0
  // Check for an overflow list with our prev-in-flow
1560
0
  nsContainerFrame* prevInFlow = (nsContainerFrame*)GetPrevInFlow();
1561
0
  if (nullptr != prevInFlow) {
1562
0
    AutoFrameListPtr prevOverflowFrames(PresContext(),
1563
0
                                        prevInFlow->StealOverflowFrames());
1564
0
    if (prevOverflowFrames) {
1565
0
      // Tables are special; they can have repeated header/footer
1566
0
      // frames on mFrames at this point.
1567
0
      NS_ASSERTION(mFrames.IsEmpty() || IsTableFrame(), "bad overflow list");
1568
0
      // When pushing and pulling frames we need to check for whether any
1569
0
      // views need to be reparented.
1570
0
      nsContainerFrame::ReparentFrameViewList(*prevOverflowFrames,
1571
0
                                              prevInFlow, this);
1572
0
      mFrames.AppendFrames(this, *prevOverflowFrames);
1573
0
      result = true;
1574
0
    }
1575
0
  }
1576
0
1577
0
  // It's also possible that we have an overflow list for ourselves.
1578
0
  return DrainSelfOverflowList() || result;
1579
0
}
1580
1581
bool
1582
nsContainerFrame::MoveInlineOverflowToChildList(nsIFrame* aLineContainer)
1583
0
{
1584
0
  MOZ_ASSERT(aLineContainer,
1585
0
             "Must have line container for moving inline overflows");
1586
0
1587
0
  bool result = false;
1588
0
1589
0
  // Check for an overflow list with our prev-in-flow
1590
0
  if (auto prevInFlow = static_cast<nsContainerFrame*>(GetPrevInFlow())) {
1591
0
    AutoFrameListPtr prevOverflowFrames(PresContext(),
1592
0
                                        prevInFlow->StealOverflowFrames());
1593
0
    if (prevOverflowFrames) {
1594
0
      // We may need to reparent floats from prev-in-flow to our line
1595
0
      // container if the container has prev continuation.
1596
0
      if (aLineContainer->GetPrevContinuation()) {
1597
0
        ReparentFloatsForInlineChild(aLineContainer,
1598
0
                                     prevOverflowFrames->FirstChild(), true,
1599
0
                                     ReparentingDirection::Forwards);
1600
0
      }
1601
0
      // When pushing and pulling frames we need to check for whether
1602
0
      // any views need to be reparented.
1603
0
      nsContainerFrame::ReparentFrameViewList(*prevOverflowFrames,
1604
0
                                              prevInFlow, this);
1605
0
      // Prepend overflow frames to the list.
1606
0
      mFrames.InsertFrames(this, nullptr, *prevOverflowFrames);
1607
0
      result = true;
1608
0
    }
1609
0
  }
1610
0
1611
0
  // It's also possible that we have overflow list for ourselves.
1612
0
  return DrainSelfOverflowList() || result;
1613
0
}
1614
1615
bool
1616
nsContainerFrame::DrainSelfOverflowList()
1617
0
{
1618
0
  AutoFrameListPtr overflowFrames(PresContext(), StealOverflowFrames());
1619
0
  if (overflowFrames) {
1620
0
    mFrames.AppendFrames(nullptr, *overflowFrames);
1621
0
    return true;
1622
0
  }
1623
0
  return false;
1624
0
}
1625
1626
nsFrameList*
1627
nsContainerFrame::DrainExcessOverflowContainersList(ChildFrameMerger aMergeFunc)
1628
0
{
1629
0
  nsFrameList* overflowContainers =
1630
0
    GetPropTableFrames(OverflowContainersProperty());
1631
0
1632
0
  NS_ASSERTION(!(overflowContainers && GetPrevInFlow()
1633
0
                 && static_cast<nsContainerFrame*>(GetPrevInFlow())
1634
0
                      ->GetPropTableFrames(ExcessOverflowContainersProperty())),
1635
0
               "conflicting overflow containers lists");
1636
0
1637
0
  if (!overflowContainers) {
1638
0
    // Drain excess from previnflow
1639
0
    nsContainerFrame* prev = (nsContainerFrame*) GetPrevInFlow();
1640
0
    if (prev) {
1641
0
      nsFrameList* excessFrames =
1642
0
        prev->RemovePropTableFrames(ExcessOverflowContainersProperty());
1643
0
      if (excessFrames) {
1644
0
        excessFrames->ApplySetParent(this);
1645
0
        nsContainerFrame::ReparentFrameViewList(*excessFrames, prev, this);
1646
0
        overflowContainers = excessFrames;
1647
0
        SetPropTableFrames(overflowContainers, OverflowContainersProperty());
1648
0
      }
1649
0
    }
1650
0
  }
1651
0
1652
0
  // Our own excess overflow containers from a previous reflow can still be
1653
0
  // present if our next-in-flow hasn't been reflown yet.  Move any children
1654
0
  // from it that don't have a continuation in this frame to the
1655
0
  // OverflowContainers list.
1656
0
  nsFrameList* selfExcessOCFrames =
1657
0
    RemovePropTableFrames(ExcessOverflowContainersProperty());
1658
0
  if (selfExcessOCFrames) {
1659
0
    nsFrameList toMove;
1660
0
    auto child = selfExcessOCFrames->FirstChild();
1661
0
    while (child) {
1662
0
      auto next = child->GetNextSibling();
1663
0
      MOZ_ASSERT(child->GetPrevInFlow(),
1664
0
                 "ExcessOverflowContainers frames must be continuations");
1665
0
      if (child->GetPrevInFlow()->GetParent() != this) {
1666
0
        selfExcessOCFrames->RemoveFrame(child);
1667
0
        toMove.AppendFrame(nullptr, child);
1668
0
      }
1669
0
      child = next;
1670
0
    }
1671
0
    if (toMove.IsEmpty()) {
1672
0
      SetPropTableFrames(selfExcessOCFrames, ExcessOverflowContainersProperty());
1673
0
    } else if (overflowContainers) {
1674
0
      aMergeFunc(*overflowContainers, toMove, this);
1675
0
      if (selfExcessOCFrames->IsEmpty()) {
1676
0
        selfExcessOCFrames->Delete(PresShell());
1677
0
      } else {
1678
0
        SetPropTableFrames(selfExcessOCFrames, ExcessOverflowContainersProperty());
1679
0
      }
1680
0
    } else {
1681
0
      if (selfExcessOCFrames->IsEmpty()) {
1682
0
        *selfExcessOCFrames = toMove;
1683
0
        overflowContainers = selfExcessOCFrames;
1684
0
      } else {
1685
0
        SetPropTableFrames(selfExcessOCFrames, ExcessOverflowContainersProperty());
1686
0
        auto shell = PresShell();
1687
0
        overflowContainers = new (shell) nsFrameList(toMove);
1688
0
      }
1689
0
      SetPropTableFrames(overflowContainers, OverflowContainersProperty());
1690
0
    }
1691
0
  }
1692
0
1693
0
  return overflowContainers;
1694
0
}
1695
1696
nsIFrame*
1697
nsContainerFrame::GetNextInFlowChild(ContinuationTraversingState& aState,
1698
                                     bool* aIsInOverflow)
1699
0
{
1700
0
  nsContainerFrame*& nextInFlow = aState.mNextInFlow;
1701
0
  while (nextInFlow) {
1702
0
    // See if there is any frame in the container
1703
0
    nsIFrame* frame = nextInFlow->mFrames.FirstChild();
1704
0
    if (frame) {
1705
0
      if (aIsInOverflow) {
1706
0
        *aIsInOverflow = false;
1707
0
      }
1708
0
      return frame;
1709
0
    }
1710
0
    // No frames in the principal list, try its overflow list
1711
0
    nsFrameList* overflowFrames = nextInFlow->GetOverflowFrames();
1712
0
    if (overflowFrames) {
1713
0
      if (aIsInOverflow) {
1714
0
        *aIsInOverflow = true;
1715
0
      }
1716
0
      return overflowFrames->FirstChild();
1717
0
    }
1718
0
    nextInFlow = static_cast<nsContainerFrame*>(nextInFlow->GetNextInFlow());
1719
0
  }
1720
0
  return nullptr;
1721
0
}
1722
1723
nsIFrame*
1724
nsContainerFrame::PullNextInFlowChild(ContinuationTraversingState& aState)
1725
0
{
1726
0
  bool isInOverflow;
1727
0
  nsIFrame* frame = GetNextInFlowChild(aState, &isInOverflow);
1728
0
  if (frame) {
1729
0
    nsContainerFrame* nextInFlow = aState.mNextInFlow;
1730
0
    if (isInOverflow) {
1731
0
      nsFrameList* overflowFrames = nextInFlow->GetOverflowFrames();
1732
0
      overflowFrames->RemoveFirstChild();
1733
0
      if (overflowFrames->IsEmpty()) {
1734
0
        nextInFlow->DestroyOverflowList();
1735
0
      }
1736
0
    } else {
1737
0
      nextInFlow->mFrames.RemoveFirstChild();
1738
0
    }
1739
0
1740
0
    // Move the frame to the principal frame list of this container
1741
0
    mFrames.AppendFrame(this, frame);
1742
0
    // AppendFrame has reparented the frame, we need
1743
0
    // to reparent the frame view then.
1744
0
    nsContainerFrame::ReparentFrameView(frame, nextInFlow, this);
1745
0
  }
1746
0
  return frame;
1747
0
}
1748
1749
/* static */ void
1750
nsContainerFrame::ReparentFloatsForInlineChild(nsIFrame* aOurLineContainer,
1751
                                               nsIFrame* aFrame,
1752
                                               bool aReparentSiblings,
1753
                                               ReparentingDirection aDirection)
1754
0
{
1755
0
  // XXXbz this would be better if it took a nsFrameList or a frame
1756
0
  // list slice....
1757
0
  NS_ASSERTION(aOurLineContainer->GetNextContinuation() ||
1758
0
               aOurLineContainer->GetPrevContinuation(),
1759
0
               "Don't call this when we have no continuation, it's a waste");
1760
0
  if (!aFrame) {
1761
0
    NS_ASSERTION(aReparentSiblings, "Why did we get called?");
1762
0
    return;
1763
0
  }
1764
0
1765
0
  nsBlockFrame* frameBlock = nsLayoutUtils::GetFloatContainingBlock(aFrame);
1766
0
  if (!frameBlock || frameBlock == aOurLineContainer) {
1767
0
    return;
1768
0
  }
1769
0
1770
0
  nsBlockFrame* ourBlock = nsLayoutUtils::GetAsBlock(aOurLineContainer);
1771
0
  NS_ASSERTION(ourBlock, "Not a block, but broke vertically?");
1772
0
1773
0
  while (true) {
1774
0
    ourBlock->ReparentFloats(aFrame, frameBlock, false, aDirection);
1775
0
1776
0
    if (!aReparentSiblings)
1777
0
      return;
1778
0
    nsIFrame* next = aFrame->GetNextSibling();
1779
0
    if (!next)
1780
0
      return;
1781
0
    if (next->GetParent() == aFrame->GetParent()) {
1782
0
      aFrame = next;
1783
0
      continue;
1784
0
    }
1785
0
    // This is paranoid and will hardly ever get hit ... but we can't actually
1786
0
    // trust that the frames in the sibling chain all have the same parent,
1787
0
    // because lazy reparenting may be going on. If we find a different
1788
0
    // parent we need to redo our analysis.
1789
0
    ReparentFloatsForInlineChild(aOurLineContainer, next, aReparentSiblings,
1790
0
                                 aDirection);
1791
0
    return;
1792
0
  }
1793
0
}
1794
1795
bool
1796
nsContainerFrame::ResolvedOrientationIsVertical()
1797
0
{
1798
0
  StyleOrient orient = StyleDisplay()->mOrient;
1799
0
  switch (orient) {
1800
0
    case StyleOrient::Horizontal:
1801
0
      return false;
1802
0
    case StyleOrient::Vertical:
1803
0
      return true;
1804
0
    case StyleOrient::Inline:
1805
0
      return GetWritingMode().IsVertical();
1806
0
    case StyleOrient::Block:
1807
0
      return !GetWritingMode().IsVertical();
1808
0
  }
1809
0
  MOZ_ASSERT_UNREACHABLE("unexpected -moz-orient value");
1810
0
  return false;
1811
0
}
1812
1813
// static
1814
bool
1815
nsContainerFrame::FrameStartsCounterScope(nsIFrame* aFrame)
1816
0
{
1817
0
  nsIContent* content = aFrame->GetContent();
1818
0
  if (!content || !content->IsHTMLElement())
1819
0
    return false;
1820
0
1821
0
  nsAtom* localName = content->NodeInfo()->NameAtom();
1822
0
  return localName == nsGkAtoms::ol ||
1823
0
         localName == nsGkAtoms::ul ||
1824
0
         localName == nsGkAtoms::dir ||
1825
0
         localName == nsGkAtoms::menu;
1826
0
}
1827
1828
bool
1829
nsContainerFrame::RenumberList()
1830
0
{
1831
0
  if (!FrameStartsCounterScope(this)) {
1832
0
    // If this frame doesn't start a counter scope then we don't need
1833
0
    // to renumber child list items.
1834
0
    return false;
1835
0
  }
1836
0
1837
0
  MOZ_ASSERT(mContent->IsHTMLElement(),
1838
0
             "FrameStartsCounterScope should only return true for HTML elements");
1839
0
1840
0
  // Setup initial list ordinal value
1841
0
  // XXX Map html's start property to counter-reset style
1842
0
  int32_t ordinal = 1;
1843
0
  int32_t increment;
1844
0
  if (mContent->IsHTMLElement(nsGkAtoms::ol) &&
1845
0
      mContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::reversed)) {
1846
0
    increment = -1;
1847
0
  } else {
1848
0
    increment = 1;
1849
0
  }
1850
0
1851
0
  nsGenericHTMLElement* hc = nsGenericHTMLElement::FromNode(mContent);
1852
0
  // Must be non-null, since FrameStartsCounterScope only returns true
1853
0
  // for HTML elements.
1854
0
  MOZ_ASSERT(hc, "How is mContent not HTML?");
1855
0
  const nsAttrValue* attr = hc->GetParsedAttr(nsGkAtoms::start);
1856
0
  nsContainerFrame* fif = static_cast<nsContainerFrame*>(FirstInFlow());
1857
0
  if (attr && attr->Type() == nsAttrValue::eInteger) {
1858
0
    ordinal = attr->GetIntegerValue();
1859
0
  } else if (increment < 0) {
1860
0
    // <ol reversed> case, or some other case with a negative increment: count
1861
0
    // up the child list
1862
0
    ordinal = 0;
1863
0
    fif->RenumberChildFrames(&ordinal, 0, -increment, true);
1864
0
  }
1865
0
1866
0
  return fif->RenumberChildFrames(&ordinal, 0, increment, false);
1867
0
}
1868
1869
// add in a sanity check for absurdly deep frame trees.  See bug 42138
1870
// can't just use IsFrameTreeTooDeep() because that method has side effects we don't want
1871
0
#define MAX_DEPTH_FOR_LIST_RENUMBERING 200  // 200 open displayable tags is pretty unrealistic
1872
1873
bool
1874
nsContainerFrame::RenumberFrameAndDescendants(int32_t* aOrdinal,
1875
                                              int32_t aDepth,
1876
                                              int32_t aIncrement,
1877
                                              bool aForCounting)
1878
0
{
1879
0
  MOZ_ASSERT(aOrdinal, "null params are immoral!");
1880
0
1881
0
  // add in a sanity check for absurdly deep frame trees.  See bug 42138
1882
0
  if (MAX_DEPTH_FOR_LIST_RENUMBERING < aDepth) {
1883
0
    return false;
1884
0
  }
1885
0
  const nsStyleDisplay* display = StyleDisplay();
1886
0
1887
0
  // drill down through any wrappers to the real frame
1888
0
  nsIFrame* kid = GetContentInsertionFrame();
1889
0
  if (!kid) {
1890
0
    return false;
1891
0
  }
1892
0
1893
0
  // Do not renumber list for summary elements.
1894
0
  HTMLSummaryElement* summary =
1895
0
    HTMLSummaryElement::FromNode(kid->GetContent());
1896
0
  if (summary && summary->IsMainSummary()) {
1897
0
    return false;
1898
0
  }
1899
0
1900
0
  bool kidRenumberedABullet = false;
1901
0
1902
0
  // If the frame is a list-item and the frame implements our
1903
0
  // block frame API then get its bullet and set the list item
1904
0
  // ordinal.
1905
0
  if (mozilla::StyleDisplay::ListItem == display->mDisplay) {
1906
0
    // Make certain that the frame is a block frame in case
1907
0
    // something foreign has crept in.
1908
0
    nsBlockFrame* listItem = nsLayoutUtils::GetAsBlock(kid);
1909
0
    if (listItem) {
1910
0
      nsBulletFrame* bullet = listItem->GetBullet();
1911
0
      if (bullet) {
1912
0
        if (!aForCounting) {
1913
0
          bool changed;
1914
0
          *aOrdinal = bullet->SetListItemOrdinal(*aOrdinal, &changed, aIncrement);
1915
0
          if (changed) {
1916
0
            kidRenumberedABullet = true;
1917
0
1918
0
            // The ordinal changed - mark the bullet frame, and any
1919
0
            // intermediate frames between it and the block (are there
1920
0
            // ever any?), dirty.
1921
0
            // The calling code will make the necessary FrameNeedsReflow
1922
0
            // call for the list ancestor.
1923
0
            bullet->AddStateBits(NS_FRAME_IS_DIRTY);
1924
0
            nsIFrame *f = bullet;
1925
0
            do {
1926
0
              nsIFrame *parent = f->GetParent();
1927
0
              parent->ChildIsDirty(f);
1928
0
              f = parent;
1929
0
            } while (f != listItem);
1930
0
          }
1931
0
        } else {
1932
0
          // We're only counting the number of children,
1933
0
          // not restyling them. Don't take |value|
1934
0
          // into account when incrementing the ordinal
1935
0
          // or dirty the bullet.
1936
0
          *aOrdinal += aIncrement;
1937
0
        }
1938
0
      }
1939
0
1940
0
      // XXX temporary? if the list-item has child list-items they
1941
0
      // should be numbered too; especially since the list-item is
1942
0
      // itself (ASSUMED!) not to be a counter-resetter.
1943
0
      bool meToo = listItem->RenumberChildFrames(aOrdinal, aDepth + 1,
1944
0
                                                 aIncrement, aForCounting);
1945
0
      if (meToo) {
1946
0
        kidRenumberedABullet = true;
1947
0
      }
1948
0
    }
1949
0
  } else if (display->mDisplay == mozilla::StyleDisplay::Block ||
1950
0
             display->mDisplay == mozilla::StyleDisplay::Flex ||
1951
0
             display->mDisplay == mozilla::StyleDisplay::Grid) {
1952
0
    if (FrameStartsCounterScope(kid)) {
1953
0
      // Don't bother recursing into a frame that is a new counter scope.
1954
0
      // Any list-items in there will be handled by it.
1955
0
    } else {
1956
0
      nsContainerFrame* container = do_QueryFrame(kid);
1957
0
      if (container) {
1958
0
        kidRenumberedABullet =
1959
0
          container->RenumberChildFrames(aOrdinal, aDepth + 1,
1960
0
                                         aIncrement, aForCounting);
1961
0
      }
1962
0
    }
1963
0
  }
1964
0
  return kidRenumberedABullet;
1965
0
}
1966
1967
bool
1968
nsContainerFrame::RenumberChildFrames(int32_t* aOrdinal,
1969
                                      int32_t aDepth,
1970
                                      int32_t aIncrement,
1971
                                      bool aForCounting)
1972
0
{
1973
0
  bool renumbered = false;
1974
0
  for (auto kid : mFrames) {
1975
0
    bool kidRenumbered =
1976
0
      kid->RenumberFrameAndDescendants(aOrdinal, aDepth, aIncrement, aForCounting);
1977
0
    if (!aForCounting && kidRenumbered) {
1978
0
      renumbered = true;
1979
0
    }
1980
0
  }
1981
0
1982
0
  // We need to set NS_FRAME_HAS_DIRTY_CHILDREN bits up the tree between
1983
0
  // the bullet and the caller of RenumberLists.  But the caller itself
1984
0
  // has to be responsible for setting the bit itself, since that caller
1985
0
  // might be making a FrameNeedsReflow call, which requires that the
1986
0
  // bit not be set yet.
1987
0
  if (renumbered && aDepth != 0) {
1988
0
    AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
1989
0
  }
1990
0
1991
0
  return renumbered;
1992
0
}
1993
1994
uint16_t
1995
nsContainerFrame::CSSAlignmentForAbsPosChild(const ReflowInput& aChildRI,
1996
                                             LogicalAxis aLogicalAxis) const
1997
0
{
1998
0
  MOZ_ASSERT(aChildRI.mFrame->IsAbsolutelyPositioned(),
1999
0
             "This method should only be called for abspos children");
2000
0
  NS_ERROR("Child classes that use css box alignment for abspos children "
2001
0
           "should provide their own implementation of this method!");
2002
0
2003
0
  // In the unexpected/unlikely event that this implementation gets invoked,
2004
0
  // just use "start" alignment.
2005
0
  return NS_STYLE_ALIGN_START;
2006
0
}
2007
2008
nsresult
2009
nsContainerFrame::AttributeChanged(int32_t         aNameSpaceID,
2010
                                   nsAtom*        aAttribute,
2011
                                   int32_t         aModType)
2012
0
{
2013
0
  nsresult rv = nsSplittableFrame::AttributeChanged(aNameSpaceID,
2014
0
                                                    aAttribute, aModType);
2015
0
  if (NS_FAILED(rv)) {
2016
0
    return rv;
2017
0
  }
2018
0
  if (nsGkAtoms::start == aAttribute ||
2019
0
      (nsGkAtoms::reversed == aAttribute &&
2020
0
       mContent->IsHTMLElement(nsGkAtoms::ol))) {
2021
0
2022
0
    // XXX Not sure if this is necessary anymore
2023
0
    if (RenumberList()) {
2024
0
      PresShell()->FrameNeedsReflow(this, nsIPresShell::eStyleChange,
2025
0
                                    NS_FRAME_HAS_DIRTY_CHILDREN);
2026
0
    }
2027
0
  }
2028
0
  return rv;
2029
0
}
2030
2031
nsOverflowContinuationTracker::nsOverflowContinuationTracker(nsContainerFrame* aFrame,
2032
                                                             bool              aWalkOOFFrames,
2033
                                                             bool              aSkipOverflowContainerChildren)
2034
  : mOverflowContList(nullptr),
2035
    mPrevOverflowCont(nullptr),
2036
    mSentry(nullptr),
2037
    mParent(aFrame),
2038
    mSkipOverflowContainerChildren(aSkipOverflowContainerChildren),
2039
    mWalkOOFFrames(aWalkOOFFrames)
2040
0
{
2041
0
  MOZ_ASSERT(aFrame, "null frame pointer");
2042
0
  SetupOverflowContList();
2043
0
}
2044
2045
void
2046
nsOverflowContinuationTracker::SetupOverflowContList()
2047
0
{
2048
0
  MOZ_ASSERT(mParent, "null frame pointer");
2049
0
  MOZ_ASSERT(!mOverflowContList, "already have list");
2050
0
  nsContainerFrame* nif =
2051
0
    static_cast<nsContainerFrame*>(mParent->GetNextInFlow());
2052
0
  if (nif) {
2053
0
    mOverflowContList = nif->GetPropTableFrames(
2054
0
      nsContainerFrame::OverflowContainersProperty());
2055
0
    if (mOverflowContList) {
2056
0
      mParent = nif;
2057
0
      SetUpListWalker();
2058
0
    }
2059
0
  }
2060
0
  if (!mOverflowContList) {
2061
0
    mOverflowContList = mParent->GetPropTableFrames(
2062
0
      nsContainerFrame::ExcessOverflowContainersProperty());
2063
0
    if (mOverflowContList) {
2064
0
      SetUpListWalker();
2065
0
    }
2066
0
  }
2067
0
}
2068
2069
/**
2070
 * Helper function to walk past overflow continuations whose prev-in-flow
2071
 * isn't a normal child and to set mSentry and mPrevOverflowCont correctly.
2072
 */
2073
void
2074
nsOverflowContinuationTracker::SetUpListWalker()
2075
0
{
2076
0
  NS_ASSERTION(!mSentry && !mPrevOverflowCont,
2077
0
               "forgot to reset mSentry or mPrevOverflowCont");
2078
0
  if (mOverflowContList) {
2079
0
    nsIFrame* cur = mOverflowContList->FirstChild();
2080
0
    if (mSkipOverflowContainerChildren) {
2081
0
      while (cur && (cur->GetPrevInFlow()->GetStateBits()
2082
0
                     & NS_FRAME_IS_OVERFLOW_CONTAINER)) {
2083
0
        mPrevOverflowCont = cur;
2084
0
        cur = cur->GetNextSibling();
2085
0
      }
2086
0
      while (cur && (!(cur->GetStateBits() & NS_FRAME_OUT_OF_FLOW)
2087
0
                     == mWalkOOFFrames)) {
2088
0
        mPrevOverflowCont = cur;
2089
0
        cur = cur->GetNextSibling();
2090
0
      }
2091
0
    }
2092
0
    if (cur) {
2093
0
      mSentry = cur->GetPrevInFlow();
2094
0
    }
2095
0
  }
2096
0
}
2097
2098
/**
2099
 * Helper function to step forward through the overflow continuations list.
2100
 * Sets mSentry and mPrevOverflowCont, skipping over OOF or non-OOF frames
2101
 * as appropriate. May only be called when we have already set up an
2102
 * mOverflowContList; mOverflowContList cannot be null.
2103
 */
2104
void
2105
nsOverflowContinuationTracker::StepForward()
2106
0
{
2107
0
  MOZ_ASSERT(mOverflowContList, "null list");
2108
0
2109
0
  // Step forward
2110
0
  if (mPrevOverflowCont) {
2111
0
    mPrevOverflowCont = mPrevOverflowCont->GetNextSibling();
2112
0
  }
2113
0
  else {
2114
0
    mPrevOverflowCont = mOverflowContList->FirstChild();
2115
0
  }
2116
0
2117
0
  // Skip over oof or non-oof frames as appropriate
2118
0
  if (mSkipOverflowContainerChildren) {
2119
0
    nsIFrame* cur = mPrevOverflowCont->GetNextSibling();
2120
0
    while (cur && (!(cur->GetStateBits() & NS_FRAME_OUT_OF_FLOW)
2121
0
                   == mWalkOOFFrames)) {
2122
0
      mPrevOverflowCont = cur;
2123
0
      cur = cur->GetNextSibling();
2124
0
    }
2125
0
  }
2126
0
2127
0
  // Set up the sentry
2128
0
  mSentry = (mPrevOverflowCont->GetNextSibling())
2129
0
            ? mPrevOverflowCont->GetNextSibling()->GetPrevInFlow()
2130
0
            : nullptr;
2131
0
}
2132
2133
nsresult
2134
nsOverflowContinuationTracker::Insert(nsIFrame*       aOverflowCont,
2135
                                      nsReflowStatus& aReflowStatus)
2136
0
{
2137
0
  MOZ_ASSERT(aOverflowCont, "null frame pointer");
2138
0
  MOZ_ASSERT(!mSkipOverflowContainerChildren || mWalkOOFFrames ==
2139
0
             !!(aOverflowCont->GetStateBits() & NS_FRAME_OUT_OF_FLOW),
2140
0
             "shouldn't insert frame that doesn't match walker type");
2141
0
  MOZ_ASSERT(aOverflowCont->GetPrevInFlow(),
2142
0
             "overflow containers must have a prev-in-flow");
2143
0
2144
0
  nsresult rv = NS_OK;
2145
0
  bool reparented = false;
2146
0
  nsPresContext* presContext = aOverflowCont->PresContext();
2147
0
  bool addToList = !mSentry || aOverflowCont != mSentry->GetNextInFlow();
2148
0
2149
0
  // If we have a list and aOverflowCont is already in it then don't try to
2150
0
  // add it again.
2151
0
  if (addToList && aOverflowCont->GetParent() == mParent &&
2152
0
      (aOverflowCont->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) &&
2153
0
      mOverflowContList && mOverflowContList->ContainsFrame(aOverflowCont)) {
2154
0
    addToList = false;
2155
0
    mPrevOverflowCont = aOverflowCont->GetPrevSibling();
2156
0
  }
2157
0
2158
0
  if (addToList) {
2159
0
    if (aOverflowCont->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) {
2160
0
      // aOverflowCont is in some other overflow container list,
2161
0
      // steal it first
2162
0
      NS_ASSERTION(!(mOverflowContList &&
2163
0
                     mOverflowContList->ContainsFrame(aOverflowCont)),
2164
0
                   "overflow containers out of order");
2165
0
      rv = aOverflowCont->GetParent()->StealFrame(aOverflowCont);
2166
0
      NS_ENSURE_SUCCESS(rv, rv);
2167
0
    }
2168
0
    else {
2169
0
      aOverflowCont->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
2170
0
    }
2171
0
    if (!mOverflowContList) {
2172
0
      mOverflowContList = new (presContext->PresShell()) nsFrameList();
2173
0
      mParent->SetPropTableFrames(mOverflowContList,
2174
0
        nsContainerFrame::ExcessOverflowContainersProperty());
2175
0
      SetUpListWalker();
2176
0
    }
2177
0
    if (aOverflowCont->GetParent() != mParent) {
2178
0
      nsContainerFrame::ReparentFrameView(aOverflowCont,
2179
0
                                          aOverflowCont->GetParent(),
2180
0
                                          mParent);
2181
0
      reparented = true;
2182
0
    }
2183
0
2184
0
    // If aOverflowCont has a prev/next-in-flow that might be in
2185
0
    // mOverflowContList we need to find it and insert after/before it to
2186
0
    // maintain the order amongst next-in-flows in this list.
2187
0
    nsIFrame* pif = aOverflowCont->GetPrevInFlow();
2188
0
    nsIFrame* nif = aOverflowCont->GetNextInFlow();
2189
0
    if ((pif && pif->GetParent() == mParent && pif != mPrevOverflowCont) ||
2190
0
        (nif && nif->GetParent() == mParent && mPrevOverflowCont)) {
2191
0
      for (nsFrameList::Enumerator e(*mOverflowContList); !e.AtEnd(); e.Next()) {
2192
0
        nsIFrame* f = e.get();
2193
0
        if (f == pif) {
2194
0
          mPrevOverflowCont = pif;
2195
0
          break;
2196
0
        }
2197
0
        if (f == nif) {
2198
0
          mPrevOverflowCont = f->GetPrevSibling();
2199
0
          break;
2200
0
        }
2201
0
      }
2202
0
    }
2203
0
2204
0
    mOverflowContList->InsertFrame(mParent, mPrevOverflowCont, aOverflowCont);
2205
0
    aReflowStatus.SetNextInFlowNeedsReflow();
2206
0
  }
2207
0
2208
0
  // If we need to reflow it, mark it dirty
2209
0
  if (aReflowStatus.NextInFlowNeedsReflow())
2210
0
    aOverflowCont->AddStateBits(NS_FRAME_IS_DIRTY);
2211
0
2212
0
  // It's in our list, just step forward
2213
0
  StepForward();
2214
0
  NS_ASSERTION(mPrevOverflowCont == aOverflowCont ||
2215
0
               (mSkipOverflowContainerChildren &&
2216
0
                (mPrevOverflowCont->GetStateBits() & NS_FRAME_OUT_OF_FLOW) !=
2217
0
                (aOverflowCont->GetStateBits() & NS_FRAME_OUT_OF_FLOW)),
2218
0
              "OverflowContTracker in unexpected state");
2219
0
2220
0
  if (addToList) {
2221
0
    // Convert all non-overflow-container continuations of aOverflowCont
2222
0
    // into overflow containers and move them to our overflow
2223
0
    // tracker. This preserves the invariant that the next-continuations
2224
0
    // of an overflow container are also overflow containers.
2225
0
    nsIFrame* f = aOverflowCont->GetNextContinuation();
2226
0
    if (f && (!(f->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) ||
2227
0
              (!reparented && f->GetParent() == mParent) ||
2228
0
              (reparented && f->GetParent() != mParent))) {
2229
0
      if (!(f->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) {
2230
0
        rv = f->GetParent()->StealFrame(f);
2231
0
        NS_ENSURE_SUCCESS(rv, rv);
2232
0
      }
2233
0
      Insert(f, aReflowStatus);
2234
0
    }
2235
0
  }
2236
0
  return rv;
2237
0
}
2238
2239
void
2240
nsOverflowContinuationTracker::BeginFinish(nsIFrame* aChild)
2241
0
{
2242
0
  MOZ_ASSERT(aChild, "null ptr");
2243
0
  MOZ_ASSERT(aChild->GetNextInFlow(),
2244
0
             "supposed to call Finish *before* deleting next-in-flow!");
2245
0
2246
0
  for (nsIFrame* f = aChild; f; f = f->GetNextInFlow()) {
2247
0
    // We'll update these in EndFinish after the next-in-flows are gone.
2248
0
    if (f == mPrevOverflowCont) {
2249
0
      mSentry = nullptr;
2250
0
      mPrevOverflowCont = nullptr;
2251
0
      break;
2252
0
    }
2253
0
    if (f == mSentry) {
2254
0
      mSentry = nullptr;
2255
0
      break;
2256
0
    }
2257
0
  }
2258
0
}
2259
2260
void
2261
nsOverflowContinuationTracker::EndFinish(nsIFrame* aChild)
2262
0
{
2263
0
  if (!mOverflowContList) {
2264
0
    return;
2265
0
  }
2266
0
  // Forget mOverflowContList if it was deleted.
2267
0
  nsFrameList* eoc = mParent->GetProperty
2268
0
    (nsContainerFrame::ExcessOverflowContainersProperty());
2269
0
  if (eoc != mOverflowContList) {
2270
0
    nsFrameList* oc = static_cast<nsFrameList*>(mParent->GetProperty
2271
0
                        (nsContainerFrame::OverflowContainersProperty()));
2272
0
    if (oc != mOverflowContList) {
2273
0
      // mOverflowContList was deleted
2274
0
      mPrevOverflowCont = nullptr;
2275
0
      mSentry = nullptr;
2276
0
      mParent = aChild->GetParent();
2277
0
      mOverflowContList = nullptr;
2278
0
      SetupOverflowContList();
2279
0
      return;
2280
0
    }
2281
0
  }
2282
0
  // The list survived, update mSentry if needed.
2283
0
  if (!mSentry) {
2284
0
    if (!mPrevOverflowCont) {
2285
0
      SetUpListWalker();
2286
0
    } else {
2287
0
      mozilla::AutoRestore<nsIFrame*> saved(mPrevOverflowCont);
2288
0
      // step backward to make StepForward() use our current mPrevOverflowCont
2289
0
      mPrevOverflowCont = mPrevOverflowCont->GetPrevSibling();
2290
0
      StepForward();
2291
0
    }
2292
0
  }
2293
0
}
2294
2295
/////////////////////////////////////////////////////////////////////////////
2296
// Debugging
2297
2298
#ifdef DEBUG_FRAME_DUMP
2299
void
2300
nsContainerFrame::List(FILE* out, const char* aPrefix, uint32_t aFlags) const
2301
{
2302
  nsCString str;
2303
  ListGeneric(str, aPrefix, aFlags);
2304
2305
  // Output the children
2306
  bool outputOneList = false;
2307
  ChildListIterator lists(this);
2308
  for (; !lists.IsDone(); lists.Next()) {
2309
    if (outputOneList) {
2310
      str += aPrefix;
2311
    }
2312
    if (lists.CurrentID() != kPrincipalList) {
2313
      if (!outputOneList) {
2314
        str += "\n";
2315
        str += aPrefix;
2316
      }
2317
      str += nsPrintfCString("%s %p ", mozilla::layout::ChildListName(lists.CurrentID()),
2318
                             &GetChildList(lists.CurrentID()));
2319
    }
2320
    fprintf_stderr(out, "%s<\n", str.get());
2321
    str = "";
2322
    nsFrameList::Enumerator childFrames(lists.CurrentList());
2323
    for (; !childFrames.AtEnd(); childFrames.Next()) {
2324
      nsIFrame* kid = childFrames.get();
2325
      // Verify the child frame's parent frame pointer is correct
2326
      NS_ASSERTION(kid->GetParent() == this, "bad parent frame pointer");
2327
2328
      // Have the child frame list
2329
      nsCString pfx(aPrefix);
2330
      pfx += "  ";
2331
      kid->List(out, pfx.get(), aFlags);
2332
    }
2333
    fprintf_stderr(out, "%s>\n", aPrefix);
2334
    outputOneList = true;
2335
  }
2336
2337
  if (!outputOneList) {
2338
    fprintf_stderr(out, "%s<>\n", str.get());
2339
  }
2340
}
2341
#endif