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 /* a presentation of a document, part 1 */
8
9 #include "nsPresContext.h"
10 #include "nsPresContextInlines.h"
11
12 #include "mozilla/ArrayUtils.h"
13 #if defined(MOZ_WIDGET_ANDROID)
14 # include "mozilla/AsyncEventDispatcher.h"
15 #endif
16 #include "mozilla/CycleCollectedJSContext.h"
17 #include "mozilla/DebugOnly.h"
18 #include "mozilla/Encoding.h"
19 #include "mozilla/EventDispatcher.h"
20 #include "mozilla/EventStateManager.h"
21 #include "mozilla/PresShell.h"
22 #include "mozilla/PresShellInlines.h"
23 #include "mozilla/dom/ContentParent.h"
24
25 #include "base/basictypes.h"
26
27 #include "nsCOMPtr.h"
28 #include "nsCSSFrameConstructor.h"
29 #include "nsDocShell.h"
30 #include "nsIContentViewer.h"
31 #include "nsPIDOMWindow.h"
32 #include "mozilla/ServoStyleSet.h"
33 #include "mozilla/MediaFeatureChange.h"
34 #include "nsIContent.h"
35 #include "nsIFrame.h"
36 #include "mozilla/dom/BrowsingContext.h"
37 #include "mozilla/dom/Document.h"
38 #include "mozilla/dom/DocumentInlines.h"
39 #include "nsIPrintSettings.h"
40 #include "nsLanguageAtomService.h"
41 #include "mozilla/LookAndFeel.h"
42 #include "nsIInterfaceRequestorUtils.h"
43 #include "nsHTMLDocument.h"
44 #include "nsIWeakReferenceUtils.h"
45 #include "nsThreadUtils.h"
46 #include "nsLayoutUtils.h"
47 #include "nsViewManager.h"
48 #include "mozilla/RestyleManager.h"
49 #include "SurfaceCacheUtils.h"
50 #include "gfxPlatform.h"
51 #include "nsFontFaceLoader.h"
52 #include "mozilla/AnimationEventDispatcher.h"
53 #include "mozilla/EffectCompositor.h"
54 #include "mozilla/EventListenerManager.h"
55 #include "prenv.h"
56 #include "nsTransitionManager.h"
57 #include "nsAnimationManager.h"
58 #include "CounterStyleManager.h"
59 #include "mozilla/MemoryReporting.h"
60 #include "mozilla/dom/Element.h"
61 #include "nsIMessageManager.h"
62 #include "mozilla/dom/HTMLBodyElement.h"
63 #include "mozilla/dom/MediaQueryList.h"
64 #include "mozilla/SMILAnimationController.h"
65 #include "mozilla/css/ImageLoader.h"
66 #include "mozilla/dom/PBrowserParent.h"
67 #include "mozilla/dom/BrowserChild.h"
68 #include "mozilla/dom/BrowserParent.h"
69 #include "mozilla/StaticPresData.h"
70 #include "nsRefreshDriver.h"
71 #include "Layers.h"
72 #include "LayerUserData.h"
73 #include "ClientLayerManager.h"
74 #include "mozilla/dom/NotifyPaintEvent.h"
75
76 #include "nsFrameLoader.h"
77 #include "nsContentUtils.h"
78 #include "nsPIWindowRoot.h"
79 #include "mozilla/Preferences.h"
80 #include "gfxTextRun.h"
81 #include "nsFontFaceUtils.h"
82 #include "mozilla/GlobalStyleSheetCache.h"
83 #include "mozilla/ServoBindings.h"
84 #include "mozilla/StaticPrefs_layout.h"
85 #include "mozilla/StaticPrefs_zoom.h"
86 #include "mozilla/StyleSheet.h"
87 #include "mozilla/StyleSheetInlines.h"
88 #include "mozilla/Telemetry.h"
89 #include "mozilla/dom/Performance.h"
90 #include "mozilla/dom/PerformanceTiming.h"
91 #include "mozilla/dom/PerformancePaintTiming.h"
92 #include "mozilla/layers/APZThreadUtils.h"
93 #include "MobileViewportManager.h"
94 #include "mozilla/dom/ImageTracker.h"
95
96 // Needed for Start/Stop of Image Animation
97 #include "imgIContainer.h"
98 #include "nsIImageLoadingContent.h"
99
100 #include "nsBidiUtils.h"
101 #include "nsServiceManagerUtils.h"
102 #include "nsBidi.h"
103
104 #include "mozilla/dom/URL.h"
105 #include "mozilla/ServoCSSParser.h"
106
107 using namespace mozilla;
108 using namespace mozilla::dom;
109 using namespace mozilla::gfx;
110 using namespace mozilla::layers;
111
112 uint8_t gNotifySubDocInvalidationData;
113
114 /**
115 * Layer UserData for ContainerLayers that want to be notified
116 * of local invalidations of them and their descendant layers.
117 * Pass a callback to ComputeDifferences to have these called.
118 */
119 class ContainerLayerPresContext : public LayerUserData {
120 public:
121 nsPresContext* mPresContext;
122 };
123
IsDOMPaintEventPending()124 bool nsPresContext::IsDOMPaintEventPending() {
125 if (!mTransactions.IsEmpty()) {
126 return true;
127 }
128
129 nsRootPresContext* drpc = GetRootPresContext();
130 if (drpc && drpc->mRefreshDriver->ViewManagerFlushIsPending()) {
131 // Since we're promising that there will be a MozAfterPaint event
132 // fired, we record an empty invalidation in case display list
133 // invalidation doesn't invalidate anything further.
134 NotifyInvalidation(drpc->mRefreshDriver->LastTransactionId().Next(),
135 nsRect(0, 0, 0, 0));
136 return true;
137 }
138 return false;
139 }
140
ForceReflowForFontInfoUpdate()141 void nsPresContext::ForceReflowForFontInfoUpdate() {
142 // Flush the device context's font cache, so that we won't risk getting
143 // stale nsFontMetrics objects from it.
144 DeviceContext()->FlushFontCache();
145
146 // If there's a user font set, discard any src:local() faces it may have
147 // loaded because their font entries may no longer be valid.
148 if (Document()->GetFonts()) {
149 Document()->GetFonts()->GetUserFontSet()->ForgetLocalFaces();
150 }
151
152 // We can trigger reflow by pretending a font.* preference has changed;
153 // this is the same mechanism as gfxPlatform::ForceGlobalReflow() uses
154 // if new fonts are installed during the session, for example.
155 PreferenceChanged("font.internaluseonly.changed");
156 }
157
IsVisualCharset(NotNull<const Encoding * > aCharset)158 static bool IsVisualCharset(NotNull<const Encoding*> aCharset) {
159 return aCharset == ISO_8859_8_ENCODING;
160 }
161
nsPresContext(dom::Document * aDocument,nsPresContextType aType)162 nsPresContext::nsPresContext(dom::Document* aDocument, nsPresContextType aType)
163 : mPresShell(nullptr),
164 mDocument(aDocument),
165 mMedium(aType == eContext_Galley ? nsGkAtoms::screen : nsGkAtoms::print),
166 mSystemFontScale(1.0),
167 mTextZoom(1.0),
168 mEffectiveTextZoom(1.0),
169 mFullZoom(1.0),
170 mLastFontInflationScreenSize(gfxSize(-1.0, -1.0)),
171 mCurAppUnitsPerDevPixel(0),
172 mAutoQualityMinFontSizePixelsPref(0),
173 mDynamicToolbarMaxHeight(0),
174 mDynamicToolbarHeight(0),
175 mPageSize(-1, -1),
176 mPageScale(0.0),
177 mPPScale(1.0f),
178 mViewportScrollOverrideElement(nullptr),
179 mElementsRestyled(0),
180 mFramesConstructed(0),
181 mFramesReflowed(0),
182 mChangeHintForPrefChange(nsChangeHint(0)),
183 mInterruptChecksToSkip(0),
184 mNextFrameRateMultiplier(0),
185 mViewportScrollStyles(StyleOverflow::Auto, StyleOverflow::Auto),
186 // mImageAnimationMode is initialised below, in constructor body
187 mImageAnimationModePref(imgIContainer::kNormalAnimMode),
188 mType(aType),
189 mInflationDisabledForShrinkWrap(false),
190 mInteractionTimeEnabled(true),
191 mHasPendingInterrupt(false),
192 mHasEverBuiltInvisibleText(false),
193 mPendingInterruptFromTest(false),
194 mInterruptsEnabled(false),
195 mSendAfterPaintToContent(false),
196 mDrawImageBackground(true), // always draw the background
197 mDrawColorBackground(true),
198 // mNeverAnimate is initialised below, in constructor body
199 mPaginated(aType != eContext_Galley),
200 mCanPaginatedScroll(false),
201 mDoScaledTwips(true),
202 mIsRootPaginatedDocument(false),
203 mPrefBidiDirection(false),
204 mPrefScrollbarSide(0),
205 mPendingThemeChanged(false),
206 mPendingThemeChangeKind(0),
207 mPendingUIResolutionChanged(false),
208 mPostedPrefChangedRunnable(false),
209 mIsGlyph(false),
210 mUsesExChUnits(false),
211 mCounterStylesDirty(true),
212 mFontFeatureValuesDirty(true),
213 mSuppressResizeReflow(false),
214 mIsVisual(false),
215 mPaintFlashing(false),
216 mPaintFlashingInitialized(false),
217 mHasWarnedAboutPositionedTableParts(false),
218 mHasWarnedAboutTooLargeDashedOrDottedRadius(false),
219 mQuirkSheetAdded(false),
220 mHadNonBlankPaint(false),
221 mHadContentfulPaint(false),
222 mHadNonTickContentfulPaint(false),
223 mHadContentfulPaintComposite(false)
224 #ifdef DEBUG
225 ,
226 mInitialized(false)
227 #endif
228 {
229 #ifdef DEBUG
230 PodZero(&mLayoutPhaseCount);
231 #endif
232
233 if (!IsDynamic()) {
234 mImageAnimationMode = imgIContainer::kDontAnimMode;
235 mNeverAnimate = true;
236 } else {
237 mImageAnimationMode = imgIContainer::kNormalAnimMode;
238 mNeverAnimate = false;
239 }
240 NS_ASSERTION(mDocument, "Null document");
241
242 // if text perf logging enabled, init stats struct
243 if (MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_textperf), LogLevel::Warning)) {
244 mTextPerf = MakeUnique<gfxTextPerfMetrics>();
245 }
246
247 if (Preferences::GetBool(GFX_MISSING_FONTS_NOTIFY_PREF)) {
248 mMissingFonts = MakeUnique<gfxMissingFontRecorder>();
249 }
250
251 // TODO: check whether this is too expensive to do for all pages;
252 // maybe we could throttle and only collect stats on 1% of page-loads,
253 // or something like that.
254 mFontStats = MakeUnique<FontMatchingStats>();
255
256 if (StaticPrefs::layout_dynamic_toolbar_max_height() > 0) {
257 // The pref for dynamic toolbar max height is only used in reftests so it's
258 // fine to set here.
259 mDynamicToolbarMaxHeight = StaticPrefs::layout_dynamic_toolbar_max_height();
260 }
261 }
262
263 static const char* gExactCallbackPrefs[] = {
264 "browser.underline_anchors",
265 "browser.anchor_color",
266 "browser.active_color",
267 "browser.visited_color",
268 "image.animation_mode",
269 "dom.send_after_paint_to_content",
270 "layout.css.dpi",
271 "layout.css.devPixelsPerPx",
272 "nglayout.debug.paint_flashing",
273 "nglayout.debug.paint_flashing_chrome",
274 "intl.accept_languages",
275 "dom.meta-viewport.enabled",
276 nullptr,
277 };
278
279 static const char* gPrefixCallbackPrefs[] = {
280 "font.", "browser.display.", "browser.viewport.",
281 "bidi.", "gfx.font_rendering.", nullptr,
282 };
283
Destroy()284 void nsPresContext::Destroy() {
285 if (mEventManager) {
286 // unclear if these are needed, but can't hurt
287 mEventManager->NotifyDestroyPresContext(this);
288 mEventManager->SetPresContext(nullptr);
289 mEventManager = nullptr;
290 }
291
292 // Unregister preference callbacks
293 Preferences::UnregisterPrefixCallbacks(nsPresContext::PreferenceChanged,
294 gPrefixCallbackPrefs, this);
295 Preferences::UnregisterCallbacks(nsPresContext::PreferenceChanged,
296 gExactCallbackPrefs, this);
297
298 mRefreshDriver = nullptr;
299 MOZ_ASSERT(mManagedPostRefreshObservers.IsEmpty());
300 }
301
~nsPresContext()302 nsPresContext::~nsPresContext() {
303 MOZ_ASSERT(!mPresShell, "Presshell forgot to clear our mPresShell pointer");
304 DetachPresShell();
305
306 Destroy();
307 }
308
309 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsPresContext)
NS_INTERFACE_MAP_ENTRY(nsISupports)310 NS_INTERFACE_MAP_ENTRY(nsISupports)
311 NS_INTERFACE_MAP_END
312
313 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsPresContext)
314 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(nsPresContext, LastRelease())
315
316 void nsPresContext::LastRelease() {
317 if (mMissingFonts) {
318 mMissingFonts->Clear();
319 }
320 }
321
322 NS_IMPL_CYCLE_COLLECTION_CLASS(nsPresContext)
323
324 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsPresContext)
325 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnimationEventDispatcher);
326 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument);
327 // NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mDeviceContext); // not xpcom
328 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEffectCompositor);
329 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEventManager);
330 // NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mLanguage); // an atom
331
332 // NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTheme); // a service
333 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrintSettings);
334 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
335
336 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsPresContext)
337 NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnimationEventDispatcher);
338 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument);
339 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDeviceContext); // worth bothering?
340 NS_IMPL_CYCLE_COLLECTION_UNLINK(mEffectCompositor);
341 // NS_RELEASE(tmp->mLanguage); // an atom
342 // NS_IMPL_CYCLE_COLLECTION_UNLINK(mTheme); // a service
343 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrintSettings);
344 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_PTR
345
346 tmp->Destroy();
347 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
348
349 // Set to true when LookAndFeelChanged needs to be called. This is used
350 // because the look and feel is a service, so there's no need to notify it from
351 // more than one prescontext.
352 static bool sLookAndFeelChanged;
353
354 // Set to true when ThemeChanged needs to be called on mTheme. This is used
355 // because mTheme is a service, so there's no need to notify it from more than
356 // one prescontext.
357 static bool sThemeChanged;
358
IsChrome() const359 bool nsPresContext::IsChrome() const {
360 return Document()->IsInChromeDocShell();
361 }
362
GetUserPreferences()363 void nsPresContext::GetUserPreferences() {
364 if (!GetPresShell()) {
365 // No presshell means nothing to do here. We'll do this when we
366 // get a presshell.
367 return;
368 }
369
370 mAutoQualityMinFontSizePixelsPref =
371 Preferences::GetInt("browser.display.auto_quality_min_font_size");
372
373 PreferenceSheet::EnsureInitialized();
374
375 mSendAfterPaintToContent = Preferences::GetBool(
376 "dom.send_after_paint_to_content", mSendAfterPaintToContent);
377
378 mPrefScrollbarSide = Preferences::GetInt("layout.scrollbar.side");
379
380 Document()->SetMayNeedFontPrefsUpdate();
381
382 // * image animation
383 nsAutoCString animatePref;
384 Preferences::GetCString("image.animation_mode", animatePref);
385 if (animatePref.EqualsLiteral("normal"))
386 mImageAnimationModePref = imgIContainer::kNormalAnimMode;
387 else if (animatePref.EqualsLiteral("none"))
388 mImageAnimationModePref = imgIContainer::kDontAnimMode;
389 else if (animatePref.EqualsLiteral("once"))
390 mImageAnimationModePref = imgIContainer::kLoopOnceAnimMode;
391 else // dynamic change to invalid value should act like it does initially
392 mImageAnimationModePref = imgIContainer::kNormalAnimMode;
393
394 uint32_t bidiOptions = GetBidi();
395
396 int32_t prefInt = Preferences::GetInt(IBMBIDI_TEXTDIRECTION_STR,
397 GET_BIDI_OPTION_DIRECTION(bidiOptions));
398 SET_BIDI_OPTION_DIRECTION(bidiOptions, prefInt);
399 mPrefBidiDirection = prefInt;
400
401 prefInt = Preferences::GetInt(IBMBIDI_TEXTTYPE_STR,
402 GET_BIDI_OPTION_TEXTTYPE(bidiOptions));
403 SET_BIDI_OPTION_TEXTTYPE(bidiOptions, prefInt);
404
405 prefInt = Preferences::GetInt(IBMBIDI_NUMERAL_STR,
406 GET_BIDI_OPTION_NUMERAL(bidiOptions));
407 SET_BIDI_OPTION_NUMERAL(bidiOptions, prefInt);
408
409 // We don't need to force reflow: either we are initializing a new
410 // prescontext or we are being called from UpdateAfterPreferencesChanged()
411 // which triggers a reflow anyway.
412 SetBidi(bidiOptions);
413 }
414
InvalidatePaintedLayers()415 void nsPresContext::InvalidatePaintedLayers() {
416 if (!mPresShell) {
417 return;
418 }
419 if (nsIFrame* rootFrame = mPresShell->GetRootFrame()) {
420 // FrameLayerBuilder caches invalidation-related values that depend on the
421 // appunits-per-dev-pixel ratio, so ensure that all PaintedLayer drawing
422 // is completely flushed.
423 rootFrame->InvalidateFrameSubtree();
424 }
425 }
426
AppUnitsPerDevPixelChanged()427 void nsPresContext::AppUnitsPerDevPixelChanged() {
428 int32_t oldAppUnitsPerDevPixel = mCurAppUnitsPerDevPixel;
429
430 InvalidatePaintedLayers();
431
432 if (mDeviceContext) {
433 mDeviceContext->FlushFontCache();
434 }
435
436 MediaFeatureValuesChanged(
437 {RestyleHint::RecascadeSubtree(), NS_STYLE_HINT_REFLOW,
438 MediaFeatureChangeReason::ResolutionChange},
439 MediaFeatureChangePropagation::JustThisDocument);
440
441 mCurAppUnitsPerDevPixel = mDeviceContext->AppUnitsPerDevPixel();
442
443 // Recompute the size for vh units since it's changed by the dynamic toolbar
444 // max height which is stored in screen coord.
445 if (IsRootContentDocumentCrossProcess()) {
446 AdjustSizeForViewportUnits();
447 }
448
449 // nsSubDocumentFrame uses a AppUnitsPerDevPixel difference between parent and
450 // child document to determine if it needs to build a nsDisplayZoom item. So
451 // if we that changes then we need to invalidate the subdoc frame so that
452 // item gets created/removed.
453 if (mPresShell) {
454 if (nsIFrame* frame = mPresShell->GetRootFrame()) {
455 frame = nsLayoutUtils::GetCrossDocParentFrameInProcess(frame);
456 if (frame) {
457 int32_t parentAPD = frame->PresContext()->AppUnitsPerDevPixel();
458 if ((parentAPD == oldAppUnitsPerDevPixel) !=
459 (parentAPD == mCurAppUnitsPerDevPixel)) {
460 frame->InvalidateFrame();
461 }
462 }
463 }
464 }
465
466 // We would also have to look at all of our child subdocuments but the
467 // InvalidatePaintedLayers call above calls InvalidateFrameSubtree which
468 // would invalidate all subdocument frames already.
469 }
470
471 // static
PreferenceChanged(const char * aPrefName,void * aSelf)472 void nsPresContext::PreferenceChanged(const char* aPrefName, void* aSelf) {
473 static_cast<nsPresContext*>(aSelf)->PreferenceChanged(aPrefName);
474 }
475
PreferenceChanged(const char * aPrefName)476 void nsPresContext::PreferenceChanged(const char* aPrefName) {
477 nsDependentCString prefName(aPrefName);
478 if (prefName.EqualsLiteral("layout.css.dpi") ||
479 prefName.EqualsLiteral("layout.css.devPixelsPerPx")) {
480 int32_t oldAppUnitsPerDevPixel = mDeviceContext->AppUnitsPerDevPixel();
481 // We need to assume the DPI changes, since `mDeviceContext` is shared with
482 // other documents, and we'd need to save the return value of the first call
483 // for all of them.
484 Unused << mDeviceContext->CheckDPIChange();
485 if (mPresShell) {
486 OwningNonNull<mozilla::PresShell> presShell(*mPresShell);
487 // Re-fetch the view manager's window dimensions in case there's a
488 // deferred resize which hasn't affected our mVisibleArea yet
489 nscoord oldWidthAppUnits, oldHeightAppUnits;
490 RefPtr<nsViewManager> vm = presShell->GetViewManager();
491 if (!vm) {
492 return;
493 }
494 vm->GetWindowDimensions(&oldWidthAppUnits, &oldHeightAppUnits);
495 float oldWidthDevPixels = oldWidthAppUnits / oldAppUnitsPerDevPixel;
496 float oldHeightDevPixels = oldHeightAppUnits / oldAppUnitsPerDevPixel;
497
498 AppUnitsPerDevPixelChanged();
499
500 nscoord width = NSToCoordRound(oldWidthDevPixels * AppUnitsPerDevPixel());
501 nscoord height =
502 NSToCoordRound(oldHeightDevPixels * AppUnitsPerDevPixel());
503 vm->SetWindowDimensions(width, height);
504 }
505 return;
506 }
507
508 if (StringBeginsWith(prefName, "browser.viewport."_ns) ||
509 StringBeginsWith(prefName, "font.size.inflation."_ns) ||
510 prefName.EqualsLiteral("dom.meta-viewport.enabled")) {
511 if (mPresShell) {
512 mPresShell->MaybeReflowForInflationScreenSizeChange();
513 }
514 }
515
516 // Changing any of these potentially changes the value of @media
517 // (prefers-contrast).
518 if (prefName.EqualsLiteral("layout.css.prefers-contrast.enabled") ||
519 prefName.EqualsLiteral("browser.display.document_color_use") ||
520 prefName.EqualsLiteral("privacy.resistFingerprinting") ||
521 prefName.EqualsLiteral("browser.display.foreground_color") ||
522 prefName.EqualsLiteral("browser.display.background_color")) {
523 MediaFeatureValuesChanged({MediaFeatureChangeReason::PreferenceChange},
524 MediaFeatureChangePropagation::JustThisDocument);
525 }
526 if (prefName.EqualsLiteral(GFX_MISSING_FONTS_NOTIFY_PREF)) {
527 if (Preferences::GetBool(GFX_MISSING_FONTS_NOTIFY_PREF)) {
528 if (!mMissingFonts) {
529 mMissingFonts = MakeUnique<gfxMissingFontRecorder>();
530 // trigger reflow to detect missing fonts on the current page
531 mChangeHintForPrefChange |= NS_STYLE_HINT_REFLOW;
532 }
533 } else {
534 if (mMissingFonts) {
535 mMissingFonts->Clear();
536 }
537 mMissingFonts = nullptr;
538 }
539 }
540 if (prefName.EqualsLiteral("font.internaluseonly.changed") &&
541 !IsPrintingOrPrintPreview()) {
542 // If there's a change to the font list, we generally need to reconstruct
543 // frames, as the existing frames may be referring to fonts that are no
544 // longer available. But in the case of a static-clone document used for
545 // printing or print-preview, this is undesirable because the nsPrintJob
546 // is holding weak refs to frames that will get blown away unexpectedly by
547 // this reconstruction. So the prescontext for a print/preview doc ignores
548 // the font-list update.
549 //
550 // This means the print document may still be using cached fonts that are
551 // no longer present in the font list, but that should be safe given that
552 // all the required font instances have already been created, so it won't
553 // be depending on access to the font-list entries.
554 //
555 // XXX Actually, I think it's probably a bad idea to do *any* restyling of
556 // print documents in response to pref changes. We could be in the middle
557 // of printing the document, and reflowing all the frames might cause some
558 // kind of unwanted mid-document discontinuity.
559 mChangeHintForPrefChange |= nsChangeHint_ReconstructFrame;
560 } else if (StringBeginsWith(prefName, "font."_ns) ||
561 // Changes to font family preferences don't change anything in the
562 // computed style data, so the style system won't generate a reflow
563 // hint for us. We need to do that manually.
564 prefName.EqualsLiteral("intl.accept_languages") ||
565 // Changes to bidi prefs need to trigger a reflow (see bug 443629)
566 StringBeginsWith(prefName, "bidi."_ns) ||
567 // Changes to font_rendering prefs need to trigger a reflow
568 StringBeginsWith(prefName, "gfx.font_rendering."_ns)) {
569 mChangeHintForPrefChange |= NS_STYLE_HINT_REFLOW;
570 }
571
572 // We will end up calling InvalidatePreferenceSheets one from each pres
573 // context, but all it's doing is clearing its cached sheet pointers, so it
574 // won't be wastefully recreating the sheet multiple times.
575 //
576 // The first pres context that has its pref changed runnable called will
577 // be the one to cause the reconstruction of the pref style sheet.
578 GlobalStyleSheetCache::InvalidatePreferenceSheets();
579 PreferenceSheet::Refresh();
580 DispatchPrefChangedRunnableIfNeeded();
581
582 if (prefName.EqualsLiteral("nglayout.debug.paint_flashing") ||
583 prefName.EqualsLiteral("nglayout.debug.paint_flashing_chrome")) {
584 mPaintFlashingInitialized = false;
585 return;
586 }
587 }
588
DispatchPrefChangedRunnableIfNeeded()589 void nsPresContext::DispatchPrefChangedRunnableIfNeeded() {
590 if (mPostedPrefChangedRunnable) {
591 return;
592 }
593
594 nsCOMPtr<nsIRunnable> runnable =
595 NewRunnableMethod("nsPresContext::UpdateAfterPreferencesChanged", this,
596 &nsPresContext::UpdateAfterPreferencesChanged);
597 nsresult rv = Document()->Dispatch(TaskCategory::Other, runnable.forget());
598 if (NS_SUCCEEDED(rv)) {
599 mPostedPrefChangedRunnable = true;
600 }
601 }
602
UpdateAfterPreferencesChanged()603 void nsPresContext::UpdateAfterPreferencesChanged() {
604 mPostedPrefChangedRunnable = false;
605 if (!mPresShell) {
606 return;
607 }
608
609 if (mDocument->IsInChromeDocShell()) {
610 // FIXME(emilio): Do really all these prefs not affect chrome docs? I
611 // suspect we should move this check somewhere else.
612 return;
613 }
614
615 StaticPresData::Get()->InvalidateFontPrefs();
616
617 // Initialize our state from the user preferences
618 GetUserPreferences();
619
620 // update the presShell: tell it to set the preference style rules up
621 mPresShell->UpdatePreferenceStyles();
622
623 InvalidatePaintedLayers();
624 mDeviceContext->FlushFontCache();
625
626 // Preferences require rerunning selector matching because we rebuild
627 // the pref style sheet for some preference changes.
628 RebuildAllStyleData(mChangeHintForPrefChange, RestyleHint::RestyleSubtree());
629 mChangeHintForPrefChange = nsChangeHint(0);
630 }
631
Init(nsDeviceContext * aDeviceContext)632 nsresult nsPresContext::Init(nsDeviceContext* aDeviceContext) {
633 NS_ASSERTION(!mInitialized, "attempt to reinit pres context");
634 NS_ENSURE_ARG(aDeviceContext);
635
636 mDeviceContext = aDeviceContext;
637
638 // In certain rare cases (such as changing page mode), we tear down layout
639 // state and re-initialize a new prescontext for a document. Given that we
640 // hang style state off the DOM, we detect that re-initialization case and
641 // lazily drop the servo data. We don't do this eagerly during layout teardown
642 // because that would incur an extra whole-tree traversal that's unnecessary
643 // most of the time.
644 //
645 // FIXME(emilio): I'm pretty sure this doesn't happen after bug 1414999.
646 Element* root = mDocument->GetRootElement();
647 if (root && root->HasServoData()) {
648 RestyleManager::ClearServoDataFromSubtree(root);
649 }
650
651 if (mDeviceContext->SetFullZoom(mFullZoom)) {
652 mDeviceContext->FlushFontCache();
653 }
654 mCurAppUnitsPerDevPixel = mDeviceContext->AppUnitsPerDevPixel();
655
656 mEventManager = new mozilla::EventStateManager();
657
658 mAnimationEventDispatcher = new mozilla::AnimationEventDispatcher(this);
659 mEffectCompositor = new mozilla::EffectCompositor(this);
660 mTransitionManager = MakeUnique<nsTransitionManager>(this);
661 mAnimationManager = MakeUnique<nsAnimationManager>(this);
662
663 if (mDocument->GetDisplayDocument()) {
664 NS_ASSERTION(mDocument->GetDisplayDocument()->GetPresContext(),
665 "Why are we being initialized?");
666 mRefreshDriver =
667 mDocument->GetDisplayDocument()->GetPresContext()->RefreshDriver();
668 } else {
669 dom::Document* parent = mDocument->GetInProcessParentDocument();
670 // Unfortunately, sometimes |parent| here has no presshell because
671 // printing screws up things. Assert that in other cases it does,
672 // but whenever the shell is null just fall back on using our own
673 // refresh driver.
674 NS_ASSERTION(
675 !parent || mDocument->IsStaticDocument() || parent->GetPresShell(),
676 "How did we end up with a presshell if our parent doesn't "
677 "have one?");
678 if (parent && parent->GetPresContext()) {
679 // XXX the document can change in AttachPresShell, does this work?
680 dom::BrowsingContext* browsingContext = mDocument->GetBrowsingContext();
681 if (browsingContext && !browsingContext->IsTop()) {
682 Element* containingElement = mDocument->GetEmbedderElement();
683 if (!containingElement->IsXULElement() ||
684 !containingElement->HasAttr(kNameSpaceID_None,
685 nsGkAtoms::forceOwnRefreshDriver)) {
686 mRefreshDriver = parent->GetPresContext()->RefreshDriver();
687 }
688 }
689 }
690
691 if (!mRefreshDriver) {
692 mRefreshDriver = new nsRefreshDriver(this);
693 }
694 }
695
696 // Register callbacks so we're notified when the preferences change
697 Preferences::RegisterPrefixCallbacks(nsPresContext::PreferenceChanged,
698 gPrefixCallbackPrefs, this);
699 Preferences::RegisterCallbacks(nsPresContext::PreferenceChanged,
700 gExactCallbackPrefs, this);
701
702 nsresult rv = mEventManager->Init();
703 NS_ENSURE_SUCCESS(rv, rv);
704
705 mEventManager->SetPresContext(this);
706
707 #if defined(MOZ_WIDGET_ANDROID)
708 if (IsRootContentDocumentCrossProcess() &&
709 MOZ_LIKELY(
710 !Preferences::HasUserValue("layout.dynamic-toolbar-max-height"))) {
711 if (BrowserChild* browserChild =
712 BrowserChild::GetFrom(mDocument->GetDocShell())) {
713 mDynamicToolbarMaxHeight = browserChild->GetDynamicToolbarMaxHeight();
714 mDynamicToolbarHeight = mDynamicToolbarMaxHeight;
715 }
716 }
717 #endif
718
719 #ifdef DEBUG
720 mInitialized = true;
721 #endif
722
723 return NS_OK;
724 }
725
726 // Note: We don't hold a reference on the shell; it has a reference to
727 // us
AttachPresShell(mozilla::PresShell * aPresShell)728 void nsPresContext::AttachPresShell(mozilla::PresShell* aPresShell) {
729 MOZ_ASSERT(!mPresShell);
730 mPresShell = aPresShell;
731
732 mRestyleManager = MakeUnique<mozilla::RestyleManager>(this);
733
734 // Since CounterStyleManager is also the name of a method of
735 // nsPresContext, it is necessary to prefix the class with the mozilla
736 // namespace here.
737 mCounterStyleManager = new mozilla::CounterStyleManager(this);
738
739 dom::Document* doc = mPresShell->GetDocument();
740 MOZ_ASSERT(doc);
741 // Have to update PresContext's mDocument before calling any other methods.
742 mDocument = doc;
743 // Initialize our state from the user preferences, now that we
744 // have a presshell, and hence a document.
745 GetUserPreferences();
746
747 nsIURI* docURI = doc->GetDocumentURI();
748
749 if (IsDynamic() && docURI) {
750 if (!docURI->SchemeIs("chrome") && !docURI->SchemeIs("resource"))
751 mImageAnimationMode = mImageAnimationModePref;
752 else
753 mImageAnimationMode = imgIContainer::kNormalAnimMode;
754 }
755
756 UpdateCharSet(doc->GetDocumentCharacterSet());
757 }
758
RecomputeBrowsingContextDependentData()759 void nsPresContext::RecomputeBrowsingContextDependentData() {
760 MOZ_ASSERT(mDocument);
761 dom::Document* doc = mDocument;
762 // Resource documents inherit all this state from their display document.
763 while (dom::Document* outer = doc->GetDisplayDocument()) {
764 doc = outer;
765 }
766 auto* browsingContext = doc->GetBrowsingContext();
767 if (!browsingContext) {
768 // This can legitimately happen for e.g. SVG images. Those just get scaled
769 // as a result of the zoom on the embedder document so it doesn't really
770 // matter... Medium also doesn't affect those.
771 return;
772 }
773 SetFullZoom(browsingContext->FullZoom());
774 SetTextZoom(browsingContext->TextZoom());
775 SetOverrideDPPX(browsingContext->OverrideDPPX());
776 if (doc == mDocument) {
777 // Medium doesn't apply to resource documents, etc.
778 auto* top = browsingContext->Top();
779 RefPtr<nsAtom> mediumToEmulate;
780 if (MOZ_UNLIKELY(!top->GetMediumOverride().IsEmpty())) {
781 nsAutoString lower;
782 nsContentUtils::ASCIIToLower(top->GetMediumOverride(), lower);
783 mediumToEmulate = NS_Atomize(lower);
784 }
785 EmulateMedium(mediumToEmulate);
786 }
787
788 mDocument->EnumerateExternalResources([](dom::Document& aSubResource) {
789 if (nsPresContext* subResourcePc = aSubResource.GetPresContext()) {
790 subResourcePc->RecomputeBrowsingContextDependentData();
791 }
792 return CallState::Continue;
793 });
794 }
795
DetachPresShell()796 void nsPresContext::DetachPresShell() {
797 // The counter style manager's destructor needs to deallocate with the
798 // presshell arena. Disconnect it before nulling out the shell.
799 //
800 // XXXbholley: Given recent refactorings, it probably makes more sense to
801 // just null our mPresShell at the bottom of this function. I'm leaving it
802 // this way to preserve the old ordering, but I doubt anything would break.
803 if (mCounterStyleManager) {
804 mCounterStyleManager->Disconnect();
805 mCounterStyleManager = nullptr;
806 }
807
808 mPresShell = nullptr;
809
810 if (mAnimationEventDispatcher) {
811 mAnimationEventDispatcher->Disconnect();
812 mAnimationEventDispatcher = nullptr;
813 }
814 if (mEffectCompositor) {
815 mEffectCompositor->Disconnect();
816 mEffectCompositor = nullptr;
817 }
818 if (mTransitionManager) {
819 mTransitionManager->Disconnect();
820 mTransitionManager = nullptr;
821 }
822 if (mAnimationManager) {
823 mAnimationManager->Disconnect();
824 mAnimationManager = nullptr;
825 }
826 if (mRestyleManager) {
827 mRestyleManager->Disconnect();
828 mRestyleManager = nullptr;
829 }
830 if (mRefreshDriver && mRefreshDriver->GetPresContext() == this) {
831 mRefreshDriver->Disconnect();
832 // Can't null out the refresh driver here.
833 }
834 }
835
DocumentCharSetChanged(NotNull<const Encoding * > aCharSet)836 void nsPresContext::DocumentCharSetChanged(NotNull<const Encoding*> aCharSet) {
837 UpdateCharSet(aCharSet);
838 mDeviceContext->FlushFontCache();
839
840 // If a document contains one or more <script> elements, frame construction
841 // might happen earlier than the UpdateCharSet(), so we need to restyle
842 // descendants to make their style data up-to-date.
843 //
844 // FIXME(emilio): Revisit whether this is true after bug 1438911.
845 RebuildAllStyleData(NS_STYLE_HINT_REFLOW, RestyleHint::RecascadeSubtree());
846 }
847
UpdateCharSet(NotNull<const Encoding * > aCharSet)848 void nsPresContext::UpdateCharSet(NotNull<const Encoding*> aCharSet) {
849 switch (GET_BIDI_OPTION_TEXTTYPE(GetBidi())) {
850 case IBMBIDI_TEXTTYPE_LOGICAL:
851 SetVisualMode(false);
852 break;
853
854 case IBMBIDI_TEXTTYPE_VISUAL:
855 SetVisualMode(true);
856 break;
857
858 case IBMBIDI_TEXTTYPE_CHARSET:
859 default:
860 SetVisualMode(IsVisualCharset(aCharSet));
861 }
862 }
863
GetParentPresContext() const864 nsPresContext* nsPresContext::GetParentPresContext() const {
865 mozilla::PresShell* presShell = GetPresShell();
866 if (presShell) {
867 nsViewManager* viewManager = presShell->GetViewManager();
868 if (viewManager) {
869 nsView* view = viewManager->GetRootView();
870 if (view) {
871 view = view->GetParent(); // anonymous inner view
872 if (view) {
873 view = view->GetParent(); // subdocumentframe's view
874 if (view) {
875 nsIFrame* f = view->GetFrame();
876 if (f) {
877 return f->PresContext();
878 }
879 }
880 }
881 }
882 }
883 }
884 return nullptr;
885 }
886
GetInProcessRootContentDocumentPresContext()887 nsPresContext* nsPresContext::GetInProcessRootContentDocumentPresContext() {
888 if (IsChrome()) return nullptr;
889 nsPresContext* pc = this;
890 for (;;) {
891 nsPresContext* parent = pc->GetParentPresContext();
892 if (!parent || parent->IsChrome()) return pc;
893 pc = parent;
894 }
895 }
896
GetNearestWidget(nsPoint * aOffset)897 nsIWidget* nsPresContext::GetNearestWidget(nsPoint* aOffset) {
898 NS_ENSURE_TRUE(mPresShell, nullptr);
899 nsViewManager* vm = mPresShell->GetViewManager();
900 NS_ENSURE_TRUE(vm, nullptr);
901 nsView* rootView = vm->GetRootView();
902 NS_ENSURE_TRUE(rootView, nullptr);
903 return rootView->GetNearestWidget(aOffset);
904 }
905
GetRootWidget() const906 nsIWidget* nsPresContext::GetRootWidget() const {
907 NS_ENSURE_TRUE(mPresShell, nullptr);
908 nsViewManager* vm = mPresShell->GetViewManager();
909 if (!vm) {
910 return nullptr;
911 }
912 nsCOMPtr<nsIWidget> widget;
913 vm->GetRootWidget(getter_AddRefs(widget));
914 return widget.get();
915 }
916
917 // We may want to replace this with something faster, maybe caching the root
918 // prescontext
GetRootPresContext() const919 nsRootPresContext* nsPresContext::GetRootPresContext() const {
920 nsPresContext* pc = const_cast<nsPresContext*>(this);
921 for (;;) {
922 nsPresContext* parent = pc->GetParentPresContext();
923 if (!parent) break;
924 pc = parent;
925 }
926 return pc->IsRoot() ? static_cast<nsRootPresContext*>(pc) : nullptr;
927 }
928
929 // Helper function for setting Anim Mode on image
SetImgAnimModeOnImgReq(imgIRequest * aImgReq,uint16_t aMode)930 static void SetImgAnimModeOnImgReq(imgIRequest* aImgReq, uint16_t aMode) {
931 if (aImgReq) {
932 nsCOMPtr<imgIContainer> imgCon;
933 aImgReq->GetImage(getter_AddRefs(imgCon));
934 if (imgCon) {
935 imgCon->SetAnimationMode(aMode);
936 }
937 }
938 }
939
940 // IMPORTANT: Assumption is that all images for a Presentation
941 // have the same Animation Mode (pavlov said this was OK)
942 //
943 // Walks content and set the animation mode
944 // this is a way to turn on/off image animations
SetImgAnimations(nsIContent * aParent,uint16_t aMode)945 void nsPresContext::SetImgAnimations(nsIContent* aParent, uint16_t aMode) {
946 nsCOMPtr<nsIImageLoadingContent> imgContent(do_QueryInterface(aParent));
947 if (imgContent) {
948 nsCOMPtr<imgIRequest> imgReq;
949 imgContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
950 getter_AddRefs(imgReq));
951 SetImgAnimModeOnImgReq(imgReq, aMode);
952 }
953
954 for (nsIContent* childContent = aParent->GetFirstChild(); childContent;
955 childContent = childContent->GetNextSibling()) {
956 SetImgAnimations(childContent, aMode);
957 }
958 }
959
SetSMILAnimations(dom::Document * aDoc,uint16_t aNewMode,uint16_t aOldMode)960 void nsPresContext::SetSMILAnimations(dom::Document* aDoc, uint16_t aNewMode,
961 uint16_t aOldMode) {
962 if (aDoc->HasAnimationController()) {
963 SMILAnimationController* controller = aDoc->GetAnimationController();
964 switch (aNewMode) {
965 case imgIContainer::kNormalAnimMode:
966 case imgIContainer::kLoopOnceAnimMode:
967 if (aOldMode == imgIContainer::kDontAnimMode)
968 controller->Resume(SMILTimeContainer::PAUSE_USERPREF);
969 break;
970
971 case imgIContainer::kDontAnimMode:
972 if (aOldMode != imgIContainer::kDontAnimMode)
973 controller->Pause(SMILTimeContainer::PAUSE_USERPREF);
974 break;
975 }
976 }
977 }
978
SetImageAnimationMode(uint16_t aMode)979 void nsPresContext::SetImageAnimationMode(uint16_t aMode) {
980 NS_ASSERTION(aMode == imgIContainer::kNormalAnimMode ||
981 aMode == imgIContainer::kDontAnimMode ||
982 aMode == imgIContainer::kLoopOnceAnimMode,
983 "Wrong Animation Mode is being set!");
984
985 // Image animation mode cannot be changed when rendering to a printer.
986 if (!IsDynamic()) return;
987
988 // Now walk the content tree and set the animation mode
989 // on all the images.
990 if (mPresShell) {
991 dom::Document* doc = mPresShell->GetDocument();
992 if (doc) {
993 doc->StyleImageLoader()->SetAnimationMode(aMode);
994
995 Element* rootElement = doc->GetRootElement();
996 if (rootElement) {
997 SetImgAnimations(rootElement, aMode);
998 }
999 SetSMILAnimations(doc, aMode, mImageAnimationMode);
1000 }
1001 }
1002
1003 mImageAnimationMode = aMode;
1004 }
1005
UpdateEffectiveTextZoom()1006 void nsPresContext::UpdateEffectiveTextZoom() {
1007 float newZoom = mSystemFontScale * mTextZoom;
1008 float minZoom = StaticPrefs::zoom_minPercent() / 100.0f;
1009 float maxZoom = StaticPrefs::zoom_maxPercent() / 100.0f;
1010
1011 if (newZoom < minZoom) {
1012 newZoom = minZoom;
1013 } else if (newZoom > maxZoom) {
1014 newZoom = maxZoom;
1015 }
1016
1017 mEffectiveTextZoom = newZoom;
1018
1019 // Media queries could have changed, since we changed the meaning
1020 // of 'em' units in them.
1021 MediaFeatureValuesChanged(
1022 {RestyleHint::RecascadeSubtree(), NS_STYLE_HINT_REFLOW,
1023 MediaFeatureChangeReason::ZoomChange},
1024 MediaFeatureChangePropagation::JustThisDocument);
1025 }
1026
GetDeviceFullZoom()1027 float nsPresContext::GetDeviceFullZoom() {
1028 return mDeviceContext->GetFullZoom();
1029 }
1030
SetFullZoom(float aZoom)1031 void nsPresContext::SetFullZoom(float aZoom) {
1032 if (!mPresShell || mFullZoom == aZoom) {
1033 return;
1034 }
1035
1036 // Re-fetch the view manager's window dimensions in case there's a deferred
1037 // resize which hasn't affected our mVisibleArea yet
1038 nscoord oldWidthAppUnits, oldHeightAppUnits;
1039 mPresShell->GetViewManager()->GetWindowDimensions(&oldWidthAppUnits,
1040 &oldHeightAppUnits);
1041 float oldWidthDevPixels = oldWidthAppUnits / float(mCurAppUnitsPerDevPixel);
1042 float oldHeightDevPixels = oldHeightAppUnits / float(mCurAppUnitsPerDevPixel);
1043 mDeviceContext->SetFullZoom(aZoom);
1044
1045 NS_ASSERTION(!mSuppressResizeReflow,
1046 "two zooms happening at the same time? Impossible!");
1047
1048 // We can't suppress the resize reflow if we support APZ zooming, as MVM
1049 // relies on ResizeReflowIgnoreOverride() actually updating layout to update
1050 // the viewport based on that.
1051 RefPtr<MobileViewportManager> mvm = mPresShell->GetMobileViewportManager();
1052 mSuppressResizeReflow = !mvm;
1053
1054 mFullZoom = aZoom;
1055
1056 AppUnitsPerDevPixelChanged();
1057
1058 mPresShell->GetViewManager()->SetWindowDimensions(
1059 NSToCoordRound(oldWidthDevPixels * AppUnitsPerDevPixel()),
1060 NSToCoordRound(oldHeightDevPixels * AppUnitsPerDevPixel()));
1061
1062 mSuppressResizeReflow = false;
1063 }
1064
SetOverrideDPPX(float aDPPX)1065 void nsPresContext::SetOverrideDPPX(float aDPPX) {
1066 // SetOverrideDPPX is called during navigations, including history
1067 // traversals. In that case, it's typically called with our current value,
1068 // and we don't need to actually do anything.
1069 if (aDPPX == GetOverrideDPPX()) {
1070 return;
1071 }
1072
1073 mMediaEmulationData.mDPPX = aDPPX;
1074 MediaFeatureValuesChanged({MediaFeatureChangeReason::ResolutionChange},
1075 MediaFeatureChangePropagation::JustThisDocument);
1076 }
1077
ScreenSizeInchesForFontInflation(bool * aChanged)1078 gfxSize nsPresContext::ScreenSizeInchesForFontInflation(bool* aChanged) {
1079 if (aChanged) {
1080 *aChanged = false;
1081 }
1082
1083 nsDeviceContext* dx = DeviceContext();
1084 nsRect clientRect;
1085 dx->GetClientRect(clientRect); // FIXME: GetClientRect looks expensive
1086 float unitsPerInch = dx->AppUnitsPerPhysicalInch();
1087 gfxSize deviceSizeInches(float(clientRect.width) / unitsPerInch,
1088 float(clientRect.height) / unitsPerInch);
1089
1090 if (mLastFontInflationScreenSize == gfxSize(-1.0, -1.0)) {
1091 mLastFontInflationScreenSize = deviceSizeInches;
1092 }
1093
1094 if (deviceSizeInches != mLastFontInflationScreenSize && aChanged) {
1095 *aChanged = true;
1096 mLastFontInflationScreenSize = deviceSizeInches;
1097 }
1098
1099 return deviceSizeInches;
1100 }
1101
CheckOverflow(const ComputedStyle * aComputedStyle,ScrollStyles * aStyles)1102 static bool CheckOverflow(const ComputedStyle* aComputedStyle,
1103 ScrollStyles* aStyles) {
1104 // If they're not styled yet, we'll get around to it when constructing frames
1105 // for the element.
1106 if (!aComputedStyle) {
1107 return false;
1108 }
1109 const nsStyleDisplay* display = aComputedStyle->StyleDisplay();
1110
1111 // If they will generate no box, just don't.
1112 if (display->mDisplay == StyleDisplay::None ||
1113 display->mDisplay == StyleDisplay::Contents) {
1114 return false;
1115 }
1116
1117 // NOTE(emilio): This check needs to match the one in
1118 // Document::IsPotentiallyScrollable.
1119 if (display->OverflowIsVisibleInBothAxis()) {
1120 return false;
1121 }
1122
1123 *aStyles =
1124 ScrollStyles(*display, ScrollStyles::MapOverflowToValidScrollStyle);
1125 return true;
1126 }
1127
1128 // https://drafts.csswg.org/css-overflow/#overflow-propagation
1129 //
1130 // NOTE(emilio): We may need to use out-of-date styles for this, since this is
1131 // called from nsCSSFrameConstructor::ContentRemoved. We could refactor this a
1132 // bit to avoid doing that, and also fix correctness issues (we don't invalidate
1133 // properly when we insert a body element and there is a previous one, for
1134 // example).
GetPropagatedScrollStylesForViewport(nsPresContext * aPresContext,ScrollStyles * aStyles)1135 static Element* GetPropagatedScrollStylesForViewport(
1136 nsPresContext* aPresContext, ScrollStyles* aStyles) {
1137 Document* document = aPresContext->Document();
1138 Element* docElement = document->GetRootElement();
1139 // docElement might be null if we're doing this after removing it.
1140 if (!docElement) {
1141 return nullptr;
1142 }
1143
1144 // Check the style on the document root element
1145 const auto* rootStyle = Servo_Element_GetMaybeOutOfDateStyle(docElement);
1146 if (CheckOverflow(rootStyle, aStyles)) {
1147 // tell caller we stole the overflow style from the root element
1148 return docElement;
1149 }
1150
1151 // Don't look in the BODY for non-HTML documents or HTML documents
1152 // with non-HTML roots.
1153 // XXX this should be earlier; we shouldn't even look at the document root
1154 // for non-HTML documents. Fix this once we support explicit CSS styling
1155 // of the viewport
1156 if (!document->IsHTMLOrXHTML() || !docElement->IsHTMLElement()) {
1157 return nullptr;
1158 }
1159
1160 Element* bodyElement = document->AsHTMLDocument()->GetBodyElement();
1161 if (!bodyElement) {
1162 return nullptr;
1163 }
1164
1165 MOZ_ASSERT(bodyElement->IsHTMLElement(nsGkAtoms::body),
1166 "GetBodyElement returned something bogus");
1167
1168 const auto* bodyStyle = Servo_Element_GetMaybeOutOfDateStyle(bodyElement);
1169 if (CheckOverflow(bodyStyle, aStyles)) {
1170 // tell caller we stole the overflow style from the body element
1171 return bodyElement;
1172 }
1173
1174 return nullptr;
1175 }
1176
UpdateViewportScrollStylesOverride()1177 Element* nsPresContext::UpdateViewportScrollStylesOverride() {
1178 ScrollStyles oldViewportScrollStyles = mViewportScrollStyles;
1179
1180 // Start off with our default styles, and then update them as needed.
1181 mViewportScrollStyles =
1182 ScrollStyles(StyleOverflow::Auto, StyleOverflow::Auto);
1183 mViewportScrollOverrideElement = nullptr;
1184 // Don't propagate the scrollbar state in printing or print preview.
1185 if (!IsPaginated()) {
1186 mViewportScrollOverrideElement =
1187 GetPropagatedScrollStylesForViewport(this, &mViewportScrollStyles);
1188 }
1189
1190 dom::Document* document = Document();
1191 if (Element* fullscreenElement = document->GetFullscreenElement()) {
1192 // If the document is in fullscreen, but the fullscreen element is
1193 // not the root element, we should explicitly suppress the scrollbar
1194 // here. Note that, we still need to return the original element
1195 // the styles are from, so that the state of those elements is not
1196 // affected across fullscreen change.
1197 if (fullscreenElement != document->GetRootElement() &&
1198 fullscreenElement != mViewportScrollOverrideElement) {
1199 mViewportScrollStyles =
1200 ScrollStyles(StyleOverflow::Hidden, StyleOverflow::Hidden);
1201 }
1202 }
1203
1204 if (mViewportScrollStyles != oldViewportScrollStyles) {
1205 if (mPresShell) {
1206 if (nsIFrame* frame = mPresShell->GetRootFrame()) {
1207 frame->SchedulePaint();
1208 }
1209 }
1210 }
1211
1212 return mViewportScrollOverrideElement;
1213 }
1214
ElementWouldPropagateScrollStyles(const Element & aElement)1215 bool nsPresContext::ElementWouldPropagateScrollStyles(const Element& aElement) {
1216 if (aElement.GetParent() && !aElement.IsHTMLElement(nsGkAtoms::body)) {
1217 // We certainly won't be propagating from this element.
1218 return false;
1219 }
1220
1221 // Go ahead and just call GetPropagatedScrollStylesForViewport, but update
1222 // a dummy ScrollStyles we don't care about. It'll do a bit of extra work,
1223 // but saves us having to have more complicated code or more code duplication;
1224 // in practice we will make this call quite rarely, because we checked for all
1225 // the common cases above.
1226 ScrollStyles dummy(StyleOverflow::Auto, StyleOverflow::Auto);
1227 return GetPropagatedScrollStylesForViewport(this, &dummy) == &aElement;
1228 }
1229
GetContainerWeak() const1230 nsISupports* nsPresContext::GetContainerWeak() const {
1231 return mDocument->GetDocShell();
1232 }
1233
GetDocShell() const1234 nsDocShell* nsPresContext::GetDocShell() const {
1235 return nsDocShell::Cast(mDocument->GetDocShell());
1236 }
1237
BidiEnabled() const1238 bool nsPresContext::BidiEnabled() const { return Document()->GetBidiEnabled(); }
1239
SetBidiEnabled() const1240 void nsPresContext::SetBidiEnabled() const { Document()->SetBidiEnabled(); }
1241
SetBidi(uint32_t aSource)1242 void nsPresContext::SetBidi(uint32_t aSource) {
1243 // Don't do all this stuff unless the options have changed.
1244 if (aSource == GetBidi()) {
1245 return;
1246 }
1247
1248 Document()->SetBidiOptions(aSource);
1249 if (IBMBIDI_TEXTDIRECTION_RTL == GET_BIDI_OPTION_DIRECTION(aSource) ||
1250 IBMBIDI_NUMERAL_HINDI == GET_BIDI_OPTION_NUMERAL(aSource)) {
1251 SetBidiEnabled();
1252 }
1253 if (IBMBIDI_TEXTTYPE_VISUAL == GET_BIDI_OPTION_TEXTTYPE(aSource)) {
1254 SetVisualMode(true);
1255 } else if (IBMBIDI_TEXTTYPE_LOGICAL == GET_BIDI_OPTION_TEXTTYPE(aSource)) {
1256 SetVisualMode(false);
1257 } else {
1258 SetVisualMode(IsVisualCharset(Document()->GetDocumentCharacterSet()));
1259 }
1260 }
1261
GetBidi() const1262 uint32_t nsPresContext::GetBidi() const { return Document()->GetBidiOptions(); }
1263
RecordInteractionTime(InteractionType aType,const TimeStamp & aTimeStamp)1264 void nsPresContext::RecordInteractionTime(InteractionType aType,
1265 const TimeStamp& aTimeStamp) {
1266 if (!mInteractionTimeEnabled || aTimeStamp.IsNull()) {
1267 return;
1268 }
1269
1270 // Array of references to the member variable of each time stamp
1271 // for the different interaction types, keyed by InteractionType.
1272 TimeStamp nsPresContext::*interactionTimes[] = {
1273 &nsPresContext::mFirstClickTime, &nsPresContext::mFirstKeyTime,
1274 &nsPresContext::mFirstMouseMoveTime, &nsPresContext::mFirstScrollTime};
1275
1276 // Array of histogram IDs for the different interaction types,
1277 // keyed by InteractionType.
1278 Telemetry::HistogramID histogramIds[] = {
1279 Telemetry::TIME_TO_FIRST_CLICK_MS, Telemetry::TIME_TO_FIRST_KEY_INPUT_MS,
1280 Telemetry::TIME_TO_FIRST_MOUSE_MOVE_MS,
1281 Telemetry::TIME_TO_FIRST_SCROLL_MS};
1282
1283 TimeStamp& interactionTime =
1284 this->*(interactionTimes[static_cast<uint32_t>(aType)]);
1285 if (!interactionTime.IsNull()) {
1286 // We have already recorded an interaction time.
1287 return;
1288 }
1289
1290 // Record the interaction time if it occurs after the first paint
1291 // of the top level content document.
1292 nsPresContext* inProcessRootPresContext =
1293 GetInProcessRootContentDocumentPresContext();
1294
1295 if (!inProcessRootPresContext ||
1296 !inProcessRootPresContext->IsRootContentDocumentCrossProcess()) {
1297 // There is no top content pres context, or we are in a cross process
1298 // document so we don't care about the interaction time. Record a value
1299 // anyways to avoid trying to find the top content pres context in future
1300 // interactions.
1301 interactionTime = TimeStamp::Now();
1302 return;
1303 }
1304
1305 if (inProcessRootPresContext->mFirstNonBlankPaintTime.IsNull() ||
1306 inProcessRootPresContext->mFirstNonBlankPaintTime > aTimeStamp) {
1307 // Top content pres context has not had a non-blank paint yet
1308 // or the event timestamp is before the first non-blank paint,
1309 // so don't record interaction time.
1310 return;
1311 }
1312
1313 // Check if we are recording the first of any of the interaction types.
1314 bool isFirstInteraction = true;
1315 for (TimeStamp nsPresContext::*memberPtr : interactionTimes) {
1316 TimeStamp& timeStamp = this->*(memberPtr);
1317 if (!timeStamp.IsNull()) {
1318 isFirstInteraction = false;
1319 break;
1320 }
1321 }
1322
1323 interactionTime = TimeStamp::Now();
1324 // Only the top level content pres context reports first interaction
1325 // time to telemetry (if it hasn't already done so).
1326 if (this == inProcessRootPresContext) {
1327 if (Telemetry::CanRecordExtended()) {
1328 double millis =
1329 (interactionTime - mFirstNonBlankPaintTime).ToMilliseconds();
1330 Telemetry::Accumulate(histogramIds[static_cast<uint32_t>(aType)], millis);
1331
1332 if (isFirstInteraction) {
1333 Telemetry::Accumulate(Telemetry::TIME_TO_FIRST_INTERACTION_MS, millis);
1334 }
1335 }
1336 } else {
1337 inProcessRootPresContext->RecordInteractionTime(aType, aTimeStamp);
1338 }
1339 }
1340
EnsureTheme()1341 nsITheme* nsPresContext::EnsureTheme() {
1342 MOZ_ASSERT(!mTheme);
1343 if (Document()->ShouldAvoidNativeTheme()) {
1344 mTheme = do_GetBasicNativeThemeDoNotUseDirectly();
1345 } else {
1346 mTheme = do_GetNativeThemeDoNotUseDirectly();
1347 }
1348 MOZ_RELEASE_ASSERT(mTheme);
1349 return mTheme;
1350 }
1351
ThemeChanged(widget::ThemeChangeKind aKind)1352 void nsPresContext::ThemeChanged(widget::ThemeChangeKind aKind) {
1353 // NOTE(emilio): This ideally wouldn't need to be a _TEXT() marker, but
1354 // otherwise the stack is not captured, see bug 1670046.
1355 PROFILER_MARKER_TEXT("ThemeChanged", LAYOUT, MarkerStack::Capture(), ""_ns);
1356
1357 mPendingThemeChangeKind |= unsigned(aKind);
1358
1359 if (!mPendingThemeChanged) {
1360 sLookAndFeelChanged = true;
1361 sThemeChanged = true;
1362
1363 nsCOMPtr<nsIRunnable> ev =
1364 NewRunnableMethod("nsPresContext::ThemeChangedInternal", this,
1365 &nsPresContext::ThemeChangedInternal);
1366 nsresult rv = Document()->Dispatch(TaskCategory::Other, ev.forget());
1367 if (NS_SUCCEEDED(rv)) {
1368 mPendingThemeChanged = true;
1369 }
1370 }
1371 }
1372
ThemeChangedInternal()1373 void nsPresContext::ThemeChangedInternal() {
1374 mPendingThemeChanged = false;
1375
1376 const auto kind = widget::ThemeChangeKind(mPendingThemeChangeKind);
1377 mPendingThemeChangeKind = 0;
1378
1379 // Tell the theme that it changed, so it can flush any handles to stale theme
1380 // data.
1381 if (mTheme && sThemeChanged) {
1382 mTheme->ThemeChanged();
1383 sThemeChanged = false;
1384 }
1385
1386 if (sLookAndFeelChanged) {
1387 // Clear all cached LookAndFeel colors.
1388 LookAndFeel::Refresh();
1389 sLookAndFeelChanged = false;
1390
1391 // Vector images (SVG) may be using theme colors so we discard all cached
1392 // surfaces. (We could add a vector image only version of DiscardAll, but
1393 // in bug 940625 we decided theme changes are rare enough not to bother.)
1394 image::SurfaceCacheUtils::DiscardAll();
1395
1396 if (XRE_IsParentProcess()) {
1397 ContentParent::BroadcastThemeUpdate(kind);
1398 }
1399 }
1400
1401 // Reset default background and foreground colors for the document since they
1402 // may be using system colors.
1403 PreferenceSheet::Refresh();
1404
1405 // Changes to system metrics and other look and feel values can change media
1406 // queries on them.
1407 //
1408 // Changes in theme can change system colors (whose changes are properly
1409 // reflected in computed style data), system fonts (whose changes are
1410 // some reflected (like sizes and such) and some not), and -moz-appearance
1411 // (whose changes are not), so we need to recascade for the first, and reflow
1412 // for the rest.
1413 auto restyleHint = (kind & widget::ThemeChangeKind::Style)
1414 ? RestyleHint::RecascadeSubtree()
1415 : RestyleHint{0};
1416 auto changeHint = (kind & widget::ThemeChangeKind::Layout)
1417 ? NS_STYLE_HINT_REFLOW
1418 : nsChangeHint(0);
1419 MediaFeatureValuesChanged(
1420 {restyleHint, changeHint, MediaFeatureChangeReason::SystemMetricsChange},
1421 MediaFeatureChangePropagation::All);
1422 }
1423
UIResolutionChanged()1424 void nsPresContext::UIResolutionChanged() {
1425 if (!mPendingUIResolutionChanged) {
1426 nsCOMPtr<nsIRunnable> ev =
1427 NewRunnableMethod("nsPresContext::UIResolutionChangedInternal", this,
1428 &nsPresContext::UIResolutionChangedInternal);
1429 nsresult rv = Document()->Dispatch(TaskCategory::Other, ev.forget());
1430 if (NS_SUCCEEDED(rv)) {
1431 mPendingUIResolutionChanged = true;
1432 }
1433 }
1434 }
1435
UIResolutionChangedSync()1436 void nsPresContext::UIResolutionChangedSync() {
1437 if (!mPendingUIResolutionChanged) {
1438 mPendingUIResolutionChanged = true;
1439 UIResolutionChangedInternalScale(0.0);
1440 }
1441 }
1442
NotifyTabUIResolutionChanged(nsIRemoteTab * aTab,void * aArg)1443 static void NotifyTabUIResolutionChanged(nsIRemoteTab* aTab, void* aArg) {
1444 aTab->NotifyResolutionChanged();
1445 }
1446
NotifyChildrenUIResolutionChanged(nsPIDOMWindowOuter * aWindow)1447 static void NotifyChildrenUIResolutionChanged(nsPIDOMWindowOuter* aWindow) {
1448 nsCOMPtr<Document> doc = aWindow->GetExtantDoc();
1449 RefPtr<nsPIWindowRoot> topLevelWin = nsContentUtils::GetWindowRoot(doc);
1450 if (!topLevelWin) {
1451 return;
1452 }
1453 topLevelWin->EnumerateBrowsers(NotifyTabUIResolutionChanged, nullptr);
1454 }
1455
UIResolutionChangedInternal()1456 void nsPresContext::UIResolutionChangedInternal() {
1457 UIResolutionChangedInternalScale(0.0);
1458 }
1459
UIResolutionChangedInternalScale(double aScale)1460 void nsPresContext::UIResolutionChangedInternalScale(double aScale) {
1461 mPendingUIResolutionChanged = false;
1462
1463 mDeviceContext->CheckDPIChange(&aScale);
1464 if (mCurAppUnitsPerDevPixel != mDeviceContext->AppUnitsPerDevPixel()) {
1465 AppUnitsPerDevPixelChanged();
1466 }
1467
1468 // Recursively notify all remote leaf descendants of the change.
1469 if (nsPIDOMWindowOuter* window = mDocument->GetWindow()) {
1470 NotifyChildrenUIResolutionChanged(window);
1471 }
1472
1473 auto recurse = [aScale](dom::Document& aSubDoc) {
1474 if (nsPresContext* pc = aSubDoc.GetPresContext()) {
1475 // For subdocuments, we want to apply the parent's scale, because there
1476 // are cases where the subdoc's device context is connected to a widget
1477 // that has an out-of-date resolution (it's on a different screen, but
1478 // currently hidden, and will not be updated until shown): bug 1249279.
1479 pc->UIResolutionChangedInternalScale(aScale);
1480 }
1481 return CallState::Continue;
1482 };
1483 mDocument->EnumerateSubDocuments(recurse);
1484 }
1485
EmulateMedium(nsAtom * aMediaType)1486 void nsPresContext::EmulateMedium(nsAtom* aMediaType) {
1487 MOZ_ASSERT(!aMediaType || aMediaType->IsAsciiLowercase());
1488 RefPtr<const nsAtom> oldMedium = Medium();
1489 mMediaEmulationData.mMedium = aMediaType;
1490
1491 if (Medium() != oldMedium) {
1492 MediaFeatureValuesChanged({MediaFeatureChangeReason::MediumChange},
1493 MediaFeatureChangePropagation::JustThisDocument);
1494 }
1495 }
1496
ContentLanguageChanged()1497 void nsPresContext::ContentLanguageChanged() {
1498 PostRebuildAllStyleDataEvent(nsChangeHint(0),
1499 RestyleHint::RecascadeSubtree());
1500 }
1501
RegisterManagedPostRefreshObserver(ManagedPostRefreshObserver * aObserver)1502 void nsPresContext::RegisterManagedPostRefreshObserver(
1503 ManagedPostRefreshObserver* aObserver) {
1504 if (MOZ_UNLIKELY(!mPresShell)) {
1505 // If we're detached from our pres shell already, refuse to keep observer
1506 // around, as that'd create a cycle.
1507 RefPtr<ManagedPostRefreshObserver> obs = aObserver;
1508 obs->Cancel();
1509 return;
1510 }
1511
1512 RefreshDriver()->AddPostRefreshObserver(
1513 static_cast<nsAPostRefreshObserver*>(aObserver));
1514 mManagedPostRefreshObservers.AppendElement(aObserver);
1515 }
1516
UnregisterManagedPostRefreshObserver(ManagedPostRefreshObserver * aObserver)1517 void nsPresContext::UnregisterManagedPostRefreshObserver(
1518 ManagedPostRefreshObserver* aObserver) {
1519 RefreshDriver()->RemovePostRefreshObserver(
1520 static_cast<nsAPostRefreshObserver*>(aObserver));
1521 DebugOnly<bool> removed =
1522 mManagedPostRefreshObservers.RemoveElement(aObserver);
1523 MOZ_ASSERT(removed,
1524 "ManagedPostRefreshObserver should be owned by PresContext");
1525 }
1526
CancelManagedPostRefreshObservers()1527 void nsPresContext::CancelManagedPostRefreshObservers() {
1528 auto observers = std::move(mManagedPostRefreshObservers);
1529 nsRefreshDriver* driver = RefreshDriver();
1530 for (const auto& observer : observers) {
1531 observer->Cancel();
1532 driver->RemovePostRefreshObserver(
1533 static_cast<nsAPostRefreshObserver*>(observer));
1534 }
1535 }
1536
RebuildAllStyleData(nsChangeHint aExtraHint,const RestyleHint & aRestyleHint)1537 void nsPresContext::RebuildAllStyleData(nsChangeHint aExtraHint,
1538 const RestyleHint& aRestyleHint) {
1539 if (!mPresShell) {
1540 // We must have been torn down. Nothing to do here.
1541 return;
1542 }
1543
1544 // TODO(emilio): It's unclear to me why would these three calls below be
1545 // needed. In particular, RebuildAllStyleData doesn't rebuild rules or
1546 // specified style information and such (note the comment in
1547 // RestyleManager::RebuildAllStyleData re. the funny semantics), so I
1548 // don't know why should we rebuild the user font set / counter styles /
1549 // etc...
1550 mDocument->MarkUserFontSetDirty();
1551 MarkCounterStylesDirty();
1552 MarkFontFeatureValuesDirty();
1553 PostRebuildAllStyleDataEvent(aExtraHint, aRestyleHint);
1554 }
1555
PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint,const RestyleHint & aRestyleHint)1556 void nsPresContext::PostRebuildAllStyleDataEvent(
1557 nsChangeHint aExtraHint, const RestyleHint& aRestyleHint) {
1558 if (!mPresShell) {
1559 // We must have been torn down. Nothing to do here.
1560 return;
1561 }
1562 if (aRestyleHint.DefinitelyRecascadesAllSubtree()) {
1563 mUsesExChUnits = false;
1564 }
1565 RestyleManager()->RebuildAllStyleData(aExtraHint, aRestyleHint);
1566 }
1567
MediaFeatureValuesChanged(const MediaFeatureChange & aChange,MediaFeatureChangePropagation aPropagation)1568 void nsPresContext::MediaFeatureValuesChanged(
1569 const MediaFeatureChange& aChange,
1570 MediaFeatureChangePropagation aPropagation) {
1571 if (mPresShell) {
1572 mPresShell->EnsureStyleFlush();
1573 }
1574
1575 if (!mPendingMediaFeatureValuesChange) {
1576 mPendingMediaFeatureValuesChange = MakeUnique<MediaFeatureChange>(aChange);
1577 } else {
1578 *mPendingMediaFeatureValuesChange |= aChange;
1579 }
1580
1581 if (aPropagation & MediaFeatureChangePropagation::Images) {
1582 // Propagate the media feature value change down to any SVG images the
1583 // document is using.
1584 mDocument->ImageTracker()->MediaFeatureValuesChangedAllDocuments(aChange);
1585 }
1586
1587 if (aPropagation & MediaFeatureChangePropagation::SubDocuments) {
1588 // And then into any subdocuments.
1589 auto recurse = [&aChange, aPropagation](dom::Document& aSubDoc) {
1590 if (nsPresContext* pc = aSubDoc.GetPresContext()) {
1591 pc->MediaFeatureValuesChanged(aChange, aPropagation);
1592 }
1593 return CallState::Continue;
1594 };
1595 mDocument->EnumerateSubDocuments(recurse);
1596 }
1597 }
1598
FlushPendingMediaFeatureValuesChanged()1599 bool nsPresContext::FlushPendingMediaFeatureValuesChanged() {
1600 if (!mPendingMediaFeatureValuesChange) {
1601 return false;
1602 }
1603
1604 MediaFeatureChange change = *mPendingMediaFeatureValuesChange;
1605 mPendingMediaFeatureValuesChange.reset();
1606
1607 // MediumFeaturesChanged updates the applied rules, so it always gets called.
1608 if (mPresShell) {
1609 change.mRestyleHint |=
1610 mPresShell->StyleSet()->MediumFeaturesChanged(change.mReason);
1611 }
1612
1613 const bool changedStyle = change.mRestyleHint || change.mChangeHint;
1614 if (changedStyle) {
1615 RebuildAllStyleData(change.mChangeHint, change.mRestyleHint);
1616 }
1617
1618 if (mDocument->IsBeingUsedAsImage()) {
1619 MOZ_ASSERT(mDocument->MediaQueryLists().isEmpty());
1620 return changedStyle;
1621 }
1622
1623 mDocument->NotifyMediaFeatureValuesChanged();
1624
1625 // https://drafts.csswg.org/cssom-view/#evaluate-media-queries-and-report-changes
1626 //
1627 // Media query list listeners should be notified from a queued task
1628 // (in HTML5 terms), although we also want to notify them on certain
1629 // flushes. (We're already running off an event.)
1630 //
1631 // TODO: This should be better integrated into the "update the rendering"
1632 // steps: https://html.spec.whatwg.org/#update-the-rendering
1633 //
1634 // Note that we do this after the new style from media queries in
1635 // style sheets has been computed.
1636
1637 if (mDocument->MediaQueryLists().isEmpty()) {
1638 return changedStyle;
1639 }
1640
1641 // We build a list of all the notifications we're going to send
1642 // before we send any of them.
1643 nsTArray<RefPtr<mozilla::dom::MediaQueryList>> listsToNotify;
1644 for (MediaQueryList* mql = mDocument->MediaQueryLists().getFirst(); mql;
1645 mql = static_cast<LinkedListElement<MediaQueryList>*>(mql)->getNext()) {
1646 if (mql->MediaFeatureValuesChanged()) {
1647 listsToNotify.AppendElement(mql);
1648 }
1649 }
1650
1651 if (!listsToNotify.IsEmpty()) {
1652 nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
1653 "nsPresContext::FlushPendingMediaFeatureValuesChanged",
1654 [list = std::move(listsToNotify)] {
1655 for (const auto& mql : list) {
1656 nsAutoMicroTask mt;
1657 mql->FireChangeEvent();
1658 }
1659 }));
1660 }
1661
1662 return changedStyle;
1663 }
1664
SizeModeChanged(nsSizeMode aSizeMode)1665 void nsPresContext::SizeModeChanged(nsSizeMode aSizeMode) {
1666 if (nsPIDOMWindowOuter* window = mDocument->GetWindow()) {
1667 nsContentUtils::CallOnAllRemoteChildren(
1668 window, [&aSizeMode](BrowserParent* aBrowserParent) -> CallState {
1669 aBrowserParent->SizeModeChanged(aSizeMode);
1670 return CallState::Continue;
1671 });
1672 }
1673 MediaFeatureValuesChanged({MediaFeatureChangeReason::SizeModeChange},
1674 MediaFeatureChangePropagation::SubDocuments);
1675 }
1676
CompatibilityMode() const1677 nsCompatibility nsPresContext::CompatibilityMode() const {
1678 return Document()->GetCompatibilityMode();
1679 }
1680
SetPaginatedScrolling(bool aPaginated)1681 void nsPresContext::SetPaginatedScrolling(bool aPaginated) {
1682 if (mType == eContext_PrintPreview || mType == eContext_PageLayout) {
1683 mCanPaginatedScroll = aPaginated;
1684 }
1685 }
1686
SetPrintSettings(nsIPrintSettings * aPrintSettings)1687 void nsPresContext::SetPrintSettings(nsIPrintSettings* aPrintSettings) {
1688 if (mMedium != nsGkAtoms::print) {
1689 return;
1690 }
1691
1692 mPrintSettings = aPrintSettings;
1693 mDefaultPageMargin = nsMargin();
1694 if (!mPrintSettings) {
1695 return;
1696 }
1697
1698 // Set the presentation context to the value in the print settings.
1699 mDrawColorBackground = mPrintSettings->GetPrintBGColors();
1700 mDrawImageBackground = mPrintSettings->GetPrintBGImages();
1701
1702 nsIntMargin unwriteableTwips = mPrintSettings->GetUnwriteableMarginInTwips();
1703 NS_ASSERTION(unwriteableTwips.top >= 0 && unwriteableTwips.right >= 0 &&
1704 unwriteableTwips.bottom >= 0 && unwriteableTwips.left >= 0,
1705 "Unwriteable twips should be non-negative");
1706
1707 nsIntMargin marginTwips = mPrintSettings->GetMarginInTwips();
1708 marginTwips.EnsureAtLeast(unwriteableTwips);
1709 mDefaultPageMargin = nsPresContext::CSSTwipsToAppUnits(marginTwips);
1710 }
1711
EnsureVisible()1712 bool nsPresContext::EnsureVisible() {
1713 nsCOMPtr<nsIDocShell> docShell(GetDocShell());
1714 if (!docShell) {
1715 return false;
1716 }
1717 nsCOMPtr<nsIContentViewer> cv;
1718 docShell->GetContentViewer(getter_AddRefs(cv));
1719 // Make sure this is the content viewer we belong with
1720 if (!cv || cv->GetPresContext() != this) {
1721 return false;
1722 }
1723 // OK, this is us. We want to call Show() on the content viewer.
1724 nsresult result = cv->Show();
1725 return NS_SUCCEEDED(result);
1726 }
1727
1728 #ifdef MOZ_REFLOW_PERF
CountReflows(const char * aName,nsIFrame * aFrame)1729 void nsPresContext::CountReflows(const char* aName, nsIFrame* aFrame) {
1730 if (mPresShell) {
1731 mPresShell->CountReflows(aName, aFrame);
1732 }
1733 }
1734 #endif
1735
HasAuthorSpecifiedRules(const nsIFrame * aFrame,uint32_t aRuleTypeMask) const1736 bool nsPresContext::HasAuthorSpecifiedRules(const nsIFrame* aFrame,
1737 uint32_t aRuleTypeMask) const {
1738 const bool padding = aRuleTypeMask & NS_AUTHOR_SPECIFIED_PADDING;
1739 const bool borderBackground =
1740 aRuleTypeMask & NS_AUTHOR_SPECIFIED_BORDER_OR_BACKGROUND;
1741 const auto& style = *aFrame->Style();
1742
1743 if (padding && style.HasAuthorSpecifiedPadding()) {
1744 return true;
1745 }
1746
1747 if (borderBackground && style.HasAuthorSpecifiedBorderOrBackground()) {
1748 return true;
1749 }
1750
1751 return false;
1752 }
1753
GetUserFontSet()1754 gfxUserFontSet* nsPresContext::GetUserFontSet() {
1755 return mDocument->GetUserFontSet();
1756 }
1757
UserFontSetUpdated(gfxUserFontEntry * aUpdatedFont)1758 void nsPresContext::UserFontSetUpdated(gfxUserFontEntry* aUpdatedFont) {
1759 if (!mPresShell) {
1760 return;
1761 }
1762
1763 // Note: this method is called without a font when rules in the userfont set
1764 // are updated.
1765 //
1766 // We can avoid a full restyle if font-metric-dependent units are not in use,
1767 // since we know there's no style resolution that would depend on this font
1768 // and trigger its load.
1769 //
1770 // TODO(emilio): We could be more granular if we knew which families have
1771 // potentially changed.
1772 if (!aUpdatedFont) {
1773 auto hint =
1774 UsesExChUnits() ? RestyleHint::RecascadeSubtree() : RestyleHint{0};
1775 PostRebuildAllStyleDataEvent(NS_STYLE_HINT_REFLOW, hint);
1776 return;
1777 }
1778
1779 // Iterate over the frame tree looking for frames associated with the
1780 // downloadable font family in question. If a frame's nsStyleFont has
1781 // the name, check the font group associated with the metrics to see if
1782 // it contains that specific font (i.e. the one chosen within the family
1783 // given the weight, width, and slant from the nsStyleFont). If it does,
1784 // mark that frame dirty and skip inspecting its descendants.
1785 if (nsIFrame* root = mPresShell->GetRootFrame()) {
1786 nsFontFaceUtils::MarkDirtyForFontChange(root, aUpdatedFont);
1787 }
1788 }
1789
1790 class CounterStyleCleaner final : public nsAPostRefreshObserver {
1791 public:
CounterStyleCleaner(nsRefreshDriver * aRefreshDriver,CounterStyleManager * aCounterStyleManager)1792 CounterStyleCleaner(nsRefreshDriver* aRefreshDriver,
1793 CounterStyleManager* aCounterStyleManager)
1794 : mRefreshDriver(aRefreshDriver),
1795 mCounterStyleManager(aCounterStyleManager) {}
1796 virtual ~CounterStyleCleaner() = default;
1797
DidRefresh()1798 void DidRefresh() final {
1799 mRefreshDriver->RemovePostRefreshObserver(this);
1800 mCounterStyleManager->CleanRetiredStyles();
1801 delete this;
1802 }
1803
1804 private:
1805 RefPtr<nsRefreshDriver> mRefreshDriver;
1806 RefPtr<CounterStyleManager> mCounterStyleManager;
1807 };
1808
FlushCounterStyles()1809 void nsPresContext::FlushCounterStyles() {
1810 if (!mPresShell) {
1811 return; // we've been torn down
1812 }
1813 if (mCounterStyleManager->IsInitial()) {
1814 // Still in its initial state, no need to clean.
1815 return;
1816 }
1817
1818 if (mCounterStylesDirty) {
1819 bool changed = mCounterStyleManager->NotifyRuleChanged();
1820 if (changed) {
1821 PresShell()->NotifyCounterStylesAreDirty();
1822 PostRebuildAllStyleDataEvent(NS_STYLE_HINT_REFLOW, RestyleHint{0});
1823 RefreshDriver()->AddPostRefreshObserver(
1824 new CounterStyleCleaner(RefreshDriver(), mCounterStyleManager));
1825 }
1826 mCounterStylesDirty = false;
1827 }
1828 }
1829
MarkCounterStylesDirty()1830 void nsPresContext::MarkCounterStylesDirty() {
1831 if (mCounterStyleManager->IsInitial()) {
1832 // Still in its initial state, no need to touch anything.
1833 return;
1834 }
1835
1836 mCounterStylesDirty = true;
1837 }
1838
NotifyMissingFonts()1839 void nsPresContext::NotifyMissingFonts() {
1840 if (mMissingFonts) {
1841 mMissingFonts->Flush();
1842 }
1843 }
1844
EnsureSafeToHandOutCSSRules()1845 void nsPresContext::EnsureSafeToHandOutCSSRules() {
1846 if (!mPresShell->StyleSet()->EnsureUniqueInnerOnCSSSheets()) {
1847 // Nothing to do.
1848 return;
1849 }
1850
1851 RebuildAllStyleData(nsChangeHint(0), RestyleHint::RestyleSubtree());
1852 }
1853
FireDOMPaintEvent(nsTArray<nsRect> * aList,TransactionId aTransactionId,mozilla::TimeStamp aTimeStamp)1854 void nsPresContext::FireDOMPaintEvent(
1855 nsTArray<nsRect>* aList, TransactionId aTransactionId,
1856 mozilla::TimeStamp aTimeStamp /* = mozilla::TimeStamp() */) {
1857 nsPIDOMWindowInner* ourWindow = mDocument->GetInnerWindow();
1858 if (!ourWindow) return;
1859
1860 nsCOMPtr<EventTarget> dispatchTarget = do_QueryInterface(ourWindow);
1861 nsCOMPtr<EventTarget> eventTarget = dispatchTarget;
1862 if (!IsChrome() && !mSendAfterPaintToContent) {
1863 // Don't tell the window about this event, it should not know that
1864 // something happened in a subdocument. Tell only the chrome event handler.
1865 // (Events sent to the window get propagated to the chrome event handler
1866 // automatically.)
1867 dispatchTarget = ourWindow->GetParentTarget();
1868 if (!dispatchTarget) {
1869 return;
1870 }
1871 }
1872
1873 if (aTimeStamp.IsNull()) {
1874 aTimeStamp = mozilla::TimeStamp::Now();
1875 }
1876 DOMHighResTimeStamp timeStamp = 0;
1877 if (ourWindow) {
1878 mozilla::dom::Performance* perf = ourWindow->GetPerformance();
1879 if (perf) {
1880 timeStamp = perf->GetDOMTiming()->TimeStampToDOMHighRes(aTimeStamp);
1881 }
1882 }
1883
1884 // Events sent to the window get propagated to the chrome event handler
1885 // automatically.
1886 //
1887 // This will empty our list in case dispatching the event causes more damage
1888 // (hopefully it won't, or we're likely to get an infinite loop! At least
1889 // it won't be blocking app execution though).
1890 RefPtr<NotifyPaintEvent> event =
1891 NS_NewDOMNotifyPaintEvent(eventTarget, this, nullptr, eAfterPaint, aList,
1892 uint64_t(aTransactionId), timeStamp);
1893
1894 // Even if we're not telling the window about the event (so eventTarget is
1895 // the chrome event handler, not the window), the window is still
1896 // logically the event target.
1897 event->SetTarget(eventTarget);
1898 event->SetTrusted(true);
1899 EventDispatcher::DispatchDOMEvent(dispatchTarget, nullptr,
1900 static_cast<Event*>(event), this, nullptr);
1901 }
1902
MayHavePaintEventListener(nsPIDOMWindowInner * aInnerWindow)1903 static bool MayHavePaintEventListener(nsPIDOMWindowInner* aInnerWindow) {
1904 if (!aInnerWindow) return false;
1905 if (aInnerWindow->HasPaintEventListeners()) return true;
1906
1907 EventTarget* parentTarget = aInnerWindow->GetParentTarget();
1908 if (!parentTarget) return false;
1909
1910 EventListenerManager* manager = nullptr;
1911 if ((manager = parentTarget->GetExistingListenerManager()) &&
1912 manager->MayHavePaintEventListener()) {
1913 return true;
1914 }
1915
1916 nsCOMPtr<nsINode> node;
1917 if (parentTarget != aInnerWindow->GetChromeEventHandler()) {
1918 nsCOMPtr<nsIInProcessContentFrameMessageManager> mm =
1919 do_QueryInterface(parentTarget);
1920 if (mm) {
1921 node = mm->GetOwnerContent();
1922 }
1923 }
1924
1925 if (!node) {
1926 node = do_QueryInterface(parentTarget);
1927 }
1928 if (node)
1929 return MayHavePaintEventListener(node->OwnerDoc()->GetInnerWindow());
1930
1931 nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(parentTarget);
1932 if (window) return MayHavePaintEventListener(window);
1933
1934 nsCOMPtr<nsPIWindowRoot> root = do_QueryInterface(parentTarget);
1935 EventTarget* browserChildGlobal;
1936 return root && (browserChildGlobal = root->GetParentTarget()) &&
1937 (manager = browserChildGlobal->GetExistingListenerManager()) &&
1938 manager->MayHavePaintEventListener();
1939 }
1940
MayHavePaintEventListener()1941 bool nsPresContext::MayHavePaintEventListener() {
1942 return ::MayHavePaintEventListener(mDocument->GetInnerWindow());
1943 }
1944
MayHavePaintEventListenerInSubDocument()1945 bool nsPresContext::MayHavePaintEventListenerInSubDocument() {
1946 if (MayHavePaintEventListener()) {
1947 return true;
1948 }
1949
1950 bool result = false;
1951 auto recurse = [&result](dom::Document& aSubDoc) {
1952 if (nsPresContext* pc = aSubDoc.GetPresContext()) {
1953 if (pc->MayHavePaintEventListenerInSubDocument()) {
1954 result = true;
1955 return CallState::Stop;
1956 }
1957 }
1958 return CallState::Continue;
1959 };
1960
1961 mDocument->EnumerateSubDocuments(recurse);
1962 return result;
1963 }
1964
NotifyInvalidation(TransactionId aTransactionId,const nsIntRect & aRect)1965 void nsPresContext::NotifyInvalidation(TransactionId aTransactionId,
1966 const nsIntRect& aRect) {
1967 // Prevent values from overflow after DevPixelsToAppUnits().
1968 //
1969 // DevPixelsTopAppUnits() will multiple a factor (60) to the value,
1970 // it may make the result value over the edge (overflow) of max or
1971 // min value of int32_t. Compute the max sized dev pixel rect that
1972 // we can support and intersect with it.
1973 nsIntRect clampedRect = nsIntRect::MaxIntRect();
1974 clampedRect.ScaleInverseRoundIn(AppUnitsPerDevPixel());
1975
1976 clampedRect = clampedRect.Intersect(aRect);
1977
1978 nsRect rect(DevPixelsToAppUnits(clampedRect.x),
1979 DevPixelsToAppUnits(clampedRect.y),
1980 DevPixelsToAppUnits(clampedRect.width),
1981 DevPixelsToAppUnits(clampedRect.height));
1982 NotifyInvalidation(aTransactionId, rect);
1983 }
1984
GetInvalidations(TransactionId aTransactionId)1985 nsPresContext::TransactionInvalidations* nsPresContext::GetInvalidations(
1986 TransactionId aTransactionId) {
1987 for (TransactionInvalidations& t : mTransactions) {
1988 if (t.mTransactionId == aTransactionId) {
1989 return &t;
1990 }
1991 }
1992 return nullptr;
1993 }
1994
NotifyInvalidation(TransactionId aTransactionId,const nsRect & aRect)1995 void nsPresContext::NotifyInvalidation(TransactionId aTransactionId,
1996 const nsRect& aRect) {
1997 MOZ_ASSERT(GetContainerWeak(), "Invalidation in detached pres context");
1998
1999 // If there is no paint event listener, then we don't need to fire
2000 // the asynchronous event. We don't even need to record invalidation.
2001 // MayHavePaintEventListener is pretty cheap and we could make it
2002 // even cheaper by providing a more efficient
2003 // nsPIDOMWindow::GetListenerManager.
2004
2005 nsPresContext* pc;
2006 for (pc = this; pc; pc = pc->GetParentPresContext()) {
2007 TransactionInvalidations* transaction =
2008 pc->GetInvalidations(aTransactionId);
2009 if (transaction) {
2010 break;
2011 } else {
2012 transaction = pc->mTransactions.AppendElement();
2013 transaction->mTransactionId = aTransactionId;
2014 }
2015 }
2016
2017 TransactionInvalidations* transaction = GetInvalidations(aTransactionId);
2018 MOZ_ASSERT(transaction);
2019 transaction->mInvalidations.AppendElement(aRect);
2020 }
2021
2022 /* static */
NotifySubDocInvalidation(ContainerLayer * aContainer,const nsIntRegion * aRegion)2023 void nsPresContext::NotifySubDocInvalidation(ContainerLayer* aContainer,
2024 const nsIntRegion* aRegion) {
2025 ContainerLayerPresContext* data = static_cast<ContainerLayerPresContext*>(
2026 aContainer->GetUserData(&gNotifySubDocInvalidationData));
2027 if (!data) {
2028 return;
2029 }
2030
2031 TransactionId transactionId = aContainer->Manager()->GetLastTransactionId();
2032 IntRect visibleBounds =
2033 aContainer->GetVisibleRegion().GetBounds().ToUnknownRect();
2034
2035 if (!aRegion) {
2036 IntRect rect(IntPoint(0, 0), visibleBounds.Size());
2037 data->mPresContext->NotifyInvalidation(transactionId, rect);
2038 return;
2039 }
2040
2041 nsIntPoint topLeft = visibleBounds.TopLeft();
2042 for (auto iter = aRegion->RectIter(); !iter.Done(); iter.Next()) {
2043 nsIntRect rect(iter.Get());
2044 // PresContext coordinate space is relative to the start of our visible
2045 // region. Is this really true? This feels like the wrong way to get the
2046 // right answer.
2047 rect.MoveBy(-topLeft);
2048 data->mPresContext->NotifyInvalidation(transactionId, rect);
2049 }
2050 }
2051
SetNotifySubDocInvalidationData(ContainerLayer * aContainer)2052 void nsPresContext::SetNotifySubDocInvalidationData(
2053 ContainerLayer* aContainer) {
2054 ContainerLayerPresContext* pres = new ContainerLayerPresContext;
2055 pres->mPresContext = this;
2056 aContainer->SetUserData(&gNotifySubDocInvalidationData, pres);
2057 }
2058
2059 /* static */
ClearNotifySubDocInvalidationData(ContainerLayer * aContainer)2060 void nsPresContext::ClearNotifySubDocInvalidationData(
2061 ContainerLayer* aContainer) {
2062 aContainer->SetUserData(&gNotifySubDocInvalidationData, nullptr);
2063 }
2064
2065 class DelayedFireDOMPaintEvent : public Runnable {
2066 public:
DelayedFireDOMPaintEvent(nsPresContext * aPresContext,nsTArray<nsRect> && aList,TransactionId aTransactionId,const mozilla::TimeStamp & aTimeStamp=mozilla::TimeStamp ())2067 DelayedFireDOMPaintEvent(
2068 nsPresContext* aPresContext, nsTArray<nsRect>&& aList,
2069 TransactionId aTransactionId,
2070 const mozilla::TimeStamp& aTimeStamp = mozilla::TimeStamp())
2071 : mozilla::Runnable("DelayedFireDOMPaintEvent"),
2072 mPresContext(aPresContext),
2073 mTransactionId(aTransactionId),
2074 mTimeStamp(aTimeStamp),
2075 mList(std::move(aList)) {
2076 MOZ_ASSERT(mPresContext->GetContainerWeak(),
2077 "DOMPaintEvent requested for a detached pres context");
2078 }
Run()2079 NS_IMETHOD Run() override {
2080 // The pres context might have been detached during the delay -
2081 // that's fine, just don't fire the event.
2082 if (mPresContext->GetContainerWeak()) {
2083 mPresContext->FireDOMPaintEvent(&mList, mTransactionId, mTimeStamp);
2084 }
2085 return NS_OK;
2086 }
2087
2088 RefPtr<nsPresContext> mPresContext;
2089 TransactionId mTransactionId;
2090 const mozilla::TimeStamp mTimeStamp;
2091 nsTArray<nsRect> mList;
2092 };
2093
NotifyRevokingDidPaint(TransactionId aTransactionId)2094 void nsPresContext::NotifyRevokingDidPaint(TransactionId aTransactionId) {
2095 if ((IsRoot() || !PresShell()->IsVisible()) && mTransactions.IsEmpty()) {
2096 return;
2097 }
2098
2099 TransactionInvalidations* transaction = nullptr;
2100 for (auto& t : mTransactions) {
2101 if (t.mTransactionId == aTransactionId) {
2102 transaction = &t;
2103 break;
2104 }
2105 }
2106 // If there are no transaction invalidations (which imply callers waiting
2107 // on the event) for this revoked id, then we don't need to fire a
2108 // MozAfterPaint.
2109 if (!transaction) {
2110 return;
2111 }
2112
2113 // If there are queued transactions with an earlier id, we can't send
2114 // our event now since it will arrive out of order. Set the waiting for
2115 // previous transaction flag to true, and we'll send the event when
2116 // the others are completed.
2117 // If this is the only transaction, then we can send it immediately.
2118 if (mTransactions.Length() == 1) {
2119 nsCOMPtr<nsIRunnable> ev = new DelayedFireDOMPaintEvent(
2120 this, std::move(transaction->mInvalidations),
2121 transaction->mTransactionId, mozilla::TimeStamp());
2122 nsContentUtils::AddScriptRunner(ev);
2123 mTransactions.RemoveElementAt(0);
2124 } else {
2125 transaction->mIsWaitingForPreviousTransaction = true;
2126 }
2127
2128 auto recurse = [&aTransactionId](dom::Document& aSubDoc) {
2129 if (nsPresContext* pc = aSubDoc.GetPresContext()) {
2130 pc->NotifyRevokingDidPaint(aTransactionId);
2131 }
2132 return CallState::Continue;
2133 };
2134 mDocument->EnumerateSubDocuments(recurse);
2135 }
2136
NotifyDidPaintForSubtree(TransactionId aTransactionId,const mozilla::TimeStamp & aTimeStamp)2137 void nsPresContext::NotifyDidPaintForSubtree(
2138 TransactionId aTransactionId, const mozilla::TimeStamp& aTimeStamp) {
2139 if (mFirstContentfulPaintTransactionId && !mHadContentfulPaintComposite) {
2140 if (aTransactionId >= *mFirstContentfulPaintTransactionId) {
2141 mHadContentfulPaintComposite = true;
2142 RefPtr<nsDOMNavigationTiming> timing = mDocument->GetNavigationTiming();
2143 if (timing && !IsPrintingOrPrintPreview()) {
2144 timing->NotifyContentfulPaintForRootContentDocument(aTimeStamp);
2145 }
2146 }
2147 }
2148
2149 if (IsRoot() && mTransactions.IsEmpty()) {
2150 return;
2151 }
2152
2153 if (!PresShell()->IsVisible() && mTransactions.IsEmpty()) {
2154 return;
2155 }
2156
2157 // Non-root prescontexts fire MozAfterPaint to all their descendants
2158 // unconditionally, even if no invalidations have been collected. This is
2159 // because we don't want to eat the cost of collecting invalidations for
2160 // every subdocument (which would require putting every subdocument in its
2161 // own layer).
2162
2163 bool sent = false;
2164 uint32_t i = 0;
2165 while (i < mTransactions.Length()) {
2166 if (mTransactions[i].mTransactionId <= aTransactionId) {
2167 if (!mTransactions[i].mInvalidations.IsEmpty()) {
2168 nsCOMPtr<nsIRunnable> ev = new DelayedFireDOMPaintEvent(
2169 this, std::move(mTransactions[i].mInvalidations),
2170 mTransactions[i].mTransactionId, aTimeStamp);
2171 NS_DispatchToCurrentThreadQueue(ev.forget(),
2172 EventQueuePriority::MediumHigh);
2173 sent = true;
2174 }
2175 mTransactions.RemoveElementAt(i);
2176 } else {
2177 // If there are transaction which is waiting for this transaction,
2178 // we should fire a MozAfterPaint immediately.
2179 if (sent && mTransactions[i].mIsWaitingForPreviousTransaction) {
2180 nsCOMPtr<nsIRunnable> ev = new DelayedFireDOMPaintEvent(
2181 this, std::move(mTransactions[i].mInvalidations),
2182 mTransactions[i].mTransactionId, aTimeStamp);
2183 NS_DispatchToCurrentThreadQueue(ev.forget(),
2184 EventQueuePriority::MediumHigh);
2185 sent = true;
2186 mTransactions.RemoveElementAt(i);
2187 continue;
2188 }
2189 i++;
2190 }
2191 }
2192
2193 if (!sent) {
2194 nsTArray<nsRect> dummy;
2195 nsCOMPtr<nsIRunnable> ev = new DelayedFireDOMPaintEvent(
2196 this, std::move(dummy), aTransactionId, aTimeStamp);
2197 NS_DispatchToCurrentThreadQueue(ev.forget(),
2198 EventQueuePriority::MediumHigh);
2199 }
2200
2201 auto recurse = [&aTransactionId, &aTimeStamp](dom::Document& aSubDoc) {
2202 if (nsPresContext* pc = aSubDoc.GetPresContext()) {
2203 pc->NotifyDidPaintForSubtree(aTransactionId, aTimeStamp);
2204 }
2205 return CallState::Continue;
2206 };
2207 mDocument->EnumerateSubDocuments(recurse);
2208 }
2209
CreateTimer(nsTimerCallbackFunc aCallback,const char * aName,uint32_t aDelay)2210 already_AddRefed<nsITimer> nsPresContext::CreateTimer(
2211 nsTimerCallbackFunc aCallback, const char* aName, uint32_t aDelay) {
2212 nsCOMPtr<nsITimer> timer;
2213 NS_NewTimerWithFuncCallback(getter_AddRefs(timer), aCallback, this, aDelay,
2214 nsITimer::TYPE_ONE_SHOT, aName,
2215 Document()->EventTargetFor(TaskCategory::Other));
2216 return timer.forget();
2217 }
2218
2219 static bool sGotInterruptEnv = false;
2220 enum InterruptMode { ModeRandom, ModeCounter, ModeEvent };
2221 // Controlled by the GECKO_REFLOW_INTERRUPT_MODE env var; allowed values are
2222 // "random" (except on Windows) or "counter". If neither is used, the mode is
2223 // ModeEvent.
2224 static InterruptMode sInterruptMode = ModeEvent;
2225 #ifndef XP_WIN
2226 // Used for the "random" mode. Controlled by the GECKO_REFLOW_INTERRUPT_SEED
2227 // env var.
2228 static uint32_t sInterruptSeed = 1;
2229 #endif
2230 // Used for the "counter" mode. This is the number of unskipped interrupt
2231 // checks that have to happen before we interrupt. Controlled by the
2232 // GECKO_REFLOW_INTERRUPT_FREQUENCY env var.
2233 static uint32_t sInterruptMaxCounter = 10;
2234 // Used for the "counter" mode. This counts up to sInterruptMaxCounter and is
2235 // then reset to 0.
2236 static uint32_t sInterruptCounter;
2237 // Number of interrupt checks to skip before really trying to interrupt.
2238 // Controlled by the GECKO_REFLOW_INTERRUPT_CHECKS_TO_SKIP env var.
2239 static uint32_t sInterruptChecksToSkip = 200;
2240 // Number of milliseconds that a reflow should be allowed to run for before we
2241 // actually allow interruption. Controlled by the
2242 // GECKO_REFLOW_MIN_NOINTERRUPT_DURATION env var. Can't be initialized here,
2243 // because TimeDuration/TimeStamp is not safe to use in static constructors..
2244 static TimeDuration sInterruptTimeout;
2245
GetInterruptEnv()2246 static void GetInterruptEnv() {
2247 char* ev = PR_GetEnv("GECKO_REFLOW_INTERRUPT_MODE");
2248 if (ev) {
2249 #ifndef XP_WIN
2250 if (PL_strcasecmp(ev, "random") == 0) {
2251 ev = PR_GetEnv("GECKO_REFLOW_INTERRUPT_SEED");
2252 if (ev) {
2253 sInterruptSeed = atoi(ev);
2254 }
2255 srandom(sInterruptSeed);
2256 sInterruptMode = ModeRandom;
2257 } else
2258 #endif
2259 if (PL_strcasecmp(ev, "counter") == 0) {
2260 ev = PR_GetEnv("GECKO_REFLOW_INTERRUPT_FREQUENCY");
2261 if (ev) {
2262 sInterruptMaxCounter = atoi(ev);
2263 }
2264 sInterruptCounter = 0;
2265 sInterruptMode = ModeCounter;
2266 }
2267 }
2268 ev = PR_GetEnv("GECKO_REFLOW_INTERRUPT_CHECKS_TO_SKIP");
2269 if (ev) {
2270 sInterruptChecksToSkip = atoi(ev);
2271 }
2272
2273 ev = PR_GetEnv("GECKO_REFLOW_MIN_NOINTERRUPT_DURATION");
2274 int duration_ms = ev ? atoi(ev) : 100;
2275 sInterruptTimeout = TimeDuration::FromMilliseconds(duration_ms);
2276 }
2277
HavePendingInputEvent()2278 bool nsPresContext::HavePendingInputEvent() {
2279 switch (sInterruptMode) {
2280 #ifndef XP_WIN
2281 case ModeRandom:
2282 return (random() & 1);
2283 #endif
2284 case ModeCounter:
2285 if (sInterruptCounter < sInterruptMaxCounter) {
2286 ++sInterruptCounter;
2287 return false;
2288 }
2289 sInterruptCounter = 0;
2290 return true;
2291 default:
2292 case ModeEvent: {
2293 nsIFrame* f = PresShell()->GetRootFrame();
2294 if (f) {
2295 nsIWidget* w = f->GetNearestWidget();
2296 if (w) {
2297 return w->HasPendingInputEvent();
2298 }
2299 }
2300 return false;
2301 }
2302 }
2303 }
2304
HasPendingRestyleOrReflow()2305 bool nsPresContext::HasPendingRestyleOrReflow() {
2306 mozilla::PresShell* presShell = PresShell();
2307 return presShell->NeedStyleFlush() || presShell->HasPendingReflow();
2308 }
2309
ReflowStarted(bool aInterruptible)2310 void nsPresContext::ReflowStarted(bool aInterruptible) {
2311 #ifdef NOISY_INTERRUPTIBLE_REFLOW
2312 if (!aInterruptible) {
2313 printf("STARTING NONINTERRUPTIBLE REFLOW\n");
2314 }
2315 #endif
2316 // We don't support interrupting in paginated contexts, since page
2317 // sequences only handle initial reflow
2318 mInterruptsEnabled = aInterruptible && !IsPaginated() &&
2319 StaticPrefs::layout_interruptible_reflow_enabled();
2320
2321 // Don't set mHasPendingInterrupt based on HavePendingInputEvent() here. If
2322 // we ever change that, then we need to update the code in
2323 // PresShell::DoReflow to only add the just-reflown root to dirty roots if
2324 // it's actually dirty. Otherwise we can end up adding a root that has no
2325 // interruptible descendants, just because we detected an interrupt at reflow
2326 // start.
2327 mHasPendingInterrupt = false;
2328
2329 mInterruptChecksToSkip = sInterruptChecksToSkip;
2330
2331 if (mInterruptsEnabled) {
2332 mReflowStartTime = TimeStamp::Now();
2333 }
2334 }
2335
CheckForInterrupt(nsIFrame * aFrame)2336 bool nsPresContext::CheckForInterrupt(nsIFrame* aFrame) {
2337 if (mHasPendingInterrupt) {
2338 mPresShell->FrameNeedsToContinueReflow(aFrame);
2339 return true;
2340 }
2341
2342 if (!sGotInterruptEnv) {
2343 sGotInterruptEnv = true;
2344 GetInterruptEnv();
2345 }
2346
2347 if (!mInterruptsEnabled) {
2348 return false;
2349 }
2350
2351 if (mInterruptChecksToSkip > 0) {
2352 --mInterruptChecksToSkip;
2353 return false;
2354 }
2355 mInterruptChecksToSkip = sInterruptChecksToSkip;
2356
2357 // Don't interrupt if it's been less than sInterruptTimeout since we started
2358 // the reflow.
2359 mHasPendingInterrupt =
2360 TimeStamp::Now() - mReflowStartTime > sInterruptTimeout &&
2361 HavePendingInputEvent() && !IsChrome();
2362
2363 if (mPendingInterruptFromTest) {
2364 mPendingInterruptFromTest = false;
2365 mHasPendingInterrupt = true;
2366 }
2367
2368 if (mHasPendingInterrupt) {
2369 #ifdef NOISY_INTERRUPTIBLE_REFLOW
2370 printf("*** DETECTED pending interrupt (time=%lld)\n", PR_Now());
2371 #endif /* NOISY_INTERRUPTIBLE_REFLOW */
2372 mPresShell->FrameNeedsToContinueReflow(aFrame);
2373 }
2374 return mHasPendingInterrupt;
2375 }
2376
GetPrimaryFrameFor(nsIContent * aContent)2377 nsIFrame* nsPresContext::GetPrimaryFrameFor(nsIContent* aContent) {
2378 MOZ_ASSERT(aContent, "Don't do that");
2379 if (GetPresShell() &&
2380 GetPresShell()->GetDocument() == aContent->GetComposedDoc()) {
2381 return aContent->GetPrimaryFrame();
2382 }
2383 return nullptr;
2384 }
2385
SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const2386 size_t nsPresContext::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
2387 // Measurement may be added later if DMD finds it is worthwhile.
2388 return 0;
2389 }
2390
IsRootContentDocument() const2391 bool nsPresContext::IsRootContentDocument() const {
2392 // Default to the in process version for now, but we should try
2393 // to remove callers of this.
2394 return IsRootContentDocumentInProcess();
2395 }
2396
IsRootContentDocumentInProcess() const2397 bool nsPresContext::IsRootContentDocumentInProcess() const {
2398 if (mDocument->IsResourceDoc()) {
2399 return false;
2400 }
2401 if (IsChrome()) {
2402 return false;
2403 }
2404 // We may not have a root frame, so use views.
2405 nsView* view = PresShell()->GetViewManager()->GetRootView();
2406 if (!view) {
2407 return false;
2408 }
2409 view = view->GetParent(); // anonymous inner view
2410 if (!view) {
2411 return true;
2412 }
2413 view = view->GetParent(); // subdocumentframe's view
2414 if (!view) {
2415 return true;
2416 }
2417
2418 nsIFrame* f = view->GetFrame();
2419 return (f && f->PresContext()->IsChrome());
2420 }
2421
IsRootContentDocumentCrossProcess() const2422 bool nsPresContext::IsRootContentDocumentCrossProcess() const {
2423 if (mDocument->IsResourceDoc()) {
2424 return false;
2425 }
2426
2427 return mDocument->IsTopLevelContentDocument();
2428 }
2429
NotifyNonBlankPaint()2430 void nsPresContext::NotifyNonBlankPaint() {
2431 MOZ_ASSERT(!mHadNonBlankPaint);
2432 mHadNonBlankPaint = true;
2433 if (IsRootContentDocument()) {
2434 RefPtr<nsDOMNavigationTiming> timing = mDocument->GetNavigationTiming();
2435 if (timing && !IsPrintingOrPrintPreview()) {
2436 timing->NotifyNonBlankPaintForRootContentDocument();
2437 }
2438
2439 mFirstNonBlankPaintTime = TimeStamp::Now();
2440 }
2441 }
2442
NotifyContentfulPaint()2443 void nsPresContext::NotifyContentfulPaint() {
2444 nsRootPresContext* rootPresContext = GetRootPresContext();
2445 if (!rootPresContext) {
2446 return;
2447 }
2448 if (!mHadContentfulPaint) {
2449 #if defined(MOZ_WIDGET_ANDROID)
2450 if (!mHadNonTickContentfulPaint) {
2451 (new AsyncEventDispatcher(mDocument, u"MozFirstContentfulPaint"_ns,
2452 CanBubble::eYes, ChromeOnlyDispatch::eYes))
2453 ->PostDOMEvent();
2454 }
2455 #endif
2456 if (!rootPresContext->RefreshDriver()->IsInRefresh()) {
2457 if (!mHadNonTickContentfulPaint) {
2458 rootPresContext->RefreshDriver()
2459 ->AddForceNotifyContentfulPaintPresContext(this);
2460 mHadNonTickContentfulPaint = true;
2461 }
2462 return;
2463 }
2464 mHadContentfulPaint = true;
2465 mFirstContentfulPaintTransactionId =
2466 Some(rootPresContext->mRefreshDriver->LastTransactionId().Next());
2467 if (nsPIDOMWindowInner* innerWindow = mDocument->GetInnerWindow()) {
2468 if (Performance* perf = innerWindow->GetPerformance()) {
2469 TimeStamp nowTime = rootPresContext->RefreshDriver()->MostRecentRefresh(
2470 /* aEnsureTimerStarted */ false);
2471 MOZ_ASSERT(!nowTime.IsNull(),
2472 "Most recent refresh timestamp should exist since we are in "
2473 "a refresh driver tick");
2474 MOZ_ASSERT(rootPresContext->RefreshDriver()->IsInRefresh(),
2475 "We should only notify contentful paint during refresh "
2476 "driver ticks");
2477 RefPtr<PerformancePaintTiming> paintTiming = new PerformancePaintTiming(
2478 perf, u"first-contentful-paint"_ns, nowTime);
2479 perf->SetFCPTimingEntry(paintTiming);
2480 }
2481 }
2482 }
2483 }
2484
NotifyPaintStatusReset()2485 void nsPresContext::NotifyPaintStatusReset() {
2486 mHadNonBlankPaint = false;
2487 mHadContentfulPaint = false;
2488 #if defined(MOZ_WIDGET_ANDROID)
2489 (new AsyncEventDispatcher(mDocument, u"MozPaintStatusReset"_ns,
2490 CanBubble::eYes, ChromeOnlyDispatch::eYes))
2491 ->PostDOMEvent();
2492 #endif
2493 mHadNonTickContentfulPaint = false;
2494 }
2495
NotifyDOMContentFlushed()2496 void nsPresContext::NotifyDOMContentFlushed() {
2497 NS_ENSURE_TRUE_VOID(mPresShell);
2498 if (IsRootContentDocument()) {
2499 RefPtr<nsDOMNavigationTiming> timing = mDocument->GetNavigationTiming();
2500 if (timing) {
2501 timing->NotifyDOMContentFlushedForRootContentDocument();
2502 }
2503 }
2504 }
2505
GetPaintFlashing() const2506 bool nsPresContext::GetPaintFlashing() const {
2507 if (!mPaintFlashingInitialized) {
2508 bool pref = Preferences::GetBool("nglayout.debug.paint_flashing");
2509 if (!pref && IsChrome()) {
2510 pref = Preferences::GetBool("nglayout.debug.paint_flashing_chrome");
2511 }
2512 mPaintFlashing = pref;
2513 mPaintFlashingInitialized = true;
2514 }
2515 return mPaintFlashing;
2516 }
2517
GfxUnitsToAppUnits(gfxFloat aGfxUnits) const2518 nscoord nsPresContext::GfxUnitsToAppUnits(gfxFloat aGfxUnits) const {
2519 return mDeviceContext->GfxUnitsToAppUnits(aGfxUnits);
2520 }
2521
AppUnitsToGfxUnits(nscoord aAppUnits) const2522 gfxFloat nsPresContext::AppUnitsToGfxUnits(nscoord aAppUnits) const {
2523 return mDeviceContext->AppUnitsToGfxUnits(aAppUnits);
2524 }
2525
PhysicalMillimetersToAppUnits(float aMM) const2526 nscoord nsPresContext::PhysicalMillimetersToAppUnits(float aMM) const {
2527 float inches = aMM / MM_PER_INCH_FLOAT;
2528 return NSToCoordFloorClamped(
2529 inches * float(DeviceContext()->AppUnitsPerPhysicalInch()));
2530 }
2531
IsDeviceSizePageSize()2532 bool nsPresContext::IsDeviceSizePageSize() {
2533 nsIDocShell* docShell = GetDocShell();
2534 return docShell && docShell->GetDeviceSizeIsPageSize();
2535 }
2536
GetRestyleGeneration() const2537 uint64_t nsPresContext::GetRestyleGeneration() const {
2538 if (!mRestyleManager) {
2539 return 0;
2540 }
2541 return mRestyleManager->GetRestyleGeneration();
2542 }
2543
GetUndisplayedRestyleGeneration() const2544 uint64_t nsPresContext::GetUndisplayedRestyleGeneration() const {
2545 if (!mRestyleManager) {
2546 return 0;
2547 }
2548 return mRestyleManager->GetUndisplayedRestyleGeneration();
2549 }
2550
GetBidiEngine()2551 nsBidi& nsPresContext::GetBidiEngine() {
2552 MOZ_ASSERT(NS_IsMainThread());
2553
2554 if (!mBidiEngine) {
2555 mBidiEngine.reset(new nsBidi());
2556 }
2557 return *mBidiEngine;
2558 }
2559
FlushFontFeatureValues()2560 void nsPresContext::FlushFontFeatureValues() {
2561 if (!mPresShell) {
2562 return; // we've been torn down
2563 }
2564
2565 if (mFontFeatureValuesDirty) {
2566 ServoStyleSet* styleSet = mPresShell->StyleSet();
2567 mFontFeatureValuesLookup = styleSet->BuildFontFeatureValueSet();
2568 mFontFeatureValuesDirty = false;
2569 }
2570 }
2571
SetVisibleArea(const nsRect & r)2572 void nsPresContext::SetVisibleArea(const nsRect& r) {
2573 if (!r.IsEqualEdges(mVisibleArea)) {
2574 mVisibleArea = r;
2575 mSizeForViewportUnits = mVisibleArea.Size();
2576 if (IsRootContentDocumentCrossProcess()) {
2577 AdjustSizeForViewportUnits();
2578 }
2579 // Visible area does not affect media queries when paginated.
2580 if (!IsRootPaginatedDocument()) {
2581 MediaFeatureValuesChanged(
2582 {mozilla::MediaFeatureChangeReason::ViewportChange},
2583 MediaFeatureChangePropagation::JustThisDocument);
2584 }
2585 }
2586 }
2587
SetDynamicToolbarMaxHeight(ScreenIntCoord aHeight)2588 void nsPresContext::SetDynamicToolbarMaxHeight(ScreenIntCoord aHeight) {
2589 MOZ_ASSERT(IsRootContentDocumentCrossProcess());
2590
2591 if (mDynamicToolbarMaxHeight == aHeight) {
2592 return;
2593 }
2594 mDynamicToolbarMaxHeight = aHeight;
2595 mDynamicToolbarHeight = aHeight;
2596
2597 AdjustSizeForViewportUnits();
2598
2599 if (RefPtr<mozilla::PresShell> presShell = mPresShell) {
2600 // Changing the max height of the dynamic toolbar changes the ICB size, we
2601 // need to kick a reflow with the current window dimensions since the max
2602 // height change doesn't change the window dimensions but
2603 // PresShell::ResizeReflow ends up subtracting the new dynamic toolbar
2604 // height from the window dimensions and kick a reflow with the proper ICB
2605 // size.
2606 nscoord currentWidth, currentHeight;
2607 presShell->GetViewManager()->GetWindowDimensions(¤tWidth,
2608 ¤tHeight);
2609 presShell->ResizeReflow(currentWidth, currentHeight,
2610 ResizeReflowOptions::NoOption);
2611 }
2612 }
2613
AdjustSizeForViewportUnits()2614 void nsPresContext::AdjustSizeForViewportUnits() {
2615 MOZ_ASSERT(IsRootContentDocumentCrossProcess());
2616 if (mVisibleArea.height == NS_UNCONSTRAINEDSIZE) {
2617 // Ignore `NS_UNCONSTRAINEDSIZE` since it's a temporary state during a
2618 // reflow. We will end up calling this function again with a proper size in
2619 // the same reflow.
2620 return;
2621 }
2622
2623 if (MOZ_UNLIKELY(mVisibleArea.height +
2624 NSIntPixelsToAppUnits(mDynamicToolbarMaxHeight,
2625 mCurAppUnitsPerDevPixel) >
2626 nscoord_MAX)) {
2627 MOZ_ASSERT_UNREACHABLE("The dynamic toolbar max height is probably wrong");
2628 return;
2629 }
2630
2631 mSizeForViewportUnits.height =
2632 mVisibleArea.height +
2633 NSIntPixelsToAppUnits(mDynamicToolbarMaxHeight, mCurAppUnitsPerDevPixel);
2634 }
2635
UpdateDynamicToolbarOffset(ScreenIntCoord aOffset)2636 void nsPresContext::UpdateDynamicToolbarOffset(ScreenIntCoord aOffset) {
2637 MOZ_ASSERT(IsRootContentDocumentCrossProcess());
2638 if (!mPresShell) {
2639 return;
2640 }
2641
2642 if (!HasDynamicToolbar()) {
2643 return;
2644 }
2645
2646 MOZ_ASSERT(-mDynamicToolbarMaxHeight <= aOffset && aOffset <= 0);
2647 if (mDynamicToolbarHeight == mDynamicToolbarMaxHeight + aOffset) {
2648 return;
2649 }
2650
2651 // Forcibly flush position:fixed elements in the case where the dynamic
2652 // toolbar is going to be completely hidden or starts to be visible so that
2653 // %-based style values will be recomputed with the visual viewport size which
2654 // is including the area covered by the dynamic toolbar.
2655 if (mDynamicToolbarHeight == 0 || aOffset == -mDynamicToolbarMaxHeight) {
2656 mPresShell->MarkFixedFramesForReflow(IntrinsicDirty::Resize);
2657 mPresShell->AddResizeEventFlushObserverIfNeeded();
2658 }
2659
2660 mDynamicToolbarHeight = mDynamicToolbarMaxHeight + aOffset;
2661
2662 if (RefPtr<MobileViewportManager> mvm =
2663 mPresShell->GetMobileViewportManager()) {
2664 mvm->UpdateVisualViewportSizeByDynamicToolbar(-aOffset);
2665 }
2666 }
2667
GetDynamicToolbarState() const2668 DynamicToolbarState nsPresContext::GetDynamicToolbarState() const {
2669 if (!IsRootContentDocumentCrossProcess() || !HasDynamicToolbar()) {
2670 return DynamicToolbarState::None;
2671 }
2672
2673 if (mDynamicToolbarMaxHeight == mDynamicToolbarHeight) {
2674 return DynamicToolbarState::Expanded;
2675 } else if (mDynamicToolbarHeight == 0) {
2676 return DynamicToolbarState::Collapsed;
2677 }
2678 return DynamicToolbarState::InTransition;
2679 }
2680
SetSafeAreaInsets(const ScreenIntMargin & aSafeAreaInsets)2681 void nsPresContext::SetSafeAreaInsets(const ScreenIntMargin& aSafeAreaInsets) {
2682 if (mSafeAreaInsets == aSafeAreaInsets) {
2683 return;
2684 }
2685 mSafeAreaInsets = aSafeAreaInsets;
2686
2687 PostRebuildAllStyleDataEvent(nsChangeHint(0),
2688 RestyleHint::RecascadeSubtree());
2689 }
2690
2691 #ifdef DEBUG
2692
ValidatePresShellAndDocumentReleation() const2693 void nsPresContext::ValidatePresShellAndDocumentReleation() const {
2694 NS_ASSERTION(!mPresShell || !mPresShell->GetDocument() ||
2695 mPresShell->GetDocument() == mDocument,
2696 "nsPresContext doesn't have the same document as nsPresShell!");
2697 }
2698
2699 #endif // #ifdef DEBUG
2700
nsRootPresContext(dom::Document * aDocument,nsPresContextType aType)2701 nsRootPresContext::nsRootPresContext(dom::Document* aDocument,
2702 nsPresContextType aType)
2703 : nsPresContext(aDocument, aType) {}
2704
AddWillPaintObserver(nsIRunnable * aRunnable)2705 void nsRootPresContext::AddWillPaintObserver(nsIRunnable* aRunnable) {
2706 if (!mWillPaintFallbackEvent.IsPending()) {
2707 mWillPaintFallbackEvent = new RunWillPaintObservers(this);
2708 Document()->Dispatch(TaskCategory::Other,
2709 do_AddRef(mWillPaintFallbackEvent));
2710 }
2711 mWillPaintObservers.AppendElement(aRunnable);
2712 }
2713
2714 /**
2715 * Run all runnables that need to get called before the next paint.
2716 */
FlushWillPaintObservers()2717 void nsRootPresContext::FlushWillPaintObservers() {
2718 mWillPaintFallbackEvent = nullptr;
2719 nsTArray<nsCOMPtr<nsIRunnable>> observers = std::move(mWillPaintObservers);
2720 for (uint32_t i = 0; i < observers.Length(); ++i) {
2721 observers[i]->Run();
2722 }
2723 }
2724
SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const2725 size_t nsRootPresContext::SizeOfExcludingThis(
2726 MallocSizeOf aMallocSizeOf) const {
2727 return nsPresContext::SizeOfExcludingThis(aMallocSizeOf);
2728
2729 // Measurement of the following members may be added later if DMD finds it is
2730 // worthwhile:
2731 // - mWillPaintObservers
2732 // - mWillPaintFallbackEvent
2733 }
2734