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, ®ion);
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