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