Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/layout/base/RestyleManager.cpp
Line
Count
Source (jump to first uncovered line)
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/. */
6
7
#include "mozilla/RestyleManager.h"
8
9
#include "mozilla/AutoRestyleTimelineMarker.h"
10
#include "mozilla/AutoTimelineMarker.h"
11
#include "mozilla/ComputedStyle.h"
12
#include "mozilla/ComputedStyleInlines.h"
13
#include "mozilla/DocumentStyleRootIterator.h"
14
#include "mozilla/LayerAnimationInfo.h"
15
#include "mozilla/ServoBindings.h"
16
#include "mozilla/ServoStyleSetInlines.h"
17
#include "mozilla/Unused.h"
18
#include "mozilla/ViewportFrame.h"
19
#include "mozilla/dom/ChildIterator.h"
20
#include "mozilla/dom/ElementInlines.h"
21
#include "mozilla/dom/HTMLBodyElement.h"
22
23
#include "Layers.h"
24
#include "LayerAnimationInfo.h" // For LayerAnimationInfo::sRecords
25
#include "nsAnimationManager.h"
26
#include "nsBlockFrame.h"
27
#include "nsBulletFrame.h"
28
#include "nsContentUtils.h"
29
#include "nsCSSFrameConstructor.h"
30
#include "nsCSSRendering.h"
31
#include "nsIDocumentInlines.h"
32
#include "nsIFrame.h"
33
#include "nsIFrameInlines.h"
34
#include "nsImageFrame.h"
35
#include "nsIPresShellInlines.h"
36
#include "nsPlaceholderFrame.h"
37
#include "nsPrintfCString.h"
38
#include "nsRefreshDriver.h"
39
#include "nsStyleChangeList.h"
40
#include "nsStyleUtil.h"
41
#include "nsTransitionManager.h"
42
#include "StickyScrollContainer.h"
43
#include "mozilla/EffectSet.h"
44
#include "mozilla/IntegerRange.h"
45
#include "mozilla/ViewportFrame.h"
46
#include "SVGObserverUtils.h"
47
#include "SVGTextFrame.h"
48
#include "ActiveLayerTracker.h"
49
#include "nsSVGIntegrationUtils.h"
50
51
#ifdef ACCESSIBILITY
52
#include "nsAccessibilityService.h"
53
#endif
54
55
using namespace mozilla::dom;
56
57
namespace mozilla {
58
59
RestyleManager::RestyleManager(nsPresContext* aPresContext)
60
  : mPresContext(aPresContext)
61
  , mRestyleGeneration(1)
62
  , mUndisplayedRestyleGeneration(1)
63
  , mInStyleRefresh(false)
64
  , mAnimationGeneration(0)
65
0
{
66
0
  MOZ_ASSERT(mPresContext);
67
0
}
68
69
void
70
RestyleManager::ContentInserted(nsIContent* aChild)
71
0
{
72
0
  MOZ_ASSERT(aChild->GetParentNode());
73
0
  RestyleForInsertOrChange(aChild);
74
0
}
75
76
void
77
RestyleManager::ContentAppended(nsIContent* aFirstNewContent)
78
0
{
79
0
  MOZ_ASSERT(aFirstNewContent->GetParent());
80
0
81
0
  // The container cannot be a document, but might be a ShadowRoot.
82
0
  if (!aFirstNewContent->GetParentNode()->IsElement()) {
83
0
    return;
84
0
  }
85
0
  Element* container = aFirstNewContent->GetParentNode()->AsElement();
86
0
87
#ifdef DEBUG
88
  {
89
    for (nsIContent* cur = aFirstNewContent; cur; cur = cur->GetNextSibling()) {
90
      NS_ASSERTION(!cur->IsRootOfAnonymousSubtree(),
91
                   "anonymous nodes should not be in child lists");
92
    }
93
  }
94
#endif
95
  uint32_t selectorFlags =
96
0
    container->GetFlags() & (NODE_ALL_SELECTOR_FLAGS &
97
0
                             ~NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS);
98
0
  if (selectorFlags == 0)
99
0
    return;
100
0
101
0
  if (selectorFlags & NODE_HAS_EMPTY_SELECTOR) {
102
0
    // see whether we need to restyle the container
103
0
    bool wasEmpty = true; // :empty or :-moz-only-whitespace
104
0
    for (nsIContent* cur = container->GetFirstChild();
105
0
         cur != aFirstNewContent;
106
0
         cur = cur->GetNextSibling()) {
107
0
      // We don't know whether we're testing :empty or :-moz-only-whitespace,
108
0
      // so be conservative and assume :-moz-only-whitespace (i.e., make
109
0
      // IsSignificantChild less likely to be true, and thus make us more
110
0
      // likely to restyle).
111
0
      if (nsStyleUtil::IsSignificantChild(cur, false)) {
112
0
        wasEmpty = false;
113
0
        break;
114
0
      }
115
0
    }
116
0
    if (wasEmpty) {
117
0
      RestyleForEmptyChange(container);
118
0
      return;
119
0
    }
120
0
  }
121
0
122
0
  if (selectorFlags & NODE_HAS_SLOW_SELECTOR) {
123
0
    PostRestyleEvent(container, eRestyle_Subtree, nsChangeHint(0));
124
0
    // Restyling the container is the most we can do here, so we're done.
125
0
    return;
126
0
  }
127
0
128
0
  if (selectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) {
129
0
    // restyle the last element child before this node
130
0
    for (nsIContent* cur = aFirstNewContent->GetPreviousSibling();
131
0
         cur;
132
0
         cur = cur->GetPreviousSibling()) {
133
0
      if (cur->IsElement()) {
134
0
        PostRestyleEvent(cur->AsElement(), eRestyle_Subtree, nsChangeHint(0));
135
0
        break;
136
0
      }
137
0
    }
138
0
  }
139
0
}
140
141
void
142
RestyleManager::RestyleForEmptyChange(Element* aContainer)
143
0
{
144
0
  // In some cases (:empty + E, :empty ~ E), a change in the content of
145
0
  // an element requires restyling its parent's siblings.
146
0
  nsRestyleHint hint = eRestyle_Subtree;
147
0
  nsIContent* grandparent = aContainer->GetParent();
148
0
  if (grandparent &&
149
0
      (grandparent->GetFlags() & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS)) {
150
0
    hint = nsRestyleHint(hint | eRestyle_LaterSiblings);
151
0
  }
152
0
  PostRestyleEvent(aContainer, hint, nsChangeHint(0));
153
0
}
154
155
void
156
RestyleManager::MaybeRestyleForEdgeChildChange(Element* aContainer,
157
                                               nsIContent* aChangedChild)
158
0
{
159
0
  MOZ_ASSERT(aContainer->GetFlags() & NODE_HAS_EDGE_CHILD_SELECTOR);
160
0
  MOZ_ASSERT(aChangedChild->GetParent() == aContainer);
161
0
  // restyle the previously-first element child if it is after this node
162
0
  bool passedChild = false;
163
0
  for (nsIContent* content = aContainer->GetFirstChild();
164
0
       content;
165
0
       content = content->GetNextSibling()) {
166
0
    if (content == aChangedChild) {
167
0
      passedChild = true;
168
0
      continue;
169
0
    }
170
0
    if (content->IsElement()) {
171
0
      if (passedChild) {
172
0
        PostRestyleEvent(content->AsElement(), eRestyle_Subtree,
173
0
                         nsChangeHint(0));
174
0
      }
175
0
      break;
176
0
    }
177
0
  }
178
0
  // restyle the previously-last element child if it is before this node
179
0
  passedChild = false;
180
0
  for (nsIContent* content = aContainer->GetLastChild();
181
0
       content;
182
0
       content = content->GetPreviousSibling()) {
183
0
    if (content == aChangedChild) {
184
0
      passedChild = true;
185
0
      continue;
186
0
    }
187
0
    if (content->IsElement()) {
188
0
      if (passedChild) {
189
0
        PostRestyleEvent(content->AsElement(), eRestyle_Subtree,
190
0
                         nsChangeHint(0));
191
0
      }
192
0
      break;
193
0
    }
194
0
  }
195
0
}
196
197
// Needed since we can't use PostRestyleEvent on non-elements (with
198
// eRestyle_LaterSiblings or nsRestyleHint(eRestyle_Subtree |
199
// eRestyle_LaterSiblings) as appropriate).
200
static void
201
RestyleSiblingsStartingWith(RestyleManager* aRestyleManager,
202
                            nsIContent* aStartingSibling /* may be null */)
203
0
{
204
0
  for (nsIContent* sibling = aStartingSibling; sibling;
205
0
       sibling = sibling->GetNextSibling()) {
206
0
    if (sibling->IsElement()) {
207
0
      aRestyleManager->
208
0
        PostRestyleEvent(sibling->AsElement(),
209
0
                         nsRestyleHint(eRestyle_Subtree | eRestyle_LaterSiblings),
210
0
                         nsChangeHint(0));
211
0
      break;
212
0
    }
213
0
  }
214
0
}
215
216
template<typename CharT>
217
bool
218
WhitespaceOnly(const CharT* aBuffer, size_t aUpTo)
219
0
{
220
0
  for (auto index : IntegerRange(aUpTo)) {
221
0
    if (!dom::IsSpaceCharacter(aBuffer[index])) {
222
0
      return false;
223
0
    }
224
0
  }
225
0
  return true;
226
0
}
Unexecuted instantiation: bool mozilla::WhitespaceOnly<char16_t>(char16_t const*, unsigned long)
Unexecuted instantiation: bool mozilla::WhitespaceOnly<char>(char const*, unsigned long)
227
228
template<typename CharT>
229
bool
230
WhitespaceOnlyChangedOnAppend(const CharT* aBuffer,
231
                              size_t aOldLength,
232
                              size_t aNewLength)
233
0
{
234
0
  MOZ_ASSERT(aOldLength <= aNewLength);
235
0
  if (!WhitespaceOnly(aBuffer, aOldLength)) {
236
0
    // The old text was already not whitespace-only.
237
0
    return false;
238
0
  }
239
0
240
0
  return !WhitespaceOnly(aBuffer + aOldLength, aNewLength - aOldLength);
241
0
}
Unexecuted instantiation: bool mozilla::WhitespaceOnlyChangedOnAppend<char16_t>(char16_t const*, unsigned long, unsigned long)
Unexecuted instantiation: bool mozilla::WhitespaceOnlyChangedOnAppend<char>(char const*, unsigned long, unsigned long)
242
243
static bool
244
HasAnySignificantSibling(Element* aContainer, nsIContent* aChild)
245
0
{
246
0
  MOZ_ASSERT(aChild->GetParent() == aContainer);
247
0
  for (nsIContent* child = aContainer->GetFirstChild();
248
0
       child;
249
0
       child = child->GetNextSibling()) {
250
0
    if (child == aChild) {
251
0
      continue;
252
0
    }
253
0
    // We don't know whether we're testing :empty or :-moz-only-whitespace,
254
0
    // so be conservative and assume :-moz-only-whitespace (i.e., make
255
0
    // IsSignificantChild less likely to be true, and thus make us more
256
0
    // likely to restyle).
257
0
    if (nsStyleUtil::IsSignificantChild(child, false)) {
258
0
      return true;
259
0
    }
260
0
  }
261
0
262
0
  return false;
263
0
}
264
265
void
266
RestyleManager::CharacterDataChanged(nsIContent* aContent,
267
                                     const CharacterDataChangeInfo& aInfo)
268
0
{
269
0
  nsINode* parent = aContent->GetParentNode();
270
0
  MOZ_ASSERT(parent, "How were we notified of a stray node?");
271
0
272
0
  uint32_t slowSelectorFlags = parent->GetFlags() & NODE_ALL_SELECTOR_FLAGS;
273
0
  if (!(slowSelectorFlags & (NODE_HAS_EMPTY_SELECTOR |
274
0
                             NODE_HAS_EDGE_CHILD_SELECTOR))) {
275
0
    // Nothing to do, no other slow selector can change as a result of this.
276
0
    return;
277
0
  }
278
0
279
0
  if (!aContent->IsText()) {
280
0
    // Doesn't matter to styling (could be a processing instruction or a
281
0
    // comment), it can't change whether any selectors match or don't.
282
0
    return;
283
0
  }
284
0
285
0
286
0
  if (MOZ_UNLIKELY(!parent->IsElement())) {
287
0
    MOZ_ASSERT(parent->IsShadowRoot());
288
0
    return;
289
0
  }
290
0
291
0
  if (MOZ_UNLIKELY(aContent->IsRootOfAnonymousSubtree())) {
292
0
    // This is an anonymous node and thus isn't in child lists, so isn't taken
293
0
    // into account for selector matching the relevant selectors here.
294
0
    return;
295
0
  }
296
0
297
0
  // Handle appends specially since they're common and we can know both the old
298
0
  // and the new text exactly.
299
0
  //
300
0
  // TODO(emilio): This could be made much more general if :-moz-only-whitespace
301
0
  // / :-moz-first-node and :-moz-last-node didn't exist. In that case we only
302
0
  // need to know whether we went from empty to non-empty, and that's trivial to
303
0
  // know, with CharacterDataChangeInfo...
304
0
  if (!aInfo.mAppend) {
305
0
    // FIXME(emilio): This restyles unnecessarily if the text node is the only
306
0
    // child of the parent element. Fortunately, it's uncommon to have such
307
0
    // nodes and this not being an append.
308
0
    //
309
0
    // See the testcase in bug 1427625 for a test-case that triggers this.
310
0
    RestyleForInsertOrChange(aContent);
311
0
    return;
312
0
  }
313
0
314
0
  const nsTextFragment* text = aContent->GetText();
315
0
316
0
  const size_t oldLength = aInfo.mChangeStart;
317
0
  const size_t newLength = text->GetLength();
318
0
319
0
  const bool emptyChanged = !oldLength && newLength;
320
0
321
0
  const bool whitespaceOnlyChanged = text->Is2b()
322
0
    ? WhitespaceOnlyChangedOnAppend(text->Get2b(), oldLength, newLength)
323
0
    : WhitespaceOnlyChangedOnAppend(text->Get1b(), oldLength, newLength);
324
0
325
0
  if (!emptyChanged && !whitespaceOnlyChanged) {
326
0
    return;
327
0
  }
328
0
329
0
  if (slowSelectorFlags & NODE_HAS_EMPTY_SELECTOR) {
330
0
    if (!HasAnySignificantSibling(parent->AsElement(), aContent)) {
331
0
      // We used to be empty, restyle the parent.
332
0
      RestyleForEmptyChange(parent->AsElement());
333
0
      return;
334
0
    }
335
0
  }
336
0
337
0
  if (slowSelectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) {
338
0
    MaybeRestyleForEdgeChildChange(parent->AsElement(), aContent);
339
0
  }
340
0
}
341
342
// Restyling for a ContentInserted or CharacterDataChanged notification.
343
// This could be used for ContentRemoved as well if we got the
344
// notification before the removal happened (and sometimes
345
// CharacterDataChanged is more like a removal than an addition).
346
// The comments are written and variables are named in terms of it being
347
// a ContentInserted notification.
348
void
349
RestyleManager::RestyleForInsertOrChange(nsIContent* aChild)
350
0
{
351
0
  nsINode* parentNode = aChild->GetParentNode();
352
0
353
0
  MOZ_ASSERT(parentNode);
354
0
  // The container might be a document or a ShadowRoot.
355
0
  if (!parentNode->IsElement()) {
356
0
    return;
357
0
  }
358
0
  Element* container = parentNode->AsElement();
359
0
360
0
  NS_ASSERTION(!aChild->IsRootOfAnonymousSubtree(),
361
0
               "anonymous nodes should not be in child lists");
362
0
  uint32_t selectorFlags = container->GetFlags() & NODE_ALL_SELECTOR_FLAGS;
363
0
  if (selectorFlags == 0)
364
0
    return;
365
0
366
0
  if (selectorFlags & NODE_HAS_EMPTY_SELECTOR) {
367
0
    // See whether we need to restyle the container due to :empty /
368
0
    // :-moz-only-whitespace.
369
0
    const bool wasEmpty = !HasAnySignificantSibling(container, aChild);
370
0
    if (wasEmpty) {
371
0
      // FIXME(emilio): When coming from CharacterDataChanged this can restyle
372
0
      // unnecessarily. Also can restyle unnecessarily if aChild is not
373
0
      // significant anyway, though that's more unlikely.
374
0
      RestyleForEmptyChange(container);
375
0
      return;
376
0
    }
377
0
  }
378
0
379
0
  if (selectorFlags & NODE_HAS_SLOW_SELECTOR) {
380
0
    PostRestyleEvent(container, eRestyle_Subtree, nsChangeHint(0));
381
0
    // Restyling the container is the most we can do here, so we're done.
382
0
    return;
383
0
  }
384
0
385
0
  if (selectorFlags & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS) {
386
0
    // Restyle all later siblings.
387
0
    RestyleSiblingsStartingWith(this, aChild->GetNextSibling());
388
0
  }
389
0
390
0
  if (selectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) {
391
0
    MaybeRestyleForEdgeChildChange(container, aChild);
392
0
  }
393
0
}
394
395
void
396
RestyleManager::ContentRemoved(nsIContent* aOldChild,
397
                               nsIContent* aFollowingSibling)
398
0
{
399
0
  MOZ_ASSERT(aOldChild->GetParentNode());
400
0
401
0
  // Computed style data isn't useful for detached nodes, and we'll need to
402
0
  // recompute it anyway if we ever insert the nodes back into a document.
403
0
  if (aOldChild->IsElement()) {
404
0
    RestyleManager::ClearServoDataFromSubtree(aOldChild->AsElement());
405
0
  }
406
0
407
0
  // The container might be a document or a ShadowRoot.
408
0
  if (!aOldChild->GetParentNode()->IsElement()) {
409
0
    return;
410
0
  }
411
0
  Element* container = aOldChild->GetParentNode()->AsElement();
412
0
413
0
  if (aOldChild->IsRootOfAnonymousSubtree()) {
414
0
    // This should be an assert, but this is called incorrectly in
415
0
    // HTMLEditor::DeleteRefToAnonymousNode and the assertions were clogging
416
0
    // up the logs.  Make it an assert again when that's fixed.
417
0
    MOZ_ASSERT(aOldChild->GetProperty(nsGkAtoms::restylableAnonymousNode),
418
0
               "anonymous nodes should not be in child lists (bug 439258)");
419
0
  }
420
0
  uint32_t selectorFlags = container->GetFlags() & NODE_ALL_SELECTOR_FLAGS;
421
0
  if (selectorFlags == 0)
422
0
    return;
423
0
424
0
  if (selectorFlags & NODE_HAS_EMPTY_SELECTOR) {
425
0
    // see whether we need to restyle the container
426
0
    bool isEmpty = true; // :empty or :-moz-only-whitespace
427
0
    for (nsIContent* child = container->GetFirstChild();
428
0
         child;
429
0
         child = child->GetNextSibling()) {
430
0
      // We don't know whether we're testing :empty or :-moz-only-whitespace,
431
0
      // so be conservative and assume :-moz-only-whitespace (i.e., make
432
0
      // IsSignificantChild less likely to be true, and thus make us more
433
0
      // likely to restyle).
434
0
      if (nsStyleUtil::IsSignificantChild(child, false)) {
435
0
        isEmpty = false;
436
0
        break;
437
0
      }
438
0
    }
439
0
    if (isEmpty) {
440
0
      RestyleForEmptyChange(container);
441
0
      return;
442
0
    }
443
0
  }
444
0
445
0
  if (selectorFlags & NODE_HAS_SLOW_SELECTOR) {
446
0
    PostRestyleEvent(container, eRestyle_Subtree, nsChangeHint(0));
447
0
    // Restyling the container is the most we can do here, so we're done.
448
0
    return;
449
0
  }
450
0
451
0
  if (selectorFlags & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS) {
452
0
    // Restyle all later siblings.
453
0
    RestyleSiblingsStartingWith(this, aFollowingSibling);
454
0
  }
455
0
456
0
  if (selectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) {
457
0
    // restyle the now-first element child if it was after aOldChild
458
0
    bool reachedFollowingSibling = false;
459
0
    for (nsIContent* content = container->GetFirstChild();
460
0
         content;
461
0
         content = content->GetNextSibling()) {
462
0
      if (content == aFollowingSibling) {
463
0
        reachedFollowingSibling = true;
464
0
        // do NOT continue here; we might want to restyle this node
465
0
      }
466
0
      if (content->IsElement()) {
467
0
        if (reachedFollowingSibling) {
468
0
          PostRestyleEvent(content->AsElement(), eRestyle_Subtree,
469
0
                           nsChangeHint(0));
470
0
        }
471
0
        break;
472
0
      }
473
0
    }
474
0
    // restyle the now-last element child if it was before aOldChild
475
0
    reachedFollowingSibling = (aFollowingSibling == nullptr);
476
0
    for (nsIContent* content = container->GetLastChild();
477
0
         content;
478
0
         content = content->GetPreviousSibling()) {
479
0
      if (content->IsElement()) {
480
0
        if (reachedFollowingSibling) {
481
0
          PostRestyleEvent(content->AsElement(), eRestyle_Subtree, nsChangeHint(0));
482
0
        }
483
0
        break;
484
0
      }
485
0
      if (content == aFollowingSibling) {
486
0
        reachedFollowingSibling = true;
487
0
      }
488
0
    }
489
0
  }
490
0
}
491
492
/**
493
 * Calculates the change hint and the restyle hint for a given content state
494
 * change.
495
 *
496
 * This is called from both Restyle managers.
497
 */
498
void
499
RestyleManager::ContentStateChangedInternal(const Element& aElement,
500
                                            EventStates aStateMask,
501
                                            nsChangeHint* aOutChangeHint)
502
0
{
503
0
  MOZ_ASSERT(!mInStyleRefresh);
504
0
  MOZ_ASSERT(aOutChangeHint);
505
0
506
0
  *aOutChangeHint = nsChangeHint(0);
507
0
  // Any change to a content state that affects which frames we construct
508
0
  // must lead to a frame reconstruct here if we already have a frame.
509
0
  // Note that we never decide through non-CSS means to not create frames
510
0
  // based on content states, so if we already don't have a frame we don't
511
0
  // need to force a reframe -- if it's needed, the HasStateDependentStyle
512
0
  // call will handle things.
513
0
  nsIFrame* primaryFrame = aElement.GetPrimaryFrame();
514
0
  if (primaryFrame) {
515
0
    // If it's generated content, ignore LOADING/etc state changes on it.
516
0
    if (!primaryFrame->IsGeneratedContentFrame() &&
517
0
        aStateMask.HasAtLeastOneOfStates(NS_EVENT_STATE_BROKEN |
518
0
                                         NS_EVENT_STATE_USERDISABLED |
519
0
                                         NS_EVENT_STATE_SUPPRESSED |
520
0
                                         NS_EVENT_STATE_LOADING)) {
521
0
      *aOutChangeHint = nsChangeHint_ReconstructFrame;
522
0
    } else {
523
0
      auto* disp = primaryFrame->StyleDisplay();
524
0
      if (disp->HasAppearance()) {
525
0
        nsITheme* theme = PresContext()->GetTheme();
526
0
        if (theme &&
527
0
            theme->ThemeSupportsWidget(PresContext(), primaryFrame,
528
0
                                       disp->mAppearance)) {
529
0
          bool repaint = false;
530
0
          theme->WidgetStateChanged(primaryFrame, disp->mAppearance, nullptr,
531
0
                                    &repaint, nullptr);
532
0
          if (repaint) {
533
0
            *aOutChangeHint |= nsChangeHint_RepaintFrame;
534
0
          }
535
0
        }
536
0
      }
537
0
    }
538
0
539
0
    primaryFrame->ContentStatesChanged(aStateMask);
540
0
  }
541
0
542
0
  if (aStateMask.HasState(NS_EVENT_STATE_VISITED)) {
543
0
    // Exposing information to the page about whether the link is
544
0
    // visited or not isn't really something we can worry about here.
545
0
    // FIXME: We could probably do this a bit better.
546
0
    *aOutChangeHint |= nsChangeHint_RepaintFrame;
547
0
  }
548
0
}
549
550
/* static */ nsCString
551
RestyleManager::RestyleHintToString(nsRestyleHint aHint)
552
0
{
553
0
  nsCString result;
554
0
  bool any = false;
555
0
  const char* names[] = {
556
0
    "Self", "SomeDescendants", "Subtree", "LaterSiblings", "CSSTransitions",
557
0
    "CSSAnimations", "StyleAttribute", "StyleAttribute_Animations",
558
0
    "Force", "ForceDescendants"
559
0
  };
560
0
  uint32_t hint = aHint & ((1 << ArrayLength(names)) - 1);
561
0
  uint32_t rest = aHint & ~((1 << ArrayLength(names)) - 1);
562
0
  for (uint32_t i = 0; i < ArrayLength(names); i++) {
563
0
    if (hint & (1 << i)) {
564
0
      if (any) {
565
0
        result.AppendLiteral(" | ");
566
0
      }
567
0
      result.AppendPrintf("eRestyle_%s", names[i]);
568
0
      any = true;
569
0
    }
570
0
  }
571
0
  if (rest) {
572
0
    if (any) {
573
0
      result.AppendLiteral(" | ");
574
0
    }
575
0
    result.AppendPrintf("0x%0x", rest);
576
0
  } else {
577
0
    if (!any) {
578
0
      result.AppendLiteral("0");
579
0
    }
580
0
  }
581
0
  return result;
582
0
}
583
584
#ifdef DEBUG
585
/* static */ nsCString
586
RestyleManager::ChangeHintToString(nsChangeHint aHint)
587
{
588
  nsCString result;
589
  bool any = false;
590
  const char* names[] = {
591
    "RepaintFrame", "NeedReflow", "ClearAncestorIntrinsics",
592
    "ClearDescendantIntrinsics", "NeedDirtyReflow", "SyncFrameView",
593
    "UpdateCursor", "UpdateEffects", "UpdateOpacityLayer",
594
    "UpdateTransformLayer", "ReconstructFrame", "UpdateOverflow",
595
    "UpdateSubtreeOverflow", "UpdatePostTransformOverflow",
596
    "UpdateParentOverflow",
597
    "ChildrenOnlyTransform", "RecomputePosition", "UpdateContainingBlock",
598
    "BorderStyleNoneChange", "UpdateTextPath", "SchedulePaint",
599
    "NeutralChange", "InvalidateRenderingObservers",
600
    "ReflowChangesSizeOrPosition", "UpdateComputedBSize",
601
    "UpdateUsesOpacity", "UpdateBackgroundPosition",
602
    "AddOrRemoveTransform", "ScrollbarChange",
603
    "UpdateWidgetProperties", "UpdateTableCellSpans",
604
    "VisibilityChange"
605
  };
606
  static_assert(nsChangeHint_AllHints ==
607
                  static_cast<uint32_t>((1ull << ArrayLength(names)) - 1),
608
                "Name list doesn't match change hints.");
609
  uint32_t hint = aHint & static_cast<uint32_t>((1ull << ArrayLength(names)) - 1);
610
  uint32_t rest = aHint & ~static_cast<uint32_t>((1ull << ArrayLength(names)) - 1);
611
  if ((hint & NS_STYLE_HINT_REFLOW) == NS_STYLE_HINT_REFLOW) {
612
    result.AppendLiteral("NS_STYLE_HINT_REFLOW");
613
    hint = hint & ~NS_STYLE_HINT_REFLOW;
614
    any = true;
615
  } else if ((hint & nsChangeHint_AllReflowHints) == nsChangeHint_AllReflowHints) {
616
    result.AppendLiteral("nsChangeHint_AllReflowHints");
617
    hint = hint & ~nsChangeHint_AllReflowHints;
618
    any = true;
619
  } else if ((hint & NS_STYLE_HINT_VISUAL) == NS_STYLE_HINT_VISUAL) {
620
    result.AppendLiteral("NS_STYLE_HINT_VISUAL");
621
    hint = hint & ~NS_STYLE_HINT_VISUAL;
622
    any = true;
623
  }
624
  for (uint32_t i = 0; i < ArrayLength(names); i++) {
625
    if (hint & (1u << i)) {
626
      if (any) {
627
        result.AppendLiteral(" | ");
628
      }
629
      result.AppendPrintf("nsChangeHint_%s", names[i]);
630
      any = true;
631
    }
632
  }
633
  if (rest) {
634
    if (any) {
635
      result.AppendLiteral(" | ");
636
    }
637
    result.AppendPrintf("0x%0x", rest);
638
  } else {
639
    if (!any) {
640
      result.AppendLiteral("nsChangeHint(0)");
641
    }
642
  }
643
  return result;
644
}
645
#endif
646
647
/**
648
 * Frame construction helpers follow.
649
 */
650
#ifdef DEBUG
651
static bool gInApplyRenderingChangeToTree = false;
652
#endif
653
654
/**
655
 * Sync views on aFrame and all of aFrame's descendants (following placeholders),
656
 * if aChange has nsChangeHint_SyncFrameView.
657
 * Calls DoApplyRenderingChangeToTree on all aFrame's out-of-flow descendants
658
 * (following placeholders), if aChange has nsChangeHint_RepaintFrame.
659
 * aFrame should be some combination of nsChangeHint_SyncFrameView,
660
 * nsChangeHint_RepaintFrame, nsChangeHint_UpdateOpacityLayer and
661
 * nsChangeHint_SchedulePaint, nothing else.
662
*/
663
static void SyncViewsAndInvalidateDescendants(nsIFrame* aFrame,
664
                                              nsChangeHint aChange);
665
666
static void StyleChangeReflow(nsIFrame* aFrame, nsChangeHint aHint);
667
668
/**
669
 * To handle nsChangeHint_ChildrenOnlyTransform we must iterate over the child
670
 * frames of the SVG frame concerned. This helper function is used to find that
671
 * SVG frame when we encounter nsChangeHint_ChildrenOnlyTransform to ensure
672
 * that we iterate over the intended children, since sometimes we end up
673
 * handling that hint while processing hints for one of the SVG frame's
674
 * ancestor frames.
675
 *
676
 * The reason that we sometimes end up trying to process the hint for an
677
 * ancestor of the SVG frame that the hint is intended for is due to the way we
678
 * process restyle events. ApplyRenderingChangeToTree adjusts the frame from
679
 * the restyled element's principle frame to one of its ancestor frames based
680
 * on what nsCSSRendering::FindBackground returns, since the background style
681
 * may have been propagated up to an ancestor frame. Processing hints using an
682
 * ancestor frame is fine in general, but nsChangeHint_ChildrenOnlyTransform is
683
 * a special case since it is intended to update the children of a specific
684
 * frame.
685
 */
686
static nsIFrame*
687
GetFrameForChildrenOnlyTransformHint(nsIFrame* aFrame)
688
0
{
689
0
  if (aFrame->IsViewportFrame()) {
690
0
    // This happens if the root-<svg> is fixed positioned, in which case we
691
0
    // can't use aFrame->GetContent() to find the primary frame, since
692
0
    // GetContent() returns nullptr for ViewportFrame.
693
0
    aFrame = aFrame->PrincipalChildList().FirstChild();
694
0
  }
695
0
  // For an nsHTMLScrollFrame, this will get the SVG frame that has the
696
0
  // children-only transforms:
697
0
  aFrame = aFrame->GetContent()->GetPrimaryFrame();
698
0
  if (aFrame->IsSVGOuterSVGFrame()) {
699
0
    aFrame = aFrame->PrincipalChildList().FirstChild();
700
0
    MOZ_ASSERT(aFrame->IsSVGOuterSVGAnonChildFrame(),
701
0
               "Where is the nsSVGOuterSVGFrame's anon child??");
702
0
  }
703
0
  MOZ_ASSERT(aFrame->IsFrameOfType(nsIFrame::eSVG | nsIFrame::eSVGContainer),
704
0
             "Children-only transforms only expected on SVG frames");
705
0
  return aFrame;
706
0
}
707
708
// Returns true if this function managed to successfully move a frame, and
709
// false if it could not process the position change, and a reflow should
710
// be performed instead.
711
static bool
712
RecomputePosition(nsIFrame* aFrame)
713
0
{
714
0
  // Don't process position changes on table frames, since we already handle
715
0
  // the dynamic position change on the table wrapper frame, and the
716
0
  // reflow-based fallback code path also ignores positions on inner table
717
0
  // frames.
718
0
  if (aFrame->IsTableFrame()) {
719
0
    return true;
720
0
  }
721
0
722
0
  const nsStyleDisplay* display = aFrame->StyleDisplay();
723
0
  // Changes to the offsets of a non-positioned element can safely be ignored.
724
0
  if (display->mPosition == NS_STYLE_POSITION_STATIC) {
725
0
    return true;
726
0
  }
727
0
728
0
  // Don't process position changes on frames which have views or the ones which
729
0
  // have a view somewhere in their descendants, because the corresponding view
730
0
  // needs to be repositioned properly as well.
731
0
  if (aFrame->HasView() ||
732
0
      (aFrame->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW)) {
733
0
    StyleChangeReflow(aFrame, nsChangeHint_NeedReflow);
734
0
    return false;
735
0
  }
736
0
737
0
  // Flexbox and Grid layout supports CSS Align and the optimizations below
738
0
  // don't support that yet.
739
0
  if (aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
740
0
    nsIFrame* ph = aFrame->GetPlaceholderFrame();
741
0
    if (ph && ph->HasAnyStateBits(PLACEHOLDER_STATICPOS_NEEDS_CSSALIGN)) {
742
0
      StyleChangeReflow(aFrame, nsChangeHint_NeedReflow);
743
0
      return false;
744
0
    }
745
0
  }
746
0
747
0
  aFrame->SchedulePaint();
748
0
749
0
  // For relative positioning, we can simply update the frame rect
750
0
  if (display->IsRelativelyPositionedStyle()) {
751
0
    // Move the frame
752
0
    if (display->mPosition == NS_STYLE_POSITION_STICKY) {
753
0
      // Update sticky positioning for an entire element at once, starting with
754
0
      // the first continuation or ib-split sibling.
755
0
      // It's rare that the frame we already have isn't already the first
756
0
      // continuation or ib-split sibling, but it can happen when styles differ
757
0
      // across continuations such as ::first-line or ::first-letter, and in
758
0
      // those cases we will generally (but maybe not always) do the work twice.
759
0
      nsIFrame* firstContinuation =
760
0
        nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
761
0
762
0
      StickyScrollContainer::ComputeStickyOffsets(firstContinuation);
763
0
      StickyScrollContainer* ssc =
764
0
        StickyScrollContainer::GetStickyScrollContainerForFrame(
765
0
          firstContinuation);
766
0
      if (ssc) {
767
0
        ssc->PositionContinuations(firstContinuation);
768
0
      }
769
0
    } else {
770
0
      MOZ_ASSERT(NS_STYLE_POSITION_RELATIVE == display->mPosition,
771
0
                 "Unexpected type of positioning");
772
0
      for (nsIFrame* cont = aFrame; cont;
773
0
           cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
774
0
        nsIFrame* cb = cont->GetContainingBlock();
775
0
        nsMargin newOffsets;
776
0
        WritingMode wm = cb->GetWritingMode();
777
0
        const LogicalSize size(wm, cb->GetContentRectRelativeToSelf().Size());
778
0
779
0
        ReflowInput::ComputeRelativeOffsets(wm, cont, size, newOffsets);
780
0
        NS_ASSERTION(newOffsets.left == -newOffsets.right &&
781
0
                     newOffsets.top == -newOffsets.bottom,
782
0
                     "ComputeRelativeOffsets should return valid results");
783
0
784
0
        // ReflowInput::ApplyRelativePositioning would work here, but
785
0
        // since we've already checked mPosition and aren't changing the frame's
786
0
        // normal position, go ahead and add the offsets directly.
787
0
        // First, we need to ensure that the normal position is stored though.
788
0
        bool hasProperty;
789
0
        nsPoint normalPosition = cont->GetNormalPosition(&hasProperty);
790
0
        if (!hasProperty) {
791
0
          cont->AddProperty(nsIFrame::NormalPositionProperty(),
792
0
                            new nsPoint(normalPosition));
793
0
        }
794
0
        cont->SetPosition(normalPosition +
795
0
                          nsPoint(newOffsets.left, newOffsets.top));
796
0
      }
797
0
    }
798
0
799
0
    return true;
800
0
  }
801
0
802
0
  // For the absolute positioning case, set up a fake HTML reflow state for
803
0
  // the frame, and then get the offsets and size from it. If the frame's size
804
0
  // doesn't need to change, we can simply update the frame position. Otherwise
805
0
  // we fall back to a reflow.
806
0
  RefPtr<gfxContext> rc =
807
0
    aFrame->PresShell()->CreateReferenceRenderingContext();
808
0
809
0
  // Construct a bogus parent reflow state so that there's a usable
810
0
  // containing block reflow state.
811
0
  nsIFrame* parentFrame = aFrame->GetParent();
812
0
  WritingMode parentWM = parentFrame->GetWritingMode();
813
0
  WritingMode frameWM = aFrame->GetWritingMode();
814
0
  LogicalSize parentSize = parentFrame->GetLogicalSize();
815
0
816
0
  nsFrameState savedState = parentFrame->GetStateBits();
817
0
  ReflowInput parentReflowInput(aFrame->PresContext(), parentFrame, rc,
818
0
                                parentSize);
819
0
  parentFrame->RemoveStateBits(~nsFrameState(0));
820
0
  parentFrame->AddStateBits(savedState);
821
0
822
0
  // The bogus parent state here was created with no parent state of its own,
823
0
  // and therefore it won't have an mCBReflowInput set up.
824
0
  // But we may need one (for InitCBReflowInput in a child state), so let's
825
0
  // try to create one here for the cases where it will be needed.
826
0
  Maybe<ReflowInput> cbReflowInput;
827
0
  nsIFrame* cbFrame = parentFrame->GetContainingBlock();
828
0
  if (cbFrame && (aFrame->GetContainingBlock() != parentFrame ||
829
0
                  parentFrame->IsTableFrame())) {
830
0
    LogicalSize cbSize = cbFrame->GetLogicalSize();
831
0
    cbReflowInput.emplace(cbFrame->PresContext(), cbFrame, rc, cbSize);
832
0
    cbReflowInput->ComputedPhysicalMargin() = cbFrame->GetUsedMargin();
833
0
    cbReflowInput->ComputedPhysicalPadding() = cbFrame->GetUsedPadding();
834
0
    cbReflowInput->ComputedPhysicalBorderPadding() =
835
0
      cbFrame->GetUsedBorderAndPadding();
836
0
    parentReflowInput.mCBReflowInput = cbReflowInput.ptr();
837
0
  }
838
0
839
0
  NS_WARNING_ASSERTION(parentSize.ISize(parentWM) != NS_INTRINSICSIZE &&
840
0
                       parentSize.BSize(parentWM) != NS_INTRINSICSIZE,
841
0
                       "parentSize should be valid");
842
0
  parentReflowInput.SetComputedISize(std::max(parentSize.ISize(parentWM), 0));
843
0
  parentReflowInput.SetComputedBSize(std::max(parentSize.BSize(parentWM), 0));
844
0
  parentReflowInput.ComputedPhysicalMargin().SizeTo(0, 0, 0, 0);
845
0
846
0
  parentReflowInput.ComputedPhysicalPadding() = parentFrame->GetUsedPadding();
847
0
  parentReflowInput.ComputedPhysicalBorderPadding() =
848
0
    parentFrame->GetUsedBorderAndPadding();
849
0
  LogicalSize availSize = parentSize.ConvertTo(frameWM, parentWM);
850
0
  availSize.BSize(frameWM) = NS_INTRINSICSIZE;
851
0
852
0
  ViewportFrame* viewport = do_QueryFrame(parentFrame);
853
0
  nsSize cbSize = viewport ?
854
0
    viewport->AdjustReflowInputAsContainingBlock(&parentReflowInput).Size()
855
0
    : aFrame->GetContainingBlock()->GetSize();
856
0
  const nsMargin& parentBorder =
857
0
    parentReflowInput.mStyleBorder->GetComputedBorder();
858
0
  cbSize -= nsSize(parentBorder.LeftRight(), parentBorder.TopBottom());
859
0
  LogicalSize lcbSize(frameWM, cbSize);
860
0
  ReflowInput reflowInput(aFrame->PresContext(), parentReflowInput, aFrame,
861
0
                          availSize, &lcbSize);
862
0
  nsSize computedSize(reflowInput.ComputedWidth(),
863
0
                      reflowInput.ComputedHeight());
864
0
  computedSize.width += reflowInput.ComputedPhysicalBorderPadding().LeftRight();
865
0
  if (computedSize.height != NS_INTRINSICSIZE) {
866
0
    computedSize.height +=
867
0
      reflowInput.ComputedPhysicalBorderPadding().TopBottom();
868
0
  }
869
0
  nsSize size = aFrame->GetSize();
870
0
  // The RecomputePosition hint is not used if any offset changed between auto
871
0
  // and non-auto. If computedSize.height == NS_INTRINSICSIZE then the new
872
0
  // element height will be its intrinsic height, and since 'top' and 'bottom''s
873
0
  // auto-ness hasn't changed, the old height must also be its intrinsic
874
0
  // height, which we can assume hasn't changed (or reflow would have
875
0
  // been triggered).
876
0
  if (computedSize.width == size.width &&
877
0
      (computedSize.height == NS_INTRINSICSIZE || computedSize.height == size.height)) {
878
0
    // If we're solving for 'left' or 'top', then compute it here, in order to
879
0
    // match the reflow code path.
880
0
    if (NS_AUTOOFFSET == reflowInput.ComputedPhysicalOffsets().left) {
881
0
      reflowInput.ComputedPhysicalOffsets().left = cbSize.width -
882
0
                                          reflowInput.ComputedPhysicalOffsets().right -
883
0
                                          reflowInput.ComputedPhysicalMargin().right -
884
0
                                          size.width -
885
0
                                          reflowInput.ComputedPhysicalMargin().left;
886
0
    }
887
0
888
0
    if (NS_AUTOOFFSET == reflowInput.ComputedPhysicalOffsets().top) {
889
0
      reflowInput.ComputedPhysicalOffsets().top = cbSize.height -
890
0
                                         reflowInput.ComputedPhysicalOffsets().bottom -
891
0
                                         reflowInput.ComputedPhysicalMargin().bottom -
892
0
                                         size.height -
893
0
                                         reflowInput.ComputedPhysicalMargin().top;
894
0
    }
895
0
896
0
    // Move the frame
897
0
    nsPoint pos(parentBorder.left + reflowInput.ComputedPhysicalOffsets().left +
898
0
                reflowInput.ComputedPhysicalMargin().left,
899
0
                parentBorder.top + reflowInput.ComputedPhysicalOffsets().top +
900
0
                reflowInput.ComputedPhysicalMargin().top);
901
0
    aFrame->SetPosition(pos);
902
0
903
0
    return true;
904
0
  }
905
0
906
0
  // Fall back to a reflow
907
0
  StyleChangeReflow(aFrame, nsChangeHint_NeedReflow);
908
0
  return false;
909
0
}
910
911
static bool
912
HasBoxAncestor(nsIFrame* aFrame)
913
0
{
914
0
  for (nsIFrame* f = aFrame; f; f = f->GetParent()) {
915
0
    if (f->IsXULBoxFrame()) {
916
0
      return true;
917
0
    }
918
0
  }
919
0
  return false;
920
0
}
921
922
/**
923
 * Return true if aFrame's subtree has placeholders for out-of-flow content
924
 * whose 'position' style's bit in aPositionMask is set.
925
 */
926
static bool
927
FrameHasPositionedPlaceholderDescendants(nsIFrame* aFrame,
928
                                         uint32_t aPositionMask)
929
0
{
930
0
  MOZ_ASSERT(aPositionMask & (1 << NS_STYLE_POSITION_FIXED));
931
0
932
0
  for (nsIFrame::ChildListIterator lists(aFrame); !lists.IsDone(); lists.Next()) {
933
0
    for (nsIFrame* f : lists.CurrentList()) {
934
0
      if (f->IsPlaceholderFrame()) {
935
0
        nsIFrame* outOfFlow =
936
0
          nsPlaceholderFrame::GetRealFrameForPlaceholder(f);
937
0
        // If SVG text frames could appear here, they could confuse us since
938
0
        // they ignore their position style ... but they can't.
939
0
        NS_ASSERTION(!nsSVGUtils::IsInSVGTextSubtree(outOfFlow),
940
0
                     "SVG text frames can't be out of flow");
941
0
        if (aPositionMask & (1 << outOfFlow->StyleDisplay()->mPosition)) {
942
0
          return true;
943
0
        }
944
0
      }
945
0
      uint32_t positionMask = aPositionMask;
946
0
      // NOTE:  It's tempting to check f->IsAbsPosContainingBlock() or
947
0
      // f->IsFixedPosContainingBlock() here.  However, that would only
948
0
      // be testing the *new* style of the frame, which might exclude
949
0
      // descendants that currently have this frame as an abs-pos
950
0
      // containing block.  Taking the codepath where we don't reframe
951
0
      // could lead to an unsafe call to
952
0
      // cont->MarkAsNotAbsoluteContainingBlock() before we've reframed
953
0
      // the descendant and taken it off the absolute list.
954
0
      if (FrameHasPositionedPlaceholderDescendants(f, positionMask)) {
955
0
        return true;
956
0
      }
957
0
    }
958
0
  }
959
0
  return false;
960
0
}
961
962
static bool
963
NeedToReframeForAddingOrRemovingTransform(nsIFrame* aFrame)
964
0
{
965
0
  static_assert(0 <= NS_STYLE_POSITION_ABSOLUTE &&
966
0
                NS_STYLE_POSITION_ABSOLUTE < 32, "Style constant out of range");
967
0
  static_assert(0 <= NS_STYLE_POSITION_FIXED &&
968
0
                NS_STYLE_POSITION_FIXED < 32, "Style constant out of range");
969
0
970
0
  uint32_t positionMask;
971
0
  // Don't call aFrame->IsPositioned here, since that returns true if
972
0
  // the frame already has a transform, and we want to ignore that here
973
0
  if (aFrame->IsAbsolutelyPositioned() || aFrame->IsRelativelyPositioned()) {
974
0
    // This frame is a container for abs-pos descendants whether or not it
975
0
    // has a transform.
976
0
    // So abs-pos descendants are no problem; we only need to reframe if
977
0
    // we have fixed-pos descendants.
978
0
    positionMask = 1 << NS_STYLE_POSITION_FIXED;
979
0
  } else {
980
0
    // This frame may not be a container for abs-pos descendants already.
981
0
    // So reframe if we have abs-pos or fixed-pos descendants.
982
0
    positionMask =
983
0
      (1 << NS_STYLE_POSITION_FIXED) | (1 << NS_STYLE_POSITION_ABSOLUTE);
984
0
  }
985
0
  for (nsIFrame* f = aFrame; f;
986
0
       f = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(f)) {
987
0
    if (FrameHasPositionedPlaceholderDescendants(f, positionMask)) {
988
0
      return true;
989
0
    }
990
0
  }
991
0
  return false;
992
0
}
993
994
static void
995
DoApplyRenderingChangeToTree(nsIFrame* aFrame,
996
                             nsChangeHint aChange)
997
0
{
998
0
  MOZ_ASSERT(gInApplyRenderingChangeToTree,
999
0
             "should only be called within ApplyRenderingChangeToTree");
1000
0
1001
0
  for ( ; aFrame; aFrame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame)) {
1002
0
    // Invalidate and sync views on all descendant frames, following placeholders.
1003
0
    // We don't need to update transforms in SyncViewsAndInvalidateDescendants, because
1004
0
    // there can't be any out-of-flows or popups that need to be transformed;
1005
0
    // all out-of-flow descendants of the transformed element must also be
1006
0
    // descendants of the transformed frame.
1007
0
    SyncViewsAndInvalidateDescendants(aFrame,
1008
0
      nsChangeHint(aChange & (nsChangeHint_RepaintFrame |
1009
0
                              nsChangeHint_SyncFrameView |
1010
0
                              nsChangeHint_UpdateOpacityLayer |
1011
0
                              nsChangeHint_SchedulePaint)));
1012
0
    // This must be set to true if the rendering change needs to
1013
0
    // invalidate content.  If it's false, a composite-only paint
1014
0
    // (empty transaction) will be scheduled.
1015
0
    bool needInvalidatingPaint = false;
1016
0
1017
0
    // if frame has view, will already be invalidated
1018
0
    if (aChange & nsChangeHint_RepaintFrame) {
1019
0
      // Note that this whole block will be skipped when painting is suppressed
1020
0
      // (due to our caller ApplyRendingChangeToTree() discarding the
1021
0
      // nsChangeHint_RepaintFrame hint).  If you add handling for any other
1022
0
      // hints within this block, be sure that they too should be ignored when
1023
0
      // painting is suppressed.
1024
0
      needInvalidatingPaint = true;
1025
0
      aFrame->InvalidateFrameSubtree();
1026
0
      if ((aChange & nsChangeHint_UpdateEffects) &&
1027
0
          aFrame->IsFrameOfType(nsIFrame::eSVG) &&
1028
0
          !(aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG)) {
1029
0
        // Need to update our overflow rects:
1030
0
        nsSVGUtils::ScheduleReflowSVG(aFrame);
1031
0
      }
1032
0
1033
0
      ActiveLayerTracker::NotifyNeedsRepaint(aFrame);
1034
0
    }
1035
0
    if (aChange & nsChangeHint_UpdateTextPath) {
1036
0
      if (nsSVGUtils::IsInSVGTextSubtree(aFrame)) {
1037
0
        // Invalidate and reflow the entire SVGTextFrame:
1038
0
        NS_ASSERTION(aFrame->GetContent()->IsSVGElement(nsGkAtoms::textPath),
1039
0
                     "expected frame for a <textPath> element");
1040
0
        nsIFrame* text = nsLayoutUtils::GetClosestFrameOfType(
1041
0
          aFrame, LayoutFrameType::SVGText);
1042
0
        NS_ASSERTION(text, "expected to find an ancestor SVGTextFrame");
1043
0
        static_cast<SVGTextFrame*>(text)->NotifyGlyphMetricsChange();
1044
0
      } else {
1045
0
        MOZ_ASSERT(false, "unexpected frame got nsChangeHint_UpdateTextPath");
1046
0
      }
1047
0
    }
1048
0
    if (aChange & nsChangeHint_UpdateOpacityLayer) {
1049
0
      // FIXME/bug 796697: we can get away with empty transactions for
1050
0
      // opacity updates in many cases.
1051
0
      needInvalidatingPaint = true;
1052
0
1053
0
      ActiveLayerTracker::NotifyRestyle(aFrame, eCSSProperty_opacity);
1054
0
      if (nsSVGIntegrationUtils::UsingEffectsForFrame(aFrame)) {
1055
0
        // SVG effects paints the opacity without using
1056
0
        // nsDisplayOpacity. We need to invalidate manually.
1057
0
        aFrame->InvalidateFrameSubtree();
1058
0
      }
1059
0
    }
1060
0
    if ((aChange & nsChangeHint_UpdateTransformLayer) &&
1061
0
        aFrame->IsTransformed()) {
1062
0
      ActiveLayerTracker::NotifyRestyle(aFrame, eCSSProperty_transform);
1063
0
      // If we're not already going to do an invalidating paint, see
1064
0
      // if we can get away with only updating the transform on a
1065
0
      // layer for this frame, and not scheduling an invalidating
1066
0
      // paint.
1067
0
      if (!needInvalidatingPaint) {
1068
0
        nsDisplayItem::Layer* layer;
1069
0
        needInvalidatingPaint |= !aFrame->TryUpdateTransformOnly(&layer);
1070
0
1071
0
        if (!needInvalidatingPaint) {
1072
0
          // Since we're not going to paint, we need to resend animation
1073
0
          // data to the layer.
1074
0
          MOZ_ASSERT(layer, "this can't happen if there's no layer");
1075
0
          nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(
1076
0
            layer, nullptr, nullptr, aFrame, eCSSProperty_transform);
1077
0
        }
1078
0
      }
1079
0
    }
1080
0
    if (aChange & nsChangeHint_ChildrenOnlyTransform) {
1081
0
      needInvalidatingPaint = true;
1082
0
      nsIFrame* childFrame =
1083
0
        GetFrameForChildrenOnlyTransformHint(aFrame)->PrincipalChildList().FirstChild();
1084
0
      for ( ; childFrame; childFrame = childFrame->GetNextSibling()) {
1085
0
        ActiveLayerTracker::NotifyRestyle(childFrame, eCSSProperty_transform);
1086
0
      }
1087
0
    }
1088
0
    if (aChange & nsChangeHint_SchedulePaint) {
1089
0
      needInvalidatingPaint = true;
1090
0
    }
1091
0
    aFrame->SchedulePaint(needInvalidatingPaint
1092
0
                            ? nsIFrame::PAINT_DEFAULT
1093
0
                            : nsIFrame::PAINT_COMPOSITE_ONLY);
1094
0
  }
1095
0
}
1096
1097
static void
1098
SyncViewsAndInvalidateDescendants(nsIFrame* aFrame, nsChangeHint aChange)
1099
0
{
1100
0
  MOZ_ASSERT(gInApplyRenderingChangeToTree,
1101
0
             "should only be called within ApplyRenderingChangeToTree");
1102
0
1103
0
  NS_ASSERTION(nsChangeHint_size_t(aChange) ==
1104
0
                          (aChange & (nsChangeHint_RepaintFrame |
1105
0
                                      nsChangeHint_SyncFrameView |
1106
0
                                      nsChangeHint_UpdateOpacityLayer |
1107
0
                                      nsChangeHint_SchedulePaint)),
1108
0
               "Invalid change flag");
1109
0
1110
0
  if (aChange & nsChangeHint_SyncFrameView) {
1111
0
    aFrame->SyncFrameViewProperties();
1112
0
  }
1113
0
1114
0
  nsIFrame::ChildListIterator lists(aFrame);
1115
0
  for (; !lists.IsDone(); lists.Next()) {
1116
0
    for (nsIFrame* child : lists.CurrentList()) {
1117
0
      if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
1118
0
        // only do frames that don't have placeholders
1119
0
        if (child->IsPlaceholderFrame()) {
1120
0
          // do the out-of-flow frame and its continuations
1121
0
          nsIFrame* outOfFlowFrame =
1122
0
            nsPlaceholderFrame::GetRealFrameForPlaceholder(child);
1123
0
          DoApplyRenderingChangeToTree(outOfFlowFrame, aChange);
1124
0
        } else if (lists.CurrentID() == nsIFrame::kPopupList) {
1125
0
          DoApplyRenderingChangeToTree(child, aChange);
1126
0
        } else { // regular frame
1127
0
          SyncViewsAndInvalidateDescendants(child, aChange);
1128
0
        }
1129
0
      }
1130
0
    }
1131
0
  }
1132
0
}
1133
1134
static bool
1135
IsPrimaryFrameOfRootOrBodyElement(nsIFrame* aFrame)
1136
0
{
1137
0
  nsIContent* content = aFrame->GetContent();
1138
0
  if (!content) {
1139
0
    return false;
1140
0
  }
1141
0
1142
0
  nsIDocument* document = content->OwnerDoc();
1143
0
  Element* root = document->GetRootElement();
1144
0
  if (!root) {
1145
0
    return false;
1146
0
  }
1147
0
  nsIFrame* rootFrame = root->GetPrimaryFrame();
1148
0
  if (!rootFrame) {
1149
0
    return false;
1150
0
  }
1151
0
  if (aFrame == rootFrame) {
1152
0
    return true;
1153
0
  }
1154
0
1155
0
  Element* body = document->GetBodyElement();
1156
0
  if (!body) {
1157
0
    return false;
1158
0
  }
1159
0
  nsIFrame* bodyFrame = body->GetPrimaryFrame();
1160
0
  if (!bodyFrame) {
1161
0
    return false;
1162
0
  }
1163
0
  if (aFrame == bodyFrame) {
1164
0
    return true;
1165
0
  }
1166
0
1167
0
  return false;
1168
0
}
1169
1170
static void
1171
ApplyRenderingChangeToTree(nsIPresShell* aPresShell,
1172
                           nsIFrame* aFrame,
1173
                           nsChangeHint aChange)
1174
0
{
1175
0
  // We check StyleDisplay()->HasTransformStyle() in addition to checking
1176
0
  // IsTransformed() since we can get here for some frames that don't support
1177
0
  // CSS transforms.
1178
0
  NS_ASSERTION(!(aChange & nsChangeHint_UpdateTransformLayer) ||
1179
0
               aFrame->IsTransformed() ||
1180
0
               aFrame->StyleDisplay()->HasTransformStyle(),
1181
0
               "Unexpected UpdateTransformLayer hint");
1182
0
1183
0
  if (aPresShell->IsPaintingSuppressed()) {
1184
0
    // Don't allow synchronous rendering changes when painting is turned off.
1185
0
    aChange &= ~nsChangeHint_RepaintFrame;
1186
0
    if (!aChange) {
1187
0
      return;
1188
0
    }
1189
0
  }
1190
0
1191
0
// Trigger rendering updates by damaging this frame and any
1192
0
// continuations of this frame.
1193
#ifdef DEBUG
1194
  gInApplyRenderingChangeToTree = true;
1195
#endif
1196
0
  if (aChange & nsChangeHint_RepaintFrame) {
1197
0
    // If the frame is the primary frame of either the body element or
1198
0
    // the html element, we propagate the repaint change hint to the
1199
0
    // viewport. This is necessary for background and scrollbar colors
1200
0
    // propagation.
1201
0
    if (IsPrimaryFrameOfRootOrBodyElement(aFrame)) {
1202
0
      nsIFrame* rootFrame = aFrame->
1203
0
        PresShell()->FrameConstructor()->GetRootFrame();
1204
0
      MOZ_ASSERT(rootFrame, "No root frame?");
1205
0
      DoApplyRenderingChangeToTree(rootFrame, nsChangeHint_RepaintFrame);
1206
0
      aChange &= ~nsChangeHint_RepaintFrame;
1207
0
      if (!aChange) {
1208
0
        return;
1209
0
      }
1210
0
    }
1211
0
  }
1212
0
  DoApplyRenderingChangeToTree(aFrame, aChange);
1213
#ifdef DEBUG
1214
  gInApplyRenderingChangeToTree = false;
1215
#endif
1216
}
1217
1218
static void
1219
AddSubtreeToOverflowTracker(nsIFrame* aFrame,
1220
                            OverflowChangedTracker& aOverflowChangedTracker)
1221
0
{
1222
0
  if (aFrame->FrameMaintainsOverflow()) {
1223
0
    aOverflowChangedTracker.AddFrame(aFrame,
1224
0
                                     OverflowChangedTracker::CHILDREN_CHANGED);
1225
0
  }
1226
0
  nsIFrame::ChildListIterator lists(aFrame);
1227
0
  for (; !lists.IsDone(); lists.Next()) {
1228
0
    for (nsIFrame* child : lists.CurrentList()) {
1229
0
      AddSubtreeToOverflowTracker(child, aOverflowChangedTracker);
1230
0
    }
1231
0
  }
1232
0
}
1233
1234
static void
1235
StyleChangeReflow(nsIFrame* aFrame, nsChangeHint aHint)
1236
0
{
1237
0
  nsIPresShell::IntrinsicDirty dirtyType;
1238
0
  if (aHint & nsChangeHint_ClearDescendantIntrinsics) {
1239
0
    NS_ASSERTION(aHint & nsChangeHint_ClearAncestorIntrinsics,
1240
0
                 "Please read the comments in nsChangeHint.h");
1241
0
    NS_ASSERTION(aHint & nsChangeHint_NeedDirtyReflow,
1242
0
                 "ClearDescendantIntrinsics requires NeedDirtyReflow");
1243
0
    dirtyType = nsIPresShell::eStyleChange;
1244
0
  } else if ((aHint & nsChangeHint_UpdateComputedBSize) &&
1245
0
             aFrame->HasAnyStateBits(
1246
0
               NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE)) {
1247
0
    dirtyType = nsIPresShell::eStyleChange;
1248
0
  } else if (aHint & nsChangeHint_ClearAncestorIntrinsics) {
1249
0
    dirtyType = nsIPresShell::eTreeChange;
1250
0
  } else if ((aHint & nsChangeHint_UpdateComputedBSize) &&
1251
0
             HasBoxAncestor(aFrame)) {
1252
0
    // The frame's computed BSize is changing, and we have a box ancestor
1253
0
    // whose cached intrinsic height may need to be updated.
1254
0
    dirtyType = nsIPresShell::eTreeChange;
1255
0
  } else {
1256
0
    dirtyType = nsIPresShell::eResize;
1257
0
  }
1258
0
1259
0
  nsFrameState dirtyBits;
1260
0
  if (aFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) {
1261
0
    dirtyBits = nsFrameState(0);
1262
0
  } else if ((aHint & nsChangeHint_NeedDirtyReflow) ||
1263
0
             dirtyType == nsIPresShell::eStyleChange) {
1264
0
    dirtyBits = NS_FRAME_IS_DIRTY;
1265
0
  } else {
1266
0
    dirtyBits = NS_FRAME_HAS_DIRTY_CHILDREN;
1267
0
  }
1268
0
1269
0
  // If we're not going to clear any intrinsic sizes on the frames, and
1270
0
  // there are no dirty bits to set, then there's nothing to do.
1271
0
  if (dirtyType == nsIPresShell::eResize && !dirtyBits)
1272
0
    return;
1273
0
1274
0
  nsIPresShell::ReflowRootHandling rootHandling;
1275
0
  if (aHint & nsChangeHint_ReflowChangesSizeOrPosition) {
1276
0
    rootHandling = nsIPresShell::ePositionOrSizeChange;
1277
0
  } else {
1278
0
    rootHandling = nsIPresShell::eNoPositionOrSizeChange;
1279
0
  }
1280
0
1281
0
  do {
1282
0
    aFrame->PresShell()->FrameNeedsReflow(
1283
0
      aFrame, dirtyType, dirtyBits, rootHandling);
1284
0
    aFrame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame);
1285
0
  } while (aFrame);
1286
0
}
1287
1288
// Get the next sibling which might have a frame.  This only considers siblings
1289
// that stylo post-traversal looks at, so only elements and text.  In
1290
// particular, it ignores comments.
1291
static nsIContent*
1292
NextSiblingWhichMayHaveFrame(nsIContent* aContent)
1293
0
{
1294
0
  for (nsIContent* next = aContent->GetNextSibling();
1295
0
       next;
1296
0
       next = next->GetNextSibling()) {
1297
0
    if (next->IsElement() || next->IsText()) {
1298
0
      return next;
1299
0
    }
1300
0
  }
1301
0
1302
0
  return nullptr;
1303
0
}
1304
1305
void
1306
RestyleManager::ProcessRestyledFrames(nsStyleChangeList& aChangeList)
1307
0
{
1308
0
  NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
1309
0
               "Someone forgot a script blocker");
1310
0
1311
0
  // See bug 1378219 comment 9:
1312
0
  // Recursive calls here are a bit worrying, but apparently do happen in the
1313
0
  // wild (although not currently in any of our automated tests). Try to get a
1314
0
  // stack from Nightly/Dev channel to figure out what's going on and whether
1315
0
  // it's OK.
1316
0
  MOZ_DIAGNOSTIC_ASSERT(!mDestroyedFrames, "ProcessRestyledFrames recursion");
1317
0
1318
0
  if (aChangeList.IsEmpty()) {
1319
0
    return;
1320
0
  }
1321
0
1322
0
  // If mDestroyedFrames is null, we want to create a new hashtable here
1323
0
  // and destroy it on exit; but if it is already non-null (because we're in
1324
0
  // a recursive call), we will continue to use the existing table to
1325
0
  // accumulate destroyed frames, and NOT clear mDestroyedFrames on exit.
1326
0
  // We use a MaybeClearDestroyedFrames helper to conditionally reset the
1327
0
  // mDestroyedFrames pointer when this method returns.
1328
0
  typedef decltype(mDestroyedFrames) DestroyedFramesT;
1329
0
  class MOZ_RAII MaybeClearDestroyedFrames
1330
0
  {
1331
0
  private:
1332
0
    DestroyedFramesT& mDestroyedFramesRef; // ref to caller's mDestroyedFrames
1333
0
    const bool        mResetOnDestruction;
1334
0
  public:
1335
0
    explicit MaybeClearDestroyedFrames(DestroyedFramesT& aTarget)
1336
0
      : mDestroyedFramesRef(aTarget)
1337
0
      , mResetOnDestruction(!aTarget) // reset only if target starts out null
1338
0
    {
1339
0
    }
1340
0
    ~MaybeClearDestroyedFrames()
1341
0
    {
1342
0
      if (mResetOnDestruction) {
1343
0
        mDestroyedFramesRef.reset(nullptr);
1344
0
      }
1345
0
    }
1346
0
  };
1347
0
1348
0
  MaybeClearDestroyedFrames maybeClear(mDestroyedFrames);
1349
0
  if (!mDestroyedFrames) {
1350
0
    mDestroyedFrames = MakeUnique<nsTHashtable<nsPtrHashKey<const nsIFrame>>>();
1351
0
  }
1352
0
1353
0
  AUTO_PROFILER_LABEL("RestyleManager::ProcessRestyledFrames", LAYOUT);
1354
0
1355
0
  nsPresContext* presContext = PresContext();
1356
0
  nsCSSFrameConstructor* frameConstructor = presContext->FrameConstructor();
1357
0
1358
0
  // Handle nsChangeHint_ScrollbarChange, by either updating the
1359
0
  // scrollbars on the viewport, or upgrading the change hint to frame-reconstruct.
1360
0
  for (nsStyleChangeData& data : aChangeList) {
1361
0
    if (data.mHint & nsChangeHint_ScrollbarChange) {
1362
0
      data.mHint &= ~nsChangeHint_ScrollbarChange;
1363
0
      bool doReconstruct = true; // assume the worst
1364
0
1365
0
      // Only bother with this if we're html/body, since:
1366
0
      //  (a) It'd be *expensive* to reframe these particular nodes.  They're
1367
0
      //      at the root, so reframing would mean rebuilding the world.
1368
0
      //  (b) It's often *unnecessary* to reframe for "overflow" changes on
1369
0
      //      these particular nodes.  In general, the only reason we reframe
1370
0
      //      for "overflow" changes is so we can construct (or destroy) a
1371
0
      //      scrollframe & scrollbars -- and the html/body nodes often don't
1372
0
      //      need their own scrollframe/scrollbars because they coopt the ones
1373
0
      //      on the viewport (which always exist). So depending on whether
1374
0
      //      that's happening, we can skip the reframe for these nodes.
1375
0
      if (data.mContent->IsAnyOfHTMLElements(nsGkAtoms::body,
1376
0
                                             nsGkAtoms::html)) {
1377
0
        // If the restyled element provided/provides the scrollbar styles for
1378
0
        // the viewport before and/or after this restyle, AND it's not coopting
1379
0
        // that responsibility from some other element (which would need
1380
0
        // reconstruction to make its own scrollframe now), THEN: we don't need
1381
0
        // to reconstruct - we can just reflow, because no scrollframe is being
1382
0
        // added/removed.
1383
0
        nsIContent* prevOverrideNode =
1384
0
          presContext->GetViewportScrollStylesOverrideElement();
1385
0
        nsIContent* newOverrideNode =
1386
0
          presContext->UpdateViewportScrollStylesOverride();
1387
0
1388
0
        if (data.mContent == prevOverrideNode ||
1389
0
            data.mContent == newOverrideNode) {
1390
0
          // If we get here, the restyled element provided the scrollbar styles
1391
0
          // for viewport before this restyle, OR it will provide them after.
1392
0
          if (!prevOverrideNode || !newOverrideNode ||
1393
0
              prevOverrideNode == newOverrideNode) {
1394
0
            // If we get here, the restyled element is NOT replacing (or being
1395
0
            // replaced by) some other element as the viewport's
1396
0
            // scrollbar-styles provider. (If it were, we'd potentially need to
1397
0
            // reframe to create a dedicated scrollframe for whichever element
1398
0
            // is being booted from providing viewport scrollbar styles.)
1399
0
            //
1400
0
            // Under these conditions, we're OK to assume that this "overflow"
1401
0
            // change only impacts the root viewport's scrollframe, which
1402
0
            // already exists, so we can simply reflow instead of reframing.
1403
0
            // When requesting this reflow, we send the exact same change hints
1404
0
            // that "width" and "height" would send (since conceptually,
1405
0
            // adding/removing scrollbars is like changing the available
1406
0
            // space).
1407
0
            data.mHint |= (nsChangeHint_ReflowHintsForISizeChange |
1408
0
                           nsChangeHint_ReflowHintsForBSizeChange);
1409
0
            doReconstruct = false;
1410
0
          }
1411
0
        }
1412
0
      }
1413
0
      if (doReconstruct) {
1414
0
        data.mHint |= nsChangeHint_ReconstructFrame;
1415
0
      }
1416
0
    }
1417
0
  }
1418
0
1419
0
  bool didUpdateCursor = false;
1420
0
1421
0
  for (size_t i = 0; i < aChangeList.Length(); ++i) {
1422
0
1423
0
    // Collect and coalesce adjacent siblings for lazy frame construction.
1424
0
    // Eventually it would be even better to make RecreateFramesForContent
1425
0
    // accept a range and coalesce all adjacent reconstructs (bug 1344139).
1426
0
    size_t lazyRangeStart = i;
1427
0
    while (i < aChangeList.Length() &&
1428
0
           aChangeList[i].mContent &&
1429
0
           aChangeList[i].mContent->HasFlag(NODE_NEEDS_FRAME) &&
1430
0
           (i == lazyRangeStart ||
1431
0
            NextSiblingWhichMayHaveFrame(aChangeList[i - 1].mContent) ==
1432
0
              aChangeList[i].mContent))
1433
0
    {
1434
0
      MOZ_ASSERT(aChangeList[i].mHint & nsChangeHint_ReconstructFrame);
1435
0
      MOZ_ASSERT(!aChangeList[i].mFrame);
1436
0
      ++i;
1437
0
    }
1438
0
    if (i != lazyRangeStart) {
1439
0
      nsIContent* start = aChangeList[lazyRangeStart].mContent;
1440
0
      nsIContent* end = NextSiblingWhichMayHaveFrame(aChangeList[i-1].mContent);
1441
0
      if (!end) {
1442
0
        frameConstructor->ContentAppended(
1443
0
            start,
1444
0
            nsCSSFrameConstructor::InsertionKind::Sync);
1445
0
      } else {
1446
0
        frameConstructor->ContentRangeInserted(
1447
0
            start,
1448
0
            end,
1449
0
            nullptr,
1450
0
            nsCSSFrameConstructor::InsertionKind::Sync);
1451
0
      }
1452
0
    }
1453
0
    for (size_t j = lazyRangeStart; j < i; ++j) {
1454
0
      MOZ_ASSERT(!aChangeList[j].mContent->GetPrimaryFrame() ||
1455
0
                 !aChangeList[j].mContent->HasFlag(NODE_NEEDS_FRAME));
1456
0
    }
1457
0
    if (i == aChangeList.Length()) {
1458
0
      break;
1459
0
    }
1460
0
1461
0
    const nsStyleChangeData& data = aChangeList[i];
1462
0
    nsIFrame* frame = data.mFrame;
1463
0
    nsIContent* content = data.mContent;
1464
0
    nsChangeHint hint = data.mHint;
1465
0
    bool didReflowThisFrame = false;
1466
0
1467
0
    NS_ASSERTION(!(hint & nsChangeHint_AllReflowHints) ||
1468
0
                 (hint & nsChangeHint_NeedReflow),
1469
0
                 "Reflow hint bits set without actually asking for a reflow");
1470
0
1471
0
    // skip any frame that has been destroyed due to a ripple effect
1472
0
    if (frame && mDestroyedFrames->Contains(frame)) {
1473
0
      continue;
1474
0
    }
1475
0
1476
0
    if (frame && frame->GetContent() != content) {
1477
0
      // XXXbz this is due to image maps messing with the primary frame of
1478
0
      // <area>s.  See bug 135040.  Remove this block once that's fixed.
1479
0
      frame = nullptr;
1480
0
      if (!(hint & nsChangeHint_ReconstructFrame)) {
1481
0
        continue;
1482
0
      }
1483
0
    }
1484
0
1485
0
    if ((hint & nsChangeHint_UpdateContainingBlock) && frame &&
1486
0
        !(hint & nsChangeHint_ReconstructFrame)) {
1487
0
      if (NeedToReframeForAddingOrRemovingTransform(frame) ||
1488
0
          frame->IsFieldSetFrame() ||
1489
0
          frame->GetContentInsertionFrame() != frame) {
1490
0
        // The frame has positioned children that need to be reparented, or
1491
0
        // it can't easily be converted to/from being an abs-pos container
1492
0
        // correctly.
1493
0
        hint |= nsChangeHint_ReconstructFrame;
1494
0
      } else {
1495
0
        for (nsIFrame* cont = frame; cont;
1496
0
             cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
1497
0
          // Normally frame construction would set state bits as needed,
1498
0
          // but we're not going to reconstruct the frame so we need to set them.
1499
0
          // It's because we need to set this state on each affected frame
1500
0
          // that we can't coalesce nsChangeHint_UpdateContainingBlock hints up
1501
0
          // to ancestors (i.e. it can't be an change hint that is handled for
1502
0
          // descendants).
1503
0
          if (cont->IsAbsPosContainingBlock()) {
1504
0
            if (!cont->IsAbsoluteContainer() &&
1505
0
                (cont->GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN)) {
1506
0
              cont->MarkAsAbsoluteContainingBlock();
1507
0
            }
1508
0
          } else {
1509
0
            if (cont->IsAbsoluteContainer()) {
1510
0
              if (cont->HasAbsolutelyPositionedChildren()) {
1511
0
                // If |cont| still has absolutely positioned children,
1512
0
                // we can't call MarkAsNotAbsoluteContainingBlock.  This
1513
0
                // will remove a frame list that still has children in
1514
0
                // it that we need to keep track of.
1515
0
                // The optimization of removing it isn't particularly
1516
0
                // important, although it does mean we skip some tests.
1517
0
                NS_WARNING("skipping removal of absolute containing block");
1518
0
              } else {
1519
0
                cont->MarkAsNotAbsoluteContainingBlock();
1520
0
              }
1521
0
            }
1522
0
          }
1523
0
        }
1524
0
      }
1525
0
    }
1526
0
1527
0
    if ((hint & nsChangeHint_AddOrRemoveTransform) && frame &&
1528
0
        !(hint & nsChangeHint_ReconstructFrame)) {
1529
0
      for (nsIFrame* cont = frame; cont;
1530
0
           cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
1531
0
        if (cont->StyleDisplay()->HasTransform(cont)) {
1532
0
          cont->AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
1533
0
        }
1534
0
        // Don't remove NS_FRAME_MAY_BE_TRANSFORMED since it may still be
1535
0
        // transformed by other means. It's OK to have the bit even if it's
1536
0
        // not needed.
1537
0
      }
1538
0
    }
1539
0
1540
0
    if (hint & nsChangeHint_ReconstructFrame) {
1541
0
      // If we ever start passing true here, be careful of restyles
1542
0
      // that involve a reframe and animations.  In particular, if the
1543
0
      // restyle we're processing here is an animation restyle, but
1544
0
      // the style resolution we will do for the frame construction
1545
0
      // happens async when we're not in an animation restyle already,
1546
0
      // problems could arise.
1547
0
      // We could also have problems with triggering of CSS transitions
1548
0
      // on elements whose frames are reconstructed, since we depend on
1549
0
      // the reconstruction happening synchronously.
1550
0
      frameConstructor->RecreateFramesForContent(
1551
0
        content, nsCSSFrameConstructor::InsertionKind::Sync);
1552
0
    } else {
1553
0
      NS_ASSERTION(frame, "This shouldn't happen");
1554
0
1555
0
      if (!frame->FrameMaintainsOverflow()) {
1556
0
        // frame does not maintain overflow rects, so avoid calling
1557
0
        // FinishAndStoreOverflow on it:
1558
0
        hint &= ~(nsChangeHint_UpdateOverflow |
1559
0
                  nsChangeHint_ChildrenOnlyTransform |
1560
0
                  nsChangeHint_UpdatePostTransformOverflow |
1561
0
                  nsChangeHint_UpdateParentOverflow);
1562
0
      }
1563
0
1564
0
      if (!(frame->GetStateBits() & NS_FRAME_MAY_BE_TRANSFORMED)) {
1565
0
        // Frame can not be transformed, and thus a change in transform will
1566
0
        // have no effect and we should not use the
1567
0
        // nsChangeHint_UpdatePostTransformOverflow hint.
1568
0
        hint &= ~nsChangeHint_UpdatePostTransformOverflow;
1569
0
      }
1570
0
1571
0
      if (hint & nsChangeHint_AddOrRemoveTransform) {
1572
0
        // When dropping a running transform animation we will first add an
1573
0
        // nsChangeHint_UpdateTransformLayer hint as part of the animation-only
1574
0
        // restyle. During the subsequent regular restyle, if the animation was
1575
0
        // the only reason the element had any transform applied, we will add
1576
0
        // nsChangeHint_AddOrRemoveTransform as part of the regular restyle.
1577
0
        //
1578
0
        // With the Gecko backend, these two change hints are processed
1579
0
        // after each restyle but when using the Servo backend they accumulate
1580
0
        // and are processed together after we have already removed the
1581
0
        // transform as part of the regular restyle. Since we don't actually
1582
0
        // need the nsChangeHint_UpdateTransformLayer hint if we already have
1583
0
        // a nsChangeHint_AddOrRemoveTransform hint, and since we
1584
0
        // will fail an assertion in ApplyRenderingChangeToTree if we try
1585
0
        // specify nsChangeHint_UpdateTransformLayer but don't have any
1586
0
        // transform style, we just drop the unneeded hint here.
1587
0
        hint &= ~nsChangeHint_UpdateTransformLayer;
1588
0
      }
1589
0
1590
0
      if (hint & nsChangeHint_UpdateEffects) {
1591
0
        for (nsIFrame* cont = frame; cont;
1592
0
             cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
1593
0
          SVGObserverUtils::UpdateEffects(cont);
1594
0
        }
1595
0
      }
1596
0
      if ((hint & nsChangeHint_InvalidateRenderingObservers) ||
1597
0
          ((hint & nsChangeHint_UpdateOpacityLayer) &&
1598
0
           frame->IsFrameOfType(nsIFrame::eSVG) &&
1599
0
           !(frame->GetStateBits() & NS_STATE_IS_OUTER_SVG))) {
1600
0
        SVGObserverUtils::InvalidateRenderingObservers(frame);
1601
0
        frame->SchedulePaint();
1602
0
      }
1603
0
      if (hint & nsChangeHint_NeedReflow) {
1604
0
        StyleChangeReflow(frame, hint);
1605
0
        didReflowThisFrame = true;
1606
0
      }
1607
0
1608
0
      // Here we need to propagate repaint frame change hint instead of update
1609
0
      // opacity layer change hint when we do opacity optimization for SVG.
1610
0
      // We can't do it in nsStyleEffects::CalcDifference() just like we do
1611
0
      // for the optimization for 0.99 over opacity values since we have no way
1612
0
      // to call nsSVGUtils::CanOptimizeOpacity() there.
1613
0
      if ((hint & nsChangeHint_UpdateOpacityLayer) &&
1614
0
          nsSVGUtils::CanOptimizeOpacity(frame) &&
1615
0
          frame->IsFrameOfType(nsIFrame::eSVGGeometry)) {
1616
0
        hint &= ~nsChangeHint_UpdateOpacityLayer;
1617
0
        hint |= nsChangeHint_RepaintFrame;
1618
0
      }
1619
0
1620
0
      if ((hint & nsChangeHint_UpdateUsesOpacity) &&
1621
0
          frame->IsFrameOfType(nsIFrame::eTablePart)) {
1622
0
        NS_ASSERTION(hint & nsChangeHint_UpdateOpacityLayer,
1623
0
                     "should only return UpdateUsesOpacity hint "
1624
0
                     "when also returning UpdateOpacityLayer hint");
1625
0
        // When an internal table part (including cells) changes between
1626
0
        // having opacity 1 and non-1, it changes whether its
1627
0
        // backgrounds (and those of table parts inside of it) are
1628
0
        // painted as part of the table's nsDisplayTableBorderBackground
1629
0
        // display item, or part of its own display item.  That requires
1630
0
        // invalidation, so change UpdateOpacityLayer to RepaintFrame.
1631
0
        hint &= ~nsChangeHint_UpdateOpacityLayer;
1632
0
        hint |= nsChangeHint_RepaintFrame;
1633
0
      }
1634
0
1635
0
      // Opacity disables preserve-3d, so if we toggle it, then we also need
1636
0
      // to update the overflow areas of all potentially affected frames.
1637
0
      if ((hint & nsChangeHint_UpdateUsesOpacity) &&
1638
0
          frame->StyleDisplay()->mTransformStyle == NS_STYLE_TRANSFORM_STYLE_PRESERVE_3D) {
1639
0
        hint |= nsChangeHint_UpdateSubtreeOverflow;
1640
0
      }
1641
0
1642
0
      if (hint & nsChangeHint_UpdateBackgroundPosition) {
1643
0
        // For most frame types, DLBI can detect background position changes,
1644
0
        // so we only need to schedule a paint.
1645
0
        hint |= nsChangeHint_SchedulePaint;
1646
0
        if (frame->IsFrameOfType(nsIFrame::eTablePart) ||
1647
0
            frame->IsFrameOfType(nsIFrame::eMathML)) {
1648
0
          // Table parts and MathML frames don't build display items for their
1649
0
          // backgrounds, so DLBI can't detect background-position changes for
1650
0
          // these frames. Repaint the whole frame.
1651
0
          hint |= nsChangeHint_RepaintFrame;
1652
0
        }
1653
0
      }
1654
0
1655
0
      if (hint & (nsChangeHint_RepaintFrame | nsChangeHint_SyncFrameView |
1656
0
                  nsChangeHint_UpdateOpacityLayer | nsChangeHint_UpdateTransformLayer |
1657
0
                  nsChangeHint_ChildrenOnlyTransform | nsChangeHint_SchedulePaint)) {
1658
0
        ApplyRenderingChangeToTree(presContext->PresShell(), frame, hint);
1659
0
      }
1660
0
      if ((hint & nsChangeHint_RecomputePosition) && !didReflowThisFrame) {
1661
0
        ActiveLayerTracker::NotifyOffsetRestyle(frame);
1662
0
        // It is possible for this to fall back to a reflow
1663
0
        if (!RecomputePosition(frame)) {
1664
0
          didReflowThisFrame = true;
1665
0
        }
1666
0
      }
1667
0
      NS_ASSERTION(!(hint & nsChangeHint_ChildrenOnlyTransform) ||
1668
0
                   (hint & nsChangeHint_UpdateOverflow),
1669
0
                   "nsChangeHint_UpdateOverflow should be passed too");
1670
0
      if (!didReflowThisFrame &&
1671
0
          (hint & (nsChangeHint_UpdateOverflow |
1672
0
                   nsChangeHint_UpdatePostTransformOverflow |
1673
0
                   nsChangeHint_UpdateParentOverflow |
1674
0
                   nsChangeHint_UpdateSubtreeOverflow))) {
1675
0
        if (hint & nsChangeHint_UpdateSubtreeOverflow) {
1676
0
          for (nsIFrame* cont = frame; cont; cont =
1677
0
                 nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
1678
0
            AddSubtreeToOverflowTracker(cont, mOverflowChangedTracker);
1679
0
          }
1680
0
          // The work we just did in AddSubtreeToOverflowTracker
1681
0
          // subsumes some of the other hints:
1682
0
          hint &= ~(nsChangeHint_UpdateOverflow |
1683
0
                    nsChangeHint_UpdatePostTransformOverflow);
1684
0
        }
1685
0
        if (hint & nsChangeHint_ChildrenOnlyTransform) {
1686
0
          // The overflow areas of the child frames need to be updated:
1687
0
          nsIFrame* hintFrame = GetFrameForChildrenOnlyTransformHint(frame);
1688
0
          nsIFrame* childFrame = hintFrame->PrincipalChildList().FirstChild();
1689
0
          NS_ASSERTION(!nsLayoutUtils::GetNextContinuationOrIBSplitSibling(frame),
1690
0
                       "SVG frames should not have continuations "
1691
0
                       "or ib-split siblings");
1692
0
          NS_ASSERTION(!nsLayoutUtils::GetNextContinuationOrIBSplitSibling(hintFrame),
1693
0
                       "SVG frames should not have continuations "
1694
0
                       "or ib-split siblings");
1695
0
          for ( ; childFrame; childFrame = childFrame->GetNextSibling()) {
1696
0
            MOZ_ASSERT(childFrame->IsFrameOfType(nsIFrame::eSVG),
1697
0
                       "Not expecting non-SVG children");
1698
0
            // If |childFrame| is dirty or has dirty children, we don't bother
1699
0
            // updating overflows since that will happen when it's reflowed.
1700
0
            if (!(childFrame->GetStateBits() &
1701
0
                  (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN))) {
1702
0
              mOverflowChangedTracker.AddFrame(childFrame,
1703
0
                                        OverflowChangedTracker::CHILDREN_CHANGED);
1704
0
            }
1705
0
            NS_ASSERTION(!nsLayoutUtils::GetNextContinuationOrIBSplitSibling(childFrame),
1706
0
                         "SVG frames should not have continuations "
1707
0
                         "or ib-split siblings");
1708
0
            NS_ASSERTION(childFrame->GetParent() == hintFrame,
1709
0
                         "SVG child frame not expected to have different parent");
1710
0
          }
1711
0
        }
1712
0
        // If |frame| is dirty or has dirty children, we don't bother updating
1713
0
        // overflows since that will happen when it's reflowed.
1714
0
        if (!(frame->GetStateBits() &
1715
0
              (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN))) {
1716
0
          if (hint & (nsChangeHint_UpdateOverflow |
1717
0
                      nsChangeHint_UpdatePostTransformOverflow)) {
1718
0
            OverflowChangedTracker::ChangeKind changeKind;
1719
0
            // If we have both nsChangeHint_UpdateOverflow and
1720
0
            // nsChangeHint_UpdatePostTransformOverflow,
1721
0
            // CHILDREN_CHANGED is selected as it is
1722
0
            // strictly stronger.
1723
0
            if (hint & nsChangeHint_UpdateOverflow) {
1724
0
              changeKind = OverflowChangedTracker::CHILDREN_CHANGED;
1725
0
            } else {
1726
0
              changeKind = OverflowChangedTracker::TRANSFORM_CHANGED;
1727
0
            }
1728
0
            for (nsIFrame* cont = frame; cont; cont =
1729
0
                   nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
1730
0
              mOverflowChangedTracker.AddFrame(cont, changeKind);
1731
0
            }
1732
0
          }
1733
0
          // UpdateParentOverflow hints need to be processed in addition
1734
0
          // to the above, since if the processing of the above hints
1735
0
          // yields no change, the update will not propagate to the
1736
0
          // parent.
1737
0
          if (hint & nsChangeHint_UpdateParentOverflow) {
1738
0
            MOZ_ASSERT(frame->GetParent(),
1739
0
                       "shouldn't get style hints for the root frame");
1740
0
            for (nsIFrame* cont = frame; cont; cont =
1741
0
                   nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
1742
0
              mOverflowChangedTracker.AddFrame(cont->GetParent(),
1743
0
                                   OverflowChangedTracker::CHILDREN_CHANGED);
1744
0
            }
1745
0
          }
1746
0
        }
1747
0
      }
1748
0
      if ((hint & nsChangeHint_UpdateCursor) && !didUpdateCursor) {
1749
0
        presContext->PresShell()->SynthesizeMouseMove(false);
1750
0
        didUpdateCursor = true;
1751
0
      }
1752
0
      if (hint & nsChangeHint_UpdateWidgetProperties) {
1753
0
        frame->UpdateWidgetProperties();
1754
0
      }
1755
0
      if (hint & nsChangeHint_UpdateTableCellSpans) {
1756
0
        frameConstructor->UpdateTableCellSpans(content);
1757
0
      }
1758
0
      if (hint & nsChangeHint_VisibilityChange) {
1759
0
        frame->UpdateVisibleDescendantsState();
1760
0
      }
1761
0
    }
1762
0
  }
1763
0
1764
0
  aChangeList.Clear();
1765
0
}
1766
1767
/* static */ uint64_t
1768
RestyleManager::GetAnimationGenerationForFrame(nsIFrame* aFrame)
1769
0
{
1770
0
  EffectSet* effectSet = EffectSet::GetEffectSet(aFrame);
1771
0
  return effectSet ? effectSet->GetAnimationGeneration() : 0;
1772
0
}
1773
1774
void
1775
RestyleManager::IncrementAnimationGeneration()
1776
0
{
1777
0
  // We update the animation generation at start of each call to
1778
0
  // ProcessPendingRestyles so we should ignore any subsequent (redundant)
1779
0
  // calls that occur while we are still processing restyles.
1780
0
  if (!mInStyleRefresh) {
1781
0
    ++mAnimationGeneration;
1782
0
  }
1783
0
}
1784
1785
/* static */ void
1786
RestyleManager::AddLayerChangesForAnimation(nsIFrame* aFrame,
1787
                                            nsIContent* aContent,
1788
                                            nsChangeHint aHintForThisFrame,
1789
                                            nsStyleChangeList&
1790
                                              aChangeListToProcess)
1791
0
{
1792
0
  if (!aFrame || !aContent) {
1793
0
    return;
1794
0
  }
1795
0
1796
0
  uint64_t frameGeneration =
1797
0
    RestyleManager::GetAnimationGenerationForFrame(aFrame);
1798
0
1799
0
  nsChangeHint hint = nsChangeHint(0);
1800
0
  for (const LayerAnimationInfo::Record& layerInfo :
1801
0
         LayerAnimationInfo::sRecords) {
1802
0
    Maybe<uint64_t> generation =
1803
0
      layers::AnimationInfo::GetGenerationFromFrame(aFrame,
1804
0
                                                    layerInfo.mLayerType);
1805
0
    if (generation && frameGeneration != *generation) {
1806
0
      // If we have a transform layer bug don't have any transform style, we
1807
0
      // probably just removed the transform but haven't destroyed the layer
1808
0
      // yet. In this case we will typically add the appropriate change hint
1809
0
      // (nsChangeHint_UpdateContainingBlock) when we compare styles so in
1810
0
      // theory we could skip adding any change hint here.
1811
0
      //
1812
0
      // However, sometimes when we compare styles we'll get no change. For
1813
0
      // example, if the transform style was 'none' when we sent the transform
1814
0
      // animation to the compositor and the current transform style is now
1815
0
      // 'none' we'll think nothing changed but actually we still need to
1816
0
      // trigger an update to clear whatever style the transform animation set
1817
0
      // on the compositor. To handle this case we simply set all the change
1818
0
      // hints relevant to removing transform style (since we don't know exactly
1819
0
      // what changes happened while the animation was running on the
1820
0
      // compositor).
1821
0
      //
1822
0
      // Note that we *don't* add nsChangeHint_UpdateTransformLayer since if we
1823
0
      // did, ApplyRenderingChangeToTree would complain that we're updating a
1824
0
      // transform layer without a transform.
1825
0
      if (layerInfo.mLayerType == DisplayItemType::TYPE_TRANSFORM &&
1826
0
          !aFrame->StyleDisplay()->HasTransformStyle()) {
1827
0
        // Add all the hints for a removing a transform if they are not already
1828
0
        // set for this frame.
1829
0
        if (!(NS_IsHintSubset(
1830
0
                nsChangeHint_ComprehensiveAddOrRemoveTransform,
1831
0
                aHintForThisFrame))) {
1832
0
          hint |= nsChangeHint_ComprehensiveAddOrRemoveTransform;
1833
0
        }
1834
0
        continue;
1835
0
      }
1836
0
      hint |= layerInfo.mChangeHint;
1837
0
    }
1838
0
1839
0
    // We consider it's the first paint for the frame if we have an animation
1840
0
    // for the property but have no layer, for the case of WebRender,  no
1841
0
    // corresponding animation info.
1842
0
    // Note that in case of animations which has properties preventing running
1843
0
    // on the compositor, e.g., width or height, corresponding layer is not
1844
0
    // created at all, but even in such cases, we normally set valid change
1845
0
    // hint for such animations in each tick, i.e. restyles in each tick. As
1846
0
    // a result, we usually do restyles for such animations in every tick on
1847
0
    // the main-thread.  The only animations which will be affected by this
1848
0
    // explicit change hint are animations that have opacity/transform but did
1849
0
    // not have those properies just before. e.g, setting transform by
1850
0
    // setKeyframes or changing target element from other target which prevents
1851
0
    // running on the compositor, etc.
1852
0
    if (!generation &&
1853
0
        nsLayoutUtils::HasEffectiveAnimation(aFrame, layerInfo.mProperty)) {
1854
0
      hint |= layerInfo.mChangeHint;
1855
0
    }
1856
0
  }
1857
0
1858
0
  if (hint) {
1859
0
    aChangeListToProcess.AppendChange(aFrame, aContent, hint);
1860
0
  }
1861
0
}
1862
1863
RestyleManager::AnimationsWithDestroyedFrame::AnimationsWithDestroyedFrame(
1864
                                                RestyleManager* aRestyleManager)
1865
  : mRestyleManager(aRestyleManager)
1866
  , mRestorePointer(mRestyleManager->mAnimationsWithDestroyedFrame)
1867
0
{
1868
0
  MOZ_ASSERT(!mRestyleManager->mAnimationsWithDestroyedFrame,
1869
0
             "shouldn't construct recursively");
1870
0
  mRestyleManager->mAnimationsWithDestroyedFrame = this;
1871
0
}
1872
1873
void
1874
RestyleManager::AnimationsWithDestroyedFrame
1875
              ::StopAnimationsForElementsWithoutFrames()
1876
0
{
1877
0
  StopAnimationsWithoutFrame(mContents, CSSPseudoElementType::NotPseudo);
1878
0
  StopAnimationsWithoutFrame(mBeforeContents, CSSPseudoElementType::before);
1879
0
  StopAnimationsWithoutFrame(mAfterContents, CSSPseudoElementType::after);
1880
0
}
1881
1882
void
1883
RestyleManager::AnimationsWithDestroyedFrame
1884
              ::StopAnimationsWithoutFrame(
1885
                  nsTArray<RefPtr<nsIContent>>& aArray,
1886
                  CSSPseudoElementType aPseudoType)
1887
0
{
1888
0
  nsAnimationManager* animationManager =
1889
0
    mRestyleManager->PresContext()->AnimationManager();
1890
0
  nsTransitionManager* transitionManager =
1891
0
    mRestyleManager->PresContext()->TransitionManager();
1892
0
  for (nsIContent* content : aArray) {
1893
0
    if (aPseudoType == CSSPseudoElementType::NotPseudo) {
1894
0
      if (content->GetPrimaryFrame()) {
1895
0
        continue;
1896
0
      }
1897
0
    } else if (aPseudoType == CSSPseudoElementType::before) {
1898
0
      if (nsLayoutUtils::GetBeforeFrame(content)) {
1899
0
        continue;
1900
0
      }
1901
0
    } else if (aPseudoType == CSSPseudoElementType::after) {
1902
0
      if (nsLayoutUtils::GetAfterFrame(content)) {
1903
0
        continue;
1904
0
      }
1905
0
    }
1906
0
    dom::Element* element = content->AsElement();
1907
0
1908
0
    animationManager->StopAnimationsForElement(element, aPseudoType);
1909
0
    transitionManager->StopAnimationsForElement(element, aPseudoType);
1910
0
1911
0
    // All other animations should keep running but not running on the
1912
0
    // *compositor* at this point.
1913
0
    EffectSet* effectSet = EffectSet::GetEffectSet(element, aPseudoType);
1914
0
    if (effectSet) {
1915
0
      for (KeyframeEffect* effect : *effectSet) {
1916
0
        effect->ResetIsRunningOnCompositor();
1917
0
      }
1918
0
    }
1919
0
  }
1920
0
}
1921
1922
#ifdef DEBUG
1923
static bool
1924
IsAnonBox(const nsIFrame* aFrame)
1925
{
1926
  return aFrame->Style()->IsAnonBox();
1927
}
1928
1929
static const nsIFrame*
1930
FirstContinuationOrPartOfIBSplit(const nsIFrame* aFrame)
1931
{
1932
  if (!aFrame) {
1933
    return nullptr;
1934
  }
1935
1936
  return nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
1937
}
1938
1939
static const nsIFrame*
1940
ExpectedOwnerForChild(const nsIFrame* aFrame)
1941
{
1942
  const nsIFrame* parent = aFrame->GetParent();
1943
  if (aFrame->IsTableFrame()) {
1944
    MOZ_ASSERT(parent->IsTableWrapperFrame());
1945
    parent = parent->GetParent();
1946
  }
1947
1948
  if (IsAnonBox(aFrame) && !aFrame->IsTextFrame()) {
1949
    if (parent->IsLineFrame()) {
1950
      parent = parent->GetParent();
1951
    }
1952
    return parent->IsViewportFrame() ?
1953
      nullptr : FirstContinuationOrPartOfIBSplit(parent);
1954
  }
1955
1956
  if (aFrame->IsBulletFrame()) {
1957
    return FirstContinuationOrPartOfIBSplit(parent);
1958
  }
1959
1960
  if (aFrame->IsLineFrame()) {
1961
    // A ::first-line always ends up here via its block, which is therefore the
1962
    // right expected owner.  That block can be an
1963
    // anonymous box.  For example, we could have a ::first-line on a columnated
1964
    // block; the blockframe is the column-content anonymous box in that case.
1965
    // So we don't want to end up in the code below, which steps out of anon
1966
    // boxes.  Just return the parent of the line frame, which is the block.
1967
    return parent;
1968
  }
1969
1970
  if (aFrame->IsLetterFrame()) {
1971
    // Ditto for ::first-letter. A first-letter always arrives here via its
1972
    // direct parent, except when it's parented to a ::first-line.
1973
    if (parent->IsLineFrame()) {
1974
      parent = parent->GetParent();
1975
    }
1976
    return FirstContinuationOrPartOfIBSplit(parent);
1977
  }
1978
1979
  if (parent->IsLetterFrame()) {
1980
    // Things never have ::first-letter as their expected parent.  Go
1981
    // on up to the ::first-letter's parent.
1982
    parent = parent->GetParent();
1983
  }
1984
1985
  parent = FirstContinuationOrPartOfIBSplit(parent);
1986
1987
  // We've handled already anon boxes and bullet frames, so now we're looking at
1988
  // a frame of a DOM element or pseudo. Hop through anon and line-boxes
1989
  // generated by our DOM parent, and go find the owner frame for it.
1990
  while (parent && (IsAnonBox(parent) || parent->IsLineFrame())) {
1991
    auto* pseudo = parent->Style()->GetPseudo();
1992
    if (pseudo == nsCSSAnonBoxes::tableWrapper()) {
1993
      const nsIFrame* tableFrame = parent->PrincipalChildList().FirstChild();
1994
      MOZ_ASSERT(tableFrame->IsTableFrame());
1995
      // Handle :-moz-table and :-moz-inline-table.
1996
      parent = IsAnonBox(tableFrame) ? parent->GetParent() : tableFrame;
1997
    } else {
1998
      // We get the in-flow parent here so that we can handle the OOF anonymous
1999
      // boxed to get the correct parent.
2000
      parent = parent->GetInFlowParent();
2001
    }
2002
    parent = FirstContinuationOrPartOfIBSplit(parent);
2003
  }
2004
2005
  return parent;
2006
}
2007
2008
void
2009
ServoRestyleState::AssertOwner(const ServoRestyleState& aParent) const
2010
{
2011
  MOZ_ASSERT(mOwner);
2012
  MOZ_ASSERT(!mOwner->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW));
2013
  // We allow aParent.mOwner to be null, for cases when we're not starting at
2014
  // the root of the tree.  We also allow aParent.mOwner to be somewhere up our
2015
  // expected owner chain not our immediate owner, which allows us creating long
2016
  // chains of ServoRestyleStates in some cases where it's just not worth it.
2017
#ifdef DEBUG
2018
  if (aParent.mOwner) {
2019
    const nsIFrame* owner = ExpectedOwnerForChild(mOwner);
2020
    if (owner != aParent.mOwner) {
2021
      MOZ_ASSERT(IsAnonBox(owner),
2022
                 "Should only have expected owner weirdness when anon boxes are involved");
2023
      bool found = false;
2024
      for (; owner; owner = ExpectedOwnerForChild(owner)) {
2025
        if (owner == aParent.mOwner) {
2026
          found = true;
2027
          break;
2028
        }
2029
      }
2030
      MOZ_ASSERT(found, "Must have aParent.mOwner on our expected owner chain");
2031
    }
2032
  }
2033
#endif
2034
}
2035
2036
nsChangeHint
2037
ServoRestyleState::ChangesHandledFor(const nsIFrame* aFrame) const
2038
{
2039
  if (!mOwner) {
2040
    MOZ_ASSERT(!mChangesHandled);
2041
    return mChangesHandled;
2042
  }
2043
2044
  MOZ_ASSERT(mOwner == ExpectedOwnerForChild(aFrame),
2045
             "Missed some frame in the hierarchy?");
2046
  return mChangesHandled;
2047
}
2048
#endif
2049
2050
void
2051
ServoRestyleState::AddPendingWrapperRestyle(nsIFrame* aWrapperFrame)
2052
0
{
2053
0
  MOZ_ASSERT(aWrapperFrame->Style()->IsWrapperAnonBox(),
2054
0
             "All our wrappers are anon boxes, and why would we restyle "
2055
0
             "non-inheriting ones?");
2056
0
  MOZ_ASSERT(aWrapperFrame->Style()->IsInheritingAnonBox(),
2057
0
             "All our wrappers are anon boxes, and why would we restyle "
2058
0
             "non-inheriting ones?");
2059
0
  MOZ_ASSERT(aWrapperFrame->Style()->GetPseudo() !=
2060
0
             nsCSSAnonBoxes::cellContent(),
2061
0
             "Someone should be using TableAwareParentFor");
2062
0
  MOZ_ASSERT(aWrapperFrame->Style()->GetPseudo() !=
2063
0
             nsCSSAnonBoxes::tableWrapper(),
2064
0
             "Someone should be using TableAwareParentFor");
2065
0
  // Make sure we only add first continuations.
2066
0
  aWrapperFrame = aWrapperFrame->FirstContinuation();
2067
0
  nsIFrame* last = mPendingWrapperRestyles.SafeLastElement(nullptr);
2068
0
  if (last == aWrapperFrame) {
2069
0
    // Already queued up, nothing to do.
2070
0
    return;
2071
0
  }
2072
0
2073
0
  // Make sure to queue up parents before children.  But don't queue up
2074
0
  // ancestors of non-anonymous boxes here; those are handled when we traverse
2075
0
  // their non-anonymous kids.
2076
0
  if (aWrapperFrame->ParentIsWrapperAnonBox()) {
2077
0
    AddPendingWrapperRestyle(TableAwareParentFor(aWrapperFrame));
2078
0
  }
2079
0
2080
0
  // If the append fails, we'll fail to restyle properly, but that's probably
2081
0
  // better than crashing.
2082
0
  if (mPendingWrapperRestyles.AppendElement(aWrapperFrame, fallible)) {
2083
0
    aWrapperFrame->SetIsWrapperAnonBoxNeedingRestyle(true);
2084
0
  }
2085
0
}
2086
2087
void
2088
ServoRestyleState::ProcessWrapperRestyles(nsIFrame* aParentFrame)
2089
0
{
2090
0
  size_t i = mPendingWrapperRestyleOffset;
2091
0
  while (i < mPendingWrapperRestyles.Length()) {
2092
0
    i += ProcessMaybeNestedWrapperRestyle(aParentFrame, i);
2093
0
  }
2094
0
2095
0
  mPendingWrapperRestyles.TruncateLength(mPendingWrapperRestyleOffset);
2096
0
}
2097
2098
size_t
2099
ServoRestyleState::ProcessMaybeNestedWrapperRestyle(nsIFrame* aParent,
2100
                                                    size_t aIndex)
2101
0
{
2102
0
  // The frame at index aIndex is something we should restyle ourselves, but
2103
0
  // following frames may need separate ServoRestyleStates to restyle.
2104
0
  MOZ_ASSERT(aIndex < mPendingWrapperRestyles.Length());
2105
0
2106
0
  nsIFrame* cur = mPendingWrapperRestyles[aIndex];
2107
0
  MOZ_ASSERT(cur->Style()->IsWrapperAnonBox());
2108
0
2109
0
  // Where is cur supposed to inherit from?  From its parent frame, except in
2110
0
  // the case when cur is a table, in which case it should be its grandparent.
2111
0
  // Also, not in the case when the resulting frame would be a first-line; in
2112
0
  // that case we should be inheriting from the block, and the first-line will
2113
0
  // do its fixup later if needed.
2114
0
  //
2115
0
  // Note that after we do all that fixup the parent we get might still not be
2116
0
  // aParent; for example aParent could be a scrollframe, in which case we
2117
0
  // should inherit from the scrollcontent frame.  Or the parent might be some
2118
0
  // continuation of aParent.
2119
0
  //
2120
0
  // Try to assert as much as we can about the parent we actually end up using
2121
0
  // without triggering bogus asserts in all those various edge cases.
2122
0
  nsIFrame* parent = cur->GetParent();
2123
0
  if (cur->IsTableFrame()) {
2124
0
    MOZ_ASSERT(parent->IsTableWrapperFrame());
2125
0
    parent = parent->GetParent();
2126
0
  }
2127
0
  if (parent->IsLineFrame()) {
2128
0
    parent = parent->GetParent();
2129
0
  }
2130
0
  MOZ_ASSERT(FirstContinuationOrPartOfIBSplit(parent) == aParent ||
2131
0
             (parent->Style()->IsInheritingAnonBox() &&
2132
0
              parent->GetContent() == aParent->GetContent()));
2133
0
2134
0
  // Now "this" is a ServoRestyleState for aParent, so if parent is not a next
2135
0
  // continuation (possibly across ib splits) of aParent we need a new
2136
0
  // ServoRestyleState for the kid.
2137
0
  Maybe<ServoRestyleState> parentRestyleState;
2138
0
  nsIFrame* parentForRestyle =
2139
0
    nsLayoutUtils::FirstContinuationOrIBSplitSibling(parent);
2140
0
  if (parentForRestyle != aParent) {
2141
0
    parentRestyleState.emplace(*parentForRestyle, *this, nsChangeHint_Empty,
2142
0
                               Type::InFlow);
2143
0
  }
2144
0
  ServoRestyleState& curRestyleState =
2145
0
    parentRestyleState ? *parentRestyleState : *this;
2146
0
2147
0
  // This frame may already have been restyled.  Even if it has, we can't just
2148
0
  // return, because the next frame may be a kid of it that does need restyling.
2149
0
  if (cur->IsWrapperAnonBoxNeedingRestyle()) {
2150
0
    parentForRestyle->UpdateStyleOfChildAnonBox(cur, curRestyleState);
2151
0
    cur->SetIsWrapperAnonBoxNeedingRestyle(false);
2152
0
  }
2153
0
2154
0
  size_t numProcessed = 1;
2155
0
2156
0
  // Note: no overflow possible here, since aIndex < length.
2157
0
  if (aIndex + 1 < mPendingWrapperRestyles.Length()) {
2158
0
    nsIFrame* next = mPendingWrapperRestyles[aIndex + 1];
2159
0
    if (TableAwareParentFor(next) == cur &&
2160
0
        next->IsWrapperAnonBoxNeedingRestyle()) {
2161
0
      // It might be nice if we could do better than nsChangeHint_Empty.  On
2162
0
      // the other hand, presumably our mChangesHandled already has the bits
2163
0
      // we really want here so in practice it doesn't matter.
2164
0
      ServoRestyleState childState(*cur, curRestyleState, nsChangeHint_Empty,
2165
0
                                   Type::InFlow,
2166
0
                                   /* aAssertWrapperRestyleLength = */ false);
2167
0
      numProcessed += childState.ProcessMaybeNestedWrapperRestyle(cur,
2168
0
                                                                  aIndex + 1);
2169
0
    }
2170
0
  }
2171
0
2172
0
  return numProcessed;
2173
0
}
2174
2175
nsIFrame*
2176
ServoRestyleState::TableAwareParentFor(const nsIFrame* aChild)
2177
0
{
2178
0
  // We want to get the anon box parent for aChild. where aChild has
2179
0
  // ParentIsWrapperAnonBox().
2180
0
  //
2181
0
  // For the most part this is pretty straightforward, but there are two
2182
0
  // wrinkles.  First, if aChild is a table, then we really want the parent of
2183
0
  // its table wrapper.
2184
0
  if (aChild->IsTableFrame()) {
2185
0
    aChild = aChild->GetParent();
2186
0
    MOZ_ASSERT(aChild->IsTableWrapperFrame());
2187
0
  }
2188
0
2189
0
  nsIFrame* parent = aChild->GetParent();
2190
0
  // Now if parent is a cell-content frame, we actually want the cellframe.
2191
0
  if (parent->Style()->GetPseudo() == nsCSSAnonBoxes::cellContent()) {
2192
0
    parent = parent->GetParent();
2193
0
  } else if (parent->IsTableWrapperFrame()) {
2194
0
    // Must be a caption.  In that case we want the table here.
2195
0
    MOZ_ASSERT(aChild->StyleDisplay()->mDisplay == StyleDisplay::TableCaption);
2196
0
    parent = parent->PrincipalChildList().FirstChild();
2197
0
  }
2198
0
  return parent;
2199
0
}
2200
2201
void
2202
RestyleManager::PostRestyleEvent(Element* aElement,
2203
                                 nsRestyleHint aRestyleHint,
2204
                                 nsChangeHint aMinChangeHint)
2205
0
{
2206
0
  MOZ_ASSERT(!(aMinChangeHint & nsChangeHint_NeutralChange),
2207
0
             "Didn't expect explicit change hints to be neutral!");
2208
0
  if (MOZ_UNLIKELY(IsDisconnected()) ||
2209
0
      MOZ_UNLIKELY(PresContext()->PresShell()->IsDestroying())) {
2210
0
    return;
2211
0
  }
2212
0
2213
0
  // We allow posting restyles from within change hint handling, but not from
2214
0
  // within the restyle algorithm itself.
2215
0
  MOZ_ASSERT(!ServoStyleSet::IsInServoTraversal());
2216
0
2217
0
  if (aRestyleHint == 0 && !aMinChangeHint) {
2218
0
    return; // Nothing to do.
2219
0
  }
2220
0
2221
0
  // Assuming the restyle hints will invalidate cached style for
2222
0
  // getComputedStyle, since we don't know if any of the restyling that we do
2223
0
  // would affect undisplayed elements.
2224
0
  if (aRestyleHint) {
2225
0
    IncrementUndisplayedRestyleGeneration();
2226
0
  }
2227
0
2228
0
  // Processing change hints sometimes causes new change hints to be generated,
2229
0
  // and very occasionally, additional restyle hints. We collect the change
2230
0
  // hints manually to avoid re-traversing the DOM to find them.
2231
0
  if (mReentrantChanges && !aRestyleHint) {
2232
0
    mReentrantChanges->AppendElement(ReentrantChange { aElement, aMinChangeHint });
2233
0
    return;
2234
0
  }
2235
0
2236
0
  if (aRestyleHint & ~eRestyle_AllHintsWithAnimations) {
2237
0
    mHaveNonAnimationRestyles = true;
2238
0
  }
2239
0
2240
0
  if (aRestyleHint & eRestyle_LaterSiblings) {
2241
0
    aRestyleHint &= ~eRestyle_LaterSiblings;
2242
0
2243
0
    nsRestyleHint siblingHint = eRestyle_Subtree;
2244
0
    Element* current = aElement->GetNextElementSibling();
2245
0
    while (current) {
2246
0
      Servo_NoteExplicitHints(current, siblingHint, nsChangeHint(0));
2247
0
      current = current->GetNextElementSibling();
2248
0
    }
2249
0
  }
2250
0
2251
0
  if (aRestyleHint || aMinChangeHint) {
2252
0
    Servo_NoteExplicitHints(aElement, aRestyleHint, aMinChangeHint);
2253
0
  }
2254
0
}
2255
2256
void
2257
RestyleManager::PostRestyleEventForAnimations(
2258
  Element* aElement,
2259
  CSSPseudoElementType aPseudoType,
2260
  nsRestyleHint aRestyleHint)
2261
0
{
2262
0
  Element* elementToRestyle =
2263
0
    EffectCompositor::GetElementToRestyle(aElement, aPseudoType);
2264
0
2265
0
  if (!elementToRestyle) {
2266
0
    // FIXME: Bug 1371107: When reframing happens,
2267
0
    // EffectCompositor::mElementsToRestyle still has unbound old pseudo
2268
0
    // element. We should drop it.
2269
0
    return;
2270
0
  }
2271
0
2272
0
  AutoRestyleTimelineMarker marker(mPresContext->GetDocShell(),
2273
0
                                   true /* animation-only */);
2274
0
  Servo_NoteExplicitHints(elementToRestyle, aRestyleHint, nsChangeHint(0));
2275
0
}
2276
2277
void
2278
RestyleManager::RebuildAllStyleData(nsChangeHint aExtraHint,
2279
                                         nsRestyleHint aRestyleHint)
2280
0
{
2281
0
  // NOTE(emilio): GeckoRestlyeManager does a sync style flush, which seems not
2282
0
  // to be needed in my testing.
2283
0
  PostRebuildAllStyleDataEvent(aExtraHint, aRestyleHint);
2284
0
}
2285
2286
void
2287
RestyleManager::PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint,
2288
                                                  nsRestyleHint aRestyleHint)
2289
0
{
2290
0
  // NOTE(emilio): The semantics of these methods are quite funny, in the sense
2291
0
  // that we're not supposed to need to rebuild the actual stylist data.
2292
0
  //
2293
0
  // That's handled as part of the MediumFeaturesChanged stuff, if needed.
2294
0
  StyleSet()->ClearCachedStyleData();
2295
0
2296
0
  DocumentStyleRootIterator iter(mPresContext->Document());
2297
0
  while (Element* root = iter.GetNextStyleRoot()) {
2298
0
    PostRestyleEvent(root, aRestyleHint, aExtraHint);
2299
0
  }
2300
0
2301
0
  // TODO(emilio, bz): Extensions can add/remove stylesheets that can affect
2302
0
  // non-inheriting anon boxes. It's not clear if we want to support that, but
2303
0
  // if we do, we need to re-selector-match them here.
2304
0
}
2305
2306
/* static */ void
2307
RestyleManager::ClearServoDataFromSubtree(Element* aElement, IncludeRoot aIncludeRoot)
2308
0
{
2309
0
  if (aElement->HasServoData()) {
2310
0
    StyleChildrenIterator it(aElement);
2311
0
    for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
2312
0
      if (n->IsElement()) {
2313
0
        ClearServoDataFromSubtree(n->AsElement(), IncludeRoot::Yes);
2314
0
      }
2315
0
    }
2316
0
  }
2317
0
2318
0
  if (MOZ_LIKELY(aIncludeRoot == IncludeRoot::Yes)) {
2319
0
    aElement->ClearServoData();
2320
0
    MOZ_ASSERT(!aElement->HasAnyOfFlags(Element::kAllServoDescendantBits | NODE_NEEDS_FRAME));
2321
0
    MOZ_ASSERT(aElement != aElement->OwnerDoc()->GetServoRestyleRoot());
2322
0
  }
2323
0
}
2324
2325
/* static */ void
2326
RestyleManager::ClearRestyleStateFromSubtree(Element* aElement)
2327
0
{
2328
0
  if (aElement->HasAnyOfFlags(Element::kAllServoDescendantBits)) {
2329
0
    StyleChildrenIterator it(aElement);
2330
0
    for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
2331
0
      if (n->IsElement()) {
2332
0
        ClearRestyleStateFromSubtree(n->AsElement());
2333
0
      }
2334
0
    }
2335
0
  }
2336
0
2337
0
  bool wasRestyled;
2338
0
  Unused << Servo_TakeChangeHint(aElement, &wasRestyled);
2339
0
  aElement->UnsetFlags(Element::kAllServoDescendantBits);
2340
0
}
2341
2342
/**
2343
 * This struct takes care of encapsulating some common state that text nodes may
2344
 * need to track during the post-traversal.
2345
 *
2346
 * This is currently used to properly compute change hints when the parent
2347
 * element of this node is a display: contents node, and also to avoid computing
2348
 * the style for text children more than once per element.
2349
 */
2350
struct RestyleManager::TextPostTraversalState
2351
{
2352
public:
2353
  TextPostTraversalState(Element& aParentElement,
2354
                         ComputedStyle* aParentContext,
2355
                         bool aDisplayContentsParentStyleChanged,
2356
                         ServoRestyleState& aParentRestyleState)
2357
    : mParentElement(aParentElement)
2358
    , mParentContext(aParentContext)
2359
    , mParentRestyleState(aParentRestyleState)
2360
    , mStyle(nullptr)
2361
    , mShouldPostHints(aDisplayContentsParentStyleChanged)
2362
    , mShouldComputeHints(aDisplayContentsParentStyleChanged)
2363
    , mComputedHint(nsChangeHint_Empty)
2364
0
  {}
2365
2366
0
  nsStyleChangeList& ChangeList() { return mParentRestyleState.ChangeList(); }
2367
2368
  ComputedStyle& ComputeStyle(nsIContent* aTextNode)
2369
0
  {
2370
0
    if (!mStyle) {
2371
0
      mStyle = mParentRestyleState.StyleSet().ResolveStyleForText(
2372
0
        aTextNode, &ParentStyle());
2373
0
    }
2374
0
    MOZ_ASSERT(mStyle);
2375
0
    return *mStyle;
2376
0
  }
2377
2378
  void ComputeHintIfNeeded(nsIContent* aContent,
2379
                           nsIFrame* aTextFrame,
2380
                           ComputedStyle& aNewStyle)
2381
0
  {
2382
0
    MOZ_ASSERT(aTextFrame);
2383
0
    MOZ_ASSERT(aNewStyle.GetPseudo() == nsCSSAnonBoxes::mozText());
2384
0
2385
0
    if (MOZ_LIKELY(!mShouldPostHints)) {
2386
0
      return;
2387
0
    }
2388
0
2389
0
    ComputedStyle* oldStyle = aTextFrame->Style();
2390
0
    MOZ_ASSERT(oldStyle->GetPseudo() == nsCSSAnonBoxes::mozText());
2391
0
2392
0
    // We rely on the fact that all the text children for the same element share
2393
0
    // style to avoid recomputing style differences for all of them.
2394
0
    //
2395
0
    // TODO(emilio): The above may not be true for ::first-{line,letter}, but
2396
0
    // we'll cross that bridge when we support those in stylo.
2397
0
    if (mShouldComputeHints) {
2398
0
      mShouldComputeHints = false;
2399
0
      uint32_t equalStructs;
2400
0
      mComputedHint = oldStyle->CalcStyleDifference(&aNewStyle, &equalStructs);
2401
0
      mComputedHint = NS_RemoveSubsumedHints(
2402
0
        mComputedHint, mParentRestyleState.ChangesHandledFor(aTextFrame));
2403
0
    }
2404
0
2405
0
    if (mComputedHint) {
2406
0
      mParentRestyleState.ChangeList().AppendChange(
2407
0
        aTextFrame, aContent, mComputedHint);
2408
0
    }
2409
0
  }
2410
2411
private:
2412
0
  ComputedStyle& ParentStyle() {
2413
0
    if (!mParentContext) {
2414
0
      mLazilyResolvedParentContext =
2415
0
        mParentRestyleState.StyleSet().ResolveServoStyle(mParentElement);
2416
0
      mParentContext = mLazilyResolvedParentContext;
2417
0
    }
2418
0
    return *mParentContext;
2419
0
  }
2420
2421
  Element& mParentElement;
2422
  ComputedStyle* mParentContext;
2423
  RefPtr<ComputedStyle> mLazilyResolvedParentContext;
2424
  ServoRestyleState& mParentRestyleState;
2425
  RefPtr<ComputedStyle> mStyle;
2426
  bool mShouldPostHints;
2427
  bool mShouldComputeHints;
2428
  nsChangeHint mComputedHint;
2429
};
2430
2431
static void
2432
UpdateBackdropIfNeeded(nsIFrame* aFrame,
2433
                       ServoStyleSet& aStyleSet,
2434
                       nsStyleChangeList& aChangeList)
2435
0
{
2436
0
  const nsStyleDisplay* display = aFrame->Style()->StyleDisplay();
2437
0
  if (display->mTopLayer != NS_STYLE_TOP_LAYER_TOP) {
2438
0
    return;
2439
0
  }
2440
0
2441
0
  // Elements in the top layer are guaranteed to have absolute or fixed
2442
0
  // position per https://fullscreen.spec.whatwg.org/#new-stacking-layer.
2443
0
  MOZ_ASSERT(display->IsAbsolutelyPositionedStyle());
2444
0
2445
0
  nsIFrame* backdropPlaceholder =
2446
0
    aFrame->GetChildList(nsIFrame::kBackdropList).FirstChild();
2447
0
  if (!backdropPlaceholder) {
2448
0
    return;
2449
0
  }
2450
0
2451
0
  MOZ_ASSERT(backdropPlaceholder->IsPlaceholderFrame());
2452
0
  nsIFrame* backdropFrame =
2453
0
    nsPlaceholderFrame::GetRealFrameForPlaceholder(backdropPlaceholder);
2454
0
  MOZ_ASSERT(backdropFrame->IsBackdropFrame());
2455
0
  MOZ_ASSERT(backdropFrame->Style()->GetPseudoType() ==
2456
0
             CSSPseudoElementType::backdrop);
2457
0
2458
0
  RefPtr<ComputedStyle> newStyle =
2459
0
    aStyleSet.ResolvePseudoElementStyle(aFrame->GetContent()->AsElement(),
2460
0
                                        CSSPseudoElementType::backdrop,
2461
0
                                        aFrame->Style(),
2462
0
                                        /* aPseudoElement = */ nullptr);
2463
0
2464
0
  // NOTE(emilio): We can't use the changes handled for the owner of the
2465
0
  // backdrop frame, since it's out of flow, and parented to the viewport or
2466
0
  // canvas frame (depending on the `position` value).
2467
0
  MOZ_ASSERT(backdropFrame->GetParent()->IsViewportFrame() ||
2468
0
             backdropFrame->GetParent()->IsCanvasFrame());
2469
0
  nsTArray<nsIFrame*> wrappersToRestyle;
2470
0
  ServoRestyleState state(aStyleSet, aChangeList, wrappersToRestyle);
2471
0
  nsIFrame::UpdateStyleOfOwnedChildFrame(backdropFrame, newStyle, state);
2472
0
}
2473
2474
static void
2475
UpdateFirstLetterIfNeeded(nsIFrame* aFrame, ServoRestyleState& aRestyleState)
2476
0
{
2477
0
  MOZ_ASSERT(!aFrame->IsFrameOfType(nsIFrame::eBlockFrame),
2478
0
             "You're probably duplicating work with UpdatePseudoElementStyles!");
2479
0
  if (!aFrame->HasFirstLetterChild()) {
2480
0
    return;
2481
0
  }
2482
0
2483
0
  // We need to find the block the first-letter is associated with so we can
2484
0
  // find the right element for the first-letter's style resolution.  Might as
2485
0
  // well just delegate the whole thing to that block.
2486
0
  nsIFrame* block = aFrame->GetParent();
2487
0
  while (!block->IsFrameOfType(nsIFrame::eBlockFrame)) {
2488
0
    block = block->GetParent();
2489
0
  }
2490
0
2491
0
  static_cast<nsBlockFrame*>(block->FirstContinuation())->
2492
0
    UpdateFirstLetterStyle(aRestyleState);
2493
0
}
2494
2495
static void
2496
UpdateOneAdditionalComputedStyle(nsIFrame* aFrame,
2497
                                uint32_t aIndex,
2498
                                ComputedStyle& aOldContext,
2499
                                ServoRestyleState& aRestyleState)
2500
0
{
2501
0
  auto pseudoType = aOldContext.GetPseudoType();
2502
0
  MOZ_ASSERT(pseudoType != CSSPseudoElementType::NotPseudo);
2503
0
  MOZ_ASSERT(
2504
0
      !nsCSSPseudoElements::PseudoElementSupportsUserActionState(pseudoType));
2505
0
2506
0
  RefPtr<ComputedStyle> newStyle =
2507
0
    aRestyleState.StyleSet().ResolvePseudoElementStyle(
2508
0
        aFrame->GetContent()->AsElement(),
2509
0
        pseudoType,
2510
0
        aFrame->Style(),
2511
0
        /* aPseudoElement = */ nullptr);
2512
0
2513
0
  uint32_t equalStructs; // Not used, actually.
2514
0
  nsChangeHint childHint =
2515
0
    aOldContext.CalcStyleDifference(newStyle, &equalStructs);
2516
0
  if (!aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
2517
0
    childHint = NS_RemoveSubsumedHints(
2518
0
        childHint, aRestyleState.ChangesHandledFor(aFrame));
2519
0
  }
2520
0
2521
0
  if (childHint) {
2522
0
    if (childHint & nsChangeHint_ReconstructFrame) {
2523
0
      // If we generate a reconstruct here, remove any non-reconstruct hints we
2524
0
      // may have already generated for this content.
2525
0
      aRestyleState.ChangeList().PopChangesForContent(aFrame->GetContent());
2526
0
    }
2527
0
    aRestyleState.ChangeList().AppendChange(
2528
0
        aFrame, aFrame->GetContent(), childHint);
2529
0
  }
2530
0
2531
0
  aFrame->SetAdditionalComputedStyle(aIndex, newStyle);
2532
0
}
2533
2534
static void
2535
UpdateAdditionalComputedStyles(nsIFrame* aFrame,
2536
                              ServoRestyleState& aRestyleState)
2537
0
{
2538
0
  MOZ_ASSERT(aFrame);
2539
0
  MOZ_ASSERT(aFrame->GetContent() && aFrame->GetContent()->IsElement());
2540
0
2541
0
  // FIXME(emilio): Consider adding a bit or something to avoid the initial
2542
0
  // virtual call?
2543
0
  uint32_t index = 0;
2544
0
  while (auto* oldStyle = aFrame->GetAdditionalComputedStyle(index)) {
2545
0
    UpdateOneAdditionalComputedStyle(
2546
0
        aFrame, index++, *oldStyle, aRestyleState);
2547
0
  }
2548
0
}
2549
2550
static void
2551
UpdateFramePseudoElementStyles(nsIFrame* aFrame,
2552
                               ServoRestyleState& aRestyleState)
2553
0
{
2554
0
  if (aFrame->IsFrameOfType(nsIFrame::eBlockFrame)) {
2555
0
    static_cast<nsBlockFrame*>(aFrame)->UpdatePseudoElementStyles(aRestyleState);
2556
0
  } else {
2557
0
    UpdateFirstLetterIfNeeded(aFrame, aRestyleState);
2558
0
  }
2559
0
2560
0
  UpdateBackdropIfNeeded(
2561
0
    aFrame, aRestyleState.StyleSet(), aRestyleState.ChangeList());
2562
0
}
2563
2564
enum class ServoPostTraversalFlags : uint32_t
2565
{
2566
  Empty = 0,
2567
  // Whether parent was restyled.
2568
  ParentWasRestyled = 1 << 0,
2569
  // Skip sending accessibility notifications for all descendants.
2570
  SkipA11yNotifications = 1 << 1,
2571
  // Always send accessibility notifications if the element is shown.
2572
  // The SkipA11yNotifications flag above overrides this flag.
2573
  SendA11yNotificationsIfShown = 1 << 2,
2574
};
2575
2576
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(ServoPostTraversalFlags)
2577
2578
// Send proper accessibility notifications and return post traversal
2579
// flags for kids.
2580
static ServoPostTraversalFlags
2581
SendA11yNotifications(nsPresContext* aPresContext,
2582
                      Element* aElement,
2583
                      ComputedStyle* aOldComputedStyle,
2584
                      ComputedStyle* aNewComputedStyle,
2585
                      ServoPostTraversalFlags aFlags)
2586
0
{
2587
0
  using Flags = ServoPostTraversalFlags;
2588
0
  MOZ_ASSERT(!(aFlags & Flags::SkipA11yNotifications) ||
2589
0
             !(aFlags & Flags::SendA11yNotificationsIfShown),
2590
0
             "The two a11y flags should never be set together");
2591
0
2592
0
#ifdef ACCESSIBILITY
2593
0
  nsAccessibilityService* accService = GetAccService();
2594
0
  if (!accService) {
2595
0
    // If we don't have accessibility service, accessibility is not
2596
0
    // enabled. Just skip everything.
2597
0
    return Flags::Empty;
2598
0
  }
2599
0
  if (aFlags & Flags::SkipA11yNotifications) {
2600
0
    // Propogate the skipping flag to descendants.
2601
0
    return Flags::SkipA11yNotifications;
2602
0
  }
2603
0
2604
0
  bool needsNotify = false;
2605
0
  bool isVisible = aNewComputedStyle->StyleVisibility()->IsVisible();
2606
0
  if (aFlags & Flags::SendA11yNotificationsIfShown) {
2607
0
    if (!isVisible) {
2608
0
      // Propagate the sending-if-shown flag to descendants.
2609
0
      return Flags::SendA11yNotificationsIfShown;
2610
0
    }
2611
0
    // We have asked accessibility service to remove the whole subtree
2612
0
    // of element which becomes invisible from the accessible tree, but
2613
0
    // this element is visible, so we need to add it back.
2614
0
    needsNotify = true;
2615
0
  } else {
2616
0
    // If we shouldn't skip in any case, we need to check whether our
2617
0
    // own visibility has changed.
2618
0
    bool wasVisible = aOldComputedStyle->StyleVisibility()->IsVisible();
2619
0
    needsNotify = wasVisible != isVisible;
2620
0
  }
2621
0
2622
0
  if (needsNotify) {
2623
0
    nsIPresShell* presShell = aPresContext->PresShell();
2624
0
    if (isVisible) {
2625
0
      accService->ContentRangeInserted(
2626
0
        presShell, aElement, aElement->GetNextSibling());
2627
0
      // We are adding the subtree. Accessibility service would handle
2628
0
      // descendants, so we should just skip them from notifying.
2629
0
      return Flags::SkipA11yNotifications;
2630
0
    }
2631
0
    // Remove the subtree of this invisible element, and ask any shown
2632
0
    // descendant to add themselves back.
2633
0
    accService->ContentRemoved(presShell, aElement);
2634
0
    return Flags::SendA11yNotificationsIfShown;
2635
0
  }
2636
0
#endif
2637
0
2638
0
  return Flags::Empty;
2639
0
}
2640
2641
bool
2642
RestyleManager::ProcessPostTraversal(
2643
  Element* aElement,
2644
  ComputedStyle* aParentContext,
2645
  ServoRestyleState& aRestyleState,
2646
  ServoPostTraversalFlags aFlags)
2647
0
{
2648
0
  nsIFrame* styleFrame = nsLayoutUtils::GetStyleFrame(aElement);
2649
0
  nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
2650
0
2651
0
  MOZ_DIAGNOSTIC_ASSERT(aElement->HasServoData(),
2652
0
                        "Element without Servo data on a post-traversal? How?");
2653
0
2654
0
  // NOTE(emilio): This is needed because for table frames the bit is set on the
2655
0
  // table wrapper (which is the primary frame), not on the table itself.
2656
0
  const bool isOutOfFlow =
2657
0
    primaryFrame &&
2658
0
    primaryFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW);
2659
0
2660
0
  // Grab the change hint from Servo.
2661
0
  bool wasRestyled;
2662
0
  nsChangeHint changeHint =
2663
0
    static_cast<nsChangeHint>(Servo_TakeChangeHint(aElement, &wasRestyled));
2664
0
2665
0
  // We should really fix the weird primary frame mapping for image maps
2666
0
  // (bug 135040)...
2667
0
  if (styleFrame && styleFrame->GetContent() != aElement) {
2668
0
    MOZ_ASSERT(static_cast<nsImageFrame*>(do_QueryFrame(styleFrame)));
2669
0
    styleFrame = nullptr;
2670
0
  }
2671
0
2672
0
  // Handle lazy frame construction by posting a reconstruct for any lazily-
2673
0
  // constructed roots.
2674
0
  if (aElement->HasFlag(NODE_NEEDS_FRAME)) {
2675
0
    changeHint |= nsChangeHint_ReconstructFrame;
2676
0
    MOZ_ASSERT(!styleFrame);
2677
0
  }
2678
0
2679
0
  if (styleFrame) {
2680
0
    MOZ_ASSERT(primaryFrame);
2681
0
2682
0
    nsIFrame* maybeAnonBoxChild;
2683
0
    if (isOutOfFlow) {
2684
0
      maybeAnonBoxChild = primaryFrame->GetPlaceholderFrame();
2685
0
    } else {
2686
0
      maybeAnonBoxChild = primaryFrame;
2687
0
      changeHint = NS_RemoveSubsumedHints(
2688
0
        changeHint, aRestyleState.ChangesHandledFor(styleFrame));
2689
0
    }
2690
0
2691
0
    // If the parent wasn't restyled, the styles of our anon box parents won't
2692
0
    // change either.
2693
0
    if ((aFlags & ServoPostTraversalFlags::ParentWasRestyled) &&
2694
0
        maybeAnonBoxChild->ParentIsWrapperAnonBox()) {
2695
0
      aRestyleState.AddPendingWrapperRestyle(
2696
0
        ServoRestyleState::TableAwareParentFor(maybeAnonBoxChild));
2697
0
    }
2698
0
  }
2699
0
2700
0
  // Although we shouldn't generate non-ReconstructFrame hints for elements with
2701
0
  // no frames, we can still get them here if they were explicitly posted by
2702
0
  // PostRestyleEvent, such as a RepaintFrame hint when a :link changes to be
2703
0
  // :visited.  Skip processing these hints if there is no frame.
2704
0
  if ((styleFrame || (changeHint & nsChangeHint_ReconstructFrame)) && changeHint) {
2705
0
    aRestyleState.ChangeList().AppendChange(styleFrame, aElement, changeHint);
2706
0
  }
2707
0
2708
0
  // If our change hint is reconstruct, we delegate to the frame constructor,
2709
0
  // which consumes the new style and expects the old style to be on the frame.
2710
0
  //
2711
0
  // XXXbholley: We should teach the frame constructor how to clear the dirty
2712
0
  // descendants bit to avoid the traversal here.
2713
0
  if (changeHint & nsChangeHint_ReconstructFrame) {
2714
0
    ClearRestyleStateFromSubtree(aElement);
2715
0
    return true;
2716
0
  }
2717
0
2718
0
  // TODO(emilio): We could avoid some refcount traffic here, specially in the
2719
0
  // ComputedStyle case, which uses atomic refcounting.
2720
0
  //
2721
0
  // Hold the ComputedStyle alive, because it could become a dangling pointer
2722
0
  // during the replacement. In practice it's not a huge deal, but better not
2723
0
  // playing with dangling pointers if not needed.
2724
0
  //
2725
0
  // NOTE(emilio): We could keep around the old computed style for display:
2726
0
  // contents elements too, but we don't really need it right now.
2727
0
  RefPtr<ComputedStyle> oldOrDisplayContentsStyle =
2728
0
    styleFrame ? styleFrame->Style() : nullptr;
2729
0
2730
0
  MOZ_ASSERT(!(styleFrame && Servo_Element_IsDisplayContents(aElement)),
2731
0
             "display: contents node has a frame, yet we didn't reframe it"
2732
0
             " above?");
2733
0
  const bool isDisplayContents =
2734
0
    !styleFrame && aElement->HasServoData() &&
2735
0
    Servo_Element_IsDisplayContents(aElement);
2736
0
  if (isDisplayContents) {
2737
0
    oldOrDisplayContentsStyle =
2738
0
      aRestyleState.StyleSet().ResolveServoStyle(*aElement);
2739
0
  }
2740
0
2741
0
  Maybe<ServoRestyleState> thisFrameRestyleState;
2742
0
  if (styleFrame) {
2743
0
    auto type = isOutOfFlow
2744
0
      ? ServoRestyleState::Type::OutOfFlow
2745
0
      : ServoRestyleState::Type::InFlow;
2746
0
2747
0
    thisFrameRestyleState.emplace(*styleFrame, aRestyleState, changeHint, type);
2748
0
  }
2749
0
2750
0
  // We can't really assume as used changes from display: contents elements (or
2751
0
  // other elements without frames).
2752
0
  ServoRestyleState& childrenRestyleState =
2753
0
    thisFrameRestyleState ? *thisFrameRestyleState : aRestyleState;
2754
0
2755
0
  RefPtr<ComputedStyle> upToDateContext =
2756
0
    wasRestyled
2757
0
      ? aRestyleState.StyleSet().ResolveServoStyle(*aElement)
2758
0
      : oldOrDisplayContentsStyle;
2759
0
2760
0
  ServoPostTraversalFlags childrenFlags =
2761
0
    wasRestyled ? ServoPostTraversalFlags::ParentWasRestyled
2762
0
                : ServoPostTraversalFlags::Empty;
2763
0
2764
0
  if (wasRestyled && oldOrDisplayContentsStyle) {
2765
0
    MOZ_ASSERT(styleFrame || isDisplayContents);
2766
0
2767
0
    // Note that upToDateContext could be the same as oldOrDisplayContentsStyle,
2768
0
    // but it doesn't matter, since the only point of it is calling FinishStyle
2769
0
    // on the relevant structs, and those don't matter for display: contents.
2770
0
    upToDateContext->ResolveSameStructsAs(oldOrDisplayContentsStyle);
2771
0
2772
0
    // We want to walk all the continuations here, even the ones with different
2773
0
    // styles.  In practice, the only reason we get continuations with different
2774
0
    // styles here is ::first-line (::first-letter never affects element
2775
0
    // styles).  But in that case, newStyle is the right context for the
2776
0
    // _later_ continuations anyway (the ones not affected by ::first-line), not
2777
0
    // the earlier ones, so there is no point stopping right at the point when
2778
0
    // we'd actually be setting the right ComputedStyle.
2779
0
    //
2780
0
    // This does mean that we may be setting the wrong ComputedStyle on our
2781
0
    // initial continuations; ::first-line fixes that up after the fact.
2782
0
    for (nsIFrame* f = styleFrame; f; f = f->GetNextContinuation()) {
2783
0
      MOZ_ASSERT_IF(f != styleFrame, !f->GetAdditionalComputedStyle(0));
2784
0
      f->SetComputedStyle(upToDateContext);
2785
0
    }
2786
0
2787
0
    if (styleFrame) {
2788
0
      UpdateAdditionalComputedStyles(styleFrame, aRestyleState);
2789
0
    }
2790
0
2791
0
    if (!aElement->GetParent()) {
2792
0
      // This is the root.  Update styles on the viewport as needed.
2793
0
      ViewportFrame* viewport =
2794
0
        do_QueryFrame(mPresContext->PresShell()->GetRootFrame());
2795
0
      if (viewport) {
2796
0
        // NB: The root restyle state, not the one for our children!
2797
0
        viewport->UpdateStyle(aRestyleState);
2798
0
      }
2799
0
    }
2800
0
2801
0
    // Some changes to animations don't affect the computed style and yet still
2802
0
    // require the layer to be updated. For example, pausing an animation via
2803
0
    // the Web Animations API won't affect an element's style but still
2804
0
    // requires to update the animation on the layer.
2805
0
    //
2806
0
    // We can sometimes reach this when the animated style is being removed.
2807
0
    // Since AddLayerChangesForAnimation checks if |styleFrame| has a transform
2808
0
    // style or not, we need to call it *after* setting |newStyle| to
2809
0
    // |styleFrame| to ensure the animated transform has been removed first.
2810
0
    AddLayerChangesForAnimation(
2811
0
      styleFrame, aElement, changeHint, aRestyleState.ChangeList());
2812
0
2813
0
    childrenFlags |= SendA11yNotifications(mPresContext,
2814
0
                                           aElement,
2815
0
                                           oldOrDisplayContentsStyle,
2816
0
                                           upToDateContext,
2817
0
                                           aFlags);
2818
0
  }
2819
0
2820
0
  const bool traverseElementChildren =
2821
0
    aElement->HasAnyOfFlags(Element::kAllServoDescendantBits);
2822
0
  const bool traverseTextChildren =
2823
0
    wasRestyled || aElement->HasFlag(NODE_DESCENDANTS_NEED_FRAMES);
2824
0
  bool recreatedAnyContext = wasRestyled;
2825
0
  if (traverseElementChildren || traverseTextChildren) {
2826
0
    StyleChildrenIterator it(aElement);
2827
0
    TextPostTraversalState textState(*aElement,
2828
0
                                     upToDateContext,
2829
0
                                     isDisplayContents && wasRestyled,
2830
0
                                     childrenRestyleState);
2831
0
    for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
2832
0
      if (traverseElementChildren && n->IsElement()) {
2833
0
        recreatedAnyContext |= ProcessPostTraversal(n->AsElement(),
2834
0
                                                    upToDateContext,
2835
0
                                                    childrenRestyleState,
2836
0
                                                    childrenFlags);
2837
0
      } else if (traverseTextChildren && n->IsText()) {
2838
0
        recreatedAnyContext |= ProcessPostTraversalForText(n, textState,
2839
0
                                                           childrenRestyleState,
2840
0
                                                           childrenFlags);
2841
0
      }
2842
0
    }
2843
0
  }
2844
0
2845
0
  // We want to update frame pseudo-element styles after we've traversed our
2846
0
  // kids, because some of those updates (::first-line/::first-letter) need to
2847
0
  // modify the styles of the kids, and the child traversal above would just
2848
0
  // clobber those modifications.
2849
0
  if (styleFrame) {
2850
0
    if (wasRestyled) {
2851
0
      // Make sure to update anon boxes and pseudo bits after updating text,
2852
0
      // otherwise ProcessPostTraversalForText could clobber first-letter
2853
0
      // styles, for example.
2854
0
      styleFrame->UpdateStyleOfOwnedAnonBoxes(childrenRestyleState);
2855
0
    }
2856
0
    // Process anon box wrapper frames before ::first-line bits, but _after_
2857
0
    // owned anon boxes, since the children wrapper anon boxes could be
2858
0
    // inheriting from our own owned anon boxes.
2859
0
    childrenRestyleState.ProcessWrapperRestyles(styleFrame);
2860
0
    if (wasRestyled) {
2861
0
      UpdateFramePseudoElementStyles(styleFrame, childrenRestyleState);
2862
0
    } else if (traverseElementChildren &&
2863
0
               styleFrame->IsFrameOfType(nsIFrame::eBlockFrame)) {
2864
0
      // Even if we were not restyled, if we're a block with a first-line and
2865
0
      // one of our descendant elements which is on the first line was restyled,
2866
0
      // we need to update the styles of things on the first line, because
2867
0
      // they're wrong now.
2868
0
      //
2869
0
      // FIXME(bz) Could we do better here?  For example, could we keep track of
2870
0
      // frames that are "block with a ::first-line so we could avoid
2871
0
      // IsFrameOfType() and digging about for the first-line frame if not?
2872
0
      // Could we keep track of whether the element children we actually restyle
2873
0
      // are affected by first-line?  Something else?  Bug 1385443 tracks making
2874
0
      // this better.
2875
0
      nsIFrame* firstLineFrame =
2876
0
        static_cast<nsBlockFrame*>(styleFrame)->GetFirstLineFrame();
2877
0
      if (firstLineFrame) {
2878
0
        for (nsIFrame* kid : firstLineFrame->PrincipalChildList()) {
2879
0
          ReparentComputedStyleForFirstLine(kid);
2880
0
        }
2881
0
      }
2882
0
    }
2883
0
  }
2884
0
2885
0
  aElement->UnsetFlags(Element::kAllServoDescendantBits);
2886
0
  return recreatedAnyContext;
2887
0
}
2888
2889
bool
2890
RestyleManager::ProcessPostTraversalForText(
2891
    nsIContent* aTextNode,
2892
    TextPostTraversalState& aPostTraversalState,
2893
    ServoRestyleState& aRestyleState,
2894
    ServoPostTraversalFlags aFlags)
2895
0
{
2896
0
  // Handle lazy frame construction.
2897
0
  if (aTextNode->HasFlag(NODE_NEEDS_FRAME)) {
2898
0
    aPostTraversalState.ChangeList().AppendChange(
2899
0
      nullptr, aTextNode, nsChangeHint_ReconstructFrame);
2900
0
    return true;
2901
0
  }
2902
0
2903
0
  // Handle restyle.
2904
0
  nsIFrame* primaryFrame = aTextNode->GetPrimaryFrame();
2905
0
  if (!primaryFrame) {
2906
0
    return false;
2907
0
  }
2908
0
2909
0
  // If the parent wasn't restyled, the styles of our anon box parents won't
2910
0
  // change either.
2911
0
  if ((aFlags & ServoPostTraversalFlags::ParentWasRestyled) &&
2912
0
      primaryFrame->ParentIsWrapperAnonBox()) {
2913
0
    aRestyleState.AddPendingWrapperRestyle(
2914
0
      ServoRestyleState::TableAwareParentFor(primaryFrame));
2915
0
  }
2916
0
2917
0
  ComputedStyle& newStyle = aPostTraversalState.ComputeStyle(aTextNode);
2918
0
  aPostTraversalState.ComputeHintIfNeeded(aTextNode, primaryFrame, newStyle);
2919
0
2920
0
  // We want to walk all the continuations here, even the ones with different
2921
0
  // styles.  In practice, the only reasons we get continuations with different
2922
0
  // styles are ::first-line and ::first-letter.  But in those cases,
2923
0
  // newStyle is the right context for the _later_ continuations anyway (the
2924
0
  // ones not affected by ::first-line/::first-letter), not the earlier ones,
2925
0
  // so there is no point stopping right at the point when we'd actually be
2926
0
  // setting the right ComputedStyle.
2927
0
  //
2928
0
  // This does mean that we may be setting the wrong ComputedStyle on our
2929
0
  // initial continuations; ::first-line/::first-letter fix that up after the
2930
0
  // fact.
2931
0
  for (nsIFrame* f = primaryFrame; f; f = f->GetNextContinuation()) {
2932
0
    f->SetComputedStyle(&newStyle);
2933
0
  }
2934
0
2935
0
  return true;
2936
0
}
2937
2938
void
2939
RestyleManager::ClearSnapshots()
2940
0
{
2941
0
  for (auto iter = mSnapshots.Iter(); !iter.Done(); iter.Next()) {
2942
0
    iter.Key()->UnsetFlags(ELEMENT_HAS_SNAPSHOT | ELEMENT_HANDLED_SNAPSHOT);
2943
0
    iter.Remove();
2944
0
  }
2945
0
}
2946
2947
ServoElementSnapshot&
2948
RestyleManager::SnapshotFor(Element& aElement)
2949
0
{
2950
0
  MOZ_ASSERT(!mInStyleRefresh);
2951
0
2952
0
  // NOTE(emilio): We can handle snapshots from a one-off restyle of those that
2953
0
  // we do to restyle stuff for reconstruction, for example.
2954
0
  //
2955
0
  // It seems to be the case that we always flush in between that happens and
2956
0
  // the next attribute change, so we can assert that we haven't handled the
2957
0
  // snapshot here yet. If this assertion didn't hold, we'd need to unset that
2958
0
  // flag from here too.
2959
0
  //
2960
0
  // Can't wait to make ProcessPendingRestyles the only entry-point for styling,
2961
0
  // so this becomes much easier to reason about. Today is not that day though.
2962
0
  MOZ_ASSERT(aElement.HasServoData());
2963
0
  MOZ_ASSERT(!aElement.HasFlag(ELEMENT_HANDLED_SNAPSHOT));
2964
0
2965
0
  ServoElementSnapshot* snapshot = mSnapshots.LookupOrAdd(&aElement, aElement);
2966
0
  aElement.SetFlags(ELEMENT_HAS_SNAPSHOT);
2967
0
2968
0
  // Now that we have a snapshot, make sure a restyle is triggered.
2969
0
  aElement.NoteDirtyForServo();
2970
0
  return *snapshot;
2971
0
}
2972
2973
void
2974
RestyleManager::DoProcessPendingRestyles(ServoTraversalFlags aFlags)
2975
0
{
2976
0
  nsPresContext* presContext = PresContext();
2977
0
2978
0
  MOZ_ASSERT(presContext->Document(), "No document?  Pshaw!");
2979
0
  // FIXME(emilio): In the "flush animations" case, ideally, we should only
2980
0
  // recascade animation styles running on the compositor, so we shouldn't care
2981
0
  // about other styles, or new rules that apply to the page...
2982
0
  //
2983
0
  // However, that's not true as of right now, see bug 1388031 and bug 1388692.
2984
0
  MOZ_ASSERT((aFlags & ServoTraversalFlags::FlushThrottledAnimations) ||
2985
0
             !presContext->HasPendingMediaQueryUpdates(),
2986
0
             "Someone forgot to update media queries?");
2987
0
  MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript(), "Missing a script blocker!");
2988
0
  MOZ_ASSERT(!mInStyleRefresh, "Reentrant call?");
2989
0
2990
0
2991
0
  if (MOZ_UNLIKELY(!presContext->PresShell()->DidInitialize())) {
2992
0
    // PresShell::FlushPendingNotifications doesn't early-return in the case
2993
0
    // where the PresShell hasn't yet been initialized (and therefore we haven't
2994
0
    // yet done the initial style traversal of the DOM tree). We should arguably
2995
0
    // fix up the callers and assert against this case, but we just detect and
2996
0
    // handle it for now.
2997
0
    return;
2998
0
  }
2999
0
3000
0
  // Create a AnimationsWithDestroyedFrame during restyling process to
3001
0
  // stop animations and transitions on elements that have no frame at the end
3002
0
  // of the restyling process.
3003
0
  AnimationsWithDestroyedFrame animationsWithDestroyedFrame(this);
3004
0
3005
0
  ServoStyleSet* styleSet = StyleSet();
3006
0
  nsIDocument* doc = presContext->Document();
3007
0
3008
0
  // Ensure the refresh driver is active during traversal to avoid mutating
3009
0
  // mActiveTimer and mMostRecentRefresh time.
3010
0
  presContext->RefreshDriver()->MostRecentRefresh();
3011
0
3012
0
3013
0
  // Perform the Servo traversal, and the post-traversal if required. We do this
3014
0
  // in a loop because certain rare paths in the frame constructor (like
3015
0
  // uninstalling XBL bindings) can trigger additional style validations.
3016
0
  mInStyleRefresh = true;
3017
0
  if (mHaveNonAnimationRestyles) {
3018
0
    ++mAnimationGeneration;
3019
0
  }
3020
0
3021
0
  if (mRestyleForCSSRuleChanges) {
3022
0
    aFlags |= ServoTraversalFlags::ForCSSRuleChanges;
3023
0
  }
3024
0
3025
0
  while (styleSet->StyleDocument(aFlags)) {
3026
0
    ClearSnapshots();
3027
0
3028
0
    nsStyleChangeList currentChanges;
3029
0
    bool anyStyleChanged = false;
3030
0
3031
0
    // Recreate styles , and queue up change hints (which also handle lazy frame
3032
0
    // construction).
3033
0
    {
3034
0
      AutoRestyleTimelineMarker marker(presContext->GetDocShell(), false);
3035
0
      DocumentStyleRootIterator iter(doc->GetServoRestyleRoot());
3036
0
      while (Element* root = iter.GetNextStyleRoot()) {
3037
0
        nsTArray<nsIFrame*> wrappersToRestyle;
3038
0
        ServoRestyleState state(*styleSet, currentChanges, wrappersToRestyle);
3039
0
        ServoPostTraversalFlags flags = ServoPostTraversalFlags::Empty;
3040
0
        anyStyleChanged |= ProcessPostTraversal(root, nullptr, state, flags);
3041
0
      }
3042
0
    }
3043
0
3044
0
    doc->ClearServoRestyleRoot();
3045
0
3046
0
    // Process the change hints.
3047
0
    //
3048
0
    // Unfortunately, the frame constructor can generate new change hints while
3049
0
    // processing existing ones. We redirect those into a secondary queue and
3050
0
    // iterate until there's nothing left.
3051
0
    {
3052
0
      AutoTimelineMarker marker(
3053
0
        presContext->GetDocShell(), "StylesApplyChanges");
3054
0
      ReentrantChangeList newChanges;
3055
0
      mReentrantChanges = &newChanges;
3056
0
      while (!currentChanges.IsEmpty()) {
3057
0
        ProcessRestyledFrames(currentChanges);
3058
0
        MOZ_ASSERT(currentChanges.IsEmpty());
3059
0
        for (ReentrantChange& change: newChanges)  {
3060
0
          if (!(change.mHint & nsChangeHint_ReconstructFrame) &&
3061
0
              !change.mContent->GetPrimaryFrame()) {
3062
0
            // SVG Elements post change hints without ensuring that the primary
3063
0
            // frame will be there after that (see bug 1366142).
3064
0
            //
3065
0
            // Just ignore those, since we can't really process them.
3066
0
            continue;
3067
0
          }
3068
0
          currentChanges.AppendChange(change.mContent->GetPrimaryFrame(),
3069
0
                                      change.mContent, change.mHint);
3070
0
        }
3071
0
        newChanges.Clear();
3072
0
      }
3073
0
      mReentrantChanges = nullptr;
3074
0
    }
3075
0
3076
0
    if (anyStyleChanged) {
3077
0
      // Maybe no styles changed when:
3078
0
      //
3079
0
      //  * Only explicit change hints were posted in the first place.
3080
0
      //  * When an attribute or state change in the content happens not to need
3081
0
      //    a restyle after all.
3082
0
      //
3083
0
      // In any case, we don't need to increment the restyle generation in that
3084
0
      // case.
3085
0
      IncrementRestyleGeneration();
3086
0
    }
3087
0
  }
3088
0
3089
0
  doc->ClearServoRestyleRoot();
3090
0
3091
0
  FlushOverflowChangedTracker();
3092
0
3093
0
  ClearSnapshots();
3094
0
  styleSet->AssertTreeIsClean();
3095
0
  mHaveNonAnimationRestyles = false;
3096
0
  mRestyleForCSSRuleChanges = false;
3097
0
  mInStyleRefresh = false;
3098
0
3099
0
  // Now that everything has settled, see if we have enough free rule nodes in
3100
0
  // the tree to warrant sweeping them.
3101
0
  styleSet->MaybeGCRuleTree();
3102
0
3103
0
  // Note: We are in the scope of |animationsWithDestroyedFrame|, so
3104
0
  //       |mAnimationsWithDestroyedFrame| is still valid.
3105
0
  MOZ_ASSERT(mAnimationsWithDestroyedFrame);
3106
0
  mAnimationsWithDestroyedFrame->StopAnimationsForElementsWithoutFrames();
3107
0
}
3108
3109
#ifdef DEBUG
3110
static void
3111
VerifyFlatTree(const nsIContent& aContent)
3112
{
3113
  StyleChildrenIterator iter(&aContent);
3114
3115
  for (auto* content = iter.GetNextChild();
3116
       content;
3117
       content = iter.GetNextChild()) {
3118
    MOZ_ASSERT(content->GetFlattenedTreeParentNodeForStyle() == &aContent);
3119
    MOZ_ASSERT(!content->IsActiveChildrenElement());
3120
    VerifyFlatTree(*content);
3121
  }
3122
}
3123
#endif
3124
3125
void
3126
RestyleManager::ProcessPendingRestyles()
3127
0
{
3128
#ifdef DEBUG
3129
  if (auto* root = mPresContext->Document()->GetRootElement()) {
3130
    VerifyFlatTree(*root);
3131
  }
3132
#endif
3133
3134
0
  DoProcessPendingRestyles(ServoTraversalFlags::Empty);
3135
0
}
3136
3137
void
3138
RestyleManager::ProcessAllPendingAttributeAndStateInvalidations()
3139
0
{
3140
0
  if (mSnapshots.IsEmpty()) {
3141
0
    return;
3142
0
  }
3143
0
  for (auto iter = mSnapshots.Iter(); !iter.Done(); iter.Next()) {
3144
0
    // Servo data for the element might have been dropped. (e.g. by removing
3145
0
    // from its document)
3146
0
    if (iter.Key()->HasFlag(ELEMENT_HAS_SNAPSHOT)) {
3147
0
      Servo_ProcessInvalidations(StyleSet()->RawSet(), iter.Key(), &mSnapshots);
3148
0
    }
3149
0
  }
3150
0
  ClearSnapshots();
3151
0
}
3152
3153
bool
3154
RestyleManager::HasPendingRestyleAncestor(Element* aElement) const
3155
0
{
3156
0
  return Servo_HasPendingRestyleAncestor(aElement);
3157
0
}
3158
3159
void
3160
RestyleManager::UpdateOnlyAnimationStyles()
3161
0
{
3162
0
  bool doCSS = PresContext()->EffectCompositor()->HasPendingStyleUpdates();
3163
0
  if (!doCSS) {
3164
0
    return;
3165
0
  }
3166
0
3167
0
  DoProcessPendingRestyles(ServoTraversalFlags::FlushThrottledAnimations);
3168
0
}
3169
3170
void
3171
RestyleManager::ContentStateChanged(nsIContent* aContent,
3172
                                    EventStates aChangedBits)
3173
0
{
3174
0
  MOZ_ASSERT(!mInStyleRefresh);
3175
0
3176
0
  if (!aContent->IsElement()) {
3177
0
    return;
3178
0
  }
3179
0
3180
0
  Element& element = *aContent->AsElement();
3181
0
  if (!element.HasServoData()) {
3182
0
    return;
3183
0
  }
3184
0
3185
0
  const EventStates kVisitedAndUnvisited =
3186
0
    NS_EVENT_STATE_VISITED | NS_EVENT_STATE_UNVISITED;
3187
0
  // NOTE: We want to return ASAP for visitedness changes, but we don't want to
3188
0
  // mess up the situation where the element became a link or stopped being one.
3189
0
  if (aChangedBits.HasAllStates(kVisitedAndUnvisited) &&
3190
0
      !Gecko_VisitedStylesEnabled(element.OwnerDoc())) {
3191
0
    aChangedBits &= ~kVisitedAndUnvisited;
3192
0
    if (aChangedBits.IsEmpty()) {
3193
0
      return;
3194
0
    }
3195
0
  }
3196
0
3197
0
  nsChangeHint changeHint;
3198
0
  ContentStateChangedInternal(element, aChangedBits, &changeHint);
3199
0
3200
0
  // Don't bother taking a snapshot if no rules depend on these state bits.
3201
0
  //
3202
0
  // We always take a snapshot for the LTR/RTL event states, since Servo doesn't
3203
0
  // track those bits in the same way, and we know that :dir() rules are always
3204
0
  // present in UA style sheets.
3205
0
  //
3206
0
  // FIXME(emilio): Doesn't this early-return drop the change hint on the floor?
3207
0
  // Should it?
3208
0
  if (!aChangedBits.HasAtLeastOneOfStates(DIRECTION_STATES) &&
3209
0
      !StyleSet()->HasStateDependency(element, aChangedBits)) {
3210
0
    return;
3211
0
  }
3212
0
3213
0
  ServoElementSnapshot& snapshot = SnapshotFor(element);
3214
0
  EventStates previousState = element.StyleState() ^ aChangedBits;
3215
0
  snapshot.AddState(previousState);
3216
0
3217
0
  if (changeHint) {
3218
0
    Servo_NoteExplicitHints(&element, nsRestyleHint(0), changeHint);
3219
0
  }
3220
0
3221
0
  // Assuming we need to invalidate cached style in getComputedStyle for
3222
0
  // undisplayed elements, since we don't know if it is needed.
3223
0
  IncrementUndisplayedRestyleGeneration();
3224
0
}
3225
3226
static inline bool
3227
AttributeInfluencesOtherPseudoClassState(const Element& aElement,
3228
                                         const nsAtom* aAttribute)
3229
0
{
3230
0
  // We must record some state for :-moz-browser-frame and
3231
0
  // :-moz-table-border-nonzero.
3232
0
  if (aAttribute == nsGkAtoms::mozbrowser) {
3233
0
    return aElement.IsAnyOfHTMLElements(nsGkAtoms::iframe, nsGkAtoms::frame);
3234
0
  }
3235
0
3236
0
  if (aAttribute == nsGkAtoms::border) {
3237
0
    return aElement.IsHTMLElement(nsGkAtoms::table);
3238
0
  }
3239
0
3240
0
  return false;
3241
0
}
3242
3243
static inline bool
3244
NeedToRecordAttrChange(const ServoStyleSet& aStyleSet,
3245
                       const Element& aElement,
3246
                       int32_t aNameSpaceID,
3247
                       nsAtom* aAttribute,
3248
                       bool* aInfluencesOtherPseudoClassState)
3249
0
{
3250
0
  *aInfluencesOtherPseudoClassState =
3251
0
    AttributeInfluencesOtherPseudoClassState(aElement, aAttribute);
3252
0
3253
0
  // If the attribute influences one of the pseudo-classes that are backed by
3254
0
  // attributes, we just record it.
3255
0
  if (*aInfluencesOtherPseudoClassState) {
3256
0
    return true;
3257
0
  }
3258
0
3259
0
  // We assume that id and class attributes are used in class/id selectors, and
3260
0
  // thus record them.
3261
0
  //
3262
0
  // TODO(emilio): We keep a filter of the ids in use somewhere in the StyleSet,
3263
0
  // presumably we could try to filter the old and new id, but it's not clear
3264
0
  // it's worth it.
3265
0
  if (aNameSpaceID == kNameSpaceID_None &&
3266
0
      (aAttribute == nsGkAtoms::id || aAttribute == nsGkAtoms::_class)) {
3267
0
    return true;
3268
0
  }
3269
0
3270
0
  // We always record lang="", even though we force a subtree restyle when it
3271
0
  // changes, since it can change how its siblings match :lang(..) due to
3272
0
  // selectors like :lang(..) + div.
3273
0
  if (aAttribute == nsGkAtoms::lang) {
3274
0
    return true;
3275
0
  }
3276
0
3277
0
  // Otherwise, just record the attribute change if a selector in the page may
3278
0
  // reference it from an attribute selector.
3279
0
  return aStyleSet.MightHaveAttributeDependency(aElement, aAttribute);
3280
0
}
3281
3282
void
3283
RestyleManager::AttributeWillChange(Element* aElement,
3284
                                    int32_t aNameSpaceID,
3285
                                    nsAtom* aAttribute,
3286
                                    int32_t aModType,
3287
                                    const nsAttrValue* aNewValue)
3288
0
{
3289
0
  TakeSnapshotForAttributeChange(*aElement, aNameSpaceID, aAttribute);
3290
0
}
3291
3292
void
3293
RestyleManager::ClassAttributeWillBeChangedBySMIL(Element* aElement)
3294
0
{
3295
0
  TakeSnapshotForAttributeChange(*aElement, kNameSpaceID_None,
3296
0
                                 nsGkAtoms::_class);
3297
0
}
3298
3299
void
3300
RestyleManager::TakeSnapshotForAttributeChange(Element& aElement,
3301
                                               int32_t aNameSpaceID,
3302
                                               nsAtom* aAttribute)
3303
0
{
3304
0
  MOZ_ASSERT(!mInStyleRefresh);
3305
0
3306
0
  if (!aElement.HasServoData()) {
3307
0
    return;
3308
0
  }
3309
0
3310
0
  bool influencesOtherPseudoClassState;
3311
0
  if (!NeedToRecordAttrChange(*StyleSet(),
3312
0
                              aElement,
3313
0
                              aNameSpaceID,
3314
0
                              aAttribute,
3315
0
                              &influencesOtherPseudoClassState)) {
3316
0
    return;
3317
0
  }
3318
0
3319
0
  // We cannot tell if the attribute change will affect the styles of
3320
0
  // undisplayed elements, because we don't actually restyle those elements
3321
0
  // during the restyle traversal. So just assume that the attribute change can
3322
0
  // cause the style to change.
3323
0
  IncrementUndisplayedRestyleGeneration();
3324
0
3325
0
  // Some other random attribute changes may also affect the transitions,
3326
0
  // so we also set this true here.
3327
0
  mHaveNonAnimationRestyles = true;
3328
0
3329
0
  ServoElementSnapshot& snapshot = SnapshotFor(aElement);
3330
0
  snapshot.AddAttrs(aElement, aNameSpaceID, aAttribute);
3331
0
3332
0
  if (influencesOtherPseudoClassState) {
3333
0
    snapshot.AddOtherPseudoClassState(aElement);
3334
0
  }
3335
0
}
3336
3337
// For some attribute changes we must restyle the whole subtree:
3338
//
3339
// * <td> is affected by the cellpadding on its ancestor table
3340
// * lwtheme and lwthemetextcolor on root element of XUL document
3341
//   affects all descendants due to :-moz-lwtheme* pseudo-classes
3342
// * lang="" and xml:lang="" can affect all descendants due to :lang()
3343
//
3344
static inline bool
3345
AttributeChangeRequiresSubtreeRestyle(const Element& aElement, nsAtom* aAttr)
3346
0
{
3347
0
  if (aAttr == nsGkAtoms::cellpadding) {
3348
0
    return aElement.IsHTMLElement(nsGkAtoms::table);
3349
0
  }
3350
0
  if (aAttr == nsGkAtoms::lwtheme ||
3351
0
      aAttr == nsGkAtoms::lwthemetextcolor) {
3352
0
    return aElement.GetNameSpaceID() == kNameSpaceID_XUL &&
3353
0
      &aElement == aElement.OwnerDoc()->GetRootElement();
3354
0
  }
3355
0
3356
0
  return aAttr == nsGkAtoms::lang;
3357
0
}
3358
3359
void
3360
RestyleManager::AttributeChanged(Element* aElement,
3361
                                 int32_t aNameSpaceID,
3362
                                 nsAtom* aAttribute,
3363
                                 int32_t aModType,
3364
                                 const nsAttrValue* aOldValue)
3365
0
{
3366
0
  MOZ_ASSERT(!mInStyleRefresh);
3367
0
3368
0
  auto changeHint = nsChangeHint(0);
3369
0
  auto restyleHint = nsRestyleHint(0);
3370
0
3371
0
  changeHint |= aElement->GetAttributeChangeHint(aAttribute, aModType);
3372
0
3373
0
  if (aAttribute == nsGkAtoms::style) {
3374
0
    restyleHint |= eRestyle_StyleAttribute;
3375
0
  } else if (AttributeChangeRequiresSubtreeRestyle(*aElement, aAttribute)) {
3376
0
    restyleHint |= eRestyle_Subtree;
3377
0
  } else if (aElement->IsAttributeMapped(aAttribute)) {
3378
0
    restyleHint |= eRestyle_Self;
3379
0
  }
3380
0
3381
0
  if (nsIFrame* primaryFrame = aElement->GetPrimaryFrame()) {
3382
0
    // See if we have appearance information for a theme.
3383
0
    const nsStyleDisplay* disp = primaryFrame->StyleDisplay();
3384
0
    if (disp->HasAppearance()) {
3385
0
      nsITheme* theme = PresContext()->GetTheme();
3386
0
      if (theme && theme->ThemeSupportsWidget(PresContext(), primaryFrame,
3387
0
                                              disp->mAppearance)) {
3388
0
        bool repaint = false;
3389
0
        theme->WidgetStateChanged(primaryFrame, disp->mAppearance,
3390
0
                                  aAttribute, &repaint, aOldValue);
3391
0
        if (repaint) {
3392
0
          changeHint |= nsChangeHint_RepaintFrame;
3393
0
        }
3394
0
      }
3395
0
    }
3396
0
3397
0
    primaryFrame->AttributeChanged(aNameSpaceID, aAttribute, aModType);
3398
0
  }
3399
0
3400
0
  if (restyleHint || changeHint) {
3401
0
    Servo_NoteExplicitHints(aElement, restyleHint, changeHint);
3402
0
  }
3403
0
3404
0
  if (restyleHint) {
3405
0
    // Assuming we need to invalidate cached style in getComputedStyle for
3406
0
    // undisplayed elements, since we don't know if it is needed.
3407
0
    IncrementUndisplayedRestyleGeneration();
3408
0
3409
0
    // If we change attributes, we have to mark this to be true, so we will
3410
0
    // increase the animation generation for the new created transition if any.
3411
0
    mHaveNonAnimationRestyles = true;
3412
0
  }
3413
0
}
3414
3415
void
3416
RestyleManager::ReparentComputedStyleForFirstLine(nsIFrame* aFrame)
3417
0
{
3418
0
  // This is only called when moving frames in or out of the first-line
3419
0
  // pseudo-element (or one of its descendants).  We can't say much about
3420
0
  // aFrame's ancestors, unfortunately (e.g. during a dynamic insert into
3421
0
  // something inside an inline-block on the first line the ancestors could be
3422
0
  // totally arbitrary), but we will definitely find a line frame on the
3423
0
  // ancestor chain.  Note that the lineframe may not actually be the one that
3424
0
  // corresponds to ::first-line; when we're moving _out_ of the ::first-line it
3425
0
  // will be one of the continuations instead.
3426
#ifdef DEBUG
3427
  {
3428
    nsIFrame* f = aFrame->GetParent();
3429
    while (f && !f->IsLineFrame()) {
3430
      f = f->GetParent();
3431
    }
3432
    MOZ_ASSERT(f, "Must have found a first-line frame");
3433
  }
3434
#endif
3435
3436
0
  DoReparentComputedStyleForFirstLine(aFrame, *StyleSet());
3437
0
}
3438
3439
void
3440
RestyleManager::DoReparentComputedStyleForFirstLine(nsIFrame* aFrame,
3441
                                                    ServoStyleSet& aStyleSet)
3442
0
{
3443
0
  if (aFrame->IsBackdropFrame()) {
3444
0
    // Style context of backdrop frame has no parent style, and thus we do not
3445
0
    // need to reparent it.
3446
0
    return;
3447
0
  }
3448
0
3449
0
  if (aFrame->IsPlaceholderFrame()) {
3450
0
    // Also reparent the out-of-flow and all its continuations.  We're doing
3451
0
    // this to match Gecko for now, but it's not clear that this behavior is
3452
0
    // correct per spec.  It's certainly pretty odd for out-of-flows whose
3453
0
    // containing block is not within the first line.
3454
0
    //
3455
0
    // Right now we're somewhat inconsistent in this testcase:
3456
0
    //
3457
0
    //  <style>
3458
0
    //    div { color: orange; clear: left; }
3459
0
    //    div::first-line { color: blue; }
3460
0
    //  </style>
3461
0
    //  <div>
3462
0
    //    <span style="float: left">What color is this text?</span>
3463
0
    //  </div>
3464
0
    //  <div>
3465
0
    //    <span><span style="float: left">What color is this text?</span></span>
3466
0
    //  </div>
3467
0
    //
3468
0
    // We make the first float orange and the second float blue.  On the other
3469
0
    // hand, if the float were within an inline-block that was on the first
3470
0
    // line, arguably it _should_ inherit from the ::first-line...
3471
0
    nsIFrame* outOfFlow =
3472
0
      nsPlaceholderFrame::GetRealFrameForPlaceholder(aFrame);
3473
0
    MOZ_ASSERT(outOfFlow, "no out-of-flow frame");
3474
0
    for (; outOfFlow; outOfFlow = outOfFlow->GetNextContinuation()) {
3475
0
      DoReparentComputedStyleForFirstLine(outOfFlow, aStyleSet);
3476
0
    }
3477
0
  }
3478
0
3479
0
  // FIXME(emilio): This is the only caller of GetParentComputedStyle, let's try
3480
0
  // to remove it?
3481
0
  nsIFrame* providerFrame;
3482
0
  ComputedStyle* newParentStyle =
3483
0
    aFrame->GetParentComputedStyle(&providerFrame);
3484
0
  // If our provider is our child, we want to reparent it first, because we
3485
0
  // inherit style from it.
3486
0
  bool isChild = providerFrame && providerFrame->GetParent() == aFrame;
3487
0
  nsIFrame* providerChild = nullptr;
3488
0
  if (isChild) {
3489
0
    DoReparentComputedStyleForFirstLine(providerFrame, aStyleSet);
3490
0
    // Get the style again after ReparentComputedStyle() which might have
3491
0
    // changed it.
3492
0
    newParentStyle = providerFrame->Style();
3493
0
    providerChild = providerFrame;
3494
0
    MOZ_ASSERT(!providerFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW),
3495
0
               "Out of flow provider?");
3496
0
  }
3497
0
3498
0
  if (!newParentStyle) {
3499
0
    // No need to do anything here for this frame, but we should still reparent
3500
0
    // its descendants, because those may have styles that inherit from the
3501
0
    // parent of this frame (e.g. non-anonymous columns in an anonymous
3502
0
    // colgroup).
3503
0
    MOZ_ASSERT(aFrame->Style()->IsNonInheritingAnonBox(),
3504
0
               "Why did this frame not end up with a parent context?");
3505
0
    ReparentFrameDescendants(aFrame, providerChild, aStyleSet);
3506
0
    return;
3507
0
  }
3508
0
3509
0
  bool isElement = aFrame->GetContent()->IsElement();
3510
0
3511
0
  // We probably don't want to initiate transitions from ReparentComputedStyle,
3512
0
  // since we call it during frame construction rather than in response to
3513
0
  // dynamic changes.
3514
0
  // Also see the comment at the start of
3515
0
  // nsTransitionManager::ConsiderInitiatingTransition.
3516
0
  //
3517
0
  // We don't try to do the fancy copying from previous continuations that
3518
0
  // GeckoRestyleManager does here, because that relies on knowing the parents
3519
0
  // of ComputedStyles, and we don't know those.
3520
0
  ComputedStyle* oldStyle = aFrame->Style();
3521
0
  Element* ourElement =
3522
0
    oldStyle->GetPseudoType() == CSSPseudoElementType::NotPseudo &&
3523
0
    isElement ?
3524
0
      aFrame->GetContent()->AsElement() :
3525
0
      nullptr;
3526
0
  ComputedStyle* newParent = newParentStyle;
3527
0
3528
0
  ComputedStyle* newParentIgnoringFirstLine;
3529
0
  if (newParent->GetPseudoType() == CSSPseudoElementType::firstLine) {
3530
0
    MOZ_ASSERT(providerFrame && providerFrame->GetParent()->
3531
0
               IsFrameOfType(nsIFrame::eBlockFrame),
3532
0
               "How could we get a ::first-line parent style without having "
3533
0
               "a ::first-line provider frame?");
3534
0
    // If newParent is a ::first-line style, get the parent blockframe, and then
3535
0
    // correct it for our pseudo as needed (e.g. stepping out of anon boxes).
3536
0
    // Use the resulting style for the "parent style ignoring ::first-line".
3537
0
    nsIFrame* blockFrame = providerFrame->GetParent();
3538
0
    nsIFrame* correctedFrame =
3539
0
      nsFrame::CorrectStyleParentFrame(blockFrame, oldStyle->GetPseudo());
3540
0
    newParentIgnoringFirstLine = correctedFrame->Style();
3541
0
  } else {
3542
0
    newParentIgnoringFirstLine = newParent;
3543
0
  }
3544
0
3545
0
  if (!providerFrame) {
3546
0
    // No providerFrame means we inherited from a display:contents thing.  Our
3547
0
    // layout parent style is the style of our nearest ancestor frame.  But we have
3548
0
    // to be careful to do that with our placeholder, not with us, if we're out of
3549
0
    // flow.
3550
0
    if (aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
3551
0
      aFrame->FirstContinuation()->GetPlaceholderFrame()->GetLayoutParentStyleForOutOfFlow(&providerFrame);
3552
0
    } else {
3553
0
      providerFrame = nsFrame::CorrectStyleParentFrame(aFrame->GetParent(),
3554
0
                                                       oldStyle->GetPseudo());
3555
0
    }
3556
0
  }
3557
0
  ComputedStyle* layoutParent = providerFrame->Style();
3558
0
3559
0
  RefPtr<ComputedStyle> newStyle =
3560
0
    aStyleSet.ReparentComputedStyle(oldStyle,
3561
0
                                    newParent,
3562
0
                                    newParentIgnoringFirstLine,
3563
0
                                    layoutParent,
3564
0
                                    ourElement);
3565
0
  aFrame->SetComputedStyle(newStyle);
3566
0
3567
0
  // This logic somewhat mirrors the logic in
3568
0
  // RestyleManager::ProcessPostTraversal.
3569
0
  if (isElement) {
3570
0
    // We can't use UpdateAdditionalComputedStyles as-is because it needs a
3571
0
    // ServoRestyleState and maintaining one of those during a _frametree_
3572
0
    // traversal is basically impossible.
3573
0
    uint32_t index = 0;
3574
0
    while (auto* oldAdditionalStyle = aFrame->GetAdditionalComputedStyle(index)) {
3575
0
      RefPtr<ComputedStyle> newAdditionalContext =
3576
0
        aStyleSet.ReparentComputedStyle(oldAdditionalStyle,
3577
0
                                        newStyle,
3578
0
                                        newStyle,
3579
0
                                        newStyle,
3580
0
                                        nullptr);
3581
0
      aFrame->SetAdditionalComputedStyle(index, newAdditionalContext);
3582
0
      ++index;
3583
0
    }
3584
0
  }
3585
0
3586
0
  // Generally, owned anon boxes are our descendants.  The only exceptions are
3587
0
  // tables (for the table wrapper) and inline frames (for the block part of the
3588
0
  // block-in-inline split).  We're going to update our descendants when looping
3589
0
  // over kids, and we don't want to update the block part of a block-in-inline
3590
0
  // split if the inline is on the first line but the block is not (and if the
3591
0
  // block is, it's the child of something else on the first line and will get
3592
0
  // updated as a child).  And given how this method ends up getting called, if
3593
0
  // we reach here for a table frame, we are already in the middle of
3594
0
  // reparenting the table wrapper frame.  So no need to
3595
0
  // UpdateStyleOfOwnedAnonBoxes() here.
3596
0
3597
0
  ReparentFrameDescendants(aFrame, providerChild, aStyleSet);
3598
0
3599
0
  // We do not need to do the equivalent of UpdateFramePseudoElementStyles,
3600
0
  // because those are handled by our descendant walk.
3601
0
}
3602
3603
void
3604
RestyleManager::ReparentFrameDescendants(nsIFrame* aFrame,
3605
                                         nsIFrame* aProviderChild,
3606
                                         ServoStyleSet& aStyleSet)
3607
0
{
3608
0
  if (aFrame->GetContent()->IsElement() &&
3609
0
      !aFrame->GetContent()->AsElement()->HasServoData()) {
3610
0
    // We're getting into a display: none subtree, avoid reparenting into stuff
3611
0
    // that is going to go away anyway in seconds.
3612
0
    return;
3613
0
  }
3614
0
  nsIFrame::ChildListIterator lists(aFrame);
3615
0
  for (; !lists.IsDone(); lists.Next()) {
3616
0
    for (nsIFrame* child : lists.CurrentList()) {
3617
0
      // only do frames that are in flow
3618
0
      if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
3619
0
          child != aProviderChild) {
3620
0
        DoReparentComputedStyleForFirstLine(child, aStyleSet);
3621
0
      }
3622
0
    }
3623
0
  }
3624
0
}
3625
3626
} // namespace mozilla