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