/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* a presentation of a document, part 1 */ #include "nsPresContext.h" #include "mozilla/ArrayUtils.h" #include "mozilla/DebugOnly.h" #include "mozilla/Encoding.h" #include "mozilla/EventDispatcher.h" #include "mozilla/EventStateManager.h" #include "base/basictypes.h" #include "nsCOMPtr.h" #include "nsCSSFrameConstructor.h" #include "nsIPresShell.h" #include "nsIPresShellInlines.h" #include "nsDocShell.h" #include "nsIContentViewer.h" #include "nsPIDOMWindow.h" #include "mozilla/StyleSetHandle.h" #include "mozilla/StyleSetHandleInlines.h" #include "nsIContent.h" #include "nsIFrame.h" #include "nsIDocument.h" #include "nsIDocumentInlines.h" #include "nsIPrintSettings.h" #include "nsLanguageAtomService.h" #include "mozilla/LookAndFeel.h" #include "nsIInterfaceRequestorUtils.h" #include "nsHTMLDocument.h" #include "nsIWeakReferenceUtils.h" #include "nsThreadUtils.h" #include "nsLayoutUtils.h" #include "nsViewManager.h" #ifdef MOZ_OLD_STYLE #include "mozilla/GeckoRestyleManager.h" #endif #include "mozilla/RestyleManager.h" #include "mozilla/RestyleManagerInlines.h" #include "SurfaceCacheUtils.h" #include "nsMediaFeatures.h" #ifdef MOZ_OLD_STYLE #include "nsRuleNode.h" #endif #include "gfxPlatform.h" #ifdef MOZ_OLD_STYLE #include "nsCSSRules.h" #endif #include "nsFontFaceLoader.h" #include "mozilla/AnimationEventDispatcher.h" #include "mozilla/EffectCompositor.h" #include "mozilla/EventListenerManager.h" #include "prenv.h" #include "nsPluginFrame.h" #include "nsTransitionManager.h" #include "nsAnimationManager.h" #include "CounterStyleManager.h" #include "mozilla/MemoryReporting.h" #include "mozilla/dom/Element.h" #include "nsIMessageManager.h" #include "mozilla/dom/HTMLBodyElement.h" #include "mozilla/dom/MediaQueryList.h" #include "nsSMILAnimationController.h" #include "mozilla/css/ImageLoader.h" #include "mozilla/dom/PBrowserParent.h" #include "mozilla/dom/TabChild.h" #include "mozilla/dom/TabParent.h" #include "nsRefreshDriver.h" #include "Layers.h" #include "LayerUserData.h" #include "ClientLayerManager.h" #include "mozilla/dom/NotifyPaintEvent.h" #include "gfxPrefs.h" #include "nsIDOMChromeWindow.h" #include "nsFrameLoader.h" #include "mozilla/dom/FontFaceSet.h" #include "nsContentUtils.h" #include "nsPIWindowRoot.h" #include "mozilla/Preferences.h" #include "gfxTextRun.h" #include "nsFontFaceUtils.h" #include "nsLayoutStylesheetCache.h" #include "mozilla/ServoBindings.h" #include "mozilla/StyleSheet.h" #include "mozilla/StyleSheetInlines.h" #include "mozilla/Telemetry.h" #include "mozilla/dom/Performance.h" #include "mozilla/dom/PerformanceTiming.h" #include "mozilla/layers/APZThreadUtils.h" // Needed for Start/Stop of Image Animation #include "imgIContainer.h" #include "nsIImageLoadingContent.h" #include "nsCSSParser.h" #include "nsBidiUtils.h" #include "nsServiceManagerUtils.h" #include "nsBidi.h" #include "mozilla/dom/URL.h" #include "mozilla/ServoCSSParser.h" using namespace mozilla; using namespace mozilla::dom; using namespace mozilla::gfx; using namespace mozilla::layers; uint8_t gNotifySubDocInvalidationData; // This preference was first introduced in Bug 232227, in order to prevent // system colors from being exposed to CSS or canvas. constexpr char kUseStandinsForNativeColors[] = "ui.use_standins_for_native_colors"; /** * Layer UserData for ContainerLayers that want to be notified * of local invalidations of them and their descendant layers. * Pass a callback to ComputeDifferences to have these called. */ class ContainerLayerPresContext : public LayerUserData { public: nsPresContext* mPresContext; }; #ifdef MOZ_OLD_STYLE namespace { class CharSetChangingRunnable : public Runnable { public: CharSetChangingRunnable(nsPresContext* aPresContext, NotNull aCharSet) : Runnable("CharSetChangingRunnable"), mPresContext(aPresContext), mCharSet(aCharSet) {} NS_IMETHOD Run() override { mPresContext->DoChangeCharSet(mCharSet); return NS_OK; } private: RefPtr mPresContext; NotNull mCharSet; }; } // namespace #endif nscolor nsPresContext::MakeColorPref(const nsString& aColor) { bool ok; nscolor result; ServoStyleSet* servoStyleSet = mShell && mShell->StyleSet() ? mShell->StyleSet()->GetAsServo() : nullptr; bool useServoParser = #ifdef MOZ_OLD_STYLE servoStyleSet; #else true; #endif if (useServoParser) { ok = ServoCSSParser::ComputeColor(servoStyleSet, NS_RGB(0, 0, 0), aColor, &result); } else { #ifdef MOZ_OLD_STYLE nsCSSParser parser; nsCSSValue value; ok = parser.ParseColorString(aColor, nullptr, 0, value) && nsRuleNode::ComputeColor(value, this, nullptr, result); #else MOZ_CRASH("old style system disabled"); #endif } if (!ok) { // Any better choices? result = NS_RGB(0, 0, 0); } return result; } bool nsPresContext::IsDOMPaintEventPending() { if (!mTransactions.IsEmpty()) { return true; } nsRootPresContext* drpc = GetRootPresContext(); if (drpc && drpc->mRefreshDriver->ViewManagerFlushIsPending()) { // Since we're promising that there will be a MozAfterPaint event // fired, we record an empty invalidation in case display list // invalidation doesn't invalidate anything further. NotifyInvalidation(drpc->mRefreshDriver->LastTransactionId() + 1, nsRect(0, 0, 0, 0)); return true; } return false; } void nsPresContext::PrefChangedCallback(const char* aPrefName, void* instance_data) { RefPtr presContext = static_cast(instance_data); NS_ASSERTION(presContext, "bad instance data"); if (presContext) { presContext->PreferenceChanged(aPrefName); } } void nsPresContext::ForceReflowForFontInfoUpdate() { // We can trigger reflow by pretending a font.* preference has changed; // this is the same mechanism as gfxPlatform::ForceGlobalReflow() uses // if new fonts are installed during the session, for example. PreferenceChanged("font.internaluseonly.changed"); } static bool IsVisualCharset(NotNull aCharset) { return aCharset == ISO_8859_8_ENCODING; } nsPresContext::nsPresContext(nsIDocument* aDocument, nsPresContextType aType) : mType(aType), mShell(nullptr), mDocument(aDocument), mMedium(aType == eContext_Galley ? nsGkAtoms::screen : nsGkAtoms::print), mMediaEmulated(mMedium), mLinkHandler(nullptr), mInflationDisabledForShrinkWrap(false), mBaseMinFontSize(0), mSystemFontScale(1.0), mTextZoom(1.0), mEffectiveTextZoom(1.0), mFullZoom(1.0), mOverrideDPPX(0.0), mLastFontInflationScreenSize(gfxSize(-1.0, -1.0)), mCurAppUnitsPerDevPixel(0), mAutoQualityMinFontSizePixelsPref(0), // origin nscoord_MIN is impossible, so the first ResizeReflow always // fires mLastResizeEventVisibleArea(nsRect(nscoord_MIN, nscoord_MIN, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE)), mPageSize(-1, -1), mPageScale(0.0), mPPScale(1.0f), mDefaultColor(NS_RGBA(0, 0, 0, 0)), mBackgroundColor(NS_RGB(0xFF, 0xFF, 0xFF)), mLinkColor(NS_RGB(0x00, 0x00, 0xEE)), mActiveLinkColor(NS_RGB(0xEE, 0x00, 0x00)), mVisitedLinkColor(NS_RGB(0x55, 0x1A, 0x8B)), mFocusBackgroundColor(mBackgroundColor), mFocusTextColor(mDefaultColor), mBodyTextColor(mDefaultColor), mViewportScrollbarOverrideElement(nullptr), mViewportStyleScrollbar(NS_STYLE_OVERFLOW_AUTO, NS_STYLE_OVERFLOW_AUTO), mFocusRingWidth(1), mExistThrottledUpdates(false), // mImageAnimationMode is initialised below, in constructor body mImageAnimationModePref(imgIContainer::kNormalAnimMode), mFontGroupCacheDirty(true), mInterruptChecksToSkip(0), mElementsRestyled(0), mFramesConstructed(0), mFramesReflowed(0), mInteractionTimeEnabled(true), mTelemetryScrollLastY(0), mTelemetryScrollMaxY(0), mTelemetryScrollTotalY(0), mHasPendingInterrupt(false), mPendingInterruptFromTest(false), mInterruptsEnabled(false), mUseDocumentFonts(true), mUseDocumentColors(true), mUnderlineLinks(true), mSendAfterPaintToContent(false), mUseFocusColors(false), mFocusRingOnAnything(false), mFocusRingStyle(false), mDrawImageBackground(true), // always draw the background mDrawColorBackground(true), // mNeverAnimate is initialised below, in constructor body mIsRenderingOnlySelection(false), mPaginated(aType != eContext_Galley), mCanPaginatedScroll(false), mDoScaledTwips(true), mIsRootPaginatedDocument(false), mPrefBidiDirection(false), mPrefScrollbarSide(0), mPendingSysColorChanged(false), mPendingThemeChanged(false), mPendingUIResolutionChanged(false), mPrefChangePendingNeedsReflow(false), mIsEmulatingMedia(false), mIsGlyph(false), mUsesRootEMUnits(false), mUsesExChUnits(false), mCounterStylesDirty(true), mFontFeatureValuesDirty(true), mSuppressResizeReflow(false), mIsVisual(false), mIsChrome(false), mIsChromeOriginImage(false), mPaintFlashing(false), mPaintFlashingInitialized(false), mHasWarnedAboutPositionedTableParts(false), mHasWarnedAboutTooLargeDashedOrDottedRadius(false), mQuirkSheetAdded(false), mNeedsPrefUpdate(false), mHadNonBlankPaint(false) #ifdef RESTYLE_LOGGING , mRestyleLoggingEnabled(false) #endif #ifdef DEBUG , mInitialized(false) #endif { PodZero(&mBorderWidthTable); #ifdef DEBUG PodZero(&mLayoutPhaseCount); #endif if (!IsDynamic()) { mImageAnimationMode = imgIContainer::kDontAnimMode; mNeverAnimate = true; } else { mImageAnimationMode = imgIContainer::kNormalAnimMode; mNeverAnimate = false; } NS_ASSERTION(mDocument, "Null document"); // if text perf logging enabled, init stats struct if (MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_textperf), LogLevel::Warning)) { mTextPerf = new gfxTextPerfMetrics(); } if (Preferences::GetBool(GFX_MISSING_FONTS_NOTIFY_PREF)) { mMissingFonts = new gfxMissingFontRecorder(); } } void nsPresContext::Destroy() { if (mEventManager) { // unclear if these are needed, but can't hurt mEventManager->NotifyDestroyPresContext(this); mEventManager->SetPresContext(nullptr); mEventManager = nullptr; } // Unregister preference callbacks Preferences::UnregisterPrefixCallback(nsPresContext::PrefChangedCallback, "font.", this); Preferences::UnregisterPrefixCallback(nsPresContext::PrefChangedCallback, "browser.display.", this); Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback, "browser.underline_anchors", this); Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback, "browser.anchor_color", this); Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback, "browser.active_color", this); Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback, "browser.visited_color", this); Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback, "image.animation_mode", this); Preferences::UnregisterPrefixCallback(nsPresContext::PrefChangedCallback, "bidi.", this); Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback, "dom.send_after_paint_to_content", this); Preferences::UnregisterPrefixCallback(nsPresContext::PrefChangedCallback, "gfx.font_rendering.", this); Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback, "layout.css.dpi", this); Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback, "layout.css.devPixelsPerPx", this); Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback, "nglayout.debug.paint_flashing", this); Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback, "nglayout.debug.paint_flashing_chrome", this); Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback, kUseStandinsForNativeColors, this); Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback, "intl.accept_languages", this); mRefreshDriver = nullptr; } nsPresContext::~nsPresContext() { NS_PRECONDITION(!mShell, "Presshell forgot to clear our mShell pointer"); DetachShell(); Destroy(); } NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsPresContext) NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_END NS_IMPL_CYCLE_COLLECTING_ADDREF(nsPresContext) NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(nsPresContext, LastRelease()) void nsPresContext::LastRelease() { if (IsRoot()) { static_cast(this)->CancelAllDidPaintTimers(); } if (mMissingFonts) { mMissingFonts->Clear(); } } NS_IMPL_CYCLE_COLLECTION_CLASS(nsPresContext) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsPresContext) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnimationEventDispatcher); NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument); // NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mDeviceContext); // not xpcom NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEffectCompositor); NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEventManager); // NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mLanguage); // an atom // NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTheme); // a service // NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLangService); // a service NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrintSettings); NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsPresContext) NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnimationEventDispatcher); NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument); NS_IMPL_CYCLE_COLLECTION_UNLINK(mDeviceContext); // worth bothering? NS_IMPL_CYCLE_COLLECTION_UNLINK(mEffectCompositor); // NS_RELEASE(tmp->mLanguage); // an atom // NS_IMPL_CYCLE_COLLECTION_UNLINK(mTheme); // a service // NS_IMPL_CYCLE_COLLECTION_UNLINK(mLangService); // a service NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrintSettings); tmp->Destroy(); NS_IMPL_CYCLE_COLLECTION_UNLINK_END // whether no native theme service exists; // if this gets set to true, we'll stop asking for it. static bool sNoTheme = false; // Set to true when LookAndFeelChanged needs to be called. This is used // because the look and feel is a service, so there's no need to notify it from // more than one prescontext. static bool sLookAndFeelChanged; // Set to true when ThemeChanged needs to be called on mTheme. This is used // because mTheme is a service, so there's no need to notify it from more than // one prescontext. static bool sThemeChanged; void nsPresContext::GetDocumentColorPreferences() { // Make sure the preferences are initialized. In the normal run, // they would already be, because gfxPlatform would have been created, // but in some reference tests, that is not the case. gfxPrefs::GetSingleton(); int32_t useAccessibilityTheme = 0; bool usePrefColors = true; bool isChromeDocShell = false; static int32_t sDocumentColorsSetting; static bool sDocumentColorsSettingPrefCached = false; static bool sUseStandinsForNativeColors = false; if (!sDocumentColorsSettingPrefCached) { sDocumentColorsSettingPrefCached = true; Preferences::AddIntVarCache(&sDocumentColorsSetting, "browser.display.document_color_use", 0); // The preference "ui.use_standins_for_native_colors" also affects // default foreground and background colors. Preferences::AddBoolVarCache(&sUseStandinsForNativeColors, kUseStandinsForNativeColors); } nsIDocument* doc = mDocument->GetDisplayDocument(); if (doc && doc->GetDocShell()) { isChromeDocShell = nsIDocShellTreeItem::typeChrome == doc->GetDocShell()->ItemType(); } else { nsCOMPtr docShell(mContainer); if (docShell) { isChromeDocShell = nsIDocShellTreeItem::typeChrome == docShell->ItemType(); } } mIsChromeOriginImage = mDocument->IsBeingUsedAsImage() && IsChromeURI(mDocument->GetDocumentURI()); if (isChromeDocShell || mIsChromeOriginImage) { usePrefColors = false; } else { useAccessibilityTheme = LookAndFeel::GetInt(LookAndFeel::eIntID_UseAccessibilityTheme, 0); usePrefColors = !useAccessibilityTheme; } if (usePrefColors) { usePrefColors = !Preferences::GetBool("browser.display.use_system_colors", false); } if (sUseStandinsForNativeColors) { // Once the preference "ui.use_standins_for_native_colors" is enabled, // use fixed color values instead of prefered colors and system colors. mDefaultColor = LookAndFeel::GetColorUsingStandins( LookAndFeel::eColorID_windowtext, NS_RGB(0x00, 0x00, 0x00)); mBackgroundColor = LookAndFeel::GetColorUsingStandins( LookAndFeel::eColorID_window, NS_RGB(0xff, 0xff, 0xff)); } else if (usePrefColors) { nsAutoString colorStr; Preferences::GetString("browser.display.foreground_color", colorStr); if (!colorStr.IsEmpty()) { mDefaultColor = MakeColorPref(colorStr); } colorStr.Truncate(); Preferences::GetString("browser.display.background_color", colorStr); if (!colorStr.IsEmpty()) { mBackgroundColor = MakeColorPref(colorStr); } } else { mDefaultColor = LookAndFeel::GetColor( LookAndFeel::eColorID_WindowForeground, NS_RGB(0x00, 0x00, 0x00)); mBackgroundColor = LookAndFeel::GetColor( LookAndFeel::eColorID_WindowBackground, NS_RGB(0xFF, 0xFF, 0xFF)); } // Wherever we got the default background color from, ensure it is // opaque. mBackgroundColor = NS_ComposeColors(NS_RGB(0xFF, 0xFF, 0xFF), mBackgroundColor); // Now deal with the pref: // 0 = default: always, except in high contrast mode // 1 = always // 2 = never if (sDocumentColorsSetting == 1 || mDocument->IsBeingUsedAsImage()) { mUseDocumentColors = true; } else if (sDocumentColorsSetting == 2) { mUseDocumentColors = isChromeDocShell || mIsChromeOriginImage; } else { MOZ_ASSERT( !useAccessibilityTheme || !(isChromeDocShell || mIsChromeOriginImage), "The accessibility theme should only be on for non-chrome"); mUseDocumentColors = !useAccessibilityTheme; } } void nsPresContext::GetUserPreferences() { if (!GetPresShell()) { // No presshell means nothing to do here. We'll do this when we // get a presshell. return; } mAutoQualityMinFontSizePixelsPref = Preferences::GetInt("browser.display.auto_quality_min_font_size"); // * document colors GetDocumentColorPreferences(); mSendAfterPaintToContent = Preferences::GetBool( "dom.send_after_paint_to_content", mSendAfterPaintToContent); // * link colors mUnderlineLinks = Preferences::GetBool("browser.underline_anchors", mUnderlineLinks); nsAutoString colorStr; Preferences::GetString("browser.anchor_color", colorStr); if (!colorStr.IsEmpty()) { mLinkColor = MakeColorPref(colorStr); } colorStr.Truncate(); Preferences::GetString("browser.active_color", colorStr); if (!colorStr.IsEmpty()) { mActiveLinkColor = MakeColorPref(colorStr); } colorStr.Truncate(); Preferences::GetString("browser.visited_color", colorStr); if (!colorStr.IsEmpty()) { mVisitedLinkColor = MakeColorPref(colorStr); } mUseFocusColors = Preferences::GetBool("browser.display.use_focus_colors", mUseFocusColors); mFocusTextColor = mDefaultColor; mFocusBackgroundColor = mBackgroundColor; colorStr.Truncate(); Preferences::GetString("browser.display.focus_text_color", colorStr); if (!colorStr.IsEmpty()) { mFocusTextColor = MakeColorPref(colorStr); } colorStr.Truncate(); Preferences::GetString("browser.display.focus_background_color", colorStr); if (!colorStr.IsEmpty()) { mFocusBackgroundColor = MakeColorPref(colorStr); } mFocusRingWidth = Preferences::GetInt("browser.display.focus_ring_width", mFocusRingWidth); mFocusRingOnAnything = Preferences::GetBool( "browser.display.focus_ring_on_anything", mFocusRingOnAnything); mFocusRingStyle = Preferences::GetInt("browser.display.focus_ring_style", mFocusRingStyle); mBodyTextColor = mDefaultColor; // * use fonts? mUseDocumentFonts = Preferences::GetInt("browser.display.use_document_fonts") != 0; mPrefScrollbarSide = Preferences::GetInt("layout.scrollbar.side"); mLangGroupFontPrefs.Reset(); mFontGroupCacheDirty = true; StaticPresData::Get()->ResetCachedFontPrefs(); // * image animation nsAutoCString animatePref; Preferences::GetCString("image.animation_mode", animatePref); if (animatePref.EqualsLiteral("normal")) mImageAnimationModePref = imgIContainer::kNormalAnimMode; else if (animatePref.EqualsLiteral("none")) mImageAnimationModePref = imgIContainer::kDontAnimMode; else if (animatePref.EqualsLiteral("once")) mImageAnimationModePref = imgIContainer::kLoopOnceAnimMode; else // dynamic change to invalid value should act like it does initially mImageAnimationModePref = imgIContainer::kNormalAnimMode; uint32_t bidiOptions = GetBidi(); int32_t prefInt = Preferences::GetInt(IBMBIDI_TEXTDIRECTION_STR, GET_BIDI_OPTION_DIRECTION(bidiOptions)); SET_BIDI_OPTION_DIRECTION(bidiOptions, prefInt); mPrefBidiDirection = prefInt; prefInt = Preferences::GetInt(IBMBIDI_TEXTTYPE_STR, GET_BIDI_OPTION_TEXTTYPE(bidiOptions)); SET_BIDI_OPTION_TEXTTYPE(bidiOptions, prefInt); prefInt = Preferences::GetInt(IBMBIDI_NUMERAL_STR, GET_BIDI_OPTION_NUMERAL(bidiOptions)); SET_BIDI_OPTION_NUMERAL(bidiOptions, prefInt); // We don't need to force reflow: either we are initializing a new // prescontext or we are being called from UpdateAfterPreferencesChanged() // which triggers a reflow anyway. SetBidi(bidiOptions); } void nsPresContext::InvalidatePaintedLayers() { if (!mShell) return; if (nsIFrame* rootFrame = mShell->GetRootFrame()) { // FrameLayerBuilder caches invalidation-related values that depend on the // appunits-per-dev-pixel ratio, so ensure that all PaintedLayer drawing // is completely flushed. rootFrame->InvalidateFrameSubtree(); } } void nsPresContext::AppUnitsPerDevPixelChanged() { InvalidatePaintedLayers(); if (mDeviceContext) { mDeviceContext->FlushFontCache(); } MediaFeatureValuesChanged({eRestyle_ForceDescendants, NS_STYLE_HINT_REFLOW, MediaFeatureChangeReason::ResolutionChange}); mCurAppUnitsPerDevPixel = AppUnitsPerDevPixel(); } void nsPresContext::PreferenceChanged(const char* aPrefName) { nsDependentCString prefName(aPrefName); if (prefName.EqualsLiteral("layout.css.dpi") || prefName.EqualsLiteral("layout.css.devPixelsPerPx")) { int32_t oldAppUnitsPerDevPixel = AppUnitsPerDevPixel(); if (mDeviceContext->CheckDPIChange() && mShell) { nsCOMPtr shell = mShell; // Re-fetch the view manager's window dimensions in case there's a // deferred resize which hasn't affected our mVisibleArea yet nscoord oldWidthAppUnits, oldHeightAppUnits; RefPtr vm = shell->GetViewManager(); if (!vm) { return; } vm->GetWindowDimensions(&oldWidthAppUnits, &oldHeightAppUnits); float oldWidthDevPixels = oldWidthAppUnits / oldAppUnitsPerDevPixel; float oldHeightDevPixels = oldHeightAppUnits / oldAppUnitsPerDevPixel; nscoord width = NSToCoordRound(oldWidthDevPixels * AppUnitsPerDevPixel()); nscoord height = NSToCoordRound(oldHeightDevPixels * AppUnitsPerDevPixel()); vm->SetWindowDimensions(width, height); AppUnitsPerDevPixelChanged(); } return; } if (prefName.EqualsLiteral(GFX_MISSING_FONTS_NOTIFY_PREF)) { if (Preferences::GetBool(GFX_MISSING_FONTS_NOTIFY_PREF)) { if (!mMissingFonts) { mMissingFonts = new gfxMissingFontRecorder(); // trigger reflow to detect missing fonts on the current page mPrefChangePendingNeedsReflow = true; } } else { if (mMissingFonts) { mMissingFonts->Clear(); } mMissingFonts = nullptr; } } if (StringBeginsWith(prefName, NS_LITERAL_CSTRING("font.")) || prefName.EqualsLiteral("intl.accept_languages")) { // Changes to font family preferences don't change anything in the // computed style data, so the style system won't generate a reflow // hint for us. We need to do that manually. // FIXME We could probably also handle changes to // browser.display.auto_quality_min_font_size here, but that // probably also requires clearing the text run cache, so don't // bother (yet, anyway). mPrefChangePendingNeedsReflow = true; } if (StringBeginsWith(prefName, NS_LITERAL_CSTRING("bidi."))) { // Changes to bidi prefs need to trigger a reflow (see bug 443629) mPrefChangePendingNeedsReflow = true; // Changes to bidi.numeral also needs to empty the text run cache. // This is handled in gfxTextRunWordCache.cpp. } if (StringBeginsWith(prefName, NS_LITERAL_CSTRING("gfx.font_rendering."))) { // Changes to font_rendering prefs need to trigger a reflow mPrefChangePendingNeedsReflow = true; } // We will end up calling InvalidatePreferenceSheets one from each pres // context, but all it's doing is clearing its cached sheet pointers, so it // won't be wastefully recreating the sheet multiple times. // // The first pres context that has its pref changed runnable called will // be the one to cause the reconstruction of the pref style sheet. nsLayoutStylesheetCache::InvalidatePreferenceSheets(); DispatchPrefChangedRunnableIfNeeded(); if (prefName.EqualsLiteral("nglayout.debug.paint_flashing") || prefName.EqualsLiteral("nglayout.debug.paint_flashing_chrome")) { mPaintFlashingInitialized = false; return; } } void nsPresContext::DispatchPrefChangedRunnableIfNeeded() { if (mPostedPrefChangedRunnable) { return; } nsCOMPtr runnable = NewRunnableMethod("nsPresContext::UpdateAfterPreferencesChanged", this, &nsPresContext::UpdateAfterPreferencesChanged); nsresult rv = Document()->Dispatch(TaskCategory::Other, runnable.forget()); if (NS_SUCCEEDED(rv)) { mPostedPrefChangedRunnable = true; } } void nsPresContext::UpdateAfterPreferencesChanged() { mPostedPrefChangedRunnable = false; if (!mShell) { return; } if (!mContainer) { // Delay updating until there is a container mNeedsPrefUpdate = true; return; } nsCOMPtr docShell(mContainer); if (docShell && nsIDocShellTreeItem::typeChrome == docShell->ItemType()) { return; } // Initialize our state from the user preferences GetUserPreferences(); // update the presShell: tell it to set the preference style rules up mShell->UpdatePreferenceStyles(); InvalidatePaintedLayers(); mDeviceContext->FlushFontCache(); nsChangeHint hint = nsChangeHint(0); if (mPrefChangePendingNeedsReflow) { hint |= NS_STYLE_HINT_REFLOW; } // Preferences require rerunning selector matching because we rebuild // the pref style sheet for some preference changes. RebuildAllStyleData(hint, eRestyle_Subtree); } nsresult nsPresContext::Init(nsDeviceContext* aDeviceContext) { NS_ASSERTION(!mInitialized, "attempt to reinit pres context"); NS_ENSURE_ARG(aDeviceContext); mDeviceContext = aDeviceContext; // In certain rare cases (such as changing page mode), we tear down layout // state and re-initialize a new prescontext for a document. Given that we // hang style state off the DOM, we detect that re-initialization case and // lazily drop the servo data. We don't do this eagerly during layout teardown // because that would incur an extra whole-tree traversal that's unnecessary // most of the time. // // FIXME(emilio): I'm pretty sure this doesn't happen after bug 1414999. if (mDocument->IsStyledByServo()) { Element* root = mDocument->GetRootElement(); if (root && root->HasServoData()) { ServoRestyleManager::ClearServoDataFromSubtree(root); } } if (mDeviceContext->SetFullZoom(mFullZoom)) mDeviceContext->FlushFontCache(); mCurAppUnitsPerDevPixel = AppUnitsPerDevPixel(); mEventManager = new mozilla::EventStateManager(); mAnimationEventDispatcher = new mozilla::AnimationEventDispatcher(this); mEffectCompositor = new mozilla::EffectCompositor(this); mTransitionManager = new nsTransitionManager(this); mAnimationManager = new nsAnimationManager(this); if (mDocument->GetDisplayDocument()) { NS_ASSERTION(mDocument->GetDisplayDocument()->GetPresContext(), "Why are we being initialized?"); mRefreshDriver = mDocument->GetDisplayDocument()->GetPresContext()->RefreshDriver(); } else { nsIDocument* parent = mDocument->GetParentDocument(); // Unfortunately, sometimes |parent| here has no presshell because // printing screws up things. Assert that in other cases it does, // but whenever the shell is null just fall back on using our own // refresh driver. NS_ASSERTION(!parent || mDocument->IsStaticDocument() || parent->GetShell(), "How did we end up with a presshell if our parent doesn't " "have one?"); if (parent && parent->GetPresContext()) { // We don't have our container set yet at this point nsCOMPtr ourItem = mDocument->GetDocShell(); if (ourItem) { nsCOMPtr parentItem; ourItem->GetSameTypeParent(getter_AddRefs(parentItem)); if (parentItem) { Element* containingElement = parent->FindContentForSubDocument(mDocument); if (!containingElement->IsXULElement() || !containingElement->HasAttr(kNameSpaceID_None, nsGkAtoms::forceOwnRefreshDriver)) { mRefreshDriver = parent->GetPresContext()->RefreshDriver(); } } } } if (!mRefreshDriver) { mRefreshDriver = new nsRefreshDriver(this); } } mLangService = nsLanguageAtomService::GetService(); // Register callbacks so we're notified when the preferences change Preferences::RegisterPrefixCallback(nsPresContext::PrefChangedCallback, "font.", this); Preferences::RegisterPrefixCallback(nsPresContext::PrefChangedCallback, "browser.display.", this); Preferences::RegisterCallback(nsPresContext::PrefChangedCallback, "browser.underline_anchors", this); Preferences::RegisterCallback(nsPresContext::PrefChangedCallback, "browser.anchor_color", this); Preferences::RegisterCallback(nsPresContext::PrefChangedCallback, "browser.active_color", this); Preferences::RegisterCallback(nsPresContext::PrefChangedCallback, "browser.visited_color", this); Preferences::RegisterCallback(nsPresContext::PrefChangedCallback, "image.animation_mode", this); Preferences::RegisterPrefixCallback(nsPresContext::PrefChangedCallback, "bidi.", this); Preferences::RegisterCallback(nsPresContext::PrefChangedCallback, "dom.send_after_paint_to_content", this); Preferences::RegisterPrefixCallback(nsPresContext::PrefChangedCallback, "gfx.font_rendering.", this); Preferences::RegisterCallback(nsPresContext::PrefChangedCallback, "layout.css.dpi", this); Preferences::RegisterCallback(nsPresContext::PrefChangedCallback, "layout.css.devPixelsPerPx", this); Preferences::RegisterCallback(nsPresContext::PrefChangedCallback, "nglayout.debug.paint_flashing", this); Preferences::RegisterCallback(nsPresContext::PrefChangedCallback, "nglayout.debug.paint_flashing_chrome", this); Preferences::RegisterCallback(nsPresContext::PrefChangedCallback, kUseStandinsForNativeColors, this); Preferences::RegisterCallback(nsPresContext::PrefChangedCallback, "intl.accept_languages", this); nsresult rv = mEventManager->Init(); NS_ENSURE_SUCCESS(rv, rv); mEventManager->SetPresContext(this); #ifdef RESTYLE_LOGGING mRestyleLoggingEnabled = GeckoRestyleManager::RestyleLoggingInitiallyEnabled(); #endif #ifdef DEBUG mInitialized = true; #endif return NS_OK; } // Note: We don't hold a reference on the shell; it has a reference to // us void nsPresContext::AttachShell(nsIPresShell* aShell, StyleBackendType aBackendType) { MOZ_ASSERT(!mShell); mShell = aShell; if (aBackendType == StyleBackendType::Servo) { mRestyleManager = new ServoRestyleManager(this); } else { #ifdef MOZ_OLD_STYLE mRestyleManager = new GeckoRestyleManager(this); #else MOZ_CRASH("old style system disabled"); #endif } // Since CounterStyleManager is also the name of a method of // nsPresContext, it is necessary to prefix the class with the mozilla // namespace here. mCounterStyleManager = new mozilla::CounterStyleManager(this); nsIDocument* doc = mShell->GetDocument(); NS_ASSERTION(doc, "expect document here"); if (doc) { // Have to update PresContext's mDocument before calling any other methods. mDocument = doc; } // Initialize our state from the user preferences, now that we // have a presshell, and hence a document. GetUserPreferences(); if (doc) { nsIURI* docURI = doc->GetDocumentURI(); if (IsDynamic() && docURI) { bool isChrome = false; bool isRes = false; docURI->SchemeIs("chrome", &isChrome); docURI->SchemeIs("resource", &isRes); if (!isChrome && !isRes) mImageAnimationMode = mImageAnimationModePref; else mImageAnimationMode = imgIContainer::kNormalAnimMode; } UpdateCharSet(doc->GetDocumentCharacterSet()); } } void nsPresContext::DetachShell() { // The counter style manager's destructor needs to deallocate with the // presshell arena. Disconnect it before nulling out the shell. // // XXXbholley: Given recent refactorings, it probably makes more sense to // just null our mShell at the bottom of this function. I'm leaving it // this way to preserve the old ordering, but I doubt anything would break. if (mCounterStyleManager) { mCounterStyleManager->Disconnect(); mCounterStyleManager = nullptr; } mShell = nullptr; if (mAnimationEventDispatcher) { mAnimationEventDispatcher->Disconnect(); mAnimationEventDispatcher = nullptr; } if (mEffectCompositor) { mEffectCompositor->Disconnect(); mEffectCompositor = nullptr; } if (mTransitionManager) { mTransitionManager->Disconnect(); mTransitionManager = nullptr; } if (mAnimationManager) { mAnimationManager->Disconnect(); mAnimationManager = nullptr; } if (mRestyleManager) { mRestyleManager->Disconnect(); mRestyleManager = nullptr; } if (mRefreshDriver && mRefreshDriver->GetPresContext() == this) { mRefreshDriver->Disconnect(); // Can't null out the refresh driver here. } if (IsRoot()) { nsRootPresContext* thisRoot = static_cast(this); // Have to cancel our plugin geometry timer, because the // callback for that depends on a non-null presshell. thisRoot->CancelApplyPluginGeometryTimer(); // The did-paint timer also depends on a non-null pres shell. thisRoot->CancelAllDidPaintTimers(); } } void nsPresContext::DoChangeCharSet(NotNull aCharSet) { UpdateCharSet(aCharSet); mDeviceContext->FlushFontCache(); // In Stylo, if a document contains one or more