Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/layout/xul/nsBoxFrame.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
//
8
// Eric Vaughan
9
// Netscape Communications
10
//
11
// See documentation in associated header file
12
//
13
14
// How boxes layout
15
// ----------------
16
// Boxes layout a bit differently than html. html does a bottom up layout. Where boxes do a top down.
17
// 1) First thing a box does it goes out and askes each child for its min, max, and preferred sizes.
18
// 2) It then adds them up to determine its size.
19
// 3) If the box was asked to layout it self intrinically it will layout its children at their preferred size
20
//    otherwise it will layout the child at the size it was told to. It will squeeze or stretch its children if
21
//    Necessary.
22
//
23
// However there is a catch. Some html components like block frames can not determine their preferred size.
24
// this is their size if they were laid out intrinsically. So the box will flow the child to determine this can
25
// cache the value.
26
27
// Boxes and Incremental Reflow
28
// ----------------------------
29
// Boxes layout out top down by adding up their children's min, max, and preferred sizes. Only problem is if a incremental
30
// reflow occurs. The preferred size of a child deep in the hierarchy could change. And this could change
31
// any number of syblings around the box. Basically any children in the reflow chain must have their caches cleared
32
// so when asked for there current size they can relayout themselves.
33
34
#include "nsBoxFrame.h"
35
36
#include "gfxUtils.h"
37
#include "mozilla/gfx/2D.h"
38
#include "nsBoxLayoutState.h"
39
#include "mozilla/dom/Touch.h"
40
#include "mozilla/Move.h"
41
#include "mozilla/ComputedStyle.h"
42
#include "nsPlaceholderFrame.h"
43
#include "nsPresContext.h"
44
#include "nsCOMPtr.h"
45
#include "nsNameSpaceManager.h"
46
#include "nsGkAtoms.h"
47
#include "nsIContent.h"
48
#include "nsHTMLParts.h"
49
#include "nsViewManager.h"
50
#include "nsView.h"
51
#include "nsIPresShell.h"
52
#include "nsCSSRendering.h"
53
#include "nsIServiceManager.h"
54
#include "nsBoxLayout.h"
55
#include "nsSprocketLayout.h"
56
#include "nsIScrollableFrame.h"
57
#include "nsWidgetsCID.h"
58
#include "nsCSSAnonBoxes.h"
59
#include "nsContainerFrame.h"
60
#include "nsITheme.h"
61
#include "nsTransform2D.h"
62
#include "mozilla/EventStateManager.h"
63
#include "nsDisplayList.h"
64
#include "mozilla/Preferences.h"
65
#include "nsStyleConsts.h"
66
#include "nsLayoutUtils.h"
67
#include "nsSliderFrame.h"
68
#include <algorithm>
69
70
// Needed for Print Preview
71
#include "nsIURI.h"
72
73
#include "mozilla/TouchEvents.h"
74
75
using namespace mozilla;
76
using namespace mozilla::dom;
77
using namespace mozilla::gfx;
78
79
nsIFrame*
80
NS_NewBoxFrame(nsIPresShell* aPresShell, ComputedStyle* aStyle, bool aIsRoot, nsBoxLayout* aLayoutManager)
81
0
{
82
0
  return new (aPresShell) nsBoxFrame(aStyle, nsBoxFrame::kClassID,
83
0
                                     aIsRoot, aLayoutManager);
84
0
}
85
86
nsIFrame*
87
NS_NewBoxFrame(nsIPresShell* aPresShell, ComputedStyle* aStyle)
88
0
{
89
0
  return new (aPresShell) nsBoxFrame(aStyle);
90
0
}
91
92
NS_IMPL_FRAMEARENA_HELPERS(nsBoxFrame)
93
94
#ifdef DEBUG
95
NS_QUERYFRAME_HEAD(nsBoxFrame)
96
  NS_QUERYFRAME_ENTRY(nsBoxFrame)
97
NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
98
#endif
99
100
nsBoxFrame::nsBoxFrame(ComputedStyle* aStyle,
101
                       ClassID aID,
102
                       bool aIsRoot,
103
                       nsBoxLayout* aLayoutManager)
104
  : nsContainerFrame(aStyle, aID)
105
  , mFlex(0)
106
  , mAscent(0)
107
0
{
108
0
  AddStateBits(NS_STATE_IS_HORIZONTAL | NS_STATE_AUTO_STRETCH);
109
0
110
0
  if (aIsRoot)
111
0
     AddStateBits(NS_STATE_IS_ROOT);
112
0
113
0
  mValign = vAlign_Top;
114
0
  mHalign = hAlign_Left;
115
0
116
0
  // if no layout manager specified us the static sprocket layout
117
0
  nsCOMPtr<nsBoxLayout> layout = aLayoutManager;
118
0
119
0
  if (layout == nullptr) {
120
0
    NS_NewSprocketLayout(layout);
121
0
  }
122
0
123
0
  SetXULLayoutManager(layout);
124
0
}
125
126
nsBoxFrame::~nsBoxFrame()
127
0
{
128
0
}
129
130
void
131
nsBoxFrame::SetInitialChildList(ChildListID     aListID,
132
                                nsFrameList&    aChildList)
133
0
{
134
0
  nsContainerFrame::SetInitialChildList(aListID, aChildList);
135
0
  if (aListID == kPrincipalList) {
136
0
    // initialize our list of infos.
137
0
    nsBoxLayoutState state(PresContext());
138
0
    CheckBoxOrder();
139
0
    if (mLayoutManager)
140
0
      mLayoutManager->ChildrenSet(this, state, mFrames.FirstChild());
141
0
  }
142
0
}
143
144
/* virtual */ void
145
nsBoxFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle)
146
0
{
147
0
  nsContainerFrame::DidSetComputedStyle(aOldComputedStyle);
148
0
149
0
  // The values that CacheAttributes() computes depend on our style,
150
0
  // so we need to recompute them here...
151
0
  CacheAttributes();
152
0
}
153
154
/**
155
 * Initialize us. This is a good time to get the alignment of the box
156
 */
157
void
158
nsBoxFrame::Init(nsIContent*       aContent,
159
                 nsContainerFrame* aParent,
160
                 nsIFrame*         aPrevInFlow)
161
0
{
162
0
  nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
163
0
164
0
  if (GetStateBits() & NS_FRAME_FONT_INFLATION_CONTAINER) {
165
0
    AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT);
166
0
  }
167
0
168
0
  MarkIntrinsicISizesDirty();
169
0
170
0
  CacheAttributes();
171
0
172
0
  UpdateMouseThrough();
173
0
174
0
  // register access key
175
0
  RegUnregAccessKey(true);
176
0
}
177
178
void nsBoxFrame::UpdateMouseThrough()
179
{
180
  static Element::AttrValuesArray strings[] =
181
    {&nsGkAtoms::never, &nsGkAtoms::always, nullptr};
182
  switch (mContent->AsElement()->FindAttrValueIn(kNameSpaceID_None,
183
            nsGkAtoms::mousethrough, strings, eCaseMatters)) {
184
    case 0: AddStateBits(NS_FRAME_MOUSE_THROUGH_NEVER); break;
185
    case 1: AddStateBits(NS_FRAME_MOUSE_THROUGH_ALWAYS); break;
186
    case 2: {
187
      RemoveStateBits(NS_FRAME_MOUSE_THROUGH_ALWAYS);
188
      RemoveStateBits(NS_FRAME_MOUSE_THROUGH_NEVER);
189
      break;
190
    }
191
  }
192
}
193
194
void
195
nsBoxFrame::CacheAttributes()
196
0
{
197
0
  /*
198
0
  printf("Caching: ");
199
0
  XULDumpBox(stdout);
200
0
  printf("\n");
201
0
   */
202
0
203
0
  mValign = vAlign_Top;
204
0
  mHalign = hAlign_Left;
205
0
206
0
  bool orient = false;
207
0
  GetInitialOrientation(orient);
208
0
  if (orient)
209
0
    AddStateBits(NS_STATE_IS_HORIZONTAL);
210
0
  else
211
0
    RemoveStateBits(NS_STATE_IS_HORIZONTAL);
212
0
213
0
  bool normal = true;
214
0
  GetInitialDirection(normal);
215
0
  if (normal)
216
0
    AddStateBits(NS_STATE_IS_DIRECTION_NORMAL);
217
0
  else
218
0
    RemoveStateBits(NS_STATE_IS_DIRECTION_NORMAL);
219
0
220
0
  GetInitialVAlignment(mValign);
221
0
  GetInitialHAlignment(mHalign);
222
0
223
0
  bool equalSize = false;
224
0
  GetInitialEqualSize(equalSize);
225
0
  if (equalSize)
226
0
        AddStateBits(NS_STATE_EQUAL_SIZE);
227
0
    else
228
0
        RemoveStateBits(NS_STATE_EQUAL_SIZE);
229
0
230
0
  bool autostretch = !!(mState & NS_STATE_AUTO_STRETCH);
231
0
  GetInitialAutoStretch(autostretch);
232
0
  if (autostretch)
233
0
        AddStateBits(NS_STATE_AUTO_STRETCH);
234
0
     else
235
0
        RemoveStateBits(NS_STATE_AUTO_STRETCH);
236
0
}
237
238
bool
239
nsBoxFrame::GetInitialHAlignment(nsBoxFrame::Halignment& aHalign)
240
0
{
241
0
  if (!GetContent() || !GetContent()->IsElement())
242
0
    return false;
243
0
244
0
  Element* element = GetContent()->AsElement();
245
0
  // XXXdwh Everything inside this if statement is deprecated code.
246
0
  static Element::AttrValuesArray alignStrings[] =
247
0
    {&nsGkAtoms::left, &nsGkAtoms::right, nullptr};
248
0
  static const Halignment alignValues[] = {hAlign_Left, hAlign_Right};
249
0
  int32_t index = element->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::align,
250
0
      alignStrings, eCaseMatters);
251
0
  if (index >= 0) {
252
0
    aHalign = alignValues[index];
253
0
    return true;
254
0
  }
255
0
256
0
  // Now that the deprecated stuff is out of the way, we move on to check the appropriate
257
0
  // attribute.  For horizontal boxes, we are checking the PACK attribute.  For vertical boxes
258
0
  // we are checking the ALIGN attribute.
259
0
  nsAtom* attrName = IsXULHorizontal() ? nsGkAtoms::pack : nsGkAtoms::align;
260
0
  static Element::AttrValuesArray strings[] =
261
0
    {&nsGkAtoms::_empty, &nsGkAtoms::start, &nsGkAtoms::center, &nsGkAtoms::end, nullptr};
262
0
  static const Halignment values[] =
263
0
    {hAlign_Left/*not used*/, hAlign_Left, hAlign_Center, hAlign_Right};
264
0
  index = element->FindAttrValueIn(kNameSpaceID_None, attrName,
265
0
      strings, eCaseMatters);
266
0
267
0
  if (index == Element::ATTR_VALUE_NO_MATCH) {
268
0
    // The attr was present but had a nonsensical value. Revert to the default.
269
0
    return false;
270
0
  }
271
0
  if (index > 0) {
272
0
    aHalign = values[index];
273
0
    return true;
274
0
  }
275
0
276
0
  // Now that we've checked for the attribute it's time to check CSS.  For
277
0
  // horizontal boxes we're checking PACK.  For vertical boxes we are checking
278
0
  // ALIGN.
279
0
  const nsStyleXUL* boxInfo = StyleXUL();
280
0
  if (IsXULHorizontal()) {
281
0
    switch (boxInfo->mBoxPack) {
282
0
      case StyleBoxPack::Start:
283
0
        aHalign = nsBoxFrame::hAlign_Left;
284
0
        return true;
285
0
      case StyleBoxPack::Center:
286
0
        aHalign = nsBoxFrame::hAlign_Center;
287
0
        return true;
288
0
      case StyleBoxPack::End:
289
0
        aHalign = nsBoxFrame::hAlign_Right;
290
0
        return true;
291
0
      default: // Nonsensical value. Just bail.
292
0
        return false;
293
0
    }
294
0
  }
295
0
  else {
296
0
    switch (boxInfo->mBoxAlign) {
297
0
      case StyleBoxAlign::Start:
298
0
        aHalign = nsBoxFrame::hAlign_Left;
299
0
        return true;
300
0
      case StyleBoxAlign::Center:
301
0
        aHalign = nsBoxFrame::hAlign_Center;
302
0
        return true;
303
0
      case StyleBoxAlign::End:
304
0
        aHalign = nsBoxFrame::hAlign_Right;
305
0
        return true;
306
0
      default: // Nonsensical value. Just bail.
307
0
        return false;
308
0
    }
309
0
  }
310
0
311
0
  return false;
312
0
}
313
314
bool
315
nsBoxFrame::GetInitialVAlignment(nsBoxFrame::Valignment& aValign)
316
0
{
317
0
  if (!GetContent() || !GetContent()->IsElement())
318
0
    return false;
319
0
320
0
  Element* element = GetContent()->AsElement();
321
0
322
0
  static Element::AttrValuesArray valignStrings[] =
323
0
    {&nsGkAtoms::top, &nsGkAtoms::baseline, &nsGkAtoms::middle, &nsGkAtoms::bottom, nullptr};
324
0
  static const Valignment valignValues[] =
325
0
    {vAlign_Top, vAlign_BaseLine, vAlign_Middle, vAlign_Bottom};
326
0
  int32_t index = element->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::valign,
327
0
      valignStrings, eCaseMatters);
328
0
  if (index >= 0) {
329
0
    aValign = valignValues[index];
330
0
    return true;
331
0
  }
332
0
333
0
  // Now that the deprecated stuff is out of the way, we move on to check the appropriate
334
0
  // attribute.  For horizontal boxes, we are checking the ALIGN attribute.  For vertical boxes
335
0
  // we are checking the PACK attribute.
336
0
  nsAtom* attrName = IsXULHorizontal() ? nsGkAtoms::align : nsGkAtoms::pack;
337
0
  static Element::AttrValuesArray strings[] =
338
0
    {&nsGkAtoms::_empty, &nsGkAtoms::start, &nsGkAtoms::center,
339
0
     &nsGkAtoms::baseline, &nsGkAtoms::end, nullptr};
340
0
  static const Valignment values[] =
341
0
    {vAlign_Top/*not used*/, vAlign_Top, vAlign_Middle, vAlign_BaseLine, vAlign_Bottom};
342
0
  index = element->FindAttrValueIn(kNameSpaceID_None, attrName,
343
0
      strings, eCaseMatters);
344
0
  if (index == Element::ATTR_VALUE_NO_MATCH) {
345
0
    // The attr was present but had a nonsensical value. Revert to the default.
346
0
    return false;
347
0
  }
348
0
  if (index > 0) {
349
0
    aValign = values[index];
350
0
    return true;
351
0
  }
352
0
353
0
  // Now that we've checked for the attribute it's time to check CSS.  For
354
0
  // horizontal boxes we're checking ALIGN.  For vertical boxes we are checking
355
0
  // PACK.
356
0
  const nsStyleXUL* boxInfo = StyleXUL();
357
0
  if (IsXULHorizontal()) {
358
0
    switch (boxInfo->mBoxAlign) {
359
0
      case StyleBoxAlign::Start:
360
0
        aValign = nsBoxFrame::vAlign_Top;
361
0
        return true;
362
0
      case StyleBoxAlign::Center:
363
0
        aValign = nsBoxFrame::vAlign_Middle;
364
0
        return true;
365
0
      case StyleBoxAlign::Baseline:
366
0
        aValign = nsBoxFrame::vAlign_BaseLine;
367
0
        return true;
368
0
      case StyleBoxAlign::End:
369
0
        aValign = nsBoxFrame::vAlign_Bottom;
370
0
        return true;
371
0
      default: // Nonsensical value. Just bail.
372
0
        return false;
373
0
    }
374
0
  }
375
0
  else {
376
0
    switch (boxInfo->mBoxPack) {
377
0
      case StyleBoxPack::Start:
378
0
        aValign = nsBoxFrame::vAlign_Top;
379
0
        return true;
380
0
      case StyleBoxPack::Center:
381
0
        aValign = nsBoxFrame::vAlign_Middle;
382
0
        return true;
383
0
      case StyleBoxPack::End:
384
0
        aValign = nsBoxFrame::vAlign_Bottom;
385
0
        return true;
386
0
      default: // Nonsensical value. Just bail.
387
0
        return false;
388
0
    }
389
0
  }
390
0
391
0
  return false;
392
0
}
393
394
void
395
nsBoxFrame::GetInitialOrientation(bool& aIsHorizontal)
396
0
{
397
0
 // see if we are a vertical or horizontal box.
398
0
  if (!GetContent())
399
0
    return;
400
0
401
0
  // Check the style system first.
402
0
  const nsStyleXUL* boxInfo = StyleXUL();
403
0
  if (boxInfo->mBoxOrient == StyleBoxOrient::Horizontal) {
404
0
    aIsHorizontal = true;
405
0
  } else {
406
0
    aIsHorizontal = false;
407
0
  }
408
0
409
0
  // Now see if we have an attribute.  The attribute overrides
410
0
  // the style system value.
411
0
  if (!GetContent()->IsElement())
412
0
    return;
413
0
414
0
  static Element::AttrValuesArray strings[] =
415
0
    {&nsGkAtoms::vertical, &nsGkAtoms::horizontal, nullptr};
416
0
  int32_t index =
417
0
    GetContent()->AsElement()->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::orient,
418
0
                                               strings, eCaseMatters);
419
0
  if (index >= 0) {
420
0
    aIsHorizontal = index == 1;
421
0
  }
422
0
}
423
424
void
425
nsBoxFrame::GetInitialDirection(bool& aIsNormal)
426
0
{
427
0
  if (!GetContent())
428
0
    return;
429
0
430
0
  if (IsXULHorizontal()) {
431
0
    // For horizontal boxes only, we initialize our value based off the CSS 'direction' property.
432
0
    // This means that BiDI users will end up with horizontally inverted chrome.
433
0
    aIsNormal = (StyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR); // If text runs RTL then so do we.
434
0
  }
435
0
  else
436
0
    aIsNormal = true; // Assume a normal direction in the vertical case.
437
0
438
0
  // Now check the style system to see if we should invert aIsNormal.
439
0
  const nsStyleXUL* boxInfo = StyleXUL();
440
0
  if (boxInfo->mBoxDirection == StyleBoxDirection::Reverse) {
441
0
    aIsNormal = !aIsNormal; // Invert our direction.
442
0
  }
443
0
444
0
  if (!GetContent()->IsElement()) {
445
0
    return;
446
0
  }
447
0
448
0
  Element* element = GetContent()->AsElement();
449
0
450
0
  // Now see if we have an attribute.  The attribute overrides
451
0
  // the style system value.
452
0
  if (IsXULHorizontal()) {
453
0
    static Element::AttrValuesArray strings[] =
454
0
      {&nsGkAtoms::reverse, &nsGkAtoms::ltr, &nsGkAtoms::rtl, nullptr};
455
0
    int32_t index = element->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::dir,
456
0
        strings, eCaseMatters);
457
0
    if (index >= 0) {
458
0
      bool values[] = {!aIsNormal, true, false};
459
0
      aIsNormal = values[index];
460
0
    }
461
0
  } else if (element->AttrValueIs(kNameSpaceID_None, nsGkAtoms::dir,
462
0
                                  nsGkAtoms::reverse, eCaseMatters)) {
463
0
    aIsNormal = !aIsNormal;
464
0
  }
465
0
}
466
467
/* Returns true if it was set.
468
 */
469
bool
470
nsBoxFrame::GetInitialEqualSize(bool& aEqualSize)
471
0
{
472
0
 // see if we are a vertical or horizontal box.
473
0
  if (!GetContent() || !GetContent()->IsElement())
474
0
     return false;
475
0
476
0
  if (GetContent()->AsElement()->AttrValueIs(kNameSpaceID_None,
477
0
                                             nsGkAtoms::equalsize,
478
0
                                             nsGkAtoms::always, eCaseMatters)) {
479
0
    aEqualSize = true;
480
0
    return true;
481
0
  }
482
0
483
0
  return false;
484
0
}
485
486
/* Returns true if it was set.
487
 */
488
bool
489
nsBoxFrame::GetInitialAutoStretch(bool& aStretch)
490
0
{
491
0
  if (!GetContent())
492
0
     return false;
493
0
494
0
  // Check the align attribute.
495
0
  if (GetContent()->IsElement()) {
496
0
    static Element::AttrValuesArray strings[] =
497
0
      {&nsGkAtoms::_empty, &nsGkAtoms::stretch, nullptr};
498
0
    int32_t index =
499
0
      GetContent()->AsElement()->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::align,
500
0
                                                 strings, eCaseMatters);
501
0
    if (index != Element::ATTR_MISSING && index != 0) {
502
0
      aStretch = index == 1;
503
0
      return true;
504
0
    }
505
0
  }
506
0
507
0
  // Check the CSS box-align property.
508
0
  const nsStyleXUL* boxInfo = StyleXUL();
509
0
  aStretch = (boxInfo->mBoxAlign == StyleBoxAlign::Stretch);
510
0
511
0
  return true;
512
0
}
513
514
void
515
nsBoxFrame::DidReflow(nsPresContext*           aPresContext,
516
                      const ReflowInput*  aReflowInput)
517
0
{
518
0
  nsFrameState preserveBits =
519
0
    mState & (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN);
520
0
  nsFrame::DidReflow(aPresContext, aReflowInput);
521
0
  AddStateBits(preserveBits);
522
0
}
523
524
bool
525
nsBoxFrame::HonorPrintBackgroundSettings()
526
0
{
527
0
  return !mContent->IsInNativeAnonymousSubtree() &&
528
0
    nsContainerFrame::HonorPrintBackgroundSettings();
529
0
}
530
531
#ifdef DO_NOISY_REFLOW
532
static int myCounter = 0;
533
static void printSize(char * aDesc, nscoord aSize)
534
{
535
  printf(" %s: ", aDesc);
536
  if (aSize == NS_UNCONSTRAINEDSIZE) {
537
    printf("UC");
538
  } else {
539
    printf("%d", aSize);
540
  }
541
}
542
#endif
543
544
/* virtual */ nscoord
545
nsBoxFrame::GetMinISize(gfxContext *aRenderingContext)
546
0
{
547
0
  nscoord result;
548
0
  DISPLAY_MIN_INLINE_SIZE(this, result);
549
0
550
0
  nsBoxLayoutState state(PresContext(), aRenderingContext);
551
0
  nsSize minSize = GetXULMinSize(state);
552
0
553
0
  // GetXULMinSize returns border-box width, and we want to return content
554
0
  // width.  Since Reflow uses the reflow state's border and padding, we
555
0
  // actually just want to subtract what GetXULMinSize added, which is the
556
0
  // result of GetXULBorderAndPadding.
557
0
  nsMargin bp;
558
0
  GetXULBorderAndPadding(bp);
559
0
560
0
  result = minSize.width - bp.LeftRight();
561
0
  result = std::max(result, 0);
562
0
563
0
  return result;
564
0
}
565
566
/* virtual */ nscoord
567
nsBoxFrame::GetPrefISize(gfxContext *aRenderingContext)
568
0
{
569
0
  nscoord result;
570
0
  DISPLAY_PREF_INLINE_SIZE(this, result);
571
0
572
0
  nsBoxLayoutState state(PresContext(), aRenderingContext);
573
0
  nsSize prefSize = GetXULPrefSize(state);
574
0
575
0
  // GetXULPrefSize returns border-box width, and we want to return content
576
0
  // width.  Since Reflow uses the reflow state's border and padding, we
577
0
  // actually just want to subtract what GetXULPrefSize added, which is the
578
0
  // result of GetXULBorderAndPadding.
579
0
  nsMargin bp;
580
0
  GetXULBorderAndPadding(bp);
581
0
582
0
  result = prefSize.width - bp.LeftRight();
583
0
  result = std::max(result, 0);
584
0
585
0
  return result;
586
0
}
587
588
void
589
nsBoxFrame::Reflow(nsPresContext*          aPresContext,
590
                   ReflowOutput&     aDesiredSize,
591
                   const ReflowInput& aReflowInput,
592
                   nsReflowStatus&          aStatus)
593
0
{
594
0
  MarkInReflow();
595
0
  // If you make changes to this method, please keep nsLeafBoxFrame::Reflow
596
0
  // in sync, if the changes are applicable there.
597
0
598
0
  DO_GLOBAL_REFLOW_COUNT("nsBoxFrame");
599
0
  DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
600
0
  MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
601
0
602
0
  NS_ASSERTION(aReflowInput.ComputedWidth() >=0 &&
603
0
               aReflowInput.ComputedHeight() >= 0, "Computed Size < 0");
604
0
605
#ifdef DO_NOISY_REFLOW
606
  printf("\n-------------Starting BoxFrame Reflow ----------------------------\n");
607
  printf("%p ** nsBF::Reflow %d ", this, myCounter++);
608
609
  printSize("AW", aReflowInput.AvailableWidth());
610
  printSize("AH", aReflowInput.AvailableHeight());
611
  printSize("CW", aReflowInput.ComputedWidth());
612
  printSize("CH", aReflowInput.ComputedHeight());
613
614
  printf(" *\n");
615
616
#endif
617
618
0
  // create the layout state
619
0
  nsBoxLayoutState state(aPresContext, aReflowInput.mRenderingContext,
620
0
                         &aReflowInput, aReflowInput.mReflowDepth);
621
0
622
0
  WritingMode wm = aReflowInput.GetWritingMode();
623
0
  LogicalSize computedSize(wm, aReflowInput.ComputedISize(),
624
0
                           aReflowInput.ComputedBSize());
625
0
626
0
  LogicalMargin m = aReflowInput.ComputedLogicalBorderPadding();
627
0
  // GetXULBorderAndPadding(m);
628
0
629
0
  LogicalSize prefSize(wm);
630
0
631
0
  // if we are told to layout intrinsic then get our preferred size.
632
0
  NS_ASSERTION(computedSize.ISize(wm) != NS_INTRINSICSIZE,
633
0
               "computed inline size should always be computed");
634
0
  if (computedSize.BSize(wm) == NS_INTRINSICSIZE) {
635
0
    nsSize physicalPrefSize = GetXULPrefSize(state);
636
0
    nsSize minSize = GetXULMinSize(state);
637
0
    nsSize maxSize = GetXULMaxSize(state);
638
0
    // XXXbz isn't GetXULPrefSize supposed to bounds-check for us?
639
0
    physicalPrefSize = BoundsCheck(minSize, physicalPrefSize, maxSize);
640
0
    prefSize = LogicalSize(wm, physicalPrefSize);
641
0
  }
642
0
643
0
  // get our desiredSize
644
0
  computedSize.ISize(wm) += m.IStart(wm) + m.IEnd(wm);
645
0
646
0
  if (aReflowInput.ComputedBSize() == NS_INTRINSICSIZE) {
647
0
    computedSize.BSize(wm) = prefSize.BSize(wm);
648
0
    // prefSize is border-box but min/max constraints are content-box.
649
0
    nscoord blockDirBorderPadding =
650
0
      aReflowInput.ComputedLogicalBorderPadding().BStartEnd(wm);
651
0
    nscoord contentBSize = computedSize.BSize(wm) - blockDirBorderPadding;
652
0
    // Note: contentHeight might be negative, but that's OK because min-height
653
0
    // is never negative.
654
0
    computedSize.BSize(wm) = aReflowInput.ApplyMinMaxHeight(contentBSize) +
655
0
                             blockDirBorderPadding;
656
0
  } else {
657
0
    computedSize.BSize(wm) += m.BStart(wm) + m.BEnd(wm);
658
0
  }
659
0
660
0
  nsSize physicalSize = computedSize.GetPhysicalSize(wm);
661
0
  nsRect r(mRect.x, mRect.y, physicalSize.width, physicalSize.height);
662
0
663
0
  SetXULBounds(state, r);
664
0
665
0
  // layout our children
666
0
  XULLayout(state);
667
0
668
0
  // ok our child could have gotten bigger. So lets get its bounds
669
0
670
0
  // get the ascent
671
0
  LogicalSize boxSize = GetLogicalSize(wm);
672
0
  nscoord ascent = boxSize.BSize(wm);
673
0
674
0
  // getting the ascent could be a lot of work. Don't get it if
675
0
  // we are the root. The viewport doesn't care about it.
676
0
  if (!(mState & NS_STATE_IS_ROOT)) {
677
0
    ascent = GetXULBoxAscent(state);
678
0
  }
679
0
680
0
  aDesiredSize.SetSize(wm, boxSize);
681
0
  aDesiredSize.SetBlockStartAscent(ascent);
682
0
683
0
  aDesiredSize.mOverflowAreas = GetOverflowAreas();
684
0
685
#ifdef DO_NOISY_REFLOW
686
  {
687
    printf("%p ** nsBF(done) W:%d H:%d  ", this, aDesiredSize.Width(), aDesiredSize.Height());
688
689
    if (maxElementSize) {
690
      printf("MW:%d\n", *maxElementWidth);
691
    } else {
692
      printf("MW:?\n");
693
    }
694
695
  }
696
#endif
697
698
0
  ReflowAbsoluteFrames(aPresContext, aDesiredSize, aReflowInput, aStatus);
699
0
700
0
  NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
701
0
}
702
703
nsSize
704
nsBoxFrame::GetXULPrefSize(nsBoxLayoutState& aBoxLayoutState)
705
0
{
706
0
  NS_ASSERTION(aBoxLayoutState.GetRenderingContext(),
707
0
               "must have rendering context");
708
0
709
0
  nsSize size(0,0);
710
0
  DISPLAY_PREF_SIZE(this, size);
711
0
  if (!DoesNeedRecalc(mPrefSize)) {
712
0
    size = mPrefSize;
713
0
    return size;
714
0
  }
715
0
716
0
  if (IsXULCollapsed())
717
0
    return size;
718
0
719
0
  // if the size was not completely redefined in CSS then ask our children
720
0
  bool widthSet, heightSet;
721
0
  if (!nsIFrame::AddXULPrefSize(this, size, widthSet, heightSet))
722
0
  {
723
0
    if (mLayoutManager) {
724
0
      nsSize layoutSize = mLayoutManager->GetXULPrefSize(this, aBoxLayoutState);
725
0
      if (!widthSet)
726
0
        size.width = layoutSize.width;
727
0
      if (!heightSet)
728
0
        size.height = layoutSize.height;
729
0
    }
730
0
    else {
731
0
      size = nsBox::GetXULPrefSize(aBoxLayoutState);
732
0
    }
733
0
  }
734
0
735
0
  nsSize minSize = GetXULMinSize(aBoxLayoutState);
736
0
  nsSize maxSize = GetXULMaxSize(aBoxLayoutState);
737
0
  mPrefSize = BoundsCheck(minSize, size, maxSize);
738
0
739
0
  return mPrefSize;
740
0
}
741
742
nscoord
743
nsBoxFrame::GetXULBoxAscent(nsBoxLayoutState& aBoxLayoutState)
744
0
{
745
0
  if (!DoesNeedRecalc(mAscent))
746
0
     return mAscent;
747
0
748
0
  if (IsXULCollapsed())
749
0
    return 0;
750
0
751
0
  if (mLayoutManager)
752
0
    mAscent = mLayoutManager->GetAscent(this, aBoxLayoutState);
753
0
  else
754
0
    mAscent = nsBox::GetXULBoxAscent(aBoxLayoutState);
755
0
756
0
  return mAscent;
757
0
}
758
759
nsSize
760
nsBoxFrame::GetXULMinSize(nsBoxLayoutState& aBoxLayoutState)
761
0
{
762
0
  NS_ASSERTION(aBoxLayoutState.GetRenderingContext(),
763
0
               "must have rendering context");
764
0
765
0
  nsSize size(0,0);
766
0
  DISPLAY_MIN_SIZE(this, size);
767
0
  if (!DoesNeedRecalc(mMinSize)) {
768
0
    size = mMinSize;
769
0
    return size;
770
0
  }
771
0
772
0
  if (IsXULCollapsed())
773
0
    return size;
774
0
775
0
  // if the size was not completely redefined in CSS then ask our children
776
0
  bool widthSet, heightSet;
777
0
  if (!nsIFrame::AddXULMinSize(aBoxLayoutState, this, size, widthSet, heightSet))
778
0
  {
779
0
    if (mLayoutManager) {
780
0
      nsSize layoutSize = mLayoutManager->GetXULMinSize(this, aBoxLayoutState);
781
0
      if (!widthSet)
782
0
        size.width = layoutSize.width;
783
0
      if (!heightSet)
784
0
        size.height = layoutSize.height;
785
0
    }
786
0
    else {
787
0
      size = nsBox::GetXULMinSize(aBoxLayoutState);
788
0
    }
789
0
  }
790
0
791
0
  mMinSize = size;
792
0
793
0
  return size;
794
0
}
795
796
nsSize
797
nsBoxFrame::GetXULMaxSize(nsBoxLayoutState& aBoxLayoutState)
798
0
{
799
0
  NS_ASSERTION(aBoxLayoutState.GetRenderingContext(),
800
0
               "must have rendering context");
801
0
802
0
  nsSize size(NS_INTRINSICSIZE, NS_INTRINSICSIZE);
803
0
  DISPLAY_MAX_SIZE(this, size);
804
0
  if (!DoesNeedRecalc(mMaxSize)) {
805
0
    size = mMaxSize;
806
0
    return size;
807
0
  }
808
0
809
0
  if (IsXULCollapsed())
810
0
    return size;
811
0
812
0
  // if the size was not completely redefined in CSS then ask our children
813
0
  bool widthSet, heightSet;
814
0
  if (!nsIFrame::AddXULMaxSize(this, size, widthSet, heightSet))
815
0
  {
816
0
    if (mLayoutManager) {
817
0
      nsSize layoutSize = mLayoutManager->GetXULMaxSize(this, aBoxLayoutState);
818
0
      if (!widthSet)
819
0
        size.width = layoutSize.width;
820
0
      if (!heightSet)
821
0
        size.height = layoutSize.height;
822
0
    }
823
0
    else {
824
0
      size = nsBox::GetXULMaxSize(aBoxLayoutState);
825
0
    }
826
0
  }
827
0
828
0
  mMaxSize = size;
829
0
830
0
  return size;
831
0
}
832
833
nscoord
834
nsBoxFrame::GetXULFlex()
835
0
{
836
0
  if (!DoesNeedRecalc(mFlex))
837
0
     return mFlex;
838
0
839
0
  mFlex = nsBox::GetXULFlex();
840
0
841
0
  return mFlex;
842
0
}
843
844
/**
845
 * If subclassing please subclass this method not layout.
846
 * layout will call this method.
847
 */
848
NS_IMETHODIMP
849
nsBoxFrame::DoXULLayout(nsBoxLayoutState& aState)
850
0
{
851
0
  uint32_t oldFlags = aState.LayoutFlags();
852
0
  aState.SetLayoutFlags(0);
853
0
854
0
  nsresult rv = NS_OK;
855
0
  if (mLayoutManager) {
856
0
    CoordNeedsRecalc(mAscent);
857
0
    rv = mLayoutManager->XULLayout(this, aState);
858
0
  }
859
0
860
0
  aState.SetLayoutFlags(oldFlags);
861
0
862
0
  if (HasAbsolutelyPositionedChildren()) {
863
0
    // Set up a |reflowInput| to pass into ReflowAbsoluteFrames
864
0
    WritingMode wm = GetWritingMode();
865
0
    ReflowInput reflowInput(aState.PresContext(), this,
866
0
                                  aState.GetRenderingContext(),
867
0
                                  LogicalSize(wm, GetLogicalSize().ISize(wm),
868
0
                                              NS_UNCONSTRAINEDSIZE));
869
0
870
0
    // Set up a |desiredSize| to pass into ReflowAbsoluteFrames
871
0
    ReflowOutput desiredSize(reflowInput);
872
0
    desiredSize.Width() = mRect.width;
873
0
    desiredSize.Height() = mRect.height;
874
0
875
0
    // get the ascent (cribbed from ::Reflow)
876
0
    nscoord ascent = mRect.height;
877
0
878
0
    // getting the ascent could be a lot of work. Don't get it if
879
0
    // we are the root. The viewport doesn't care about it.
880
0
    if (!(mState & NS_STATE_IS_ROOT)) {
881
0
      ascent = GetXULBoxAscent(aState);
882
0
    }
883
0
    desiredSize.SetBlockStartAscent(ascent);
884
0
    desiredSize.mOverflowAreas = GetOverflowAreas();
885
0
886
0
    AddStateBits(NS_FRAME_IN_REFLOW);
887
0
    // Set up a |reflowStatus| to pass into ReflowAbsoluteFrames
888
0
    // (just a dummy value; hopefully that's OK)
889
0
    nsReflowStatus reflowStatus;
890
0
    ReflowAbsoluteFrames(aState.PresContext(), desiredSize,
891
0
                         reflowInput, reflowStatus);
892
0
    RemoveStateBits(NS_FRAME_IN_REFLOW);
893
0
  }
894
0
895
0
  return rv;
896
0
}
897
898
void
899
nsBoxFrame::DestroyFrom(nsIFrame* aDestructRoot, PostDestroyData& aPostDestroyData)
900
0
{
901
0
  // unregister access key
902
0
  RegUnregAccessKey(false);
903
0
904
0
  // clean up the container box's layout manager and child boxes
905
0
  SetXULLayoutManager(nullptr);
906
0
907
0
  nsContainerFrame::DestroyFrom(aDestructRoot, aPostDestroyData);
908
0
}
909
910
/* virtual */ void
911
nsBoxFrame::MarkIntrinsicISizesDirty()
912
0
{
913
0
  SizeNeedsRecalc(mPrefSize);
914
0
  SizeNeedsRecalc(mMinSize);
915
0
  SizeNeedsRecalc(mMaxSize);
916
0
  CoordNeedsRecalc(mFlex);
917
0
  CoordNeedsRecalc(mAscent);
918
0
919
0
  if (mLayoutManager) {
920
0
    nsBoxLayoutState state(PresContext());
921
0
    mLayoutManager->IntrinsicISizesDirty(this, state);
922
0
  }
923
0
924
0
  // Don't call base class method, since everything it does is within an
925
0
  // IsXULBoxWrapped check.
926
0
}
927
928
void
929
nsBoxFrame::RemoveFrame(ChildListID     aListID,
930
                        nsIFrame*       aOldFrame)
931
0
{
932
0
  MOZ_ASSERT(aListID == kPrincipalList, "We don't support out-of-flow kids");
933
0
934
0
  nsPresContext* presContext = PresContext();
935
0
  nsBoxLayoutState state(presContext);
936
0
937
0
  // remove the child frame
938
0
  mFrames.RemoveFrame(aOldFrame);
939
0
940
0
  // notify the layout manager
941
0
  if (mLayoutManager)
942
0
    mLayoutManager->ChildrenRemoved(this, state, aOldFrame);
943
0
944
0
  // destroy the child frame
945
0
  aOldFrame->Destroy();
946
0
947
0
  // mark us dirty and generate a reflow command
948
0
  PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange,
949
0
                                NS_FRAME_HAS_DIRTY_CHILDREN);
950
0
}
951
952
void
953
nsBoxFrame::InsertFrames(ChildListID     aListID,
954
                         nsIFrame*       aPrevFrame,
955
                         nsFrameList&    aFrameList)
956
0
{
957
0
   NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
958
0
                "inserting after sibling frame with different parent");
959
0
   NS_ASSERTION(!aPrevFrame || mFrames.ContainsFrame(aPrevFrame),
960
0
                "inserting after sibling frame not in our child list");
961
0
   MOZ_ASSERT(aListID == kPrincipalList, "We don't support out-of-flow kids");
962
0
963
0
   nsBoxLayoutState state(PresContext());
964
0
965
0
   // insert the child frames
966
0
   const nsFrameList::Slice& newFrames =
967
0
     mFrames.InsertFrames(this, aPrevFrame, aFrameList);
968
0
969
0
   // notify the layout manager
970
0
   if (mLayoutManager)
971
0
     mLayoutManager->ChildrenInserted(this, state, aPrevFrame, newFrames);
972
0
973
0
   // Make sure to check box order _after_ notifying the layout
974
0
   // manager; otherwise the slice we give the layout manager will
975
0
   // just be bogus.  If the layout manager cares about the order, we
976
0
   // just lose.
977
0
   CheckBoxOrder();
978
0
979
0
   PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange,
980
0
                                 NS_FRAME_HAS_DIRTY_CHILDREN);
981
0
}
982
983
984
void
985
nsBoxFrame::AppendFrames(ChildListID     aListID,
986
                         nsFrameList&    aFrameList)
987
0
{
988
0
   MOZ_ASSERT(aListID == kPrincipalList, "We don't support out-of-flow kids");
989
0
990
0
   nsBoxLayoutState state(PresContext());
991
0
992
0
   // append the new frames
993
0
   const nsFrameList::Slice& newFrames = mFrames.AppendFrames(this, aFrameList);
994
0
995
0
   // notify the layout manager
996
0
   if (mLayoutManager)
997
0
     mLayoutManager->ChildrenAppended(this, state, newFrames);
998
0
999
0
   // Make sure to check box order _after_ notifying the layout
1000
0
   // manager; otherwise the slice we give the layout manager will
1001
0
   // just be bogus.  If the layout manager cares about the order, we
1002
0
   // just lose.
1003
0
   CheckBoxOrder();
1004
0
1005
0
   // XXXbz why is this NS_FRAME_FIRST_REFLOW check here?
1006
0
   if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
1007
0
     PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange,
1008
0
                                   NS_FRAME_HAS_DIRTY_CHILDREN);
1009
0
   }
1010
0
}
1011
1012
/* virtual */ nsContainerFrame*
1013
nsBoxFrame::GetContentInsertionFrame()
1014
0
{
1015
0
  if (GetStateBits() & NS_STATE_BOX_WRAPS_KIDS_IN_BLOCK)
1016
0
    return PrincipalChildList().FirstChild()->GetContentInsertionFrame();
1017
0
  return nsContainerFrame::GetContentInsertionFrame();
1018
0
}
1019
1020
nsresult
1021
nsBoxFrame::AttributeChanged(int32_t aNameSpaceID,
1022
                             nsAtom* aAttribute,
1023
                             int32_t aModType)
1024
0
{
1025
0
  nsresult rv = nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute,
1026
0
                                                   aModType);
1027
0
1028
0
  // Ignore 'width', 'height', 'screenX', 'screenY' and 'sizemode' on a
1029
0
  // <window>.
1030
0
  if (mContent->IsAnyOfXULElements(nsGkAtoms::window,
1031
0
                                   nsGkAtoms::page,
1032
0
                                   nsGkAtoms::dialog,
1033
0
                                   nsGkAtoms::wizard) &&
1034
0
      (nsGkAtoms::width == aAttribute ||
1035
0
       nsGkAtoms::height == aAttribute ||
1036
0
       nsGkAtoms::screenX == aAttribute ||
1037
0
       nsGkAtoms::screenY == aAttribute ||
1038
0
       nsGkAtoms::sizemode == aAttribute)) {
1039
0
    return rv;
1040
0
  }
1041
0
1042
0
  if (aAttribute == nsGkAtoms::width       ||
1043
0
      aAttribute == nsGkAtoms::height      ||
1044
0
      aAttribute == nsGkAtoms::align       ||
1045
0
      aAttribute == nsGkAtoms::valign      ||
1046
0
      aAttribute == nsGkAtoms::left        ||
1047
0
      aAttribute == nsGkAtoms::top         ||
1048
0
      aAttribute == nsGkAtoms::right        ||
1049
0
      aAttribute == nsGkAtoms::bottom       ||
1050
0
      aAttribute == nsGkAtoms::start        ||
1051
0
      aAttribute == nsGkAtoms::end          ||
1052
0
      aAttribute == nsGkAtoms::minwidth     ||
1053
0
      aAttribute == nsGkAtoms::maxwidth     ||
1054
0
      aAttribute == nsGkAtoms::minheight    ||
1055
0
      aAttribute == nsGkAtoms::maxheight    ||
1056
0
      aAttribute == nsGkAtoms::flex         ||
1057
0
      aAttribute == nsGkAtoms::orient       ||
1058
0
      aAttribute == nsGkAtoms::pack         ||
1059
0
      aAttribute == nsGkAtoms::dir          ||
1060
0
      aAttribute == nsGkAtoms::mousethrough ||
1061
0
      aAttribute == nsGkAtoms::equalsize) {
1062
0
1063
0
    if (aAttribute == nsGkAtoms::align  ||
1064
0
        aAttribute == nsGkAtoms::valign ||
1065
0
        aAttribute == nsGkAtoms::orient  ||
1066
0
        aAttribute == nsGkAtoms::pack    ||
1067
0
        aAttribute == nsGkAtoms::dir) {
1068
0
1069
0
      mValign = nsBoxFrame::vAlign_Top;
1070
0
      mHalign = nsBoxFrame::hAlign_Left;
1071
0
1072
0
      bool orient = true;
1073
0
      GetInitialOrientation(orient);
1074
0
      if (orient)
1075
0
        AddStateBits(NS_STATE_IS_HORIZONTAL);
1076
0
      else
1077
0
        RemoveStateBits(NS_STATE_IS_HORIZONTAL);
1078
0
1079
0
      bool normal = true;
1080
0
      GetInitialDirection(normal);
1081
0
      if (normal)
1082
0
        AddStateBits(NS_STATE_IS_DIRECTION_NORMAL);
1083
0
      else
1084
0
        RemoveStateBits(NS_STATE_IS_DIRECTION_NORMAL);
1085
0
1086
0
      GetInitialVAlignment(mValign);
1087
0
      GetInitialHAlignment(mHalign);
1088
0
1089
0
      bool equalSize = false;
1090
0
      GetInitialEqualSize(equalSize);
1091
0
      if (equalSize)
1092
0
        AddStateBits(NS_STATE_EQUAL_SIZE);
1093
0
      else
1094
0
        RemoveStateBits(NS_STATE_EQUAL_SIZE);
1095
0
1096
0
      bool autostretch = !!(mState & NS_STATE_AUTO_STRETCH);
1097
0
      GetInitialAutoStretch(autostretch);
1098
0
      if (autostretch)
1099
0
        AddStateBits(NS_STATE_AUTO_STRETCH);
1100
0
      else
1101
0
        RemoveStateBits(NS_STATE_AUTO_STRETCH);
1102
0
    }
1103
0
    else if (aAttribute == nsGkAtoms::left ||
1104
0
             aAttribute == nsGkAtoms::top ||
1105
0
             aAttribute == nsGkAtoms::right ||
1106
0
             aAttribute == nsGkAtoms::bottom ||
1107
0
             aAttribute == nsGkAtoms::start ||
1108
0
             aAttribute == nsGkAtoms::end) {
1109
0
      RemoveStateBits(NS_STATE_STACK_NOT_POSITIONED);
1110
0
    }
1111
0
    else if (aAttribute == nsGkAtoms::mousethrough) {
1112
0
      UpdateMouseThrough();
1113
0
    }
1114
0
1115
0
    PresShell()->FrameNeedsReflow(this, nsIPresShell::eStyleChange,
1116
0
                                  NS_FRAME_IS_DIRTY);
1117
0
  }
1118
0
  else if (aAttribute == nsGkAtoms::ordinal) {
1119
0
    nsIFrame* parent = GetParentXULBox(this);
1120
0
    // If our parent is not a box, there's not much we can do... but in that
1121
0
    // case our ordinal doesn't matter anyway, so that's ok.
1122
0
    // Also don't bother with popup frames since they are kept on the
1123
0
    // kPopupList and XULRelayoutChildAtOrdinal() only handles
1124
0
    // principal children.
1125
0
    if (parent && !(GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
1126
0
        StyleDisplay()->mDisplay != mozilla::StyleDisplay::MozPopup) {
1127
0
      parent->XULRelayoutChildAtOrdinal(this);
1128
0
      // XXXldb Should this instead be a tree change on the child or parent?
1129
0
      PresShell()->FrameNeedsReflow(parent, nsIPresShell::eStyleChange,
1130
0
                                    NS_FRAME_IS_DIRTY);
1131
0
    }
1132
0
  }
1133
0
  // If the accesskey changed, register for the new value
1134
0
  // The old value has been unregistered in nsXULElement::SetAttr
1135
0
  else if (aAttribute == nsGkAtoms::accesskey) {
1136
0
    RegUnregAccessKey(true);
1137
0
  }
1138
0
  else if (aAttribute == nsGkAtoms::rows &&
1139
0
           mContent->IsXULElement(nsGkAtoms::tree)) {
1140
0
    // Reflow ourselves and all our children if "rows" changes, since
1141
0
    // nsTreeBodyFrame's layout reads this from its parent (this frame).
1142
0
    PresShell()->FrameNeedsReflow(this, nsIPresShell::eStyleChange,
1143
0
                                  NS_FRAME_IS_DIRTY);
1144
0
  }
1145
0
1146
0
  return rv;
1147
0
}
1148
1149
void
1150
nsBoxFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
1151
                             const nsDisplayListSet& aLists)
1152
0
{
1153
0
  bool forceLayer = false;
1154
0
1155
0
  if (GetContent()->IsXULElement()) {
1156
0
    // forcelayer is only supported on XUL elements with box layout
1157
0
    if (GetContent()->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::layer)) {
1158
0
      forceLayer = true;
1159
0
    }
1160
0
    // Check for frames that are marked as a part of the region used
1161
0
    // in calculating glass margins on Windows.
1162
0
    const nsStyleDisplay* styles = StyleDisplay();
1163
0
    if (styles && styles->mAppearance == StyleAppearance::MozWinExcludeGlass) {
1164
0
      aBuilder->AddWindowExcludeGlassRegion(
1165
0
          this,
1166
0
          nsRect(aBuilder->ToReferenceFrame(this), GetSize()));
1167
0
    }
1168
0
  }
1169
0
1170
0
  nsDisplayListCollection tempLists(aBuilder);
1171
0
  const nsDisplayListSet& destination = forceLayer ? tempLists : aLists;
1172
0
1173
0
  DisplayBorderBackgroundOutline(aBuilder, destination);
1174
0
1175
0
  Maybe<nsDisplayListBuilder::AutoContainerASRTracker> contASRTracker;
1176
0
  if (forceLayer) {
1177
0
    contASRTracker.emplace(aBuilder);
1178
0
  }
1179
0
1180
0
  BuildDisplayListForChildren(aBuilder, destination);
1181
0
1182
0
  // see if we have to draw a selection frame around this container
1183
0
  DisplaySelectionOverlay(aBuilder, destination.Content());
1184
0
1185
0
  if (forceLayer) {
1186
0
    // This is a bit of a hack. Collect up all descendant display items
1187
0
    // and merge them into a single Content() list. This can cause us
1188
0
    // to violate CSS stacking order, but forceLayer is a magic
1189
0
    // XUL-only extension anyway.
1190
0
    nsDisplayList masterList;
1191
0
    masterList.AppendToTop(tempLists.BorderBackground());
1192
0
    masterList.AppendToTop(tempLists.BlockBorderBackgrounds());
1193
0
    masterList.AppendToTop(tempLists.Floats());
1194
0
    masterList.AppendToTop(tempLists.Content());
1195
0
    masterList.AppendToTop(tempLists.PositionedDescendants());
1196
0
    masterList.AppendToTop(tempLists.Outlines());
1197
0
1198
0
    const ActiveScrolledRoot* ownLayerASR = contASRTracker->GetContainerASR();
1199
0
1200
0
    DisplayListClipState::AutoSaveRestore ownLayerClipState(aBuilder);
1201
0
1202
0
    // Wrap the list to make it its own layer
1203
0
    aLists.Content()->AppendToTop(
1204
0
      MakeDisplayItem<nsDisplayOwnLayer>(aBuilder, this, &masterList, ownLayerASR,
1205
0
                                         nsDisplayOwnLayerFlags::eNone,
1206
0
                                         mozilla::layers::ScrollbarData{}, true, true));
1207
0
  }
1208
0
}
1209
1210
void
1211
nsBoxFrame::BuildDisplayListForChildren(nsDisplayListBuilder*   aBuilder,
1212
                                        const nsDisplayListSet& aLists)
1213
0
{
1214
0
  nsIFrame* kid = mFrames.FirstChild();
1215
0
  // Put each child's background onto the BlockBorderBackgrounds list
1216
0
  // to emulate the existing two-layer XUL painting scheme.
1217
0
  nsDisplayListSet set(aLists, aLists.BlockBorderBackgrounds());
1218
0
  // The children should be in the right order
1219
0
  while (kid) {
1220
0
    BuildDisplayListForChild(aBuilder, kid, set);
1221
0
    kid = kid->GetNextSibling();
1222
0
  }
1223
0
}
1224
1225
#ifdef DEBUG_FRAME_DUMP
1226
nsresult
1227
nsBoxFrame::GetFrameName(nsAString& aResult) const
1228
{
1229
  return MakeFrameName(NS_LITERAL_STRING("Box"), aResult);
1230
}
1231
#endif
1232
1233
// If you make changes to this function, check its counterparts
1234
// in nsTextBoxFrame and nsXULLabelFrame
1235
void
1236
nsBoxFrame::RegUnregAccessKey(bool aDoReg)
1237
0
{
1238
0
  MOZ_ASSERT(mContent);
1239
0
1240
0
  // only support accesskeys for the following elements
1241
0
  if (!mContent->IsAnyOfXULElements(nsGkAtoms::button,
1242
0
                                    nsGkAtoms::toolbarbutton,
1243
0
                                    nsGkAtoms::checkbox,
1244
0
                                    nsGkAtoms::textbox,
1245
0
                                    nsGkAtoms::tab,
1246
0
                                    nsGkAtoms::radio)) {
1247
0
    return;
1248
0
  }
1249
0
1250
0
  nsAutoString accessKey;
1251
0
  mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::accesskey, accessKey);
1252
0
1253
0
  if (accessKey.IsEmpty())
1254
0
    return;
1255
0
1256
0
  // With a valid PresContext we can get the ESM
1257
0
  // and register the access key
1258
0
  EventStateManager* esm = PresContext()->EventStateManager();
1259
0
1260
0
  uint32_t key = accessKey.First();
1261
0
  if (aDoReg)
1262
0
    esm->RegisterAccessKey(mContent->AsElement(), key);
1263
0
  else
1264
0
    esm->UnregisterAccessKey(mContent->AsElement(), key);
1265
0
}
1266
1267
void
1268
nsBoxFrame::AppendDirectlyOwnedAnonBoxes(nsTArray<OwnedAnonBox>& aResult)
1269
0
{
1270
0
  if (GetStateBits() & NS_STATE_BOX_WRAPS_KIDS_IN_BLOCK) {
1271
0
    aResult.AppendElement(OwnedAnonBox(PrincipalChildList().FirstChild()));
1272
0
  }
1273
0
}
1274
1275
// Helper less-than-or-equal function, used in CheckBoxOrder() as a
1276
// template-parameter for the sorting functions.
1277
static bool
1278
IsBoxOrdinalLEQ(nsIFrame* aFrame1,
1279
                nsIFrame* aFrame2)
1280
0
{
1281
0
  // If we've got a placeholder frame, use its out-of-flow frame's ordinal val.
1282
0
  nsIFrame* aRealFrame1 = nsPlaceholderFrame::GetRealFrameFor(aFrame1);
1283
0
  nsIFrame* aRealFrame2 = nsPlaceholderFrame::GetRealFrameFor(aFrame2);
1284
0
  return aRealFrame1->GetXULOrdinal() <= aRealFrame2->GetXULOrdinal();
1285
0
}
1286
1287
void
1288
nsBoxFrame::CheckBoxOrder()
1289
0
{
1290
0
  if (!nsIFrame::IsFrameListSorted<IsBoxOrdinalLEQ>(mFrames)) {
1291
0
    nsIFrame::SortFrameList<IsBoxOrdinalLEQ>(mFrames);
1292
0
  }
1293
0
}
1294
1295
nsresult
1296
nsBoxFrame::LayoutChildAt(nsBoxLayoutState& aState, nsIFrame* aBox, const nsRect& aRect)
1297
0
{
1298
0
  // get the current rect
1299
0
  nsRect oldRect(aBox->GetRect());
1300
0
  aBox->SetXULBounds(aState, aRect);
1301
0
1302
0
  bool layout = NS_SUBTREE_DIRTY(aBox);
1303
0
1304
0
  if (layout || (oldRect.width != aRect.width || oldRect.height != aRect.height))  {
1305
0
    return aBox->XULLayout(aState);
1306
0
  }
1307
0
1308
0
  return NS_OK;
1309
0
}
1310
1311
nsresult
1312
nsBoxFrame::XULRelayoutChildAtOrdinal(nsIFrame* aChild)
1313
0
{
1314
0
  uint32_t ord = aChild->GetXULOrdinal();
1315
0
1316
0
  nsIFrame* child = mFrames.FirstChild();
1317
0
  nsIFrame* newPrevSib = nullptr;
1318
0
1319
0
  while (child) {
1320
0
    if (ord < child->GetXULOrdinal()) {
1321
0
      break;
1322
0
    }
1323
0
1324
0
    if (child != aChild) {
1325
0
      newPrevSib = child;
1326
0
    }
1327
0
1328
0
    child = GetNextXULBox(child);
1329
0
  }
1330
0
1331
0
  if (aChild->GetPrevSibling() == newPrevSib) {
1332
0
    // This box is not moving.
1333
0
    return NS_OK;
1334
0
  }
1335
0
1336
0
  // Take |aChild| out of its old position in the child list.
1337
0
  mFrames.RemoveFrame(aChild);
1338
0
1339
0
  // Insert it after |newPrevSib| or at the start if it's null.
1340
0
  mFrames.InsertFrame(nullptr, newPrevSib, aChild);
1341
0
1342
0
  return NS_OK;
1343
0
}
1344
1345
/**
1346
 * This wrapper class lets us redirect mouse hits from descendant frames
1347
 * of a menu to the menu itself, if they didn't specify 'allowevents'.
1348
 *
1349
 * The wrapper simply turns a hit on a descendant element
1350
 * into a hit on the menu itself, unless there is an element between the target
1351
 * and the menu with the "allowevents" attribute.
1352
 *
1353
 * This is used by nsMenuFrame and nsTreeColFrame.
1354
 *
1355
 * Note that turning a hit on a descendant element into nullptr, so events
1356
 * could fall through to the menu background, might be an appealing simplification
1357
 * but it would mean slightly strange behaviour in some cases, because grabber
1358
 * wrappers can be created for many individual lists and items, so the exact
1359
 * fallthrough behaviour would be complex. E.g. an element with "allowevents"
1360
 * on top of the Content() list could receive the event even if it was covered
1361
 * by a PositionedDescenants() element without "allowevents". It is best to
1362
 * never convert a non-null hit into null.
1363
 */
1364
// REVIEW: This is roughly of what nsMenuFrame::GetFrameForPoint used to do.
1365
// I've made 'allowevents' affect child elements because that seems the only
1366
// reasonable thing to do.
1367
class nsDisplayXULEventRedirector final : public nsDisplayWrapList
1368
{
1369
public:
1370
  nsDisplayXULEventRedirector(nsDisplayListBuilder* aBuilder,
1371
                              nsIFrame* aFrame, nsDisplayItem* aItem,
1372
                              nsIFrame* aTargetFrame)
1373
0
    : nsDisplayWrapList(aBuilder, aFrame, aItem), mTargetFrame(aTargetFrame) {}
1374
  nsDisplayXULEventRedirector(nsDisplayListBuilder* aBuilder,
1375
                              nsIFrame* aFrame, nsDisplayList* aList,
1376
                              nsIFrame* aTargetFrame)
1377
0
    : nsDisplayWrapList(aBuilder, aFrame, aList), mTargetFrame(aTargetFrame) {}
1378
  virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
1379
                       HitTestState* aState,
1380
                       nsTArray<nsIFrame*> *aOutFrames) override;
1381
0
  virtual bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override {
1382
0
    return false;
1383
0
  }
1384
  NS_DISPLAY_DECL_NAME("XULEventRedirector", TYPE_XUL_EVENT_REDIRECTOR)
1385
private:
1386
  nsIFrame* mTargetFrame;
1387
};
1388
1389
void nsDisplayXULEventRedirector::HitTest(nsDisplayListBuilder* aBuilder,
1390
    const nsRect& aRect, HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames)
1391
0
{
1392
0
  nsTArray<nsIFrame*> outFrames;
1393
0
  mList.HitTest(aBuilder, aRect, aState, &outFrames);
1394
0
1395
0
  bool topMostAdded = false;
1396
0
  uint32_t localLength = outFrames.Length();
1397
0
1398
0
  for (uint32_t i = 0; i < localLength; i++) {
1399
0
1400
0
    for (nsIContent* content = outFrames.ElementAt(i)->GetContent();
1401
0
         content && content != mTargetFrame->GetContent();
1402
0
         content = content->GetParent()) {
1403
0
      if (!content->IsElement() ||
1404
0
          !content->AsElement()->AttrValueIs(kNameSpaceID_None,
1405
0
                                             nsGkAtoms::allowevents,
1406
0
                                             nsGkAtoms::_true, eCaseMatters)) {
1407
0
        continue;
1408
0
      }
1409
0
1410
0
      // Events are allowed on 'frame', so let it go.
1411
0
      aOutFrames->AppendElement(outFrames.ElementAt(i));
1412
0
      topMostAdded = true;
1413
0
    }
1414
0
1415
0
    // If there was no hit on the topmost frame or its ancestors,
1416
0
    // add the target frame itself as the first candidate (see bug 562554).
1417
0
    if (!topMostAdded) {
1418
0
      topMostAdded = true;
1419
0
      aOutFrames->AppendElement(mTargetFrame);
1420
0
    }
1421
0
  }
1422
0
}
1423
1424
class nsXULEventRedirectorWrapper final : public nsDisplayWrapper
1425
{
1426
public:
1427
  explicit nsXULEventRedirectorWrapper(nsIFrame* aTargetFrame)
1428
0
      : mTargetFrame(aTargetFrame) {}
1429
  virtual nsDisplayItem* WrapList(nsDisplayListBuilder* aBuilder,
1430
                                  nsIFrame* aFrame,
1431
0
                                  nsDisplayList* aList) override {
1432
0
    return MakeDisplayItem<nsDisplayXULEventRedirector>(aBuilder, aFrame, aList,
1433
0
                                                        mTargetFrame);
1434
0
  }
1435
  virtual nsDisplayItem* WrapItem(nsDisplayListBuilder* aBuilder,
1436
0
                                  nsDisplayItem* aItem) override {
1437
0
    return MakeDisplayItem<nsDisplayXULEventRedirector>(aBuilder, aItem->Frame(), aItem,
1438
0
                                                        mTargetFrame);
1439
0
  }
1440
private:
1441
  nsIFrame* mTargetFrame;
1442
};
1443
1444
void
1445
nsBoxFrame::WrapListsInRedirector(nsDisplayListBuilder*   aBuilder,
1446
                                  const nsDisplayListSet& aIn,
1447
                                  const nsDisplayListSet& aOut)
1448
0
{
1449
0
  nsXULEventRedirectorWrapper wrapper(this);
1450
0
  wrapper.WrapLists(aBuilder, this, aIn, aOut);
1451
0
}
1452
1453
bool
1454
0
nsBoxFrame::GetEventPoint(WidgetGUIEvent* aEvent, nsPoint &aPoint) {
1455
0
  LayoutDeviceIntPoint refPoint;
1456
0
  bool res = GetEventPoint(aEvent, refPoint);
1457
0
  aPoint = nsLayoutUtils::GetEventCoordinatesRelativeTo(
1458
0
    aEvent, refPoint, this);
1459
0
  return res;
1460
0
}
1461
1462
bool
1463
0
nsBoxFrame::GetEventPoint(WidgetGUIEvent* aEvent, LayoutDeviceIntPoint& aPoint) {
1464
0
  NS_ENSURE_TRUE(aEvent, false);
1465
0
1466
0
  WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
1467
0
  if (touchEvent) {
1468
0
    // return false if there is more than one touch on the page, or if
1469
0
    // we can't find a touch point
1470
0
    if (touchEvent->mTouches.Length() != 1) {
1471
0
      return false;
1472
0
    }
1473
0
1474
0
    dom::Touch* touch = touchEvent->mTouches.SafeElementAt(0);
1475
0
    if (!touch) {
1476
0
      return false;
1477
0
    }
1478
0
    aPoint = touch->mRefPoint;
1479
0
  } else {
1480
0
    aPoint = aEvent->mRefPoint;
1481
0
  }
1482
0
  return true;
1483
0
}