Bug 1861709 replace AudioCallbackDriver::ThreadRunning() assertions that mean to...
[gecko.git] / layout / base / nsPresContext.cpp
blob7b5da89a0b069558a20f66616cbb7bee5cd905c4
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/ContentBlockingAllowList.h"
81 #include "mozilla/GlobalStyleSheetCache.h"
82 #include "mozilla/ServoBindings.h"
83 #include "mozilla/StaticPrefs_bidi.h"
84 #include "mozilla/StaticPrefs_layout.h"
85 #include "mozilla/StaticPrefs_widget.h"
86 #include "mozilla/StaticPrefs_zoom.h"
87 #include "mozilla/StyleSheet.h"
88 #include "mozilla/StyleSheetInlines.h"
89 #include "mozilla/Telemetry.h"
90 #include "mozilla/TimelineManager.h"
91 #include "mozilla/dom/Performance.h"
92 #include "mozilla/dom/PerformanceTiming.h"
93 #include "mozilla/dom/PerformancePaintTiming.h"
94 #include "mozilla/layers/APZThreadUtils.h"
95 #include "MobileViewportManager.h"
96 #include "mozilla/dom/ImageTracker.h"
97 #ifdef ACCESSIBILITY
98 # include "mozilla/a11y/DocAccessible.h"
99 #endif
101 // Needed for Start/Stop of Image Animation
102 #include "imgIContainer.h"
103 #include "nsIImageLoadingContent.h"
105 #include "nsBidiUtils.h"
106 #include "nsServiceManagerUtils.h"
108 #include "mozilla/dom/URL.h"
109 #include "mozilla/ServoCSSParser.h"
111 using namespace mozilla;
112 using namespace mozilla::dom;
113 using namespace mozilla::gfx;
114 using namespace mozilla::layers;
117 * Layer UserData for ContainerLayers that want to be notified
118 * of local invalidations of them and their descendant layers.
119 * Pass a callback to ComputeDifferences to have these called.
121 class ContainerLayerPresContext : public LayerUserData {
122 public:
123 nsPresContext* mPresContext;
126 bool nsPresContext::IsDOMPaintEventPending() {
127 if (!mTransactions.IsEmpty()) {
128 return true;
131 nsRootPresContext* drpc = GetRootPresContext();
132 if (drpc && drpc->mRefreshDriver->ViewManagerFlushIsPending()) {
133 // Since we're promising that there will be a MozAfterPaint event
134 // fired, we record an empty invalidation in case display list
135 // invalidation doesn't invalidate anything further.
136 NotifyInvalidation(drpc->mRefreshDriver->LastTransactionId().Next(),
137 nsRect(0, 0, 0, 0));
138 return true;
140 return false;
143 struct WeakRunnableMethod : Runnable {
144 using Method = void (nsPresContext::*)();
146 WeakRunnableMethod(const char* aName, nsPresContext* aPc, Method aMethod)
147 : Runnable(aName), mPresContext(aPc), mMethod(aMethod) {}
149 NS_IMETHOD Run() override {
150 if (nsPresContext* pc = mPresContext.get()) {
151 (pc->*mMethod)();
153 return NS_OK;
156 private:
157 WeakPtr<nsPresContext> mPresContext;
158 Method mMethod;
161 // When forcing a font-info-update reflow from style, we don't need to reframe,
162 // but we'll need to restyle to pick up updated font metrics. In order to avoid
163 // synchronously having to deal with multiple restyles, we use an early refresh
164 // driver runner, which should prevent flashing for users.
166 // We might do a bit of extra work if the page flushes layout between the
167 // restyle and when this happens, which is a bit unfortunate, but not worse than
168 // what we used to do...
170 // A better solution would be to be able to synchronously initialize font
171 // information from style worker threads, perhaps...
172 void nsPresContext::ForceReflowForFontInfoUpdateFromStyle() {
173 if (mPendingFontInfoUpdateReflowFromStyle) {
174 return;
177 mPendingFontInfoUpdateReflowFromStyle = true;
178 nsCOMPtr<nsIRunnable> ev = new WeakRunnableMethod(
179 "nsPresContext::DoForceReflowForFontInfoUpdateFromStyle", this,
180 &nsPresContext::DoForceReflowForFontInfoUpdateFromStyle);
181 RefreshDriver()->AddEarlyRunner(ev);
184 void nsPresContext::DoForceReflowForFontInfoUpdateFromStyle() {
185 mPendingFontInfoUpdateReflowFromStyle = false;
186 ForceReflowForFontInfoUpdate(false);
189 void nsPresContext::ForceReflowForFontInfoUpdate(bool aNeedsReframe) {
190 // In the case of a static-clone document used for printing or print-preview,
191 // this is undesirable because the nsPrintJob is holding weak refs to frames
192 // that will get blown away unexpectedly by this reconstruction. So the
193 // prescontext for a print/preview doc ignores the font-list update.
195 // This means the print document may still be using cached fonts that are no
196 // longer present in the font list, but that should be safe given that all the
197 // required font instances have already been created, so it won't be depending
198 // on access to the font-list entries.
200 // XXX Actually, I think it's probably a bad idea to do *any* restyling of
201 // print documents in response to pref changes. We could be in the middle
202 // of printing the document, and reflowing all the frames might cause some
203 // kind of unwanted mid-document discontinuity.
204 if (IsPrintingOrPrintPreview()) {
205 return;
208 // If there's a user font set, discard any src:local() faces it may have
209 // loaded because their font entries may no longer be valid.
210 if (auto* fonts = Document()->GetFonts()) {
211 fonts->GetImpl()->ForgetLocalFaces();
214 FlushFontCache();
216 nsChangeHint changeHint =
217 aNeedsReframe ? nsChangeHint_ReconstructFrame : NS_STYLE_HINT_REFLOW;
219 // We also need to trigger restyling for ex/ch units changes to take effect,
220 // if needed.
221 auto restyleHint = StyleSet()->UsesFontMetrics()
222 ? RestyleHint::RecascadeSubtree()
223 : RestyleHint{0};
225 RebuildAllStyleData(changeHint, restyleHint);
228 static bool IsVisualCharset(NotNull<const Encoding*> aCharset) {
229 return aCharset == ISO_8859_8_ENCODING;
232 nsPresContext::nsPresContext(dom::Document* aDocument, nsPresContextType aType)
233 : mPresShell(nullptr),
234 mDocument(aDocument),
235 mMedium(aType == eContext_Galley ? nsGkAtoms::screen : nsGkAtoms::print),
236 mTextZoom(1.0),
237 mFullZoom(1.0),
238 mLastFontInflationScreenSize(gfxSize(-1.0, -1.0)),
239 mCurAppUnitsPerDevPixel(0),
240 mDynamicToolbarMaxHeight(0),
241 mDynamicToolbarHeight(0),
242 mPageSize(-1, -1),
243 mPageScale(0.0),
244 mPPScale(1.0f),
245 mViewportScrollOverrideElement(nullptr),
246 mElementsRestyled(0),
247 mFramesConstructed(0),
248 mFramesReflowed(0),
249 mAnimationTriggeredRestyles(0),
250 mInterruptChecksToSkip(0),
251 mNextFrameRateMultiplier(0),
252 mMeasuredTicksSinceLoading(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 mHadFirstContentfulPaint(false),
287 mHadNonTickContentfulPaint(false),
288 mHadContentfulPaintComposite(false),
289 mUserInputEventsAllowed(false),
290 #ifdef DEBUG
291 mInitialized(false),
292 #endif
293 mOverriddenOrEmbedderColorScheme(dom::PrefersColorSchemeOverride::None) {
294 #ifdef DEBUG
295 PodZero(&mLayoutPhaseCount);
296 #endif
298 if (!IsDynamic()) {
299 mImageAnimationMode = imgIContainer::kDontAnimMode;
300 mNeverAnimate = true;
301 } else {
302 mImageAnimationMode = imgIContainer::kNormalAnimMode;
303 mNeverAnimate = false;
305 NS_ASSERTION(mDocument, "Null document");
307 // if text perf logging enabled, init stats struct
308 if (MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_textperf), LogLevel::Warning)) {
309 mTextPerf = MakeUnique<gfxTextPerfMetrics>();
312 if (StaticPrefs::gfx_missing_fonts_notify()) {
313 mMissingFonts = MakeUnique<gfxMissingFontRecorder>();
316 if (StaticPrefs::layout_dynamic_toolbar_max_height() > 0) {
317 // The pref for dynamic toolbar max height is only used in reftests so it's
318 // fine to set here.
319 mDynamicToolbarMaxHeight = StaticPrefs::layout_dynamic_toolbar_max_height();
322 UpdateFontVisibility();
325 static const char* gExactCallbackPrefs[] = {
326 "browser.active_color",
327 "browser.anchor_color",
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 "layout.css.text-transform.uppercase-eszett.enabled",
336 "privacy.trackingprotection.enabled",
337 "ui.use_standins_for_native_colors",
338 nullptr,
341 static const char* gPrefixCallbackPrefs[] = {
342 "bidi.", "browser.display.", "browser.viewport.",
343 "font.", "gfx.font_rendering.", "layout.css.font-visibility.",
344 nullptr,
347 void nsPresContext::Destroy() {
348 if (mEventManager) {
349 // unclear if these are needed, but can't hurt
350 mEventManager->NotifyDestroyPresContext(this);
351 mEventManager->SetPresContext(nullptr);
352 mEventManager = nullptr;
355 if (mFontCache) {
356 mFontCache->Destroy();
357 mFontCache = nullptr;
360 // Unregister preference callbacks
361 Preferences::UnregisterPrefixCallbacks(nsPresContext::PreferenceChanged,
362 gPrefixCallbackPrefs, this);
363 Preferences::UnregisterCallbacks(nsPresContext::PreferenceChanged,
364 gExactCallbackPrefs, this);
366 mRefreshDriver = nullptr;
367 MOZ_ASSERT(mManagedPostRefreshObservers.IsEmpty());
370 nsPresContext::~nsPresContext() {
371 MOZ_ASSERT(!mPresShell, "Presshell forgot to clear our mPresShell pointer");
372 DetachPresShell();
374 Destroy();
377 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsPresContext)
378 NS_INTERFACE_MAP_ENTRY(nsISupports)
379 NS_INTERFACE_MAP_END
381 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsPresContext)
382 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(nsPresContext, LastRelease())
384 void nsPresContext::LastRelease() {
385 if (mMissingFonts) {
386 mMissingFonts->Clear();
390 NS_IMPL_CYCLE_COLLECTION_CLASS(nsPresContext)
392 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsPresContext)
393 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnimationEventDispatcher);
394 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument);
395 // NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mDeviceContext); // not xpcom
396 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEffectCompositor);
397 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEventManager);
398 // NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mLanguage); // an atom
400 // NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTheme); // a service
401 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrintSettings);
402 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
404 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsPresContext)
405 NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnimationEventDispatcher);
406 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument);
407 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDeviceContext); // worth bothering?
408 NS_IMPL_CYCLE_COLLECTION_UNLINK(mEffectCompositor);
409 // NS_RELEASE(tmp->mLanguage); // an atom
410 // NS_IMPL_CYCLE_COLLECTION_UNLINK(mTheme); // a service
411 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrintSettings);
412 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_PTR
414 tmp->Destroy();
415 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
417 bool nsPresContext::IsChrome() const {
418 return Document()->IsInChromeDocShell();
421 void nsPresContext::GetUserPreferences() {
422 if (!GetPresShell()) {
423 // No presshell means nothing to do here. We'll do this when we
424 // get a presshell.
425 return;
428 PreferenceSheet::EnsureInitialized();
430 mSendAfterPaintToContent = Preferences::GetBool(
431 "dom.send_after_paint_to_content", mSendAfterPaintToContent);
433 mPrefScrollbarSide = Preferences::GetInt("layout.scrollbar.side");
435 Document()->SetMayNeedFontPrefsUpdate();
437 // * image animation
438 nsAutoCString animatePref;
439 Preferences::GetCString("image.animation_mode", animatePref);
440 if (animatePref.EqualsLiteral("normal"))
441 mImageAnimationModePref = imgIContainer::kNormalAnimMode;
442 else if (animatePref.EqualsLiteral("none"))
443 mImageAnimationModePref = imgIContainer::kDontAnimMode;
444 else if (animatePref.EqualsLiteral("once"))
445 mImageAnimationModePref = imgIContainer::kLoopOnceAnimMode;
446 else // dynamic change to invalid value should act like it does initially
447 mImageAnimationModePref = imgIContainer::kNormalAnimMode;
449 uint32_t bidiOptions = GetBidi();
451 mPrefBidiDirection = StaticPrefs::bidi_direction();
452 SET_BIDI_OPTION_DIRECTION(bidiOptions, mPrefBidiDirection);
453 SET_BIDI_OPTION_TEXTTYPE(bidiOptions, StaticPrefs::bidi_texttype());
454 SET_BIDI_OPTION_NUMERAL(bidiOptions, StaticPrefs::bidi_numeral());
456 // We don't need to force reflow: either we are initializing a new
457 // prescontext or we are being called from PreferenceChanged()
458 // which triggers a reflow anyway.
459 SetBidi(bidiOptions);
462 void nsPresContext::InvalidatePaintedLayers() {
463 if (!mPresShell) {
464 return;
466 if (nsIFrame* rootFrame = mPresShell->GetRootFrame()) {
467 // FrameLayerBuilder caches invalidation-related values that depend on the
468 // appunits-per-dev-pixel ratio, so ensure that all PaintedLayer drawing
469 // is completely flushed.
470 rootFrame->InvalidateFrameSubtree();
474 void nsPresContext::AppUnitsPerDevPixelChanged() {
475 int32_t oldAppUnitsPerDevPixel = mCurAppUnitsPerDevPixel;
477 InvalidatePaintedLayers();
479 FlushFontCache();
481 MediaFeatureValuesChanged(
482 {RestyleHint::RecascadeSubtree(), NS_STYLE_HINT_REFLOW,
483 MediaFeatureChangeReason::ResolutionChange},
484 MediaFeatureChangePropagation::JustThisDocument);
486 mCurAppUnitsPerDevPixel = mDeviceContext->AppUnitsPerDevPixel();
488 #ifdef ACCESSIBILITY
489 if (mCurAppUnitsPerDevPixel != oldAppUnitsPerDevPixel) {
490 if (nsAccessibilityService* accService = GetAccService()) {
491 accService->NotifyOfDevPixelRatioChange(mPresShell,
492 mCurAppUnitsPerDevPixel);
495 #endif
497 // Recompute the size for vh units since it's changed by the dynamic toolbar
498 // max height which is stored in screen coord.
499 if (IsRootContentDocumentCrossProcess()) {
500 AdjustSizeForViewportUnits();
503 // nsSubDocumentFrame uses a AppUnitsPerDevPixel difference between parent and
504 // child document to determine if it needs to build a nsDisplayZoom item. So
505 // if we that changes then we need to invalidate the subdoc frame so that
506 // item gets created/removed.
507 if (mPresShell) {
508 if (nsIFrame* frame = mPresShell->GetRootFrame()) {
509 frame = nsLayoutUtils::GetCrossDocParentFrameInProcess(frame);
510 if (frame) {
511 int32_t parentAPD = frame->PresContext()->AppUnitsPerDevPixel();
512 if ((parentAPD == oldAppUnitsPerDevPixel) !=
513 (parentAPD == mCurAppUnitsPerDevPixel)) {
514 frame->InvalidateFrame();
520 // We would also have to look at all of our child subdocuments but the
521 // InvalidatePaintedLayers call above calls InvalidateFrameSubtree which
522 // would invalidate all subdocument frames already.
525 // static
526 void nsPresContext::PreferenceChanged(const char* aPrefName, void* aSelf) {
527 static_cast<nsPresContext*>(aSelf)->PreferenceChanged(aPrefName);
530 void nsPresContext::PreferenceChanged(const char* aPrefName) {
531 nsDependentCString prefName(aPrefName);
532 if (prefName.EqualsLiteral("layout.css.dpi") ||
533 prefName.EqualsLiteral("layout.css.devPixelsPerPx")) {
534 int32_t oldAppUnitsPerDevPixel = mDeviceContext->AppUnitsPerDevPixel();
535 // We need to assume the DPI changes, since `mDeviceContext` is shared with
536 // other documents, and we'd need to save the return value of the first call
537 // for all of them.
538 Unused << mDeviceContext->CheckDPIChange();
539 if (mPresShell) {
540 OwningNonNull<mozilla::PresShell> presShell(*mPresShell);
541 // Re-fetch the view manager's window dimensions in case there's a
542 // deferred resize which hasn't affected our mVisibleArea yet
543 nscoord oldWidthAppUnits, oldHeightAppUnits;
544 RefPtr<nsViewManager> vm = presShell->GetViewManager();
545 if (!vm) {
546 return;
548 vm->GetWindowDimensions(&oldWidthAppUnits, &oldHeightAppUnits);
549 float oldWidthDevPixels = oldWidthAppUnits / oldAppUnitsPerDevPixel;
550 float oldHeightDevPixels = oldHeightAppUnits / oldAppUnitsPerDevPixel;
552 UIResolutionChangedInternal();
554 nscoord width = NSToCoordRound(oldWidthDevPixels * AppUnitsPerDevPixel());
555 nscoord height =
556 NSToCoordRound(oldHeightDevPixels * AppUnitsPerDevPixel());
557 vm->SetWindowDimensions(width, height);
559 return;
562 if (StringBeginsWith(prefName, "browser.viewport."_ns) ||
563 StringBeginsWith(prefName, "font.size.inflation."_ns) ||
564 prefName.EqualsLiteral("dom.meta-viewport.enabled")) {
565 if (mPresShell) {
566 mPresShell->MaybeReflowForInflationScreenSizeChange();
570 auto changeHint = nsChangeHint{0};
571 auto restyleHint = RestyleHint{0};
572 // Changing any of these potentially changes the value of @media
573 // (prefers-contrast).
574 // The layout.css.prefers-contrast.enabled pref itself is not handled here,
575 // because that pref doesn't just affect the "live" value of the media query;
576 // it affects whether it is parsed at all.
577 if (prefName.EqualsLiteral("browser.display.document_color_use") ||
578 prefName.EqualsLiteral("browser.display.foreground_color") ||
579 prefName.EqualsLiteral("browser.display.background_color")) {
580 MediaFeatureValuesChanged({MediaFeatureChangeReason::PreferenceChange},
581 MediaFeatureChangePropagation::JustThisDocument);
583 if (prefName.EqualsLiteral(GFX_MISSING_FONTS_NOTIFY_PREF)) {
584 if (StaticPrefs::gfx_missing_fonts_notify()) {
585 if (!mMissingFonts) {
586 mMissingFonts = MakeUnique<gfxMissingFontRecorder>();
587 // trigger reflow to detect missing fonts on the current page
588 changeHint |= NS_STYLE_HINT_REFLOW;
590 } else {
591 if (mMissingFonts) {
592 mMissingFonts->Clear();
594 mMissingFonts = nullptr;
598 if (StringBeginsWith(prefName, "font."_ns) ||
599 // Changes to font family preferences don't change anything in the
600 // computed style data, so the style system won't generate a reflow hint
601 // for us. We need to do that manually.
602 prefName.EqualsLiteral("intl.accept_languages") ||
603 // Changes to bidi prefs need to trigger a reflow (see bug 443629)
604 StringBeginsWith(prefName, "bidi."_ns) ||
605 // Changes to font_rendering prefs need to trigger a reflow
606 StringBeginsWith(prefName, "gfx.font_rendering."_ns)) {
607 changeHint |= NS_STYLE_HINT_REFLOW;
608 if (StyleSet()->UsesFontMetrics()) {
609 restyleHint |= RestyleHint::RecascadeSubtree();
613 if (prefName.EqualsLiteral(
614 "layout.css.text-transform.uppercase-eszett.enabled")) {
615 changeHint |= NS_STYLE_HINT_REFLOW;
618 if (PreferenceSheet::AffectedByPref(prefName)) {
619 restyleHint |= RestyleHint::RestyleSubtree();
620 PreferenceSheet::Refresh();
623 // Same, this just frees a bunch of memory.
624 StaticPresData::Get()->InvalidateFontPrefs();
625 Document()->SetMayNeedFontPrefsUpdate();
627 // Initialize our state from the user preferences.
628 GetUserPreferences();
630 FlushFontCache();
631 if (UpdateFontVisibility()) {
632 changeHint |= NS_STYLE_HINT_REFLOW;
635 // Preferences require rerunning selector matching because we rebuild
636 // the pref style sheet for some preference changes.
637 if (changeHint || restyleHint) {
638 RebuildAllStyleData(changeHint, restyleHint);
641 InvalidatePaintedLayers();
644 nsresult nsPresContext::Init(nsDeviceContext* aDeviceContext) {
645 NS_ASSERTION(!mInitialized, "attempt to reinit pres context");
646 NS_ENSURE_ARG(aDeviceContext);
648 mDeviceContext = aDeviceContext;
650 // In certain rare cases (such as changing page mode), we tear down layout
651 // state and re-initialize a new prescontext for a document. Given that we
652 // hang style state off the DOM, we detect that re-initialization case and
653 // lazily drop the servo data. We don't do this eagerly during layout teardown
654 // because that would incur an extra whole-tree traversal that's unnecessary
655 // most of the time.
657 // FIXME(emilio): I'm pretty sure this doesn't happen after bug 1414999.
658 Element* root = mDocument->GetRootElement();
659 if (root && root->HasServoData()) {
660 RestyleManager::ClearServoDataFromSubtree(root);
663 if (mDeviceContext->SetFullZoom(mFullZoom)) {
664 FlushFontCache();
666 mCurAppUnitsPerDevPixel = mDeviceContext->AppUnitsPerDevPixel();
668 mEventManager = new mozilla::EventStateManager();
670 mAnimationEventDispatcher = new mozilla::AnimationEventDispatcher(this);
671 mEffectCompositor = new mozilla::EffectCompositor(this);
672 mTransitionManager = MakeUnique<nsTransitionManager>(this);
673 mAnimationManager = MakeUnique<nsAnimationManager>(this);
674 mTimelineManager = MakeUnique<mozilla::TimelineManager>(this);
676 if (mDocument->GetDisplayDocument()) {
677 NS_ASSERTION(mDocument->GetDisplayDocument()->GetPresContext(),
678 "Why are we being initialized?");
679 mRefreshDriver =
680 mDocument->GetDisplayDocument()->GetPresContext()->RefreshDriver();
681 } else {
682 dom::Document* parent = mDocument->GetInProcessParentDocument();
683 // Unfortunately, sometimes |parent| here has no presshell because
684 // printing screws up things. Assert that in other cases it does,
685 // but whenever the shell is null just fall back on using our own
686 // refresh driver.
687 NS_ASSERTION(
688 !parent || mDocument->IsStaticDocument() || parent->GetPresShell(),
689 "How did we end up with a presshell if our parent doesn't "
690 "have one?");
691 if (parent && parent->GetPresContext()) {
692 // XXX the document can change in AttachPresShell, does this work?
693 dom::BrowsingContext* browsingContext = mDocument->GetBrowsingContext();
694 if (browsingContext && !browsingContext->IsTop()) {
695 Element* containingElement = mDocument->GetEmbedderElement();
696 if (!containingElement->IsXULElement() ||
697 !containingElement->HasAttr(nsGkAtoms::forceOwnRefreshDriver)) {
698 mRefreshDriver = parent->GetPresContext()->RefreshDriver();
703 if (!mRefreshDriver) {
704 mRefreshDriver = new nsRefreshDriver(this);
708 // Register callbacks so we're notified when the preferences change
709 Preferences::RegisterPrefixCallbacks(nsPresContext::PreferenceChanged,
710 gPrefixCallbackPrefs, this);
711 Preferences::RegisterCallbacks(nsPresContext::PreferenceChanged,
712 gExactCallbackPrefs, this);
714 nsresult rv = mEventManager->Init();
715 NS_ENSURE_SUCCESS(rv, rv);
717 mEventManager->SetPresContext(this);
719 #if defined(MOZ_WIDGET_ANDROID)
720 if (IsRootContentDocumentCrossProcess() &&
721 MOZ_LIKELY(
722 !Preferences::HasUserValue("layout.dynamic-toolbar-max-height"))) {
723 if (BrowserChild* browserChild =
724 BrowserChild::GetFrom(mDocument->GetDocShell())) {
725 mDynamicToolbarMaxHeight = browserChild->GetDynamicToolbarMaxHeight();
726 mDynamicToolbarHeight = mDynamicToolbarMaxHeight;
729 #endif
731 #ifdef DEBUG
732 mInitialized = true;
733 #endif
735 return NS_OK;
738 bool nsPresContext::UpdateFontVisibility() {
739 FontVisibility oldValue = mFontVisibility;
742 * Expected behavior in order of precedence:
743 * 1 Chrome Rules give User Level (3)
744 * 2 RFP gives Highest Level (1 aka Base)
745 * 3 An RFPTarget of Base gives Base Level (1)
746 * 4 An RFPTarget of LangPack gives LangPack Level (2)
747 * 5 The value of the Standard Font Visibility Pref
749 * If the ETP toggle is disabled (aka
750 * ContentBlockingAllowList::Check is true), it will only override 3-5,
751 * not rules 1 or 2.
754 // Rule 1: Allow all font access for privileged contexts, including
755 // chrome and devtools contexts.
756 if (Document()->ChromeRulesEnabled()) {
757 mFontVisibility = FontVisibility::User;
758 return mFontVisibility != oldValue;
761 // Is this a private browsing context?
762 bool isPrivate = false;
763 if (nsCOMPtr<nsILoadContext> loadContext = mDocument->GetLoadContext()) {
764 isPrivate = loadContext->UsePrivateBrowsing();
767 int32_t level;
768 // Rule 3
769 if (mDocument->ShouldResistFingerprinting(
770 RFPTarget::FontVisibilityBaseSystem)) {
771 // Rule 2: Check RFP pref
772 // This is inside Rule 3 in case this document is exempted from RFP.
773 // But if it is not exempted, and RFP is enabled, we return immediately
774 // to prevent the override below from occurring.
775 if (nsRFPService::IsRFPPrefEnabled(isPrivate)) {
776 mFontVisibility = FontVisibility::Base;
777 return mFontVisibility != oldValue;
780 level = int32_t(FontVisibility::Base);
782 // Rule 4
783 else if (mDocument->ShouldResistFingerprinting(
784 RFPTarget::FontVisibilityLangPack)) {
785 level = int32_t(FontVisibility::LangPack);
787 // Rule 5
788 else {
789 level = StaticPrefs::layout_css_font_visibility();
792 // Override Rules 3-5 Only: Determine if the user has exempted the
793 // domain from tracking protections, if so, use the default value.
794 if (level != StaticPrefs::layout_css_font_visibility() &&
795 ContentBlockingAllowList::Check(mDocument->CookieJarSettings())) {
796 level = StaticPrefs::layout_css_font_visibility();
799 // Clamp result to the valid range of levels.
800 level = std::max(std::min(level, int32_t(FontVisibility::User)),
801 int32_t(FontVisibility::Base));
803 mFontVisibility = FontVisibility(level);
804 return mFontVisibility != oldValue;
807 void nsPresContext::ReportBlockedFontFamilyName(const nsCString& aFamily,
808 FontVisibility aVisibility) {
809 if (!mBlockedFonts.EnsureInserted(aFamily)) {
810 return;
812 nsAutoString msg;
813 msg.AppendPrintf(
814 "Request for font \"%s\" blocked at visibility level %d (requires %d)\n",
815 aFamily.get(), int(GetFontVisibility()), int(aVisibility));
816 nsContentUtils::ReportToConsoleNonLocalized(msg, nsIScriptError::warningFlag,
817 "Security"_ns, mDocument);
820 void nsPresContext::ReportBlockedFontFamily(const fontlist::Family& aFamily) {
821 auto* fontList = gfxPlatformFontList::PlatformFontList()->SharedFontList();
822 const nsCString& name = aFamily.DisplayName().AsString(fontList);
823 ReportBlockedFontFamilyName(name, aFamily.Visibility());
826 void nsPresContext::ReportBlockedFontFamily(const gfxFontFamily& aFamily) {
827 ReportBlockedFontFamilyName(aFamily.Name(), aFamily.Visibility());
830 void nsPresContext::InitFontCache() {
831 if (!mFontCache) {
832 mFontCache = new nsFontCache();
833 mFontCache->Init(this);
837 void nsPresContext::UpdateFontCacheUserFonts(gfxUserFontSet* aUserFontSet) {
838 if (mFontCache) {
839 mFontCache->UpdateUserFonts(aUserFontSet);
843 already_AddRefed<nsFontMetrics> nsPresContext::GetMetricsFor(
844 const nsFont& aFont, const nsFontMetrics::Params& aParams) {
845 InitFontCache();
846 return mFontCache->GetMetricsFor(aFont, aParams);
849 nsresult nsPresContext::FlushFontCache() {
850 if (mFontCache) {
851 mFontCache->Flush();
853 return NS_OK;
856 nsresult nsPresContext::FontMetricsDeleted(const nsFontMetrics* aFontMetrics) {
857 if (mFontCache) {
858 mFontCache->FontMetricsDeleted(aFontMetrics);
860 return NS_OK;
863 // Note: We don't hold a reference on the shell; it has a reference to
864 // us
865 void nsPresContext::AttachPresShell(mozilla::PresShell* aPresShell) {
866 MOZ_ASSERT(!mPresShell);
867 mPresShell = aPresShell;
869 mRestyleManager = MakeUnique<mozilla::RestyleManager>(this);
871 // Since CounterStyleManager is also the name of a method of
872 // nsPresContext, it is necessary to prefix the class with the mozilla
873 // namespace here.
874 mCounterStyleManager = new mozilla::CounterStyleManager(this);
876 dom::Document* doc = mPresShell->GetDocument();
877 MOZ_ASSERT(doc);
878 // Have to update PresContext's mDocument before calling any other methods.
879 mDocument = doc;
881 LookAndFeel::HandleGlobalThemeChange();
883 // Initialize our state from the user preferences, now that we
884 // have a presshell, and hence a document.
885 GetUserPreferences();
887 EnsureTheme();
889 nsIURI* docURI = doc->GetDocumentURI();
891 if (IsDynamic() && docURI) {
892 if (!docURI->SchemeIs("chrome") && !docURI->SchemeIs("resource"))
893 mImageAnimationMode = mImageAnimationModePref;
894 else
895 mImageAnimationMode = imgIContainer::kNormalAnimMode;
898 UpdateCharSet(doc->GetDocumentCharacterSet());
901 Maybe<ColorScheme> nsPresContext::GetOverriddenOrEmbedderColorScheme() const {
902 if (Medium() == nsGkAtoms::print) {
903 return Some(ColorScheme::Light);
906 switch (mOverriddenOrEmbedderColorScheme) {
907 case dom::PrefersColorSchemeOverride::Dark:
908 return Some(ColorScheme::Dark);
909 case dom::PrefersColorSchemeOverride::Light:
910 return Some(ColorScheme::Light);
911 case dom::PrefersColorSchemeOverride::None:
912 case dom::PrefersColorSchemeOverride::EndGuard_:
913 break;
916 return Nothing();
919 void nsPresContext::SetColorSchemeOverride(
920 PrefersColorSchemeOverride aOverride) {
921 auto oldScheme = mDocument->PreferredColorScheme();
923 mOverriddenOrEmbedderColorScheme = aOverride;
925 if (mDocument->PreferredColorScheme() != oldScheme) {
926 MediaFeatureValuesChanged(
927 MediaFeatureChange::ForPreferredColorSchemeChange(),
928 MediaFeatureChangePropagation::JustThisDocument);
932 void nsPresContext::RecomputeBrowsingContextDependentData() {
933 MOZ_ASSERT(mDocument);
934 dom::Document* doc = mDocument;
935 // Resource documents inherit all this state from their display document.
936 while (dom::Document* outer = doc->GetDisplayDocument()) {
937 doc = outer;
939 auto* browsingContext = doc->GetBrowsingContext();
940 if (!browsingContext) {
941 // This can legitimately happen for e.g. SVG images. Those just get scaled
942 // as a result of the zoom on the embedder document so it doesn't really
943 // matter... Medium also doesn't affect those.
944 return;
946 if (!IsPrintingOrPrintPreview()) {
947 auto systemZoom = LookAndFeel::SystemZoomSettings();
948 SetFullZoom(browsingContext->FullZoom() * systemZoom.mFullZoom);
949 SetTextZoom(browsingContext->TextZoom() * systemZoom.mTextZoom);
950 SetOverrideDPPX(browsingContext->OverrideDPPX());
953 auto* top = browsingContext->Top();
954 SetColorSchemeOverride([&] {
955 auto overriden = top->PrefersColorSchemeOverride();
956 if (overriden != PrefersColorSchemeOverride::None) {
957 return overriden;
959 if (!StaticPrefs::
960 layout_css_iframe_embedder_prefers_color_scheme_content_enabled()) {
961 return top->GetEmbedderColorSchemes().mPreferred;
963 return browsingContext->GetEmbedderColorSchemes().mPreferred;
964 }());
966 SetInRDMPane(top->GetInRDMPane());
968 if (doc == mDocument) {
969 // Medium doesn't apply to resource documents, etc.
970 RefPtr<nsAtom> mediumToEmulate;
971 if (MOZ_UNLIKELY(!top->GetMediumOverride().IsEmpty())) {
972 nsAutoString lower;
973 nsContentUtils::ASCIIToLower(top->GetMediumOverride(), lower);
974 mediumToEmulate = NS_Atomize(lower);
976 EmulateMedium(mediumToEmulate);
979 mDocument->EnumerateExternalResources([](dom::Document& aSubResource) {
980 if (nsPresContext* subResourcePc = aSubResource.GetPresContext()) {
981 subResourcePc->RecomputeBrowsingContextDependentData();
983 return CallState::Continue;
987 void nsPresContext::DetachPresShell() {
988 // The counter style manager's destructor needs to deallocate with the
989 // presshell arena. Disconnect it before nulling out the shell.
991 // XXXbholley: Given recent refactorings, it probably makes more sense to
992 // just null our mPresShell at the bottom of this function. I'm leaving it
993 // this way to preserve the old ordering, but I doubt anything would break.
994 if (mCounterStyleManager) {
995 mCounterStyleManager->Disconnect();
996 mCounterStyleManager = nullptr;
999 mPresShell = nullptr;
1001 CancelManagedPostRefreshObservers();
1003 if (mAnimationEventDispatcher) {
1004 mAnimationEventDispatcher->Disconnect();
1005 mAnimationEventDispatcher = nullptr;
1007 if (mEffectCompositor) {
1008 mEffectCompositor->Disconnect();
1009 mEffectCompositor = nullptr;
1011 if (mTransitionManager) {
1012 mTransitionManager->Disconnect();
1013 mTransitionManager = nullptr;
1015 if (mAnimationManager) {
1016 mAnimationManager->Disconnect();
1017 mAnimationManager = nullptr;
1019 if (mTimelineManager) {
1020 mTimelineManager->Disconnect();
1021 mTimelineManager = nullptr;
1023 if (mRestyleManager) {
1024 mRestyleManager->Disconnect();
1025 mRestyleManager = nullptr;
1027 if (mRefreshDriver && mRefreshDriver->GetPresContext() == this) {
1028 mRefreshDriver->Disconnect();
1029 // Can't null out the refresh driver here.
1033 struct QueryContainerState {
1034 nsSize mSize;
1035 WritingMode mWm;
1037 nscoord GetInlineSize() const { return LogicalSize(mWm, mSize).ISize(mWm); }
1039 bool Changed(const QueryContainerState& aNewState, StyleContainerType aType) {
1040 switch (aType) {
1041 case StyleContainerType::Normal:
1042 break;
1043 case StyleContainerType::Size:
1044 return mSize != aNewState.mSize;
1045 case StyleContainerType::InlineSize:
1046 return GetInlineSize() != aNewState.GetInlineSize();
1048 return false;
1051 NS_DECLARE_FRAME_PROPERTY_DELETABLE(ContainerState, QueryContainerState);
1053 void nsPresContext::RegisterContainerQueryFrame(nsIFrame* aFrame) {
1054 mContainerQueryFrames.Add(aFrame);
1057 void nsPresContext::UnregisterContainerQueryFrame(nsIFrame* aFrame) {
1058 mContainerQueryFrames.Remove(aFrame);
1061 void nsPresContext::FinishedContainerQueryUpdate() {
1062 mUpdatedContainerQueryContents.Clear();
1065 bool nsPresContext::UpdateContainerQueryStyles() {
1066 if (mContainerQueryFrames.IsEmpty()) {
1067 return false;
1070 AUTO_PROFILER_LABEL_RELEVANT_FOR_JS("Container Query Styles Update", LAYOUT);
1071 AUTO_PROFILER_MARKER_TEXT("UpdateContainerQueryStyles", LAYOUT, {}, ""_ns);
1073 PresShell()->DoFlushLayout(/* aInterruptible = */ false);
1075 AutoTArray<nsIFrame*, 8> framesToUpdate;
1077 bool anyChanged = false;
1078 for (nsIFrame* frame : mContainerQueryFrames.IterFromShallowest()) {
1079 MOZ_ASSERT(frame->IsPrimaryFrame());
1081 auto type = frame->StyleDisplay()->mContainerType;
1082 MOZ_ASSERT(type != StyleContainerType::Normal,
1083 "Non-container frames shouldn't be in this type");
1085 const QueryContainerState newState{frame->GetSize(),
1086 frame->GetWritingMode()};
1087 QueryContainerState* oldState = frame->GetProperty(ContainerState());
1089 const bool changed = !oldState || oldState->Changed(newState, type);
1091 // Make sure to update the state regardless. It's cheap and it keeps tracks
1092 // of both axes correctly even if only one axis is contained.
1093 if (oldState) {
1094 *oldState = newState;
1095 } else {
1096 frame->SetProperty(ContainerState(), new QueryContainerState(newState));
1099 if (!changed) {
1100 continue;
1103 const bool updatingAncestor = [&] {
1104 for (nsIFrame* f : framesToUpdate) {
1105 if (nsLayoutUtils::IsProperAncestorFrame(f, frame)) {
1106 return true;
1109 return false;
1110 }();
1112 if (updatingAncestor) {
1113 // We're going to update an ancestor container of this frame already,
1114 // avoid updating this one too until all our ancestor containers are
1115 // updated.
1116 continue;
1119 // To prevent unstable layout, only update once per-element per-flush.
1120 if (NS_WARN_IF(!mUpdatedContainerQueryContents.EnsureInserted(
1121 frame->GetContent()))) {
1122 continue;
1125 framesToUpdate.AppendElement(frame);
1127 // TODO(emilio): More fine-grained invalidation rather than invalidating the
1128 // whole subtree, probably!
1129 RestyleManager()->PostRestyleEvent(frame->GetContent()->AsElement(),
1130 RestyleHint::RestyleSubtree(),
1131 nsChangeHint(0));
1132 anyChanged = true;
1134 return anyChanged;
1137 void nsPresContext::DocumentCharSetChanged(NotNull<const Encoding*> aCharSet) {
1138 UpdateCharSet(aCharSet);
1139 FlushFontCache();
1141 // If a document contains one or more <script> elements, frame construction
1142 // might happen earlier than the UpdateCharSet(), so we need to restyle
1143 // descendants to make their style data up-to-date.
1145 // FIXME(emilio): Revisit whether this is true after bug 1438911.
1146 RebuildAllStyleData(NS_STYLE_HINT_REFLOW, RestyleHint::RecascadeSubtree());
1149 void nsPresContext::UpdateCharSet(NotNull<const Encoding*> aCharSet) {
1150 switch (GET_BIDI_OPTION_TEXTTYPE(GetBidi())) {
1151 case IBMBIDI_TEXTTYPE_LOGICAL:
1152 SetVisualMode(false);
1153 break;
1155 case IBMBIDI_TEXTTYPE_VISUAL:
1156 SetVisualMode(true);
1157 break;
1159 case IBMBIDI_TEXTTYPE_CHARSET:
1160 default:
1161 SetVisualMode(IsVisualCharset(aCharSet));
1165 nsPresContext* nsPresContext::GetParentPresContext() const {
1166 mozilla::PresShell* presShell = GetPresShell();
1167 if (presShell) {
1168 nsViewManager* viewManager = presShell->GetViewManager();
1169 if (viewManager) {
1170 nsView* view = viewManager->GetRootView();
1171 if (view) {
1172 view = view->GetParent(); // anonymous inner view
1173 if (view) {
1174 view = view->GetParent(); // subdocumentframe's view
1175 if (view) {
1176 nsIFrame* f = view->GetFrame();
1177 if (f) {
1178 return f->PresContext();
1185 return nullptr;
1188 nsPresContext* nsPresContext::GetInProcessRootContentDocumentPresContext() {
1189 if (IsChrome()) return nullptr;
1190 nsPresContext* pc = this;
1191 for (;;) {
1192 nsPresContext* parent = pc->GetParentPresContext();
1193 if (!parent || parent->IsChrome()) return pc;
1194 pc = parent;
1198 nsIWidget* nsPresContext::GetNearestWidget(nsPoint* aOffset) {
1199 NS_ENSURE_TRUE(mPresShell, nullptr);
1200 nsViewManager* vm = mPresShell->GetViewManager();
1201 NS_ENSURE_TRUE(vm, nullptr);
1202 nsView* rootView = vm->GetRootView();
1203 NS_ENSURE_TRUE(rootView, nullptr);
1204 return rootView->GetNearestWidget(aOffset);
1207 nsIWidget* nsPresContext::GetRootWidget() const {
1208 NS_ENSURE_TRUE(mPresShell, nullptr);
1209 nsViewManager* vm = mPresShell->GetViewManager();
1210 if (!vm) {
1211 return nullptr;
1213 return vm->GetRootWidget();
1216 // We may want to replace this with something faster, maybe caching the root
1217 // prescontext
1218 nsRootPresContext* nsPresContext::GetRootPresContext() const {
1219 nsPresContext* pc = const_cast<nsPresContext*>(this);
1220 for (;;) {
1221 nsPresContext* parent = pc->GetParentPresContext();
1222 if (!parent) break;
1223 pc = parent;
1225 return pc->IsRoot() ? static_cast<nsRootPresContext*>(pc) : nullptr;
1228 bool nsPresContext::UserInputEventsAllowed() {
1229 MOZ_ASSERT(IsRoot());
1230 if (mUserInputEventsAllowed) {
1231 return true;
1234 // Special document
1235 if (Document()->IsInitialDocument()) {
1236 return true;
1239 if (mRefreshDriver->IsThrottled()) {
1240 MOZ_ASSERT(!mPresShell->IsVisible());
1241 // This implies that the BC is not visibile and users can't
1242 // interact with it, so we are okay with handling user inputs here.
1243 return true;
1246 if (mMeasuredTicksSinceLoading <
1247 StaticPrefs::dom_input_events_security_minNumTicks()) {
1248 return false;
1251 if (!StaticPrefs::dom_input_events_security_minTimeElapsedInMS()) {
1252 return true;
1255 dom::Document* doc = Document();
1257 MOZ_ASSERT_IF(StaticPrefs::dom_input_events_security_minNumTicks(),
1258 doc->GetReadyStateEnum() >= Document::READYSTATE_LOADING);
1260 TimeStamp loadingOrRestoredFromBFCacheTime =
1261 doc->GetLoadingOrRestoredFromBFCacheTimeStamp();
1262 MOZ_ASSERT(!loadingOrRestoredFromBFCacheTime.IsNull());
1264 TimeDuration elapsed = TimeStamp::Now() - loadingOrRestoredFromBFCacheTime;
1265 if (elapsed.ToMilliseconds() >=
1266 StaticPrefs::dom_input_events_security_minTimeElapsedInMS()) {
1267 mUserInputEventsAllowed = true;
1268 return true;
1271 return false;
1274 void nsPresContext::MaybeIncreaseMeasuredTicksSinceLoading() {
1275 MOZ_ASSERT(IsRoot());
1276 if (mMeasuredTicksSinceLoading >=
1277 StaticPrefs::dom_input_events_security_minNumTicks()) {
1278 return;
1281 // We consider READYSTATE_LOADING is the point when the page
1282 // becomes interactive
1283 if (Document()->GetReadyStateEnum() >= Document::READYSTATE_LOADING ||
1284 Document()->IsInitialDocument()) {
1285 ++mMeasuredTicksSinceLoading;
1288 if (mMeasuredTicksSinceLoading <
1289 StaticPrefs::dom_input_events_security_minNumTicks()) {
1290 // Here we are forcing refresh driver to run because we can't always
1291 // guarantee refresh driver will run enough times to meet the minNumTicks
1292 // requirement. i.e. about:blank.
1293 if (!RefreshDriver()->HasPendingTick()) {
1294 RefreshDriver()->InitializeTimer();
1299 bool nsPresContext::NeedsMoreTicksForUserInput() const {
1300 MOZ_ASSERT(IsRoot());
1301 return mMeasuredTicksSinceLoading <
1302 StaticPrefs::dom_input_events_security_minNumTicks();
1305 // Helper function for setting Anim Mode on image
1306 static void SetImgAnimModeOnImgReq(imgIRequest* aImgReq, uint16_t aMode) {
1307 if (aImgReq) {
1308 nsCOMPtr<imgIContainer> imgCon;
1309 aImgReq->GetImage(getter_AddRefs(imgCon));
1310 if (imgCon) {
1311 imgCon->SetAnimationMode(aMode);
1316 // IMPORTANT: Assumption is that all images for a Presentation
1317 // have the same Animation Mode (pavlov said this was OK)
1319 // Walks content and set the animation mode
1320 // this is a way to turn on/off image animations
1321 void nsPresContext::SetImgAnimations(nsIContent* aParent, uint16_t aMode) {
1322 nsCOMPtr<nsIImageLoadingContent> imgContent(do_QueryInterface(aParent));
1323 if (imgContent) {
1324 nsCOMPtr<imgIRequest> imgReq;
1325 imgContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
1326 getter_AddRefs(imgReq));
1327 SetImgAnimModeOnImgReq(imgReq, aMode);
1330 for (nsIContent* childContent = aParent->GetFirstChild(); childContent;
1331 childContent = childContent->GetNextSibling()) {
1332 SetImgAnimations(childContent, aMode);
1336 void nsPresContext::SetSMILAnimations(dom::Document* aDoc, uint16_t aNewMode,
1337 uint16_t aOldMode) {
1338 if (aDoc->HasAnimationController()) {
1339 SMILAnimationController* controller = aDoc->GetAnimationController();
1340 switch (aNewMode) {
1341 case imgIContainer::kNormalAnimMode:
1342 case imgIContainer::kLoopOnceAnimMode:
1343 if (aOldMode == imgIContainer::kDontAnimMode)
1344 controller->Resume(SMILTimeContainer::PAUSE_USERPREF);
1345 break;
1347 case imgIContainer::kDontAnimMode:
1348 if (aOldMode != imgIContainer::kDontAnimMode)
1349 controller->Pause(SMILTimeContainer::PAUSE_USERPREF);
1350 break;
1355 void nsPresContext::SetImageAnimationMode(uint16_t aMode) {
1356 NS_ASSERTION(aMode == imgIContainer::kNormalAnimMode ||
1357 aMode == imgIContainer::kDontAnimMode ||
1358 aMode == imgIContainer::kLoopOnceAnimMode,
1359 "Wrong Animation Mode is being set!");
1361 // Image animation mode cannot be changed when rendering to a printer.
1362 if (!IsDynamic()) return;
1364 // Now walk the content tree and set the animation mode
1365 // on all the images.
1366 if (mPresShell) {
1367 dom::Document* doc = mPresShell->GetDocument();
1368 if (doc) {
1369 doc->StyleImageLoader()->SetAnimationMode(aMode);
1371 Element* rootElement = doc->GetRootElement();
1372 if (rootElement) {
1373 SetImgAnimations(rootElement, aMode);
1375 SetSMILAnimations(doc, aMode, mImageAnimationMode);
1379 mImageAnimationMode = aMode;
1382 void nsPresContext::SetTextZoom(float aTextZoom) {
1383 float newZoom = aTextZoom;
1384 float minZoom = StaticPrefs::zoom_minPercent() / 100.0f;
1385 float maxZoom = StaticPrefs::zoom_maxPercent() / 100.0f;
1387 if (newZoom < minZoom) {
1388 newZoom = minZoom;
1389 } else if (newZoom > maxZoom) {
1390 newZoom = maxZoom;
1393 if (newZoom == mTextZoom) {
1394 return;
1397 mTextZoom = newZoom;
1399 // Media queries could have changed, since we changed the meaning
1400 // of 'em' units in them.
1401 MediaFeatureValuesChanged(
1402 {RestyleHint::RecascadeSubtree(), NS_STYLE_HINT_REFLOW,
1403 MediaFeatureChangeReason::ZoomChange},
1404 MediaFeatureChangePropagation::JustThisDocument);
1407 void nsPresContext::SetInRDMPane(bool aInRDMPane) {
1408 if (mInRDMPane == aInRDMPane) {
1409 return;
1411 mInRDMPane = aInRDMPane;
1412 RecomputeTheme();
1415 float nsPresContext::GetDeviceFullZoom() {
1416 return mDeviceContext->GetFullZoom();
1419 void nsPresContext::SetFullZoom(float aZoom) {
1420 if (!mPresShell || mFullZoom == aZoom) {
1421 return;
1424 // Re-fetch the view manager's window dimensions in case there's a deferred
1425 // resize which hasn't affected our mVisibleArea yet
1426 nscoord oldWidthAppUnits, oldHeightAppUnits;
1427 mPresShell->GetViewManager()->GetWindowDimensions(&oldWidthAppUnits,
1428 &oldHeightAppUnits);
1429 float oldWidthDevPixels = oldWidthAppUnits / float(mCurAppUnitsPerDevPixel);
1430 float oldHeightDevPixels = oldHeightAppUnits / float(mCurAppUnitsPerDevPixel);
1431 mDeviceContext->SetFullZoom(aZoom);
1433 mFullZoom = aZoom;
1435 AppUnitsPerDevPixelChanged();
1437 mPresShell->GetViewManager()->SetWindowDimensions(
1438 NSToCoordRound(oldWidthDevPixels * AppUnitsPerDevPixel()),
1439 NSToCoordRound(oldHeightDevPixels * AppUnitsPerDevPixel()));
1442 void nsPresContext::SetOverrideDPPX(float aDPPX) {
1443 // SetOverrideDPPX is called during navigations, including history
1444 // traversals. In that case, it's typically called with our current value,
1445 // and we don't need to actually do anything.
1446 if (aDPPX == GetOverrideDPPX()) {
1447 return;
1450 mMediaEmulationData.mDPPX = aDPPX;
1451 MediaFeatureValuesChanged({MediaFeatureChangeReason::ResolutionChange},
1452 MediaFeatureChangePropagation::JustThisDocument);
1455 gfxSize nsPresContext::ScreenSizeInchesForFontInflation(bool* aChanged) {
1456 if (aChanged) {
1457 *aChanged = false;
1460 nsDeviceContext* dx = DeviceContext();
1461 nsRect clientRect;
1462 dx->GetClientRect(clientRect); // FIXME: GetClientRect looks expensive
1463 float unitsPerInch = dx->AppUnitsPerPhysicalInch();
1464 gfxSize deviceSizeInches(float(clientRect.width) / unitsPerInch,
1465 float(clientRect.height) / unitsPerInch);
1467 if (mLastFontInflationScreenSize == gfxSize(-1.0, -1.0)) {
1468 mLastFontInflationScreenSize = deviceSizeInches;
1471 if (deviceSizeInches != mLastFontInflationScreenSize && aChanged) {
1472 *aChanged = true;
1473 mLastFontInflationScreenSize = deviceSizeInches;
1476 return deviceSizeInches;
1479 static bool CheckOverflow(const ComputedStyle* aComputedStyle,
1480 ScrollStyles* aStyles) {
1481 // If they're not styled yet, we'll get around to it when constructing frames
1482 // for the element.
1483 if (!aComputedStyle) {
1484 return false;
1486 const nsStyleDisplay* display = aComputedStyle->StyleDisplay();
1488 // If they will generate no box, just don't.
1489 if (display->mDisplay == StyleDisplay::None ||
1490 display->mDisplay == StyleDisplay::Contents) {
1491 return false;
1494 // NOTE(emilio): This check needs to match the one in
1495 // Document::IsPotentiallyScrollable.
1496 if (display->OverflowIsVisibleInBothAxis()) {
1497 return false;
1500 *aStyles =
1501 ScrollStyles(*display, ScrollStyles::MapOverflowToValidScrollStyle);
1502 return true;
1505 // https://drafts.csswg.org/css-overflow/#overflow-propagation
1507 // NOTE(emilio): We may need to use out-of-date styles for this, since this is
1508 // called from nsCSSFrameConstructor::ContentRemoved. We could refactor this a
1509 // bit to avoid doing that, and also fix correctness issues (we don't invalidate
1510 // properly when we insert a body element and there is a previous one, for
1511 // example).
1512 static Element* GetPropagatedScrollStylesForViewport(
1513 nsPresContext* aPresContext, ScrollStyles* aStyles) {
1514 Document* document = aPresContext->Document();
1515 Element* docElement = document->GetRootElement();
1516 // docElement might be null if we're doing this after removing it.
1517 if (!docElement) {
1518 return nullptr;
1521 // Check the style on the document root element
1522 const auto* rootStyle = Servo_Element_GetMaybeOutOfDateStyle(docElement);
1523 if (CheckOverflow(rootStyle, aStyles)) {
1524 // tell caller we stole the overflow style from the root element
1525 return docElement;
1528 if (rootStyle && rootStyle->StyleDisplay()->IsContainAny()) {
1529 return nullptr;
1532 // Don't look in the BODY for non-HTML documents or HTML documents
1533 // with non-HTML roots.
1534 // XXX this should be earlier; we shouldn't even look at the document root
1535 // for non-HTML documents. Fix this once we support explicit CSS styling
1536 // of the viewport
1537 if (!document->IsHTMLOrXHTML() || !docElement->IsHTMLElement()) {
1538 return nullptr;
1541 Element* bodyElement = document->AsHTMLDocument()->GetBodyElement();
1542 if (!bodyElement) {
1543 return nullptr;
1546 MOZ_ASSERT(bodyElement->IsHTMLElement(nsGkAtoms::body),
1547 "GetBodyElement returned something bogus");
1549 const auto* bodyStyle = Servo_Element_GetMaybeOutOfDateStyle(bodyElement);
1550 if (bodyStyle && bodyStyle->StyleDisplay()->IsContainAny()) {
1551 return nullptr;
1554 if (CheckOverflow(bodyStyle, aStyles)) {
1555 // tell caller we stole the overflow style from the body element
1556 return bodyElement;
1559 return nullptr;
1562 Element* nsPresContext::UpdateViewportScrollStylesOverride() {
1563 ScrollStyles oldViewportScrollStyles = mViewportScrollStyles;
1565 // Start off with our default styles, and then update them as needed.
1566 mViewportScrollStyles =
1567 ScrollStyles(StyleOverflow::Auto, StyleOverflow::Auto);
1568 mViewportScrollOverrideElement = nullptr;
1569 // Don't propagate the scrollbar state in printing or print preview.
1570 if (!IsPaginated()) {
1571 mViewportScrollOverrideElement =
1572 GetPropagatedScrollStylesForViewport(this, &mViewportScrollStyles);
1575 dom::Document* document = Document();
1576 if (Element* fsElement = document->GetUnretargetedFullscreenElement()) {
1577 // If the document is in fullscreen, but the fullscreen element is
1578 // not the root element, we should explicitly suppress the scrollbar
1579 // here. Note that, we still need to return the original element
1580 // the styles are from, so that the state of those elements is not
1581 // affected across fullscreen change.
1582 if (fsElement != document->GetRootElement() &&
1583 fsElement != mViewportScrollOverrideElement) {
1584 mViewportScrollStyles =
1585 ScrollStyles(StyleOverflow::Hidden, StyleOverflow::Hidden);
1589 if (mViewportScrollStyles != oldViewportScrollStyles) {
1590 if (mPresShell) {
1591 if (nsIFrame* frame = mPresShell->GetRootFrame()) {
1592 frame->SchedulePaint();
1597 return mViewportScrollOverrideElement;
1600 bool nsPresContext::ElementWouldPropagateScrollStyles(const Element& aElement) {
1601 if (aElement.GetParent() && !aElement.IsHTMLElement(nsGkAtoms::body)) {
1602 // We certainly won't be propagating from this element.
1603 return false;
1606 // Go ahead and just call GetPropagatedScrollStylesForViewport, but update
1607 // a dummy ScrollStyles we don't care about. It'll do a bit of extra work,
1608 // but saves us having to have more complicated code or more code duplication;
1609 // in practice we will make this call quite rarely, because we checked for all
1610 // the common cases above.
1611 ScrollStyles dummy(StyleOverflow::Auto, StyleOverflow::Auto);
1612 return GetPropagatedScrollStylesForViewport(this, &dummy) == &aElement;
1615 nsISupports* nsPresContext::GetContainerWeak() const {
1616 return mDocument->GetDocShell();
1619 ColorScheme nsPresContext::DefaultBackgroundColorScheme() const {
1620 dom::Document* doc = Document();
1621 // Use a dark background for top-level about:blank that is inaccessible to
1622 // content JS.
1623 if (doc->IsLikelyContentInaccessibleTopLevelAboutBlank()) {
1624 return doc->PreferredColorScheme(Document::IgnoreRFP::Yes);
1626 // Prefer the root color-scheme (since generally the default canvas
1627 // background comes from the root element's background-color), and fall back
1628 // to the default color-scheme if not available.
1629 if (auto* frame = FrameConstructor()->GetRootElementStyleFrame()) {
1630 return LookAndFeel::ColorSchemeForFrame(frame);
1632 return doc->DefaultColorScheme();
1635 nscolor nsPresContext::DefaultBackgroundColor() const {
1636 if (!GetBackgroundColorDraw()) {
1637 return NS_RGB(255, 255, 255);
1639 return PrefSheetPrefs()
1640 .ColorsFor(DefaultBackgroundColorScheme())
1641 .mDefaultBackground;
1644 nsDocShell* nsPresContext::GetDocShell() const {
1645 return nsDocShell::Cast(mDocument->GetDocShell());
1648 bool nsPresContext::BidiEnabled() const { return Document()->GetBidiEnabled(); }
1650 void nsPresContext::SetBidiEnabled() const { Document()->SetBidiEnabled(); }
1652 void nsPresContext::SetBidi(uint32_t aSource) {
1653 // Don't do all this stuff unless the options have changed.
1654 if (aSource == GetBidi()) {
1655 return;
1658 Document()->SetBidiOptions(aSource);
1659 if (IBMBIDI_TEXTDIRECTION_RTL == GET_BIDI_OPTION_DIRECTION(aSource) ||
1660 IBMBIDI_NUMERAL_HINDI == GET_BIDI_OPTION_NUMERAL(aSource)) {
1661 SetBidiEnabled();
1663 if (IBMBIDI_TEXTTYPE_VISUAL == GET_BIDI_OPTION_TEXTTYPE(aSource)) {
1664 SetVisualMode(true);
1665 } else if (IBMBIDI_TEXTTYPE_LOGICAL == GET_BIDI_OPTION_TEXTTYPE(aSource)) {
1666 SetVisualMode(false);
1667 } else {
1668 SetVisualMode(IsVisualCharset(Document()->GetDocumentCharacterSet()));
1672 uint32_t nsPresContext::GetBidi() const { return Document()->GetBidiOptions(); }
1674 void nsPresContext::RecordInteractionTime(InteractionType aType,
1675 const TimeStamp& aTimeStamp) {
1676 if (!mInteractionTimeEnabled || aTimeStamp.IsNull()) {
1677 return;
1680 // Array of references to the member variable of each time stamp
1681 // for the different interaction types, keyed by InteractionType.
1682 TimeStamp nsPresContext::*interactionTimes[] = {
1683 &nsPresContext::mFirstClickTime, &nsPresContext::mFirstKeyTime,
1684 &nsPresContext::mFirstMouseMoveTime, &nsPresContext::mFirstScrollTime};
1686 // Array of histogram IDs for the different interaction types,
1687 // keyed by InteractionType.
1688 Telemetry::HistogramID histogramIds[] = {
1689 Telemetry::TIME_TO_FIRST_CLICK_MS, Telemetry::TIME_TO_FIRST_KEY_INPUT_MS,
1690 Telemetry::TIME_TO_FIRST_MOUSE_MOVE_MS,
1691 Telemetry::TIME_TO_FIRST_SCROLL_MS};
1693 TimeStamp& interactionTime =
1694 this->*(interactionTimes[static_cast<uint32_t>(aType)]);
1695 if (!interactionTime.IsNull()) {
1696 // We have already recorded an interaction time.
1697 return;
1700 // Record the interaction time if it occurs after the first paint
1701 // of the top level content document.
1702 nsPresContext* inProcessRootPresContext =
1703 GetInProcessRootContentDocumentPresContext();
1705 if (!inProcessRootPresContext ||
1706 !inProcessRootPresContext->IsRootContentDocumentCrossProcess()) {
1707 // There is no top content pres context, or we are in a cross process
1708 // document so we don't care about the interaction time. Record a value
1709 // anyways to avoid trying to find the top content pres context in future
1710 // interactions.
1711 interactionTime = TimeStamp::Now();
1712 return;
1715 if (inProcessRootPresContext->mFirstNonBlankPaintTime.IsNull() ||
1716 inProcessRootPresContext->mFirstNonBlankPaintTime > aTimeStamp) {
1717 // Top content pres context has not had a non-blank paint yet
1718 // or the event timestamp is before the first non-blank paint,
1719 // so don't record interaction time.
1720 return;
1723 // Check if we are recording the first of any of the interaction types.
1724 bool isFirstInteraction = true;
1725 for (TimeStamp nsPresContext::*memberPtr : interactionTimes) {
1726 TimeStamp& timeStamp = this->*(memberPtr);
1727 if (!timeStamp.IsNull()) {
1728 isFirstInteraction = false;
1729 break;
1733 interactionTime = TimeStamp::Now();
1734 // Only the top level content pres context reports first interaction
1735 // time to telemetry (if it hasn't already done so).
1736 if (this == inProcessRootPresContext) {
1737 if (Telemetry::CanRecordExtended()) {
1738 double millis =
1739 (interactionTime - mFirstNonBlankPaintTime).ToMilliseconds();
1740 Telemetry::Accumulate(histogramIds[static_cast<uint32_t>(aType)], millis);
1742 if (isFirstInteraction) {
1743 Telemetry::Accumulate(Telemetry::TIME_TO_FIRST_INTERACTION_MS, millis);
1746 } else {
1747 inProcessRootPresContext->RecordInteractionTime(aType, aTimeStamp);
1751 nsITheme* nsPresContext::Theme() const {
1752 MOZ_ASSERT(mTheme);
1753 return mTheme;
1756 void nsPresContext::EnsureTheme() {
1757 MOZ_ASSERT(!mTheme);
1758 if (Document()->ShouldAvoidNativeTheme()) {
1759 if (mInRDMPane) {
1760 mTheme = do_GetRDMThemeDoNotUseDirectly();
1761 } else {
1762 mTheme = do_GetBasicNativeThemeDoNotUseDirectly();
1764 } else {
1765 mTheme = do_GetNativeThemeDoNotUseDirectly();
1767 MOZ_RELEASE_ASSERT(mTheme);
1770 void nsPresContext::RecomputeTheme() {
1771 if (!mTheme) {
1772 return;
1774 nsCOMPtr<nsITheme> oldTheme = std::move(mTheme);
1775 EnsureTheme();
1776 if (oldTheme == mTheme) {
1777 return;
1779 // Theme affects layout information (as it affects whether we create
1780 // scrollbar buttons for example) and also style (affects the
1781 // scrollbar-inline-size env var).
1782 RebuildAllStyleData(nsChangeHint_ReconstructFrame,
1783 RestyleHint::RecascadeSubtree());
1784 // This is a bit of a lie, but this affects the overlay-scrollbars
1785 // media query and it's the code-path that gets taken for regular system
1786 // metrics changes via ThemeChanged().
1787 MediaFeatureValuesChanged({MediaFeatureChangeReason::SystemMetricsChange},
1788 MediaFeatureChangePropagation::JustThisDocument);
1791 bool nsPresContext::UseOverlayScrollbars() const {
1792 return LookAndFeel::GetInt(LookAndFeel::IntID::UseOverlayScrollbars) ||
1793 mInRDMPane;
1796 void nsPresContext::ThemeChanged(widget::ThemeChangeKind aKind) {
1797 PROFILER_MARKER_UNTYPED("ThemeChanged", LAYOUT, MarkerStack::Capture());
1799 mPendingThemeChangeKind |= unsigned(aKind);
1801 if (!mPendingThemeChanged) {
1802 nsCOMPtr<nsIRunnable> ev =
1803 new WeakRunnableMethod("nsPresContext::ThemeChangedInternal", this,
1804 &nsPresContext::ThemeChangedInternal);
1805 RefreshDriver()->AddEarlyRunner(ev);
1806 mPendingThemeChanged = true;
1808 MOZ_ASSERT(LookAndFeel::HasPendingGlobalThemeChange());
1811 void nsPresContext::ThemeChangedInternal() {
1812 MOZ_ASSERT(mPendingThemeChanged);
1814 mPendingThemeChanged = false;
1816 const auto kind = widget::ThemeChangeKind(mPendingThemeChangeKind);
1817 mPendingThemeChangeKind = 0;
1819 LookAndFeel::HandleGlobalThemeChange();
1821 // Full zoom might have changed as a result of the text scale factor.
1822 RecomputeBrowsingContextDependentData();
1824 // Changes to system metrics and other look and feel values can change media
1825 // queries on them.
1827 // Changes in theme can change system colors (whose changes are properly
1828 // reflected in computed style data), system fonts (whose changes are
1829 // some reflected (like sizes and such) and some not), and -moz-appearance
1830 // (whose changes are not), so we need to recascade for the first, and reflow
1831 // for the rest.
1832 auto restyleHint = (kind & widget::ThemeChangeKind::Style)
1833 ? RestyleHint::RecascadeSubtree()
1834 : RestyleHint{0};
1835 auto changeHint = (kind & widget::ThemeChangeKind::Layout)
1836 ? NS_STYLE_HINT_REFLOW
1837 : nsChangeHint(0);
1838 MediaFeatureValuesChanged(
1839 {restyleHint, changeHint, MediaFeatureChangeReason::SystemMetricsChange},
1840 MediaFeatureChangePropagation::All);
1842 if (Document()->IsInChromeDocShell()) {
1843 if (RefPtr<nsPIDOMWindowInner> win = Document()->GetInnerWindow()) {
1844 nsContentUtils::DispatchEventOnlyToChrome(
1845 Document(), nsGlobalWindowInner::Cast(win), u"nativethemechange"_ns,
1846 CanBubble::eYes, Cancelable::eYes, nullptr);
1851 void nsPresContext::UIResolutionChanged() {
1852 if (!mPendingUIResolutionChanged) {
1853 nsCOMPtr<nsIRunnable> ev =
1854 NewRunnableMethod("nsPresContext::UIResolutionChangedInternal", this,
1855 &nsPresContext::UIResolutionChangedInternal);
1856 nsresult rv = Document()->Dispatch(ev.forget());
1857 if (NS_SUCCEEDED(rv)) {
1858 mPendingUIResolutionChanged = true;
1863 void nsPresContext::UIResolutionChangedSync() {
1864 if (!mPendingUIResolutionChanged) {
1865 mPendingUIResolutionChanged = true;
1866 UIResolutionChangedInternal();
1870 static void NotifyTabUIResolutionChanged(nsIRemoteTab* aTab, void* aArg) {
1871 aTab->NotifyResolutionChanged();
1874 static void NotifyChildrenUIResolutionChanged(nsPIDOMWindowOuter* aWindow) {
1875 nsCOMPtr<Document> doc = aWindow->GetExtantDoc();
1876 RefPtr<nsPIWindowRoot> topLevelWin = nsContentUtils::GetWindowRoot(doc);
1877 if (!topLevelWin) {
1878 return;
1880 topLevelWin->EnumerateBrowsers(NotifyTabUIResolutionChanged, nullptr);
1883 void nsPresContext::UIResolutionChangedInternal() {
1884 mPendingUIResolutionChanged = false;
1886 mDeviceContext->CheckDPIChange();
1887 if (mCurAppUnitsPerDevPixel != mDeviceContext->AppUnitsPerDevPixel()) {
1888 AppUnitsPerDevPixelChanged();
1891 if (mPresShell) {
1892 mPresShell->RefreshZoomConstraintsForScreenSizeChange();
1893 if (RefPtr<MobileViewportManager> mvm =
1894 mPresShell->GetMobileViewportManager()) {
1895 mvm->UpdateSizesBeforeReflow();
1899 // Recursively notify all remote leaf descendants of the change.
1900 if (nsPIDOMWindowOuter* window = mDocument->GetWindow()) {
1901 NotifyChildrenUIResolutionChanged(window);
1904 auto recurse = [](dom::Document& aSubDoc) {
1905 if (nsPresContext* pc = aSubDoc.GetPresContext()) {
1906 pc->UIResolutionChangedInternal();
1908 return CallState::Continue;
1910 mDocument->EnumerateSubDocuments(recurse);
1913 void nsPresContext::EmulateMedium(nsAtom* aMediaType) {
1914 MOZ_ASSERT(!aMediaType || aMediaType->IsAsciiLowercase());
1916 RefPtr<const nsAtom> oldMedium = Medium();
1917 auto oldScheme = mDocument->PreferredColorScheme();
1919 mMediaEmulationData.mMedium = aMediaType;
1921 if (Medium() == oldMedium) {
1922 return;
1925 MediaFeatureChange change(MediaFeatureChangeReason::MediumChange);
1926 if (oldScheme != mDocument->PreferredColorScheme()) {
1927 change |= MediaFeatureChange::ForPreferredColorSchemeChange();
1929 MediaFeatureValuesChanged(change,
1930 MediaFeatureChangePropagation::JustThisDocument);
1933 void nsPresContext::ContentLanguageChanged() {
1934 PostRebuildAllStyleDataEvent(nsChangeHint(0),
1935 RestyleHint::RecascadeSubtree());
1938 void nsPresContext::RegisterManagedPostRefreshObserver(
1939 ManagedPostRefreshObserver* aObserver) {
1940 if (MOZ_UNLIKELY(!mPresShell)) {
1941 // If we're detached from our pres shell already, refuse to keep observer
1942 // around, as that'd create a cycle.
1943 RefPtr<ManagedPostRefreshObserver> obs = aObserver;
1944 obs->Cancel();
1945 return;
1948 RefreshDriver()->AddPostRefreshObserver(
1949 static_cast<nsAPostRefreshObserver*>(aObserver));
1950 mManagedPostRefreshObservers.AppendElement(aObserver);
1953 void nsPresContext::UnregisterManagedPostRefreshObserver(
1954 ManagedPostRefreshObserver* aObserver) {
1955 RefreshDriver()->RemovePostRefreshObserver(
1956 static_cast<nsAPostRefreshObserver*>(aObserver));
1957 DebugOnly<bool> removed =
1958 mManagedPostRefreshObservers.RemoveElement(aObserver);
1959 MOZ_ASSERT(removed,
1960 "ManagedPostRefreshObserver should be owned by PresContext");
1963 void nsPresContext::CancelManagedPostRefreshObservers() {
1964 auto observers = std::move(mManagedPostRefreshObservers);
1965 nsRefreshDriver* driver = RefreshDriver();
1966 for (const auto& observer : observers) {
1967 observer->Cancel();
1968 driver->RemovePostRefreshObserver(
1969 static_cast<nsAPostRefreshObserver*>(observer));
1973 void nsPresContext::RebuildAllStyleData(nsChangeHint aExtraHint,
1974 const RestyleHint& aRestyleHint) {
1975 if (!mPresShell) {
1976 // We must have been torn down. Nothing to do here.
1977 return;
1980 // TODO(emilio): It's unclear to me why would these three calls below be
1981 // needed. In particular, RebuildAllStyleData doesn't rebuild rules or
1982 // specified style information and such (note the comment in
1983 // RestyleManager::RebuildAllStyleData re. the funny semantics), so I
1984 // don't know why should we rebuild the user font set / counter styles /
1985 // etc...
1986 mDocument->MarkUserFontSetDirty();
1987 MarkCounterStylesDirty();
1988 MarkFontFeatureValuesDirty();
1989 MarkFontPaletteValuesDirty();
1990 PostRebuildAllStyleDataEvent(aExtraHint, aRestyleHint);
1993 void nsPresContext::PostRebuildAllStyleDataEvent(
1994 nsChangeHint aExtraHint, const RestyleHint& aRestyleHint) {
1995 if (!mPresShell) {
1996 // We must have been torn down. Nothing to do here.
1997 return;
1999 RestyleManager()->RebuildAllStyleData(aExtraHint, aRestyleHint);
2002 void nsPresContext::MediaFeatureValuesChanged(
2003 const MediaFeatureChange& aChange,
2004 MediaFeatureChangePropagation aPropagation) {
2005 if (mPresShell) {
2006 mPresShell->EnsureStyleFlush();
2009 if (!mDocument->MediaQueryLists().isEmpty()) {
2010 RefreshDriver()->ScheduleMediaQueryListenerUpdate();
2013 if (!mPendingMediaFeatureValuesChange) {
2014 mPendingMediaFeatureValuesChange = MakeUnique<MediaFeatureChange>(aChange);
2015 } else {
2016 *mPendingMediaFeatureValuesChange |= aChange;
2019 if (aPropagation & MediaFeatureChangePropagation::Images) {
2020 // Propagate the media feature value change down to any SVG images the
2021 // document is using.
2022 mDocument->ImageTracker()->MediaFeatureValuesChangedAllDocuments(aChange);
2025 if (aPropagation & MediaFeatureChangePropagation::SubDocuments) {
2026 // And then into any subdocuments.
2027 auto recurse = [&aChange, aPropagation](dom::Document& aSubDoc) {
2028 if (nsPresContext* pc = aSubDoc.GetPresContext()) {
2029 pc->MediaFeatureValuesChanged(aChange, aPropagation);
2031 return CallState::Continue;
2033 mDocument->EnumerateSubDocuments(recurse);
2036 // We notify the media feature values changed for the responsive content of
2037 // HTMLImageElements synchronously, so their image sources are always
2038 // up-to-date when running the image load tasks in the microtasks.
2039 mDocument->NotifyMediaFeatureValuesChanged();
2042 bool nsPresContext::FlushPendingMediaFeatureValuesChanged() {
2043 if (!mPendingMediaFeatureValuesChange) {
2044 return false;
2047 MediaFeatureChange change = *mPendingMediaFeatureValuesChange;
2048 mPendingMediaFeatureValuesChange.reset();
2050 // MediumFeaturesChanged updates the applied rules, so it always gets called.
2051 if (mPresShell) {
2052 change.mRestyleHint |=
2053 mPresShell->StyleSet()->MediumFeaturesChanged(change.mReason);
2056 const bool changedStyle = change.mRestyleHint || change.mChangeHint;
2057 if (changedStyle) {
2058 RebuildAllStyleData(change.mChangeHint, change.mRestyleHint);
2061 for (MediaQueryList* mql : mDocument->MediaQueryLists()) {
2062 mql->MediaFeatureValuesChanged();
2065 return changedStyle;
2068 void nsPresContext::SizeModeChanged(nsSizeMode aSizeMode) {
2069 if (nsPIDOMWindowOuter* window = mDocument->GetWindow()) {
2070 nsContentUtils::CallOnAllRemoteChildren(
2071 window, [&aSizeMode](BrowserParent* aBrowserParent) -> CallState {
2072 aBrowserParent->SizeModeChanged(aSizeMode);
2073 return CallState::Continue;
2076 MediaFeatureValuesChanged({MediaFeatureChangeReason::SizeModeChange},
2077 MediaFeatureChangePropagation::SubDocuments);
2080 nsCompatibility nsPresContext::CompatibilityMode() const {
2081 return Document()->GetCompatibilityMode();
2084 void nsPresContext::SetPaginatedScrolling(bool aPaginated) {
2085 if (mType == eContext_PrintPreview || mType == eContext_PageLayout) {
2086 mCanPaginatedScroll = aPaginated;
2090 void nsPresContext::SetPrintSettings(nsIPrintSettings* aPrintSettings) {
2091 if (mMedium != nsGkAtoms::print) {
2092 return;
2095 mPrintSettings = aPrintSettings;
2096 mDefaultPageMargin = nsMargin();
2097 if (!mPrintSettings) {
2098 return;
2101 // Set the presentation context to the value in the print settings.
2102 mDrawColorBackground = mPrintSettings->GetPrintBGColors();
2103 mDrawImageBackground = mPrintSettings->GetPrintBGImages();
2105 nsIntMargin marginTwips = mPrintSettings->GetMarginInTwips();
2106 if (!mPrintSettings->GetIgnoreUnwriteableMargins()) {
2107 nsIntMargin unwriteableTwips =
2108 mPrintSettings->GetUnwriteableMarginInTwips();
2109 NS_ASSERTION(unwriteableTwips.top >= 0 && unwriteableTwips.right >= 0 &&
2110 unwriteableTwips.bottom >= 0 && unwriteableTwips.left >= 0,
2111 "Unwriteable twips should be non-negative");
2112 marginTwips.EnsureAtLeast(unwriteableTwips);
2114 mDefaultPageMargin = nsPresContext::CSSTwipsToAppUnits(marginTwips);
2117 bool nsPresContext::EnsureVisible() {
2118 BrowsingContext* browsingContext =
2119 mDocument ? mDocument->GetBrowsingContext() : nullptr;
2120 if (!browsingContext || browsingContext->IsInBFCache()) {
2121 return false;
2124 nsCOMPtr<nsIDocShell> docShell(GetDocShell());
2125 if (!docShell) {
2126 return false;
2128 nsCOMPtr<nsIContentViewer> cv;
2129 docShell->GetContentViewer(getter_AddRefs(cv));
2130 // Make sure this is the content viewer we belong with
2131 if (!cv || cv->GetPresContext() != this) {
2132 return false;
2134 // OK, this is us. We want to call Show() on the content viewer.
2135 nsresult result = cv->Show();
2136 return NS_SUCCEEDED(result);
2139 #ifdef MOZ_REFLOW_PERF
2140 void nsPresContext::CountReflows(const char* aName, nsIFrame* aFrame) {
2141 if (mPresShell) {
2142 mPresShell->CountReflows(aName, aFrame);
2145 #endif
2147 gfxUserFontSet* nsPresContext::GetUserFontSet() {
2148 return mDocument->GetUserFontSet();
2151 void nsPresContext::UserFontSetUpdated(gfxUserFontEntry* aUpdatedFont) {
2152 if (!mPresShell) {
2153 return;
2156 // Note: this method is called without a font when rules in the userfont set
2157 // are updated.
2159 // We can avoid a full restyle if font-metric-dependent units are not in use,
2160 // since we know there's no style resolution that would depend on this font
2161 // and trigger its load.
2163 // TODO(emilio): We could be more granular if we knew which families have
2164 // potentially changed.
2165 if (!aUpdatedFont) {
2166 auto hint = StyleSet()->UsesFontMetrics() ? RestyleHint::RecascadeSubtree()
2167 : RestyleHint{0};
2168 PostRebuildAllStyleDataEvent(NS_STYLE_HINT_REFLOW, hint);
2169 return;
2172 // Iterate over the frame tree looking for frames associated with the
2173 // downloadable font family in question. If a frame's nsStyleFont has
2174 // the name, check the font group associated with the metrics to see if
2175 // it contains that specific font (i.e. the one chosen within the family
2176 // given the weight, width, and slant from the nsStyleFont). If it does,
2177 // mark that frame dirty and skip inspecting its descendants.
2178 if (nsIFrame* root = mPresShell->GetRootFrame()) {
2179 nsFontFaceUtils::MarkDirtyForFontChange(root, aUpdatedFont);
2183 class CounterStyleCleaner final : public nsAPostRefreshObserver {
2184 public:
2185 CounterStyleCleaner(nsRefreshDriver* aRefreshDriver,
2186 CounterStyleManager* aCounterStyleManager)
2187 : mRefreshDriver(aRefreshDriver),
2188 mCounterStyleManager(aCounterStyleManager) {}
2189 virtual ~CounterStyleCleaner() = default;
2191 void DidRefresh() final {
2192 mRefreshDriver->RemovePostRefreshObserver(this);
2193 mCounterStyleManager->CleanRetiredStyles();
2194 delete this;
2197 private:
2198 RefPtr<nsRefreshDriver> mRefreshDriver;
2199 RefPtr<CounterStyleManager> mCounterStyleManager;
2202 void nsPresContext::FlushCounterStyles() {
2203 if (!mPresShell) {
2204 return; // we've been torn down
2206 if (mCounterStyleManager->IsInitial()) {
2207 // Still in its initial state, no need to clean.
2208 return;
2211 if (mCounterStylesDirty) {
2212 bool changed = mCounterStyleManager->NotifyRuleChanged();
2213 if (changed) {
2214 PresShell()->NotifyCounterStylesAreDirty();
2215 PostRebuildAllStyleDataEvent(NS_STYLE_HINT_REFLOW, RestyleHint{0});
2216 RefreshDriver()->AddPostRefreshObserver(
2217 new CounterStyleCleaner(RefreshDriver(), mCounterStyleManager));
2219 mCounterStylesDirty = false;
2223 void nsPresContext::MarkCounterStylesDirty() {
2224 if (mCounterStyleManager->IsInitial()) {
2225 // Still in its initial state, no need to touch anything.
2226 return;
2229 mCounterStylesDirty = true;
2232 void nsPresContext::NotifyMissingFonts() {
2233 if (mMissingFonts) {
2234 mMissingFonts->Flush();
2238 void nsPresContext::EnsureSafeToHandOutCSSRules() {
2239 if (!mPresShell->StyleSet()->EnsureUniqueInnerOnCSSSheets()) {
2240 // Nothing to do.
2241 return;
2244 RebuildAllStyleData(nsChangeHint(0), RestyleHint::RestyleSubtree());
2247 void nsPresContext::FireDOMPaintEvent(
2248 nsTArray<nsRect>* aList, TransactionId aTransactionId,
2249 mozilla::TimeStamp aTimeStamp /* = mozilla::TimeStamp() */) {
2250 nsPIDOMWindowInner* ourWindow = mDocument->GetInnerWindow();
2251 if (!ourWindow) return;
2253 nsCOMPtr<EventTarget> dispatchTarget = do_QueryInterface(ourWindow);
2254 nsCOMPtr<EventTarget> eventTarget = dispatchTarget;
2255 if (!IsChrome() && !mSendAfterPaintToContent) {
2256 // Don't tell the window about this event, it should not know that
2257 // something happened in a subdocument. Tell only the chrome event handler.
2258 // (Events sent to the window get propagated to the chrome event handler
2259 // automatically.)
2260 dispatchTarget = ourWindow->GetParentTarget();
2261 if (!dispatchTarget) {
2262 return;
2266 if (aTimeStamp.IsNull()) {
2267 aTimeStamp = mozilla::TimeStamp::Now();
2269 DOMHighResTimeStamp timeStamp = 0;
2270 if (ourWindow) {
2271 mozilla::dom::Performance* perf = ourWindow->GetPerformance();
2272 if (perf) {
2273 timeStamp = perf->GetDOMTiming()->TimeStampToDOMHighRes(aTimeStamp);
2277 // Events sent to the window get propagated to the chrome event handler
2278 // automatically.
2280 // This will empty our list in case dispatching the event causes more damage
2281 // (hopefully it won't, or we're likely to get an infinite loop! At least
2282 // it won't be blocking app execution though).
2283 RefPtr<NotifyPaintEvent> event =
2284 NS_NewDOMNotifyPaintEvent(eventTarget, this, nullptr, eAfterPaint, aList,
2285 uint64_t(aTransactionId), timeStamp);
2287 // Even if we're not telling the window about the event (so eventTarget is
2288 // the chrome event handler, not the window), the window is still
2289 // logically the event target.
2290 event->SetTarget(eventTarget);
2291 event->SetTrusted(true);
2292 EventDispatcher::DispatchDOMEvent(dispatchTarget, nullptr,
2293 static_cast<Event*>(event), this, nullptr);
2296 static bool MayHavePaintEventListener(nsPIDOMWindowInner* aInnerWindow) {
2297 if (!aInnerWindow) return false;
2298 if (aInnerWindow->HasPaintEventListeners()) return true;
2300 EventTarget* parentTarget = aInnerWindow->GetParentTarget();
2301 if (!parentTarget) return false;
2303 EventListenerManager* manager = nullptr;
2304 if ((manager = parentTarget->GetExistingListenerManager()) &&
2305 manager->MayHavePaintEventListener()) {
2306 return true;
2309 nsCOMPtr<nsINode> node;
2310 if (parentTarget != aInnerWindow->GetChromeEventHandler()) {
2311 nsCOMPtr<nsIInProcessContentFrameMessageManager> mm =
2312 do_QueryInterface(parentTarget);
2313 if (mm) {
2314 node = mm->GetOwnerContent();
2318 if (!node) {
2319 node = nsINode::FromEventTarget(parentTarget);
2321 if (node) {
2322 return MayHavePaintEventListener(node->OwnerDoc()->GetInnerWindow());
2325 if (nsCOMPtr<nsPIDOMWindowInner> window =
2326 nsPIDOMWindowInner::FromEventTarget(parentTarget)) {
2327 return MayHavePaintEventListener(window);
2330 if (nsCOMPtr<nsPIWindowRoot> root =
2331 nsPIWindowRoot::FromEventTarget(parentTarget)) {
2332 EventTarget* browserChildGlobal;
2333 return root && (browserChildGlobal = root->GetParentTarget()) &&
2334 (manager = browserChildGlobal->GetExistingListenerManager()) &&
2335 manager->MayHavePaintEventListener();
2338 return false;
2341 bool nsPresContext::MayHavePaintEventListener() {
2342 return ::MayHavePaintEventListener(mDocument->GetInnerWindow());
2345 void nsPresContext::NotifyInvalidation(TransactionId aTransactionId,
2346 const nsIntRect& aRect) {
2347 // Prevent values from overflow after DevPixelsToAppUnits().
2349 // DevPixelsTopAppUnits() will multiple a factor (60) to the value,
2350 // it may make the result value over the edge (overflow) of max or
2351 // min value of int32_t. Compute the max sized dev pixel rect that
2352 // we can support and intersect with it.
2353 nsIntRect clampedRect = nsIntRect::MaxIntRect();
2354 clampedRect.ScaleInverseRoundIn(AppUnitsPerDevPixel());
2356 clampedRect = clampedRect.Intersect(aRect);
2358 nsRect rect(DevPixelsToAppUnits(clampedRect.x),
2359 DevPixelsToAppUnits(clampedRect.y),
2360 DevPixelsToAppUnits(clampedRect.width),
2361 DevPixelsToAppUnits(clampedRect.height));
2362 NotifyInvalidation(aTransactionId, rect);
2365 nsPresContext::TransactionInvalidations* nsPresContext::GetInvalidations(
2366 TransactionId aTransactionId) {
2367 for (TransactionInvalidations& t : mTransactions) {
2368 if (t.mTransactionId == aTransactionId) {
2369 return &t;
2372 return nullptr;
2375 void nsPresContext::NotifyInvalidation(TransactionId aTransactionId,
2376 const nsRect& aRect) {
2377 MOZ_ASSERT(GetContainerWeak(), "Invalidation in detached pres context");
2379 // If there is no paint event listener, then we don't need to fire
2380 // the asynchronous event. We don't even need to record invalidation.
2381 // MayHavePaintEventListener is pretty cheap and we could make it
2382 // even cheaper by providing a more efficient
2383 // nsPIDOMWindow::GetListenerManager.
2385 nsPresContext* pc;
2386 for (pc = this; pc; pc = pc->GetParentPresContext()) {
2387 TransactionInvalidations* transaction =
2388 pc->GetInvalidations(aTransactionId);
2389 if (transaction) {
2390 break;
2391 } else {
2392 transaction = pc->mTransactions.AppendElement();
2393 transaction->mTransactionId = aTransactionId;
2397 TransactionInvalidations* transaction = GetInvalidations(aTransactionId);
2398 MOZ_ASSERT(transaction);
2399 transaction->mInvalidations.AppendElement(aRect);
2402 class DelayedFireDOMPaintEvent : public Runnable {
2403 public:
2404 DelayedFireDOMPaintEvent(
2405 nsPresContext* aPresContext, nsTArray<nsRect>&& aList,
2406 TransactionId aTransactionId,
2407 const mozilla::TimeStamp& aTimeStamp = mozilla::TimeStamp())
2408 : mozilla::Runnable("DelayedFireDOMPaintEvent"),
2409 mPresContext(aPresContext),
2410 mTransactionId(aTransactionId),
2411 mTimeStamp(aTimeStamp),
2412 mList(std::move(aList)) {
2413 MOZ_ASSERT(mPresContext->GetContainerWeak(),
2414 "DOMPaintEvent requested for a detached pres context");
2416 NS_IMETHOD Run() override {
2417 // The pres context might have been detached during the delay -
2418 // that's fine, just don't fire the event.
2419 if (mPresContext->GetContainerWeak()) {
2420 mPresContext->FireDOMPaintEvent(&mList, mTransactionId, mTimeStamp);
2422 return NS_OK;
2425 RefPtr<nsPresContext> mPresContext;
2426 TransactionId mTransactionId;
2427 const mozilla::TimeStamp mTimeStamp;
2428 nsTArray<nsRect> mList;
2431 void nsPresContext::NotifyRevokingDidPaint(TransactionId aTransactionId) {
2432 if ((IsRoot() || !PresShell()->IsVisible()) && mTransactions.IsEmpty()) {
2433 return;
2436 TransactionInvalidations* transaction = nullptr;
2437 for (auto& t : mTransactions) {
2438 if (t.mTransactionId == aTransactionId) {
2439 transaction = &t;
2440 break;
2443 // If there are no transaction invalidations (which imply callers waiting
2444 // on the event) for this revoked id, then we don't need to fire a
2445 // MozAfterPaint.
2446 if (!transaction) {
2447 return;
2450 // If there are queued transactions with an earlier id, we can't send
2451 // our event now since it will arrive out of order. Set the waiting for
2452 // previous transaction flag to true, and we'll send the event when
2453 // the others are completed.
2454 // If this is the only transaction, then we can send it immediately.
2455 if (mTransactions.Length() == 1) {
2456 nsCOMPtr<nsIRunnable> ev = new DelayedFireDOMPaintEvent(
2457 this, std::move(transaction->mInvalidations),
2458 transaction->mTransactionId, mozilla::TimeStamp());
2459 nsContentUtils::AddScriptRunner(ev);
2460 mTransactions.RemoveElementAt(0);
2461 } else {
2462 transaction->mIsWaitingForPreviousTransaction = true;
2465 auto recurse = [&aTransactionId](dom::Document& aSubDoc) {
2466 if (nsPresContext* pc = aSubDoc.GetPresContext()) {
2467 pc->NotifyRevokingDidPaint(aTransactionId);
2469 return CallState::Continue;
2471 mDocument->EnumerateSubDocuments(recurse);
2474 void nsPresContext::NotifyDidPaintForSubtree(
2475 TransactionId aTransactionId, const mozilla::TimeStamp& aTimeStamp) {
2476 if (mFirstContentfulPaintTransactionId && !mHadContentfulPaintComposite) {
2477 if (aTransactionId >= *mFirstContentfulPaintTransactionId) {
2478 mHadContentfulPaintComposite = true;
2479 RefPtr<nsDOMNavigationTiming> timing = mDocument->GetNavigationTiming();
2480 if (timing && !IsPrintingOrPrintPreview()) {
2481 timing->NotifyContentfulCompositeForRootContentDocument(aTimeStamp);
2486 if (IsRoot() && mTransactions.IsEmpty()) {
2487 return;
2490 if (!PresShell()->IsVisible() && mTransactions.IsEmpty()) {
2491 return;
2494 // Non-root prescontexts fire MozAfterPaint to all their descendants
2495 // unconditionally, even if no invalidations have been collected. This is
2496 // because we don't want to eat the cost of collecting invalidations for
2497 // every subdocument (which would require putting every subdocument in its
2498 // own layer).
2500 bool sent = false;
2501 uint32_t i = 0;
2502 while (i < mTransactions.Length()) {
2503 if (mTransactions[i].mTransactionId <= aTransactionId) {
2504 if (!mTransactions[i].mInvalidations.IsEmpty()) {
2505 nsCOMPtr<nsIRunnable> ev = new DelayedFireDOMPaintEvent(
2506 this, std::move(mTransactions[i].mInvalidations),
2507 mTransactions[i].mTransactionId, aTimeStamp);
2508 NS_DispatchToCurrentThreadQueue(ev.forget(),
2509 EventQueuePriority::MediumHigh);
2510 sent = true;
2512 mTransactions.RemoveElementAt(i);
2513 } else {
2514 // If there are transaction which is waiting for this transaction,
2515 // we should fire a MozAfterPaint immediately.
2516 if (sent && mTransactions[i].mIsWaitingForPreviousTransaction) {
2517 nsCOMPtr<nsIRunnable> ev = new DelayedFireDOMPaintEvent(
2518 this, std::move(mTransactions[i].mInvalidations),
2519 mTransactions[i].mTransactionId, aTimeStamp);
2520 NS_DispatchToCurrentThreadQueue(ev.forget(),
2521 EventQueuePriority::MediumHigh);
2522 sent = true;
2523 mTransactions.RemoveElementAt(i);
2524 continue;
2526 i++;
2530 if (!sent) {
2531 nsTArray<nsRect> dummy;
2532 nsCOMPtr<nsIRunnable> ev = new DelayedFireDOMPaintEvent(
2533 this, std::move(dummy), aTransactionId, aTimeStamp);
2534 NS_DispatchToCurrentThreadQueue(ev.forget(),
2535 EventQueuePriority::MediumHigh);
2538 auto recurse = [&aTransactionId, &aTimeStamp](dom::Document& aSubDoc) {
2539 if (nsPresContext* pc = aSubDoc.GetPresContext()) {
2540 pc->NotifyDidPaintForSubtree(aTransactionId, aTimeStamp);
2542 return CallState::Continue;
2544 mDocument->EnumerateSubDocuments(recurse);
2547 already_AddRefed<nsITimer> nsPresContext::CreateTimer(
2548 nsTimerCallbackFunc aCallback, const char* aName, uint32_t aDelay) {
2549 nsCOMPtr<nsITimer> timer;
2550 NS_NewTimerWithFuncCallback(getter_AddRefs(timer), aCallback, this, aDelay,
2551 nsITimer::TYPE_ONE_SHOT, aName,
2552 GetMainThreadSerialEventTarget());
2553 return timer.forget();
2556 static bool sGotInterruptEnv = false;
2557 enum InterruptMode { ModeRandom, ModeCounter, ModeEvent };
2558 // Controlled by the GECKO_REFLOW_INTERRUPT_MODE env var; allowed values are
2559 // "random" (except on Windows) or "counter". If neither is used, the mode is
2560 // ModeEvent.
2561 static InterruptMode sInterruptMode = ModeEvent;
2562 #ifndef XP_WIN
2563 // Used for the "random" mode. Controlled by the GECKO_REFLOW_INTERRUPT_SEED
2564 // env var.
2565 static uint32_t sInterruptSeed = 1;
2566 #endif
2567 // Used for the "counter" mode. This is the number of unskipped interrupt
2568 // checks that have to happen before we interrupt. Controlled by the
2569 // GECKO_REFLOW_INTERRUPT_FREQUENCY env var.
2570 static uint32_t sInterruptMaxCounter = 10;
2571 // Used for the "counter" mode. This counts up to sInterruptMaxCounter and is
2572 // then reset to 0.
2573 static uint32_t sInterruptCounter;
2574 // Number of interrupt checks to skip before really trying to interrupt.
2575 // Controlled by the GECKO_REFLOW_INTERRUPT_CHECKS_TO_SKIP env var.
2576 static uint32_t sInterruptChecksToSkip = 200;
2577 // Number of milliseconds that a reflow should be allowed to run for before we
2578 // actually allow interruption. Controlled by the
2579 // GECKO_REFLOW_MIN_NOINTERRUPT_DURATION env var. Can't be initialized here,
2580 // because TimeDuration/TimeStamp is not safe to use in static constructors..
2581 static TimeDuration sInterruptTimeout;
2583 static void GetInterruptEnv() {
2584 char* ev = PR_GetEnv("GECKO_REFLOW_INTERRUPT_MODE");
2585 if (ev) {
2586 #ifndef XP_WIN
2587 if (nsCRT::strcasecmp(ev, "random") == 0) {
2588 ev = PR_GetEnv("GECKO_REFLOW_INTERRUPT_SEED");
2589 if (ev) {
2590 sInterruptSeed = atoi(ev);
2592 srandom(sInterruptSeed);
2593 sInterruptMode = ModeRandom;
2594 } else
2595 #endif
2596 if (PL_strcasecmp(ev, "counter") == 0) {
2597 ev = PR_GetEnv("GECKO_REFLOW_INTERRUPT_FREQUENCY");
2598 if (ev) {
2599 sInterruptMaxCounter = atoi(ev);
2601 sInterruptCounter = 0;
2602 sInterruptMode = ModeCounter;
2605 ev = PR_GetEnv("GECKO_REFLOW_INTERRUPT_CHECKS_TO_SKIP");
2606 if (ev) {
2607 sInterruptChecksToSkip = atoi(ev);
2610 ev = PR_GetEnv("GECKO_REFLOW_MIN_NOINTERRUPT_DURATION");
2611 int duration_ms = ev ? atoi(ev) : 100;
2612 sInterruptTimeout = TimeDuration::FromMilliseconds(duration_ms);
2615 bool nsPresContext::HavePendingInputEvent() {
2616 switch (sInterruptMode) {
2617 #ifndef XP_WIN
2618 case ModeRandom:
2619 return (random() & 1);
2620 #endif
2621 case ModeCounter:
2622 if (sInterruptCounter < sInterruptMaxCounter) {
2623 ++sInterruptCounter;
2624 return false;
2626 sInterruptCounter = 0;
2627 return true;
2628 default:
2629 case ModeEvent: {
2630 nsIFrame* f = PresShell()->GetRootFrame();
2631 if (f) {
2632 nsIWidget* w = f->GetNearestWidget();
2633 if (w) {
2634 return w->HasPendingInputEvent();
2637 return false;
2642 bool nsPresContext::HasPendingRestyleOrReflow() {
2643 mozilla::PresShell* presShell = PresShell();
2644 return presShell->NeedStyleFlush() || presShell->HasPendingReflow();
2647 void nsPresContext::ReflowStarted(bool aInterruptible) {
2648 #ifdef NOISY_INTERRUPTIBLE_REFLOW
2649 if (!aInterruptible) {
2650 printf("STARTING NONINTERRUPTIBLE REFLOW\n");
2652 #endif
2653 // We don't support interrupting in paginated contexts, since page
2654 // sequences only handle initial reflow
2655 mInterruptsEnabled = aInterruptible && !IsPaginated() &&
2656 StaticPrefs::layout_interruptible_reflow_enabled();
2658 // Don't set mHasPendingInterrupt based on HavePendingInputEvent() here. If
2659 // we ever change that, then we need to update the code in
2660 // PresShell::DoReflow to only add the just-reflown root to dirty roots if
2661 // it's actually dirty. Otherwise we can end up adding a root that has no
2662 // interruptible descendants, just because we detected an interrupt at reflow
2663 // start.
2664 mHasPendingInterrupt = false;
2666 mInterruptChecksToSkip = sInterruptChecksToSkip;
2668 if (mInterruptsEnabled) {
2669 mReflowStartTime = TimeStamp::Now();
2673 bool nsPresContext::CheckForInterrupt(nsIFrame* aFrame) {
2674 if (mHasPendingInterrupt) {
2675 mPresShell->FrameNeedsToContinueReflow(aFrame);
2676 return true;
2679 if (!sGotInterruptEnv) {
2680 sGotInterruptEnv = true;
2681 GetInterruptEnv();
2684 if (!mInterruptsEnabled) {
2685 return false;
2688 if (mInterruptChecksToSkip > 0) {
2689 --mInterruptChecksToSkip;
2690 return false;
2692 mInterruptChecksToSkip = sInterruptChecksToSkip;
2694 // Don't interrupt if it's been less than sInterruptTimeout since we started
2695 // the reflow.
2696 mHasPendingInterrupt =
2697 TimeStamp::Now() - mReflowStartTime > sInterruptTimeout &&
2698 HavePendingInputEvent() && !IsChrome();
2700 if (mPendingInterruptFromTest) {
2701 mPendingInterruptFromTest = false;
2702 mHasPendingInterrupt = true;
2705 if (mHasPendingInterrupt) {
2706 #ifdef NOISY_INTERRUPTIBLE_REFLOW
2707 printf("*** DETECTED pending interrupt (time=%lld)\n", PR_Now());
2708 #endif /* NOISY_INTERRUPTIBLE_REFLOW */
2709 mPresShell->FrameNeedsToContinueReflow(aFrame);
2711 return mHasPendingInterrupt;
2714 nsIFrame* nsPresContext::GetPrimaryFrameFor(nsIContent* aContent) {
2715 MOZ_ASSERT(aContent, "Don't do that");
2716 if (GetPresShell() &&
2717 GetPresShell()->GetDocument() == aContent->GetComposedDoc()) {
2718 return aContent->GetPrimaryFrame();
2720 return nullptr;
2723 size_t nsPresContext::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
2724 // Measurement may be added later if DMD finds it is worthwhile.
2725 return 0;
2728 bool nsPresContext::IsRootContentDocumentInProcess() const {
2729 if (mDocument->IsResourceDoc()) {
2730 return false;
2732 if (IsChrome()) {
2733 return false;
2735 // We may not have a root frame, so use views.
2736 nsView* view = PresShell()->GetViewManager()->GetRootView();
2737 if (!view) {
2738 return false;
2740 view = view->GetParent(); // anonymous inner view
2741 if (!view) {
2742 return true;
2744 view = view->GetParent(); // subdocumentframe's view
2745 if (!view) {
2746 return true;
2749 nsIFrame* f = view->GetFrame();
2750 return (f && f->PresContext()->IsChrome());
2753 bool nsPresContext::IsRootContentDocumentCrossProcess() const {
2754 if (mDocument->IsResourceDoc()) {
2755 return false;
2758 if (BrowsingContext* browsingContext = mDocument->GetBrowsingContext()) {
2759 if (browsingContext->GetEmbeddedInContentDocument()) {
2760 return false;
2763 return mDocument->IsTopLevelContentDocument();
2766 void nsPresContext::NotifyNonBlankPaint() {
2767 MOZ_ASSERT(!mHadNonBlankPaint);
2768 mHadNonBlankPaint = true;
2769 if (IsRootContentDocumentCrossProcess()) {
2770 RefPtr<nsDOMNavigationTiming> timing = mDocument->GetNavigationTiming();
2771 if (timing && !IsPrintingOrPrintPreview()) {
2772 timing->NotifyNonBlankPaintForRootContentDocument();
2775 mFirstNonBlankPaintTime = TimeStamp::Now();
2777 if (IsChrome() && IsRoot()) {
2778 if (nsCOMPtr<nsIWidget> rootWidget = GetRootWidget()) {
2779 rootWidget->DidGetNonBlankPaint();
2784 void nsPresContext::NotifyContentfulPaint() {
2785 if (mHadFirstContentfulPaint) {
2786 return;
2788 nsRootPresContext* rootPresContext = GetRootPresContext();
2789 if (!rootPresContext) {
2790 return;
2792 if (!mHadNonTickContentfulPaint) {
2793 #ifdef MOZ_WIDGET_ANDROID
2794 (new AsyncEventDispatcher(mDocument, u"MozFirstContentfulPaint"_ns,
2795 CanBubble::eYes, ChromeOnlyDispatch::eYes))
2796 ->PostDOMEvent();
2797 #endif
2799 if (!rootPresContext->RefreshDriver()->IsInRefresh()) {
2800 if (!mHadNonTickContentfulPaint) {
2801 rootPresContext->RefreshDriver()
2802 ->AddForceNotifyContentfulPaintPresContext(this);
2803 mHadNonTickContentfulPaint = true;
2805 return;
2807 mHadFirstContentfulPaint = true;
2808 mFirstContentfulPaintTransactionId =
2809 Some(rootPresContext->mRefreshDriver->LastTransactionId().Next());
2810 if (nsPIDOMWindowInner* innerWindow = mDocument->GetInnerWindow()) {
2811 if (Performance* perf = innerWindow->GetPerformance()) {
2812 TimeStamp nowTime = rootPresContext->RefreshDriver()->MostRecentRefresh(
2813 /* aEnsureTimerStarted */ false);
2814 MOZ_ASSERT(!nowTime.IsNull(),
2815 "Most recent refresh timestamp should exist since we are in "
2816 "a refresh driver tick");
2817 MOZ_ASSERT(rootPresContext->RefreshDriver()->IsInRefresh(),
2818 "We should only notify contentful paint during refresh "
2819 "driver ticks");
2820 RefPtr<PerformancePaintTiming> paintTiming = new PerformancePaintTiming(
2821 perf, u"first-contentful-paint"_ns, nowTime);
2822 perf->SetFCPTimingEntry(paintTiming);
2824 if (profiler_thread_is_being_profiled_for_markers()) {
2825 RefPtr<nsDOMNavigationTiming> timing = mDocument->GetNavigationTiming();
2826 if (timing) {
2827 TimeStamp navigationStart = timing->GetNavigationStartTimeStamp();
2828 TimeDuration elapsed = nowTime - navigationStart;
2829 nsIURI* docURI = Document()->GetDocumentURI();
2830 nsPrintfCString marker(
2831 "Contentful paint after %dms for URL %s",
2832 int(elapsed.ToMilliseconds()),
2833 nsContentUtils::TruncatedURLForDisplay(docURI).get());
2834 PROFILER_MARKER_TEXT(
2835 "FirstContentfulPaint", DOM,
2836 MarkerOptions(MarkerTiming::Interval(navigationStart, nowTime),
2837 MarkerInnerWindowId(innerWindow->WindowID())),
2838 marker);
2845 void nsPresContext::NotifyPaintStatusReset() {
2846 mHadNonBlankPaint = false;
2847 mHadFirstContentfulPaint = false;
2848 #if defined(MOZ_WIDGET_ANDROID)
2849 (new AsyncEventDispatcher(mDocument, u"MozPaintStatusReset"_ns,
2850 CanBubble::eYes, ChromeOnlyDispatch::eYes))
2851 ->PostDOMEvent();
2852 #endif
2853 mHadNonTickContentfulPaint = false;
2856 void nsPresContext::NotifyDOMContentFlushed() {
2857 NS_ENSURE_TRUE_VOID(mPresShell);
2858 if (IsRootContentDocumentCrossProcess()) {
2859 RefPtr<nsDOMNavigationTiming> timing = mDocument->GetNavigationTiming();
2860 if (timing) {
2861 timing->NotifyDOMContentFlushedForRootContentDocument();
2866 nscoord nsPresContext::GfxUnitsToAppUnits(gfxFloat aGfxUnits) const {
2867 return mDeviceContext->GfxUnitsToAppUnits(aGfxUnits);
2870 gfxFloat nsPresContext::AppUnitsToGfxUnits(nscoord aAppUnits) const {
2871 return mDeviceContext->AppUnitsToGfxUnits(aAppUnits);
2874 nscoord nsPresContext::PhysicalMillimetersToAppUnits(float aMM) const {
2875 float inches = aMM / MM_PER_INCH_FLOAT;
2876 return NSToCoordFloorClamped(
2877 inches * float(DeviceContext()->AppUnitsPerPhysicalInch()));
2880 uint64_t nsPresContext::GetRestyleGeneration() const {
2881 if (!mRestyleManager) {
2882 return 0;
2884 return mRestyleManager->GetRestyleGeneration();
2887 uint64_t nsPresContext::GetUndisplayedRestyleGeneration() const {
2888 if (!mRestyleManager) {
2889 return 0;
2891 return mRestyleManager->GetUndisplayedRestyleGeneration();
2894 mozilla::intl::Bidi& nsPresContext::BidiEngine() {
2895 MOZ_ASSERT(NS_IsMainThread());
2897 if (!mBidiEngine) {
2898 mBidiEngine = MakeUnique<mozilla::intl::Bidi>();
2900 return *mBidiEngine;
2903 void nsPresContext::FlushFontFeatureValues() {
2904 if (!mPresShell) {
2905 return; // we've been torn down
2908 if (!mFontFeatureValuesDirty) {
2909 return;
2912 ServoStyleSet* styleSet = mPresShell->StyleSet();
2913 mFontFeatureValuesLookup = styleSet->BuildFontFeatureValueSet();
2914 mFontFeatureValuesDirty = false;
2917 void nsPresContext::FlushFontPaletteValues() {
2918 if (!mPresShell) {
2919 return; // we've been torn down
2922 if (!mFontPaletteValuesDirty) {
2923 return;
2926 ServoStyleSet* styleSet = mPresShell->StyleSet();
2927 mFontPaletteValueSet = styleSet->BuildFontPaletteValueSet();
2928 mFontPaletteValuesDirty = false;
2930 // Even if we're not reflowing anything, a change to the palette means we
2931 // need to repaint in order to show the new colors.
2932 InvalidatePaintedLayers();
2935 void nsPresContext::SetVisibleArea(const nsRect& r) {
2936 if (!r.IsEqualEdges(mVisibleArea)) {
2937 mVisibleArea = r;
2938 mSizeForViewportUnits = mVisibleArea.Size();
2939 if (IsRootContentDocumentCrossProcess()) {
2940 AdjustSizeForViewportUnits();
2942 // Visible area does not affect media queries when paginated.
2943 if (!IsRootPaginatedDocument()) {
2944 MediaFeatureValuesChanged(
2945 {mozilla::MediaFeatureChangeReason::ViewportChange},
2946 MediaFeatureChangePropagation::JustThisDocument);
2951 void nsPresContext::SetDynamicToolbarMaxHeight(ScreenIntCoord aHeight) {
2952 MOZ_ASSERT(IsRootContentDocumentCrossProcess());
2954 if (mDynamicToolbarMaxHeight == aHeight) {
2955 return;
2957 mDynamicToolbarMaxHeight = aHeight;
2958 mDynamicToolbarHeight = aHeight;
2960 AdjustSizeForViewportUnits();
2962 if (RefPtr<mozilla::PresShell> presShell = mPresShell) {
2963 // Changing the max height of the dynamic toolbar changes the ICB size, we
2964 // need to kick a reflow with the current window dimensions since the max
2965 // height change doesn't change the window dimensions but
2966 // PresShell::ResizeReflow ends up subtracting the new dynamic toolbar
2967 // height from the window dimensions and kick a reflow with the proper ICB
2968 // size.
2969 presShell->ForceResizeReflowWithCurrentDimensions();
2973 void nsPresContext::AdjustSizeForViewportUnits() {
2974 MOZ_ASSERT(IsRootContentDocumentCrossProcess());
2975 if (mVisibleArea.height == NS_UNCONSTRAINEDSIZE) {
2976 // Ignore `NS_UNCONSTRAINEDSIZE` since it's a temporary state during a
2977 // reflow. We will end up calling this function again with a proper size in
2978 // the same reflow.
2979 return;
2982 if (MOZ_UNLIKELY(mVisibleArea.height +
2983 NSIntPixelsToAppUnits(mDynamicToolbarMaxHeight,
2984 mCurAppUnitsPerDevPixel) >
2985 nscoord_MAX)) {
2986 MOZ_ASSERT_UNREACHABLE("The dynamic toolbar max height is probably wrong");
2987 return;
2990 mSizeForViewportUnits.height =
2991 mVisibleArea.height +
2992 NSIntPixelsToAppUnits(mDynamicToolbarMaxHeight, mCurAppUnitsPerDevPixel);
2995 void nsPresContext::UpdateDynamicToolbarOffset(ScreenIntCoord aOffset) {
2996 MOZ_ASSERT(IsRootContentDocumentCrossProcess());
2997 if (!mPresShell) {
2998 return;
3001 if (!HasDynamicToolbar()) {
3002 return;
3005 MOZ_ASSERT(-mDynamicToolbarMaxHeight <= aOffset && aOffset <= 0);
3006 if (mDynamicToolbarHeight == mDynamicToolbarMaxHeight + aOffset) {
3007 return;
3010 // Forcibly flush position:fixed elements in the case where the dynamic
3011 // toolbar is going to be completely hidden or starts to be visible so that
3012 // %-based style values will be recomputed with the visual viewport size which
3013 // is including the area covered by the dynamic toolbar.
3014 if (mDynamicToolbarHeight == 0 || aOffset == -mDynamicToolbarMaxHeight) {
3015 mPresShell->MarkFixedFramesForReflow(IntrinsicDirty::None);
3016 mPresShell->AddResizeEventFlushObserverIfNeeded();
3019 mDynamicToolbarHeight = mDynamicToolbarMaxHeight + aOffset;
3021 if (RefPtr<MobileViewportManager> mvm =
3022 mPresShell->GetMobileViewportManager()) {
3023 mvm->UpdateVisualViewportSizeByDynamicToolbar(-aOffset);
3026 mPresShell->StyleSet()->InvalidateForViewportUnits(
3027 ServoStyleSet::OnlyDynamic::Yes);
3030 DynamicToolbarState nsPresContext::GetDynamicToolbarState() const {
3031 if (!IsRootContentDocumentCrossProcess() || !HasDynamicToolbar()) {
3032 return DynamicToolbarState::None;
3035 if (mDynamicToolbarMaxHeight == mDynamicToolbarHeight) {
3036 return DynamicToolbarState::Expanded;
3037 } else if (mDynamicToolbarHeight == 0) {
3038 return DynamicToolbarState::Collapsed;
3040 return DynamicToolbarState::InTransition;
3043 void nsPresContext::SetSafeAreaInsets(const ScreenIntMargin& aSafeAreaInsets) {
3044 if (mSafeAreaInsets == aSafeAreaInsets) {
3045 return;
3047 mSafeAreaInsets = aSafeAreaInsets;
3049 PostRebuildAllStyleDataEvent(nsChangeHint(0),
3050 RestyleHint::RecascadeSubtree());
3053 #ifdef DEBUG
3055 void nsPresContext::ValidatePresShellAndDocumentReleation() const {
3056 NS_ASSERTION(!mPresShell || !mPresShell->GetDocument() ||
3057 mPresShell->GetDocument() == mDocument,
3058 "nsPresContext doesn't have the same document as nsPresShell!");
3061 #endif // #ifdef DEBUG
3063 nsRootPresContext::nsRootPresContext(dom::Document* aDocument,
3064 nsPresContextType aType)
3065 : nsPresContext(aDocument, aType) {}
3067 void nsRootPresContext::AddWillPaintObserver(nsIRunnable* aRunnable) {
3068 if (!mWillPaintFallbackEvent.IsPending()) {
3069 mWillPaintFallbackEvent = new RunWillPaintObservers(this);
3070 Document()->Dispatch(do_AddRef(mWillPaintFallbackEvent));
3072 mWillPaintObservers.AppendElement(aRunnable);
3076 * Run all runnables that need to get called before the next paint.
3078 void nsRootPresContext::FlushWillPaintObservers() {
3079 mWillPaintFallbackEvent = nullptr;
3080 nsTArray<nsCOMPtr<nsIRunnable>> observers = std::move(mWillPaintObservers);
3081 for (uint32_t i = 0; i < observers.Length(); ++i) {
3082 observers[i]->Run();
3086 size_t nsRootPresContext::SizeOfExcludingThis(
3087 MallocSizeOf aMallocSizeOf) const {
3088 return nsPresContext::SizeOfExcludingThis(aMallocSizeOf);
3090 // Measurement of the following members may be added later if DMD finds it is
3091 // worthwhile:
3092 // - mWillPaintObservers
3093 // - mWillPaintFallbackEvent