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