2012-06-27 02:12:13 +04:00
|
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* vim: set ts=2 et sw=2 tw=80: */
|
|
|
|
|
|
|
|
/* This Source Code is subject to the terms of the Mozilla Public License
|
|
|
|
* version 2.0 (the "License"). You can obtain a copy of the License at
|
|
|
|
* http://mozilla.org/MPL/2.0/. */
|
|
|
|
|
2016-04-21 02:43:24 +03:00
|
|
|
/* rendering object for CSS "display: flex" and "display: -webkit-box" */
|
2012-06-27 02:12:13 +04:00
|
|
|
|
|
|
|
#ifndef nsFlexContainerFrame_h___
|
|
|
|
#define nsFlexContainerFrame_h___
|
|
|
|
|
|
|
|
#include "nsContainerFrame.h"
|
2016-07-08 09:08:00 +03:00
|
|
|
#include "mozilla/UniquePtr.h"
|
2014-03-18 05:23:23 +04:00
|
|
|
|
|
|
|
namespace mozilla {
|
|
|
|
template <class T> class LinkedList;
|
2015-01-23 21:25:58 +03:00
|
|
|
class LogicalPoint;
|
2015-07-13 18:25:42 +03:00
|
|
|
} // namespace mozilla
|
2012-06-27 02:12:13 +04:00
|
|
|
|
2014-05-25 02:20:40 +04:00
|
|
|
nsContainerFrame* NS_NewFlexContainerFrame(nsIPresShell* aPresShell,
|
|
|
|
nsStyleContext* aContext);
|
2012-06-27 02:12:13 +04:00
|
|
|
|
2016-04-21 02:43:24 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* This is the rendering object used for laying out elements with
|
|
|
|
* "display: flex" or "display: inline-flex".
|
|
|
|
*
|
|
|
|
* We also use this class for elements with "display: -webkit-box" or
|
|
|
|
* "display: -webkit-inline-box" (but not "-moz-box" / "-moz-inline-box" --
|
|
|
|
* those are rendered with old-school XUL frame classes).
|
|
|
|
*
|
|
|
|
* Note: we represent the -webkit-box family of properties (-webkit-box-orient,
|
|
|
|
* -webkit-box-flex, etc.) as aliases for their -moz equivalents. And for
|
|
|
|
* -webkit-{inline-}box containers, nsFlexContainerFrame will honor those
|
|
|
|
* "legacy" properties for alignment/flexibility/etc. *instead of* honoring the
|
|
|
|
* modern flexbox & alignment properties. For brevity, many comments in
|
|
|
|
* nsFlexContainerFrame.cpp simply refer to these properties using their
|
|
|
|
* "-webkit" versions, since we're mostly expecting to encounter them in that
|
|
|
|
* form. (Technically, the "-moz" versions of these properties *can* influence
|
|
|
|
* layout here as well (since that's what the -webkit versions are aliased to)
|
|
|
|
* -- but only inside of a "display:-webkit-{inline-}box" container.)
|
|
|
|
*/
|
2017-04-30 18:30:08 +03:00
|
|
|
class nsFlexContainerFrame final : public nsContainerFrame
|
|
|
|
{
|
2014-03-08 03:58:38 +04:00
|
|
|
public:
|
2017-05-26 13:11:11 +03:00
|
|
|
NS_DECL_FRAMEARENA_HELPERS(nsFlexContainerFrame)
|
2012-06-27 02:12:13 +04:00
|
|
|
NS_DECL_QUERYFRAME
|
|
|
|
|
|
|
|
// Factory method:
|
2014-05-25 02:20:40 +04:00
|
|
|
friend nsContainerFrame* NS_NewFlexContainerFrame(nsIPresShell* aPresShell,
|
|
|
|
nsStyleContext* aContext);
|
2012-06-27 02:12:13 +04:00
|
|
|
|
2014-01-22 02:51:58 +04:00
|
|
|
// Forward-decls of helper classes
|
2014-01-22 05:05:07 +04:00
|
|
|
class FlexItem;
|
|
|
|
class FlexLine;
|
|
|
|
class FlexboxAxisTracker;
|
2014-06-19 04:57:51 +04:00
|
|
|
struct StrutInfo;
|
2017-02-06 15:06:57 +03:00
|
|
|
class CachedMeasuringReflowResult;
|
2014-01-22 02:51:58 +04:00
|
|
|
|
2012-06-27 02:12:13 +04:00
|
|
|
// nsIFrame overrides
|
2016-12-02 12:02:00 +03:00
|
|
|
void Init(nsIContent* aContent,
|
|
|
|
nsContainerFrame* aParent,
|
|
|
|
nsIFrame* aPrevInFlow) override;
|
|
|
|
|
2017-01-18 03:27:03 +03:00
|
|
|
void BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
|
|
|
const nsDisplayListSet& aLists) override;
|
2012-09-30 10:38:46 +04:00
|
|
|
|
2017-01-06 17:34:16 +03:00
|
|
|
void MarkIntrinsicISizesDirty() override;
|
|
|
|
|
2017-01-18 03:27:03 +03:00
|
|
|
void Reflow(nsPresContext* aPresContext,
|
|
|
|
ReflowOutput& aDesiredSize,
|
|
|
|
const ReflowInput& aReflowInput,
|
|
|
|
nsReflowStatus& aStatus) override;
|
2012-09-30 10:38:46 +04:00
|
|
|
|
2017-06-09 22:14:53 +03:00
|
|
|
nscoord GetMinISize(gfxContext* aRenderingContext) override;
|
|
|
|
nscoord GetPrefISize(gfxContext* aRenderingContext) override;
|
2012-09-30 10:38:46 +04:00
|
|
|
|
2014-01-06 03:31:14 +04:00
|
|
|
#ifdef DEBUG_FRAME_DUMP
|
2017-01-18 03:27:03 +03:00
|
|
|
nsresult GetFrameName(nsAString& aResult) const override;
|
2014-01-06 03:31:14 +04:00
|
|
|
#endif
|
2016-10-11 22:54:00 +03:00
|
|
|
|
|
|
|
nscoord GetLogicalBaseline(mozilla::WritingMode aWM) const override;
|
|
|
|
|
2016-12-21 01:56:35 +03:00
|
|
|
bool GetVerticalAlignBaseline(mozilla::WritingMode aWM,
|
|
|
|
nscoord* aBaseline) const override
|
|
|
|
{
|
|
|
|
return GetNaturalBaselineBOffset(aWM, BaselineSharingGroup::eFirst, aBaseline);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool GetNaturalBaselineBOffset(mozilla::WritingMode aWM,
|
|
|
|
BaselineSharingGroup aBaselineGroup,
|
|
|
|
nscoord* aBaseline) const override
|
|
|
|
{
|
|
|
|
if (HasAnyStateBits(NS_STATE_FLEX_SYNTHESIZE_BASELINE)) {
|
|
|
|
return false;
|
|
|
|
}
|
2016-12-21 01:56:35 +03:00
|
|
|
*aBaseline = aBaselineGroup == BaselineSharingGroup::eFirst ?
|
|
|
|
mBaselineFromLastReflow : mLastBaselineFromLastReflow;
|
2016-12-21 01:56:35 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-10-31 18:59:40 +03:00
|
|
|
// nsContainerFrame overrides
|
|
|
|
uint16_t CSSAlignmentForAbsPosChild(
|
|
|
|
const ReflowInput& aChildRI,
|
|
|
|
mozilla::LogicalAxis aLogicalAxis) const override;
|
|
|
|
|
2012-09-30 10:38:46 +04:00
|
|
|
// Flexbox-specific public methods
|
|
|
|
bool IsHorizontal();
|
2012-06-27 02:12:13 +04:00
|
|
|
|
2016-10-15 03:23:36 +03:00
|
|
|
/**
|
|
|
|
* Helper function to calculate packing space and initial offset of alignment
|
|
|
|
* subjects in MainAxisPositionTracker() and CrossAxisPositionTracker() for
|
2016-10-15 03:16:00 +03:00
|
|
|
* space-between, space-around, and space-evenly.
|
2016-10-15 03:23:36 +03:00
|
|
|
*
|
|
|
|
* @param aNumThingsToPack Number of alignment subjects.
|
|
|
|
* @param aAlignVal Value for align-self or justify-self.
|
|
|
|
* @param aFirstSubjectOffset Outparam for first subject offset.
|
|
|
|
* @param aNumPackingSpacesRemaining Outparam for number of equal-sized
|
|
|
|
* packing spaces to apply between each
|
|
|
|
* alignment subject.
|
|
|
|
* @param aPackingSpaceRemaining Outparam for total amount of packing
|
|
|
|
* space to be divided up.
|
|
|
|
*/
|
|
|
|
static void CalculatePackingSpace(uint32_t aNumThingsToPack,
|
|
|
|
uint8_t aAlignVal,
|
|
|
|
nscoord* aFirstSubjectOffset,
|
|
|
|
uint32_t* aNumPackingSpacesRemaining,
|
|
|
|
nscoord* aPackingSpaceRemaining);
|
|
|
|
|
2012-06-27 02:12:13 +04:00
|
|
|
protected:
|
|
|
|
// Protected constructor & destructor
|
2016-04-18 08:51:36 +03:00
|
|
|
explicit nsFlexContainerFrame(nsStyleContext* aContext)
|
2017-05-26 13:11:11 +03:00
|
|
|
: nsContainerFrame(aContext, kClassID)
|
2016-10-11 22:54:00 +03:00
|
|
|
, mBaselineFromLastReflow(NS_INTRINSIC_WIDTH_UNKNOWN)
|
2016-12-21 01:56:35 +03:00
|
|
|
, mLastBaselineFromLastReflow(NS_INTRINSIC_WIDTH_UNKNOWN)
|
2012-09-30 10:38:46 +04:00
|
|
|
{}
|
2017-04-30 18:30:08 +03:00
|
|
|
|
2012-06-27 02:12:13 +04:00
|
|
|
virtual ~nsFlexContainerFrame();
|
|
|
|
|
2014-01-22 02:51:57 +04:00
|
|
|
/*
|
|
|
|
* This method does the bulk of the flex layout, implementing the algorithm
|
|
|
|
* described at:
|
|
|
|
* http://dev.w3.org/csswg/css-flexbox/#layout-algorithm
|
|
|
|
* (with a few initialization pieces happening in the caller, Reflow().
|
|
|
|
*
|
|
|
|
* Since this is a helper for Reflow(), this takes all the same parameters
|
|
|
|
* as Reflow(), plus a few more parameters that Reflow() sets up for us.
|
|
|
|
*
|
|
|
|
* (The logic behind the division of work between Reflow and DoFlexLayout is
|
|
|
|
* as follows: DoFlexLayout() begins at the step that we have to jump back
|
|
|
|
* to, if we find any visibility:collapse children, and Reflow() does
|
|
|
|
* everything before that point.)
|
|
|
|
*/
|
2014-06-13 19:37:02 +04:00
|
|
|
void DoFlexLayout(nsPresContext* aPresContext,
|
2016-07-21 13:36:38 +03:00
|
|
|
ReflowOutput& aDesiredSize,
|
2016-07-21 13:36:39 +03:00
|
|
|
const ReflowInput& aReflowInput,
|
2014-06-13 19:37:02 +04:00
|
|
|
nsReflowStatus& aStatus,
|
|
|
|
nscoord aContentBoxMainSize,
|
2015-05-12 23:34:22 +03:00
|
|
|
nscoord aAvailableBSizeForContent,
|
2014-06-13 19:37:02 +04:00
|
|
|
nsTArray<StrutInfo>& aStruts,
|
|
|
|
const FlexboxAxisTracker& aAxisTracker);
|
2014-01-22 02:51:57 +04:00
|
|
|
|
2012-12-27 00:17:52 +04:00
|
|
|
/**
|
|
|
|
* Checks whether our child-frame list "mFrames" is sorted, using the given
|
|
|
|
* IsLessThanOrEqual function, and sorts it if it's not already sorted.
|
|
|
|
*
|
|
|
|
* XXXdholbert Once we support pagination, we need to make this function
|
|
|
|
* check our continuations as well (or wrap it in a function that does).
|
|
|
|
*
|
|
|
|
* @return true if we had to sort mFrames, false if it was already sorted.
|
|
|
|
*/
|
|
|
|
template<bool IsLessThanOrEqual(nsIFrame*, nsIFrame*)>
|
|
|
|
bool SortChildrenIfNeeded();
|
|
|
|
|
2012-09-30 10:38:46 +04:00
|
|
|
// Protected flex-container-specific methods / member-vars
|
|
|
|
#ifdef DEBUG
|
|
|
|
void SanityCheckAnonymousFlexItems() const;
|
|
|
|
#endif // DEBUG
|
|
|
|
|
2014-07-22 19:24:36 +04:00
|
|
|
/*
|
|
|
|
* Returns a new FlexItem for the given child frame, allocated on the heap.
|
|
|
|
* Guaranteed to return non-null. Caller is responsible for managing the
|
|
|
|
* FlexItem's lifetime.
|
|
|
|
*
|
|
|
|
* Before returning, this method also processes the FlexItem to resolve its
|
|
|
|
* flex basis (including e.g. auto-height) as well as to resolve
|
|
|
|
* "min-height:auto", via ResolveAutoFlexBasisAndMinSize(). (Basically, the
|
|
|
|
* returned FlexItem will be ready to participate in the "Resolve the
|
|
|
|
* Flexible Lengths" step of the Flex Layout Algorithm.)
|
|
|
|
*/
|
2016-07-08 09:08:00 +03:00
|
|
|
mozilla::UniquePtr<FlexItem> GenerateFlexItemForChild(nsPresContext* aPresContext,
|
2014-03-18 05:23:23 +04:00
|
|
|
nsIFrame* aChildFrame,
|
2016-07-21 13:36:39 +03:00
|
|
|
const ReflowInput& aParentReflowInput,
|
2014-03-18 05:23:23 +04:00
|
|
|
const FlexboxAxisTracker& aAxisTracker);
|
2013-11-01 06:39:02 +04:00
|
|
|
|
2017-01-06 17:34:16 +03:00
|
|
|
/**
|
|
|
|
* This method gets a cached measuring reflow for a flex item, or does it and
|
|
|
|
* caches it.
|
|
|
|
*
|
|
|
|
* This avoids exponential reflows, see the comment on
|
|
|
|
* CachedMeasuringReflowResult.
|
|
|
|
*/
|
|
|
|
const CachedMeasuringReflowResult& MeasureAscentAndHeightForFlexItem(
|
|
|
|
FlexItem& aItem,
|
|
|
|
nsPresContext* aPresContext,
|
|
|
|
ReflowInput& aChildReflowInput);
|
|
|
|
|
2014-07-22 19:24:36 +04:00
|
|
|
/**
|
|
|
|
* This method performs a "measuring" reflow to get the content height of
|
|
|
|
* aFlexItem.Frame() (treating it as if it had auto-height), & returns the
|
|
|
|
* resulting height.
|
|
|
|
* (Helper for ResolveAutoFlexBasisAndMinSize().)
|
|
|
|
*/
|
2014-07-22 19:24:36 +04:00
|
|
|
nscoord MeasureFlexItemContentHeight(nsPresContext* aPresContext,
|
|
|
|
FlexItem& aFlexItem,
|
|
|
|
bool aForceVerticalResizeForMeasuringReflow,
|
2016-07-21 13:36:39 +03:00
|
|
|
const ReflowInput& aParentReflowInput);
|
2014-07-22 19:24:36 +04:00
|
|
|
|
2014-07-22 19:24:36 +04:00
|
|
|
/**
|
|
|
|
* This method resolves an "auto" flex-basis and/or min-main-size value
|
|
|
|
* on aFlexItem, if needed.
|
|
|
|
* (Helper for GenerateFlexItemForChild().)
|
|
|
|
*/
|
2014-07-22 19:24:36 +04:00
|
|
|
void ResolveAutoFlexBasisAndMinSize(nsPresContext* aPresContext,
|
|
|
|
FlexItem& aFlexItem,
|
2016-07-21 13:36:39 +03:00
|
|
|
const ReflowInput& aItemReflowInput,
|
2014-07-22 19:24:36 +04:00
|
|
|
const FlexboxAxisTracker& aAxisTracker);
|
2012-09-30 10:38:46 +04:00
|
|
|
|
2016-10-31 18:58:17 +03:00
|
|
|
/**
|
|
|
|
* This method:
|
|
|
|
* - Creates FlexItems for all of our child frames (except placeholders).
|
|
|
|
* - Groups those FlexItems into FlexLines.
|
|
|
|
* - Returns those FlexLines in the outparam |aLines|.
|
|
|
|
*
|
|
|
|
* For any child frames which are placeholders, this method will instead just
|
|
|
|
* append that child to the outparam |aPlaceholders| for separate handling.
|
|
|
|
* (Absolutely positioned children of a flex container are *not* flex items.)
|
|
|
|
*/
|
2014-06-13 19:37:02 +04:00
|
|
|
void GenerateFlexLines(nsPresContext* aPresContext,
|
2016-07-21 13:36:39 +03:00
|
|
|
const ReflowInput& aReflowInput,
|
2014-06-13 19:37:02 +04:00
|
|
|
nscoord aContentBoxMainSize,
|
2015-05-12 23:34:22 +03:00
|
|
|
nscoord aAvailableBSizeForContent,
|
2014-06-13 19:37:02 +04:00
|
|
|
const nsTArray<StrutInfo>& aStruts,
|
|
|
|
const FlexboxAxisTracker& aAxisTracker,
|
2016-10-31 18:58:17 +03:00
|
|
|
nsTArray<nsIFrame*>& aPlaceholders,
|
2014-06-13 19:37:02 +04:00
|
|
|
mozilla::LinkedList<FlexLine>& aLines);
|
2012-09-30 10:38:46 +04:00
|
|
|
|
2016-07-21 13:36:39 +03:00
|
|
|
nscoord GetMainSizeFromReflowInput(const ReflowInput& aReflowInput,
|
2013-12-05 22:57:51 +04:00
|
|
|
const FlexboxAxisTracker& aAxisTracker);
|
2012-09-30 10:38:46 +04:00
|
|
|
|
2016-07-21 13:36:39 +03:00
|
|
|
nscoord ComputeCrossSize(const ReflowInput& aReflowInput,
|
2013-12-07 01:38:49 +04:00
|
|
|
const FlexboxAxisTracker& aAxisTracker,
|
2014-02-07 05:04:52 +04:00
|
|
|
nscoord aSumLineCrossSizes,
|
2015-05-12 23:34:22 +03:00
|
|
|
nscoord aAvailableBSizeForContent,
|
2013-12-07 01:38:49 +04:00
|
|
|
bool* aIsDefinite,
|
|
|
|
nsReflowStatus& aStatus);
|
2013-11-21 22:20:01 +04:00
|
|
|
|
2014-06-13 19:37:02 +04:00
|
|
|
void SizeItemInCrossAxis(nsPresContext* aPresContext,
|
|
|
|
const FlexboxAxisTracker& aAxisTracker,
|
2016-07-21 13:36:39 +03:00
|
|
|
ReflowInput& aChildReflowInput,
|
2014-06-13 19:37:02 +04:00
|
|
|
FlexItem& aItem);
|
2012-09-30 10:38:46 +04:00
|
|
|
|
2015-01-24 01:15:11 +03:00
|
|
|
/**
|
|
|
|
* Moves the given flex item's frame to the given LogicalPosition (modulo any
|
|
|
|
* relative positioning).
|
|
|
|
*
|
|
|
|
* This can be used in cases where we've already done a "measuring reflow"
|
|
|
|
* for the flex item at the correct size, and hence can skip its final reflow
|
|
|
|
* (but still need to move it to the right final position).
|
|
|
|
*
|
2016-07-21 13:36:39 +03:00
|
|
|
* @param aReflowInput The flex container's reflow state.
|
2015-01-24 01:15:11 +03:00
|
|
|
* @param aItem The flex item whose frame should be moved.
|
|
|
|
* @param aFramePos The position where the flex item's frame should
|
|
|
|
* be placed. (pre-relative positioning)
|
2015-07-16 12:07:57 +03:00
|
|
|
* @param aContainerSize The flex container's size (required by some methods
|
2015-01-24 01:15:11 +03:00
|
|
|
* that we call, to interpret aFramePos correctly).
|
|
|
|
*/
|
2016-07-21 13:36:39 +03:00
|
|
|
void MoveFlexItemToFinalPosition(const ReflowInput& aReflowInput,
|
2015-01-24 01:15:11 +03:00
|
|
|
const FlexItem& aItem,
|
|
|
|
mozilla::LogicalPoint& aFramePos,
|
2015-07-16 12:07:57 +03:00
|
|
|
const nsSize& aContainerSize);
|
2015-01-23 21:25:58 +03:00
|
|
|
/**
|
|
|
|
* Helper-function to reflow a child frame, at its final position determined
|
|
|
|
* by flex layout.
|
|
|
|
*
|
|
|
|
* @param aPresContext The presentation context being used in reflow.
|
|
|
|
* @param aAxisTracker A FlexboxAxisTracker with the flex container's axes.
|
2016-07-21 13:36:39 +03:00
|
|
|
* @param aReflowInput The flex container's reflow state.
|
2015-01-23 21:25:58 +03:00
|
|
|
* @param aItem The flex item to be reflowed.
|
|
|
|
* @param aFramePos The position where the flex item's frame should
|
|
|
|
* be placed. (pre-relative positioning)
|
2015-07-16 12:07:57 +03:00
|
|
|
* @param aContainerSize The flex container's size (required by some methods
|
2015-01-23 21:25:58 +03:00
|
|
|
* that we call, to interpret aFramePos correctly).
|
|
|
|
*/
|
|
|
|
void ReflowFlexItem(nsPresContext* aPresContext,
|
|
|
|
const FlexboxAxisTracker& aAxisTracker,
|
2016-07-21 13:36:39 +03:00
|
|
|
const ReflowInput& aReflowInput,
|
2015-01-23 21:25:59 +03:00
|
|
|
const FlexItem& aItem,
|
2015-01-23 21:25:58 +03:00
|
|
|
mozilla::LogicalPoint& aFramePos,
|
2015-07-16 12:07:57 +03:00
|
|
|
const nsSize& aContainerSize);
|
2015-01-23 21:25:58 +03:00
|
|
|
|
2016-10-31 18:58:17 +03:00
|
|
|
/**
|
|
|
|
* Helper-function to perform a "dummy reflow" on all our nsPlaceholderFrame
|
|
|
|
* children, at the container's content-box origin.
|
|
|
|
*
|
|
|
|
* This doesn't actually represent the static position of the placeholders'
|
|
|
|
* out-of-flow (OOF) frames -- we can't compute that until we've reflowed the
|
|
|
|
* OOF, because (depending on the CSS Align properties) the static position
|
|
|
|
* may be influenced by the OOF's size. So for now, we just co-opt the
|
|
|
|
* placeholder to store the flex container's logical content-box origin, and
|
|
|
|
* we defer to nsAbsoluteContainingBlock to determine the OOF's actual static
|
|
|
|
* position (using this origin, the OOF's size, and the CSS Align
|
|
|
|
* properties).
|
|
|
|
*
|
|
|
|
* @param aPresContext The presentation context being used in reflow.
|
|
|
|
* @param aReflowInput The flex container's reflow input.
|
|
|
|
* @param aPlaceholders An array of all the flex container's
|
|
|
|
* nsPlaceholderFrame children.
|
|
|
|
* @param aContentBoxOrigin The flex container's logical content-box
|
|
|
|
* origin (in its own coordinate space).
|
|
|
|
* @param aContainerSize The flex container's size (required by some
|
|
|
|
* reflow methods to interpret positions correctly).
|
|
|
|
*/
|
|
|
|
void ReflowPlaceholders(nsPresContext* aPresContext,
|
|
|
|
const ReflowInput& aReflowInput,
|
|
|
|
nsTArray<nsIFrame*>& aPlaceholders,
|
|
|
|
const mozilla::LogicalPoint& aContentBoxOrigin,
|
|
|
|
const nsSize& aContainerSize);
|
|
|
|
|
2016-10-11 22:54:00 +03:00
|
|
|
nscoord mBaselineFromLastReflow;
|
2016-12-21 01:56:35 +03:00
|
|
|
// Note: the last baseline is a distance from our border-box end edge.
|
|
|
|
nscoord mLastBaselineFromLastReflow;
|
2012-06-27 02:12:13 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
#endif /* nsFlexContainerFrame_h___ */
|