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