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 "mozilla/ArrayUtils.h"
12 #include "mozilla/DebugOnly.h"
13 #include "mozilla/Encoding.h"
14 #include "mozilla/EventDispatcher.h"
15 #include "mozilla/EventStateManager.h"
16 
17 #include "base/basictypes.h"
18 
19 #include "nsCOMPtr.h"
20 #include "nsCSSFrameConstructor.h"
21 #include "nsIPresShell.h"
22 #include "nsIPresShellInlines.h"
23 #include "nsDocShell.h"
24 #include "nsIContentViewer.h"
25 #include "nsPIDOMWindow.h"
26 #include "mozilla/StyleSetHandle.h"
27 #include "mozilla/StyleSetHandleInlines.h"
28 #include "nsIContent.h"
29 #include "nsIFrame.h"
30 #include "nsIDocument.h"
31 #include "nsIDocumentInlines.h"
32 #include "nsIPrintSettings.h"
33 #include "nsLanguageAtomService.h"
34 #include "mozilla/LookAndFeel.h"
35 #include "nsIInterfaceRequestorUtils.h"
36 #include "nsHTMLDocument.h"
37 #include "nsIWeakReferenceUtils.h"
38 #include "nsThreadUtils.h"
39 #include "nsLayoutUtils.h"
40 #include "nsViewManager.h"
41 #ifdef MOZ_OLD_STYLE
42 #include "mozilla/GeckoRestyleManager.h"
43 #endif
44 #include "mozilla/RestyleManager.h"
45 #include "mozilla/RestyleManagerInlines.h"
46 #include "SurfaceCacheUtils.h"
47 #include "nsMediaFeatures.h"
48 #ifdef MOZ_OLD_STYLE
49 #include "nsRuleNode.h"
50 #endif
51 #include "gfxPlatform.h"
52 #ifdef MOZ_OLD_STYLE
53 #include "nsCSSRules.h"
54 #endif
55 #include "nsFontFaceLoader.h"
56 #include "mozilla/AnimationEventDispatcher.h"
57 #include "mozilla/EffectCompositor.h"
58 #include "mozilla/EventListenerManager.h"
59 #include "prenv.h"
60 #include "nsPluginFrame.h"
61 #include "nsTransitionManager.h"
62 #include "nsAnimationManager.h"
63 #include "CounterStyleManager.h"
64 #include "mozilla/MemoryReporting.h"
65 #include "mozilla/dom/Element.h"
66 #include "nsIMessageManager.h"
67 #include "mozilla/dom/HTMLBodyElement.h"
68 #include "mozilla/dom/MediaQueryList.h"
69 #include "nsSMILAnimationController.h"
70 #include "mozilla/css/ImageLoader.h"
71 #include "mozilla/dom/PBrowserParent.h"
72 #include "mozilla/dom/TabChild.h"
73 #include "mozilla/dom/TabParent.h"
74 #include "nsRefreshDriver.h"
75 #include "Layers.h"
76 #include "LayerUserData.h"
77 #include "ClientLayerManager.h"
78 #include "mozilla/dom/NotifyPaintEvent.h"
79 #include "gfxPrefs.h"
80 #include "nsIDOMChromeWindow.h"
81 #include "nsFrameLoader.h"
82 #include "mozilla/dom/FontFaceSet.h"
83 #include "nsContentUtils.h"
84 #include "nsPIWindowRoot.h"
85 #include "mozilla/Preferences.h"
86 #include "gfxTextRun.h"
87 #include "nsFontFaceUtils.h"
88 #include "nsLayoutStylesheetCache.h"
89 #include "mozilla/ServoBindings.h"
90 #include "mozilla/StyleSheet.h"
91 #include "mozilla/StyleSheetInlines.h"
92 #include "mozilla/Telemetry.h"
93 #include "mozilla/dom/Performance.h"
94 #include "mozilla/dom/PerformanceTiming.h"
95 #include "mozilla/layers/APZThreadUtils.h"
96 
97 // Needed for Start/Stop of Image Animation
98 #include "imgIContainer.h"
99 #include "nsIImageLoadingContent.h"
100 
101 #include "nsCSSParser.h"
102 #include "nsBidiUtils.h"
103 #include "nsServiceManagerUtils.h"
104 #include "nsBidi.h"
105 
106 #include "mozilla/dom/URL.h"
107 #include "mozilla/ServoCSSParser.h"
108 
109 using namespace mozilla;
110 using namespace mozilla::dom;
111 using namespace mozilla::gfx;
112 using namespace mozilla::layers;
113 
114 uint8_t gNotifySubDocInvalidationData;
115 
116 // This preference was first introduced in Bug 232227, in order to prevent
117 // system colors from being exposed to CSS or canvas.
118 constexpr char kUseStandinsForNativeColors[] =
119     "ui.use_standins_for_native_colors";
120 
121 /**
122  * Layer UserData for ContainerLayers that want to be notified
123  * of local invalidations of them and their descendant layers.
124  * Pass a callback to ComputeDifferences to have these called.
125  */
126 class ContainerLayerPresContext : public LayerUserData {
127  public:
128   nsPresContext* mPresContext;
129 };
130 
131 #ifdef MOZ_OLD_STYLE
132 
133 namespace {
134 
135 class CharSetChangingRunnable : public Runnable {
136  public:
CharSetChangingRunnable(nsPresContext * aPresContext,NotNull<const Encoding * > aCharSet)137   CharSetChangingRunnable(nsPresContext* aPresContext,
138                           NotNull<const Encoding*> aCharSet)
139       : Runnable("CharSetChangingRunnable"),
140         mPresContext(aPresContext),
141         mCharSet(aCharSet) {}
142 
Run()143   NS_IMETHOD Run() override {
144     mPresContext->DoChangeCharSet(mCharSet);
145     return NS_OK;
146   }
147 
148  private:
149   RefPtr<nsPresContext> mPresContext;
150   NotNull<const Encoding*> mCharSet;
151 };
152 
153 }  // namespace
154 
155 #endif
156 
MakeColorPref(const nsString & aColor)157 nscolor nsPresContext::MakeColorPref(const nsString& aColor) {
158   bool ok;
159   nscolor result;
160 
161   ServoStyleSet* servoStyleSet =
162       mShell && mShell->StyleSet() ? mShell->StyleSet()->GetAsServo() : nullptr;
163 
164   bool useServoParser =
165 #ifdef MOZ_OLD_STYLE
166       servoStyleSet;
167 #else
168       true;
169 #endif
170 
171   if (useServoParser) {
172     ok = ServoCSSParser::ComputeColor(servoStyleSet, NS_RGB(0, 0, 0), aColor,
173                                       &result);
174   } else {
175 #ifdef MOZ_OLD_STYLE
176     nsCSSParser parser;
177     nsCSSValue value;
178     ok = parser.ParseColorString(aColor, nullptr, 0, value) &&
179          nsRuleNode::ComputeColor(value, this, nullptr, result);
180 #else
181     MOZ_CRASH("old style system disabled");
182 #endif
183   }
184 
185   if (!ok) {
186     // Any better choices?
187     result = NS_RGB(0, 0, 0);
188   }
189 
190   return result;
191 }
192 
IsDOMPaintEventPending()193 bool nsPresContext::IsDOMPaintEventPending() {
194   if (!mTransactions.IsEmpty()) {
195     return true;
196   }
197   nsRootPresContext* drpc = GetRootPresContext();
198   if (drpc && drpc->mRefreshDriver->ViewManagerFlushIsPending()) {
199     // Since we're promising that there will be a MozAfterPaint event
200     // fired, we record an empty invalidation in case display list
201     // invalidation doesn't invalidate anything further.
202     NotifyInvalidation(drpc->mRefreshDriver->LastTransactionId() + 1,
203                        nsRect(0, 0, 0, 0));
204     return true;
205   }
206   return false;
207 }
208 
PrefChangedCallback(const char * aPrefName,void * instance_data)209 void nsPresContext::PrefChangedCallback(const char* aPrefName,
210                                         void* instance_data) {
211   RefPtr<nsPresContext> presContext =
212       static_cast<nsPresContext*>(instance_data);
213 
214   NS_ASSERTION(presContext, "bad instance data");
215   if (presContext) {
216     presContext->PreferenceChanged(aPrefName);
217   }
218 }
219 
ForceReflowForFontInfoUpdate()220 void nsPresContext::ForceReflowForFontInfoUpdate() {
221   // We can trigger reflow by pretending a font.* preference has changed;
222   // this is the same mechanism as gfxPlatform::ForceGlobalReflow() uses
223   // if new fonts are installed during the session, for example.
224   PreferenceChanged("font.internaluseonly.changed");
225 }
226 
IsVisualCharset(NotNull<const Encoding * > aCharset)227 static bool IsVisualCharset(NotNull<const Encoding*> aCharset) {
228   return aCharset == ISO_8859_8_ENCODING;
229 }
230 
nsPresContext(nsIDocument * aDocument,nsPresContextType aType)231 nsPresContext::nsPresContext(nsIDocument* aDocument, nsPresContextType aType)
232     : mType(aType),
233       mShell(nullptr),
234       mDocument(aDocument),
235       mMedium(aType == eContext_Galley ? nsGkAtoms::screen : nsGkAtoms::print),
236       mMediaEmulated(mMedium),
237       mLinkHandler(nullptr),
238       mInflationDisabledForShrinkWrap(false),
239       mBaseMinFontSize(0),
240       mSystemFontScale(1.0),
241       mTextZoom(1.0),
242       mEffectiveTextZoom(1.0),
243       mFullZoom(1.0),
244       mOverrideDPPX(0.0),
245       mLastFontInflationScreenSize(gfxSize(-1.0, -1.0)),
246       mCurAppUnitsPerDevPixel(0),
247       mAutoQualityMinFontSizePixelsPref(0),
248       // origin nscoord_MIN is impossible, so the first ResizeReflow always
249       // fires
250       mLastResizeEventVisibleArea(nsRect(nscoord_MIN, nscoord_MIN,
251                                          NS_UNCONSTRAINEDSIZE,
252                                          NS_UNCONSTRAINEDSIZE)),
253       mPageSize(-1, -1),
254       mPageScale(0.0),
255       mPPScale(1.0f),
256       mDefaultColor(NS_RGBA(0, 0, 0, 0)),
257       mBackgroundColor(NS_RGB(0xFF, 0xFF, 0xFF)),
258       mLinkColor(NS_RGB(0x00, 0x00, 0xEE)),
259       mActiveLinkColor(NS_RGB(0xEE, 0x00, 0x00)),
260       mVisitedLinkColor(NS_RGB(0x55, 0x1A, 0x8B)),
261       mFocusBackgroundColor(mBackgroundColor),
262       mFocusTextColor(mDefaultColor),
263       mBodyTextColor(mDefaultColor),
264       mViewportScrollbarOverrideElement(nullptr),
265       mViewportStyleScrollbar(NS_STYLE_OVERFLOW_AUTO, NS_STYLE_OVERFLOW_AUTO),
266       mFocusRingWidth(1),
267       mExistThrottledUpdates(false),
268       // mImageAnimationMode is initialised below, in constructor body
269       mImageAnimationModePref(imgIContainer::kNormalAnimMode),
270       mFontGroupCacheDirty(true),
271       mInterruptChecksToSkip(0),
272       mElementsRestyled(0),
273       mFramesConstructed(0),
274       mFramesReflowed(0),
275       mInteractionTimeEnabled(true),
276       mTelemetryScrollLastY(0),
277       mTelemetryScrollMaxY(0),
278       mTelemetryScrollTotalY(0),
279       mHasPendingInterrupt(false),
280       mPendingInterruptFromTest(false),
281       mInterruptsEnabled(false),
282       mUseDocumentFonts(true),
283       mUseDocumentColors(true),
284       mUnderlineLinks(true),
285       mSendAfterPaintToContent(false),
286       mUseFocusColors(false),
287       mFocusRingOnAnything(false),
288       mFocusRingStyle(false),
289       mDrawImageBackground(true),  // always draw the background
290       mDrawColorBackground(true),
291       // mNeverAnimate is initialised below, in constructor body
292       mIsRenderingOnlySelection(false),
293       mPaginated(aType != eContext_Galley),
294       mCanPaginatedScroll(false),
295       mDoScaledTwips(true),
296       mIsRootPaginatedDocument(false),
297       mPrefBidiDirection(false),
298       mPrefScrollbarSide(0),
299       mPendingSysColorChanged(false),
300       mPendingThemeChanged(false),
301       mPendingUIResolutionChanged(false),
302       mPrefChangePendingNeedsReflow(false),
303       mIsEmulatingMedia(false),
304       mIsGlyph(false),
305       mUsesRootEMUnits(false),
306       mUsesExChUnits(false),
307       mCounterStylesDirty(true),
308       mFontFeatureValuesDirty(true),
309       mSuppressResizeReflow(false),
310       mIsVisual(false),
311       mIsChrome(false),
312       mIsChromeOriginImage(false),
313       mPaintFlashing(false),
314       mPaintFlashingInitialized(false),
315       mHasWarnedAboutPositionedTableParts(false),
316       mHasWarnedAboutTooLargeDashedOrDottedRadius(false),
317       mQuirkSheetAdded(false),
318       mNeedsPrefUpdate(false),
319       mHadNonBlankPaint(false)
320 #ifdef RESTYLE_LOGGING
321       ,
322       mRestyleLoggingEnabled(false)
323 #endif
324 #ifdef DEBUG
325       ,
326       mInitialized(false)
327 #endif
328 {
329   PodZero(&mBorderWidthTable);
330 #ifdef DEBUG
331   PodZero(&mLayoutPhaseCount);
332 #endif
333 
334   if (!IsDynamic()) {
335     mImageAnimationMode = imgIContainer::kDontAnimMode;
336     mNeverAnimate = true;
337   } else {
338     mImageAnimationMode = imgIContainer::kNormalAnimMode;
339     mNeverAnimate = false;
340   }
341   NS_ASSERTION(mDocument, "Null document");
342 
343   // if text perf logging enabled, init stats struct
344   if (MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_textperf), LogLevel::Warning)) {
345     mTextPerf = new gfxTextPerfMetrics();
346   }
347 
348   if (Preferences::GetBool(GFX_MISSING_FONTS_NOTIFY_PREF)) {
349     mMissingFonts = new gfxMissingFontRecorder();
350   }
351 }
352 
Destroy()353 void nsPresContext::Destroy() {
354   if (mEventManager) {
355     // unclear if these are needed, but can't hurt
356     mEventManager->NotifyDestroyPresContext(this);
357     mEventManager->SetPresContext(nullptr);
358     mEventManager = nullptr;
359   }
360 
361   // Unregister preference callbacks
362   Preferences::UnregisterPrefixCallback(nsPresContext::PrefChangedCallback,
363                                         "font.", this);
364   Preferences::UnregisterPrefixCallback(nsPresContext::PrefChangedCallback,
365                                         "browser.display.", this);
366   Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
367                                   "browser.underline_anchors", this);
368   Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
369                                   "browser.anchor_color", this);
370   Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
371                                   "browser.active_color", this);
372   Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
373                                   "browser.visited_color", this);
374   Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
375                                   "image.animation_mode", this);
376   Preferences::UnregisterPrefixCallback(nsPresContext::PrefChangedCallback,
377                                         "bidi.", this);
378   Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
379                                   "dom.send_after_paint_to_content", this);
380   Preferences::UnregisterPrefixCallback(nsPresContext::PrefChangedCallback,
381                                         "gfx.font_rendering.", this);
382   Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
383                                   "layout.css.dpi", this);
384   Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
385                                   "layout.css.devPixelsPerPx", this);
386   Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
387                                   "nglayout.debug.paint_flashing", this);
388   Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
389                                   "nglayout.debug.paint_flashing_chrome", this);
390   Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
391                                   kUseStandinsForNativeColors, this);
392   Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
393                                   "intl.accept_languages", this);
394 
395   mRefreshDriver = nullptr;
396 }
397 
~nsPresContext()398 nsPresContext::~nsPresContext() {
399   NS_PRECONDITION(!mShell, "Presshell forgot to clear our mShell pointer");
400   DetachShell();
401 
402   Destroy();
403 }
404 
405 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsPresContext)
NS_INTERFACE_MAP_ENTRY(nsISupports)406   NS_INTERFACE_MAP_ENTRY(nsISupports)
407 NS_INTERFACE_MAP_END
408 
409 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsPresContext)
410 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(nsPresContext, LastRelease())
411 
412 void nsPresContext::LastRelease() {
413   if (IsRoot()) {
414     static_cast<nsRootPresContext*>(this)->CancelAllDidPaintTimers();
415   }
416   if (mMissingFonts) {
417     mMissingFonts->Clear();
418   }
419 }
420 
421 NS_IMPL_CYCLE_COLLECTION_CLASS(nsPresContext)
422 
423 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsPresContext)
424   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnimationEventDispatcher);
425   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument);
426   // NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mDeviceContext); // not xpcom
427   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEffectCompositor);
428   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEventManager);
429   // NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mLanguage); // an atom
430 
431   // NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTheme); // a service
432   // NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLangService); // a service
433   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrintSettings);
434 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
435 
436 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsPresContext)
437   NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnimationEventDispatcher);
438   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument);
439   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDeviceContext);  // worth bothering?
440   NS_IMPL_CYCLE_COLLECTION_UNLINK(mEffectCompositor);
441   // NS_RELEASE(tmp->mLanguage); // an atom
442   // NS_IMPL_CYCLE_COLLECTION_UNLINK(mTheme); // a service
443   // NS_IMPL_CYCLE_COLLECTION_UNLINK(mLangService); // a service
444   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrintSettings);
445 
446   tmp->Destroy();
447 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
448 
449 // whether no native theme service exists;
450 // if this gets set to true, we'll stop asking for it.
451 static bool sNoTheme = false;
452 
453 // Set to true when LookAndFeelChanged needs to be called.  This is used
454 // because the look and feel is a service, so there's no need to notify it from
455 // more than one prescontext.
456 static bool sLookAndFeelChanged;
457 
458 // Set to true when ThemeChanged needs to be called on mTheme.  This is used
459 // because mTheme is a service, so there's no need to notify it from more than
460 // one prescontext.
461 static bool sThemeChanged;
462 
GetDocumentColorPreferences()463 void nsPresContext::GetDocumentColorPreferences() {
464   // Make sure the preferences are initialized.  In the normal run,
465   // they would already be, because gfxPlatform would have been created,
466   // but in some reference tests, that is not the case.
467   gfxPrefs::GetSingleton();
468 
469   int32_t useAccessibilityTheme = 0;
470   bool usePrefColors = true;
471   bool isChromeDocShell = false;
472   static int32_t sDocumentColorsSetting;
473   static bool sDocumentColorsSettingPrefCached = false;
474   static bool sUseStandinsForNativeColors = false;
475   if (!sDocumentColorsSettingPrefCached) {
476     sDocumentColorsSettingPrefCached = true;
477     Preferences::AddIntVarCache(&sDocumentColorsSetting,
478                                 "browser.display.document_color_use", 0);
479 
480     // The preference "ui.use_standins_for_native_colors" also affects
481     // default foreground and background colors.
482     Preferences::AddBoolVarCache(&sUseStandinsForNativeColors,
483                                  kUseStandinsForNativeColors);
484   }
485 
486   nsIDocument* doc = mDocument->GetDisplayDocument();
487   if (doc && doc->GetDocShell()) {
488     isChromeDocShell =
489         nsIDocShellTreeItem::typeChrome == doc->GetDocShell()->ItemType();
490   } else {
491     nsCOMPtr<nsIDocShellTreeItem> docShell(mContainer);
492     if (docShell) {
493       isChromeDocShell =
494           nsIDocShellTreeItem::typeChrome == docShell->ItemType();
495     }
496   }
497 
498   mIsChromeOriginImage = mDocument->IsBeingUsedAsImage() &&
499                          IsChromeURI(mDocument->GetDocumentURI());
500 
501   if (isChromeDocShell || mIsChromeOriginImage) {
502     usePrefColors = false;
503   } else {
504     useAccessibilityTheme =
505         LookAndFeel::GetInt(LookAndFeel::eIntID_UseAccessibilityTheme, 0);
506     usePrefColors = !useAccessibilityTheme;
507   }
508   if (usePrefColors) {
509     usePrefColors =
510         !Preferences::GetBool("browser.display.use_system_colors", false);
511   }
512 
513   if (sUseStandinsForNativeColors) {
514     // Once the preference "ui.use_standins_for_native_colors" is enabled,
515     // use fixed color values instead of prefered colors and system colors.
516     mDefaultColor = LookAndFeel::GetColorUsingStandins(
517         LookAndFeel::eColorID_windowtext, NS_RGB(0x00, 0x00, 0x00));
518     mBackgroundColor = LookAndFeel::GetColorUsingStandins(
519         LookAndFeel::eColorID_window, NS_RGB(0xff, 0xff, 0xff));
520   } else if (usePrefColors) {
521     nsAutoString colorStr;
522     Preferences::GetString("browser.display.foreground_color", colorStr);
523     if (!colorStr.IsEmpty()) {
524       mDefaultColor = MakeColorPref(colorStr);
525     }
526 
527     colorStr.Truncate();
528     Preferences::GetString("browser.display.background_color", colorStr);
529     if (!colorStr.IsEmpty()) {
530       mBackgroundColor = MakeColorPref(colorStr);
531     }
532   } else {
533     mDefaultColor = LookAndFeel::GetColor(
534         LookAndFeel::eColorID_WindowForeground, NS_RGB(0x00, 0x00, 0x00));
535     mBackgroundColor = LookAndFeel::GetColor(
536         LookAndFeel::eColorID_WindowBackground, NS_RGB(0xFF, 0xFF, 0xFF));
537   }
538 
539   // Wherever we got the default background color from, ensure it is
540   // opaque.
541   mBackgroundColor =
542       NS_ComposeColors(NS_RGB(0xFF, 0xFF, 0xFF), mBackgroundColor);
543 
544   // Now deal with the pref:
545   // 0 = default: always, except in high contrast mode
546   // 1 = always
547   // 2 = never
548   if (sDocumentColorsSetting == 1 || mDocument->IsBeingUsedAsImage()) {
549     mUseDocumentColors = true;
550   } else if (sDocumentColorsSetting == 2) {
551     mUseDocumentColors = isChromeDocShell || mIsChromeOriginImage;
552   } else {
553     MOZ_ASSERT(
554         !useAccessibilityTheme || !(isChromeDocShell || mIsChromeOriginImage),
555         "The accessibility theme should only be on for non-chrome");
556     mUseDocumentColors = !useAccessibilityTheme;
557   }
558 }
559 
GetUserPreferences()560 void nsPresContext::GetUserPreferences() {
561   if (!GetPresShell()) {
562     // No presshell means nothing to do here.  We'll do this when we
563     // get a presshell.
564     return;
565   }
566 
567   mAutoQualityMinFontSizePixelsPref =
568       Preferences::GetInt("browser.display.auto_quality_min_font_size");
569 
570   // * document colors
571   GetDocumentColorPreferences();
572 
573   mSendAfterPaintToContent = Preferences::GetBool(
574       "dom.send_after_paint_to_content", mSendAfterPaintToContent);
575 
576   // * link colors
577   mUnderlineLinks =
578       Preferences::GetBool("browser.underline_anchors", mUnderlineLinks);
579 
580   nsAutoString colorStr;
581   Preferences::GetString("browser.anchor_color", colorStr);
582   if (!colorStr.IsEmpty()) {
583     mLinkColor = MakeColorPref(colorStr);
584   }
585 
586   colorStr.Truncate();
587   Preferences::GetString("browser.active_color", colorStr);
588   if (!colorStr.IsEmpty()) {
589     mActiveLinkColor = MakeColorPref(colorStr);
590   }
591 
592   colorStr.Truncate();
593   Preferences::GetString("browser.visited_color", colorStr);
594   if (!colorStr.IsEmpty()) {
595     mVisitedLinkColor = MakeColorPref(colorStr);
596   }
597 
598   mUseFocusColors =
599       Preferences::GetBool("browser.display.use_focus_colors", mUseFocusColors);
600 
601   mFocusTextColor = mDefaultColor;
602   mFocusBackgroundColor = mBackgroundColor;
603 
604   colorStr.Truncate();
605   Preferences::GetString("browser.display.focus_text_color", colorStr);
606   if (!colorStr.IsEmpty()) {
607     mFocusTextColor = MakeColorPref(colorStr);
608   }
609 
610   colorStr.Truncate();
611   Preferences::GetString("browser.display.focus_background_color", colorStr);
612   if (!colorStr.IsEmpty()) {
613     mFocusBackgroundColor = MakeColorPref(colorStr);
614   }
615 
616   mFocusRingWidth =
617       Preferences::GetInt("browser.display.focus_ring_width", mFocusRingWidth);
618 
619   mFocusRingOnAnything = Preferences::GetBool(
620       "browser.display.focus_ring_on_anything", mFocusRingOnAnything);
621 
622   mFocusRingStyle =
623       Preferences::GetInt("browser.display.focus_ring_style", mFocusRingStyle);
624 
625   mBodyTextColor = mDefaultColor;
626 
627   // * use fonts?
628   mUseDocumentFonts =
629       Preferences::GetInt("browser.display.use_document_fonts") != 0;
630 
631   mPrefScrollbarSide = Preferences::GetInt("layout.scrollbar.side");
632 
633   mLangGroupFontPrefs.Reset();
634   mFontGroupCacheDirty = true;
635   StaticPresData::Get()->ResetCachedFontPrefs();
636 
637   // * image animation
638   nsAutoCString animatePref;
639   Preferences::GetCString("image.animation_mode", animatePref);
640   if (animatePref.EqualsLiteral("normal"))
641     mImageAnimationModePref = imgIContainer::kNormalAnimMode;
642   else if (animatePref.EqualsLiteral("none"))
643     mImageAnimationModePref = imgIContainer::kDontAnimMode;
644   else if (animatePref.EqualsLiteral("once"))
645     mImageAnimationModePref = imgIContainer::kLoopOnceAnimMode;
646   else  // dynamic change to invalid value should act like it does initially
647     mImageAnimationModePref = imgIContainer::kNormalAnimMode;
648 
649   uint32_t bidiOptions = GetBidi();
650 
651   int32_t prefInt = Preferences::GetInt(IBMBIDI_TEXTDIRECTION_STR,
652                                         GET_BIDI_OPTION_DIRECTION(bidiOptions));
653   SET_BIDI_OPTION_DIRECTION(bidiOptions, prefInt);
654   mPrefBidiDirection = prefInt;
655 
656   prefInt = Preferences::GetInt(IBMBIDI_TEXTTYPE_STR,
657                                 GET_BIDI_OPTION_TEXTTYPE(bidiOptions));
658   SET_BIDI_OPTION_TEXTTYPE(bidiOptions, prefInt);
659 
660   prefInt = Preferences::GetInt(IBMBIDI_NUMERAL_STR,
661                                 GET_BIDI_OPTION_NUMERAL(bidiOptions));
662   SET_BIDI_OPTION_NUMERAL(bidiOptions, prefInt);
663 
664   // We don't need to force reflow: either we are initializing a new
665   // prescontext or we are being called from UpdateAfterPreferencesChanged()
666   // which triggers a reflow anyway.
667   SetBidi(bidiOptions);
668 }
669 
InvalidatePaintedLayers()670 void nsPresContext::InvalidatePaintedLayers() {
671   if (!mShell) return;
672   if (nsIFrame* rootFrame = mShell->GetRootFrame()) {
673     // FrameLayerBuilder caches invalidation-related values that depend on the
674     // appunits-per-dev-pixel ratio, so ensure that all PaintedLayer drawing
675     // is completely flushed.
676     rootFrame->InvalidateFrameSubtree();
677   }
678 }
679 
AppUnitsPerDevPixelChanged()680 void nsPresContext::AppUnitsPerDevPixelChanged() {
681   InvalidatePaintedLayers();
682 
683   if (mDeviceContext) {
684     mDeviceContext->FlushFontCache();
685   }
686 
687   MediaFeatureValuesChanged({eRestyle_ForceDescendants, NS_STYLE_HINT_REFLOW,
688                              MediaFeatureChangeReason::ResolutionChange});
689 
690   mCurAppUnitsPerDevPixel = AppUnitsPerDevPixel();
691 }
692 
PreferenceChanged(const char * aPrefName)693 void nsPresContext::PreferenceChanged(const char* aPrefName) {
694   nsDependentCString prefName(aPrefName);
695   if (prefName.EqualsLiteral("layout.css.dpi") ||
696       prefName.EqualsLiteral("layout.css.devPixelsPerPx")) {
697     int32_t oldAppUnitsPerDevPixel = AppUnitsPerDevPixel();
698     if (mDeviceContext->CheckDPIChange() && mShell) {
699       nsCOMPtr<nsIPresShell> shell = mShell;
700       // Re-fetch the view manager's window dimensions in case there's a
701       // deferred resize which hasn't affected our mVisibleArea yet
702       nscoord oldWidthAppUnits, oldHeightAppUnits;
703       RefPtr<nsViewManager> vm = shell->GetViewManager();
704       if (!vm) {
705         return;
706       }
707       vm->GetWindowDimensions(&oldWidthAppUnits, &oldHeightAppUnits);
708       float oldWidthDevPixels = oldWidthAppUnits / oldAppUnitsPerDevPixel;
709       float oldHeightDevPixels = oldHeightAppUnits / oldAppUnitsPerDevPixel;
710 
711       nscoord width = NSToCoordRound(oldWidthDevPixels * AppUnitsPerDevPixel());
712       nscoord height =
713           NSToCoordRound(oldHeightDevPixels * AppUnitsPerDevPixel());
714       vm->SetWindowDimensions(width, height);
715 
716       AppUnitsPerDevPixelChanged();
717     }
718     return;
719   }
720   if (prefName.EqualsLiteral(GFX_MISSING_FONTS_NOTIFY_PREF)) {
721     if (Preferences::GetBool(GFX_MISSING_FONTS_NOTIFY_PREF)) {
722       if (!mMissingFonts) {
723         mMissingFonts = new gfxMissingFontRecorder();
724         // trigger reflow to detect missing fonts on the current page
725         mPrefChangePendingNeedsReflow = true;
726       }
727     } else {
728       if (mMissingFonts) {
729         mMissingFonts->Clear();
730       }
731       mMissingFonts = nullptr;
732     }
733   }
734   if (StringBeginsWith(prefName, NS_LITERAL_CSTRING("font.")) ||
735       prefName.EqualsLiteral("intl.accept_languages")) {
736     // Changes to font family preferences don't change anything in the
737     // computed style data, so the style system won't generate a reflow
738     // hint for us.  We need to do that manually.
739 
740     // FIXME We could probably also handle changes to
741     // browser.display.auto_quality_min_font_size here, but that
742     // probably also requires clearing the text run cache, so don't
743     // bother (yet, anyway).
744     mPrefChangePendingNeedsReflow = true;
745   }
746   if (StringBeginsWith(prefName, NS_LITERAL_CSTRING("bidi."))) {
747     // Changes to bidi prefs need to trigger a reflow (see bug 443629)
748     mPrefChangePendingNeedsReflow = true;
749 
750     // Changes to bidi.numeral also needs to empty the text run cache.
751     // This is handled in gfxTextRunWordCache.cpp.
752   }
753   if (StringBeginsWith(prefName, NS_LITERAL_CSTRING("gfx.font_rendering."))) {
754     // Changes to font_rendering prefs need to trigger a reflow
755     mPrefChangePendingNeedsReflow = true;
756   }
757 
758   // We will end up calling InvalidatePreferenceSheets one from each pres
759   // context, but all it's doing is clearing its cached sheet pointers, so it
760   // won't be wastefully recreating the sheet multiple times.
761   //
762   // The first pres context that has its pref changed runnable called will
763   // be the one to cause the reconstruction of the pref style sheet.
764   nsLayoutStylesheetCache::InvalidatePreferenceSheets();
765   DispatchPrefChangedRunnableIfNeeded();
766 
767   if (prefName.EqualsLiteral("nglayout.debug.paint_flashing") ||
768       prefName.EqualsLiteral("nglayout.debug.paint_flashing_chrome")) {
769     mPaintFlashingInitialized = false;
770     return;
771   }
772 }
773 
DispatchPrefChangedRunnableIfNeeded()774 void nsPresContext::DispatchPrefChangedRunnableIfNeeded() {
775   if (mPostedPrefChangedRunnable) {
776     return;
777   }
778 
779   nsCOMPtr<nsIRunnable> runnable =
780       NewRunnableMethod("nsPresContext::UpdateAfterPreferencesChanged", this,
781                         &nsPresContext::UpdateAfterPreferencesChanged);
782   nsresult rv = Document()->Dispatch(TaskCategory::Other, runnable.forget());
783   if (NS_SUCCEEDED(rv)) {
784     mPostedPrefChangedRunnable = true;
785   }
786 }
787 
UpdateAfterPreferencesChanged()788 void nsPresContext::UpdateAfterPreferencesChanged() {
789   mPostedPrefChangedRunnable = false;
790   if (!mShell) {
791     return;
792   }
793 
794   if (!mContainer) {
795     // Delay updating until there is a container
796     mNeedsPrefUpdate = true;
797     return;
798   }
799 
800   nsCOMPtr<nsIDocShellTreeItem> docShell(mContainer);
801   if (docShell && nsIDocShellTreeItem::typeChrome == docShell->ItemType()) {
802     return;
803   }
804 
805   // Initialize our state from the user preferences
806   GetUserPreferences();
807 
808   // update the presShell: tell it to set the preference style rules up
809   mShell->UpdatePreferenceStyles();
810 
811   InvalidatePaintedLayers();
812   mDeviceContext->FlushFontCache();
813 
814   nsChangeHint hint = nsChangeHint(0);
815 
816   if (mPrefChangePendingNeedsReflow) {
817     hint |= NS_STYLE_HINT_REFLOW;
818   }
819 
820   // Preferences require rerunning selector matching because we rebuild
821   // the pref style sheet for some preference changes.
822   RebuildAllStyleData(hint, eRestyle_Subtree);
823 }
824 
Init(nsDeviceContext * aDeviceContext)825 nsresult nsPresContext::Init(nsDeviceContext* aDeviceContext) {
826   NS_ASSERTION(!mInitialized, "attempt to reinit pres context");
827   NS_ENSURE_ARG(aDeviceContext);
828 
829   mDeviceContext = aDeviceContext;
830 
831   // In certain rare cases (such as changing page mode), we tear down layout
832   // state and re-initialize a new prescontext for a document. Given that we
833   // hang style state off the DOM, we detect that re-initialization case and
834   // lazily drop the servo data. We don't do this eagerly during layout teardown
835   // because that would incur an extra whole-tree traversal that's unnecessary
836   // most of the time.
837   //
838   // FIXME(emilio): I'm pretty sure this doesn't happen after bug 1414999.
839   if (mDocument->IsStyledByServo()) {
840     Element* root = mDocument->GetRootElement();
841     if (root && root->HasServoData()) {
842       ServoRestyleManager::ClearServoDataFromSubtree(root);
843     }
844   }
845 
846   if (mDeviceContext->SetFullZoom(mFullZoom)) mDeviceContext->FlushFontCache();
847   mCurAppUnitsPerDevPixel = AppUnitsPerDevPixel();
848 
849   mEventManager = new mozilla::EventStateManager();
850 
851   mAnimationEventDispatcher = new mozilla::AnimationEventDispatcher(this);
852   mEffectCompositor = new mozilla::EffectCompositor(this);
853   mTransitionManager = new nsTransitionManager(this);
854   mAnimationManager = new nsAnimationManager(this);
855 
856   if (mDocument->GetDisplayDocument()) {
857     NS_ASSERTION(mDocument->GetDisplayDocument()->GetPresContext(),
858                  "Why are we being initialized?");
859     mRefreshDriver =
860         mDocument->GetDisplayDocument()->GetPresContext()->RefreshDriver();
861   } else {
862     nsIDocument* parent = mDocument->GetParentDocument();
863     // Unfortunately, sometimes |parent| here has no presshell because
864     // printing screws up things.  Assert that in other cases it does,
865     // but whenever the shell is null just fall back on using our own
866     // refresh driver.
867     NS_ASSERTION(!parent || mDocument->IsStaticDocument() || parent->GetShell(),
868                  "How did we end up with a presshell if our parent doesn't "
869                  "have one?");
870     if (parent && parent->GetPresContext()) {
871       // We don't have our container set yet at this point
872       nsCOMPtr<nsIDocShellTreeItem> ourItem = mDocument->GetDocShell();
873       if (ourItem) {
874         nsCOMPtr<nsIDocShellTreeItem> parentItem;
875         ourItem->GetSameTypeParent(getter_AddRefs(parentItem));
876         if (parentItem) {
877           Element* containingElement =
878               parent->FindContentForSubDocument(mDocument);
879           if (!containingElement->IsXULElement() ||
880               !containingElement->HasAttr(kNameSpaceID_None,
881                                           nsGkAtoms::forceOwnRefreshDriver)) {
882             mRefreshDriver = parent->GetPresContext()->RefreshDriver();
883           }
884         }
885       }
886     }
887 
888     if (!mRefreshDriver) {
889       mRefreshDriver = new nsRefreshDriver(this);
890     }
891   }
892 
893   mLangService = nsLanguageAtomService::GetService();
894 
895   // Register callbacks so we're notified when the preferences change
896   Preferences::RegisterPrefixCallback(nsPresContext::PrefChangedCallback,
897                                       "font.", this);
898   Preferences::RegisterPrefixCallback(nsPresContext::PrefChangedCallback,
899                                       "browser.display.", this);
900   Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
901                                 "browser.underline_anchors", this);
902   Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
903                                 "browser.anchor_color", this);
904   Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
905                                 "browser.active_color", this);
906   Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
907                                 "browser.visited_color", this);
908   Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
909                                 "image.animation_mode", this);
910   Preferences::RegisterPrefixCallback(nsPresContext::PrefChangedCallback,
911                                       "bidi.", this);
912   Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
913                                 "dom.send_after_paint_to_content", this);
914   Preferences::RegisterPrefixCallback(nsPresContext::PrefChangedCallback,
915                                       "gfx.font_rendering.", this);
916   Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
917                                 "layout.css.dpi", this);
918   Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
919                                 "layout.css.devPixelsPerPx", this);
920   Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
921                                 "nglayout.debug.paint_flashing", this);
922   Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
923                                 "nglayout.debug.paint_flashing_chrome", this);
924   Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
925                                 kUseStandinsForNativeColors, this);
926   Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
927                                 "intl.accept_languages", this);
928 
929   nsresult rv = mEventManager->Init();
930   NS_ENSURE_SUCCESS(rv, rv);
931 
932   mEventManager->SetPresContext(this);
933 
934 #ifdef RESTYLE_LOGGING
935   mRestyleLoggingEnabled =
936       GeckoRestyleManager::RestyleLoggingInitiallyEnabled();
937 #endif
938 
939 #ifdef DEBUG
940   mInitialized = true;
941 #endif
942 
943   return NS_OK;
944 }
945 
946 // Note: We don't hold a reference on the shell; it has a reference to
947 // us
AttachShell(nsIPresShell * aShell,StyleBackendType aBackendType)948 void nsPresContext::AttachShell(nsIPresShell* aShell,
949                                 StyleBackendType aBackendType) {
950   MOZ_ASSERT(!mShell);
951   mShell = aShell;
952 
953   if (aBackendType == StyleBackendType::Servo) {
954     mRestyleManager = new ServoRestyleManager(this);
955   } else {
956 #ifdef MOZ_OLD_STYLE
957     mRestyleManager = new GeckoRestyleManager(this);
958 #else
959     MOZ_CRASH("old style system disabled");
960 #endif
961   }
962 
963   // Since CounterStyleManager is also the name of a method of
964   // nsPresContext, it is necessary to prefix the class with the mozilla
965   // namespace here.
966   mCounterStyleManager = new mozilla::CounterStyleManager(this);
967 
968   nsIDocument* doc = mShell->GetDocument();
969   NS_ASSERTION(doc, "expect document here");
970   if (doc) {
971     // Have to update PresContext's mDocument before calling any other methods.
972     mDocument = doc;
973   }
974   // Initialize our state from the user preferences, now that we
975   // have a presshell, and hence a document.
976   GetUserPreferences();
977 
978   if (doc) {
979     nsIURI* docURI = doc->GetDocumentURI();
980 
981     if (IsDynamic() && docURI) {
982       bool isChrome = false;
983       bool isRes = false;
984       docURI->SchemeIs("chrome", &isChrome);
985       docURI->SchemeIs("resource", &isRes);
986 
987       if (!isChrome && !isRes)
988         mImageAnimationMode = mImageAnimationModePref;
989       else
990         mImageAnimationMode = imgIContainer::kNormalAnimMode;
991     }
992 
993     UpdateCharSet(doc->GetDocumentCharacterSet());
994   }
995 }
996 
DetachShell()997 void nsPresContext::DetachShell() {
998   // The counter style manager's destructor needs to deallocate with the
999   // presshell arena. Disconnect it before nulling out the shell.
1000   //
1001   // XXXbholley: Given recent refactorings, it probably makes more sense to
1002   // just null our mShell at the bottom of this function. I'm leaving it
1003   // this way to preserve the old ordering, but I doubt anything would break.
1004   if (mCounterStyleManager) {
1005     mCounterStyleManager->Disconnect();
1006     mCounterStyleManager = nullptr;
1007   }
1008 
1009   mShell = nullptr;
1010 
1011   if (mAnimationEventDispatcher) {
1012     mAnimationEventDispatcher->Disconnect();
1013     mAnimationEventDispatcher = nullptr;
1014   }
1015   if (mEffectCompositor) {
1016     mEffectCompositor->Disconnect();
1017     mEffectCompositor = nullptr;
1018   }
1019   if (mTransitionManager) {
1020     mTransitionManager->Disconnect();
1021     mTransitionManager = nullptr;
1022   }
1023   if (mAnimationManager) {
1024     mAnimationManager->Disconnect();
1025     mAnimationManager = nullptr;
1026   }
1027   if (mRestyleManager) {
1028     mRestyleManager->Disconnect();
1029     mRestyleManager = nullptr;
1030   }
1031   if (mRefreshDriver && mRefreshDriver->GetPresContext() == this) {
1032     mRefreshDriver->Disconnect();
1033     // Can't null out the refresh driver here.
1034   }
1035 
1036   if (IsRoot()) {
1037     nsRootPresContext* thisRoot = static_cast<nsRootPresContext*>(this);
1038 
1039     // Have to cancel our plugin geometry timer, because the
1040     // callback for that depends on a non-null presshell.
1041     thisRoot->CancelApplyPluginGeometryTimer();
1042 
1043     // The did-paint timer also depends on a non-null pres shell.
1044     thisRoot->CancelAllDidPaintTimers();
1045   }
1046 }
1047 
DoChangeCharSet(NotNull<const Encoding * > aCharSet)1048 void nsPresContext::DoChangeCharSet(NotNull<const Encoding*> aCharSet) {
1049   UpdateCharSet(aCharSet);
1050   mDeviceContext->FlushFontCache();
1051   // In Stylo, if a document contains one or more <script> elements, frame
1052   // construction might happen earlier than the UpdateCharSet(), so we need to
1053   // restyle descendants to make their style data up-to-date.
1054   RebuildAllStyleData(NS_STYLE_HINT_REFLOW, mDocument->IsStyledByServo()
1055                                                 ? eRestyle_ForceDescendants
1056                                                 : nsRestyleHint(0));
1057 }
1058 
UpdateCharSet(NotNull<const Encoding * > aCharSet)1059 void nsPresContext::UpdateCharSet(NotNull<const Encoding*> aCharSet) {
1060   mLanguage = mLangService->LookupCharSet(aCharSet);
1061   // this will be a language group (or script) code rather than a true language
1062   // code
1063 
1064   // bug 39570: moved from nsLanguageAtomService::LookupCharSet()
1065   if (mLanguage == nsGkAtoms::Unicode) {
1066     mLanguage = mLangService->GetLocaleLanguage();
1067   }
1068   mLangGroupFontPrefs.Reset();
1069   mFontGroupCacheDirty = true;
1070 
1071   switch (GET_BIDI_OPTION_TEXTTYPE(GetBidi())) {
1072     case IBMBIDI_TEXTTYPE_LOGICAL:
1073       SetVisualMode(false);
1074       break;
1075 
1076     case IBMBIDI_TEXTTYPE_VISUAL:
1077       SetVisualMode(true);
1078       break;
1079 
1080     case IBMBIDI_TEXTTYPE_CHARSET:
1081     default:
1082       SetVisualMode(IsVisualCharset(aCharSet));
1083   }
1084 }
1085 
DispatchCharSetChange(NotNull<const Encoding * > aEncoding)1086 void nsPresContext::DispatchCharSetChange(NotNull<const Encoding*> aEncoding) {
1087 #ifdef MOZ_OLD_STYLE
1088   if (!Document()->IsStyledByServo()) {
1089     RefPtr<CharSetChangingRunnable> runnable =
1090         new CharSetChangingRunnable(this, aEncoding);
1091     Document()->Dispatch(TaskCategory::Other, runnable.forget());
1092     return;
1093   }
1094 #endif
1095   // In Servo RebuildAllStyleData is async, so no need to do the runnable dance.
1096   DoChangeCharSet(aEncoding);
1097 }
1098 
GetParentPresContext()1099 nsPresContext* nsPresContext::GetParentPresContext() {
1100   nsIPresShell* shell = GetPresShell();
1101   if (shell) {
1102     nsViewManager* viewManager = shell->GetViewManager();
1103     if (viewManager) {
1104       nsView* view = viewManager->GetRootView();
1105       if (view) {
1106         view = view->GetParent();  // anonymous inner view
1107         if (view) {
1108           view = view->GetParent();  // subdocumentframe's view
1109           if (view) {
1110             nsIFrame* f = view->GetFrame();
1111             if (f) {
1112               return f->PresContext();
1113             }
1114           }
1115         }
1116       }
1117     }
1118   }
1119   return nullptr;
1120 }
1121 
GetToplevelContentDocumentPresContext()1122 nsPresContext* nsPresContext::GetToplevelContentDocumentPresContext() {
1123   if (IsChrome()) return nullptr;
1124   nsPresContext* pc = this;
1125   for (;;) {
1126     nsPresContext* parent = pc->GetParentPresContext();
1127     if (!parent || parent->IsChrome()) return pc;
1128     pc = parent;
1129   }
1130 }
1131 
GetNearestWidget(nsPoint * aOffset)1132 nsIWidget* nsPresContext::GetNearestWidget(nsPoint* aOffset) {
1133   NS_ENSURE_TRUE(mShell, nullptr);
1134   nsIFrame* frame = mShell->GetRootFrame();
1135   NS_ENSURE_TRUE(frame, nullptr);
1136   return frame->GetView()->GetNearestWidget(aOffset);
1137 }
1138 
GetRootWidget()1139 nsIWidget* nsPresContext::GetRootWidget() {
1140   NS_ENSURE_TRUE(mShell, nullptr);
1141   nsViewManager* vm = mShell->GetViewManager();
1142   if (!vm) {
1143     return nullptr;
1144   }
1145   nsCOMPtr<nsIWidget> widget;
1146   vm->GetRootWidget(getter_AddRefs(widget));
1147   return widget.get();
1148 }
1149 
1150 // We may want to replace this with something faster, maybe caching the root
1151 // prescontext
GetRootPresContext()1152 nsRootPresContext* nsPresContext::GetRootPresContext() {
1153   nsPresContext* pc = this;
1154   for (;;) {
1155     nsPresContext* parent = pc->GetParentPresContext();
1156     if (!parent) break;
1157     pc = parent;
1158   }
1159   return pc->IsRoot() ? static_cast<nsRootPresContext*>(pc) : nullptr;
1160 }
1161 
CompatibilityModeChanged()1162 void nsPresContext::CompatibilityModeChanged() {
1163   if (!mShell) {
1164     return;
1165   }
1166 
1167   nsIDocument* doc = mShell->GetDocument();
1168   if (!doc) {
1169     return;
1170   }
1171 
1172   StyleSetHandle styleSet = mShell->StyleSet();
1173   if (styleSet->IsServo()) {
1174     styleSet->AsServo()->CompatibilityModeChanged();
1175   }
1176 
1177   if (doc->IsSVGDocument()) {
1178     // SVG documents never load quirk.css.
1179     return;
1180   }
1181 
1182   bool needsQuirkSheet = CompatibilityMode() == eCompatibility_NavQuirks;
1183   if (mQuirkSheetAdded == needsQuirkSheet) {
1184     return;
1185   }
1186 
1187   auto cache = nsLayoutStylesheetCache::For(styleSet->BackendType());
1188   StyleSheet* sheet = cache->QuirkSheet();
1189 
1190   if (needsQuirkSheet) {
1191     // quirk.css needs to come after html.css; we just keep it at the end.
1192     DebugOnly<nsresult> rv =
1193         styleSet->AppendStyleSheet(SheetType::Agent, sheet);
1194     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "failed to insert quirk.css");
1195   } else {
1196     DebugOnly<nsresult> rv =
1197         styleSet->RemoveStyleSheet(SheetType::Agent, sheet);
1198     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "failed to remove quirk.css");
1199   }
1200 
1201   mQuirkSheetAdded = needsQuirkSheet;
1202 }
1203 
1204 // Helper function for setting Anim Mode on image
SetImgAnimModeOnImgReq(imgIRequest * aImgReq,uint16_t aMode)1205 static void SetImgAnimModeOnImgReq(imgIRequest* aImgReq, uint16_t aMode) {
1206   if (aImgReq) {
1207     nsCOMPtr<imgIContainer> imgCon;
1208     aImgReq->GetImage(getter_AddRefs(imgCon));
1209     if (imgCon) {
1210       imgCon->SetAnimationMode(aMode);
1211     }
1212   }
1213 }
1214 
1215 // IMPORTANT: Assumption is that all images for a Presentation
1216 // have the same Animation Mode (pavlov said this was OK)
1217 //
1218 // Walks content and set the animation mode
1219 // this is a way to turn on/off image animations
SetImgAnimations(nsIContent * aParent,uint16_t aMode)1220 void nsPresContext::SetImgAnimations(nsIContent* aParent, uint16_t aMode) {
1221   nsCOMPtr<nsIImageLoadingContent> imgContent(do_QueryInterface(aParent));
1222   if (imgContent) {
1223     nsCOMPtr<imgIRequest> imgReq;
1224     imgContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
1225                            getter_AddRefs(imgReq));
1226     SetImgAnimModeOnImgReq(imgReq, aMode);
1227   }
1228 
1229   for (nsIContent* childContent = aParent->GetFirstChild(); childContent;
1230        childContent = childContent->GetNextSibling()) {
1231     SetImgAnimations(childContent, aMode);
1232   }
1233 }
1234 
SetSMILAnimations(nsIDocument * aDoc,uint16_t aNewMode,uint16_t aOldMode)1235 void nsPresContext::SetSMILAnimations(nsIDocument* aDoc, uint16_t aNewMode,
1236                                       uint16_t aOldMode) {
1237   if (aDoc->HasAnimationController()) {
1238     nsSMILAnimationController* controller = aDoc->GetAnimationController();
1239     switch (aNewMode) {
1240       case imgIContainer::kNormalAnimMode:
1241       case imgIContainer::kLoopOnceAnimMode:
1242         if (aOldMode == imgIContainer::kDontAnimMode)
1243           controller->Resume(nsSMILTimeContainer::PAUSE_USERPREF);
1244         break;
1245 
1246       case imgIContainer::kDontAnimMode:
1247         if (aOldMode != imgIContainer::kDontAnimMode)
1248           controller->Pause(nsSMILTimeContainer::PAUSE_USERPREF);
1249         break;
1250     }
1251   }
1252 }
1253 
SetImageAnimationMode(uint16_t aMode)1254 void nsPresContext::SetImageAnimationMode(uint16_t aMode) {
1255   NS_ASSERTION(aMode == imgIContainer::kNormalAnimMode ||
1256                    aMode == imgIContainer::kDontAnimMode ||
1257                    aMode == imgIContainer::kLoopOnceAnimMode,
1258                "Wrong Animation Mode is being set!");
1259 
1260   // Image animation mode cannot be changed when rendering to a printer.
1261   if (!IsDynamic()) return;
1262 
1263   // Now walk the content tree and set the animation mode
1264   // on all the images.
1265   if (mShell != nullptr) {
1266     nsIDocument* doc = mShell->GetDocument();
1267     if (doc) {
1268       doc->StyleImageLoader()->SetAnimationMode(aMode);
1269 
1270       Element* rootElement = doc->GetRootElement();
1271       if (rootElement) {
1272         SetImgAnimations(rootElement, aMode);
1273       }
1274       SetSMILAnimations(doc, aMode, mImageAnimationMode);
1275     }
1276   }
1277 
1278   mImageAnimationMode = aMode;
1279 }
1280 
GetContentLanguage() const1281 already_AddRefed<nsAtom> nsPresContext::GetContentLanguage() const {
1282   nsAutoString language;
1283   Document()->GetContentLanguage(language);
1284   language.StripWhitespace();
1285 
1286   // Content-Language may be a comma-separated list of language codes,
1287   // in which case the HTML5 spec says to treat it as unknown
1288   if (!language.IsEmpty() && !language.Contains(char16_t(','))) {
1289     return NS_Atomize(language);
1290     // NOTE:  This does *not* count as an explicit language; in other
1291     // words, it doesn't trigger language-specific hyphenation.
1292   }
1293   return nullptr;
1294 }
1295 
UpdateEffectiveTextZoom()1296 void nsPresContext::UpdateEffectiveTextZoom() {
1297   float newZoom = mSystemFontScale * mTextZoom;
1298   float minZoom = nsLayoutUtils::MinZoom();
1299   float maxZoom = nsLayoutUtils::MaxZoom();
1300 
1301   if (newZoom < minZoom) {
1302     newZoom = minZoom;
1303   } else if (newZoom > maxZoom) {
1304     newZoom = maxZoom;
1305   }
1306 
1307   mEffectiveTextZoom = newZoom;
1308 
1309   // Media queries could have changed, since we changed the meaning
1310   // of 'em' units in them.
1311   MediaFeatureValuesChanged({eRestyle_ForceDescendants, NS_STYLE_HINT_REFLOW,
1312                              MediaFeatureChangeReason::ZoomChange});
1313 }
1314 
GetDeviceFullZoom()1315 float nsPresContext::GetDeviceFullZoom() {
1316   return mDeviceContext->GetFullZoom();
1317 }
1318 
SetFullZoom(float aZoom)1319 void nsPresContext::SetFullZoom(float aZoom) {
1320   if (!mShell || mFullZoom == aZoom) {
1321     return;
1322   }
1323 
1324   // Re-fetch the view manager's window dimensions in case there's a deferred
1325   // resize which hasn't affected our mVisibleArea yet
1326   nscoord oldWidthAppUnits, oldHeightAppUnits;
1327   mShell->GetViewManager()->GetWindowDimensions(&oldWidthAppUnits,
1328                                                 &oldHeightAppUnits);
1329   float oldWidthDevPixels = oldWidthAppUnits / float(mCurAppUnitsPerDevPixel);
1330   float oldHeightDevPixels = oldHeightAppUnits / float(mCurAppUnitsPerDevPixel);
1331   mDeviceContext->SetFullZoom(aZoom);
1332 
1333   NS_ASSERTION(!mSuppressResizeReflow,
1334                "two zooms happening at the same time? impossible!");
1335   mSuppressResizeReflow = true;
1336 
1337   mFullZoom = aZoom;
1338   mShell->GetViewManager()->SetWindowDimensions(
1339       NSToCoordRound(oldWidthDevPixels * AppUnitsPerDevPixel()),
1340       NSToCoordRound(oldHeightDevPixels * AppUnitsPerDevPixel()));
1341 
1342   AppUnitsPerDevPixelChanged();
1343 
1344   mSuppressResizeReflow = false;
1345 }
1346 
SetOverrideDPPX(float aDPPX)1347 void nsPresContext::SetOverrideDPPX(float aDPPX) {
1348   // SetOverrideDPPX is called during navigations, including history
1349   // traversals.  In that case, it's typically called with our current value,
1350   // and we don't need to actually do anything.
1351   if (aDPPX == mOverrideDPPX) {
1352     return;
1353   }
1354 
1355   mOverrideDPPX = aDPPX;
1356   MediaFeatureValuesChanged({MediaFeatureChangeReason::ResolutionChange});
1357 }
1358 
ScreenSizeInchesForFontInflation(bool * aChanged)1359 gfxSize nsPresContext::ScreenSizeInchesForFontInflation(bool* aChanged) {
1360   if (aChanged) {
1361     *aChanged = false;
1362   }
1363 
1364   nsDeviceContext* dx = DeviceContext();
1365   nsRect clientRect;
1366   dx->GetClientRect(clientRect);  // FIXME: GetClientRect looks expensive
1367   float unitsPerInch = dx->AppUnitsPerPhysicalInch();
1368   gfxSize deviceSizeInches(float(clientRect.width) / unitsPerInch,
1369                            float(clientRect.height) / unitsPerInch);
1370 
1371   if (mLastFontInflationScreenSize == gfxSize(-1.0, -1.0)) {
1372     mLastFontInflationScreenSize = deviceSizeInches;
1373   }
1374 
1375   if (deviceSizeInches != mLastFontInflationScreenSize && aChanged) {
1376     *aChanged = true;
1377     mLastFontInflationScreenSize = deviceSizeInches;
1378   }
1379 
1380   return deviceSizeInches;
1381 }
1382 
CheckOverflow(const nsStyleDisplay * aDisplay,ScrollbarStyles * aStyles)1383 static bool CheckOverflow(const nsStyleDisplay* aDisplay,
1384                           ScrollbarStyles* aStyles) {
1385   if (aDisplay->mOverflowX == NS_STYLE_OVERFLOW_VISIBLE &&
1386       aDisplay->mScrollBehavior == NS_STYLE_SCROLL_BEHAVIOR_AUTO &&
1387       aDisplay->mScrollSnapTypeX == NS_STYLE_SCROLL_SNAP_TYPE_NONE &&
1388       aDisplay->mScrollSnapTypeY == NS_STYLE_SCROLL_SNAP_TYPE_NONE &&
1389       aDisplay->mScrollSnapPointsX == nsStyleCoord(eStyleUnit_None) &&
1390       aDisplay->mScrollSnapPointsY == nsStyleCoord(eStyleUnit_None) &&
1391       !aDisplay->mScrollSnapDestination.mXPosition.mHasPercent &&
1392       !aDisplay->mScrollSnapDestination.mYPosition.mHasPercent &&
1393       aDisplay->mScrollSnapDestination.mXPosition.mLength == 0 &&
1394       aDisplay->mScrollSnapDestination.mYPosition.mLength == 0) {
1395     return false;
1396   }
1397 
1398   if (aDisplay->mOverflowX == NS_STYLE_OVERFLOW_CLIP) {
1399     *aStyles = ScrollbarStyles(NS_STYLE_OVERFLOW_HIDDEN,
1400                                NS_STYLE_OVERFLOW_HIDDEN, aDisplay);
1401   } else {
1402     *aStyles = ScrollbarStyles(aDisplay);
1403   }
1404   return true;
1405 }
1406 
GetPropagatedScrollbarStylesForViewport(nsPresContext * aPresContext,ScrollbarStyles * aStyles)1407 static Element* GetPropagatedScrollbarStylesForViewport(
1408     nsPresContext* aPresContext, ScrollbarStyles* aStyles) {
1409   nsIDocument* document = aPresContext->Document();
1410   Element* docElement = document->GetRootElement();
1411 
1412   // docElement might be null if we're doing this after removing it.
1413   if (!docElement) {
1414     return nullptr;
1415   }
1416 
1417   // Check the style on the document root element
1418   StyleSetHandle styleSet = aPresContext->StyleSet();
1419   RefPtr<nsStyleContext> rootStyle = styleSet->ResolveStyleFor(
1420       docElement, nullptr, LazyComputeBehavior::Allow);
1421   if (CheckOverflow(rootStyle->StyleDisplay(), aStyles)) {
1422     // tell caller we stole the overflow style from the root element
1423     return docElement;
1424   }
1425 
1426   // Don't look in the BODY for non-HTML documents or HTML documents
1427   // with non-HTML roots.
1428   // XXX this should be earlier; we shouldn't even look at the document root
1429   // for non-HTML documents. Fix this once we support explicit CSS styling
1430   // of the viewport
1431   // XXX what about XHTML?
1432   nsHTMLDocument* htmlDoc = document->AsHTMLDocument();
1433   if (!htmlDoc || !docElement->IsHTMLElement()) {
1434     return nullptr;
1435   }
1436 
1437   Element* bodyElement = htmlDoc->GetBodyElement();
1438   if (!bodyElement) {
1439     // No body, nothing to do here.
1440     return nullptr;
1441   }
1442 
1443   MOZ_ASSERT(bodyElement->IsHTMLElement(nsGkAtoms::body),
1444              "GetBodyElement returned something bogus");
1445 
1446   RefPtr<nsStyleContext> bodyStyle = styleSet->ResolveStyleFor(
1447       bodyElement, rootStyle, LazyComputeBehavior::Allow);
1448 
1449   if (CheckOverflow(bodyStyle->StyleDisplay(), aStyles)) {
1450     // tell caller we stole the overflow style from the body element
1451     return bodyElement;
1452   }
1453 
1454   return nullptr;
1455 }
1456 
UpdateViewportScrollbarStylesOverride()1457 Element* nsPresContext::UpdateViewportScrollbarStylesOverride() {
1458   // Start off with our default styles, and then update them as needed.
1459   mViewportStyleScrollbar =
1460       ScrollbarStyles(NS_STYLE_OVERFLOW_AUTO, NS_STYLE_OVERFLOW_AUTO);
1461   mViewportScrollbarOverrideElement = nullptr;
1462   // Don't propagate the scrollbar state in printing or print preview.
1463   if (!IsPaginated()) {
1464     mViewportScrollbarOverrideElement =
1465         GetPropagatedScrollbarStylesForViewport(this, &mViewportStyleScrollbar);
1466   }
1467 
1468   nsIDocument* document = Document();
1469   if (Element* fullscreenElement = document->GetFullscreenElement()) {
1470     // If the document is in fullscreen, but the fullscreen element is
1471     // not the root element, we should explicitly suppress the scrollbar
1472     // here. Note that, we still need to return the original element
1473     // the styles are from, so that the state of those elements is not
1474     // affected across fullscreen change.
1475     if (fullscreenElement != document->GetRootElement() &&
1476         fullscreenElement != mViewportScrollbarOverrideElement) {
1477       mViewportStyleScrollbar =
1478           ScrollbarStyles(NS_STYLE_OVERFLOW_HIDDEN, NS_STYLE_OVERFLOW_HIDDEN);
1479     }
1480   }
1481   return mViewportScrollbarOverrideElement;
1482 }
1483 
ElementWouldPropagateScrollbarStyles(Element * aElement)1484 bool nsPresContext::ElementWouldPropagateScrollbarStyles(Element* aElement) {
1485   MOZ_ASSERT(IsPaginated(), "Should only be called on paginated contexts");
1486   if (aElement->GetParent() && !aElement->IsHTMLElement(nsGkAtoms::body)) {
1487     // We certainly won't be propagating from this element.
1488     return false;
1489   }
1490 
1491   // Go ahead and just call GetPropagatedScrollbarStylesForViewport, but update
1492   // a dummy ScrollbarStyles we don't care about.  It'll do a bit of extra work,
1493   // but saves us having to have more complicated code or more code duplication;
1494   // in practice we will make this call quite rarely, because we checked for all
1495   // the common cases above.
1496   ScrollbarStyles dummy(NS_STYLE_OVERFLOW_AUTO, NS_STYLE_OVERFLOW_AUTO);
1497   return GetPropagatedScrollbarStylesForViewport(this, &dummy) == aElement;
1498 }
1499 
SetContainer(nsIDocShell * aDocShell)1500 void nsPresContext::SetContainer(nsIDocShell* aDocShell) {
1501   if (aDocShell) {
1502     NS_ASSERTION(!(mContainer && mNeedsPrefUpdate),
1503                  "Should only need pref update if mContainer is null.");
1504     mContainer = static_cast<nsDocShell*>(aDocShell);
1505     if (mNeedsPrefUpdate) {
1506       DispatchPrefChangedRunnableIfNeeded();
1507       mNeedsPrefUpdate = false;
1508     }
1509   } else {
1510     mContainer = WeakPtr<nsDocShell>();
1511   }
1512   UpdateIsChrome();
1513   if (mContainer) {
1514     GetDocumentColorPreferences();
1515   }
1516 }
1517 
GetContainerWeak() const1518 nsISupports* nsPresContext::GetContainerWeak() const {
1519   return static_cast<nsIDocShell*>(mContainer);
1520 }
1521 
GetDocShell() const1522 nsIDocShell* nsPresContext::GetDocShell() const { return mContainer; }
1523 
Detach()1524 /* virtual */ void nsPresContext::Detach() {
1525   SetContainer(nullptr);
1526   SetLinkHandler(nullptr);
1527 }
1528 
BidiEnabled() const1529 bool nsPresContext::BidiEnabled() const { return Document()->GetBidiEnabled(); }
1530 
SetBidiEnabled() const1531 void nsPresContext::SetBidiEnabled() const {
1532   if (mShell) {
1533     nsIDocument* doc = mShell->GetDocument();
1534     if (doc) {
1535       doc->SetBidiEnabled();
1536     }
1537   }
1538 }
1539 
SetBidi(uint32_t aSource)1540 void nsPresContext::SetBidi(uint32_t aSource) {
1541   // Don't do all this stuff unless the options have changed.
1542   if (aSource == GetBidi()) {
1543     return;
1544   }
1545 
1546   Document()->SetBidiOptions(aSource);
1547   if (IBMBIDI_TEXTDIRECTION_RTL == GET_BIDI_OPTION_DIRECTION(aSource) ||
1548       IBMBIDI_NUMERAL_HINDI == GET_BIDI_OPTION_NUMERAL(aSource)) {
1549     SetBidiEnabled();
1550   }
1551   if (IBMBIDI_TEXTTYPE_VISUAL == GET_BIDI_OPTION_TEXTTYPE(aSource)) {
1552     SetVisualMode(true);
1553   } else if (IBMBIDI_TEXTTYPE_LOGICAL == GET_BIDI_OPTION_TEXTTYPE(aSource)) {
1554     SetVisualMode(false);
1555   } else {
1556     nsIDocument* doc = mShell->GetDocument();
1557     if (doc) {
1558       SetVisualMode(IsVisualCharset(doc->GetDocumentCharacterSet()));
1559     }
1560   }
1561 }
1562 
GetBidi() const1563 uint32_t nsPresContext::GetBidi() const { return Document()->GetBidiOptions(); }
1564 
IsTopLevelWindowInactive()1565 bool nsPresContext::IsTopLevelWindowInactive() {
1566   return Document()->IsTopLevelWindowInactive();
1567 }
1568 
RecordInteractionTime(InteractionType aType,const TimeStamp & aTimeStamp)1569 void nsPresContext::RecordInteractionTime(InteractionType aType,
1570                                           const TimeStamp& aTimeStamp) {
1571   if (!mInteractionTimeEnabled || aTimeStamp.IsNull()) {
1572     return;
1573   }
1574 
1575   // Array of references to the member variable of each time stamp
1576   // for the different interaction types, keyed by InteractionType.
1577   TimeStamp nsPresContext::*interactionTimes[] = {
1578       &nsPresContext::mFirstClickTime, &nsPresContext::mFirstKeyTime,
1579       &nsPresContext::mFirstMouseMoveTime, &nsPresContext::mFirstScrollTime};
1580 
1581   // Array of histogram IDs for the different interaction types,
1582   // keyed by InteractionType.
1583   Telemetry::HistogramID histogramIds[] = {
1584       Telemetry::TIME_TO_FIRST_CLICK_MS, Telemetry::TIME_TO_FIRST_KEY_INPUT_MS,
1585       Telemetry::TIME_TO_FIRST_MOUSE_MOVE_MS,
1586       Telemetry::TIME_TO_FIRST_SCROLL_MS};
1587 
1588   TimeStamp& interactionTime =
1589       this->*(interactionTimes[static_cast<uint32_t>(aType)]);
1590   if (!interactionTime.IsNull()) {
1591     // We have already recorded an interaction time.
1592     return;
1593   }
1594 
1595   // Record the interaction time if it occurs after the first paint
1596   // of the top level content document.
1597   nsPresContext* topContentPresContext =
1598       GetToplevelContentDocumentPresContext();
1599 
1600   if (!topContentPresContext) {
1601     // There is no top content pres context so we don't care
1602     // about the interaction time. Record a value anyways to avoid
1603     // trying to find the top content pres context in future interactions.
1604     interactionTime = TimeStamp::Now();
1605     return;
1606   }
1607 
1608   if (topContentPresContext->mFirstNonBlankPaintTime.IsNull() ||
1609       topContentPresContext->mFirstNonBlankPaintTime > aTimeStamp) {
1610     // Top content pres context has not had a non-blank paint yet
1611     // or the event timestamp is before the first non-blank paint,
1612     // so don't record interaction time.
1613     return;
1614   }
1615 
1616   // Check if we are recording the first of any of the interaction types.
1617   bool isFirstInteraction = true;
1618   for (TimeStamp nsPresContext::*memberPtr : interactionTimes) {
1619     TimeStamp& timeStamp = this->*(memberPtr);
1620     if (!timeStamp.IsNull()) {
1621       isFirstInteraction = false;
1622       break;
1623     }
1624   }
1625 
1626   interactionTime = TimeStamp::Now();
1627   // Only the top level content pres context reports first interaction
1628   // time to telemetry (if it hasn't already done so).
1629   if (this == topContentPresContext) {
1630     if (Telemetry::CanRecordExtended()) {
1631       double millis =
1632           (interactionTime - mFirstNonBlankPaintTime).ToMilliseconds();
1633       Telemetry::Accumulate(histogramIds[static_cast<uint32_t>(aType)], millis);
1634 
1635       if (isFirstInteraction) {
1636         Telemetry::Accumulate(Telemetry::TIME_TO_FIRST_INTERACTION_MS, millis);
1637       }
1638     }
1639   } else {
1640     topContentPresContext->RecordInteractionTime(aType, aTimeStamp);
1641   }
1642 }
1643 
GetTheme()1644 nsITheme* nsPresContext::GetTheme() {
1645   if (!sNoTheme && !mTheme) {
1646     mTheme = do_GetService("@mozilla.org/chrome/chrome-native-theme;1");
1647     if (!mTheme) sNoTheme = true;
1648   }
1649 
1650   return mTheme;
1651 }
1652 
ThemeChanged()1653 void nsPresContext::ThemeChanged() {
1654   if (!mPendingThemeChanged) {
1655     sLookAndFeelChanged = true;
1656     sThemeChanged = true;
1657 
1658     nsCOMPtr<nsIRunnable> ev =
1659         NewRunnableMethod("nsPresContext::ThemeChangedInternal", this,
1660                           &nsPresContext::ThemeChangedInternal);
1661     nsresult rv = Document()->Dispatch(TaskCategory::Other, ev.forget());
1662     if (NS_SUCCEEDED(rv)) {
1663       mPendingThemeChanged = true;
1664     }
1665   }
1666 }
1667 
NotifyThemeChanged(TabParent * aTabParent,void * aArg)1668 static bool NotifyThemeChanged(TabParent* aTabParent, void* aArg) {
1669   aTabParent->ThemeChanged();
1670   return false;
1671 }
1672 
ThemeChangedInternal()1673 void nsPresContext::ThemeChangedInternal() {
1674   mPendingThemeChanged = false;
1675 
1676   // Tell the theme that it changed, so it can flush any handles to stale theme
1677   // data.
1678   if (mTheme && sThemeChanged) {
1679     mTheme->ThemeChanged();
1680     sThemeChanged = false;
1681   }
1682 
1683   if (sLookAndFeelChanged) {
1684     // Clear all cached LookAndFeel colors.
1685     LookAndFeel::Refresh();
1686     sLookAndFeelChanged = false;
1687 
1688     // Vector images (SVG) may be using theme colors so we discard all cached
1689     // surfaces. (We could add a vector image only version of DiscardAll, but
1690     // in bug 940625 we decided theme changes are rare enough not to bother.)
1691     image::SurfaceCacheUtils::DiscardAll();
1692   }
1693 
1694   RefreshSystemMetrics();
1695 
1696   // Recursively notify all remote leaf descendants that the
1697   // system theme has changed.
1698   nsContentUtils::CallOnAllRemoteChildren(mDocument->GetWindow(),
1699                                           NotifyThemeChanged, nullptr);
1700 }
1701 
SysColorChanged()1702 void nsPresContext::SysColorChanged() {
1703   if (!mPendingSysColorChanged) {
1704     sLookAndFeelChanged = true;
1705     nsCOMPtr<nsIRunnable> ev =
1706         NewRunnableMethod("nsPresContext::SysColorChangedInternal", this,
1707                           &nsPresContext::SysColorChangedInternal);
1708     nsresult rv = Document()->Dispatch(TaskCategory::Other, ev.forget());
1709     if (NS_SUCCEEDED(rv)) {
1710       mPendingSysColorChanged = true;
1711     }
1712   }
1713 }
1714 
SysColorChangedInternal()1715 void nsPresContext::SysColorChangedInternal() {
1716   mPendingSysColorChanged = false;
1717 
1718   if (sLookAndFeelChanged) {
1719     // Don't use the cached values for the system colors
1720     LookAndFeel::Refresh();
1721     sLookAndFeelChanged = false;
1722   }
1723 
1724   // Invalidate cached '-moz-windows-accent-color-applies' media query:
1725   RefreshSystemMetrics();
1726 
1727   // Reset default background and foreground colors for the document since
1728   // they may be using system colors
1729   GetDocumentColorPreferences();
1730 
1731   // The system color values are computed to colors in the style data,
1732   // so normal style data comparison is sufficient here.
1733   RebuildAllStyleData(nsChangeHint(0), nsRestyleHint(0));
1734 }
1735 
RefreshSystemMetrics()1736 void nsPresContext::RefreshSystemMetrics() {
1737   // This will force the system metrics to be generated the next time they're
1738   // used.
1739   nsMediaFeatures::FreeSystemMetrics();
1740 
1741   // Changes to system metrics can change media queries on them.
1742   //
1743   // Changes in theme can change system colors (whose changes are
1744   // properly reflected in computed style data), system fonts (whose
1745   // changes are not), and -moz-appearance (whose changes likewise are
1746   // not), so we need to recascade for the first, and reflow for the rest.
1747   MediaFeatureValuesChanged({
1748       eRestyle_ForceDescendants,
1749       NS_STYLE_HINT_REFLOW,
1750       MediaFeatureChangeReason::SystemMetricsChange,
1751   });
1752 }
1753 
UIResolutionChanged()1754 void nsPresContext::UIResolutionChanged() {
1755   if (!mPendingUIResolutionChanged) {
1756     nsCOMPtr<nsIRunnable> ev =
1757         NewRunnableMethod("nsPresContext::UIResolutionChangedInternal", this,
1758                           &nsPresContext::UIResolutionChangedInternal);
1759     nsresult rv = Document()->Dispatch(TaskCategory::Other, ev.forget());
1760     if (NS_SUCCEEDED(rv)) {
1761       mPendingUIResolutionChanged = true;
1762     }
1763   }
1764 }
1765 
UIResolutionChangedSync()1766 void nsPresContext::UIResolutionChangedSync() {
1767   if (!mPendingUIResolutionChanged) {
1768     mPendingUIResolutionChanged = true;
1769     UIResolutionChangedInternalScale(0.0);
1770   }
1771 }
1772 
UIResolutionChangedSubdocumentCallback(nsIDocument * aDocument,void * aData)1773 /*static*/ bool nsPresContext::UIResolutionChangedSubdocumentCallback(
1774     nsIDocument* aDocument, void* aData) {
1775   nsPresContext* pc = aDocument->GetPresContext();
1776   if (pc) {
1777     // For subdocuments, we want to apply the parent's scale, because there
1778     // are cases where the subdoc's device context is connected to a widget
1779     // that has an out-of-date resolution (it's on a different screen, but
1780     // currently hidden, and will not be updated until shown): bug 1249279.
1781     double scale = *static_cast<double*>(aData);
1782     pc->UIResolutionChangedInternalScale(scale);
1783   }
1784   return true;
1785 }
1786 
NotifyTabUIResolutionChanged(TabParent * aTab,void * aArg)1787 static void NotifyTabUIResolutionChanged(TabParent* aTab, void* aArg) {
1788   aTab->UIResolutionChanged();
1789 }
1790 
NotifyChildrenUIResolutionChanged(nsPIDOMWindowOuter * aWindow)1791 static void NotifyChildrenUIResolutionChanged(nsPIDOMWindowOuter* aWindow) {
1792   nsCOMPtr<nsIDocument> doc = aWindow->GetExtantDoc();
1793   RefPtr<nsPIWindowRoot> topLevelWin = nsContentUtils::GetWindowRoot(doc);
1794   if (!topLevelWin) {
1795     return;
1796   }
1797   topLevelWin->EnumerateBrowsers(NotifyTabUIResolutionChanged, nullptr);
1798 }
1799 
UIResolutionChangedInternal()1800 void nsPresContext::UIResolutionChangedInternal() {
1801   UIResolutionChangedInternalScale(0.0);
1802 }
1803 
UIResolutionChangedInternalScale(double aScale)1804 void nsPresContext::UIResolutionChangedInternalScale(double aScale) {
1805   mPendingUIResolutionChanged = false;
1806 
1807   mDeviceContext->CheckDPIChange(&aScale);
1808   if (mCurAppUnitsPerDevPixel != AppUnitsPerDevPixel()) {
1809     AppUnitsPerDevPixelChanged();
1810   }
1811 
1812   // Recursively notify all remote leaf descendants of the change.
1813   if (nsPIDOMWindowOuter* window = mDocument->GetWindow()) {
1814     NotifyChildrenUIResolutionChanged(window);
1815   }
1816 
1817   mDocument->EnumerateSubDocuments(UIResolutionChangedSubdocumentCallback,
1818                                    &aScale);
1819 }
1820 
EmulateMedium(const nsAString & aMediaType)1821 void nsPresContext::EmulateMedium(const nsAString& aMediaType) {
1822   nsAtom* previousMedium = Medium();
1823   mIsEmulatingMedia = true;
1824 
1825   nsAutoString mediaType;
1826   nsContentUtils::ASCIIToLower(aMediaType, mediaType);
1827 
1828   mMediaEmulated = NS_Atomize(mediaType);
1829   if (mMediaEmulated != previousMedium && mShell) {
1830     MediaFeatureValuesChanged({MediaFeatureChangeReason::MediumChange});
1831   }
1832 }
1833 
StopEmulatingMedium()1834 void nsPresContext::StopEmulatingMedium() {
1835   nsAtom* previousMedium = Medium();
1836   mIsEmulatingMedia = false;
1837   if (Medium() != previousMedium) {
1838     MediaFeatureValuesChanged({MediaFeatureChangeReason::MediumChange});
1839   }
1840 }
1841 
ForceCacheLang(nsAtom * aLanguage)1842 void nsPresContext::ForceCacheLang(nsAtom* aLanguage) {
1843   // force it to be cached
1844   GetDefaultFont(kPresContext_DefaultVariableFont_ID, aLanguage);
1845   mLanguagesUsed.PutEntry(aLanguage);
1846 }
1847 
CacheAllLangs()1848 void nsPresContext::CacheAllLangs() {
1849   if (mFontGroupCacheDirty) {
1850     RefPtr<nsAtom> thisLang = nsStyleFont::GetLanguage(this);
1851     GetDefaultFont(kPresContext_DefaultVariableFont_ID, thisLang.get());
1852     GetDefaultFont(kPresContext_DefaultVariableFont_ID, nsGkAtoms::x_math);
1853     // https://bugzilla.mozilla.org/show_bug.cgi?id=1362599#c12
1854     GetDefaultFont(kPresContext_DefaultVariableFont_ID, nsGkAtoms::Unicode);
1855     for (auto iter = mLanguagesUsed.Iter(); !iter.Done(); iter.Next()) {
1856       GetDefaultFont(kPresContext_DefaultVariableFont_ID, iter.Get()->GetKey());
1857     }
1858   }
1859   mFontGroupCacheDirty = false;
1860 }
1861 
RebuildAllStyleData(nsChangeHint aExtraHint,nsRestyleHint aRestyleHint)1862 void nsPresContext::RebuildAllStyleData(nsChangeHint aExtraHint,
1863                                         nsRestyleHint aRestyleHint) {
1864   if (!mShell) {
1865     // We must have been torn down. Nothing to do here.
1866     return;
1867   }
1868 
1869   // FIXME(emilio): Why is it safe to reset mUsesRootEMUnits / mUsesEXChUnits
1870   // here if there's no restyle hint? That looks pretty bogus.
1871   mUsesRootEMUnits = false;
1872   mUsesExChUnits = false;
1873   if (mShell->StyleSet()->IsGecko()) {
1874 #ifdef MOZ_OLD_STYLE
1875     mShell->StyleSet()->AsGecko()->SetUsesViewportUnits(false);
1876 #else
1877     MOZ_CRASH("old style system disabled");
1878 #endif
1879   }
1880 
1881   // TODO(emilio): It's unclear to me why would these three calls below be
1882   // needed. In particular, RebuildAllStyleData doesn't rebuild rules or
1883   // specified style information and such (note the comment in
1884   // ServoRestyleManager::RebuildAllStyleData re. the funny semantics), so I
1885   // don't know why should we rebuild the user font set / counter styles /
1886   // etc...
1887   mDocument->MarkUserFontSetDirty();
1888   MarkCounterStylesDirty();
1889   MarkFontFeatureValuesDirty();
1890 
1891   RestyleManager()->RebuildAllStyleData(aExtraHint, aRestyleHint);
1892 }
1893 
PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint,nsRestyleHint aRestyleHint)1894 void nsPresContext::PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint,
1895                                                  nsRestyleHint aRestyleHint) {
1896   if (!mShell) {
1897     // We must have been torn down. Nothing to do here.
1898     return;
1899   }
1900   RestyleManager()->PostRebuildAllStyleDataEvent(aExtraHint, aRestyleHint);
1901 }
1902 
1903 struct MediaFeatureHints {
1904   nsRestyleHint restyleHint;
1905   nsChangeHint changeHint;
1906 };
1907 
MediaFeatureValuesChangedAllDocumentsCallback(nsIDocument * aDocument,void * aChange)1908 static bool MediaFeatureValuesChangedAllDocumentsCallback(
1909     nsIDocument* aDocument, void* aChange) {
1910   auto* change = static_cast<const MediaFeatureChange*>(aChange);
1911   if (nsPresContext* pc = aDocument->GetPresContext()) {
1912     pc->MediaFeatureValuesChangedAllDocuments(*change);
1913   }
1914   return true;
1915 }
1916 
MediaFeatureValuesChangedAllDocuments(const MediaFeatureChange & aChange)1917 void nsPresContext::MediaFeatureValuesChangedAllDocuments(
1918     const MediaFeatureChange& aChange) {
1919   MediaFeatureValuesChanged(aChange);
1920   mDocument->EnumerateSubDocuments(
1921       MediaFeatureValuesChangedAllDocumentsCallback,
1922       const_cast<MediaFeatureChange*>(&aChange));
1923 }
1924 
FlushPendingMediaFeatureValuesChanged()1925 void nsPresContext::FlushPendingMediaFeatureValuesChanged() {
1926   if (!mPendingMediaFeatureValuesChange) {
1927     return;
1928   }
1929 
1930   MediaFeatureChange change = *mPendingMediaFeatureValuesChange;
1931   mPendingMediaFeatureValuesChange.reset();
1932 
1933   // MediumFeaturesChanged updates the applied rules, so it always gets called.
1934   if (mShell) {
1935     change.mRestyleHint |=
1936         mShell->StyleSet()->MediumFeaturesChanged(change.mReason);
1937   }
1938 
1939   if (change.mRestyleHint || change.mChangeHint) {
1940     RebuildAllStyleData(change.mChangeHint, change.mRestyleHint);
1941   }
1942 
1943   if (!mShell || !mShell->DidInitialize()) {
1944     return;
1945   }
1946 
1947   if (mDocument->IsBeingUsedAsImage()) {
1948     MOZ_ASSERT(mDocument->MediaQueryLists().isEmpty());
1949     return;
1950   }
1951 
1952   mDocument->NotifyMediaFeatureValuesChanged();
1953 
1954   MOZ_DIAGNOSTIC_ASSERT(nsContentUtils::IsSafeToRunScript());
1955 
1956   // Media query list listeners should be notified from a queued task
1957   // (in HTML5 terms), although we also want to notify them on certain
1958   // flushes.  (We're already running off an event.)
1959   //
1960   // Note that we do this after the new style from media queries in
1961   // style sheets has been computed.
1962 
1963   if (mDocument->MediaQueryLists().isEmpty()) {
1964     return;
1965   }
1966 
1967   // We build a list of all the notifications we're going to send
1968   // before we send any of them.
1969 
1970   // Copy pointers to all the lists into a new array, in case one of our
1971   // notifications modifies the list.
1972   nsTArray<RefPtr<mozilla::dom::MediaQueryList>> localMediaQueryLists;
1973   for (auto* mql : mDocument->MediaQueryLists()) {
1974     localMediaQueryLists.AppendElement(mql);
1975   }
1976 
1977   // Now iterate our local array of the lists.
1978   for (const auto& mql : localMediaQueryLists) {
1979     nsAutoMicroTask mt;
1980     mql->MaybeNotify();
1981   }
1982 }
1983 
NotifyTabSizeModeChanged(TabParent * aTab,void * aArg)1984 static bool NotifyTabSizeModeChanged(TabParent* aTab, void* aArg) {
1985   nsSizeMode* sizeMode = static_cast<nsSizeMode*>(aArg);
1986   aTab->SizeModeChanged(*sizeMode);
1987   return false;
1988 }
1989 
SizeModeChanged(nsSizeMode aSizeMode)1990 void nsPresContext::SizeModeChanged(nsSizeMode aSizeMode) {
1991   nsContentUtils::CallOnAllRemoteChildren(mDocument->GetWindow(),
1992                                           NotifyTabSizeModeChanged, &aSizeMode);
1993   MediaFeatureValuesChangedAllDocuments(
1994       {MediaFeatureChangeReason::SizeModeChange});
1995 }
1996 
CompatibilityMode() const1997 nsCompatibility nsPresContext::CompatibilityMode() const {
1998   return Document()->GetCompatibilityMode();
1999 }
2000 
SetPaginatedScrolling(bool aPaginated)2001 void nsPresContext::SetPaginatedScrolling(bool aPaginated) {
2002   if (mType == eContext_PrintPreview || mType == eContext_PageLayout)
2003     mCanPaginatedScroll = aPaginated;
2004 }
2005 
SetPrintSettings(nsIPrintSettings * aPrintSettings)2006 void nsPresContext::SetPrintSettings(nsIPrintSettings* aPrintSettings) {
2007   if (mMedium == nsGkAtoms::print) mPrintSettings = aPrintSettings;
2008 }
2009 
EnsureVisible()2010 bool nsPresContext::EnsureVisible() {
2011   nsCOMPtr<nsIDocShell> docShell(mContainer);
2012   if (docShell) {
2013     nsCOMPtr<nsIContentViewer> cv;
2014     docShell->GetContentViewer(getter_AddRefs(cv));
2015     // Make sure this is the content viewer we belong with
2016     if (cv) {
2017       RefPtr<nsPresContext> currentPresContext;
2018       cv->GetPresContext(getter_AddRefs(currentPresContext));
2019       if (currentPresContext == this) {
2020         // OK, this is us.  We want to call Show() on the content viewer.
2021         nsresult result = cv->Show();
2022         if (NS_SUCCEEDED(result)) {
2023           return true;
2024         }
2025       }
2026     }
2027   }
2028   return false;
2029 }
2030 
2031 #ifdef MOZ_REFLOW_PERF
CountReflows(const char * aName,nsIFrame * aFrame)2032 void nsPresContext::CountReflows(const char* aName, nsIFrame* aFrame) {
2033   if (mShell) {
2034     mShell->CountReflows(aName, aFrame);
2035   }
2036 }
2037 #endif
2038 
UpdateIsChrome()2039 void nsPresContext::UpdateIsChrome() {
2040   mIsChrome =
2041       mContainer && nsIDocShellTreeItem::typeChrome == mContainer->ItemType();
2042 }
2043 
HasAuthorSpecifiedRules(const nsIFrame * aFrame,uint32_t aRuleTypeMask) const2044 bool nsPresContext::HasAuthorSpecifiedRules(const nsIFrame* aFrame,
2045                                             uint32_t aRuleTypeMask) const {
2046   if (aFrame->StyleContext()->IsGecko()) {
2047 #ifdef MOZ_OLD_STYLE
2048     auto* geckoStyleContext = aFrame->StyleContext()->AsGecko();
2049     return nsRuleNode::HasAuthorSpecifiedRules(geckoStyleContext, aRuleTypeMask,
2050                                                UseDocumentColors());
2051 #else
2052     MOZ_CRASH("old style system disabled");
2053 #endif
2054   }
2055   Element* elem = aFrame->GetContent()->AsElement();
2056 
2057   // We need to handle non-generated content pseudos too, so we use
2058   // the parent of generated content pseudo to be consistent.
2059   if (elem->GetPseudoElementType() != CSSPseudoElementType::NotPseudo) {
2060     MOZ_ASSERT(elem->GetParent(), "Pseudo element has no parent element?");
2061     elem = elem->GetParent()->AsElement();
2062   }
2063   if (MOZ_UNLIKELY(!elem->HasServoData())) {
2064     // Probably shouldn't happen, but does. See bug 1387953
2065     return false;
2066   }
2067 
2068   nsStyleContext* styleContext = aFrame->StyleContext();
2069   CSSPseudoElementType pseudoType = styleContext->GetPseudoType();
2070   // Anonymous boxes are more complicated, and we just assume that they
2071   // cannot have any author-specified rules here.
2072   if (pseudoType == CSSPseudoElementType::InheritingAnonBox ||
2073       pseudoType == CSSPseudoElementType::NonInheritingAnonBox) {
2074     return false;
2075   }
2076   return Servo_HasAuthorSpecifiedRules(styleContext->AsServo(), elem,
2077                                        pseudoType, aRuleTypeMask,
2078                                        UseDocumentColors());
2079 }
2080 
GetUserFontSet(bool aFlushUserFontSet)2081 gfxUserFontSet* nsPresContext::GetUserFontSet(bool aFlushUserFontSet) {
2082   return mDocument->GetUserFontSet(aFlushUserFontSet);
2083 }
2084 
UserFontSetUpdated(gfxUserFontEntry * aUpdatedFont)2085 void nsPresContext::UserFontSetUpdated(gfxUserFontEntry* aUpdatedFont) {
2086   if (!mShell) return;
2087 
2088   // Note: this method is called without a font when rules in the userfont set
2089   // are updated, which may occur during reflow as a result of the lazy
2090   // initialization of the userfont set. It would be better to avoid a full
2091   // restyle but until this method is only called outside of reflow, schedule a
2092   // full restyle in these cases.
2093   if (!aUpdatedFont) {
2094     PostRebuildAllStyleDataEvent(NS_STYLE_HINT_REFLOW,
2095                                  eRestyle_ForceDescendants);
2096     return;
2097   }
2098 
2099   // Special case - if either the 'ex' or 'ch' units are used, these
2100   // depend upon font metrics. Updating this information requires
2101   // rebuilding the rule tree from the top, avoiding the reuse of cached
2102   // data even when no style rules have changed.
2103   if (UsesExChUnits()) {
2104     PostRebuildAllStyleDataEvent(nsChangeHint(0), eRestyle_ForceDescendants);
2105   }
2106 
2107   // Iterate over the frame tree looking for frames associated with the
2108   // downloadable font family in question. If a frame's nsStyleFont has
2109   // the name, check the font group associated with the metrics to see if
2110   // it contains that specific font (i.e. the one chosen within the family
2111   // given the weight, width, and slant from the nsStyleFont). If it does,
2112   // mark that frame dirty and skip inspecting its descendants.
2113   nsIFrame* root = mShell->GetRootFrame();
2114   if (root) {
2115     nsFontFaceUtils::MarkDirtyForFontChange(root, aUpdatedFont);
2116   }
2117 }
2118 
2119 class CounterStyleCleaner : public nsAPostRefreshObserver {
2120  public:
CounterStyleCleaner(nsRefreshDriver * aRefreshDriver,CounterStyleManager * aCounterStyleManager)2121   CounterStyleCleaner(nsRefreshDriver* aRefreshDriver,
2122                       CounterStyleManager* aCounterStyleManager)
2123       : mRefreshDriver(aRefreshDriver),
2124         mCounterStyleManager(aCounterStyleManager) {}
~CounterStyleCleaner()2125   virtual ~CounterStyleCleaner() {}
2126 
DidRefresh()2127   void DidRefresh() final {
2128     mRefreshDriver->RemovePostRefreshObserver(this);
2129     mCounterStyleManager->CleanRetiredStyles();
2130     delete this;
2131   }
2132 
2133  private:
2134   RefPtr<nsRefreshDriver> mRefreshDriver;
2135   RefPtr<CounterStyleManager> mCounterStyleManager;
2136 };
2137 
FlushCounterStyles()2138 void nsPresContext::FlushCounterStyles() {
2139   if (!mShell) {
2140     return;  // we've been torn down
2141   }
2142   if (mCounterStyleManager->IsInitial()) {
2143     // Still in its initial state, no need to clean.
2144     return;
2145   }
2146 
2147   if (mCounterStylesDirty) {
2148     bool changed = mCounterStyleManager->NotifyRuleChanged();
2149     if (changed) {
2150       PresShell()->NotifyCounterStylesAreDirty();
2151       PostRebuildAllStyleDataEvent(NS_STYLE_HINT_REFLOW,
2152                                    eRestyle_ForceDescendants);
2153       RefreshDriver()->AddPostRefreshObserver(
2154           new CounterStyleCleaner(RefreshDriver(), mCounterStyleManager));
2155     }
2156     mCounterStylesDirty = false;
2157   }
2158 }
2159 
MarkCounterStylesDirty()2160 void nsPresContext::MarkCounterStylesDirty() {
2161   if (mCounterStyleManager->IsInitial()) {
2162     // Still in its initial state, no need to touch anything.
2163     return;
2164   }
2165 
2166   mCounterStylesDirty = true;
2167 }
2168 
NotifyMissingFonts()2169 void nsPresContext::NotifyMissingFonts() {
2170   if (mMissingFonts) {
2171     mMissingFonts->Flush();
2172   }
2173 }
2174 
EnsureSafeToHandOutCSSRules()2175 void nsPresContext::EnsureSafeToHandOutCSSRules() {
2176   if (!mShell->StyleSet()->EnsureUniqueInnerOnCSSSheets()) {
2177     // Nothing to do.
2178     return;
2179   }
2180 
2181   RebuildAllStyleData(nsChangeHint(0), eRestyle_Subtree);
2182 }
2183 
FireDOMPaintEvent(nsTArray<nsRect> * aList,uint64_t aTransactionId,mozilla::TimeStamp aTimeStamp)2184 void nsPresContext::FireDOMPaintEvent(
2185     nsTArray<nsRect>* aList, uint64_t aTransactionId,
2186     mozilla::TimeStamp aTimeStamp /* = mozilla::TimeStamp() */) {
2187   nsPIDOMWindowInner* ourWindow = mDocument->GetInnerWindow();
2188   if (!ourWindow) return;
2189 
2190   nsCOMPtr<EventTarget> dispatchTarget = do_QueryInterface(ourWindow);
2191   nsCOMPtr<EventTarget> eventTarget = dispatchTarget;
2192   if (!IsChrome() && !mSendAfterPaintToContent) {
2193     // Don't tell the window about this event, it should not know that
2194     // something happened in a subdocument. Tell only the chrome event handler.
2195     // (Events sent to the window get propagated to the chrome event handler
2196     // automatically.)
2197     dispatchTarget = do_QueryInterface(ourWindow->GetParentTarget());
2198     if (!dispatchTarget) {
2199       return;
2200     }
2201   }
2202 
2203   if (aTimeStamp.IsNull()) {
2204     aTimeStamp = mozilla::TimeStamp::Now();
2205   }
2206   DOMHighResTimeStamp timeStamp = 0;
2207   if (ourWindow) {
2208     mozilla::dom::Performance* perf = ourWindow->GetPerformance();
2209     if (perf) {
2210       timeStamp = perf->GetDOMTiming()->TimeStampToDOMHighRes(aTimeStamp);
2211     }
2212   }
2213 
2214   // Events sent to the window get propagated to the chrome event handler
2215   // automatically.
2216   //
2217   // This will empty our list in case dispatching the event causes more damage
2218   // (hopefully it won't, or we're likely to get an infinite loop! At least
2219   // it won't be blocking app execution though).
2220   RefPtr<NotifyPaintEvent> event =
2221       NS_NewDOMNotifyPaintEvent(eventTarget, this, nullptr, eAfterPaint, aList,
2222                                 aTransactionId, timeStamp);
2223 
2224   // Even if we're not telling the window about the event (so eventTarget is
2225   // the chrome event handler, not the window), the window is still
2226   // logically the event target.
2227   event->SetTarget(eventTarget);
2228   event->SetTrusted(true);
2229   EventDispatcher::DispatchDOMEvent(dispatchTarget, nullptr,
2230                                     static_cast<Event*>(event), this, nullptr);
2231 }
2232 
MayHavePaintEventListenerSubdocumentCallback(nsIDocument * aDocument,void * aData)2233 static bool MayHavePaintEventListenerSubdocumentCallback(nsIDocument* aDocument,
2234                                                          void* aData) {
2235   bool* result = static_cast<bool*>(aData);
2236   nsPresContext* pc = aDocument->GetPresContext();
2237   if (pc) {
2238     *result = pc->MayHavePaintEventListenerInSubDocument();
2239 
2240     // If we found a paint event listener, then we can stop enumerating
2241     // sub documents.
2242     return !*result;
2243   }
2244   return true;
2245 }
2246 
MayHavePaintEventListener(nsPIDOMWindowInner * aInnerWindow)2247 static bool MayHavePaintEventListener(nsPIDOMWindowInner* aInnerWindow) {
2248   if (!aInnerWindow) return false;
2249   if (aInnerWindow->HasPaintEventListeners()) return true;
2250 
2251   EventTarget* parentTarget = aInnerWindow->GetParentTarget();
2252   if (!parentTarget) return false;
2253 
2254   EventListenerManager* manager = nullptr;
2255   if ((manager = parentTarget->GetExistingListenerManager()) &&
2256       manager->MayHavePaintEventListener()) {
2257     return true;
2258   }
2259 
2260   nsCOMPtr<nsINode> node;
2261   if (parentTarget != aInnerWindow->GetChromeEventHandler()) {
2262     nsCOMPtr<nsIInProcessContentFrameMessageManager> mm =
2263         do_QueryInterface(parentTarget);
2264     if (mm) {
2265       node = mm->GetOwnerContent();
2266     }
2267   }
2268 
2269   if (!node) {
2270     node = do_QueryInterface(parentTarget);
2271   }
2272   if (node)
2273     return MayHavePaintEventListener(node->OwnerDoc()->GetInnerWindow());
2274 
2275   nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(parentTarget);
2276   if (window) return MayHavePaintEventListener(window);
2277 
2278   nsCOMPtr<nsPIWindowRoot> root = do_QueryInterface(parentTarget);
2279   EventTarget* tabChildGlobal;
2280   return root && (tabChildGlobal = root->GetParentTarget()) &&
2281          (manager = tabChildGlobal->GetExistingListenerManager()) &&
2282          manager->MayHavePaintEventListener();
2283 }
2284 
MayHavePaintEventListener()2285 bool nsPresContext::MayHavePaintEventListener() {
2286   return ::MayHavePaintEventListener(mDocument->GetInnerWindow());
2287 }
2288 
MayHavePaintEventListenerInSubDocument()2289 bool nsPresContext::MayHavePaintEventListenerInSubDocument() {
2290   if (MayHavePaintEventListener()) {
2291     return true;
2292   }
2293 
2294   bool result = false;
2295   mDocument->EnumerateSubDocuments(MayHavePaintEventListenerSubdocumentCallback,
2296                                    &result);
2297   return result;
2298 }
2299 
NotifyInvalidation(uint64_t aTransactionId,const nsIntRect & aRect)2300 void nsPresContext::NotifyInvalidation(uint64_t aTransactionId,
2301                                        const nsIntRect& aRect) {
2302   // Prevent values from overflow after DevPixelsToAppUnits().
2303   //
2304   // DevPixelsTopAppUnits() will multiple a factor (60) to the value,
2305   // it may make the result value over the edge (overflow) of max or
2306   // min value of int32_t. Compute the max sized dev pixel rect that
2307   // we can support and intersect with it.
2308   nsIntRect clampedRect = nsIntRect::MaxIntRect();
2309   clampedRect.ScaleInverseRoundIn(AppUnitsPerDevPixel());
2310 
2311   clampedRect = clampedRect.Intersect(aRect);
2312 
2313   nsRect rect(DevPixelsToAppUnits(clampedRect.x),
2314               DevPixelsToAppUnits(clampedRect.y),
2315               DevPixelsToAppUnits(clampedRect.width),
2316               DevPixelsToAppUnits(clampedRect.height));
2317   NotifyInvalidation(aTransactionId, rect);
2318 }
2319 
GetInvalidations(uint64_t aTransactionId)2320 nsPresContext::TransactionInvalidations* nsPresContext::GetInvalidations(
2321     uint64_t aTransactionId) {
2322   for (TransactionInvalidations& t : mTransactions) {
2323     if (t.mTransactionId == aTransactionId) {
2324       return &t;
2325     }
2326   }
2327   return nullptr;
2328 }
2329 
NotifyInvalidation(uint64_t aTransactionId,const nsRect & aRect)2330 void nsPresContext::NotifyInvalidation(uint64_t aTransactionId,
2331                                        const nsRect& aRect) {
2332   MOZ_ASSERT(GetContainerWeak(), "Invalidation in detached pres context");
2333 
2334   // If there is no paint event listener, then we don't need to fire
2335   // the asynchronous event. We don't even need to record invalidation.
2336   // MayHavePaintEventListener is pretty cheap and we could make it
2337   // even cheaper by providing a more efficient
2338   // nsPIDOMWindow::GetListenerManager.
2339 
2340   nsPresContext* pc;
2341   for (pc = this; pc; pc = pc->GetParentPresContext()) {
2342     TransactionInvalidations* transaction =
2343         pc->GetInvalidations(aTransactionId);
2344     if (transaction) {
2345       break;
2346     } else {
2347       transaction = pc->mTransactions.AppendElement();
2348       transaction->mTransactionId = aTransactionId;
2349     }
2350   }
2351   if (!pc) {
2352     nsRootPresContext* rpc = GetRootPresContext();
2353     if (rpc) {
2354       rpc->EnsureEventualDidPaintEvent(aTransactionId);
2355     }
2356   }
2357 
2358   TransactionInvalidations* transaction = GetInvalidations(aTransactionId);
2359   MOZ_ASSERT(transaction);
2360   transaction->mInvalidations.AppendElement(aRect);
2361 }
2362 
NotifySubDocInvalidation(ContainerLayer * aContainer,const nsIntRegion * aRegion)2363 /* static */ void nsPresContext::NotifySubDocInvalidation(
2364     ContainerLayer* aContainer, const nsIntRegion* aRegion) {
2365   ContainerLayerPresContext* data = static_cast<ContainerLayerPresContext*>(
2366       aContainer->GetUserData(&gNotifySubDocInvalidationData));
2367   if (!data) {
2368     return;
2369   }
2370 
2371   uint64_t transactionId = aContainer->Manager()->GetLastTransactionId();
2372   IntRect visibleBounds =
2373       aContainer->GetVisibleRegion().GetBounds().ToUnknownRect();
2374 
2375   if (!aRegion) {
2376     IntRect rect(IntPoint(0, 0), visibleBounds.Size());
2377     data->mPresContext->NotifyInvalidation(transactionId, rect);
2378     return;
2379   }
2380 
2381   nsIntPoint topLeft = visibleBounds.TopLeft();
2382   for (auto iter = aRegion->RectIter(); !iter.Done(); iter.Next()) {
2383     nsIntRect rect(iter.Get());
2384     // PresContext coordinate space is relative to the start of our visible
2385     // region. Is this really true? This feels like the wrong way to get the
2386     // right answer.
2387     rect.MoveBy(-topLeft);
2388     data->mPresContext->NotifyInvalidation(transactionId, rect);
2389   }
2390 }
2391 
SetNotifySubDocInvalidationData(ContainerLayer * aContainer)2392 void nsPresContext::SetNotifySubDocInvalidationData(
2393     ContainerLayer* aContainer) {
2394   ContainerLayerPresContext* pres = new ContainerLayerPresContext;
2395   pres->mPresContext = this;
2396   aContainer->SetUserData(&gNotifySubDocInvalidationData, pres);
2397 }
2398 
ClearNotifySubDocInvalidationData(ContainerLayer * aContainer)2399 /* static */ void nsPresContext::ClearNotifySubDocInvalidationData(
2400     ContainerLayer* aContainer) {
2401   aContainer->SetUserData(&gNotifySubDocInvalidationData, nullptr);
2402 }
2403 
2404 struct NotifyDidPaintSubdocumentCallbackClosure {
2405   uint64_t mTransactionId;
2406   const mozilla::TimeStamp& mTimeStamp;
2407 };
NotifyDidPaintSubdocumentCallback(nsIDocument * aDocument,void * aData)2408 /* static */ bool nsPresContext::NotifyDidPaintSubdocumentCallback(
2409     nsIDocument* aDocument, void* aData) {
2410   NotifyDidPaintSubdocumentCallbackClosure* closure =
2411       static_cast<NotifyDidPaintSubdocumentCallbackClosure*>(aData);
2412   nsPresContext* pc = aDocument->GetPresContext();
2413   if (pc) {
2414     pc->NotifyDidPaintForSubtree(closure->mTransactionId, closure->mTimeStamp);
2415   }
2416   return true;
2417 }
2418 
2419 class DelayedFireDOMPaintEvent : public Runnable {
2420  public:
DelayedFireDOMPaintEvent(nsPresContext * aPresContext,nsTArray<nsRect> * aList,uint64_t aTransactionId,const mozilla::TimeStamp & aTimeStamp=mozilla::TimeStamp ())2421   DelayedFireDOMPaintEvent(
2422       nsPresContext* aPresContext, nsTArray<nsRect>* aList,
2423       uint64_t aTransactionId,
2424       const mozilla::TimeStamp& aTimeStamp = mozilla::TimeStamp())
2425       : mozilla::Runnable("DelayedFireDOMPaintEvent"),
2426         mPresContext(aPresContext),
2427         mTransactionId(aTransactionId),
2428         mTimeStamp(aTimeStamp) {
2429     MOZ_ASSERT(mPresContext->GetContainerWeak(),
2430                "DOMPaintEvent requested for a detached pres context");
2431     mList.SwapElements(*aList);
2432   }
Run()2433   NS_IMETHOD Run() override {
2434     // The pres context might have been detached during the delay -
2435     // that's fine, just don't fire the event.
2436     if (mPresContext->GetContainerWeak()) {
2437       mPresContext->FireDOMPaintEvent(&mList, mTransactionId, mTimeStamp);
2438     }
2439     return NS_OK;
2440   }
2441 
2442   RefPtr<nsPresContext> mPresContext;
2443   uint64_t mTransactionId;
2444   const mozilla::TimeStamp mTimeStamp;
2445   nsTArray<nsRect> mList;
2446 };
2447 
NotifyDidPaintForSubtree(uint64_t aTransactionId,const mozilla::TimeStamp & aTimeStamp)2448 void nsPresContext::NotifyDidPaintForSubtree(
2449     uint64_t aTransactionId, const mozilla::TimeStamp& aTimeStamp) {
2450   if (IsRoot()) {
2451     static_cast<nsRootPresContext*>(this)->CancelDidPaintTimers(aTransactionId);
2452 
2453     if (mTransactions.IsEmpty()) {
2454       return;
2455     }
2456   }
2457 
2458   if (!PresShell()->IsVisible() && mTransactions.IsEmpty()) {
2459     return;
2460   }
2461 
2462   // Non-root prescontexts fire MozAfterPaint to all their descendants
2463   // unconditionally, even if no invalidations have been collected. This is
2464   // because we don't want to eat the cost of collecting invalidations for
2465   // every subdocument (which would require putting every subdocument in its
2466   // own layer).
2467 
2468   bool sent = false;
2469   uint32_t i = 0;
2470   while (i < mTransactions.Length()) {
2471     if (mTransactions[i].mTransactionId <= aTransactionId) {
2472       if (!mTransactions[i].mInvalidations.IsEmpty()) {
2473         nsCOMPtr<nsIRunnable> ev = new DelayedFireDOMPaintEvent(
2474             this, &mTransactions[i].mInvalidations,
2475             mTransactions[i].mTransactionId, aTimeStamp);
2476         nsContentUtils::AddScriptRunner(ev);
2477         sent = true;
2478       }
2479       mTransactions.RemoveElementAt(i);
2480     } else {
2481       i++;
2482     }
2483   }
2484 
2485   if (!sent) {
2486     nsTArray<nsRect> dummy;
2487     nsCOMPtr<nsIRunnable> ev =
2488         new DelayedFireDOMPaintEvent(this, &dummy, aTransactionId, aTimeStamp);
2489     nsContentUtils::AddScriptRunner(ev);
2490   }
2491 
2492   NotifyDidPaintSubdocumentCallbackClosure closure = {aTransactionId,
2493                                                       aTimeStamp};
2494   mDocument->EnumerateSubDocuments(
2495       nsPresContext::NotifyDidPaintSubdocumentCallback, &closure);
2496 }
2497 
CreateTimer(nsTimerCallbackFunc aCallback,const char * aName,uint32_t aDelay)2498 already_AddRefed<nsITimer> nsPresContext::CreateTimer(
2499     nsTimerCallbackFunc aCallback, const char* aName, uint32_t aDelay) {
2500   nsCOMPtr<nsITimer> timer;
2501   NS_NewTimerWithFuncCallback(getter_AddRefs(timer), aCallback, this, aDelay,
2502                               nsITimer::TYPE_ONE_SHOT, aName,
2503                               Document()->EventTargetFor(TaskCategory::Other));
2504   return timer.forget();
2505 }
2506 
2507 static bool sGotInterruptEnv = false;
2508 enum InterruptMode { ModeRandom, ModeCounter, ModeEvent };
2509 // Controlled by the GECKO_REFLOW_INTERRUPT_MODE env var; allowed values are
2510 // "random" (except on Windows) or "counter".  If neither is used, the mode is
2511 // ModeEvent.
2512 static InterruptMode sInterruptMode = ModeEvent;
2513 #ifndef XP_WIN
2514 // Used for the "random" mode.  Controlled by the GECKO_REFLOW_INTERRUPT_SEED
2515 // env var.
2516 static uint32_t sInterruptSeed = 1;
2517 #endif
2518 // Used for the "counter" mode.  This is the number of unskipped interrupt
2519 // checks that have to happen before we interrupt.  Controlled by the
2520 // GECKO_REFLOW_INTERRUPT_FREQUENCY env var.
2521 static uint32_t sInterruptMaxCounter = 10;
2522 // Used for the "counter" mode.  This counts up to sInterruptMaxCounter and is
2523 // then reset to 0.
2524 static uint32_t sInterruptCounter;
2525 // Number of interrupt checks to skip before really trying to interrupt.
2526 // Controlled by the GECKO_REFLOW_INTERRUPT_CHECKS_TO_SKIP env var.
2527 static uint32_t sInterruptChecksToSkip = 200;
2528 // Number of milliseconds that a reflow should be allowed to run for before we
2529 // actually allow interruption.  Controlled by the
2530 // GECKO_REFLOW_MIN_NOINTERRUPT_DURATION env var.  Can't be initialized here,
2531 // because TimeDuration/TimeStamp is not safe to use in static constructors..
2532 static TimeDuration sInterruptTimeout;
2533 
GetInterruptEnv()2534 static void GetInterruptEnv() {
2535   char* ev = PR_GetEnv("GECKO_REFLOW_INTERRUPT_MODE");
2536   if (ev) {
2537 #ifndef XP_WIN
2538     if (PL_strcasecmp(ev, "random") == 0) {
2539       ev = PR_GetEnv("GECKO_REFLOW_INTERRUPT_SEED");
2540       if (ev) {
2541         sInterruptSeed = atoi(ev);
2542       }
2543       srandom(sInterruptSeed);
2544       sInterruptMode = ModeRandom;
2545     } else
2546 #endif
2547         if (PL_strcasecmp(ev, "counter") == 0) {
2548       ev = PR_GetEnv("GECKO_REFLOW_INTERRUPT_FREQUENCY");
2549       if (ev) {
2550         sInterruptMaxCounter = atoi(ev);
2551       }
2552       sInterruptCounter = 0;
2553       sInterruptMode = ModeCounter;
2554     }
2555   }
2556   ev = PR_GetEnv("GECKO_REFLOW_INTERRUPT_CHECKS_TO_SKIP");
2557   if (ev) {
2558     sInterruptChecksToSkip = atoi(ev);
2559   }
2560 
2561   ev = PR_GetEnv("GECKO_REFLOW_MIN_NOINTERRUPT_DURATION");
2562   int duration_ms = ev ? atoi(ev) : 100;
2563   sInterruptTimeout = TimeDuration::FromMilliseconds(duration_ms);
2564 }
2565 
HavePendingInputEvent()2566 bool nsPresContext::HavePendingInputEvent() {
2567   switch (sInterruptMode) {
2568 #ifndef XP_WIN
2569     case ModeRandom:
2570       return (random() & 1);
2571 #endif
2572     case ModeCounter:
2573       if (sInterruptCounter < sInterruptMaxCounter) {
2574         ++sInterruptCounter;
2575         return false;
2576       }
2577       sInterruptCounter = 0;
2578       return true;
2579     default:
2580     case ModeEvent: {
2581       nsIFrame* f = PresShell()->GetRootFrame();
2582       if (f) {
2583         nsIWidget* w = f->GetNearestWidget();
2584         if (w) {
2585           return w->HasPendingInputEvent();
2586         }
2587       }
2588       return false;
2589     }
2590   }
2591 }
2592 
NotifyFontFaceSetOnRefresh()2593 void nsPresContext::NotifyFontFaceSetOnRefresh() {
2594   FontFaceSet* set = mDocument->GetFonts();
2595   if (set) {
2596     set->DidRefresh();
2597   }
2598 }
2599 
HasPendingRestyleOrReflow()2600 bool nsPresContext::HasPendingRestyleOrReflow() {
2601   nsIPresShell* shell = PresShell();
2602   return shell->NeedStyleFlush() || shell->HasPendingReflow();
2603 }
2604 
ReflowStarted(bool aInterruptible)2605 void nsPresContext::ReflowStarted(bool aInterruptible) {
2606 #ifdef NOISY_INTERRUPTIBLE_REFLOW
2607   if (!aInterruptible) {
2608     printf("STARTING NONINTERRUPTIBLE REFLOW\n");
2609   }
2610 #endif
2611   // We don't support interrupting in paginated contexts, since page
2612   // sequences only handle initial reflow
2613   mInterruptsEnabled = aInterruptible && !IsPaginated() &&
2614                        nsLayoutUtils::InterruptibleReflowEnabled();
2615 
2616   // Don't set mHasPendingInterrupt based on HavePendingInputEvent() here.  If
2617   // we ever change that, then we need to update the code in
2618   // PresShell::DoReflow to only add the just-reflown root to dirty roots if
2619   // it's actually dirty.  Otherwise we can end up adding a root that has no
2620   // interruptible descendants, just because we detected an interrupt at reflow
2621   // start.
2622   mHasPendingInterrupt = false;
2623 
2624   mInterruptChecksToSkip = sInterruptChecksToSkip;
2625 
2626   if (mInterruptsEnabled) {
2627     mReflowStartTime = TimeStamp::Now();
2628   }
2629 }
2630 
CheckForInterrupt(nsIFrame * aFrame)2631 bool nsPresContext::CheckForInterrupt(nsIFrame* aFrame) {
2632   if (mHasPendingInterrupt) {
2633     mShell->FrameNeedsToContinueReflow(aFrame);
2634     return true;
2635   }
2636 
2637   if (!sGotInterruptEnv) {
2638     sGotInterruptEnv = true;
2639     GetInterruptEnv();
2640   }
2641 
2642   if (!mInterruptsEnabled) {
2643     return false;
2644   }
2645 
2646   if (mInterruptChecksToSkip > 0) {
2647     --mInterruptChecksToSkip;
2648     return false;
2649   }
2650   mInterruptChecksToSkip = sInterruptChecksToSkip;
2651 
2652   // Don't interrupt if it's been less than sInterruptTimeout since we started
2653   // the reflow.
2654   mHasPendingInterrupt =
2655       TimeStamp::Now() - mReflowStartTime > sInterruptTimeout &&
2656       HavePendingInputEvent() && !IsChrome();
2657 
2658   if (mPendingInterruptFromTest) {
2659     mPendingInterruptFromTest = false;
2660     mHasPendingInterrupt = true;
2661   }
2662 
2663   if (mHasPendingInterrupt) {
2664 #ifdef NOISY_INTERRUPTIBLE_REFLOW
2665     printf("*** DETECTED pending interrupt (time=%lld)\n", PR_Now());
2666 #endif /* NOISY_INTERRUPTIBLE_REFLOW */
2667     mShell->FrameNeedsToContinueReflow(aFrame);
2668   }
2669   return mHasPendingInterrupt;
2670 }
2671 
GetPrimaryFrameFor(nsIContent * aContent)2672 nsIFrame* nsPresContext::GetPrimaryFrameFor(nsIContent* aContent) {
2673   NS_PRECONDITION(aContent, "Don't do that");
2674   if (GetPresShell() &&
2675       GetPresShell()->GetDocument() == aContent->GetComposedDoc()) {
2676     return aContent->GetPrimaryFrame();
2677   }
2678   return nullptr;
2679 }
2680 
SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const2681 size_t nsPresContext::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
2682   return mLangGroupFontPrefs.SizeOfExcludingThis(aMallocSizeOf);
2683 
2684   // Measurement of other members may be added later if DMD finds it is
2685   // worthwhile.
2686 }
2687 
IsRootContentDocument() const2688 bool nsPresContext::IsRootContentDocument() const {
2689   // We are a root content document if: we are not a resource doc, we are
2690   // not chrome, and we either have no parent or our parent is chrome.
2691   if (mDocument->IsResourceDoc()) {
2692     return false;
2693   }
2694   if (IsChrome()) {
2695     return false;
2696   }
2697   // We may not have a root frame, so use views.
2698   nsView* view = PresShell()->GetViewManager()->GetRootView();
2699   if (!view) {
2700     return false;
2701   }
2702   view = view->GetParent();  // anonymous inner view
2703   if (!view) {
2704     return true;
2705   }
2706   view = view->GetParent();  // subdocumentframe's view
2707   if (!view) {
2708     return true;
2709   }
2710 
2711   nsIFrame* f = view->GetFrame();
2712   return (f && f->PresContext()->IsChrome());
2713 }
2714 
NotifyNonBlankPaint()2715 void nsPresContext::NotifyNonBlankPaint() {
2716   MOZ_ASSERT(!mHadNonBlankPaint);
2717   mHadNonBlankPaint = true;
2718   if (IsRootContentDocument()) {
2719     RefPtr<nsDOMNavigationTiming> timing = mDocument->GetNavigationTiming();
2720     if (timing) {
2721       timing->NotifyNonBlankPaintForRootContentDocument();
2722     }
2723 
2724     mFirstNonBlankPaintTime = TimeStamp::Now();
2725   }
2726 }
2727 
GetPaintFlashing() const2728 bool nsPresContext::GetPaintFlashing() const {
2729   if (!mPaintFlashingInitialized) {
2730     bool pref = Preferences::GetBool("nglayout.debug.paint_flashing");
2731     if (!pref && IsChrome()) {
2732       pref = Preferences::GetBool("nglayout.debug.paint_flashing_chrome");
2733     }
2734     mPaintFlashing = pref;
2735     mPaintFlashingInitialized = true;
2736   }
2737   return mPaintFlashing;
2738 }
2739 
AppUnitsPerDevPixel() const2740 int32_t nsPresContext::AppUnitsPerDevPixel() const {
2741   return mDeviceContext->AppUnitsPerDevPixel();
2742 }
2743 
GfxUnitsToAppUnits(gfxFloat aGfxUnits) const2744 nscoord nsPresContext::GfxUnitsToAppUnits(gfxFloat aGfxUnits) const {
2745   return mDeviceContext->GfxUnitsToAppUnits(aGfxUnits);
2746 }
2747 
AppUnitsToGfxUnits(nscoord aAppUnits) const2748 gfxFloat nsPresContext::AppUnitsToGfxUnits(nscoord aAppUnits) const {
2749   return mDeviceContext->AppUnitsToGfxUnits(aAppUnits);
2750 }
2751 
PhysicalMillimetersToAppUnits(float aMM) const2752 nscoord nsPresContext::PhysicalMillimetersToAppUnits(float aMM) const {
2753   float inches = aMM / MM_PER_INCH_FLOAT;
2754   return NSToCoordFloorClamped(
2755       inches * float(DeviceContext()->AppUnitsPerPhysicalInch()));
2756 }
2757 
IsDeviceSizePageSize()2758 bool nsPresContext::IsDeviceSizePageSize() {
2759   bool isDeviceSizePageSize = false;
2760   nsCOMPtr<nsIDocShell> docShell(mContainer);
2761   if (docShell) {
2762     isDeviceSizePageSize = docShell->GetDeviceSizeIsPageSize();
2763   }
2764   return isDeviceSizePageSize;
2765 }
2766 
GetRestyleGeneration() const2767 uint64_t nsPresContext::GetRestyleGeneration() const {
2768   if (!mRestyleManager) {
2769     return 0;
2770   }
2771   return mRestyleManager->GetRestyleGeneration();
2772 }
2773 
GetUndisplayedRestyleGeneration() const2774 uint64_t nsPresContext::GetUndisplayedRestyleGeneration() const {
2775   if (!mRestyleManager) {
2776     return 0;
2777   }
2778   return mRestyleManager->GetUndisplayedRestyleGeneration();
2779 }
2780 
GetBidiEngine()2781 nsBidi& nsPresContext::GetBidiEngine() {
2782   MOZ_ASSERT(NS_IsMainThread());
2783 
2784   if (!mBidiEngine) {
2785     mBidiEngine.reset(new nsBidi());
2786   }
2787   return *mBidiEngine;
2788 }
2789 
FlushFontFeatureValues()2790 void nsPresContext::FlushFontFeatureValues() {
2791   if (!mShell) {
2792     return;  // we've been torn down
2793   }
2794 
2795   if (mFontFeatureValuesDirty) {
2796     StyleSetHandle styleSet = mShell->StyleSet();
2797     mFontFeatureValuesLookup = styleSet->BuildFontFeatureValueSet();
2798     mFontFeatureValuesDirty = false;
2799   }
2800 }
2801 
nsRootPresContext(nsIDocument * aDocument,nsPresContextType aType)2802 nsRootPresContext::nsRootPresContext(nsIDocument* aDocument,
2803                                      nsPresContextType aType)
2804     : nsPresContext(aDocument, aType) {}
2805 
~nsRootPresContext()2806 nsRootPresContext::~nsRootPresContext() {
2807   NS_ASSERTION(mRegisteredPlugins.Count() == 0,
2808                "All plugins should have been unregistered");
2809   CancelAllDidPaintTimers();
2810   CancelApplyPluginGeometryTimer();
2811 }
2812 
Detach()2813 /* virtual */ void nsRootPresContext::Detach() {
2814   CancelAllDidPaintTimers();
2815   // XXXmats maybe also CancelApplyPluginGeometryTimer(); ?
2816   nsPresContext::Detach();
2817 }
2818 
RegisterPluginForGeometryUpdates(nsIContent * aPlugin)2819 void nsRootPresContext::RegisterPluginForGeometryUpdates(nsIContent* aPlugin) {
2820   mRegisteredPlugins.PutEntry(aPlugin);
2821 }
2822 
UnregisterPluginForGeometryUpdates(nsIContent * aPlugin)2823 void nsRootPresContext::UnregisterPluginForGeometryUpdates(
2824     nsIContent* aPlugin) {
2825   mRegisteredPlugins.RemoveEntry(aPlugin);
2826 }
2827 
ComputePluginGeometryUpdates(nsIFrame * aFrame,nsDisplayListBuilder * aBuilder,nsDisplayList * aList)2828 void nsRootPresContext::ComputePluginGeometryUpdates(
2829     nsIFrame* aFrame, nsDisplayListBuilder* aBuilder, nsDisplayList* aList) {
2830   if (mRegisteredPlugins.Count() == 0) {
2831     return;
2832   }
2833 
2834   // Initially make the next state for each plugin descendant of aFrame be
2835   // "hidden". Plugins that are visible will have their next state set to
2836   // unhidden by nsDisplayPlugin::ComputeVisibility.
2837   for (auto iter = mRegisteredPlugins.Iter(); !iter.Done(); iter.Next()) {
2838     auto f =
2839         static_cast<nsPluginFrame*>(iter.Get()->GetKey()->GetPrimaryFrame());
2840     if (!f) {
2841       NS_WARNING("Null frame in ComputePluginGeometryUpdates");
2842       continue;
2843     }
2844     if (!nsLayoutUtils::IsAncestorFrameCrossDoc(aFrame, f)) {
2845       // f is not managed by this frame so we should ignore it.
2846       continue;
2847     }
2848     f->SetEmptyWidgetConfiguration();
2849   }
2850 
2851   if (aBuilder) {
2852     MOZ_ASSERT(aList);
2853     nsIFrame* rootFrame = mShell->GetRootFrame();
2854 
2855     if (rootFrame && aBuilder->ContainsPluginItem()) {
2856       aBuilder->SetForPluginGeometry(true);
2857       // Merging and flattening has already been done and we should not do it
2858       // again. nsDisplayScroll(Info)Layer doesn't support trying to flatten
2859       // again.
2860       aBuilder->SetAllowMergingAndFlattening(false);
2861       nsRegion region = rootFrame->GetVisualOverflowRectRelativeToSelf();
2862       // nsDisplayPlugin::ComputeVisibility will automatically set a non-hidden
2863       // widget configuration for the plugin, if it's visible.
2864       aList->ComputeVisibilityForRoot(aBuilder, &region);
2865       aBuilder->SetForPluginGeometry(false);
2866     }
2867   }
2868 
2869 #ifdef XP_MACOSX
2870   // We control painting of Mac plugins, so just apply geometry updates now.
2871   // This is not happening during a paint event.
2872   ApplyPluginGeometryUpdates();
2873 #else
2874   if (XRE_IsParentProcess()) {
2875     InitApplyPluginGeometryTimer();
2876   }
2877 #endif
2878 }
2879 
ApplyPluginGeometryUpdatesCallback(nsITimer * aTimer,void * aClosure)2880 static void ApplyPluginGeometryUpdatesCallback(nsITimer* aTimer,
2881                                                void* aClosure) {
2882   static_cast<nsRootPresContext*>(aClosure)->ApplyPluginGeometryUpdates();
2883 }
2884 
InitApplyPluginGeometryTimer()2885 void nsRootPresContext::InitApplyPluginGeometryTimer() {
2886   if (mApplyPluginGeometryTimer) {
2887     return;
2888   }
2889 
2890   // We'll apply the plugin geometry updates during the next compositing paint
2891   // in this presContext (either from nsPresShell::WillPaintWindow or from
2892   // nsPresShell::DidPaintWindow, depending on the platform).  But paints might
2893   // get optimized away if the old plugin geometry covers the invalid region,
2894   // so set a backup timer to do this too.  We want to make sure this
2895   // won't fire before our normal paint notifications, if those would
2896   // update the geometry, so set it for double the refresh driver interval.
2897   mApplyPluginGeometryTimer = CreateTimer(
2898       ApplyPluginGeometryUpdatesCallback, "ApplyPluginGeometryUpdatesCallback",
2899       nsRefreshDriver::DefaultInterval() * 2);
2900 }
2901 
CancelApplyPluginGeometryTimer()2902 void nsRootPresContext::CancelApplyPluginGeometryTimer() {
2903   if (mApplyPluginGeometryTimer) {
2904     mApplyPluginGeometryTimer->Cancel();
2905     mApplyPluginGeometryTimer = nullptr;
2906   }
2907 }
2908 
2909 #ifndef XP_MACOSX
2910 
HasOverlap(const LayoutDeviceIntPoint & aOffset1,const nsTArray<LayoutDeviceIntRect> & aClipRects1,const LayoutDeviceIntPoint & aOffset2,const nsTArray<LayoutDeviceIntRect> & aClipRects2)2911 static bool HasOverlap(const LayoutDeviceIntPoint& aOffset1,
2912                        const nsTArray<LayoutDeviceIntRect>& aClipRects1,
2913                        const LayoutDeviceIntPoint& aOffset2,
2914                        const nsTArray<LayoutDeviceIntRect>& aClipRects2) {
2915   LayoutDeviceIntPoint offsetDelta = aOffset1 - aOffset2;
2916   for (uint32_t i = 0; i < aClipRects1.Length(); ++i) {
2917     for (uint32_t j = 0; j < aClipRects2.Length(); ++j) {
2918       if ((aClipRects1[i] + offsetDelta).Intersects(aClipRects2[j])) {
2919         return true;
2920       }
2921     }
2922   }
2923   return false;
2924 }
2925 
2926 /**
2927  * Given a list of plugin windows to move to new locations, sort the list
2928  * so that for each window move, the window moves to a location that
2929  * does not intersect other windows. This minimizes flicker and repainting.
2930  * It's not always possible to do this perfectly, since in general
2931  * we might have cycles. But we do our best.
2932  * We need to take into account that windows are clipped to particular
2933  * regions and the clip regions change as the windows are moved.
2934  */
SortConfigurations(nsTArray<nsIWidget::Configuration> * aConfigurations)2935 static void SortConfigurations(
2936     nsTArray<nsIWidget::Configuration>* aConfigurations) {
2937   if (aConfigurations->Length() > 10) {
2938     // Give up, we don't want to get bogged down here
2939     return;
2940   }
2941 
2942   nsTArray<nsIWidget::Configuration> pluginsToMove;
2943   pluginsToMove.SwapElements(*aConfigurations);
2944 
2945   // Our algorithm is quite naive. At each step we try to identify
2946   // a window that can be moved to its new location that won't overlap
2947   // any other windows at the new location. If there is no such
2948   // window, we just move the last window in the list anyway.
2949   while (!pluginsToMove.IsEmpty()) {
2950     // Find a window whose destination does not overlap any other window
2951     uint32_t i;
2952     for (i = 0; i + 1 < pluginsToMove.Length(); ++i) {
2953       nsIWidget::Configuration* config = &pluginsToMove[i];
2954       bool foundOverlap = false;
2955       for (uint32_t j = 0; j < pluginsToMove.Length(); ++j) {
2956         if (i == j) continue;
2957         LayoutDeviceIntRect bounds = pluginsToMove[j].mChild->GetBounds();
2958         AutoTArray<LayoutDeviceIntRect, 1> clipRects;
2959         pluginsToMove[j].mChild->GetWindowClipRegion(&clipRects);
2960         if (HasOverlap(bounds.TopLeft(), clipRects, config->mBounds.TopLeft(),
2961                        config->mClipRegion)) {
2962           foundOverlap = true;
2963           break;
2964         }
2965       }
2966       if (!foundOverlap) break;
2967     }
2968     // Note that we always move the last plugin in pluginsToMove, if we
2969     // can't find any other plugin to move
2970     aConfigurations->AppendElement(pluginsToMove[i]);
2971     pluginsToMove.RemoveElementAt(i);
2972   }
2973 }
2974 
PluginGetGeometryUpdate(nsTHashtable<nsRefPtrHashKey<nsIContent>> & aPlugins,nsTArray<nsIWidget::Configuration> * aConfigurations)2975 static void PluginGetGeometryUpdate(
2976     nsTHashtable<nsRefPtrHashKey<nsIContent>>& aPlugins,
2977     nsTArray<nsIWidget::Configuration>* aConfigurations) {
2978   for (auto iter = aPlugins.Iter(); !iter.Done(); iter.Next()) {
2979     auto f =
2980         static_cast<nsPluginFrame*>(iter.Get()->GetKey()->GetPrimaryFrame());
2981     if (!f) {
2982       NS_WARNING("Null frame in PluginGeometryUpdate");
2983       continue;
2984     }
2985     f->GetWidgetConfiguration(aConfigurations);
2986   }
2987 }
2988 
2989 #endif  // #ifndef XP_MACOSX
2990 
PluginDidSetGeometry(nsTHashtable<nsRefPtrHashKey<nsIContent>> & aPlugins)2991 static void PluginDidSetGeometry(
2992     nsTHashtable<nsRefPtrHashKey<nsIContent>>& aPlugins) {
2993   for (auto iter = aPlugins.Iter(); !iter.Done(); iter.Next()) {
2994     auto f =
2995         static_cast<nsPluginFrame*>(iter.Get()->GetKey()->GetPrimaryFrame());
2996     if (!f) {
2997       NS_WARNING("Null frame in PluginDidSetGeometry");
2998       continue;
2999     }
3000     f->DidSetWidgetGeometry();
3001   }
3002 }
3003 
ApplyPluginGeometryUpdates()3004 void nsRootPresContext::ApplyPluginGeometryUpdates() {
3005 #ifndef XP_MACOSX
3006   CancelApplyPluginGeometryTimer();
3007 
3008   nsTArray<nsIWidget::Configuration> configurations;
3009   PluginGetGeometryUpdate(mRegisteredPlugins, &configurations);
3010   // Walk mRegisteredPlugins and ask each plugin for its configuration
3011   if (!configurations.IsEmpty()) {
3012     nsIWidget* widget = configurations[0].mChild->GetParent();
3013     NS_ASSERTION(widget, "Plugins must have a parent window");
3014     SortConfigurations(&configurations);
3015     widget->ConfigureChildren(configurations);
3016   }
3017 #endif  // #ifndef XP_MACOSX
3018 
3019   PluginDidSetGeometry(mRegisteredPlugins);
3020 }
3021 
CollectPluginGeometryUpdates(LayerManager * aLayerManager)3022 void nsRootPresContext::CollectPluginGeometryUpdates(
3023     LayerManager* aLayerManager) {
3024 #ifndef XP_MACOSX
3025   // Collect and pass plugin widget configurations down to the compositor
3026   // for transmission to the chrome process.
3027   NS_ASSERTION(aLayerManager, "layer manager is invalid!");
3028   mozilla::layers::ClientLayerManager* clm =
3029       aLayerManager->AsClientLayerManager();
3030 
3031   nsTArray<nsIWidget::Configuration> configurations;
3032   // If there aren't any plugins to configure, clear the plugin data cache
3033   // in the layer system.
3034   if (!mRegisteredPlugins.Count() && clm) {
3035     clm->StorePluginWidgetConfigurations(configurations);
3036     return;
3037   }
3038   PluginGetGeometryUpdate(mRegisteredPlugins, &configurations);
3039   if (configurations.IsEmpty()) {
3040     PluginDidSetGeometry(mRegisteredPlugins);
3041     return;
3042   }
3043   SortConfigurations(&configurations);
3044   if (clm) {
3045     clm->StorePluginWidgetConfigurations(configurations);
3046   }
3047   PluginDidSetGeometry(mRegisteredPlugins);
3048 #endif  // #ifndef XP_MACOSX
3049 }
3050 
EnsureEventualDidPaintEvent(uint64_t aTransactionId)3051 void nsRootPresContext::EnsureEventualDidPaintEvent(uint64_t aTransactionId) {
3052   for (NotifyDidPaintTimer& t : mNotifyDidPaintTimers) {
3053     if (t.mTransactionId == aTransactionId) {
3054       return;
3055     }
3056   }
3057 
3058   nsCOMPtr<nsITimer> timer;
3059   RefPtr<nsRootPresContext> self = this;
3060   nsresult rv = NS_NewTimerWithCallback(
3061       getter_AddRefs(timer),
3062       NewNamedTimerCallback(
3063           [self, aTransactionId]() {
3064             nsAutoScriptBlocker blockScripts;
3065             self->NotifyDidPaintForSubtree(aTransactionId);
3066           },
3067           "NotifyDidPaintForSubtree"),
3068       100, nsITimer::TYPE_ONE_SHOT,
3069       Document()->EventTargetFor(TaskCategory::Other));
3070 
3071   if (NS_SUCCEEDED(rv)) {
3072     NotifyDidPaintTimer* t = mNotifyDidPaintTimers.AppendElement();
3073     t->mTransactionId = aTransactionId;
3074     t->mTimer = timer;
3075   }
3076 }
3077 
CancelDidPaintTimers(uint64_t aTransactionId)3078 void nsRootPresContext::CancelDidPaintTimers(uint64_t aTransactionId) {
3079   uint32_t i = 0;
3080   while (i < mNotifyDidPaintTimers.Length()) {
3081     if (mNotifyDidPaintTimers[i].mTransactionId <= aTransactionId) {
3082       mNotifyDidPaintTimers[i].mTimer->Cancel();
3083       mNotifyDidPaintTimers.RemoveElementAt(i);
3084     } else {
3085       i++;
3086     }
3087   }
3088 }
3089 
CancelAllDidPaintTimers()3090 void nsRootPresContext::CancelAllDidPaintTimers() {
3091   for (uint32_t i = 0; i < mNotifyDidPaintTimers.Length(); i++) {
3092     mNotifyDidPaintTimers[i].mTimer->Cancel();
3093   }
3094   mNotifyDidPaintTimers.Clear();
3095 }
3096 
AddWillPaintObserver(nsIRunnable * aRunnable)3097 void nsRootPresContext::AddWillPaintObserver(nsIRunnable* aRunnable) {
3098   if (!mWillPaintFallbackEvent.IsPending()) {
3099     mWillPaintFallbackEvent = new RunWillPaintObservers(this);
3100     Document()->Dispatch(TaskCategory::Other,
3101                          do_AddRef(mWillPaintFallbackEvent));
3102   }
3103   mWillPaintObservers.AppendElement(aRunnable);
3104 }
3105 
3106 /**
3107  * Run all runnables that need to get called before the next paint.
3108  */
FlushWillPaintObservers()3109 void nsRootPresContext::FlushWillPaintObservers() {
3110   mWillPaintFallbackEvent = nullptr;
3111   nsTArray<nsCOMPtr<nsIRunnable>> observers;
3112   observers.SwapElements(mWillPaintObservers);
3113   for (uint32_t i = 0; i < observers.Length(); ++i) {
3114     observers[i]->Run();
3115   }
3116 }
3117 
SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const3118 size_t nsRootPresContext::SizeOfExcludingThis(
3119     MallocSizeOf aMallocSizeOf) const {
3120   return nsPresContext::SizeOfExcludingThis(aMallocSizeOf);
3121 
3122   // Measurement of the following members may be added later if DMD finds it is
3123   // worthwhile:
3124   // - mNotifyDidPaintTimer
3125   // - mRegisteredPlugins
3126   // - mWillPaintObservers
3127   // - mWillPaintFallbackEvent
3128 }
3129