Bug 1709347 - Add CanvasRenderingContext2D.reset(). r=lsalzman,webidl,smaug
[gecko.git] / layout / base / nsPresContext.cpp
blobc33fc085d3eba911be2079c4a787d4fe8f481bbe
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/. */
7 /* a presentation of a document, part 1 */
9 #include "nsPresContext.h"
10 #include "nsPresContextInlines.h"
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"
24 #include "base/basictypes.h"
25 #include "nsCRT.h"
26 #include "nsCOMPtr.h"
27 #include "nsCSSFrameConstructor.h"
28 #include "nsDocShell.h"
29 #include "nsIConsoleService.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 "gfxPlatform.h"
50 #include "nsFontFaceLoader.h"
51 #include "mozilla/AnimationEventDispatcher.h"
52 #include "mozilla/EffectCompositor.h"
53 #include "mozilla/EventListenerManager.h"
54 #include "prenv.h"
55 #include "nsTransitionManager.h"
56 #include "nsAnimationManager.h"
57 #include "CounterStyleManager.h"
58 #include "mozilla/MemoryReporting.h"
59 #include "mozilla/dom/Element.h"
60 #include "nsIMessageManager.h"
61 #include "mozilla/dom/HTMLBodyElement.h"
62 #include "mozilla/dom/MediaQueryList.h"
63 #include "mozilla/SMILAnimationController.h"
64 #include "mozilla/css/ImageLoader.h"
65 #include "mozilla/dom/PBrowserParent.h"
66 #include "mozilla/dom/BrowserChild.h"
67 #include "mozilla/dom/BrowserParent.h"
68 #include "mozilla/dom/FontFaceSet.h"
69 #include "mozilla/StaticPresData.h"
70 #include "nsRefreshDriver.h"
71 #include "LayerUserData.h"
72 #include "mozilla/dom/NotifyPaintEvent.h"
73 #include "nsFontCache.h"
74 #include "nsFrameLoader.h"
75 #include "nsContentUtils.h"
76 #include "nsPIWindowRoot.h"
77 #include "mozilla/Preferences.h"
78 #include "gfxTextRun.h"
79 #include "nsFontFaceUtils.h"
80 #include "mozilla/GlobalStyleSheetCache.h"
81 #include "mozilla/ServoBindings.h"
82 #include "mozilla/StaticPrefs_bidi.h"
83 #include "mozilla/StaticPrefs_layout.h"
84 #include "mozilla/StaticPrefs_widget.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/TimelineManager.h"
90 #include "mozilla/dom/Performance.h"
91 #include "mozilla/dom/PerformanceTiming.h"
92 #include "mozilla/dom/PerformancePaintTiming.h"
93 #include "mozilla/layers/APZThreadUtils.h"
94 #include "MobileViewportManager.h"
95 #include "mozilla/dom/ImageTracker.h"
96 #ifdef ACCESSIBILITY
97 # include "mozilla/a11y/DocAccessible.h"
98 #endif
100 // Needed for Start/Stop of Image Animation
101 #include "imgIContainer.h"
102 #include "nsIImageLoadingContent.h"
104 #include "nsBidiUtils.h"
105 #include "nsServiceManagerUtils.h"
107 #include "mozilla/dom/URL.h"
108 #include "mozilla/ServoCSSParser.h"
110 using namespace mozilla;
111 using namespace mozilla::dom;
112 using namespace mozilla::gfx;
113 using namespace mozilla::layers;
116 * Layer UserData for ContainerLayers that want to be notified
117 * of local invalidations of them and their descendant layers.
118 * Pass a callback to ComputeDifferences to have these called.
120 class ContainerLayerPresContext : public LayerUserData {
121 public:
122 nsPresContext* mPresContext;
125 bool nsPresContext::IsDOMPaintEventPending() {
126 if (!mTransactions.IsEmpty()) {
127 return true;
130 nsRootPresContext* drpc = GetRootPresContext();
131 if (drpc && drpc->mRefreshDriver->ViewManagerFlushIsPending()) {
132 // Since we're promising that there will be a MozAfterPaint event
133 // fired, we record an empty invalidation in case display list
134 // invalidation doesn't invalidate anything further.
135 NotifyInvalidation(drpc->mRefreshDriver->LastTransactionId().Next(),
136 nsRect(0, 0, 0, 0));
137 return true;
139 return false;
142 struct WeakRunnableMethod : Runnable {
143 using Method = void (nsPresContext::*)();
145 WeakRunnableMethod(const char* aName, nsPresContext* aPc, Method aMethod)
146 : Runnable(aName), mPresContext(aPc), mMethod(aMethod) {}
148 NS_IMETHOD Run() override {
149 if (nsPresContext* pc = mPresContext.get()) {
150 (pc->*mMethod)();
152 return NS_OK;
155 private:
156 WeakPtr<nsPresContext> mPresContext;
157 Method mMethod;
160 // When forcing a font-info-update reflow from style, we don't need to reframe,
161 // but we'll need to restyle to pick up updated font metrics. In order to avoid
162 // synchronously having to deal with multiple restyles, we use an early refresh
163 // driver runner, which should prevent flashing for users.
165 // We might do a bit of extra work if the page flushes layout between the
166 // restyle and when this happens, which is a bit unfortunate, but not worse than
167 // what we used to do...
169 // A better solution would be to be able to synchronously initialize font
170 // information from style worker threads, perhaps...
171 void nsPresContext::ForceReflowForFontInfoUpdateFromStyle() {
172 if (mPendingFontInfoUpdateReflowFromStyle) {
173 return;
176 mPendingFontInfoUpdateReflowFromStyle = true;
177 nsCOMPtr<nsIRunnable> ev = new WeakRunnableMethod(
178 "nsPresContext::DoForceReflowForFontInfoUpdateFromStyle", this,
179 &nsPresContext::DoForceReflowForFontInfoUpdateFromStyle);
180 RefreshDriver()->AddEarlyRunner(ev);
183 void nsPresContext::DoForceReflowForFontInfoUpdateFromStyle() {
184 mPendingFontInfoUpdateReflowFromStyle = false;
185 ForceReflowForFontInfoUpdate(false);
188 void nsPresContext::ForceReflowForFontInfoUpdate(bool aNeedsReframe) {
189 // In the case of a static-clone document used for printing or print-preview,
190 // this is undesirable because the nsPrintJob is holding weak refs to frames
191 // that will get blown away unexpectedly by this reconstruction. So the
192 // prescontext for a print/preview doc ignores the font-list update.
194 // This means the print document may still be using cached fonts that are no
195 // longer present in the font list, but that should be safe given that all the
196 // required font instances have already been created, so it won't be depending
197 // on access to the font-list entries.
199 // XXX Actually, I think it's probably a bad idea to do *any* restyling of
200 // print documents in response to pref changes. We could be in the middle
201 // of printing the document, and reflowing all the frames might cause some
202 // kind of unwanted mid-document discontinuity.
203 if (IsPrintingOrPrintPreview()) {
204 return;
207 // If there's a user font set, discard any src:local() faces it may have
208 // loaded because their font entries may no longer be valid.
209 if (auto* fonts = Document()->GetFonts()) {
210 fonts->GetImpl()->ForgetLocalFaces();
213 FlushFontCache();
215 nsChangeHint changeHint =
216 aNeedsReframe ? nsChangeHint_ReconstructFrame : NS_STYLE_HINT_REFLOW;
218 // We also need to trigger restyling for ex/ch units changes to take effect,
219 // if needed.
220 auto restyleHint = StyleSet()->UsesFontMetrics()
221 ? RestyleHint::RecascadeSubtree()
222 : RestyleHint{0};
224 RebuildAllStyleData(changeHint, restyleHint);
227 static bool IsVisualCharset(NotNull<const Encoding*> aCharset) {
228 return aCharset == ISO_8859_8_ENCODING;
231 nsPresContext::nsPresContext(dom::Document* aDocument, nsPresContextType aType)
232 : mPresShell(nullptr),
233 mDocument(aDocument),
234 mMedium(aType == eContext_Galley ? nsGkAtoms::screen : nsGkAtoms::print),
235 mSystemFontScale(1.0),
236 mTextZoom(1.0),
237 mEffectiveTextZoom(1.0),
238 mFullZoom(1.0),
239 mLastFontInflationScreenSize(gfxSize(-1.0, -1.0)),
240 mCurAppUnitsPerDevPixel(0),
241 mAutoQualityMinFontSizePixelsPref(0),
242 mDynamicToolbarMaxHeight(0),
243 mDynamicToolbarHeight(0),
244 mPageSize(-1, -1),
245 mPageScale(0.0),
246 mPPScale(1.0f),
247 mViewportScrollOverrideElement(nullptr),
248 mElementsRestyled(0),
249 mFramesConstructed(0),
250 mFramesReflowed(0),
251 mInterruptChecksToSkip(0),
252 mNextFrameRateMultiplier(0),
253 mViewportScrollStyles(StyleOverflow::Auto, StyleOverflow::Auto),
254 // mImageAnimationMode is initialised below, in constructor body
255 mImageAnimationModePref(imgIContainer::kNormalAnimMode),
256 mType(aType),
257 mInflationDisabledForShrinkWrap(false),
258 mInteractionTimeEnabled(true),
259 mHasPendingInterrupt(false),
260 mHasEverBuiltInvisibleText(false),
261 mPendingInterruptFromTest(false),
262 mInterruptsEnabled(false),
263 mSendAfterPaintToContent(false),
264 mDrawImageBackground(true), // always draw the background
265 mDrawColorBackground(true),
266 // mNeverAnimate is initialised below, in constructor body
267 mPaginated(aType != eContext_Galley),
268 mCanPaginatedScroll(false),
269 mDoScaledTwips(true),
270 mIsRootPaginatedDocument(false),
271 mPrefBidiDirection(false),
272 mPrefScrollbarSide(0),
273 mPendingThemeChanged(false),
274 mPendingThemeChangeKind(0),
275 mPendingUIResolutionChanged(false),
276 mPendingFontInfoUpdateReflowFromStyle(false),
277 mIsGlyph(false),
278 mCounterStylesDirty(true),
279 mFontFeatureValuesDirty(true),
280 mFontPaletteValuesDirty(true),
281 mIsVisual(false),
282 mInRDMPane(false),
283 mHasWarnedAboutTooLargeDashedOrDottedRadius(false),
284 mQuirkSheetAdded(false),
285 mHadNonBlankPaint(false),
286 mHadContentfulPaint(false),
287 mHadNonTickContentfulPaint(false),
288 mHadContentfulPaintComposite(false),
289 #ifdef DEBUG
290 mInitialized(false),
291 #endif
292 mOverriddenOrEmbedderColorScheme(dom::PrefersColorSchemeOverride::None) {
293 #ifdef DEBUG
294 PodZero(&mLayoutPhaseCount);
295 #endif
297 if (!IsDynamic()) {
298 mImageAnimationMode = imgIContainer::kDontAnimMode;
299 mNeverAnimate = true;
300 } else {
301 mImageAnimationMode = imgIContainer::kNormalAnimMode;
302 mNeverAnimate = false;
304 NS_ASSERTION(mDocument, "Null document");
306 // if text perf logging enabled, init stats struct
307 if (MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_textperf), LogLevel::Warning)) {
308 mTextPerf = MakeUnique<gfxTextPerfMetrics>();
311 if (StaticPrefs::gfx_missing_fonts_notify()) {
312 mMissingFonts = MakeUnique<gfxMissingFontRecorder>();
315 if (StaticPrefs::layout_dynamic_toolbar_max_height() > 0) {
316 // The pref for dynamic toolbar max height is only used in reftests so it's
317 // fine to set here.
318 mDynamicToolbarMaxHeight = StaticPrefs::layout_dynamic_toolbar_max_height();
321 UpdateFontVisibility();
324 static const char* gExactCallbackPrefs[] = {
325 "browser.active_color",
326 "browser.anchor_color",
327 "browser.underline_anchors",
328 "browser.visited_color",
329 "dom.meta-viewport.enabled",
330 "dom.send_after_paint_to_content",
331 "image.animation_mode",
332 "intl.accept_languages",
333 "layout.css.devPixelsPerPx",
334 "layout.css.dpi",
335 "privacy.trackingprotection.enabled",
336 "ui.use_standins_for_native_colors",
337 nullptr,
340 static const char* gPrefixCallbackPrefs[] = {
341 "bidi.", "browser.display.", "browser.viewport.",
342 "font.", "gfx.font_rendering.", "layout.css.font-visibility.",
343 nullptr,
346 void nsPresContext::Destroy() {
347 if (mEventManager) {
348 // unclear if these are needed, but can't hurt
349 mEventManager->NotifyDestroyPresContext(this);
350 mEventManager->SetPresContext(nullptr);
351 mEventManager = nullptr;
354 if (mFontCache) {
355 mFontCache->Destroy();
356 mFontCache = nullptr;
359 // Unregister preference callbacks
360 Preferences::UnregisterPrefixCallbacks(nsPresContext::PreferenceChanged,
361 gPrefixCallbackPrefs, this);
362 Preferences::UnregisterCallbacks(nsPresContext::PreferenceChanged,
363 gExactCallbackPrefs, this);
365 mRefreshDriver = nullptr;
366 MOZ_ASSERT(mManagedPostRefreshObservers.IsEmpty());
369 nsPresContext::~nsPresContext() {
370 MOZ_ASSERT(!mPresShell, "Presshell forgot to clear our mPresShell pointer");
371 DetachPresShell();
373 Destroy();
376 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsPresContext)
377 NS_INTERFACE_MAP_ENTRY(nsISupports)
378 NS_INTERFACE_MAP_END
380 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsPresContext)
381 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(nsPresContext, LastRelease())
383 void nsPresContext::LastRelease() {
384 if (mMissingFonts) {
385 mMissingFonts->Clear();
389 NS_IMPL_CYCLE_COLLECTION_CLASS(nsPresContext)
391 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsPresContext)
392 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnimationEventDispatcher);
393 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument);
394 // NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mDeviceContext); // not xpcom
395 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEffectCompositor);
396 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEventManager);
397 // NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mLanguage); // an atom
399 // NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTheme); // a service
400 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrintSettings);
401 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
403 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsPresContext)
404 NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnimationEventDispatcher);
405 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument);
406 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDeviceContext); // worth bothering?
407 NS_IMPL_CYCLE_COLLECTION_UNLINK(mEffectCompositor);
408 // NS_RELEASE(tmp->mLanguage); // an atom
409 // NS_IMPL_CYCLE_COLLECTION_UNLINK(mTheme); // a service
410 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrintSettings);
411 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_PTR
413 tmp->Destroy();
414 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
416 bool nsPresContext::IsChrome() const {
417 return Document()->IsInChromeDocShell();
420 void nsPresContext::GetUserPreferences() {
421 if (!GetPresShell()) {
422 // No presshell means nothing to do here. We'll do this when we
423 // get a presshell.
424 return;
427 mAutoQualityMinFontSizePixelsPref =
428 Preferences::GetInt("browser.display.auto_quality_min_font_size");
430 PreferenceSheet::EnsureInitialized();
432 mSendAfterPaintToContent = Preferences::GetBool(
433 "dom.send_after_paint_to_content", mSendAfterPaintToContent);
435 mPrefScrollbarSide = Preferences::GetInt("layout.scrollbar.side");
437 Document()->SetMayNeedFontPrefsUpdate();
439 // * image animation
440 nsAutoCString animatePref;
441 Preferences::GetCString("image.animation_mode", animatePref);
442 if (animatePref.EqualsLiteral("normal"))
443 mImageAnimationModePref = imgIContainer::kNormalAnimMode;
444 else if (animatePref.EqualsLiteral("none"))
445 mImageAnimationModePref = imgIContainer::kDontAnimMode;
446 else if (animatePref.EqualsLiteral("once"))
447 mImageAnimationModePref = imgIContainer::kLoopOnceAnimMode;
448 else // dynamic change to invalid value should act like it does initially
449 mImageAnimationModePref = imgIContainer::kNormalAnimMode;
451 uint32_t bidiOptions = GetBidi();
453 mPrefBidiDirection = StaticPrefs::bidi_direction();
454 SET_BIDI_OPTION_DIRECTION(bidiOptions, mPrefBidiDirection);
455 SET_BIDI_OPTION_TEXTTYPE(bidiOptions, StaticPrefs::bidi_texttype());
456 SET_BIDI_OPTION_NUMERAL(bidiOptions, StaticPrefs::bidi_numeral());
458 // We don't need to force reflow: either we are initializing a new
459 // prescontext or we are being called from PreferenceChanged()
460 // which triggers a reflow anyway.
461 SetBidi(bidiOptions);
464 void nsPresContext::InvalidatePaintedLayers() {
465 if (!mPresShell) {
466 return;
468 if (nsIFrame* rootFrame = mPresShell->GetRootFrame()) {
469 // FrameLayerBuilder caches invalidation-related values that depend on the
470 // appunits-per-dev-pixel ratio, so ensure that all PaintedLayer drawing
471 // is completely flushed.
472 rootFrame->InvalidateFrameSubtree();
476 void nsPresContext::AppUnitsPerDevPixelChanged() {
477 int32_t oldAppUnitsPerDevPixel = mCurAppUnitsPerDevPixel;
479 InvalidatePaintedLayers();
481 FlushFontCache();
483 MediaFeatureValuesChanged(
484 {RestyleHint::RecascadeSubtree(), NS_STYLE_HINT_REFLOW,
485 MediaFeatureChangeReason::ResolutionChange},
486 MediaFeatureChangePropagation::JustThisDocument);
488 mCurAppUnitsPerDevPixel = mDeviceContext->AppUnitsPerDevPixel();
490 #ifdef ACCESSIBILITY
491 if (mCurAppUnitsPerDevPixel != oldAppUnitsPerDevPixel) {
492 if (nsAccessibilityService* accService = GetAccService()) {
493 accService->NotifyOfDevPixelRatioChange(mPresShell,
494 mCurAppUnitsPerDevPixel);
497 #endif
499 // Recompute the size for vh units since it's changed by the dynamic toolbar
500 // max height which is stored in screen coord.
501 if (IsRootContentDocumentCrossProcess()) {
502 AdjustSizeForViewportUnits();
505 // nsSubDocumentFrame uses a AppUnitsPerDevPixel difference between parent and
506 // child document to determine if it needs to build a nsDisplayZoom item. So
507 // if we that changes then we need to invalidate the subdoc frame so that
508 // item gets created/removed.
509 if (mPresShell) {
510 if (nsIFrame* frame = mPresShell->GetRootFrame()) {
511 frame = nsLayoutUtils::GetCrossDocParentFrameInProcess(frame);
512 if (frame) {
513 int32_t parentAPD = frame->PresContext()->AppUnitsPerDevPixel();
514 if ((parentAPD == oldAppUnitsPerDevPixel) !=
515 (parentAPD == mCurAppUnitsPerDevPixel)) {
516 frame->InvalidateFrame();
522 // We would also have to look at all of our child subdocuments but the
523 // InvalidatePaintedLayers call above calls InvalidateFrameSubtree which
524 // would invalidate all subdocument frames already.
527 // static
528 void nsPresContext::PreferenceChanged(const char* aPrefName, void* aSelf) {
529 static_cast<nsPresContext*>(aSelf)->PreferenceChanged(aPrefName);
532 void nsPresContext::PreferenceChanged(const char* aPrefName) {
533 nsDependentCString prefName(aPrefName);
534 if (prefName.EqualsLiteral("layout.css.dpi") ||
535 prefName.EqualsLiteral("layout.css.devPixelsPerPx")) {
536 int32_t oldAppUnitsPerDevPixel = mDeviceContext->AppUnitsPerDevPixel();
537 // We need to assume the DPI changes, since `mDeviceContext` is shared with
538 // other documents, and we'd need to save the return value of the first call
539 // for all of them.
540 Unused << mDeviceContext->CheckDPIChange();
541 if (mPresShell) {
542 OwningNonNull<mozilla::PresShell> presShell(*mPresShell);
543 // Re-fetch the view manager's window dimensions in case there's a
544 // deferred resize which hasn't affected our mVisibleArea yet
545 nscoord oldWidthAppUnits, oldHeightAppUnits;
546 RefPtr<nsViewManager> vm = presShell->GetViewManager();
547 if (!vm) {
548 return;
550 vm->GetWindowDimensions(&oldWidthAppUnits, &oldHeightAppUnits);
551 float oldWidthDevPixels = oldWidthAppUnits / oldAppUnitsPerDevPixel;
552 float oldHeightDevPixels = oldHeightAppUnits / oldAppUnitsPerDevPixel;
554 UIResolutionChangedInternal();
556 nscoord width = NSToCoordRound(oldWidthDevPixels * AppUnitsPerDevPixel());
557 nscoord height =
558 NSToCoordRound(oldHeightDevPixels * AppUnitsPerDevPixel());
559 vm->SetWindowDimensions(width, height);
561 return;
564 if (StringBeginsWith(prefName, "browser.viewport."_ns) ||
565 StringBeginsWith(prefName, "font.size.inflation."_ns) ||
566 prefName.EqualsLiteral("dom.meta-viewport.enabled")) {
567 if (mPresShell) {
568 mPresShell->MaybeReflowForInflationScreenSizeChange();
572 auto changeHint = nsChangeHint{0};
573 auto restyleHint = RestyleHint{0};
574 // Changing any of these potentially changes the value of @media
575 // (prefers-contrast).
576 // The layout.css.prefers-contrast.enabled pref itself is not handled here,
577 // because that pref doesn't just affect the "live" value of the media query;
578 // it affects whether it is parsed at all.
579 if (prefName.EqualsLiteral("browser.display.document_color_use") ||
580 prefName.EqualsLiteral("browser.display.foreground_color") ||
581 prefName.EqualsLiteral("browser.display.background_color")) {
582 MediaFeatureValuesChanged({MediaFeatureChangeReason::PreferenceChange},
583 MediaFeatureChangePropagation::JustThisDocument);
585 if (prefName.EqualsLiteral(GFX_MISSING_FONTS_NOTIFY_PREF)) {
586 if (StaticPrefs::gfx_missing_fonts_notify()) {
587 if (!mMissingFonts) {
588 mMissingFonts = MakeUnique<gfxMissingFontRecorder>();
589 // trigger reflow to detect missing fonts on the current page
590 changeHint |= NS_STYLE_HINT_REFLOW;
592 } else {
593 if (mMissingFonts) {
594 mMissingFonts->Clear();
596 mMissingFonts = nullptr;
600 if (StringBeginsWith(prefName, "font."_ns) ||
601 // Changes to font family preferences don't change anything in the
602 // computed style data, so the style system won't generate a reflow hint
603 // for us. We need to do that manually.
604 prefName.EqualsLiteral("intl.accept_languages") ||
605 // Changes to bidi prefs need to trigger a reflow (see bug 443629)
606 StringBeginsWith(prefName, "bidi."_ns) ||
607 // Changes to font_rendering prefs need to trigger a reflow
608 StringBeginsWith(prefName, "gfx.font_rendering."_ns)) {
609 changeHint |= NS_STYLE_HINT_REFLOW;
610 if (StyleSet()->UsesFontMetrics()) {
611 restyleHint |= RestyleHint::RecascadeSubtree();
615 // We will end up calling InvalidatePreferenceSheets one from each pres
616 // context, but all it's doing is clearing its cached sheet pointers, so it
617 // won't be wastefully recreating the sheet multiple times.
619 // The first pres context that flushes will be the one to cause the
620 // reconstruction of the pref style sheet via the UpdatePreferenceStyles call
621 // in FlushPendingNotifications.
622 if (GlobalStyleSheetCache::AffectedByPref(prefName)) {
623 restyleHint |= RestyleHint::RestyleSubtree();
624 GlobalStyleSheetCache::InvalidatePreferenceSheets();
627 if (PreferenceSheet::AffectedByPref(prefName)) {
628 restyleHint |= RestyleHint::RestyleSubtree();
629 PreferenceSheet::Refresh();
632 // Same, this just frees a bunch of memory.
633 StaticPresData::Get()->InvalidateFontPrefs();
634 Document()->SetMayNeedFontPrefsUpdate();
636 // Initialize our state from the user preferences.
637 GetUserPreferences();
639 FlushFontCache();
640 if (UpdateFontVisibility()) {
641 changeHint |= NS_STYLE_HINT_REFLOW;
644 // Preferences require rerunning selector matching because we rebuild
645 // the pref style sheet for some preference changes.
646 if (changeHint || restyleHint) {
647 RebuildAllStyleData(changeHint, restyleHint);
650 InvalidatePaintedLayers();
653 nsresult nsPresContext::Init(nsDeviceContext* aDeviceContext) {
654 NS_ASSERTION(!mInitialized, "attempt to reinit pres context");
655 NS_ENSURE_ARG(aDeviceContext);
657 mDeviceContext = aDeviceContext;
659 // In certain rare cases (such as changing page mode), we tear down layout
660 // state and re-initialize a new prescontext for a document. Given that we
661 // hang style state off the DOM, we detect that re-initialization case and
662 // lazily drop the servo data. We don't do this eagerly during layout teardown
663 // because that would incur an extra whole-tree traversal that's unnecessary
664 // most of the time.
666 // FIXME(emilio): I'm pretty sure this doesn't happen after bug 1414999.
667 Element* root = mDocument->GetRootElement();
668 if (root && root->HasServoData()) {
669 RestyleManager::ClearServoDataFromSubtree(root);
672 if (mDeviceContext->SetFullZoom(mFullZoom)) {
673 FlushFontCache();
675 mCurAppUnitsPerDevPixel = mDeviceContext->AppUnitsPerDevPixel();
677 mEventManager = new mozilla::EventStateManager();
679 mAnimationEventDispatcher = new mozilla::AnimationEventDispatcher(this);
680 mEffectCompositor = new mozilla::EffectCompositor(this);
681 mTransitionManager = MakeUnique<nsTransitionManager>(this);
682 mAnimationManager = MakeUnique<nsAnimationManager>(this);
683 mTimelineManager = MakeUnique<mozilla::TimelineManager>(this);
685 if (mDocument->GetDisplayDocument()) {
686 NS_ASSERTION(mDocument->GetDisplayDocument()->GetPresContext(),
687 "Why are we being initialized?");
688 mRefreshDriver =
689 mDocument->GetDisplayDocument()->GetPresContext()->RefreshDriver();
690 } else {
691 dom::Document* parent = mDocument->GetInProcessParentDocument();
692 // Unfortunately, sometimes |parent| here has no presshell because
693 // printing screws up things. Assert that in other cases it does,
694 // but whenever the shell is null just fall back on using our own
695 // refresh driver.
696 NS_ASSERTION(
697 !parent || mDocument->IsStaticDocument() || parent->GetPresShell(),
698 "How did we end up with a presshell if our parent doesn't "
699 "have one?");
700 if (parent && parent->GetPresContext()) {
701 // XXX the document can change in AttachPresShell, does this work?
702 dom::BrowsingContext* browsingContext = mDocument->GetBrowsingContext();
703 if (browsingContext && !browsingContext->IsTop()) {
704 Element* containingElement = mDocument->GetEmbedderElement();
705 if (!containingElement->IsXULElement() ||
706 !containingElement->HasAttr(kNameSpaceID_None,
707 nsGkAtoms::forceOwnRefreshDriver)) {
708 mRefreshDriver = parent->GetPresContext()->RefreshDriver();
713 if (!mRefreshDriver) {
714 mRefreshDriver = new nsRefreshDriver(this);
718 // Register callbacks so we're notified when the preferences change
719 Preferences::RegisterPrefixCallbacks(nsPresContext::PreferenceChanged,
720 gPrefixCallbackPrefs, this);
721 Preferences::RegisterCallbacks(nsPresContext::PreferenceChanged,
722 gExactCallbackPrefs, this);
724 nsresult rv = mEventManager->Init();
725 NS_ENSURE_SUCCESS(rv, rv);
727 mEventManager->SetPresContext(this);
729 #if defined(MOZ_WIDGET_ANDROID)
730 if (IsRootContentDocumentCrossProcess() &&
731 MOZ_LIKELY(
732 !Preferences::HasUserValue("layout.dynamic-toolbar-max-height"))) {
733 if (BrowserChild* browserChild =
734 BrowserChild::GetFrom(mDocument->GetDocShell())) {
735 mDynamicToolbarMaxHeight = browserChild->GetDynamicToolbarMaxHeight();
736 mDynamicToolbarHeight = mDynamicToolbarMaxHeight;
739 #endif
741 #ifdef DEBUG
742 mInitialized = true;
743 #endif
745 return NS_OK;
748 bool nsPresContext::UpdateFontVisibility() {
749 FontVisibility oldValue = mFontVisibility;
751 // Is this a private browsing context?
752 bool isPrivate = false;
753 if (nsCOMPtr<nsILoadContext> loadContext = mDocument->GetLoadContext()) {
754 isPrivate = loadContext->UsePrivateBrowsing();
757 // Read the relevant pref depending on RFP/trackingProtection state
758 // to determine the visibility level to use.
759 int32_t level;
760 if (mDocument->ShouldResistFingerprinting()) {
761 level = StaticPrefs::layout_css_font_visibility_resistFingerprinting();
762 } else if (StaticPrefs::privacy_trackingprotection_enabled() ||
763 (isPrivate &&
764 StaticPrefs::privacy_trackingprotection_pbmode_enabled())) {
765 level = StaticPrefs::layout_css_font_visibility_trackingprotection();
766 } else {
767 level = StaticPrefs::layout_css_font_visibility_standard();
770 // For private browsing contexts, apply the private-mode limit.
771 if (isPrivate) {
772 int32_t priv = StaticPrefs::layout_css_font_visibility_private();
773 level = std::max(std::min(level, priv), int32_t(FontVisibility::Base));
776 // Clamp result to the valid range of levels.
777 level = std::max(std::min(level, int32_t(FontVisibility::User)),
778 int32_t(FontVisibility::Base));
780 mFontVisibility = FontVisibility(level);
781 return mFontVisibility != oldValue;
784 void nsPresContext::ReportBlockedFontFamilyName(const nsCString& aFamily,
785 FontVisibility aVisibility) {
786 if (!mBlockedFonts.EnsureInserted(aFamily)) {
787 return;
789 nsAutoString msg;
790 msg.AppendPrintf(
791 "Request for font \"%s\" blocked at visibility level %d (requires %d)\n",
792 aFamily.get(), int(GetFontVisibility()), int(aVisibility));
793 nsContentUtils::ReportToConsoleNonLocalized(msg, nsIScriptError::warningFlag,
794 "Security"_ns, mDocument);
797 void nsPresContext::ReportBlockedFontFamily(const fontlist::Family& aFamily) {
798 auto* fontList = gfxPlatformFontList::PlatformFontList()->SharedFontList();
799 const nsCString& name = aFamily.DisplayName().AsString(fontList);
800 ReportBlockedFontFamilyName(name, aFamily.Visibility());
803 void nsPresContext::ReportBlockedFontFamily(const gfxFontFamily& aFamily) {
804 ReportBlockedFontFamilyName(aFamily.Name(), aFamily.Visibility());
807 void nsPresContext::InitFontCache() {
808 if (!mFontCache) {
809 mFontCache = new nsFontCache();
810 mFontCache->Init(this);
814 void nsPresContext::UpdateFontCacheUserFonts(gfxUserFontSet* aUserFontSet) {
815 if (mFontCache) {
816 mFontCache->UpdateUserFonts(aUserFontSet);
820 already_AddRefed<nsFontMetrics> nsPresContext::GetMetricsFor(
821 const nsFont& aFont, const nsFontMetrics::Params& aParams) {
822 InitFontCache();
823 return mFontCache->GetMetricsFor(aFont, aParams);
826 nsresult nsPresContext::FlushFontCache(void) {
827 if (mFontCache) {
828 mFontCache->Flush();
830 return NS_OK;
833 nsresult nsPresContext::FontMetricsDeleted(const nsFontMetrics* aFontMetrics) {
834 if (mFontCache) {
835 mFontCache->FontMetricsDeleted(aFontMetrics);
837 return NS_OK;
840 // Note: We don't hold a reference on the shell; it has a reference to
841 // us
842 void nsPresContext::AttachPresShell(mozilla::PresShell* aPresShell) {
843 MOZ_ASSERT(!mPresShell);
844 mPresShell = aPresShell;
846 mRestyleManager = MakeUnique<mozilla::RestyleManager>(this);
848 // Since CounterStyleManager is also the name of a method of
849 // nsPresContext, it is necessary to prefix the class with the mozilla
850 // namespace here.
851 mCounterStyleManager = new mozilla::CounterStyleManager(this);
853 dom::Document* doc = mPresShell->GetDocument();
854 MOZ_ASSERT(doc);
855 // Have to update PresContext's mDocument before calling any other methods.
856 mDocument = doc;
858 LookAndFeel::HandleGlobalThemeChange();
860 // Initialize our state from the user preferences, now that we
861 // have a presshell, and hence a document.
862 GetUserPreferences();
864 EnsureTheme();
866 nsIURI* docURI = doc->GetDocumentURI();
868 if (IsDynamic() && docURI) {
869 if (!docURI->SchemeIs("chrome") && !docURI->SchemeIs("resource"))
870 mImageAnimationMode = mImageAnimationModePref;
871 else
872 mImageAnimationMode = imgIContainer::kNormalAnimMode;
875 UpdateCharSet(doc->GetDocumentCharacterSet());
878 Maybe<ColorScheme> nsPresContext::GetOverriddenOrEmbedderColorScheme() const {
879 if (Medium() == nsGkAtoms::print) {
880 return Some(ColorScheme::Light);
883 switch (mOverriddenOrEmbedderColorScheme) {
884 case dom::PrefersColorSchemeOverride::Dark:
885 return Some(ColorScheme::Dark);
886 case dom::PrefersColorSchemeOverride::Light:
887 return Some(ColorScheme::Light);
888 case dom::PrefersColorSchemeOverride::None:
889 case dom::PrefersColorSchemeOverride::EndGuard_:
890 break;
893 return Nothing();
896 void nsPresContext::SetColorSchemeOverride(
897 PrefersColorSchemeOverride aOverride) {
898 auto oldScheme = mDocument->PreferredColorScheme();
900 mOverriddenOrEmbedderColorScheme = aOverride;
902 if (mDocument->PreferredColorScheme() != oldScheme) {
903 MediaFeatureValuesChanged(
904 MediaFeatureChange::ForPreferredColorSchemeChange(),
905 MediaFeatureChangePropagation::JustThisDocument);
909 void nsPresContext::RecomputeBrowsingContextDependentData() {
910 MOZ_ASSERT(mDocument);
911 dom::Document* doc = mDocument;
912 // Resource documents inherit all this state from their display document.
913 while (dom::Document* outer = doc->GetDisplayDocument()) {
914 doc = outer;
916 auto* browsingContext = doc->GetBrowsingContext();
917 if (!browsingContext) {
918 // This can legitimately happen for e.g. SVG images. Those just get scaled
919 // as a result of the zoom on the embedder document so it doesn't really
920 // matter... Medium also doesn't affect those.
921 return;
923 auto systemZoom = LookAndFeel::SystemZoomSettings();
924 SetFullZoom(browsingContext->FullZoom() * systemZoom.mFullZoom);
925 SetTextZoom(browsingContext->TextZoom() * systemZoom.mTextZoom);
926 SetOverrideDPPX(browsingContext->OverrideDPPX());
928 auto* top = browsingContext->Top();
929 SetColorSchemeOverride([&] {
930 auto overriden = top->PrefersColorSchemeOverride();
931 if (overriden != PrefersColorSchemeOverride::None) {
932 return overriden;
934 if (!StaticPrefs::
935 layout_css_iframe_embedder_prefers_color_scheme_content_enabled()) {
936 return top->GetEmbedderColorSchemes().mPreferred;
938 return browsingContext->GetEmbedderColorSchemes().mPreferred;
939 }());
941 SetInRDMPane(top->GetInRDMPane());
943 if (doc == mDocument) {
944 // Medium doesn't apply to resource documents, etc.
945 RefPtr<nsAtom> mediumToEmulate;
946 if (MOZ_UNLIKELY(!top->GetMediumOverride().IsEmpty())) {
947 nsAutoString lower;
948 nsContentUtils::ASCIIToLower(top->GetMediumOverride(), lower);
949 mediumToEmulate = NS_Atomize(lower);
951 EmulateMedium(mediumToEmulate);
954 mDocument->EnumerateExternalResources([](dom::Document& aSubResource) {
955 if (nsPresContext* subResourcePc = aSubResource.GetPresContext()) {
956 subResourcePc->RecomputeBrowsingContextDependentData();
958 return CallState::Continue;
962 void nsPresContext::DetachPresShell() {
963 // The counter style manager's destructor needs to deallocate with the
964 // presshell arena. Disconnect it before nulling out the shell.
966 // XXXbholley: Given recent refactorings, it probably makes more sense to
967 // just null our mPresShell at the bottom of this function. I'm leaving it
968 // this way to preserve the old ordering, but I doubt anything would break.
969 if (mCounterStyleManager) {
970 mCounterStyleManager->Disconnect();
971 mCounterStyleManager = nullptr;
974 mPresShell = nullptr;
976 CancelManagedPostRefreshObservers();
978 if (mAnimationEventDispatcher) {
979 mAnimationEventDispatcher->Disconnect();
980 mAnimationEventDispatcher = nullptr;
982 if (mEffectCompositor) {
983 mEffectCompositor->Disconnect();
984 mEffectCompositor = nullptr;
986 if (mTransitionManager) {
987 mTransitionManager->Disconnect();
988 mTransitionManager = nullptr;
990 if (mAnimationManager) {
991 mAnimationManager->Disconnect();
992 mAnimationManager = nullptr;
994 if (mTimelineManager) {
995 mTimelineManager->Disconnect();
996 mTimelineManager = nullptr;
998 if (mRestyleManager) {
999 mRestyleManager->Disconnect();
1000 mRestyleManager = nullptr;
1002 if (mRefreshDriver && mRefreshDriver->GetPresContext() == this) {
1003 mRefreshDriver->Disconnect();
1004 // Can't null out the refresh driver here.
1008 struct QueryContainerState {
1009 nsSize mSize;
1010 WritingMode mWm;
1012 nscoord GetInlineSize() const { return LogicalSize(mWm, mSize).ISize(mWm); }
1014 bool Changed(const QueryContainerState& aNewState, StyleContainerType aType) {
1015 switch (aType) {
1016 case StyleContainerType::Normal:
1017 break;
1018 case StyleContainerType::Size:
1019 return mSize != aNewState.mSize;
1020 case StyleContainerType::InlineSize:
1021 return GetInlineSize() != aNewState.GetInlineSize();
1023 return false;
1026 NS_DECLARE_FRAME_PROPERTY_DELETABLE(ContainerState, QueryContainerState);
1028 void nsPresContext::RegisterContainerQueryFrame(nsIFrame* aFrame) {
1029 mContainerQueryFrames.Add(aFrame);
1032 void nsPresContext::UnregisterContainerQueryFrame(nsIFrame* aFrame) {
1033 mContainerQueryFrames.Remove(aFrame);
1036 void nsPresContext::FinishedContainerQueryUpdate() {
1037 mUpdatedContainerQueryContents.Clear();
1040 bool nsPresContext::UpdateContainerQueryStyles() {
1041 if (mContainerQueryFrames.IsEmpty()) {
1042 return false;
1045 AUTO_PROFILER_MARKER_TEXT("UpdateContainerQueryStyles", LAYOUT, {}, ""_ns);
1047 PresShell()->DoFlushLayout(/* aInterruptible = */ false);
1049 AutoTArray<nsIFrame*, 8> framesToUpdate;
1051 bool anyChanged = false;
1052 for (nsIFrame* frame : mContainerQueryFrames.IterFromShallowest()) {
1053 MOZ_ASSERT(frame->IsPrimaryFrame());
1055 auto type = frame->StyleDisplay()->mContainerType;
1056 MOZ_ASSERT(type != StyleContainerType::Normal,
1057 "Non-container frames shouldn't be in this type");
1059 const QueryContainerState newState{frame->GetSize(),
1060 frame->GetWritingMode()};
1061 QueryContainerState* oldState = frame->GetProperty(ContainerState());
1063 const bool changed = !oldState || oldState->Changed(newState, type);
1065 // Make sure to update the state regardless. It's cheap and it keeps tracks
1066 // of both axes correctly even if only one axis is contained.
1067 if (oldState) {
1068 *oldState = newState;
1069 } else {
1070 frame->SetProperty(ContainerState(), new QueryContainerState(newState));
1073 if (!changed) {
1074 continue;
1077 const bool updatingAncestor = [&] {
1078 for (nsIFrame* f : framesToUpdate) {
1079 if (nsLayoutUtils::IsProperAncestorFrame(f, frame)) {
1080 return true;
1083 return false;
1084 }();
1086 if (updatingAncestor) {
1087 // We're going to update an ancestor container of this frame already,
1088 // avoid updating this one too until all our ancestor containers are
1089 // updated.
1090 continue;
1093 // To prevent unstable layout, only update once per-element per-flush.
1094 if (NS_WARN_IF(!mUpdatedContainerQueryContents.EnsureInserted(
1095 frame->GetContent()))) {
1096 continue;
1099 framesToUpdate.AppendElement(frame);
1101 // TODO(emilio): More fine-grained invalidation rather than invalidating the
1102 // whole subtree, probably!
1103 RestyleManager()->PostRestyleEvent(frame->GetContent()->AsElement(),
1104 RestyleHint::RestyleSubtree(),
1105 nsChangeHint(0));
1106 anyChanged = true;
1108 return anyChanged;
1111 void nsPresContext::DocumentCharSetChanged(NotNull<const Encoding*> aCharSet) {
1112 UpdateCharSet(aCharSet);
1113 FlushFontCache();
1115 // If a document contains one or more <script> elements, frame construction
1116 // might happen earlier than the UpdateCharSet(), so we need to restyle
1117 // descendants to make their style data up-to-date.
1119 // FIXME(emilio): Revisit whether this is true after bug 1438911.
1120 RebuildAllStyleData(NS_STYLE_HINT_REFLOW, RestyleHint::RecascadeSubtree());
1123 void nsPresContext::UpdateCharSet(NotNull<const Encoding*> aCharSet) {
1124 switch (GET_BIDI_OPTION_TEXTTYPE(GetBidi())) {
1125 case IBMBIDI_TEXTTYPE_LOGICAL:
1126 SetVisualMode(false);
1127 break;
1129 case IBMBIDI_TEXTTYPE_VISUAL:
1130 SetVisualMode(true);
1131 break;
1133 case IBMBIDI_TEXTTYPE_CHARSET:
1134 default:
1135 SetVisualMode(IsVisualCharset(aCharSet));
1139 nsPresContext* nsPresContext::GetParentPresContext() const {
1140 mozilla::PresShell* presShell = GetPresShell();
1141 if (presShell) {
1142 nsViewManager* viewManager = presShell->GetViewManager();
1143 if (viewManager) {
1144 nsView* view = viewManager->GetRootView();
1145 if (view) {
1146 view = view->GetParent(); // anonymous inner view
1147 if (view) {
1148 view = view->GetParent(); // subdocumentframe's view
1149 if (view) {
1150 nsIFrame* f = view->GetFrame();
1151 if (f) {
1152 return f->PresContext();
1159 return nullptr;
1162 nsPresContext* nsPresContext::GetInProcessRootContentDocumentPresContext() {
1163 if (IsChrome()) return nullptr;
1164 nsPresContext* pc = this;
1165 for (;;) {
1166 nsPresContext* parent = pc->GetParentPresContext();
1167 if (!parent || parent->IsChrome()) return pc;
1168 pc = parent;
1172 nsIWidget* nsPresContext::GetNearestWidget(nsPoint* aOffset) {
1173 NS_ENSURE_TRUE(mPresShell, nullptr);
1174 nsViewManager* vm = mPresShell->GetViewManager();
1175 NS_ENSURE_TRUE(vm, nullptr);
1176 nsView* rootView = vm->GetRootView();
1177 NS_ENSURE_TRUE(rootView, nullptr);
1178 return rootView->GetNearestWidget(aOffset);
1181 nsIWidget* nsPresContext::GetRootWidget() const {
1182 NS_ENSURE_TRUE(mPresShell, nullptr);
1183 nsViewManager* vm = mPresShell->GetViewManager();
1184 if (!vm) {
1185 return nullptr;
1187 return vm->GetRootWidget();
1190 // We may want to replace this with something faster, maybe caching the root
1191 // prescontext
1192 nsRootPresContext* nsPresContext::GetRootPresContext() const {
1193 nsPresContext* pc = const_cast<nsPresContext*>(this);
1194 for (;;) {
1195 nsPresContext* parent = pc->GetParentPresContext();
1196 if (!parent) break;
1197 pc = parent;
1199 return pc->IsRoot() ? static_cast<nsRootPresContext*>(pc) : nullptr;
1202 // Helper function for setting Anim Mode on image
1203 static void SetImgAnimModeOnImgReq(imgIRequest* aImgReq, uint16_t aMode) {
1204 if (aImgReq) {
1205 nsCOMPtr<imgIContainer> imgCon;
1206 aImgReq->GetImage(getter_AddRefs(imgCon));
1207 if (imgCon) {
1208 imgCon->SetAnimationMode(aMode);
1213 // IMPORTANT: Assumption is that all images for a Presentation
1214 // have the same Animation Mode (pavlov said this was OK)
1216 // Walks content and set the animation mode
1217 // this is a way to turn on/off image animations
1218 void nsPresContext::SetImgAnimations(nsIContent* aParent, uint16_t aMode) {
1219 nsCOMPtr<nsIImageLoadingContent> imgContent(do_QueryInterface(aParent));
1220 if (imgContent) {
1221 nsCOMPtr<imgIRequest> imgReq;
1222 imgContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
1223 getter_AddRefs(imgReq));
1224 SetImgAnimModeOnImgReq(imgReq, aMode);
1227 for (nsIContent* childContent = aParent->GetFirstChild(); childContent;
1228 childContent = childContent->GetNextSibling()) {
1229 SetImgAnimations(childContent, aMode);
1233 void nsPresContext::SetSMILAnimations(dom::Document* aDoc, uint16_t aNewMode,
1234 uint16_t aOldMode) {
1235 if (aDoc->HasAnimationController()) {
1236 SMILAnimationController* controller = aDoc->GetAnimationController();
1237 switch (aNewMode) {
1238 case imgIContainer::kNormalAnimMode:
1239 case imgIContainer::kLoopOnceAnimMode:
1240 if (aOldMode == imgIContainer::kDontAnimMode)
1241 controller->Resume(SMILTimeContainer::PAUSE_USERPREF);
1242 break;
1244 case imgIContainer::kDontAnimMode:
1245 if (aOldMode != imgIContainer::kDontAnimMode)
1246 controller->Pause(SMILTimeContainer::PAUSE_USERPREF);
1247 break;
1252 void nsPresContext::SetImageAnimationMode(uint16_t aMode) {
1253 NS_ASSERTION(aMode == imgIContainer::kNormalAnimMode ||
1254 aMode == imgIContainer::kDontAnimMode ||
1255 aMode == imgIContainer::kLoopOnceAnimMode,
1256 "Wrong Animation Mode is being set!");
1258 // Image animation mode cannot be changed when rendering to a printer.
1259 if (!IsDynamic()) return;
1261 // Now walk the content tree and set the animation mode
1262 // on all the images.
1263 if (mPresShell) {
1264 dom::Document* doc = mPresShell->GetDocument();
1265 if (doc) {
1266 doc->StyleImageLoader()->SetAnimationMode(aMode);
1268 Element* rootElement = doc->GetRootElement();
1269 if (rootElement) {
1270 SetImgAnimations(rootElement, aMode);
1272 SetSMILAnimations(doc, aMode, mImageAnimationMode);
1276 mImageAnimationMode = aMode;
1279 void nsPresContext::UpdateEffectiveTextZoom() {
1280 float newZoom = mSystemFontScale * mTextZoom;
1281 float minZoom = StaticPrefs::zoom_minPercent() / 100.0f;
1282 float maxZoom = StaticPrefs::zoom_maxPercent() / 100.0f;
1284 if (newZoom < minZoom) {
1285 newZoom = minZoom;
1286 } else if (newZoom > maxZoom) {
1287 newZoom = maxZoom;
1290 mEffectiveTextZoom = newZoom;
1292 // Media queries could have changed, since we changed the meaning
1293 // of 'em' units in them.
1294 MediaFeatureValuesChanged(
1295 {RestyleHint::RecascadeSubtree(), NS_STYLE_HINT_REFLOW,
1296 MediaFeatureChangeReason::ZoomChange},
1297 MediaFeatureChangePropagation::JustThisDocument);
1300 void nsPresContext::SetInRDMPane(bool aInRDMPane) {
1301 if (mInRDMPane == aInRDMPane) {
1302 return;
1304 mInRDMPane = aInRDMPane;
1305 RecomputeTheme();
1308 float nsPresContext::GetDeviceFullZoom() {
1309 return mDeviceContext->GetFullZoom();
1312 void nsPresContext::SetFullZoom(float aZoom) {
1313 if (!mPresShell || mFullZoom == aZoom) {
1314 return;
1317 // Re-fetch the view manager's window dimensions in case there's a deferred
1318 // resize which hasn't affected our mVisibleArea yet
1319 nscoord oldWidthAppUnits, oldHeightAppUnits;
1320 mPresShell->GetViewManager()->GetWindowDimensions(&oldWidthAppUnits,
1321 &oldHeightAppUnits);
1322 float oldWidthDevPixels = oldWidthAppUnits / float(mCurAppUnitsPerDevPixel);
1323 float oldHeightDevPixels = oldHeightAppUnits / float(mCurAppUnitsPerDevPixel);
1324 mDeviceContext->SetFullZoom(aZoom);
1326 mFullZoom = aZoom;
1328 AppUnitsPerDevPixelChanged();
1330 mPresShell->GetViewManager()->SetWindowDimensions(
1331 NSToCoordRound(oldWidthDevPixels * AppUnitsPerDevPixel()),
1332 NSToCoordRound(oldHeightDevPixels * AppUnitsPerDevPixel()));
1335 void nsPresContext::SetOverrideDPPX(float aDPPX) {
1336 // SetOverrideDPPX is called during navigations, including history
1337 // traversals. In that case, it's typically called with our current value,
1338 // and we don't need to actually do anything.
1339 if (aDPPX == GetOverrideDPPX()) {
1340 return;
1343 mMediaEmulationData.mDPPX = aDPPX;
1344 MediaFeatureValuesChanged({MediaFeatureChangeReason::ResolutionChange},
1345 MediaFeatureChangePropagation::JustThisDocument);
1348 gfxSize nsPresContext::ScreenSizeInchesForFontInflation(bool* aChanged) {
1349 if (aChanged) {
1350 *aChanged = false;
1353 nsDeviceContext* dx = DeviceContext();
1354 nsRect clientRect;
1355 dx->GetClientRect(clientRect); // FIXME: GetClientRect looks expensive
1356 float unitsPerInch = dx->AppUnitsPerPhysicalInch();
1357 gfxSize deviceSizeInches(float(clientRect.width) / unitsPerInch,
1358 float(clientRect.height) / unitsPerInch);
1360 if (mLastFontInflationScreenSize == gfxSize(-1.0, -1.0)) {
1361 mLastFontInflationScreenSize = deviceSizeInches;
1364 if (deviceSizeInches != mLastFontInflationScreenSize && aChanged) {
1365 *aChanged = true;
1366 mLastFontInflationScreenSize = deviceSizeInches;
1369 return deviceSizeInches;
1372 static bool CheckOverflow(const ComputedStyle* aComputedStyle,
1373 ScrollStyles* aStyles) {
1374 // If they're not styled yet, we'll get around to it when constructing frames
1375 // for the element.
1376 if (!aComputedStyle) {
1377 return false;
1379 const nsStyleDisplay* display = aComputedStyle->StyleDisplay();
1381 // If they will generate no box, just don't.
1382 if (display->mDisplay == StyleDisplay::None ||
1383 display->mDisplay == StyleDisplay::Contents) {
1384 return false;
1387 // NOTE(emilio): This check needs to match the one in
1388 // Document::IsPotentiallyScrollable.
1389 if (display->OverflowIsVisibleInBothAxis()) {
1390 return false;
1393 *aStyles =
1394 ScrollStyles(*display, ScrollStyles::MapOverflowToValidScrollStyle);
1395 return true;
1398 // https://drafts.csswg.org/css-overflow/#overflow-propagation
1400 // NOTE(emilio): We may need to use out-of-date styles for this, since this is
1401 // called from nsCSSFrameConstructor::ContentRemoved. We could refactor this a
1402 // bit to avoid doing that, and also fix correctness issues (we don't invalidate
1403 // properly when we insert a body element and there is a previous one, for
1404 // example).
1405 static Element* GetPropagatedScrollStylesForViewport(
1406 nsPresContext* aPresContext, ScrollStyles* aStyles) {
1407 Document* document = aPresContext->Document();
1408 Element* docElement = document->GetRootElement();
1409 // docElement might be null if we're doing this after removing it.
1410 if (!docElement) {
1411 return nullptr;
1414 // Check the style on the document root element
1415 const auto* rootStyle = Servo_Element_GetMaybeOutOfDateStyle(docElement);
1416 if (CheckOverflow(rootStyle, aStyles)) {
1417 // tell caller we stole the overflow style from the root element
1418 return docElement;
1421 if (rootStyle && rootStyle->StyleDisplay()->IsContainAny()) {
1422 return nullptr;
1425 // Don't look in the BODY for non-HTML documents or HTML documents
1426 // with non-HTML roots.
1427 // XXX this should be earlier; we shouldn't even look at the document root
1428 // for non-HTML documents. Fix this once we support explicit CSS styling
1429 // of the viewport
1430 if (!document->IsHTMLOrXHTML() || !docElement->IsHTMLElement()) {
1431 return nullptr;
1434 Element* bodyElement = document->AsHTMLDocument()->GetBodyElement();
1435 if (!bodyElement) {
1436 return nullptr;
1439 MOZ_ASSERT(bodyElement->IsHTMLElement(nsGkAtoms::body),
1440 "GetBodyElement returned something bogus");
1442 const auto* bodyStyle = Servo_Element_GetMaybeOutOfDateStyle(bodyElement);
1443 if (bodyStyle && bodyStyle->StyleDisplay()->IsContainAny()) {
1444 return nullptr;
1447 if (CheckOverflow(bodyStyle, aStyles)) {
1448 // tell caller we stole the overflow style from the body element
1449 return bodyElement;
1452 return nullptr;
1455 Element* nsPresContext::UpdateViewportScrollStylesOverride() {
1456 ScrollStyles oldViewportScrollStyles = mViewportScrollStyles;
1458 // Start off with our default styles, and then update them as needed.
1459 mViewportScrollStyles =
1460 ScrollStyles(StyleOverflow::Auto, StyleOverflow::Auto);
1461 mViewportScrollOverrideElement = nullptr;
1462 // Don't propagate the scrollbar state in printing or print preview.
1463 if (!IsPaginated()) {
1464 mViewportScrollOverrideElement =
1465 GetPropagatedScrollStylesForViewport(this, &mViewportScrollStyles);
1468 dom::Document* document = Document();
1469 if (Element* fsElement = document->GetUnretargetedFullscreenElement()) {
1470 // If the document is in fullscreen, but the fullscreen element is
1471 // not the root element, we should explicitly suppress the scrollbar
1472 // here. Note that, we still need to return the original element
1473 // the styles are from, so that the state of those elements is not
1474 // affected across fullscreen change.
1475 if (fsElement != document->GetRootElement() &&
1476 fsElement != mViewportScrollOverrideElement) {
1477 mViewportScrollStyles =
1478 ScrollStyles(StyleOverflow::Hidden, StyleOverflow::Hidden);
1482 if (mViewportScrollStyles != oldViewportScrollStyles) {
1483 if (mPresShell) {
1484 if (nsIFrame* frame = mPresShell->GetRootFrame()) {
1485 frame->SchedulePaint();
1490 return mViewportScrollOverrideElement;
1493 bool nsPresContext::ElementWouldPropagateScrollStyles(const Element& aElement) {
1494 if (aElement.GetParent() && !aElement.IsHTMLElement(nsGkAtoms::body)) {
1495 // We certainly won't be propagating from this element.
1496 return false;
1499 // Go ahead and just call GetPropagatedScrollStylesForViewport, but update
1500 // a dummy ScrollStyles we don't care about. It'll do a bit of extra work,
1501 // but saves us having to have more complicated code or more code duplication;
1502 // in practice we will make this call quite rarely, because we checked for all
1503 // the common cases above.
1504 ScrollStyles dummy(StyleOverflow::Auto, StyleOverflow::Auto);
1505 return GetPropagatedScrollStylesForViewport(this, &dummy) == &aElement;
1508 nsISupports* nsPresContext::GetContainerWeak() const {
1509 return mDocument->GetDocShell();
1512 ColorScheme nsPresContext::DefaultBackgroundColorScheme() const {
1513 dom::Document* doc = Document();
1514 // Use a dark background for top-level about:blank that is inaccessible to
1515 // content JS.
1517 BrowsingContext* bc = doc->GetBrowsingContext();
1518 if (bc && bc->IsTop() && !bc->HasOpener() && doc->GetDocumentURI() &&
1519 NS_IsAboutBlank(doc->GetDocumentURI())) {
1520 return doc->PreferredColorScheme(Document::IgnoreRFP::Yes);
1523 // Prefer the root color-scheme (since generally the default canvas
1524 // background comes from the root element's background-color), and fall back
1525 // to the default color-scheme if not available.
1526 if (auto* frame = FrameConstructor()->GetRootElementStyleFrame()) {
1527 return LookAndFeel::ColorSchemeForFrame(frame);
1529 return doc->DefaultColorScheme();
1532 nscolor nsPresContext::DefaultBackgroundColor() const {
1533 if (!GetBackgroundColorDraw()) {
1534 return NS_RGB(255, 255, 255);
1536 return PrefSheetPrefs()
1537 .ColorsFor(DefaultBackgroundColorScheme())
1538 .mDefaultBackground;
1541 nsDocShell* nsPresContext::GetDocShell() const {
1542 return nsDocShell::Cast(mDocument->GetDocShell());
1545 bool nsPresContext::BidiEnabled() const { return Document()->GetBidiEnabled(); }
1547 void nsPresContext::SetBidiEnabled() const { Document()->SetBidiEnabled(); }
1549 void nsPresContext::SetBidi(uint32_t aSource) {
1550 // Don't do all this stuff unless the options have changed.
1551 if (aSource == GetBidi()) {
1552 return;
1555 Document()->SetBidiOptions(aSource);
1556 if (IBMBIDI_TEXTDIRECTION_RTL == GET_BIDI_OPTION_DIRECTION(aSource) ||
1557 IBMBIDI_NUMERAL_HINDI == GET_BIDI_OPTION_NUMERAL(aSource)) {
1558 SetBidiEnabled();
1560 if (IBMBIDI_TEXTTYPE_VISUAL == GET_BIDI_OPTION_TEXTTYPE(aSource)) {
1561 SetVisualMode(true);
1562 } else if (IBMBIDI_TEXTTYPE_LOGICAL == GET_BIDI_OPTION_TEXTTYPE(aSource)) {
1563 SetVisualMode(false);
1564 } else {
1565 SetVisualMode(IsVisualCharset(Document()->GetDocumentCharacterSet()));
1569 uint32_t nsPresContext::GetBidi() const { return Document()->GetBidiOptions(); }
1571 void nsPresContext::RecordInteractionTime(InteractionType aType,
1572 const TimeStamp& aTimeStamp) {
1573 if (!mInteractionTimeEnabled || aTimeStamp.IsNull()) {
1574 return;
1577 // Array of references to the member variable of each time stamp
1578 // for the different interaction types, keyed by InteractionType.
1579 TimeStamp nsPresContext::*interactionTimes[] = {
1580 &nsPresContext::mFirstClickTime, &nsPresContext::mFirstKeyTime,
1581 &nsPresContext::mFirstMouseMoveTime, &nsPresContext::mFirstScrollTime};
1583 // Array of histogram IDs for the different interaction types,
1584 // keyed by InteractionType.
1585 Telemetry::HistogramID histogramIds[] = {
1586 Telemetry::TIME_TO_FIRST_CLICK_MS, Telemetry::TIME_TO_FIRST_KEY_INPUT_MS,
1587 Telemetry::TIME_TO_FIRST_MOUSE_MOVE_MS,
1588 Telemetry::TIME_TO_FIRST_SCROLL_MS};
1590 TimeStamp& interactionTime =
1591 this->*(interactionTimes[static_cast<uint32_t>(aType)]);
1592 if (!interactionTime.IsNull()) {
1593 // We have already recorded an interaction time.
1594 return;
1597 // Record the interaction time if it occurs after the first paint
1598 // of the top level content document.
1599 nsPresContext* inProcessRootPresContext =
1600 GetInProcessRootContentDocumentPresContext();
1602 if (!inProcessRootPresContext ||
1603 !inProcessRootPresContext->IsRootContentDocumentCrossProcess()) {
1604 // There is no top content pres context, or we are in a cross process
1605 // document so we don't care about the interaction time. Record a value
1606 // anyways to avoid trying to find the top content pres context in future
1607 // interactions.
1608 interactionTime = TimeStamp::Now();
1609 return;
1612 if (inProcessRootPresContext->mFirstNonBlankPaintTime.IsNull() ||
1613 inProcessRootPresContext->mFirstNonBlankPaintTime > aTimeStamp) {
1614 // Top content pres context has not had a non-blank paint yet
1615 // or the event timestamp is before the first non-blank paint,
1616 // so don't record interaction time.
1617 return;
1620 // Check if we are recording the first of any of the interaction types.
1621 bool isFirstInteraction = true;
1622 for (TimeStamp nsPresContext::*memberPtr : interactionTimes) {
1623 TimeStamp& timeStamp = this->*(memberPtr);
1624 if (!timeStamp.IsNull()) {
1625 isFirstInteraction = false;
1626 break;
1630 interactionTime = TimeStamp::Now();
1631 // Only the top level content pres context reports first interaction
1632 // time to telemetry (if it hasn't already done so).
1633 if (this == inProcessRootPresContext) {
1634 if (Telemetry::CanRecordExtended()) {
1635 double millis =
1636 (interactionTime - mFirstNonBlankPaintTime).ToMilliseconds();
1637 Telemetry::Accumulate(histogramIds[static_cast<uint32_t>(aType)], millis);
1639 if (isFirstInteraction) {
1640 Telemetry::Accumulate(Telemetry::TIME_TO_FIRST_INTERACTION_MS, millis);
1643 } else {
1644 inProcessRootPresContext->RecordInteractionTime(aType, aTimeStamp);
1648 nsITheme* nsPresContext::Theme() const {
1649 MOZ_ASSERT(mTheme);
1650 return mTheme;
1653 void nsPresContext::EnsureTheme() {
1654 MOZ_ASSERT(!mTheme);
1655 if (Document()->ShouldAvoidNativeTheme()) {
1656 if (mInRDMPane) {
1657 mTheme = do_GetRDMThemeDoNotUseDirectly();
1658 } else {
1659 mTheme = do_GetBasicNativeThemeDoNotUseDirectly();
1661 } else {
1662 mTheme = do_GetNativeThemeDoNotUseDirectly();
1664 MOZ_RELEASE_ASSERT(mTheme);
1667 void nsPresContext::RecomputeTheme() {
1668 if (!mTheme) {
1669 return;
1671 nsCOMPtr<nsITheme> oldTheme = std::move(mTheme);
1672 EnsureTheme();
1673 if (oldTheme == mTheme) {
1674 return;
1676 // Theme affects layout information (as it affects whether we create
1677 // scrollbar buttons for example) and also style (affects the
1678 // scrollbar-inline-size env var).
1679 RebuildAllStyleData(nsChangeHint_ReconstructFrame,
1680 RestyleHint::RecascadeSubtree());
1681 // This is a bit of a lie, but this affects the overlay-scrollbars
1682 // media query and it's the code-path that gets taken for regular system
1683 // metrics changes via ThemeChanged().
1684 MediaFeatureValuesChanged({MediaFeatureChangeReason::SystemMetricsChange},
1685 MediaFeatureChangePropagation::JustThisDocument);
1688 bool nsPresContext::UseOverlayScrollbars() const {
1689 return LookAndFeel::GetInt(LookAndFeel::IntID::UseOverlayScrollbars) ||
1690 mInRDMPane;
1693 void nsPresContext::ThemeChanged(widget::ThemeChangeKind aKind) {
1694 PROFILER_MARKER_UNTYPED("ThemeChanged", LAYOUT, MarkerStack::Capture());
1696 mPendingThemeChangeKind |= unsigned(aKind);
1698 if (!mPendingThemeChanged) {
1699 nsCOMPtr<nsIRunnable> ev =
1700 new WeakRunnableMethod("nsPresContext::ThemeChangedInternal", this,
1701 &nsPresContext::ThemeChangedInternal);
1702 RefreshDriver()->AddEarlyRunner(ev);
1703 mPendingThemeChanged = true;
1705 MOZ_ASSERT(LookAndFeel::HasPendingGlobalThemeChange());
1708 void nsPresContext::ThemeChangedInternal() {
1709 MOZ_ASSERT(mPendingThemeChanged);
1711 mPendingThemeChanged = false;
1713 const auto kind = widget::ThemeChangeKind(mPendingThemeChangeKind);
1714 mPendingThemeChangeKind = 0;
1716 LookAndFeel::HandleGlobalThemeChange();
1718 // Full zoom might have changed as a result of the text scale factor.
1719 RecomputeBrowsingContextDependentData();
1721 // Changes to system metrics and other look and feel values can change media
1722 // queries on them.
1724 // Changes in theme can change system colors (whose changes are properly
1725 // reflected in computed style data), system fonts (whose changes are
1726 // some reflected (like sizes and such) and some not), and -moz-appearance
1727 // (whose changes are not), so we need to recascade for the first, and reflow
1728 // for the rest.
1729 auto restyleHint = (kind & widget::ThemeChangeKind::Style)
1730 ? RestyleHint::RecascadeSubtree()
1731 : RestyleHint{0};
1732 auto changeHint = (kind & widget::ThemeChangeKind::Layout)
1733 ? NS_STYLE_HINT_REFLOW
1734 : nsChangeHint(0);
1735 MediaFeatureValuesChanged(
1736 {restyleHint, changeHint, MediaFeatureChangeReason::SystemMetricsChange},
1737 MediaFeatureChangePropagation::All);
1739 if (Document()->IsInChromeDocShell()) {
1740 if (RefPtr<nsPIDOMWindowInner> win = Document()->GetInnerWindow()) {
1741 nsContentUtils::DispatchEventOnlyToChrome(
1742 Document(), win, u"nativethemechange"_ns, CanBubble::eYes,
1743 Cancelable::eYes, nullptr);
1748 void nsPresContext::UIResolutionChanged() {
1749 if (!mPendingUIResolutionChanged) {
1750 nsCOMPtr<nsIRunnable> ev =
1751 NewRunnableMethod("nsPresContext::UIResolutionChangedInternal", this,
1752 &nsPresContext::UIResolutionChangedInternal);
1753 nsresult rv = Document()->Dispatch(TaskCategory::Other, ev.forget());
1754 if (NS_SUCCEEDED(rv)) {
1755 mPendingUIResolutionChanged = true;
1760 void nsPresContext::UIResolutionChangedSync() {
1761 if (!mPendingUIResolutionChanged) {
1762 mPendingUIResolutionChanged = true;
1763 UIResolutionChangedInternal();
1767 static void NotifyTabUIResolutionChanged(nsIRemoteTab* aTab, void* aArg) {
1768 aTab->NotifyResolutionChanged();
1771 static void NotifyChildrenUIResolutionChanged(nsPIDOMWindowOuter* aWindow) {
1772 nsCOMPtr<Document> doc = aWindow->GetExtantDoc();
1773 RefPtr<nsPIWindowRoot> topLevelWin = nsContentUtils::GetWindowRoot(doc);
1774 if (!topLevelWin) {
1775 return;
1777 topLevelWin->EnumerateBrowsers(NotifyTabUIResolutionChanged, nullptr);
1780 void nsPresContext::UIResolutionChangedInternal() {
1781 mPendingUIResolutionChanged = false;
1783 mDeviceContext->CheckDPIChange();
1784 if (mCurAppUnitsPerDevPixel != mDeviceContext->AppUnitsPerDevPixel()) {
1785 AppUnitsPerDevPixelChanged();
1788 if (mPresShell) {
1789 mPresShell->RefreshZoomConstraintsForScreenSizeChange();
1790 if (RefPtr<MobileViewportManager> mvm =
1791 mPresShell->GetMobileViewportManager()) {
1792 mvm->UpdateSizesBeforeReflow();
1796 // Recursively notify all remote leaf descendants of the change.
1797 if (nsPIDOMWindowOuter* window = mDocument->GetWindow()) {
1798 NotifyChildrenUIResolutionChanged(window);
1801 auto recurse = [](dom::Document& aSubDoc) {
1802 if (nsPresContext* pc = aSubDoc.GetPresContext()) {
1803 pc->UIResolutionChangedInternal();
1805 return CallState::Continue;
1807 mDocument->EnumerateSubDocuments(recurse);
1810 void nsPresContext::EmulateMedium(nsAtom* aMediaType) {
1811 MOZ_ASSERT(!aMediaType || aMediaType->IsAsciiLowercase());
1813 RefPtr<const nsAtom> oldMedium = Medium();
1814 auto oldScheme = mDocument->PreferredColorScheme();
1816 mMediaEmulationData.mMedium = aMediaType;
1818 if (Medium() == oldMedium) {
1819 return;
1822 MediaFeatureChange change(MediaFeatureChangeReason::MediumChange);
1823 if (oldScheme != mDocument->PreferredColorScheme()) {
1824 change |= MediaFeatureChange::ForPreferredColorSchemeChange();
1826 MediaFeatureValuesChanged(change,
1827 MediaFeatureChangePropagation::JustThisDocument);
1830 void nsPresContext::ContentLanguageChanged() {
1831 PostRebuildAllStyleDataEvent(nsChangeHint(0),
1832 RestyleHint::RecascadeSubtree());
1835 void nsPresContext::RegisterManagedPostRefreshObserver(
1836 ManagedPostRefreshObserver* aObserver) {
1837 if (MOZ_UNLIKELY(!mPresShell)) {
1838 // If we're detached from our pres shell already, refuse to keep observer
1839 // around, as that'd create a cycle.
1840 RefPtr<ManagedPostRefreshObserver> obs = aObserver;
1841 obs->Cancel();
1842 return;
1845 RefreshDriver()->AddPostRefreshObserver(
1846 static_cast<nsAPostRefreshObserver*>(aObserver));
1847 mManagedPostRefreshObservers.AppendElement(aObserver);
1850 void nsPresContext::UnregisterManagedPostRefreshObserver(
1851 ManagedPostRefreshObserver* aObserver) {
1852 RefreshDriver()->RemovePostRefreshObserver(
1853 static_cast<nsAPostRefreshObserver*>(aObserver));
1854 DebugOnly<bool> removed =
1855 mManagedPostRefreshObservers.RemoveElement(aObserver);
1856 MOZ_ASSERT(removed,
1857 "ManagedPostRefreshObserver should be owned by PresContext");
1860 void nsPresContext::CancelManagedPostRefreshObservers() {
1861 auto observers = std::move(mManagedPostRefreshObservers);
1862 nsRefreshDriver* driver = RefreshDriver();
1863 for (const auto& observer : observers) {
1864 observer->Cancel();
1865 driver->RemovePostRefreshObserver(
1866 static_cast<nsAPostRefreshObserver*>(observer));
1870 void nsPresContext::RebuildAllStyleData(nsChangeHint aExtraHint,
1871 const RestyleHint& aRestyleHint) {
1872 if (!mPresShell) {
1873 // We must have been torn down. Nothing to do here.
1874 return;
1877 // TODO(emilio): It's unclear to me why would these three calls below be
1878 // needed. In particular, RebuildAllStyleData doesn't rebuild rules or
1879 // specified style information and such (note the comment in
1880 // RestyleManager::RebuildAllStyleData re. the funny semantics), so I
1881 // don't know why should we rebuild the user font set / counter styles /
1882 // etc...
1883 mDocument->MarkUserFontSetDirty();
1884 MarkCounterStylesDirty();
1885 MarkFontFeatureValuesDirty();
1886 MarkFontPaletteValuesDirty();
1887 PostRebuildAllStyleDataEvent(aExtraHint, aRestyleHint);
1890 void nsPresContext::PostRebuildAllStyleDataEvent(
1891 nsChangeHint aExtraHint, const RestyleHint& aRestyleHint) {
1892 if (!mPresShell) {
1893 // We must have been torn down. Nothing to do here.
1894 return;
1896 RestyleManager()->RebuildAllStyleData(aExtraHint, aRestyleHint);
1899 void nsPresContext::MediaFeatureValuesChanged(
1900 const MediaFeatureChange& aChange,
1901 MediaFeatureChangePropagation aPropagation) {
1902 if (mPresShell) {
1903 mPresShell->EnsureStyleFlush();
1906 if (!mPendingMediaFeatureValuesChange) {
1907 mPendingMediaFeatureValuesChange = MakeUnique<MediaFeatureChange>(aChange);
1908 } else {
1909 *mPendingMediaFeatureValuesChange |= aChange;
1912 if (aPropagation & MediaFeatureChangePropagation::Images) {
1913 // Propagate the media feature value change down to any SVG images the
1914 // document is using.
1915 mDocument->ImageTracker()->MediaFeatureValuesChangedAllDocuments(aChange);
1918 if (aPropagation & MediaFeatureChangePropagation::SubDocuments) {
1919 // And then into any subdocuments.
1920 auto recurse = [&aChange, aPropagation](dom::Document& aSubDoc) {
1921 if (nsPresContext* pc = aSubDoc.GetPresContext()) {
1922 pc->MediaFeatureValuesChanged(aChange, aPropagation);
1924 return CallState::Continue;
1926 mDocument->EnumerateSubDocuments(recurse);
1929 // We notify the media feature values changed for the responsive content of
1930 // HTMLImageElements synchronously, so their image sources are always
1931 // up-to-date when running the image load tasks in the microtasks.
1932 mDocument->NotifyMediaFeatureValuesChanged();
1935 bool nsPresContext::FlushPendingMediaFeatureValuesChanged() {
1936 if (!mPendingMediaFeatureValuesChange) {
1937 return false;
1940 MediaFeatureChange change = *mPendingMediaFeatureValuesChange;
1941 mPendingMediaFeatureValuesChange.reset();
1943 // MediumFeaturesChanged updates the applied rules, so it always gets called.
1944 if (mPresShell) {
1945 change.mRestyleHint |=
1946 mPresShell->StyleSet()->MediumFeaturesChanged(change.mReason);
1949 const bool changedStyle = change.mRestyleHint || change.mChangeHint;
1950 if (changedStyle) {
1951 RebuildAllStyleData(change.mChangeHint, change.mRestyleHint);
1954 if (mDocument->IsBeingUsedAsImage()) {
1955 MOZ_ASSERT(mDocument->MediaQueryLists().isEmpty());
1956 return changedStyle;
1959 // https://drafts.csswg.org/cssom-view/#evaluate-media-queries-and-report-changes
1961 // Media query list listeners should be notified from a queued task
1962 // (in HTML5 terms), although we also want to notify them on certain
1963 // flushes. (We're already running off an event.)
1965 // TODO: This should be better integrated into the "update the rendering"
1966 // steps: https://html.spec.whatwg.org/#update-the-rendering
1968 // Note that we do this after the new style from media queries in
1969 // style sheets has been computed.
1971 if (mDocument->MediaQueryLists().isEmpty()) {
1972 return changedStyle;
1975 // We build a list of all the notifications we're going to send
1976 // before we send any of them.
1977 nsTArray<RefPtr<mozilla::dom::MediaQueryList>> listsToNotify;
1978 for (MediaQueryList* mql = mDocument->MediaQueryLists().getFirst(); mql;
1979 mql = static_cast<LinkedListElement<MediaQueryList>*>(mql)->getNext()) {
1980 if (mql->MediaFeatureValuesChanged()) {
1981 listsToNotify.AppendElement(mql);
1985 if (!listsToNotify.IsEmpty()) {
1986 nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
1987 "nsPresContext::FlushPendingMediaFeatureValuesChanged",
1988 [list = std::move(listsToNotify)] {
1989 for (const auto& mql : list) {
1990 nsAutoMicroTask mt;
1991 mql->FireChangeEvent();
1993 }));
1996 return changedStyle;
1999 void nsPresContext::SizeModeChanged(nsSizeMode aSizeMode) {
2000 if (nsPIDOMWindowOuter* window = mDocument->GetWindow()) {
2001 nsContentUtils::CallOnAllRemoteChildren(
2002 window, [&aSizeMode](BrowserParent* aBrowserParent) -> CallState {
2003 aBrowserParent->SizeModeChanged(aSizeMode);
2004 return CallState::Continue;
2007 MediaFeatureValuesChanged({MediaFeatureChangeReason::SizeModeChange},
2008 MediaFeatureChangePropagation::SubDocuments);
2011 nsCompatibility nsPresContext::CompatibilityMode() const {
2012 return Document()->GetCompatibilityMode();
2015 void nsPresContext::SetPaginatedScrolling(bool aPaginated) {
2016 if (mType == eContext_PrintPreview || mType == eContext_PageLayout) {
2017 mCanPaginatedScroll = aPaginated;
2021 void nsPresContext::SetPrintSettings(nsIPrintSettings* aPrintSettings) {
2022 if (mMedium != nsGkAtoms::print) {
2023 return;
2026 mPrintSettings = aPrintSettings;
2027 mDefaultPageMargin = nsMargin();
2028 if (!mPrintSettings) {
2029 return;
2032 // Set the presentation context to the value in the print settings.
2033 mDrawColorBackground = mPrintSettings->GetPrintBGColors();
2034 mDrawImageBackground = mPrintSettings->GetPrintBGImages();
2036 nsIntMargin marginTwips = mPrintSettings->GetMarginInTwips();
2037 if (!mPrintSettings->GetIgnoreUnwriteableMargins()) {
2038 nsIntMargin unwriteableTwips =
2039 mPrintSettings->GetUnwriteableMarginInTwips();
2040 NS_ASSERTION(unwriteableTwips.top >= 0 && unwriteableTwips.right >= 0 &&
2041 unwriteableTwips.bottom >= 0 && unwriteableTwips.left >= 0,
2042 "Unwriteable twips should be non-negative");
2043 marginTwips.EnsureAtLeast(unwriteableTwips);
2045 mDefaultPageMargin = nsPresContext::CSSTwipsToAppUnits(marginTwips);
2048 bool nsPresContext::EnsureVisible() {
2049 BrowsingContext* browsingContext =
2050 mDocument ? mDocument->GetBrowsingContext() : nullptr;
2051 if (!browsingContext || browsingContext->IsInBFCache()) {
2052 return false;
2055 nsCOMPtr<nsIDocShell> docShell(GetDocShell());
2056 if (!docShell) {
2057 return false;
2059 nsCOMPtr<nsIContentViewer> cv;
2060 docShell->GetContentViewer(getter_AddRefs(cv));
2061 // Make sure this is the content viewer we belong with
2062 if (!cv || cv->GetPresContext() != this) {
2063 return false;
2065 // OK, this is us. We want to call Show() on the content viewer.
2066 nsresult result = cv->Show();
2067 return NS_SUCCEEDED(result);
2070 #ifdef MOZ_REFLOW_PERF
2071 void nsPresContext::CountReflows(const char* aName, nsIFrame* aFrame) {
2072 if (mPresShell) {
2073 mPresShell->CountReflows(aName, aFrame);
2076 #endif
2078 gfxUserFontSet* nsPresContext::GetUserFontSet() {
2079 return mDocument->GetUserFontSet();
2082 void nsPresContext::UserFontSetUpdated(gfxUserFontEntry* aUpdatedFont) {
2083 if (!mPresShell) {
2084 return;
2087 // Note: this method is called without a font when rules in the userfont set
2088 // are updated.
2090 // We can avoid a full restyle if font-metric-dependent units are not in use,
2091 // since we know there's no style resolution that would depend on this font
2092 // and trigger its load.
2094 // TODO(emilio): We could be more granular if we knew which families have
2095 // potentially changed.
2096 if (!aUpdatedFont) {
2097 auto hint = StyleSet()->UsesFontMetrics() ? RestyleHint::RecascadeSubtree()
2098 : RestyleHint{0};
2099 PostRebuildAllStyleDataEvent(NS_STYLE_HINT_REFLOW, hint);
2100 return;
2103 // Iterate over the frame tree looking for frames associated with the
2104 // downloadable font family in question. If a frame's nsStyleFont has
2105 // the name, check the font group associated with the metrics to see if
2106 // it contains that specific font (i.e. the one chosen within the family
2107 // given the weight, width, and slant from the nsStyleFont). If it does,
2108 // mark that frame dirty and skip inspecting its descendants.
2109 if (nsIFrame* root = mPresShell->GetRootFrame()) {
2110 nsFontFaceUtils::MarkDirtyForFontChange(root, aUpdatedFont);
2114 class CounterStyleCleaner final : public nsAPostRefreshObserver {
2115 public:
2116 CounterStyleCleaner(nsRefreshDriver* aRefreshDriver,
2117 CounterStyleManager* aCounterStyleManager)
2118 : mRefreshDriver(aRefreshDriver),
2119 mCounterStyleManager(aCounterStyleManager) {}
2120 virtual ~CounterStyleCleaner() = default;
2122 void DidRefresh() final {
2123 mRefreshDriver->RemovePostRefreshObserver(this);
2124 mCounterStyleManager->CleanRetiredStyles();
2125 delete this;
2128 private:
2129 RefPtr<nsRefreshDriver> mRefreshDriver;
2130 RefPtr<CounterStyleManager> mCounterStyleManager;
2133 void nsPresContext::FlushCounterStyles() {
2134 if (!mPresShell) {
2135 return; // we've been torn down
2137 if (mCounterStyleManager->IsInitial()) {
2138 // Still in its initial state, no need to clean.
2139 return;
2142 if (mCounterStylesDirty) {
2143 bool changed = mCounterStyleManager->NotifyRuleChanged();
2144 if (changed) {
2145 PresShell()->NotifyCounterStylesAreDirty();
2146 PostRebuildAllStyleDataEvent(NS_STYLE_HINT_REFLOW, RestyleHint{0});
2147 RefreshDriver()->AddPostRefreshObserver(
2148 new CounterStyleCleaner(RefreshDriver(), mCounterStyleManager));
2150 mCounterStylesDirty = false;
2154 void nsPresContext::MarkCounterStylesDirty() {
2155 if (mCounterStyleManager->IsInitial()) {
2156 // Still in its initial state, no need to touch anything.
2157 return;
2160 mCounterStylesDirty = true;
2163 void nsPresContext::NotifyMissingFonts() {
2164 if (mMissingFonts) {
2165 mMissingFonts->Flush();
2169 void nsPresContext::EnsureSafeToHandOutCSSRules() {
2170 if (!mPresShell->StyleSet()->EnsureUniqueInnerOnCSSSheets()) {
2171 // Nothing to do.
2172 return;
2175 RebuildAllStyleData(nsChangeHint(0), RestyleHint::RestyleSubtree());
2178 void nsPresContext::FireDOMPaintEvent(
2179 nsTArray<nsRect>* aList, TransactionId aTransactionId,
2180 mozilla::TimeStamp aTimeStamp /* = mozilla::TimeStamp() */) {
2181 nsPIDOMWindowInner* ourWindow = mDocument->GetInnerWindow();
2182 if (!ourWindow) return;
2184 nsCOMPtr<EventTarget> dispatchTarget = do_QueryInterface(ourWindow);
2185 nsCOMPtr<EventTarget> eventTarget = dispatchTarget;
2186 if (!IsChrome() && !mSendAfterPaintToContent) {
2187 // Don't tell the window about this event, it should not know that
2188 // something happened in a subdocument. Tell only the chrome event handler.
2189 // (Events sent to the window get propagated to the chrome event handler
2190 // automatically.)
2191 dispatchTarget = ourWindow->GetParentTarget();
2192 if (!dispatchTarget) {
2193 return;
2197 if (aTimeStamp.IsNull()) {
2198 aTimeStamp = mozilla::TimeStamp::Now();
2200 DOMHighResTimeStamp timeStamp = 0;
2201 if (ourWindow) {
2202 mozilla::dom::Performance* perf = ourWindow->GetPerformance();
2203 if (perf) {
2204 timeStamp = perf->GetDOMTiming()->TimeStampToDOMHighRes(aTimeStamp);
2208 // Events sent to the window get propagated to the chrome event handler
2209 // automatically.
2211 // This will empty our list in case dispatching the event causes more damage
2212 // (hopefully it won't, or we're likely to get an infinite loop! At least
2213 // it won't be blocking app execution though).
2214 RefPtr<NotifyPaintEvent> event =
2215 NS_NewDOMNotifyPaintEvent(eventTarget, this, nullptr, eAfterPaint, aList,
2216 uint64_t(aTransactionId), timeStamp);
2218 // Even if we're not telling the window about the event (so eventTarget is
2219 // the chrome event handler, not the window), the window is still
2220 // logically the event target.
2221 event->SetTarget(eventTarget);
2222 event->SetTrusted(true);
2223 EventDispatcher::DispatchDOMEvent(dispatchTarget, nullptr,
2224 static_cast<Event*>(event), this, nullptr);
2227 static bool MayHavePaintEventListener(nsPIDOMWindowInner* aInnerWindow) {
2228 if (!aInnerWindow) return false;
2229 if (aInnerWindow->HasPaintEventListeners()) return true;
2231 EventTarget* parentTarget = aInnerWindow->GetParentTarget();
2232 if (!parentTarget) return false;
2234 EventListenerManager* manager = nullptr;
2235 if ((manager = parentTarget->GetExistingListenerManager()) &&
2236 manager->MayHavePaintEventListener()) {
2237 return true;
2240 nsCOMPtr<nsINode> node;
2241 if (parentTarget != aInnerWindow->GetChromeEventHandler()) {
2242 nsCOMPtr<nsIInProcessContentFrameMessageManager> mm =
2243 do_QueryInterface(parentTarget);
2244 if (mm) {
2245 node = mm->GetOwnerContent();
2249 if (!node) {
2250 node = nsINode::FromEventTarget(parentTarget);
2252 if (node) {
2253 return MayHavePaintEventListener(node->OwnerDoc()->GetInnerWindow());
2256 if (nsCOMPtr<nsPIDOMWindowInner> window =
2257 nsPIDOMWindowInner::FromEventTarget(parentTarget)) {
2258 return MayHavePaintEventListener(window);
2261 if (nsCOMPtr<nsPIWindowRoot> root =
2262 nsPIWindowRoot::FromEventTarget(parentTarget)) {
2263 EventTarget* browserChildGlobal;
2264 return root && (browserChildGlobal = root->GetParentTarget()) &&
2265 (manager = browserChildGlobal->GetExistingListenerManager()) &&
2266 manager->MayHavePaintEventListener();
2269 return false;
2272 bool nsPresContext::MayHavePaintEventListener() {
2273 return ::MayHavePaintEventListener(mDocument->GetInnerWindow());
2276 void nsPresContext::NotifyInvalidation(TransactionId aTransactionId,
2277 const nsIntRect& aRect) {
2278 // Prevent values from overflow after DevPixelsToAppUnits().
2280 // DevPixelsTopAppUnits() will multiple a factor (60) to the value,
2281 // it may make the result value over the edge (overflow) of max or
2282 // min value of int32_t. Compute the max sized dev pixel rect that
2283 // we can support and intersect with it.
2284 nsIntRect clampedRect = nsIntRect::MaxIntRect();
2285 clampedRect.ScaleInverseRoundIn(AppUnitsPerDevPixel());
2287 clampedRect = clampedRect.Intersect(aRect);
2289 nsRect rect(DevPixelsToAppUnits(clampedRect.x),
2290 DevPixelsToAppUnits(clampedRect.y),
2291 DevPixelsToAppUnits(clampedRect.width),
2292 DevPixelsToAppUnits(clampedRect.height));
2293 NotifyInvalidation(aTransactionId, rect);
2296 nsPresContext::TransactionInvalidations* nsPresContext::GetInvalidations(
2297 TransactionId aTransactionId) {
2298 for (TransactionInvalidations& t : mTransactions) {
2299 if (t.mTransactionId == aTransactionId) {
2300 return &t;
2303 return nullptr;
2306 void nsPresContext::NotifyInvalidation(TransactionId aTransactionId,
2307 const nsRect& aRect) {
2308 MOZ_ASSERT(GetContainerWeak(), "Invalidation in detached pres context");
2310 // If there is no paint event listener, then we don't need to fire
2311 // the asynchronous event. We don't even need to record invalidation.
2312 // MayHavePaintEventListener is pretty cheap and we could make it
2313 // even cheaper by providing a more efficient
2314 // nsPIDOMWindow::GetListenerManager.
2316 nsPresContext* pc;
2317 for (pc = this; pc; pc = pc->GetParentPresContext()) {
2318 TransactionInvalidations* transaction =
2319 pc->GetInvalidations(aTransactionId);
2320 if (transaction) {
2321 break;
2322 } else {
2323 transaction = pc->mTransactions.AppendElement();
2324 transaction->mTransactionId = aTransactionId;
2328 TransactionInvalidations* transaction = GetInvalidations(aTransactionId);
2329 MOZ_ASSERT(transaction);
2330 transaction->mInvalidations.AppendElement(aRect);
2333 class DelayedFireDOMPaintEvent : public Runnable {
2334 public:
2335 DelayedFireDOMPaintEvent(
2336 nsPresContext* aPresContext, nsTArray<nsRect>&& aList,
2337 TransactionId aTransactionId,
2338 const mozilla::TimeStamp& aTimeStamp = mozilla::TimeStamp())
2339 : mozilla::Runnable("DelayedFireDOMPaintEvent"),
2340 mPresContext(aPresContext),
2341 mTransactionId(aTransactionId),
2342 mTimeStamp(aTimeStamp),
2343 mList(std::move(aList)) {
2344 MOZ_ASSERT(mPresContext->GetContainerWeak(),
2345 "DOMPaintEvent requested for a detached pres context");
2347 NS_IMETHOD Run() override {
2348 // The pres context might have been detached during the delay -
2349 // that's fine, just don't fire the event.
2350 if (mPresContext->GetContainerWeak()) {
2351 mPresContext->FireDOMPaintEvent(&mList, mTransactionId, mTimeStamp);
2353 return NS_OK;
2356 RefPtr<nsPresContext> mPresContext;
2357 TransactionId mTransactionId;
2358 const mozilla::TimeStamp mTimeStamp;
2359 nsTArray<nsRect> mList;
2362 void nsPresContext::NotifyRevokingDidPaint(TransactionId aTransactionId) {
2363 if ((IsRoot() || !PresShell()->IsVisible()) && mTransactions.IsEmpty()) {
2364 return;
2367 TransactionInvalidations* transaction = nullptr;
2368 for (auto& t : mTransactions) {
2369 if (t.mTransactionId == aTransactionId) {
2370 transaction = &t;
2371 break;
2374 // If there are no transaction invalidations (which imply callers waiting
2375 // on the event) for this revoked id, then we don't need to fire a
2376 // MozAfterPaint.
2377 if (!transaction) {
2378 return;
2381 // If there are queued transactions with an earlier id, we can't send
2382 // our event now since it will arrive out of order. Set the waiting for
2383 // previous transaction flag to true, and we'll send the event when
2384 // the others are completed.
2385 // If this is the only transaction, then we can send it immediately.
2386 if (mTransactions.Length() == 1) {
2387 nsCOMPtr<nsIRunnable> ev = new DelayedFireDOMPaintEvent(
2388 this, std::move(transaction->mInvalidations),
2389 transaction->mTransactionId, mozilla::TimeStamp());
2390 nsContentUtils::AddScriptRunner(ev);
2391 mTransactions.RemoveElementAt(0);
2392 } else {
2393 transaction->mIsWaitingForPreviousTransaction = true;
2396 auto recurse = [&aTransactionId](dom::Document& aSubDoc) {
2397 if (nsPresContext* pc = aSubDoc.GetPresContext()) {
2398 pc->NotifyRevokingDidPaint(aTransactionId);
2400 return CallState::Continue;
2402 mDocument->EnumerateSubDocuments(recurse);
2405 void nsPresContext::NotifyDidPaintForSubtree(
2406 TransactionId aTransactionId, const mozilla::TimeStamp& aTimeStamp) {
2407 if (mFirstContentfulPaintTransactionId && !mHadContentfulPaintComposite) {
2408 if (aTransactionId >= *mFirstContentfulPaintTransactionId) {
2409 mHadContentfulPaintComposite = true;
2410 RefPtr<nsDOMNavigationTiming> timing = mDocument->GetNavigationTiming();
2411 if (timing && !IsPrintingOrPrintPreview()) {
2412 timing->NotifyContentfulCompositeForRootContentDocument(aTimeStamp);
2417 if (IsRoot() && mTransactions.IsEmpty()) {
2418 return;
2421 if (!PresShell()->IsVisible() && mTransactions.IsEmpty()) {
2422 return;
2425 // Non-root prescontexts fire MozAfterPaint to all their descendants
2426 // unconditionally, even if no invalidations have been collected. This is
2427 // because we don't want to eat the cost of collecting invalidations for
2428 // every subdocument (which would require putting every subdocument in its
2429 // own layer).
2431 bool sent = false;
2432 uint32_t i = 0;
2433 while (i < mTransactions.Length()) {
2434 if (mTransactions[i].mTransactionId <= aTransactionId) {
2435 if (!mTransactions[i].mInvalidations.IsEmpty()) {
2436 nsCOMPtr<nsIRunnable> ev = new DelayedFireDOMPaintEvent(
2437 this, std::move(mTransactions[i].mInvalidations),
2438 mTransactions[i].mTransactionId, aTimeStamp);
2439 NS_DispatchToCurrentThreadQueue(ev.forget(),
2440 EventQueuePriority::MediumHigh);
2441 sent = true;
2443 mTransactions.RemoveElementAt(i);
2444 } else {
2445 // If there are transaction which is waiting for this transaction,
2446 // we should fire a MozAfterPaint immediately.
2447 if (sent && mTransactions[i].mIsWaitingForPreviousTransaction) {
2448 nsCOMPtr<nsIRunnable> ev = new DelayedFireDOMPaintEvent(
2449 this, std::move(mTransactions[i].mInvalidations),
2450 mTransactions[i].mTransactionId, aTimeStamp);
2451 NS_DispatchToCurrentThreadQueue(ev.forget(),
2452 EventQueuePriority::MediumHigh);
2453 sent = true;
2454 mTransactions.RemoveElementAt(i);
2455 continue;
2457 i++;
2461 if (!sent) {
2462 nsTArray<nsRect> dummy;
2463 nsCOMPtr<nsIRunnable> ev = new DelayedFireDOMPaintEvent(
2464 this, std::move(dummy), aTransactionId, aTimeStamp);
2465 NS_DispatchToCurrentThreadQueue(ev.forget(),
2466 EventQueuePriority::MediumHigh);
2469 auto recurse = [&aTransactionId, &aTimeStamp](dom::Document& aSubDoc) {
2470 if (nsPresContext* pc = aSubDoc.GetPresContext()) {
2471 pc->NotifyDidPaintForSubtree(aTransactionId, aTimeStamp);
2473 return CallState::Continue;
2475 mDocument->EnumerateSubDocuments(recurse);
2478 already_AddRefed<nsITimer> nsPresContext::CreateTimer(
2479 nsTimerCallbackFunc aCallback, const char* aName, uint32_t aDelay) {
2480 nsCOMPtr<nsITimer> timer;
2481 NS_NewTimerWithFuncCallback(getter_AddRefs(timer), aCallback, this, aDelay,
2482 nsITimer::TYPE_ONE_SHOT, aName,
2483 Document()->EventTargetFor(TaskCategory::Other));
2484 return timer.forget();
2487 static bool sGotInterruptEnv = false;
2488 enum InterruptMode { ModeRandom, ModeCounter, ModeEvent };
2489 // Controlled by the GECKO_REFLOW_INTERRUPT_MODE env var; allowed values are
2490 // "random" (except on Windows) or "counter". If neither is used, the mode is
2491 // ModeEvent.
2492 static InterruptMode sInterruptMode = ModeEvent;
2493 #ifndef XP_WIN
2494 // Used for the "random" mode. Controlled by the GECKO_REFLOW_INTERRUPT_SEED
2495 // env var.
2496 static uint32_t sInterruptSeed = 1;
2497 #endif
2498 // Used for the "counter" mode. This is the number of unskipped interrupt
2499 // checks that have to happen before we interrupt. Controlled by the
2500 // GECKO_REFLOW_INTERRUPT_FREQUENCY env var.
2501 static uint32_t sInterruptMaxCounter = 10;
2502 // Used for the "counter" mode. This counts up to sInterruptMaxCounter and is
2503 // then reset to 0.
2504 static uint32_t sInterruptCounter;
2505 // Number of interrupt checks to skip before really trying to interrupt.
2506 // Controlled by the GECKO_REFLOW_INTERRUPT_CHECKS_TO_SKIP env var.
2507 static uint32_t sInterruptChecksToSkip = 200;
2508 // Number of milliseconds that a reflow should be allowed to run for before we
2509 // actually allow interruption. Controlled by the
2510 // GECKO_REFLOW_MIN_NOINTERRUPT_DURATION env var. Can't be initialized here,
2511 // because TimeDuration/TimeStamp is not safe to use in static constructors..
2512 static TimeDuration sInterruptTimeout;
2514 static void GetInterruptEnv() {
2515 char* ev = PR_GetEnv("GECKO_REFLOW_INTERRUPT_MODE");
2516 if (ev) {
2517 #ifndef XP_WIN
2518 if (nsCRT::strcasecmp(ev, "random") == 0) {
2519 ev = PR_GetEnv("GECKO_REFLOW_INTERRUPT_SEED");
2520 if (ev) {
2521 sInterruptSeed = atoi(ev);
2523 srandom(sInterruptSeed);
2524 sInterruptMode = ModeRandom;
2525 } else
2526 #endif
2527 if (PL_strcasecmp(ev, "counter") == 0) {
2528 ev = PR_GetEnv("GECKO_REFLOW_INTERRUPT_FREQUENCY");
2529 if (ev) {
2530 sInterruptMaxCounter = atoi(ev);
2532 sInterruptCounter = 0;
2533 sInterruptMode = ModeCounter;
2536 ev = PR_GetEnv("GECKO_REFLOW_INTERRUPT_CHECKS_TO_SKIP");
2537 if (ev) {
2538 sInterruptChecksToSkip = atoi(ev);
2541 ev = PR_GetEnv("GECKO_REFLOW_MIN_NOINTERRUPT_DURATION");
2542 int duration_ms = ev ? atoi(ev) : 100;
2543 sInterruptTimeout = TimeDuration::FromMilliseconds(duration_ms);
2546 bool nsPresContext::HavePendingInputEvent() {
2547 switch (sInterruptMode) {
2548 #ifndef XP_WIN
2549 case ModeRandom:
2550 return (random() & 1);
2551 #endif
2552 case ModeCounter:
2553 if (sInterruptCounter < sInterruptMaxCounter) {
2554 ++sInterruptCounter;
2555 return false;
2557 sInterruptCounter = 0;
2558 return true;
2559 default:
2560 case ModeEvent: {
2561 nsIFrame* f = PresShell()->GetRootFrame();
2562 if (f) {
2563 nsIWidget* w = f->GetNearestWidget();
2564 if (w) {
2565 return w->HasPendingInputEvent();
2568 return false;
2573 bool nsPresContext::HasPendingRestyleOrReflow() {
2574 mozilla::PresShell* presShell = PresShell();
2575 return presShell->NeedStyleFlush() || presShell->HasPendingReflow();
2578 void nsPresContext::ReflowStarted(bool aInterruptible) {
2579 #ifdef NOISY_INTERRUPTIBLE_REFLOW
2580 if (!aInterruptible) {
2581 printf("STARTING NONINTERRUPTIBLE REFLOW\n");
2583 #endif
2584 // We don't support interrupting in paginated contexts, since page
2585 // sequences only handle initial reflow
2586 mInterruptsEnabled = aInterruptible && !IsPaginated() &&
2587 StaticPrefs::layout_interruptible_reflow_enabled();
2589 // Don't set mHasPendingInterrupt based on HavePendingInputEvent() here. If
2590 // we ever change that, then we need to update the code in
2591 // PresShell::DoReflow to only add the just-reflown root to dirty roots if
2592 // it's actually dirty. Otherwise we can end up adding a root that has no
2593 // interruptible descendants, just because we detected an interrupt at reflow
2594 // start.
2595 mHasPendingInterrupt = false;
2597 mInterruptChecksToSkip = sInterruptChecksToSkip;
2599 if (mInterruptsEnabled) {
2600 mReflowStartTime = TimeStamp::Now();
2604 bool nsPresContext::CheckForInterrupt(nsIFrame* aFrame) {
2605 if (mHasPendingInterrupt) {
2606 mPresShell->FrameNeedsToContinueReflow(aFrame);
2607 return true;
2610 if (!sGotInterruptEnv) {
2611 sGotInterruptEnv = true;
2612 GetInterruptEnv();
2615 if (!mInterruptsEnabled) {
2616 return false;
2619 if (mInterruptChecksToSkip > 0) {
2620 --mInterruptChecksToSkip;
2621 return false;
2623 mInterruptChecksToSkip = sInterruptChecksToSkip;
2625 // Don't interrupt if it's been less than sInterruptTimeout since we started
2626 // the reflow.
2627 mHasPendingInterrupt =
2628 TimeStamp::Now() - mReflowStartTime > sInterruptTimeout &&
2629 HavePendingInputEvent() && !IsChrome();
2631 if (mPendingInterruptFromTest) {
2632 mPendingInterruptFromTest = false;
2633 mHasPendingInterrupt = true;
2636 if (mHasPendingInterrupt) {
2637 #ifdef NOISY_INTERRUPTIBLE_REFLOW
2638 printf("*** DETECTED pending interrupt (time=%lld)\n", PR_Now());
2639 #endif /* NOISY_INTERRUPTIBLE_REFLOW */
2640 mPresShell->FrameNeedsToContinueReflow(aFrame);
2642 return mHasPendingInterrupt;
2645 nsIFrame* nsPresContext::GetPrimaryFrameFor(nsIContent* aContent) {
2646 MOZ_ASSERT(aContent, "Don't do that");
2647 if (GetPresShell() &&
2648 GetPresShell()->GetDocument() == aContent->GetComposedDoc()) {
2649 return aContent->GetPrimaryFrame();
2651 return nullptr;
2654 size_t nsPresContext::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
2655 // Measurement may be added later if DMD finds it is worthwhile.
2656 return 0;
2659 bool nsPresContext::IsRootContentDocumentInProcess() const {
2660 if (mDocument->IsResourceDoc()) {
2661 return false;
2663 if (IsChrome()) {
2664 return false;
2666 // We may not have a root frame, so use views.
2667 nsView* view = PresShell()->GetViewManager()->GetRootView();
2668 if (!view) {
2669 return false;
2671 view = view->GetParent(); // anonymous inner view
2672 if (!view) {
2673 return true;
2675 view = view->GetParent(); // subdocumentframe's view
2676 if (!view) {
2677 return true;
2680 nsIFrame* f = view->GetFrame();
2681 return (f && f->PresContext()->IsChrome());
2684 bool nsPresContext::IsRootContentDocumentCrossProcess() const {
2685 if (mDocument->IsResourceDoc()) {
2686 return false;
2689 if (BrowsingContext* browsingContext = mDocument->GetBrowsingContext()) {
2690 if (browsingContext->GetEmbeddedInContentDocument()) {
2691 return false;
2694 return mDocument->IsTopLevelContentDocument();
2697 void nsPresContext::NotifyNonBlankPaint() {
2698 MOZ_ASSERT(!mHadNonBlankPaint);
2699 mHadNonBlankPaint = true;
2700 if (IsRootContentDocumentCrossProcess()) {
2701 RefPtr<nsDOMNavigationTiming> timing = mDocument->GetNavigationTiming();
2702 if (timing && !IsPrintingOrPrintPreview()) {
2703 timing->NotifyNonBlankPaintForRootContentDocument();
2706 mFirstNonBlankPaintTime = TimeStamp::Now();
2708 if (IsChrome() && IsRoot()) {
2709 if (nsCOMPtr<nsIWidget> rootWidget = GetRootWidget()) {
2710 rootWidget->DidGetNonBlankPaint();
2715 void nsPresContext::NotifyContentfulPaint() {
2716 if (mHadContentfulPaint) {
2717 return;
2719 nsRootPresContext* rootPresContext = GetRootPresContext();
2720 if (!rootPresContext) {
2721 return;
2723 if (!mHadNonTickContentfulPaint) {
2724 #ifdef MOZ_WIDGET_ANDROID
2725 (new AsyncEventDispatcher(mDocument, u"MozFirstContentfulPaint"_ns,
2726 CanBubble::eYes, ChromeOnlyDispatch::eYes))
2727 ->PostDOMEvent();
2728 #endif
2730 if (!rootPresContext->RefreshDriver()->IsInRefresh()) {
2731 if (!mHadNonTickContentfulPaint) {
2732 rootPresContext->RefreshDriver()
2733 ->AddForceNotifyContentfulPaintPresContext(this);
2734 mHadNonTickContentfulPaint = true;
2736 return;
2738 mHadContentfulPaint = true;
2739 mFirstContentfulPaintTransactionId =
2740 Some(rootPresContext->mRefreshDriver->LastTransactionId().Next());
2741 if (nsPIDOMWindowInner* innerWindow = mDocument->GetInnerWindow()) {
2742 if (Performance* perf = innerWindow->GetPerformance()) {
2743 TimeStamp nowTime = rootPresContext->RefreshDriver()->MostRecentRefresh(
2744 /* aEnsureTimerStarted */ false);
2745 MOZ_ASSERT(!nowTime.IsNull(),
2746 "Most recent refresh timestamp should exist since we are in "
2747 "a refresh driver tick");
2748 MOZ_ASSERT(rootPresContext->RefreshDriver()->IsInRefresh(),
2749 "We should only notify contentful paint during refresh "
2750 "driver ticks");
2751 RefPtr<PerformancePaintTiming> paintTiming = new PerformancePaintTiming(
2752 perf, u"first-contentful-paint"_ns, nowTime);
2753 perf->SetFCPTimingEntry(paintTiming);
2755 if (profiler_thread_is_being_profiled_for_markers()) {
2756 RefPtr<nsDOMNavigationTiming> timing = mDocument->GetNavigationTiming();
2757 if (timing) {
2758 TimeStamp navigationStart = timing->GetNavigationStartTimeStamp();
2759 TimeDuration elapsed = nowTime - navigationStart;
2760 nsIURI* docURI = Document()->GetDocumentURI();
2761 nsPrintfCString marker(
2762 "Contentful paint after %dms for URL %s",
2763 int(elapsed.ToMilliseconds()),
2764 nsContentUtils::TruncatedURLForDisplay(docURI).get());
2765 PROFILER_MARKER_TEXT(
2766 "FirstContentfulPaint", DOM,
2767 MarkerOptions(MarkerTiming::Interval(navigationStart, nowTime),
2768 MarkerInnerWindowId(innerWindow->WindowID())),
2769 marker);
2776 void nsPresContext::NotifyPaintStatusReset() {
2777 mHadNonBlankPaint = false;
2778 mHadContentfulPaint = false;
2779 #if defined(MOZ_WIDGET_ANDROID)
2780 (new AsyncEventDispatcher(mDocument, u"MozPaintStatusReset"_ns,
2781 CanBubble::eYes, ChromeOnlyDispatch::eYes))
2782 ->PostDOMEvent();
2783 #endif
2784 mHadNonTickContentfulPaint = false;
2787 void nsPresContext::NotifyDOMContentFlushed() {
2788 NS_ENSURE_TRUE_VOID(mPresShell);
2789 if (IsRootContentDocumentCrossProcess()) {
2790 RefPtr<nsDOMNavigationTiming> timing = mDocument->GetNavigationTiming();
2791 if (timing) {
2792 timing->NotifyDOMContentFlushedForRootContentDocument();
2797 nscoord nsPresContext::GfxUnitsToAppUnits(gfxFloat aGfxUnits) const {
2798 return mDeviceContext->GfxUnitsToAppUnits(aGfxUnits);
2801 gfxFloat nsPresContext::AppUnitsToGfxUnits(nscoord aAppUnits) const {
2802 return mDeviceContext->AppUnitsToGfxUnits(aAppUnits);
2805 nscoord nsPresContext::PhysicalMillimetersToAppUnits(float aMM) const {
2806 float inches = aMM / MM_PER_INCH_FLOAT;
2807 return NSToCoordFloorClamped(
2808 inches * float(DeviceContext()->AppUnitsPerPhysicalInch()));
2811 bool nsPresContext::IsDeviceSizePageSize() {
2812 nsIDocShell* docShell = GetDocShell();
2813 return docShell && docShell->GetDeviceSizeIsPageSize();
2816 uint64_t nsPresContext::GetRestyleGeneration() const {
2817 if (!mRestyleManager) {
2818 return 0;
2820 return mRestyleManager->GetRestyleGeneration();
2823 uint64_t nsPresContext::GetUndisplayedRestyleGeneration() const {
2824 if (!mRestyleManager) {
2825 return 0;
2827 return mRestyleManager->GetUndisplayedRestyleGeneration();
2830 mozilla::intl::Bidi& nsPresContext::BidiEngine() {
2831 MOZ_ASSERT(NS_IsMainThread());
2833 if (!mBidiEngine) {
2834 mBidiEngine = MakeUnique<mozilla::intl::Bidi>();
2836 return *mBidiEngine;
2839 void nsPresContext::FlushFontFeatureValues() {
2840 if (!mPresShell) {
2841 return; // we've been torn down
2844 if (!mFontFeatureValuesDirty) {
2845 return;
2848 ServoStyleSet* styleSet = mPresShell->StyleSet();
2849 mFontFeatureValuesLookup = styleSet->BuildFontFeatureValueSet();
2850 mFontFeatureValuesDirty = false;
2853 void nsPresContext::FlushFontPaletteValues() {
2854 if (!mPresShell) {
2855 return; // we've been torn down
2858 if (!mFontPaletteValuesDirty) {
2859 return;
2862 ServoStyleSet* styleSet = mPresShell->StyleSet();
2863 mFontPaletteValueSet = styleSet->BuildFontPaletteValueSet();
2864 mFontPaletteValuesDirty = false;
2866 // Even if we're not reflowing anything, a change to the palette means we
2867 // need to repaint in order to show the new colors.
2868 InvalidatePaintedLayers();
2871 void nsPresContext::SetVisibleArea(const nsRect& r) {
2872 if (!r.IsEqualEdges(mVisibleArea)) {
2873 mVisibleArea = r;
2874 mSizeForViewportUnits = mVisibleArea.Size();
2875 if (IsRootContentDocumentCrossProcess()) {
2876 AdjustSizeForViewportUnits();
2878 // Visible area does not affect media queries when paginated.
2879 if (!IsRootPaginatedDocument()) {
2880 MediaFeatureValuesChanged(
2881 {mozilla::MediaFeatureChangeReason::ViewportChange},
2882 MediaFeatureChangePropagation::JustThisDocument);
2887 void nsPresContext::SetDynamicToolbarMaxHeight(ScreenIntCoord aHeight) {
2888 MOZ_ASSERT(IsRootContentDocumentCrossProcess());
2890 if (mDynamicToolbarMaxHeight == aHeight) {
2891 return;
2893 mDynamicToolbarMaxHeight = aHeight;
2894 mDynamicToolbarHeight = aHeight;
2896 AdjustSizeForViewportUnits();
2898 if (RefPtr<mozilla::PresShell> presShell = mPresShell) {
2899 // Changing the max height of the dynamic toolbar changes the ICB size, we
2900 // need to kick a reflow with the current window dimensions since the max
2901 // height change doesn't change the window dimensions but
2902 // PresShell::ResizeReflow ends up subtracting the new dynamic toolbar
2903 // height from the window dimensions and kick a reflow with the proper ICB
2904 // size.
2905 presShell->ForceResizeReflowWithCurrentDimensions();
2909 void nsPresContext::AdjustSizeForViewportUnits() {
2910 MOZ_ASSERT(IsRootContentDocumentCrossProcess());
2911 if (mVisibleArea.height == NS_UNCONSTRAINEDSIZE) {
2912 // Ignore `NS_UNCONSTRAINEDSIZE` since it's a temporary state during a
2913 // reflow. We will end up calling this function again with a proper size in
2914 // the same reflow.
2915 return;
2918 if (MOZ_UNLIKELY(mVisibleArea.height +
2919 NSIntPixelsToAppUnits(mDynamicToolbarMaxHeight,
2920 mCurAppUnitsPerDevPixel) >
2921 nscoord_MAX)) {
2922 MOZ_ASSERT_UNREACHABLE("The dynamic toolbar max height is probably wrong");
2923 return;
2926 mSizeForViewportUnits.height =
2927 mVisibleArea.height +
2928 NSIntPixelsToAppUnits(mDynamicToolbarMaxHeight, mCurAppUnitsPerDevPixel);
2931 void nsPresContext::UpdateDynamicToolbarOffset(ScreenIntCoord aOffset) {
2932 MOZ_ASSERT(IsRootContentDocumentCrossProcess());
2933 if (!mPresShell) {
2934 return;
2937 if (!HasDynamicToolbar()) {
2938 return;
2941 MOZ_ASSERT(-mDynamicToolbarMaxHeight <= aOffset && aOffset <= 0);
2942 if (mDynamicToolbarHeight == mDynamicToolbarMaxHeight + aOffset) {
2943 return;
2946 // Forcibly flush position:fixed elements in the case where the dynamic
2947 // toolbar is going to be completely hidden or starts to be visible so that
2948 // %-based style values will be recomputed with the visual viewport size which
2949 // is including the area covered by the dynamic toolbar.
2950 if (mDynamicToolbarHeight == 0 || aOffset == -mDynamicToolbarMaxHeight) {
2951 mPresShell->MarkFixedFramesForReflow(IntrinsicDirty::None);
2952 mPresShell->AddResizeEventFlushObserverIfNeeded();
2955 mDynamicToolbarHeight = mDynamicToolbarMaxHeight + aOffset;
2957 if (RefPtr<MobileViewportManager> mvm =
2958 mPresShell->GetMobileViewportManager()) {
2959 mvm->UpdateVisualViewportSizeByDynamicToolbar(-aOffset);
2962 mPresShell->StyleSet()->InvalidateForViewportUnits(
2963 ServoStyleSet::OnlyDynamic::Yes);
2966 DynamicToolbarState nsPresContext::GetDynamicToolbarState() const {
2967 if (!IsRootContentDocumentCrossProcess() || !HasDynamicToolbar()) {
2968 return DynamicToolbarState::None;
2971 if (mDynamicToolbarMaxHeight == mDynamicToolbarHeight) {
2972 return DynamicToolbarState::Expanded;
2973 } else if (mDynamicToolbarHeight == 0) {
2974 return DynamicToolbarState::Collapsed;
2976 return DynamicToolbarState::InTransition;
2979 void nsPresContext::SetSafeAreaInsets(const ScreenIntMargin& aSafeAreaInsets) {
2980 if (mSafeAreaInsets == aSafeAreaInsets) {
2981 return;
2983 mSafeAreaInsets = aSafeAreaInsets;
2985 PostRebuildAllStyleDataEvent(nsChangeHint(0),
2986 RestyleHint::RecascadeSubtree());
2989 #ifdef DEBUG
2991 void nsPresContext::ValidatePresShellAndDocumentReleation() const {
2992 NS_ASSERTION(!mPresShell || !mPresShell->GetDocument() ||
2993 mPresShell->GetDocument() == mDocument,
2994 "nsPresContext doesn't have the same document as nsPresShell!");
2997 #endif // #ifdef DEBUG
2999 nsRootPresContext::nsRootPresContext(dom::Document* aDocument,
3000 nsPresContextType aType)
3001 : nsPresContext(aDocument, aType) {}
3003 void nsRootPresContext::AddWillPaintObserver(nsIRunnable* aRunnable) {
3004 if (!mWillPaintFallbackEvent.IsPending()) {
3005 mWillPaintFallbackEvent = new RunWillPaintObservers(this);
3006 Document()->Dispatch(TaskCategory::Other,
3007 do_AddRef(mWillPaintFallbackEvent));
3009 mWillPaintObservers.AppendElement(aRunnable);
3013 * Run all runnables that need to get called before the next paint.
3015 void nsRootPresContext::FlushWillPaintObservers() {
3016 mWillPaintFallbackEvent = nullptr;
3017 nsTArray<nsCOMPtr<nsIRunnable>> observers = std::move(mWillPaintObservers);
3018 for (uint32_t i = 0; i < observers.Length(); ++i) {
3019 observers[i]->Run();
3023 size_t nsRootPresContext::SizeOfExcludingThis(
3024 MallocSizeOf aMallocSizeOf) const {
3025 return nsPresContext::SizeOfExcludingThis(aMallocSizeOf);
3027 // Measurement of the following members may be added later if DMD finds it is
3028 // worthwhile:
3029 // - mWillPaintObservers
3030 // - mWillPaintFallbackEvent