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