Bug 1839315: part 4) Link from `SheetLoadData::mWasAlternate` to spec. r=emilio DONTBUILD
[gecko.git] / layout / generic / MiddleCroppingBlockFrame.cpp
blob4cbbb684aab4778bf9d160dd518c390db55fc163
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/. */
7 #include "MiddleCroppingBlockFrame.h"
8 #include "nsTextFrame.h"
9 #include "nsLayoutUtils.h"
10 #include "nsTextNode.h"
11 #include "nsLineLayout.h"
12 #include "gfxContext.h"
13 #include "mozilla/dom/Document.h"
14 #include "mozilla/intl/Segmenter.h"
15 #include "mozilla/ReflowInput.h"
16 #include "mozilla/ReflowOutput.h"
18 namespace mozilla {
20 NS_QUERYFRAME_HEAD(MiddleCroppingBlockFrame)
21 NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
22 NS_QUERYFRAME_ENTRY(MiddleCroppingBlockFrame)
23 NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame)
25 MiddleCroppingBlockFrame::MiddleCroppingBlockFrame(ComputedStyle* aStyle,
26 nsPresContext* aPresContext,
27 ClassID aClassID)
28 : nsBlockFrame(aStyle, aPresContext, aClassID) {}
30 MiddleCroppingBlockFrame::~MiddleCroppingBlockFrame() = default;
32 void MiddleCroppingBlockFrame::UpdateDisplayedValue(const nsAString& aValue,
33 bool aIsCropped,
34 bool aNotify) {
35 auto* text = mTextNode.get();
36 uint32_t oldLength = aNotify ? 0 : text->TextLength();
37 text->SetText(aValue, aNotify);
38 if (!aNotify) {
39 // We can't notify during Reflow so we need to tell the text frame about the
40 // text content change we just did.
41 if (auto* textFrame = static_cast<nsTextFrame*>(text->GetPrimaryFrame())) {
42 textFrame->NotifyNativeAnonymousTextnodeChange(oldLength);
44 if (LinesBegin() != LinesEnd()) {
45 AddStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION);
46 LinesBegin()->MarkDirty();
49 mCropped = aIsCropped;
52 void MiddleCroppingBlockFrame::UpdateDisplayedValueToUncroppedValue(
53 bool aNotify) {
54 nsAutoString value;
55 GetUncroppedValue(value);
56 UpdateDisplayedValue(value, /* aIsCropped = */ false, aNotify);
59 nscoord MiddleCroppingBlockFrame::GetMinISize(gfxContext* aRenderingContext) {
60 nscoord result;
61 DISPLAY_MIN_INLINE_SIZE(this, result);
63 // Our min inline size is our pref inline size
64 result = GetPrefISize(aRenderingContext);
65 return result;
68 nscoord MiddleCroppingBlockFrame::GetPrefISize(gfxContext* aRenderingContext) {
69 nscoord result;
70 DISPLAY_PREF_INLINE_SIZE(this, result);
72 nsAutoString prevValue;
73 bool restoreOldValue = false;
75 // Make sure we measure with the uncropped value.
76 if (mCropped && mCachedPrefISize == NS_INTRINSIC_ISIZE_UNKNOWN) {
77 mTextNode->GetNodeValue(prevValue);
78 restoreOldValue = true;
79 UpdateDisplayedValueToUncroppedValue(false);
82 result = nsBlockFrame::GetPrefISize(aRenderingContext);
84 if (restoreOldValue) {
85 UpdateDisplayedValue(prevValue, /* aIsCropped = */ true, false);
88 return result;
91 bool MiddleCroppingBlockFrame::CropTextToWidth(gfxContext& aRenderingContext,
92 nscoord aWidth,
93 nsString& aText) const {
94 if (aText.IsEmpty()) {
95 return false;
98 RefPtr<nsFontMetrics> fm = nsLayoutUtils::GetFontMetricsForFrame(this, 1.0f);
100 // see if the text will completely fit in the width given
101 if (nsLayoutUtils::AppUnitWidthOfStringBidi(aText, this, *fm,
102 aRenderingContext) <= aWidth) {
103 return false;
106 DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
107 const nsDependentString& kEllipsis = nsContentUtils::GetLocalizedEllipsis();
109 // see if the width is even smaller than the ellipsis
110 fm->SetTextRunRTL(false);
111 const nscoord ellipsisWidth =
112 nsLayoutUtils::AppUnitWidthOfString(kEllipsis, *fm, drawTarget);
113 if (ellipsisWidth >= aWidth) {
114 aText = kEllipsis;
115 return true;
118 // determine how much of the string will fit in the max width
119 nscoord totalWidth = ellipsisWidth;
120 const Span text(aText);
121 intl::GraphemeClusterBreakIteratorUtf16 leftIter(text);
122 intl::GraphemeClusterBreakReverseIteratorUtf16 rightIter(text);
123 uint32_t leftPos = 0;
124 uint32_t rightPos = aText.Length();
125 nsAutoString leftString, rightString;
127 while (leftPos < rightPos) {
128 Maybe<uint32_t> pos = leftIter.Next();
129 Span chars = text.FromTo(leftPos, *pos);
130 nscoord charWidth =
131 nsLayoutUtils::AppUnitWidthOfString(chars, *fm, drawTarget);
132 if (totalWidth + charWidth > aWidth) {
133 break;
136 leftString.Append(chars);
137 leftPos = *pos;
138 totalWidth += charWidth;
140 if (leftPos >= rightPos) {
141 break;
144 pos = rightIter.Next();
145 chars = text.FromTo(*pos, rightPos);
146 charWidth = nsLayoutUtils::AppUnitWidthOfString(chars, *fm, drawTarget);
147 if (totalWidth + charWidth > aWidth) {
148 break;
151 rightString.Insert(chars, 0);
152 rightPos = *pos;
153 totalWidth += charWidth;
156 aText = leftString + kEllipsis + rightString;
157 return true;
160 void MiddleCroppingBlockFrame::Reflow(nsPresContext* aPresContext,
161 ReflowOutput& aDesiredSize,
162 const ReflowInput& aReflowInput,
163 nsReflowStatus& aStatus) {
164 // Restore the uncropped value.
165 nsAutoString value;
166 GetUncroppedValue(value);
167 bool cropped = false;
168 while (true) {
169 UpdateDisplayedValue(value, cropped, false); // update the text node
170 AddStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION);
171 LinesBegin()->MarkDirty();
172 nsBlockFrame::Reflow(aPresContext, aDesiredSize, aReflowInput, aStatus);
173 if (cropped) {
174 break;
176 nscoord currentICoord = aReflowInput.mLineLayout
177 ? aReflowInput.mLineLayout->GetCurrentICoord()
178 : 0;
179 const nscoord availSize = aReflowInput.AvailableISize() - currentICoord;
180 const nscoord sizeToFit = std::min(aReflowInput.ComputedISize(), availSize);
181 if (LinesBegin()->ISize() > sizeToFit) {
182 // The value overflows - crop it and reflow again (once).
183 if (CropTextToWidth(*aReflowInput.mRenderingContext, sizeToFit, value)) {
184 nsBlockFrame::DidReflow(aPresContext, &aReflowInput);
185 aStatus.Reset();
186 MarkSubtreeDirty();
187 AddStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION);
188 mCachedMinISize = NS_INTRINSIC_ISIZE_UNKNOWN;
189 mCachedPrefISize = NS_INTRINSIC_ISIZE_UNKNOWN;
190 cropped = true;
191 continue;
194 break;
198 nsresult MiddleCroppingBlockFrame::CreateAnonymousContent(
199 nsTArray<ContentInfo>& aContent) {
200 auto* doc = PresContext()->Document();
201 mTextNode = new (doc->NodeInfoManager()) nsTextNode(doc->NodeInfoManager());
202 // Update the displayed text to reflect the current element's value.
203 UpdateDisplayedValueToUncroppedValue(false);
204 aContent.AppendElement(mTextNode);
205 return NS_OK;
208 void MiddleCroppingBlockFrame::AppendAnonymousContentTo(
209 nsTArray<nsIContent*>& aContent, uint32_t aFilter) {
210 aContent.AppendElement(mTextNode);
213 void MiddleCroppingBlockFrame::Destroy(DestroyContext& aContext) {
214 aContext.AddAnonymousContent(mTextNode.forget());
215 nsBlockFrame::Destroy(aContext);
218 } // namespace mozilla