LCOV - code coverage report
Current view: top level - layout/generic - nsFontInflationData.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 6 178 3.4 %
Date: 2017-07-14 16:53:18 Functions: 2 13 15.4 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
       2             : /* This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : /* Per-block-formatting-context manager of font size inflation for pan and zoom UI. */
       7             : 
       8             : #include "nsFontInflationData.h"
       9             : #include "FrameProperties.h"
      10             : #include "nsTextControlFrame.h"
      11             : #include "nsListControlFrame.h"
      12             : #include "nsComboboxControlFrame.h"
      13             : #include "mozilla/ReflowInput.h"
      14             : #include "nsTextFrameUtils.h"
      15             : 
      16             : using namespace mozilla;
      17             : using namespace mozilla::layout;
      18             : 
      19         145 : NS_DECLARE_FRAME_PROPERTY_DELETABLE(FontInflationDataProperty,
      20             :                                     nsFontInflationData)
      21             : 
      22             : /* static */ nsFontInflationData*
      23           0 : nsFontInflationData::FindFontInflationDataFor(const nsIFrame *aFrame)
      24             : {
      25             :   // We have one set of font inflation data per block formatting context.
      26           0 :   const nsIFrame *bfc = FlowRootFor(aFrame);
      27           0 :   NS_ASSERTION(bfc->GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT,
      28             :                "should have found a flow root");
      29             : 
      30           0 :   return bfc->GetProperty(FontInflationDataProperty());
      31             : }
      32             : 
      33             : /* static */ bool
      34           0 : nsFontInflationData::UpdateFontInflationDataISizeFor(const ReflowInput& aReflowInput)
      35             : {
      36           0 :   nsIFrame *bfc = aReflowInput.mFrame;
      37           0 :   NS_ASSERTION(bfc->GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT,
      38             :                "should have been given a flow root");
      39           0 :   nsFontInflationData *data = bfc->GetProperty(FontInflationDataProperty());
      40             :   bool oldInflationEnabled;
      41             :   nscoord oldNCAISize;
      42           0 :   if (data) {
      43           0 :     oldNCAISize = data->mNCAISize;
      44           0 :     oldInflationEnabled = data->mInflationEnabled;
      45             :   } else {
      46           0 :     data = new nsFontInflationData(bfc);
      47           0 :     bfc->SetProperty(FontInflationDataProperty(), data);
      48           0 :     oldNCAISize = -1;
      49           0 :     oldInflationEnabled = true; /* not relevant */
      50             :   }
      51             : 
      52           0 :   data->UpdateISize(aReflowInput);
      53             : 
      54           0 :   if (oldInflationEnabled != data->mInflationEnabled)
      55           0 :     return true;
      56             : 
      57           0 :   return oldInflationEnabled &&
      58           0 :          oldNCAISize != data->mNCAISize;
      59             : }
      60             : 
      61             : /* static */ void
      62         145 : nsFontInflationData::MarkFontInflationDataTextDirty(nsIFrame *aBFCFrame)
      63             : {
      64         145 :   NS_ASSERTION(aBFCFrame->GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT,
      65             :                "should have been given a flow root");
      66             : 
      67         145 :   nsFontInflationData *data = aBFCFrame->GetProperty(FontInflationDataProperty());
      68         145 :   if (data) {
      69           0 :     data->MarkTextDirty();
      70             :   }
      71         145 : }
      72             : 
      73           0 : nsFontInflationData::nsFontInflationData(nsIFrame *aBFCFrame)
      74             :   : mBFCFrame(aBFCFrame)
      75             :   , mNCAISize(0)
      76             :   , mTextAmount(0)
      77             :   , mTextThreshold(0)
      78             :   , mInflationEnabled(false)
      79           0 :   , mTextDirty(true)
      80             : {
      81           0 : }
      82             : 
      83             : /**
      84             :  * Find the closest common ancestor between aFrame1 and aFrame2, except
      85             :  * treating the parent of a frame as the first-in-flow of its parent (so
      86             :  * the result doesn't change when breaking changes).
      87             :  *
      88             :  * aKnownCommonAncestor is a known common ancestor of both.
      89             :  */
      90             : static nsIFrame*
      91           0 : NearestCommonAncestorFirstInFlow(nsIFrame *aFrame1, nsIFrame *aFrame2,
      92             :                                  nsIFrame *aKnownCommonAncestor)
      93             : {
      94           0 :   aFrame1 = aFrame1->FirstInFlow();
      95           0 :   aFrame2 = aFrame2->FirstInFlow();
      96           0 :   aKnownCommonAncestor = aKnownCommonAncestor->FirstInFlow();
      97             : 
      98           0 :   AutoTArray<nsIFrame*, 32> ancestors1, ancestors2;
      99           0 :   for (nsIFrame *f = aFrame1; f != aKnownCommonAncestor;
     100           0 :        (f = f->GetParent()) && (f = f->FirstInFlow())) {
     101           0 :     ancestors1.AppendElement(f);
     102             :   }
     103           0 :   for (nsIFrame *f = aFrame2; f != aKnownCommonAncestor;
     104           0 :        (f = f->GetParent()) && (f = f->FirstInFlow())) {
     105           0 :     ancestors2.AppendElement(f);
     106             :   }
     107             : 
     108           0 :   nsIFrame *result = aKnownCommonAncestor;
     109           0 :   uint32_t i1 = ancestors1.Length(),
     110           0 :            i2 = ancestors2.Length();
     111           0 :   while (i1-- != 0 && i2-- != 0) {
     112           0 :     if (ancestors1[i1] != ancestors2[i2]) {
     113           0 :       break;
     114             :     }
     115           0 :     result = ancestors1[i1];
     116             :   }
     117             : 
     118           0 :   return result;
     119             : }
     120             : 
     121             : static nscoord
     122           0 : ComputeDescendantISize(const ReflowInput& aAncestorReflowInput,
     123             :                        nsIFrame *aDescendantFrame)
     124             : {
     125           0 :   nsIFrame *ancestorFrame = aAncestorReflowInput.mFrame->FirstInFlow();
     126           0 :   if (aDescendantFrame == ancestorFrame) {
     127           0 :     return aAncestorReflowInput.ComputedISize();
     128             :   }
     129             : 
     130           0 :   AutoTArray<nsIFrame*, 16> frames;
     131           0 :   for (nsIFrame *f = aDescendantFrame; f != ancestorFrame;
     132           0 :        f = f->GetParent()->FirstInFlow()) {
     133           0 :     frames.AppendElement(f);
     134             :   }
     135             : 
     136             :   // This ignores the inline-size contributions made by scrollbars, though in
     137             :   // reality we don't have any scrollbars on the sorts of devices on
     138             :   // which we use font inflation, so it's not a problem.  But it may
     139             :   // occasionally cause problems when writing tests on desktop.
     140             : 
     141           0 :   uint32_t len = frames.Length();
     142             :   ReflowInput *reflowInputs = static_cast<ReflowInput*>
     143           0 :                                 (moz_xmalloc(sizeof(ReflowInput) * len));
     144           0 :   nsPresContext *presContext = aDescendantFrame->PresContext();
     145           0 :   for (uint32_t i = 0; i < len; ++i) {
     146             :     const ReflowInput &parentReflowInput =
     147           0 :       (i == 0) ? aAncestorReflowInput : reflowInputs[i - 1];
     148           0 :     nsIFrame *frame = frames[len - i - 1];
     149           0 :     WritingMode wm = frame->GetWritingMode();
     150           0 :     LogicalSize availSize = parentReflowInput.ComputedSize(wm);
     151           0 :     availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
     152           0 :     MOZ_ASSERT(frame->GetParent()->FirstInFlow() ==
     153             :                  parentReflowInput.mFrame->FirstInFlow(),
     154             :                "bad logic in this function");
     155           0 :     new (reflowInputs + i) ReflowInput(presContext, parentReflowInput,
     156           0 :                                              frame, availSize);
     157             :   }
     158             : 
     159           0 :   MOZ_ASSERT(reflowInputs[len - 1].mFrame == aDescendantFrame,
     160             :              "bad logic in this function");
     161           0 :   nscoord result = reflowInputs[len - 1].ComputedISize();
     162             : 
     163           0 :   for (uint32_t i = len; i-- != 0; ) {
     164             :     reflowInputs[i].~ReflowInput();
     165             :   }
     166           0 :   free(reflowInputs);
     167             : 
     168           0 :   return result;
     169             : }
     170             : 
     171             : void
     172           0 : nsFontInflationData::UpdateISize(const ReflowInput &aReflowInput)
     173             : {
     174           0 :   nsIFrame *bfc = aReflowInput.mFrame;
     175           0 :   NS_ASSERTION(bfc->GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT,
     176             :                "must be block formatting context");
     177             : 
     178             :   nsIFrame *firstInflatableDescendant =
     179           0 :              FindEdgeInflatableFrameIn(bfc, eFromStart);
     180           0 :   if (!firstInflatableDescendant) {
     181           0 :     mTextAmount = 0;
     182           0 :     mTextThreshold = 0; // doesn't matter
     183           0 :     mTextDirty = false;
     184           0 :     mInflationEnabled = false;
     185           0 :     return;
     186             :   }
     187             :   nsIFrame *lastInflatableDescendant =
     188           0 :              FindEdgeInflatableFrameIn(bfc, eFromEnd);
     189           0 :   MOZ_ASSERT(!firstInflatableDescendant == !lastInflatableDescendant,
     190             :              "null-ness should match; NearestCommonAncestorFirstInFlow"
     191             :              " will crash when passed null");
     192             : 
     193             :   // Particularly when we're computing for the root BFC, the inline-size of
     194             :   // nca might differ significantly for the inline-size of bfc.
     195             :   nsIFrame *nca = NearestCommonAncestorFirstInFlow(firstInflatableDescendant,
     196             :                                                    lastInflatableDescendant,
     197           0 :                                                    bfc);
     198           0 :   while (!nca->IsContainerForFontSizeInflation()) {
     199           0 :     nca = nca->GetParent()->FirstInFlow();
     200             :   }
     201             : 
     202           0 :   nscoord newNCAISize = ComputeDescendantISize(aReflowInput, nca);
     203             : 
     204             :   // See comment above "font.size.inflation.lineThreshold" in
     205             :   // modules/libpref/src/init/all.js .
     206           0 :   nsIPresShell* presShell = bfc->PresContext()->PresShell();
     207           0 :   uint32_t lineThreshold = presShell->FontSizeInflationLineThreshold();
     208           0 :   nscoord newTextThreshold = (newNCAISize * lineThreshold) / 100;
     209             : 
     210           0 :   if (mTextThreshold <= mTextAmount && mTextAmount < newTextThreshold) {
     211             :     // Because we truncate our scan when we hit sufficient text, we now
     212             :     // need to rescan.
     213           0 :     mTextDirty = true;
     214             :   }
     215             : 
     216           0 :   mNCAISize = newNCAISize;
     217           0 :   mTextThreshold = newTextThreshold;
     218           0 :   mInflationEnabled = mTextAmount >= mTextThreshold;
     219             : }
     220             : 
     221             : /* static */ nsIFrame*
     222           0 : nsFontInflationData::FindEdgeInflatableFrameIn(nsIFrame* aFrame,
     223             :                                                SearchDirection aDirection)
     224             : {
     225             :   // NOTE: This function has a similar structure to ScanTextIn!
     226             : 
     227             :   // FIXME: Should probably only scan the text that's actually going to
     228             :   // be inflated!
     229             : 
     230           0 :   nsIFormControlFrame* fcf = do_QueryFrame(aFrame);
     231           0 :   if (fcf) {
     232           0 :     return aFrame;
     233             :   }
     234             : 
     235             :   // FIXME: aDirection!
     236           0 :   AutoTArray<FrameChildList, 4> lists;
     237           0 :   aFrame->GetChildLists(&lists);
     238           0 :   for (uint32_t i = 0, len = lists.Length(); i < len; ++i) {
     239             :     const nsFrameList& list =
     240           0 :       lists[(aDirection == eFromStart) ? i : len - i - 1].mList;
     241           0 :     for (nsIFrame *kid = (aDirection == eFromStart) ? list.FirstChild()
     242           0 :                                                     : list.LastChild();
     243           0 :          kid;
     244           0 :          kid = (aDirection == eFromStart) ? kid->GetNextSibling()
     245             :                                           : kid->GetPrevSibling()) {
     246           0 :       if (kid->GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT) {
     247             :         // Goes in a different set of inflation data.
     248           0 :         continue;
     249             :       }
     250             : 
     251           0 :       if (kid->IsTextFrame()) {
     252           0 :         nsIContent *content = kid->GetContent();
     253           0 :         if (content && kid == content->GetPrimaryFrame()) {
     254             :           uint32_t len = nsTextFrameUtils::
     255           0 :             ComputeApproximateLengthWithWhitespaceCompression(
     256           0 :               content, kid->StyleText());
     257           0 :           if (len != 0) {
     258           0 :             return kid;
     259             :           }
     260             :         }
     261             :       } else {
     262             :         nsIFrame *kidResult =
     263           0 :           FindEdgeInflatableFrameIn(kid, aDirection);
     264           0 :         if (kidResult) {
     265           0 :           return kidResult;
     266             :         }
     267             :       }
     268             :     }
     269             :   }
     270             : 
     271           0 :   return nullptr;
     272             : }
     273             : 
     274             : void
     275           0 : nsFontInflationData::ScanText()
     276             : {
     277           0 :   mTextDirty = false;
     278           0 :   mTextAmount = 0;
     279           0 :   ScanTextIn(mBFCFrame);
     280           0 :   mInflationEnabled = mTextAmount >= mTextThreshold;
     281           0 : }
     282             : 
     283             : static uint32_t
     284           0 : DoCharCountOfLargestOption(nsIFrame *aContainer)
     285             : {
     286           0 :   uint32_t result = 0;
     287           0 :   for (nsIFrame* option : aContainer->PrincipalChildList()) {
     288             :     uint32_t optionResult;
     289           0 :     if (option->GetContent()->IsHTMLElement(nsGkAtoms::optgroup)) {
     290           0 :       optionResult = DoCharCountOfLargestOption(option);
     291             :     } else {
     292             :       // REVIEW: Check the frame structure for this!
     293           0 :       optionResult = 0;
     294           0 :       for (nsIFrame* optionChild : option->PrincipalChildList()) {
     295           0 :         if (optionChild->IsTextFrame()) {
     296           0 :           optionResult += nsTextFrameUtils::
     297           0 :             ComputeApproximateLengthWithWhitespaceCompression(
     298             :               optionChild->GetContent(), optionChild->StyleText());
     299             :         }
     300             :       }
     301             :     }
     302           0 :     if (optionResult > result) {
     303           0 :       result = optionResult;
     304             :     }
     305             :   }
     306           0 :   return result;
     307             : }
     308             : 
     309             : static uint32_t
     310           0 : CharCountOfLargestOption(nsIFrame *aListControlFrame)
     311             : {
     312             :   return DoCharCountOfLargestOption(
     313           0 :     static_cast<nsListControlFrame*>(aListControlFrame)->GetOptionsContainer());
     314             : }
     315             : 
     316             : void
     317           0 : nsFontInflationData::ScanTextIn(nsIFrame *aFrame)
     318             : {
     319             :   // NOTE: This function has a similar structure to FindEdgeInflatableFrameIn!
     320             : 
     321             :   // FIXME: Should probably only scan the text that's actually going to
     322             :   // be inflated!
     323             : 
     324           0 :   nsIFrame::ChildListIterator lists(aFrame);
     325           0 :   for (; !lists.IsDone(); lists.Next()) {
     326           0 :     nsFrameList::Enumerator kids(lists.CurrentList());
     327           0 :     for (; !kids.AtEnd(); kids.Next()) {
     328           0 :       nsIFrame *kid = kids.get();
     329           0 :       if (kid->GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT) {
     330             :         // Goes in a different set of inflation data.
     331           0 :         continue;
     332             :       }
     333             : 
     334           0 :       LayoutFrameType fType = kid->Type();
     335           0 :       if (fType == LayoutFrameType::Text) {
     336           0 :         nsIContent *content = kid->GetContent();
     337           0 :         if (content && kid == content->GetPrimaryFrame()) {
     338             :           uint32_t len = nsTextFrameUtils::
     339           0 :             ComputeApproximateLengthWithWhitespaceCompression(
     340           0 :               content, kid->StyleText());
     341           0 :           if (len != 0) {
     342           0 :             nscoord fontSize = kid->StyleFont()->mFont.size;
     343           0 :             if (fontSize > 0) {
     344           0 :               mTextAmount += fontSize * len;
     345             :             }
     346             :           }
     347             :         }
     348           0 :       } else if (fType == LayoutFrameType::TextInput) {
     349             :         // We don't want changes to the amount of text in a text input
     350             :         // to change what we count towards inflation.
     351           0 :         nscoord fontSize = kid->StyleFont()->mFont.size;
     352           0 :         int32_t charCount = static_cast<nsTextControlFrame*>(kid)->GetCols();
     353           0 :         mTextAmount += charCount * fontSize;
     354           0 :       } else if (fType == LayoutFrameType::ComboboxControl) {
     355             :         // See textInputFrame above (with s/amount of text/selected option/).
     356             :         // Don't just recurse down to the list control inside, since we
     357             :         // need to exclude the display frame.
     358           0 :         nscoord fontSize = kid->StyleFont()->mFont.size;
     359           0 :         int32_t charCount = CharCountOfLargestOption(
     360           0 :           static_cast<nsComboboxControlFrame*>(kid)->GetDropDown());
     361           0 :         mTextAmount += charCount * fontSize;
     362           0 :       } else if (fType == LayoutFrameType::ListControl) {
     363             :         // See textInputFrame above (with s/amount of text/selected option/).
     364           0 :         nscoord fontSize = kid->StyleFont()->mFont.size;
     365           0 :         int32_t charCount = CharCountOfLargestOption(kid);
     366           0 :         mTextAmount += charCount * fontSize;
     367             :       } else {
     368             :         // recursive step
     369           0 :         ScanTextIn(kid);
     370             :       }
     371             : 
     372           0 :       if (mTextAmount >= mTextThreshold) {
     373           0 :         return;
     374             :       }
     375             :     }
     376             :   }
     377             : }

Generated by: LCOV version 1.13