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