Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/layout/tables/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 39 kB image not shown  

Quelle  BasicTableLayoutStrategy.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
// vim:cindent:ts=2:et:sw=2:
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


/*
 * Web-compatible algorithms that determine column and table widths,
 * used for CSS2's 'table-layout: auto'.
 */


#include "BasicTableLayoutStrategy.h"

#include <algorithm>

#include "nsTableFrame.h"
#include "nsTableColFrame.h"
#include "nsTableCellFrame.h"
#include "nsLayoutUtils.h"
#include "nsGkAtoms.h"
#include "SpanningCellSorter.h"
#include "nsIContent.h"

using namespace mozilla;
using namespace mozilla::layout;

#undef DEBUG_TABLE_STRATEGY

BasicTableLayoutStrategy::BasicTableLayoutStrategy(nsTableFrame* aTableFrame)
    : nsITableLayoutStrategy(nsITableLayoutStrategy::Auto),
      mTableFrame(aTableFrame) {
  MarkIntrinsicISizesDirty();
}

/* virtual */
BasicTableLayoutStrategy::~BasicTableLayoutStrategy() = default;

/* virtual */
nscoord BasicTableLayoutStrategy::GetMinISize(gfxContext* aRenderingContext) {
  if (mMinISize == NS_INTRINSIC_ISIZE_UNKNOWN) {
    ComputeIntrinsicISizes(aRenderingContext);
  }
  return mMinISize;
}

/* virtual */
nscoord BasicTableLayoutStrategy::GetPrefISize(gfxContext* aRenderingContext,
                                               bool aComputingSize) {
  NS_ASSERTION((mPrefISize == NS_INTRINSIC_ISIZE_UNKNOWN) ==
                   (mPrefISizePctExpand == NS_INTRINSIC_ISIZE_UNKNOWN),
               "dirtyness out of sync");
  if (mPrefISize == NS_INTRINSIC_ISIZE_UNKNOWN) {
    ComputeIntrinsicISizes(aRenderingContext);
  }
  return aComputingSize ? mPrefISizePctExpand : mPrefISize;
}

struct CellISizeInfo {
  CellISizeInfo(nscoord aMinCoord, nscoord aPrefCoord, float aPrefPercent,
                bool aHasSpecifiedISize)
      : hasSpecifiedISize(aHasSpecifiedISize),
        minCoord(aMinCoord),
        prefCoord(aPrefCoord),
        prefPercent(aPrefPercent) {}

  bool hasSpecifiedISize;
  nscoord minCoord;
  nscoord prefCoord;
  float prefPercent;
};

// A helper for ComputeColumnIntrinsicISizes(), used for both column and cell
// intrinsic inline size calculations. The parts needed only for cells are
// skipped when aIsCell is false.
static CellISizeInfo GetISizeInfo(gfxContext* aRenderingContext,
                                  nsIFrame* aFrame, WritingMode aWM,
                                  bool aIsCell) {
  MOZ_ASSERT(aFrame->GetWritingMode() == aWM,
             "The caller is expected to pass aFrame's writing mode!");
  nscoord minCoord, prefCoord;
  const nsStylePosition* stylePos = aFrame->StylePosition();
  bool isQuirks =
      aFrame->PresContext()->CompatibilityMode() == eCompatibility_NavQuirks;
  nscoord boxSizingToBorderEdge = 0;
  if (aIsCell) {
    // If aFrame is a container for font size inflation, then shrink
    // wrapping inside of it should not apply font size inflation.
    AutoMaybeDisableFontInflation an(aFrame);

    // Resolve the cell's block size 'cellBSize' as a percentage basis, in case
    // it impacts its children's inline-size contributions (e.g. via percentage
    // block size + aspect-ratio). However, this behavior might not be
    // web-compatible (Bug 1461852).
    //
    // Note that if the cell *itself* has a percentage-based block size, we
    // treat it as unresolvable here by using an unconstrained cbBSize. It will
    // be resolved during the "special bsize reflow" pass if the table has a
    // specified block size. See nsTableFrame::Reflow() and
    // ReflowInput::Flags::mSpecialBSizeReflow.
    const nscoord cbBSize = NS_UNCONSTRAINEDSIZE;
    const nscoord contentEdgeToBoxSizingBSize =
        stylePos->mBoxSizing == StyleBoxSizing::Border
            ? aFrame->IntrinsicBSizeOffsets().BorderPadding()
            : 0;
    const nscoord cellBSize = nsIFrame::ComputeBSizeValueAsPercentageBasis(
        stylePos->BSize(aWM), stylePos->MinBSize(aWM), stylePos->MaxBSize(aWM),
        cbBSize, contentEdgeToBoxSizingBSize);

    const IntrinsicSizeInput input(
        aRenderingContext, Nothing(),
        Some(LogicalSize(aWM, NS_UNCONSTRAINEDSIZE, cellBSize)));
    minCoord = aFrame->GetMinISize(input);
    prefCoord = aFrame->GetPrefISize(input);
    // Until almost the end of this function, minCoord and prefCoord
    // represent the box-sizing based isize values (which mean they
    // should include inline padding and border width when
    // box-sizing is set to border-box).
    // Note that this function returns border-box isize, we add the
    // outer edges near the end of this function.

    // XXX Should we ignore percentage padding?
    nsIFrame::IntrinsicSizeOffsetData offsets = aFrame->IntrinsicISizeOffsets();
    if (stylePos->mBoxSizing == StyleBoxSizing::Content) {
      boxSizingToBorderEdge = offsets.padding + offsets.border;
    } else {
      // StyleBoxSizing::Border
      minCoord += offsets.padding + offsets.border;
      prefCoord += offsets.padding + offsets.border;
    }
  } else {
    minCoord = 0;
    prefCoord = 0;
  }
  float prefPercent = 0.0f;
  bool hasSpecifiedISize = false;

  const auto& iSize = stylePos->ISize(aWM);
  // NOTE: We're ignoring calc() units with both lengths and percentages here,
  // for lack of a sensible idea for what to do with them.  This means calc()
  // with percentages is basically handled like 'auto' for table cells and
  // columns.
  if (iSize.ConvertsToLength()) {
    hasSpecifiedISize = true;
    nscoord c = iSize.ToLength();
    // Quirk: A cell with "nowrap" set and a coord value for the
    // isize which is bigger than the intrinsic minimum isize uses
    // that coord value as the minimum isize.
    // This is kept up-to-date with dynamic changes to nowrap by code in
    // nsTableCellFrame::AttributeChanged
    if (aIsCell && c > minCoord && isQuirks &&
        aFrame->GetContent()->AsElement()->HasAttr(nsGkAtoms::nowrap)) {
      minCoord = c;
    }
    prefCoord = std::max(c, minCoord);
  } else if (iSize.ConvertsToPercentage()) {
    prefPercent = iSize.ToPercentage();
  } else if (aIsCell) {
    switch (iSize.tag) {
      case StyleSize::Tag::MaxContent:
        // 'inline-size' only affects pref isize, not min
        // isize, so don't change anything
        break;
      case StyleSize::Tag::MinContent:
        prefCoord = minCoord;
        break;
      case StyleSize::Tag::MozAvailable:
      case StyleSize::Tag::WebkitFillAvailable:
      case StyleSize::Tag::Stretch:
      case StyleSize::Tag::FitContent:
      case StyleSize::Tag::FitContentFunction:
        // TODO: Bug 1708310: Make sure fit-content() work properly in table.
      case StyleSize::Tag::Auto:
      case StyleSize::Tag::LengthPercentage:
      case StyleSize::Tag::AnchorSizeFunction:
        break;
    }
  }

  StyleMaxSize maxISize = stylePos->MaxISize(aWM);
  if (nsIFrame::ToExtremumLength(maxISize)) {
    if (!aIsCell || maxISize.BehavesLikeStretchOnInlineAxis()) {
      maxISize = StyleMaxSize::None();
    } else if (maxISize.IsFitContent() || maxISize.IsFitContentFunction()) {
      // TODO: Bug 1708310: Make sure fit-content() work properly in table.
      // for 'max-inline-size', '-moz-fit-content' is like 'max-content'
      maxISize = StyleMaxSize::MaxContent();
    }
  }
  // XXX To really implement 'max-inline-size' well, we'd need to store
  // it separately on the columns.
  const LogicalSize zeroSize(aWM);
  if (maxISize.ConvertsToLength() || nsIFrame::ToExtremumLength(maxISize)) {
    nscoord c = aFrame
                    ->ComputeISizeValue(
                        aRenderingContext, aWM, zeroSize, zeroSize, 0, maxISize,
                        stylePos->BSize(aWM), aFrame->GetAspectRatio())
                    .mISize;
    minCoord = std::min(c, minCoord);
    prefCoord = std::min(c, prefCoord);
  } else if (maxISize.ConvertsToPercentage()) {
    float p = maxISize.ToPercentage();
    if (p < prefPercent) {
      prefPercent = p;
    }
  }

  StyleSize minISize = stylePos->MinISize(aWM);
  if (nsIFrame::ToExtremumLength(maxISize)) {
    if (!aIsCell || minISize.BehavesLikeStretchOnInlineAxis()) {
      minISize = StyleSize::LengthPercentage(LengthPercentage::Zero());
    } else if (minISize.IsFitContent() || minISize.IsFitContentFunction()) {
      // TODO: Bug 1708310: Make sure fit-content() work properly in table.
      // for 'min-inline-size', '-moz-fit-content' is like 'min-content'
      minISize = StyleSize::MinContent();
    }
  }

  if (minISize.ConvertsToLength() || nsIFrame::ToExtremumLength(minISize)) {
    nscoord c = aFrame
                    ->ComputeISizeValue(
                        aRenderingContext, aWM, zeroSize, zeroSize, 0, minISize,
                        stylePos->BSize(aWM), aFrame->GetAspectRatio())
                    .mISize;
    minCoord = std::max(c, minCoord);
    prefCoord = std::max(c, prefCoord);
  } else if (minISize.ConvertsToPercentage()) {
    float p = minISize.ToPercentage();
    if (p > prefPercent) {
      prefPercent = p;
    }
  }

  // XXX Should col frame have border/padding considered?
  if (aIsCell) {
    minCoord += boxSizingToBorderEdge;
    prefCoord = NSCoordSaturatingAdd(prefCoord, boxSizingToBorderEdge);
  }

  return CellISizeInfo(minCoord, prefCoord, prefPercent, hasSpecifiedISize);
}

static inline CellISizeInfo GetCellISizeInfo(gfxContext* aRenderingContext,
                                             nsTableCellFrame* aCellFrame,
                                             WritingMode aWM) {
  return GetISizeInfo(aRenderingContext, aCellFrame, aWM, true);
}

static inline CellISizeInfo GetColISizeInfo(gfxContext* aRenderingContext,
                                            nsIFrame* aFrame, WritingMode aWM) {
  return GetISizeInfo(aRenderingContext, aFrame, aWM, false);
}

/**
 * The algorithm in this function, in addition to meeting the
 * requirements of Web-compatibility, is also invariant under reordering
 * of the rows within a table (something that most, but not all, other
 * browsers are).
 */

void BasicTableLayoutStrategy::ComputeColumnIntrinsicISizes(
    gfxContext* aRenderingContext) {
  nsTableFrame* tableFrame = mTableFrame;
  nsTableCellMap* cellMap = tableFrame->GetCellMap();
  WritingMode wm = tableFrame->GetWritingMode();

  mozilla::AutoStackArena arena;
  SpanningCellSorter spanningCells;

  // Loop over the columns to consider the columns and cells *without*
  // a colspan.
  int32_t col, col_end;
  for (col = 0, col_end = cellMap->GetColCount(); col < col_end; ++col) {
    nsTableColFrame* colFrame = tableFrame->GetColFrame(col);
    if (!colFrame) {
      NS_ERROR("column frames out of sync with cell map");
      continue;
    }
    colFrame->ResetIntrinsics();
    colFrame->ResetSpanIntrinsics();

    // Consider the isizes on the column.
    CellISizeInfo colInfo = GetColISizeInfo(aRenderingContext, colFrame, wm);
    colFrame->AddCoords(colInfo.minCoord, colInfo.prefCoord,
                        colInfo.hasSpecifiedISize);
    colFrame->AddPrefPercent(colInfo.prefPercent);

    // Consider the isizes on the column-group.  Note that we follow
    // what the HTML spec says here, and make the isize apply to
    // each column in the group, not the group as a whole.

    // If column has isize, column-group doesn't override isize.
    if (colInfo.minCoord == 0 && colInfo.prefCoord == 0 &&
        colInfo.prefPercent == 0.0f) {
      NS_ASSERTION(colFrame->GetParent()->IsTableColGroupFrame(),
                   "expected a column-group");
      colInfo = GetColISizeInfo(aRenderingContext, colFrame->GetParent(), wm);
      colFrame->AddCoords(colInfo.minCoord, colInfo.prefCoord,
                          colInfo.hasSpecifiedISize);
      colFrame->AddPrefPercent(colInfo.prefPercent);
    }

    // Consider the contents of and the isizes on the cells without
    // colspans.
    nsCellMapColumnIterator columnIter(cellMap, col);
    int32_t row, colSpan;
    nsTableCellFrame* cellFrame;
    while ((cellFrame = columnIter.GetNextFrame(&row, &colSpan))) {
      if (colSpan > 1) {
        spanningCells.AddCell(colSpan, row, col);
        continue;
      }

      CellISizeInfo info = GetCellISizeInfo(aRenderingContext, cellFrame, wm);

      colFrame->AddCoords(info.minCoord, info.prefCoord,
                          info.hasSpecifiedISize);
      colFrame->AddPrefPercent(info.prefPercent);
    }
#ifdef DEBUG_dbaron_off
    printf("table %p col %d nonspan: min=%d pref=%d spec=%d pct=%f\n",
           mTableFrame, col, colFrame->GetMinCoord(), colFrame->GetPrefCoord(),
           colFrame->GetHasSpecifiedCoord(), colFrame->GetPrefPercent());
#endif
  }
#ifdef DEBUG_TABLE_STRATEGY
  printf("ComputeColumnIntrinsicISizes single\n");
  mTableFrame->Dump(falsetruefalse);
#endif

  // Consider the cells with a colspan that we saved in the loop above
  // into the spanning cell sorter.  We consider these cells by seeing
  // if they require adding to the isizes resulting only from cells
  // with a smaller colspan, and therefore we must process them sorted
  // in increasing order by colspan.  For each colspan group, we
  // accumulate new values to accumulate in the column frame's Span*
  // members.
  //
  // Considering things only relative to the isizes resulting from
  // cells with smaller colspans (rather than incrementally including
  // the results from spanning cells, or doing spanning and
  // non-spanning cells in a single pass) means that layout remains
  // row-order-invariant and (except for percentage isizes that add to
  // more than 100%) column-order invariant.
  //
  // Starting with smaller colspans makes it more likely that we
  // satisfy all the constraints given and don't distribute space to
  // columns where we don't need it.
  SpanningCellSorter::Item* item;
  int32_t colSpan;
  while ((item = spanningCells.GetNext(&colSpan))) {
    NS_ASSERTION(colSpan > 1,
                 "cell should not have been put in spanning cell sorter");
    do {
      int32_t row = item->row;
      col = item->col;
      CellData* cellData = cellMap->GetDataAt(row, col);
      NS_ASSERTION(cellData && cellData->IsOrig(),
                   "bogus result from spanning cell sorter");

      nsTableCellFrame* cellFrame = cellData->GetCellFrame();
      NS_ASSERTION(cellFrame, "bogus result from spanning cell sorter");

      CellISizeInfo info = GetCellISizeInfo(aRenderingContext, cellFrame, wm);

      if (info.prefPercent > 0.0f) {
        DistributePctISizeToColumns(info.prefPercent, col, colSpan);
      }
      DistributeISizeToColumns(info.minCoord, col, colSpan,
                               BtlsISizeType::MinISize, info.hasSpecifiedISize);
      DistributeISizeToColumns(info.prefCoord, col, colSpan,
                               BtlsISizeType::PrefISize,
                               info.hasSpecifiedISize);
    } while ((item = item->next));

    // Combine the results of the span analysis into the main results,
    // for each increment of colspan.

    for (col = 0, col_end = cellMap->GetColCount(); col < col_end; ++col) {
      nsTableColFrame* colFrame = tableFrame->GetColFrame(col);
      if (!colFrame) {
        NS_ERROR("column frames out of sync with cell map");
        continue;
      }

      colFrame->AccumulateSpanIntrinsics();
      colFrame->ResetSpanIntrinsics();

#ifdef DEBUG_dbaron_off
      printf("table %p col %d span %d: min=%d pref=%d spec=%d pct=%f\n",
             mTableFrame, col, colSpan, colFrame->GetMinCoord(),
             colFrame->GetPrefCoord(), colFrame->GetHasSpecifiedCoord(),
             colFrame->GetPrefPercent());
#endif
    }
  }

  // Prevent percentages from adding to more than 100% by (to be
  // compatible with other browsers) treating any percentages that would
  // increase the total percentage to more than 100% as the number that
  // would increase it to only 100% (which is 0% if we've already hit
  // 100%).  This means layout depends on the order of columns.
  float pct_used = 0.0f;
  for (col = 0, col_end = cellMap->GetColCount(); col < col_end; ++col) {
    nsTableColFrame* colFrame = tableFrame->GetColFrame(col);
    if (!colFrame) {
      NS_ERROR("column frames out of sync with cell map");
      continue;
    }

    colFrame->AdjustPrefPercent(&pct_used);
  }

#ifdef DEBUG_TABLE_STRATEGY
  printf("ComputeColumnIntrinsicISizes spanning\n");
  mTableFrame->Dump(falsetruefalse);
#endif
}

void BasicTableLayoutStrategy::ComputeIntrinsicISizes(
    gfxContext* aRenderingContext) {
  ComputeColumnIntrinsicISizes(aRenderingContext);

  nsTableCellMap* cellMap = mTableFrame->GetCellMap();
  nscoord min = 0, pref = 0, max_small_pct_pref = 0, nonpct_pref_total = 0;
  float pct_total = 0.0f;  // always from 0.0f - 1.0f
  int32_t colCount = cellMap->GetColCount();
  // add a total of (colcount + 1) lots of cellSpacingX for columns where a
  // cell originates
  nscoord add = mTableFrame->GetColSpacing(colCount);

  for (int32_t col = 0; col < colCount; ++col) {
    nsTableColFrame* colFrame = mTableFrame->GetColFrame(col);
    if (!colFrame) {
      NS_ERROR("column frames out of sync with cell map");
      continue;
    }
    if (mTableFrame->ColumnHasCellSpacingBefore(col)) {
      add += mTableFrame->GetColSpacing(col - 1);
    }
    min += colFrame->GetMinCoord();
    pref = NSCoordSaturatingAdd(pref, colFrame->GetPrefCoord());

    // Percentages are of the table, so we have to reverse them for
    // intrinsic isizes.
    float p = colFrame->GetPrefPercent();
    if (p > 0.0f) {
      nscoord colPref = colFrame->GetPrefCoord();
      nscoord new_small_pct_expand =
          (colPref == nscoord_MAX ? nscoord_MAX : nscoord(float(colPref) / p));
      if (new_small_pct_expand > max_small_pct_pref) {
        max_small_pct_pref = new_small_pct_expand;
      }
      pct_total += p;
    } else {
      nonpct_pref_total =
          NSCoordSaturatingAdd(nonpct_pref_total, colFrame->GetPrefCoord());
    }
  }

  nscoord pref_pct_expand = pref;

  // Account for small percentages expanding the preferred isize of
  // *other* columns.
  if (max_small_pct_pref > pref_pct_expand) {
    pref_pct_expand = max_small_pct_pref;
  }

  // Account for large percentages expanding the preferred isize of
  // themselves.  There's no need to iterate over the columns multiple
  // times, since when there is such a need, the small percentage
  // effect is bigger anyway.  (I think!)
  NS_ASSERTION(0.0f <= pct_total && pct_total <= 1.0f,
               "column percentage inline-sizes not adjusted down to 100%");
  if (pct_total == 1.0f) {
    if (nonpct_pref_total > 0) {
      pref_pct_expand = nscoord_MAX;
      // XXX Or should I use some smaller value?  (Test this using
      // nested tables!)
    }
  } else {
    nscoord large_pct_pref =
        (nonpct_pref_total == nscoord_MAX
             ? nscoord_MAX
             : nscoord(float(nonpct_pref_total) / (1.0f - pct_total)));
    if (large_pct_pref > pref_pct_expand) {
      pref_pct_expand = large_pct_pref;
    }
  }

  // border-spacing isn't part of the basis for percentages
  if (colCount > 0) {
    min += add;
    pref = NSCoordSaturatingAdd(pref, add);
    pref_pct_expand = NSCoordSaturatingAdd(pref_pct_expand, add);
  }

  mMinISize = min;
  mPrefISize = pref;
  mPrefISizePctExpand = pref_pct_expand;
}

/* virtual */
void BasicTableLayoutStrategy::MarkIntrinsicISizesDirty() {
  mMinISize = NS_INTRINSIC_ISIZE_UNKNOWN;
  mPrefISize = NS_INTRINSIC_ISIZE_UNKNOWN;
  mPrefISizePctExpand = NS_INTRINSIC_ISIZE_UNKNOWN;
  mLastCalcISize = nscoord_MIN;
}

/* virtual */
void BasicTableLayoutStrategy::ComputeColumnISizes(
    const ReflowInput& aReflowInput) {
  nscoord iSize = aReflowInput.ComputedISize();

  if (mLastCalcISize == iSize) {
    return;
  }
  mLastCalcISize = iSize;

  NS_ASSERTION((mMinISize == NS_INTRINSIC_ISIZE_UNKNOWN) ==
                   (mPrefISize == NS_INTRINSIC_ISIZE_UNKNOWN),
               "dirtyness out of sync");
  NS_ASSERTION((mMinISize == NS_INTRINSIC_ISIZE_UNKNOWN) ==
                   (mPrefISizePctExpand == NS_INTRINSIC_ISIZE_UNKNOWN),
               "dirtyness out of sync");
  // XXX Is this needed?
  if (mMinISize == NS_INTRINSIC_ISIZE_UNKNOWN) {
    ComputeIntrinsicISizes(aReflowInput.mRenderingContext);
  }

  nsTableCellMap* cellMap = mTableFrame->GetCellMap();
  int32_t colCount = cellMap->GetColCount();
  if (colCount <= 0) {
    return;  // nothing to do
  }

  DistributeISizeToColumns(iSize, 0, colCount, BtlsISizeType::FinalISize,
                           false);

#ifdef DEBUG_TABLE_STRATEGY
  printf("ComputeColumnISizes final\n");
  mTableFrame->Dump(falsetruefalse);
#endif
}

void BasicTableLayoutStrategy::DistributePctISizeToColumns(float aSpanPrefPct,
                                                           int32_t aFirstCol,
                                                           int32_t aColCount) {
  // First loop to determine:
  int32_t nonPctColCount = 0;  // number of spanned columns without % isize
  nscoord nonPctTotalPrefISize = 0;  // total pref isize of those columns
  // and to reduce aSpanPrefPct by columns that already have % isize

  int32_t scol, scol_end;
  nsTableCellMap* cellMap = mTableFrame->GetCellMap();
  for (scol = aFirstCol, scol_end = aFirstCol + aColCount; scol < scol_end;
       ++scol) {
    nsTableColFrame* scolFrame = mTableFrame->GetColFrame(scol);
    if (!scolFrame) {
      NS_ERROR("column frames out of sync with cell map");
      continue;
    }
    float scolPct = scolFrame->GetPrefPercent();
    if (scolPct == 0.0f) {
      nonPctTotalPrefISize += scolFrame->GetPrefCoord();
      if (cellMap->GetNumCellsOriginatingInCol(scol) > 0) {
        ++nonPctColCount;
      }
    } else {
      aSpanPrefPct -= scolPct;
    }
  }

  if (aSpanPrefPct <= 0.0f || nonPctColCount == 0) {
    // There's no %-isize on the colspan left over to distribute,
    // or there are no columns to which we could distribute %-isize
    return;
  }

  // Second loop, to distribute what remains of aSpanPrefPct
  // between the non-percent-isize spanned columns
  const bool spanHasNonPctPref = nonPctTotalPrefISize > 0;  // Loop invariant
  for (scol = aFirstCol, scol_end = aFirstCol + aColCount; scol < scol_end;
       ++scol) {
    nsTableColFrame* scolFrame = mTableFrame->GetColFrame(scol);
    if (!scolFrame) {
      NS_ERROR("column frames out of sync with cell map");
      continue;
    }

    if (scolFrame->GetPrefPercent() == 0.0f) {
      NS_ASSERTION((!spanHasNonPctPref || nonPctTotalPrefISize != 0) &&
                       nonPctColCount != 0,
                   "should not be zero if we haven't allocated "
                   "all pref percent");

      float allocatedPct;  // % isize to be given to this column
      if (spanHasNonPctPref) {
        // Group so we're multiplying by 1.0f when we need
        // to use up aSpanPrefPct.
        allocatedPct = aSpanPrefPct * (float(scolFrame->GetPrefCoord()) /
                                       float(nonPctTotalPrefISize));
      } else if (cellMap->GetNumCellsOriginatingInCol(scol) > 0) {
        // distribute equally when all pref isizes are 0
        allocatedPct = aSpanPrefPct / float(nonPctColCount);
      } else {
        allocatedPct = 0.0f;
      }
      // Allocate the percent
      scolFrame->AddSpanPrefPercent(allocatedPct);

      // To avoid accumulating rounding error from division,
      // subtract this column's values from the totals.
      aSpanPrefPct -= allocatedPct;
      nonPctTotalPrefISize -= scolFrame->GetPrefCoord();
      if (cellMap->GetNumCellsOriginatingInCol(scol) > 0) {
        --nonPctColCount;
      }

      if (!aSpanPrefPct) {
        // No more span-percent-isize to distribute --> we're done.
        NS_ASSERTION(
            spanHasNonPctPref ? nonPctTotalPrefISize == 0 : nonPctColCount == 0,
            "No more pct inline-size to distribute, "
            "but there are still cols that need some.");
        return;
      }
    }
  }
}

void BasicTableLayoutStrategy::DistributeISizeToColumns(
    nscoord aISize, int32_t aFirstCol, int32_t aColCount,
    BtlsISizeType aISizeType, bool aSpanHasSpecifiedISize) {
  NS_ASSERTION(
      aISizeType != BtlsISizeType::FinalISize ||
          (aFirstCol == 0 &&
           aColCount == mTableFrame->GetCellMap()->GetColCount()),
      "Computing final column isizes, but didn't get full column range");

  nscoord subtract = 0;
  // aISize initially includes border-spacing for the boundaries in between
  // each of the columns. We start at aFirstCol + 1 because the first
  // in-between boundary would be at the left edge of column aFirstCol + 1
  for (int32_t col = aFirstCol + 1; col < aFirstCol + aColCount; ++col) {
    if (mTableFrame->ColumnHasCellSpacingBefore(col)) {
      // border-spacing isn't part of the basis for percentages.
      subtract += mTableFrame->GetColSpacing(col - 1);
    }
  }
  if (aISizeType == BtlsISizeType::FinalISize) {
    // If we're computing final col-isize, then aISize initially includes
    // border spacing on the table's far istart + far iend edge, too.  Need
    // to subtract those out, too.
    subtract += (mTableFrame->GetColSpacing(-1) +
                 mTableFrame->GetColSpacing(aColCount));
  }
  aISize = NSCoordSaturatingSubtract(aISize, subtract, nscoord_MAX);

  /*
   * The goal of this function is to distribute |aISize| between the
   * columns by making an appropriate AddSpanCoords or SetFinalISize
   * call for each column.  (We call AddSpanCoords if we're
   * distributing a column-spanning cell's minimum or preferred isize
   * to its spanned columns.  We call SetFinalISize if we're
   * distributing a table's final isize to its columns.)
   *
   * The idea is to either assign one of the following sets of isizes
   * or a weighted average of two adjacent sets of isizes.  It is not
   * possible to assign values smaller than the smallest set of
   * isizes.  However, see below for handling the case of assigning
   * values larger than the largest set of isizes.  From smallest to
   * largest, these are:
   *
   * 1. [guess_min] Assign all columns their min isize.
   *
   * 2. [guess_min_pct] Assign all columns with percentage isizes
   * their percentage isize, and all other columns their min isize.
   *
   * 3. [guess_min_spec] Assign all columns with percentage isizes
   * their percentage isize, all columns with specified coordinate
   * isizes their pref isize (since it doesn't matter whether it's the
   * largest contributor to the pref isize that was the specified
   * contributor), and all other columns their min isize.
   *
   * 4. [guess_pref] Assign all columns with percentage isizes their
   * specified isize, and all other columns their pref isize.
   *
   * If |aISize| is *larger* than what we would assign in (4), then we
   * expand the columns:
   *
   *   a. if any columns without a specified coordinate isize or
   *   percent isize have nonzero pref isize, in proportion to pref
   *   isize [total_flex_pref]
   *
   *   b. otherwise, if any columns without a specified coordinate
   *   isize or percent isize, but with cells originating in them,
   *   have zero pref isize, equally between these
   *   [numNonSpecZeroISizeCols]
   *
   *   c. otherwise, if any columns without percent isize have nonzero
   *   pref isize, in proportion to pref isize [total_fixed_pref]
   *
   *   d. otherwise, if any columns have nonzero percentage isizes, in
   *   proportion to the percentage isizes [total_pct]
   *
   *   e. otherwise, equally.
   */


  // Loop #1 over the columns, to figure out the four values above so
  // we know which case we're dealing with.

  nscoord guess_min = 0, guess_min_pct = 0, guess_min_spec = 0, guess_pref = 0,
          total_flex_pref = 0, total_fixed_pref = 0;
  float total_pct = 0.0f;  // 0.0f to 1.0f
  int32_t numInfiniteISizeCols = 0;
  int32_t numNonSpecZeroISizeCols = 0;

  int32_t col;
  nsTableCellMap* cellMap = mTableFrame->GetCellMap();
  for (col = aFirstCol; col < aFirstCol + aColCount; ++col) {
    nsTableColFrame* colFrame = mTableFrame->GetColFrame(col);
    if (!colFrame) {
      NS_ERROR("column frames out of sync with cell map");
      continue;
    }
    nscoord min_iSize = colFrame->GetMinCoord();
    guess_min += min_iSize;
    if (colFrame->GetPrefPercent() != 0.0f) {
      float pct = colFrame->GetPrefPercent();
      total_pct += pct;
      nscoord val = nscoord(float(aISize) * pct);
      if (val < min_iSize) {
        val = min_iSize;
      }
      guess_min_pct = NSCoordSaturatingAdd(guess_min_pct, val);
      guess_pref = NSCoordSaturatingAdd(guess_pref, val);
    } else {
      nscoord pref_iSize = colFrame->GetPrefCoord();
      if (pref_iSize == nscoord_MAX) {
        ++numInfiniteISizeCols;
      }
      guess_pref = NSCoordSaturatingAdd(guess_pref, pref_iSize);
      guess_min_pct = NSCoordSaturatingAdd(guess_min_pct, min_iSize);
      if (colFrame->GetHasSpecifiedCoord()) {
        // we'll add on the rest of guess_min_spec outside the
        // loop
        nscoord delta = NSCoordSaturatingSubtract(pref_iSize, min_iSize, 0);
        guess_min_spec = NSCoordSaturatingAdd(guess_min_spec, delta);
        total_fixed_pref = NSCoordSaturatingAdd(total_fixed_pref, pref_iSize);
      } else if (pref_iSize == 0) {
        if (cellMap->GetNumCellsOriginatingInCol(col) > 0) {
          ++numNonSpecZeroISizeCols;
        }
      } else {
        total_flex_pref = NSCoordSaturatingAdd(total_flex_pref, pref_iSize);
      }
    }
  }
  guess_min_spec = NSCoordSaturatingAdd(guess_min_spec, guess_min_pct);

  // Determine what we're flexing:
  enum Loop2Type {
    FLEX_PCT_SMALL,        // between (1) and (2) above
    FLEX_FIXED_SMALL,      // between (2) and (3) above
    FLEX_FLEX_SMALL,       // between (3) and (4) above
    FLEX_FLEX_LARGE,       // greater than (4) above, case (a)
    FLEX_FLEX_LARGE_ZERO,  // greater than (4) above, case (b)
    FLEX_FIXED_LARGE,      // greater than (4) above, case (c)
    FLEX_PCT_LARGE,        // greater than (4) above, case (d)
    FLEX_ALL_LARGE         // greater than (4) above, case (e)
  };

  Loop2Type l2t;
  // These are constants (over columns) for each case's math.  We use
  // a pair of nscoords rather than a float so that we can subtract
  // each column's allocation so we avoid accumulating rounding error.
  nscoord space;  // the amount of extra isize to allocate
  union {
    nscoord c;
    float f;
  } basis;  // the sum of the statistic over columns to divide it
  if (aISize < guess_pref) {
    if (aISizeType != BtlsISizeType::FinalISize && aISize <= guess_min) {
      // Return early -- we don't have any extra space to distribute.
      return;
    }
    NS_ASSERTION(
        !(aISizeType == BtlsISizeType::FinalISize && aISize < guess_min),
        "Table inline-size is less than the sum of its columns' min "
        "inline-sizes");
    if (aISize < guess_min_pct) {
      l2t = FLEX_PCT_SMALL;
      space = aISize - guess_min;
      basis.c = guess_min_pct - guess_min;
    } else if (aISize < guess_min_spec) {
      l2t = FLEX_FIXED_SMALL;
      space = aISize - guess_min_pct;
      basis.c =
          NSCoordSaturatingSubtract(guess_min_spec, guess_min_pct, nscoord_MAX);
    } else {
      l2t = FLEX_FLEX_SMALL;
      space = aISize - guess_min_spec;
      basis.c =
          NSCoordSaturatingSubtract(guess_pref, guess_min_spec, nscoord_MAX);
    }
  } else {
    space = NSCoordSaturatingSubtract(aISize, guess_pref, nscoord_MAX);
    if (total_flex_pref > 0) {
      l2t = FLEX_FLEX_LARGE;
      basis.c = total_flex_pref;
    } else if (numNonSpecZeroISizeCols > 0) {
      l2t = FLEX_FLEX_LARGE_ZERO;
      basis.c = numNonSpecZeroISizeCols;
    } else if (total_fixed_pref > 0) {
      l2t = FLEX_FIXED_LARGE;
      basis.c = total_fixed_pref;
    } else if (total_pct > 0.0f) {
      l2t = FLEX_PCT_LARGE;
      basis.f = total_pct;
    } else {
      l2t = FLEX_ALL_LARGE;
      basis.c = aColCount;
    }
  }

#ifdef DEBUG_dbaron_off
  printf(
      "ComputeColumnISizes: %d columns in isize %d,\n"
      " guesses=[%d,%d,%d,%d], totals=[%d,%d,%f],\n"
      " l2t=%d, space=%d, basis.c=%d\n",
      aColCount, aISize, guess_min, guess_min_pct, guess_min_spec, guess_pref,
      total_flex_pref, total_fixed_pref, total_pct, l2t, space, basis.c);
#endif

  for (col = aFirstCol; col < aFirstCol + aColCount; ++col) {
    nsTableColFrame* colFrame = mTableFrame->GetColFrame(col);
    if (!colFrame) {
      NS_ERROR("column frames out of sync with cell map");
      continue;
    }
    nscoord col_iSize;

    float pct = colFrame->GetPrefPercent();
    if (pct != 0.0f) {
      col_iSize = nscoord(float(aISize) * pct);
      nscoord col_min = colFrame->GetMinCoord();
      if (col_iSize < col_min) {
        col_iSize = col_min;
      }
    } else {
      col_iSize = colFrame->GetPrefCoord();
    }

    nscoord col_iSize_before_adjust = col_iSize;

    switch (l2t) {
      case FLEX_PCT_SMALL:
        col_iSize = col_iSize_before_adjust = colFrame->GetMinCoord();
        if (pct != 0.0f) {
          nscoord pct_minus_min = nscoord(float(aISize) * pct) - col_iSize;
          if (pct_minus_min > 0) {
            float c = float(space) / float(basis.c);
            basis.c -= pct_minus_min;
            col_iSize = NSCoordSaturatingAdd(
                col_iSize, NSToCoordRound(float(pct_minus_min) * c));
          }
        }
        break;
      case FLEX_FIXED_SMALL:
        if (pct == 0.0f) {
          NS_ASSERTION(col_iSize == colFrame->GetPrefCoord(),
                       "wrong inline-size assigned");
          if (colFrame->GetHasSpecifiedCoord()) {
            nscoord col_min = colFrame->GetMinCoord();
            nscoord pref_minus_min = col_iSize - col_min;
            col_iSize = col_iSize_before_adjust = col_min;
            if (pref_minus_min != 0) {
              float c = float(space) / float(basis.c);
              basis.c = NSCoordSaturatingSubtract(basis.c, pref_minus_min,
                                                  nscoord_MAX);
              col_iSize = NSCoordSaturatingAdd(
                  col_iSize, NSToCoordRound(float(pref_minus_min) * c));
            }
          } else {
            col_iSize = col_iSize_before_adjust = colFrame->GetMinCoord();
          }
        }
        break;
      case FLEX_FLEX_SMALL:
        if (pct == 0.0f && !colFrame->GetHasSpecifiedCoord()) {
          NS_ASSERTION(col_iSize == colFrame->GetPrefCoord(),
                       "wrong inline-size assigned");
          nscoord col_min = colFrame->GetMinCoord();
          nscoord pref_minus_min =
              NSCoordSaturatingSubtract(col_iSize, col_min, 0);
          col_iSize = col_iSize_before_adjust = col_min;
          if (pref_minus_min != 0) {
            float c = float(space) / float(basis.c);
            // If we have infinite-isize cols, then the standard
            // adjustment to col_iSize using 'c' won't work,
            // because basis.c and pref_minus_min are both
            // nscoord_MAX and will cancel each other out in the
            // col_iSize adjustment (making us assign all the
            // space to the first inf-isize col).  To correct for
            // this, we'll also divide by numInfiniteISizeCols to
            // spread the space equally among the inf-isize cols.
            if (numInfiniteISizeCols) {
              if (colFrame->GetPrefCoord() == nscoord_MAX) {
                c = c / float(numInfiniteISizeCols);
                --numInfiniteISizeCols;
              } else {
                c = 0.0f;
              }
            }
            basis.c =
                NSCoordSaturatingSubtract(basis.c, pref_minus_min, nscoord_MAX);
            col_iSize = NSCoordSaturatingAdd(
                col_iSize, NSToCoordRound(float(pref_minus_min) * c));
          }
        }
        break;
      case FLEX_FLEX_LARGE:
        if (pct == 0.0f && !colFrame->GetHasSpecifiedCoord()) {
          NS_ASSERTION(col_iSize == colFrame->GetPrefCoord(),
                       "wrong inline-size assigned");
          if (col_iSize != 0) {
            if (space == nscoord_MAX) {
              basis.c -= col_iSize;
              col_iSize = nscoord_MAX;
            } else {
              float c = float(space) / float(basis.c);
              basis.c =
                  NSCoordSaturatingSubtract(basis.c, col_iSize, nscoord_MAX);
              col_iSize = NSCoordSaturatingAdd(
                  col_iSize, NSToCoordRound(float(col_iSize) * c));
            }
          }
        }
        break;
      case FLEX_FLEX_LARGE_ZERO:
        if (pct == 0.0f && !colFrame->GetHasSpecifiedCoord() &&
            cellMap->GetNumCellsOriginatingInCol(col) > 0) {
          NS_ASSERTION(col_iSize == 0 && colFrame->GetPrefCoord() == 0,
                       "Since we're in FLEX_FLEX_LARGE_ZERO case, "
                       "all auto-inline-size cols should have zero "
                       "pref inline-size.");
          float c = float(space) / float(basis.c);
          col_iSize += NSToCoordRound(c);
          --basis.c;
        }
        break;
      case FLEX_FIXED_LARGE:
        if (pct == 0.0f) {
          NS_ASSERTION(col_iSize == colFrame->GetPrefCoord(),
                       "wrong inline-size assigned");
          NS_ASSERTION(
              colFrame->GetHasSpecifiedCoord() || colFrame->GetPrefCoord() == 0,
              "wrong case");
          if (col_iSize != 0) {
            float c = float(space) / float(basis.c);
            basis.c =
                NSCoordSaturatingSubtract(basis.c, col_iSize, nscoord_MAX);
            col_iSize = NSCoordSaturatingAdd(
                col_iSize, NSToCoordRound(float(col_iSize) * c));
          }
        }
        break;
      case FLEX_PCT_LARGE:
        NS_ASSERTION(pct != 0.0f || colFrame->GetPrefCoord() == 0,
                     "wrong case");
        if (pct != 0.0f) {
          float c = float(space) / basis.f;
          col_iSize = NSCoordSaturatingAdd(col_iSize, NSToCoordRound(pct * c));
          basis.f -= pct;
        }
        break;
      case FLEX_ALL_LARGE: {
        float c = float(space) / float(basis.c);
        col_iSize = NSCoordSaturatingAdd(col_iSize, NSToCoordRound(c));
        --basis.c;
      } break;
    }

    // Only subtract from space if it's a real number.
    if (space != nscoord_MAX) {
      NS_ASSERTION(col_iSize != nscoord_MAX,
                   "How is col_iSize nscoord_MAX if space isn't?");
      NS_ASSERTION(
          col_iSize_before_adjust != nscoord_MAX,
          "How is col_iSize_before_adjust nscoord_MAX if space isn't?");
      space -= col_iSize - col_iSize_before_adjust;
    }

    NS_ASSERTION(col_iSize >= colFrame->GetMinCoord(),
                 "assigned inline-size smaller than min");

    // Apply the new isize
    switch (aISizeType) {
      case BtlsISizeType::MinISize: {
        // Note: AddSpanCoords requires both a min and pref isize.
        // For the pref isize, we'll just pass in our computed
        // min isize, because the real pref isize will be at least
        // as big
        colFrame->AddSpanCoords(col_iSize, col_iSize, aSpanHasSpecifiedISize);
      } break;
      case BtlsISizeType::PrefISize: {
        // Note: AddSpanCoords requires both a min and pref isize.
        // For the min isize, we'll just pass in 0, because
        // the real min isize will be at least 0
        colFrame->AddSpanCoords(0, col_iSize, aSpanHasSpecifiedISize);
      } break;
      case BtlsISizeType::FinalISize: {
        nscoord old_final = colFrame->GetFinalISize();
        colFrame->SetFinalISize(col_iSize);

        if (old_final != col_iSize) {
          mTableFrame->DidResizeColumns();
        }
      } break;
    }
  }
  NS_ASSERTION(
      (space == 0 || space == nscoord_MAX) &&
          ((l2t == FLEX_PCT_LARGE) ? (-0.001f < basis.f && basis.f < 0.001f)
                                   : (basis.c == 0 || basis.c == nscoord_MAX)),
      "didn't subtract all that we added");
}

Messung V0.5
C=90 H=93 G=91

¤ Dauer der Verarbeitung: 0.10 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.