Files
palemoon27/layout/generic/nsFontInflationData.cpp
T
roytam1 04083ef9b4 import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1235261 - Part 1: Rename nsAutoTArray to AutoTArray. r=froydnj (0662c2ac56)
- Bug 1235261 - Part 2: Switch some uses of AutoFallibleTArray to AutoTArray. r=froydnj (ab52085f2a)
- Bug 1235261 - Part 3: Switch remaining uses of AutoFallibleTArray to AutoTArray. r=froydnj (3763b16ddd)
- Bug 1235261 - Part 4: Remove AutoFallibleTArray. r=froydnj (5480b0d786)
- Bug 1235261 - Part 5: Merge nsAutoArrayBase into AutoTArray. r=froydnj (6c64e73e3b)
- Bug 1235261 - Part 7: Remove AutoInfallibleTArray. r=froydnj (acf266464e)
- Bug 1222624: Make xpath document() function use nsIPrincipals and nsIURIs rather than strings. r=peterv (5ee694d132)
- Bug 1235261 - Part 6: Rename AutoInfallibleTArray to AutoTArray. r=froydnj (d282f7df6c)
- Bug 1241394 - Hit testing with 3d transforms should use fuzzy when comparing depths. r=thinker (6c3f50670f)
- Bug 1241394 - Follow up to fix windows build bustage. (02ab2600af)
- Bug 1241394 - Check clip for the children of the establisher. r=mattwoodrow (46f151ea55)
- bug 1241453 - allow caching proxies in xpcAccessibleDocuments r=davidb (f5d41ad2ee)
- Bug 1247364 - use AllChildrenIterator::Seek by a11y tree walker, r=davidb (0ec230908e)
- Bug 1248840 - rename TreeWalker::NextChild, r=yzen (c89ecc5a29)
- Bug 1249927 - devirtualize CanHavaAnonymousChildren, r=davdib (89e8088e63)
- Bug 1206598 - Use universal reference to reduce the redundant copy. r=nfroyd (bae4ad6dd1)
- Bug 1247364 - add AllChildrenIterator::Seek, r=bz (215abebf12)
- bug 1241453 - allow storing proxies in xpcAccessibleGeneric::mIntl r=davidb (dd5e6c896b)
- bug 1241453 - allow constructing xpcAccessibles with proxies r=davidb (d0258122be)
- bug 1241453 - fixup xpcAccessible Intl() methods to not assume mIntl is always an Accessible r=davidb (168f71fdf5)
- bug 1241453 - allow xpcAccessibleDocument::mCache to use proxies as keys r=davidb (85b7eec81c)
- bug 1241453 - assert accessibles are only added to non remote xpcAccessibleDocuments r=davidb (7731b21d17)
- bug 1243077 - add ToXPC{,Document} overloads for proxied accessibles r=davidb (7bc085f1b5)
- bug 1243077 - add AccessibleOrProxy xpcAccessible::IntlGeneric() r=davidb (006a635992)
- Bug 1245464 - initialize with 0 mSupportedIfaces in xpcAccessibleGeneric in order to avoid corrupted result after bit-wise operation. r=surkov (ae41bafcef)
- bug 1241453 - allow caching xpc documents for remote documents r=davidb (a357630690)
- bug 1241453 - factor dispatching nsIAccessibleEvents out of HandleAccEvent() r=davidb (091073d981)
- Bug 1249183 - Suppress GC harder, r=terrence (2185ccb4dd)
- Bug 1248420 - Handle JSObject::getGroup OOM in js::ArraySetLength. r=jandem (04b67c8d31)
- Bug 1242270 - Add SPS pseudo frames for the Array.prototype methods; r=shu (f5e5871439)
- Bug 1247701 - Bail from ArrayShiftDenseKernel if the array is used by for-in iteration. r=jandem (41eff38954)
- Bug 1247701 followup - Change ArrayShiftDenseKernel to receive handle. r=bz (b29ce0c555)
2023-11-09 17:37:18 +08:00

383 lines
13 KiB
C++

/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
/* 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/. */
/* Per-block-formatting-context manager of font size inflation for pan and zoom UI. */
#include "nsFontInflationData.h"
#include "FramePropertyTable.h"
#include "nsTextControlFrame.h"
#include "nsListControlFrame.h"
#include "nsComboboxControlFrame.h"
#include "nsHTMLReflowState.h"
#include "nsTextFrameUtils.h"
using namespace mozilla;
using namespace mozilla::layout;
NS_DECLARE_FRAME_PROPERTY_DELETABLE(FontInflationDataProperty,
nsFontInflationData)
/* static */ nsFontInflationData*
nsFontInflationData::FindFontInflationDataFor(const nsIFrame *aFrame)
{
// We have one set of font inflation data per block formatting context.
const nsIFrame *bfc = FlowRootFor(aFrame);
NS_ASSERTION(bfc->GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT,
"should have found a flow root");
return static_cast<nsFontInflationData*>(
bfc->Properties().Get(FontInflationDataProperty()));
}
/* static */ bool
nsFontInflationData::UpdateFontInflationDataISizeFor(const nsHTMLReflowState& aReflowState)
{
nsIFrame *bfc = aReflowState.frame;
NS_ASSERTION(bfc->GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT,
"should have been given a flow root");
FrameProperties bfcProps(bfc->Properties());
nsFontInflationData *data = static_cast<nsFontInflationData*>(
bfcProps.Get(FontInflationDataProperty()));
bool oldInflationEnabled;
nscoord oldNCAISize;
if (data) {
oldNCAISize = data->mNCAISize;
oldInflationEnabled = data->mInflationEnabled;
} else {
data = new nsFontInflationData(bfc);
bfcProps.Set(FontInflationDataProperty(), data);
oldNCAISize = -1;
oldInflationEnabled = true; /* not relevant */
}
data->UpdateISize(aReflowState);
if (oldInflationEnabled != data->mInflationEnabled)
return true;
return oldInflationEnabled &&
oldNCAISize != data->mNCAISize;
}
/* static */ void
nsFontInflationData::MarkFontInflationDataTextDirty(nsIFrame *aBFCFrame)
{
NS_ASSERTION(aBFCFrame->GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT,
"should have been given a flow root");
FrameProperties bfcProps(aBFCFrame->Properties());
nsFontInflationData *data = static_cast<nsFontInflationData*>(
bfcProps.Get(FontInflationDataProperty()));
if (data) {
data->MarkTextDirty();
}
}
nsFontInflationData::nsFontInflationData(nsIFrame *aBFCFrame)
: mBFCFrame(aBFCFrame)
, mNCAISize(0)
, mTextAmount(0)
, mTextThreshold(0)
, mInflationEnabled(false)
, mTextDirty(true)
{
}
/**
* Find the closest common ancestor between aFrame1 and aFrame2, except
* treating the parent of a frame as the first-in-flow of its parent (so
* the result doesn't change when breaking changes).
*
* aKnownCommonAncestor is a known common ancestor of both.
*/
static nsIFrame*
NearestCommonAncestorFirstInFlow(nsIFrame *aFrame1, nsIFrame *aFrame2,
nsIFrame *aKnownCommonAncestor)
{
aFrame1 = aFrame1->FirstInFlow();
aFrame2 = aFrame2->FirstInFlow();
aKnownCommonAncestor = aKnownCommonAncestor->FirstInFlow();
AutoTArray<nsIFrame*, 32> ancestors1, ancestors2;
for (nsIFrame *f = aFrame1; f != aKnownCommonAncestor;
(f = f->GetParent()) && (f = f->FirstInFlow())) {
ancestors1.AppendElement(f);
}
for (nsIFrame *f = aFrame2; f != aKnownCommonAncestor;
(f = f->GetParent()) && (f = f->FirstInFlow())) {
ancestors2.AppendElement(f);
}
nsIFrame *result = aKnownCommonAncestor;
uint32_t i1 = ancestors1.Length(),
i2 = ancestors2.Length();
while (i1-- != 0 && i2-- != 0) {
if (ancestors1[i1] != ancestors2[i2]) {
break;
}
result = ancestors1[i1];
}
return result;
}
static nscoord
ComputeDescendantISize(const nsHTMLReflowState& aAncestorReflowState,
nsIFrame *aDescendantFrame)
{
nsIFrame *ancestorFrame = aAncestorReflowState.frame->FirstInFlow();
if (aDescendantFrame == ancestorFrame) {
return aAncestorReflowState.ComputedISize();
}
AutoTArray<nsIFrame*, 16> frames;
for (nsIFrame *f = aDescendantFrame; f != ancestorFrame;
f = f->GetParent()->FirstInFlow()) {
frames.AppendElement(f);
}
// This ignores the inline-size contributions made by scrollbars, though in
// reality we don't have any scrollbars on the sorts of devices on
// which we use font inflation, so it's not a problem. But it may
// occasionally cause problems when writing tests on desktop.
uint32_t len = frames.Length();
nsHTMLReflowState *reflowStates = static_cast<nsHTMLReflowState*>
(moz_xmalloc(sizeof(nsHTMLReflowState) * len));
nsPresContext *presContext = aDescendantFrame->PresContext();
for (uint32_t i = 0; i < len; ++i) {
const nsHTMLReflowState &parentReflowState =
(i == 0) ? aAncestorReflowState : reflowStates[i - 1];
nsIFrame *frame = frames[len - i - 1];
WritingMode wm = frame->GetWritingMode();
LogicalSize availSize = parentReflowState.ComputedSize(wm);
availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
MOZ_ASSERT(frame->GetParent()->FirstInFlow() ==
parentReflowState.frame->FirstInFlow(),
"bad logic in this function");
new (reflowStates + i) nsHTMLReflowState(presContext, parentReflowState,
frame, availSize);
}
MOZ_ASSERT(reflowStates[len - 1].frame == aDescendantFrame,
"bad logic in this function");
nscoord result = reflowStates[len - 1].ComputedISize();
for (uint32_t i = len; i-- != 0; ) {
reflowStates[i].~nsHTMLReflowState();
}
moz_free(reflowStates);
return result;
}
void
nsFontInflationData::UpdateISize(const nsHTMLReflowState &aReflowState)
{
nsIFrame *bfc = aReflowState.frame;
NS_ASSERTION(bfc->GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT,
"must be block formatting context");
nsIFrame *firstInflatableDescendant =
FindEdgeInflatableFrameIn(bfc, eFromStart);
if (!firstInflatableDescendant) {
mTextAmount = 0;
mTextThreshold = 0; // doesn't matter
mTextDirty = false;
mInflationEnabled = false;
return;
}
nsIFrame *lastInflatableDescendant =
FindEdgeInflatableFrameIn(bfc, eFromEnd);
MOZ_ASSERT(!firstInflatableDescendant == !lastInflatableDescendant,
"null-ness should match; NearestCommonAncestorFirstInFlow"
" will crash when passed null");
// Particularly when we're computing for the root BFC, the inline-size of
// nca might differ significantly for the inline-size of bfc.
nsIFrame *nca = NearestCommonAncestorFirstInFlow(firstInflatableDescendant,
lastInflatableDescendant,
bfc);
while (!nca->IsContainerForFontSizeInflation()) {
nca = nca->GetParent()->FirstInFlow();
}
nscoord newNCAISize = ComputeDescendantISize(aReflowState, nca);
// See comment above "font.size.inflation.lineThreshold" in
// modules/libpref/src/init/all.js .
nsIPresShell* presShell = bfc->PresContext()->PresShell();
uint32_t lineThreshold = presShell->FontSizeInflationLineThreshold();
nscoord newTextThreshold = (newNCAISize * lineThreshold) / 100;
if (mTextThreshold <= mTextAmount && mTextAmount < newTextThreshold) {
// Because we truncate our scan when we hit sufficient text, we now
// need to rescan.
mTextDirty = true;
}
mNCAISize = newNCAISize;
mTextThreshold = newTextThreshold;
mInflationEnabled = mTextAmount >= mTextThreshold;
}
/* static */ nsIFrame*
nsFontInflationData::FindEdgeInflatableFrameIn(nsIFrame* aFrame,
SearchDirection aDirection)
{
// NOTE: This function has a similar structure to ScanTextIn!
// FIXME: Should probably only scan the text that's actually going to
// be inflated!
nsIFormControlFrame* fcf = do_QueryFrame(aFrame);
if (fcf) {
return aFrame;
}
// FIXME: aDirection!
AutoTArray<FrameChildList, 4> lists;
aFrame->GetChildLists(&lists);
for (uint32_t i = 0, len = lists.Length(); i < len; ++i) {
const nsFrameList& list =
lists[(aDirection == eFromStart) ? i : len - i - 1].mList;
for (nsIFrame *kid = (aDirection == eFromStart) ? list.FirstChild()
: list.LastChild();
kid;
kid = (aDirection == eFromStart) ? kid->GetNextSibling()
: kid->GetPrevSibling()) {
if (kid->GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT) {
// Goes in a different set of inflation data.
continue;
}
if (kid->GetType() == nsGkAtoms::textFrame) {
nsIContent *content = kid->GetContent();
if (content && kid == content->GetPrimaryFrame()) {
uint32_t len = nsTextFrameUtils::
ComputeApproximateLengthWithWhitespaceCompression(
content, kid->StyleText());
if (len != 0) {
return kid;
}
}
} else {
nsIFrame *kidResult =
FindEdgeInflatableFrameIn(kid, aDirection);
if (kidResult) {
return kidResult;
}
}
}
}
return nullptr;
}
void
nsFontInflationData::ScanText()
{
mTextDirty = false;
mTextAmount = 0;
ScanTextIn(mBFCFrame);
mInflationEnabled = mTextAmount >= mTextThreshold;
}
static uint32_t
DoCharCountOfLargestOption(nsIFrame *aContainer)
{
uint32_t result = 0;
for (nsIFrame* option : aContainer->PrincipalChildList()) {
uint32_t optionResult;
if (option->GetContent()->IsHTMLElement(nsGkAtoms::optgroup)) {
optionResult = DoCharCountOfLargestOption(option);
} else {
// REVIEW: Check the frame structure for this!
optionResult = 0;
for (nsIFrame* optionChild : option->PrincipalChildList()) {
if (optionChild->GetType() == nsGkAtoms::textFrame) {
optionResult += nsTextFrameUtils::
ComputeApproximateLengthWithWhitespaceCompression(
optionChild->GetContent(), optionChild->StyleText());
}
}
}
if (optionResult > result) {
result = optionResult;
}
}
return result;
}
static uint32_t
CharCountOfLargestOption(nsIFrame *aListControlFrame)
{
return DoCharCountOfLargestOption(
static_cast<nsListControlFrame*>(aListControlFrame)->GetOptionsContainer());
}
void
nsFontInflationData::ScanTextIn(nsIFrame *aFrame)
{
// NOTE: This function has a similar structure to FindEdgeInflatableFrameIn!
// FIXME: Should probably only scan the text that's actually going to
// be inflated!
nsIFrame::ChildListIterator lists(aFrame);
for (; !lists.IsDone(); lists.Next()) {
nsFrameList::Enumerator kids(lists.CurrentList());
for (; !kids.AtEnd(); kids.Next()) {
nsIFrame *kid = kids.get();
if (kid->GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT) {
// Goes in a different set of inflation data.
continue;
}
nsIAtom *fType = kid->GetType();
if (fType == nsGkAtoms::textFrame) {
nsIContent *content = kid->GetContent();
if (content && kid == content->GetPrimaryFrame()) {
uint32_t len = nsTextFrameUtils::
ComputeApproximateLengthWithWhitespaceCompression(
content, kid->StyleText());
if (len != 0) {
nscoord fontSize = kid->StyleFont()->mFont.size;
if (fontSize > 0) {
mTextAmount += fontSize * len;
}
}
}
} else if (fType == nsGkAtoms::textInputFrame) {
// We don't want changes to the amount of text in a text input
// to change what we count towards inflation.
nscoord fontSize = kid->StyleFont()->mFont.size;
int32_t charCount = static_cast<nsTextControlFrame*>(kid)->GetCols();
mTextAmount += charCount * fontSize;
} else if (fType == nsGkAtoms::comboboxControlFrame) {
// See textInputFrame above (with s/amount of text/selected option/).
// Don't just recurse down to the list control inside, since we
// need to exclude the display frame.
nscoord fontSize = kid->StyleFont()->mFont.size;
int32_t charCount = CharCountOfLargestOption(
static_cast<nsComboboxControlFrame*>(kid)->GetDropDown());
mTextAmount += charCount * fontSize;
} else if (fType == nsGkAtoms::listControlFrame) {
// See textInputFrame above (with s/amount of text/selected option/).
nscoord fontSize = kid->StyleFont()->mFont.size;
int32_t charCount = CharCountOfLargestOption(kid);
mTextAmount += charCount * fontSize;
} else {
// recursive step
ScanTextIn(kid);
}
if (mTextAmount >= mTextThreshold) {
return;
}
}
}
}