layout/generic/nsFontInflationData.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial