Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
* License, v. 2.0. If a copy of the MPL was not distributed with this
5
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
/* a presentation of a document, part 1 */
8
9
#include "nsPresContext.h"
10
#include "nsPresContextInlines.h"
11
12
#include "mozilla/ArrayUtils.h"
13
#include "mozilla/DebugOnly.h"
14
#include "mozilla/Encoding.h"
15
#include "mozilla/EventDispatcher.h"
16
#include "mozilla/EventStateManager.h"
17
#include "mozilla/PresShell.h"
18
#include "mozilla/PresShellInlines.h"
19
20
#include "base/basictypes.h"
21
22
#include "nsCOMPtr.h"
23
#include "nsCSSFrameConstructor.h"
24
#include "nsDocShell.h"
25
#include "nsIContentViewer.h"
26
#include "nsPIDOMWindow.h"
27
#include "mozilla/ServoStyleSet.h"
28
#include "nsIContent.h"
29
#include "nsIFrame.h"
30
#include "mozilla/dom/Document.h"
31
#include "mozilla/dom/DocumentInlines.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
#include "mozilla/RestyleManager.h"
42
#include "SurfaceCacheUtils.h"
43
#include "nsMediaFeatures.h"
44
#include "gfxPlatform.h"
45
#include "nsFontFaceLoader.h"
46
#include "mozilla/AnimationEventDispatcher.h"
47
#include "mozilla/EffectCompositor.h"
48
#include "mozilla/EventListenerManager.h"
49
#include "prenv.h"
50
#include "nsPluginFrame.h"
51
#include "nsTransitionManager.h"
52
#include "nsAnimationManager.h"
53
#include "CounterStyleManager.h"
54
#include "mozilla/MemoryReporting.h"
55
#include "mozilla/dom/Element.h"
56
#include "nsIMessageManager.h"
57
#include "mozilla/dom/HTMLBodyElement.h"
58
#include "mozilla/dom/MediaQueryList.h"
59
#include "mozilla/SMILAnimationController.h"
60
#include "mozilla/css/ImageLoader.h"
61
#include "mozilla/dom/PBrowserParent.h"
62
#include "mozilla/dom/BrowserChild.h"
63
#include "mozilla/dom/BrowserParent.h"
64
#include "nsRefreshDriver.h"
65
#include "Layers.h"
66
#include "LayerUserData.h"
67
#include "ClientLayerManager.h"
68
#include "mozilla/dom/NotifyPaintEvent.h"
69
70
#include "nsIDOMChromeWindow.h"
71
#include "nsFrameLoader.h"
72
#include "nsContentUtils.h"
73
#include "nsPIWindowRoot.h"
74
#include "mozilla/Preferences.h"
75
#include "gfxTextRun.h"
76
#include "nsFontFaceUtils.h"
77
#include "nsLayoutStylesheetCache.h"
78
#include "mozilla/ServoBindings.h"
79
#include "mozilla/StyleSheet.h"
80
#include "mozilla/StyleSheetInlines.h"
81
#include "mozilla/Telemetry.h"
82
#include "mozilla/dom/Performance.h"
83
#include "mozilla/dom/PerformanceTiming.h"
84
#include "mozilla/layers/APZThreadUtils.h"
85
86
// Needed for Start/Stop of Image Animation
87
#include "imgIContainer.h"
88
#include "nsIImageLoadingContent.h"
89
90
#include "nsBidiUtils.h"
91
#include "nsServiceManagerUtils.h"
92
#include "nsBidi.h"
93
94
#include "mozilla/dom/URL.h"
95
#include "mozilla/ServoCSSParser.h"
96
97
using namespace mozilla;
98
using namespace mozilla::dom;
99
using namespace mozilla::gfx;
100
using namespace mozilla::layers;
101
102
uint8_t gNotifySubDocInvalidationData;
103
104
/**
105
* Layer UserData for ContainerLayers that want to be notified
106
* of local invalidations of them and their descendant layers.
107
* Pass a callback to ComputeDifferences to have these called.
108
*/
109
class ContainerLayerPresContext : public LayerUserData {
110
public:
111
nsPresContext* mPresContext;
112
};
113
114
bool nsPresContext::IsDOMPaintEventPending() {
115
if (!mTransactions.IsEmpty()) {
116
return true;
117
}
118
119
nsRootPresContext* drpc = GetRootPresContext();
120
if (drpc && drpc->mRefreshDriver->ViewManagerFlushIsPending()) {
121
// Since we're promising that there will be a MozAfterPaint event
122
// fired, we record an empty invalidation in case display list
123
// invalidation doesn't invalidate anything further.
124
NotifyInvalidation(drpc->mRefreshDriver->LastTransactionId().Next(),
125
nsRect(0, 0, 0, 0));
126
return true;
127
}
128
return false;
129
}
130
131
void nsPresContext::ForceReflowForFontInfoUpdate() {
132
// We can trigger reflow by pretending a font.* preference has changed;
133
// this is the same mechanism as gfxPlatform::ForceGlobalReflow() uses
134
// if new fonts are installed during the session, for example.
135
PreferenceChanged("font.internaluseonly.changed");
136
}
137
138
static bool IsVisualCharset(NotNull<const Encoding*> aCharset) {
139
return aCharset == ISO_8859_8_ENCODING;
140
}
141
142
nsPresContext::nsPresContext(dom::Document* aDocument, nsPresContextType aType)
143
: mType(aType),
144
mPresShell(nullptr),
145
mDocument(aDocument),
146
mMedium(aType == eContext_Galley ? nsGkAtoms::screen : nsGkAtoms::print),
147
mMediaEmulated(mMedium),
148
mInflationDisabledForShrinkWrap(false),
149
mSystemFontScale(1.0),
150
mTextZoom(1.0),
151
mEffectiveTextZoom(1.0),
152
mFullZoom(1.0),
153
mOverrideDPPX(0.0),
154
mLastFontInflationScreenSize(gfxSize(-1.0, -1.0)),
155
mCurAppUnitsPerDevPixel(0),
156
mAutoQualityMinFontSizePixelsPref(0),
157
mPageSize(-1, -1),
158
mPageScale(0.0),
159
mPPScale(1.0f),
160
mViewportScrollOverrideElement(nullptr),
161
mViewportScrollStyles(StyleOverflow::Auto, StyleOverflow::Auto),
162
mExistThrottledUpdates(false),
163
// mImageAnimationMode is initialised below, in constructor body
164
mImageAnimationModePref(imgIContainer::kNormalAnimMode),
165
mInterruptChecksToSkip(0),
166
mNextFrameRateMultiplier(0),
167
mElementsRestyled(0),
168
mFramesConstructed(0),
169
mFramesReflowed(0),
170
mInteractionTimeEnabled(true),
171
mHasPendingInterrupt(false),
172
mPendingInterruptFromTest(false),
173
mInterruptsEnabled(false),
174
mSendAfterPaintToContent(false),
175
mDrawImageBackground(true), // always draw the background
176
mDrawColorBackground(true),
177
// mNeverAnimate is initialised below, in constructor body
178
mPaginated(aType != eContext_Galley),
179
mCanPaginatedScroll(false),
180
mDoScaledTwips(true),
181
mIsRootPaginatedDocument(false),
182
mPrefBidiDirection(false),
183
mPrefScrollbarSide(0),
184
mPendingSysColorChanged(false),
185
mPendingThemeChanged(false),
186
mPendingUIResolutionChanged(false),
187
mPrefChangePendingNeedsReflow(false),
188
mPostedPrefChangedRunnable(false),
189
mIsEmulatingMedia(false),
190
mIsGlyph(false),
191
mUsesRootEMUnits(false),
192
mUsesExChUnits(false),
193
mCounterStylesDirty(true),
194
mFontFeatureValuesDirty(true),
195
mSuppressResizeReflow(false),
196
mIsVisual(false),
197
mPaintFlashing(false),
198
mPaintFlashingInitialized(false),
199
mHasWarnedAboutPositionedTableParts(false),
200
mHasWarnedAboutTooLargeDashedOrDottedRadius(false),
201
mQuirkSheetAdded(false),
202
mHadNonBlankPaint(false),
203
mHadContentfulPaint(false),
204
mHadContentfulPaintComposite(false)
205
#ifdef DEBUG
206
,
207
mInitialized(false)
208
#endif
209
{
210
#ifdef DEBUG
211
PodZero(&mLayoutPhaseCount);
212
#endif
213
214
if (!IsDynamic()) {
215
mImageAnimationMode = imgIContainer::kDontAnimMode;
216
mNeverAnimate = true;
217
} else {
218
mImageAnimationMode = imgIContainer::kNormalAnimMode;
219
mNeverAnimate = false;
220
}
221
NS_ASSERTION(mDocument, "Null document");
222
223
// if text perf logging enabled, init stats struct
224
if (MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_textperf), LogLevel::Warning)) {
225
mTextPerf = MakeUnique<gfxTextPerfMetrics>();
226
}
227
228
if (Preferences::GetBool(GFX_MISSING_FONTS_NOTIFY_PREF)) {
229
mMissingFonts = MakeUnique<gfxMissingFontRecorder>();
230
}
231
}
232
233
static const char* gExactCallbackPrefs[] = {
234
"browser.underline_anchors",
235
"browser.anchor_color",
236
"browser.active_color",
237
"browser.visited_color",
238
"image.animation_mode",
239
"dom.send_after_paint_to_content",
240
"layout.css.dpi",
241
"layout.css.devPixelsPerPx",
242
"nglayout.debug.paint_flashing",
243
"nglayout.debug.paint_flashing_chrome",
244
"intl.accept_languages",
245
nullptr,
246
};
247
248
static const char* gPrefixCallbackPrefs[] = {
249
"font.", "browser.display.", "bidi.", "gfx.font_rendering.", nullptr,
250
};
251
252
void nsPresContext::Destroy() {
253
if (mEventManager) {
254
// unclear if these are needed, but can't hurt
255
mEventManager->NotifyDestroyPresContext(this);
256
mEventManager->SetPresContext(nullptr);
257
mEventManager = nullptr;
258
}
259
260
// Unregister preference callbacks
261
Preferences::UnregisterPrefixCallbacks(
262
PREF_CHANGE_METHOD(nsPresContext::PreferenceChanged),
263
gPrefixCallbackPrefs, this);
264
Preferences::UnregisterCallbacks(
265
PREF_CHANGE_METHOD(nsPresContext::PreferenceChanged), gExactCallbackPrefs,
266
this);
267
268
mRefreshDriver = nullptr;
269
}
270
271
nsPresContext::~nsPresContext() {
272
MOZ_ASSERT(!mPresShell, "Presshell forgot to clear our mPresShell pointer");
273
DetachPresShell();
274
275
Destroy();
276
}
277
278
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsPresContext)
279
NS_INTERFACE_MAP_ENTRY(nsISupports)
280
NS_INTERFACE_MAP_END
281
282
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsPresContext)
283
NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(nsPresContext, LastRelease())
284
285
void nsPresContext::LastRelease() {
286
if (mMissingFonts) {
287
mMissingFonts->Clear();
288
}
289
}
290
291
NS_IMPL_CYCLE_COLLECTION_CLASS(nsPresContext)
292
293
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsPresContext)
294
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnimationEventDispatcher);
295
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument);
296
// NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mDeviceContext); // not xpcom
297
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEffectCompositor);
298
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEventManager);
299
// NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mLanguage); // an atom
300
301
// NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTheme); // a service
302
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrintSettings);
303
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
304
305
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsPresContext)
306
NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnimationEventDispatcher);
307
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument);
308
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDeviceContext); // worth bothering?
309
NS_IMPL_CYCLE_COLLECTION_UNLINK(mEffectCompositor);
310
// NS_RELEASE(tmp->mLanguage); // an atom
311
// NS_IMPL_CYCLE_COLLECTION_UNLINK(mTheme); // a service
312
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrintSettings);
313
314
tmp->Destroy();
315
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
316
317
// whether no native theme service exists;
318
// if this gets set to true, we'll stop asking for it.
319
static bool sNoTheme = false;
320
321
// Set to true when LookAndFeelChanged needs to be called. This is used
322
// because the look and feel is a service, so there's no need to notify it from
323
// more than one prescontext.
324
static bool sLookAndFeelChanged;
325
326
// Set to true when ThemeChanged needs to be called on mTheme. This is used
327
// because mTheme is a service, so there's no need to notify it from more than
328
// one prescontext.
329
static bool sThemeChanged;
330
331
bool nsPresContext::IsChrome() const {
332
return Document()->IsInChromeDocShell();
333
}
334
335
bool nsPresContext::IsChromeOriginImage() const {
336
return Document()->IsBeingUsedAsImage() &&
337
Document()->IsDocumentURISchemeChrome();
338
}
339
340
void nsPresContext::GetDocumentColorPreferences() {
341
PreferenceSheet::EnsureInitialized();
342
}
343
344
void nsPresContext::GetUserPreferences() {
345
if (!GetPresShell()) {
346
// No presshell means nothing to do here. We'll do this when we
347
// get a presshell.
348
return;
349
}
350
351
mAutoQualityMinFontSizePixelsPref =
352
Preferences::GetInt("browser.display.auto_quality_min_font_size");
353
354
// * document colors
355
GetDocumentColorPreferences();
356
357
mSendAfterPaintToContent = Preferences::GetBool(
358
"dom.send_after_paint_to_content", mSendAfterPaintToContent);
359
360
mPrefScrollbarSide = Preferences::GetInt("layout.scrollbar.side");
361
362
Document()->SetMayNeedFontPrefsUpdate();
363
364
// * image animation
365
nsAutoCString animatePref;
366
Preferences::GetCString("image.animation_mode", animatePref);
367
if (animatePref.EqualsLiteral("normal"))
368
mImageAnimationModePref = imgIContainer::kNormalAnimMode;
369
else if (animatePref.EqualsLiteral("none"))
370
mImageAnimationModePref = imgIContainer::kDontAnimMode;
371
else if (animatePref.EqualsLiteral("once"))
372
mImageAnimationModePref = imgIContainer::kLoopOnceAnimMode;
373
else // dynamic change to invalid value should act like it does initially
374
mImageAnimationModePref = imgIContainer::kNormalAnimMode;
375
376
uint32_t bidiOptions = GetBidi();
377
378
int32_t prefInt = Preferences::GetInt(IBMBIDI_TEXTDIRECTION_STR,
379
GET_BIDI_OPTION_DIRECTION(bidiOptions));
380
SET_BIDI_OPTION_DIRECTION(bidiOptions, prefInt);
381
mPrefBidiDirection = prefInt;
382
383
prefInt = Preferences::GetInt(IBMBIDI_TEXTTYPE_STR,
384
GET_BIDI_OPTION_TEXTTYPE(bidiOptions));
385
SET_BIDI_OPTION_TEXTTYPE(bidiOptions, prefInt);
386
387
prefInt = Preferences::GetInt(IBMBIDI_NUMERAL_STR,
388
GET_BIDI_OPTION_NUMERAL(bidiOptions));
389
SET_BIDI_OPTION_NUMERAL(bidiOptions, prefInt);
390
391
// We don't need to force reflow: either we are initializing a new
392
// prescontext or we are being called from UpdateAfterPreferencesChanged()
393
// which triggers a reflow anyway.
394
SetBidi(bidiOptions);
395
}
396
397
void nsPresContext::InvalidatePaintedLayers() {
398
if (!mPresShell) {
399
return;
400
}
401
if (nsIFrame* rootFrame = mPresShell->GetRootFrame()) {
402
// FrameLayerBuilder caches invalidation-related values that depend on the
403
// appunits-per-dev-pixel ratio, so ensure that all PaintedLayer drawing
404
// is completely flushed.
405
rootFrame->InvalidateFrameSubtree();
406
}
407
}
408
409
void nsPresContext::AppUnitsPerDevPixelChanged() {
410
int32_t oldAppUnitsPerDevPixel = mCurAppUnitsPerDevPixel;
411
412
InvalidatePaintedLayers();
413
414
if (mDeviceContext) {
415
mDeviceContext->FlushFontCache();
416
}
417
418
MediaFeatureValuesChanged({RestyleHint::RecascadeSubtree(),
419
NS_STYLE_HINT_REFLOW,
420
MediaFeatureChangeReason::ResolutionChange});
421
422
mCurAppUnitsPerDevPixel = mDeviceContext->AppUnitsPerDevPixel();
423
424
// nsSubDocumentFrame uses a AppUnitsPerDevPixel difference between parent and
425
// child document to determine if it needs to build a nsDisplayZoom item. So
426
// if we that changes then we need to invalidate the subdoc frame so that
427
// item gets created/removed.
428
if (mPresShell) {
429
if (nsIFrame* frame = mPresShell->GetRootFrame()) {
430
frame = nsLayoutUtils::GetCrossDocParentFrame(frame);
431
if (frame) {
432
int32_t parentAPD = frame->PresContext()->AppUnitsPerDevPixel();
433
if ((parentAPD == oldAppUnitsPerDevPixel) !=
434
(parentAPD == mCurAppUnitsPerDevPixel)) {
435
frame->InvalidateFrame();
436
}
437
}
438
}
439
}
440
441
// We would also have to look at all of our child subdocuments but the
442
// InvalidatePaintedLayers call above calls InvalidateFrameSubtree which
443
// would invalidate all subdocument frames already.
444
}
445
446
void nsPresContext::PreferenceChanged(const char* aPrefName) {
447
nsDependentCString prefName(aPrefName);
448
if (prefName.EqualsLiteral("layout.css.dpi") ||
449
prefName.EqualsLiteral("layout.css.devPixelsPerPx")) {
450
int32_t oldAppUnitsPerDevPixel = mDeviceContext->AppUnitsPerDevPixel();
451
if (mDeviceContext->CheckDPIChange() && mPresShell) {
452
OwningNonNull<mozilla::PresShell> presShell(*mPresShell);
453
// Re-fetch the view manager's window dimensions in case there's a
454
// deferred resize which hasn't affected our mVisibleArea yet
455
nscoord oldWidthAppUnits, oldHeightAppUnits;
456
RefPtr<nsViewManager> vm = presShell->GetViewManager();
457
if (!vm) {
458
return;
459
}
460
vm->GetWindowDimensions(&oldWidthAppUnits, &oldHeightAppUnits);
461
float oldWidthDevPixels = oldWidthAppUnits / oldAppUnitsPerDevPixel;
462
float oldHeightDevPixels = oldHeightAppUnits / oldAppUnitsPerDevPixel;
463
464
AppUnitsPerDevPixelChanged();
465
466
nscoord width = NSToCoordRound(oldWidthDevPixels * AppUnitsPerDevPixel());
467
nscoord height =
468
NSToCoordRound(oldHeightDevPixels * AppUnitsPerDevPixel());
469
vm->SetWindowDimensions(width, height);
470
}
471
return;
472
}
473
if (prefName.EqualsLiteral(GFX_MISSING_FONTS_NOTIFY_PREF)) {
474
if (Preferences::GetBool(GFX_MISSING_FONTS_NOTIFY_PREF)) {
475
if (!mMissingFonts) {
476
mMissingFonts = MakeUnique<gfxMissingFontRecorder>();
477
// trigger reflow to detect missing fonts on the current page
478
mPrefChangePendingNeedsReflow = true;
479
}
480
} else {
481
if (mMissingFonts) {
482
mMissingFonts->Clear();
483
}
484
mMissingFonts = nullptr;
485
}
486
}
487
if (StringBeginsWith(prefName, NS_LITERAL_CSTRING("font.")) ||
488
prefName.EqualsLiteral("intl.accept_languages")) {
489
// Changes to font family preferences don't change anything in the
490
// computed style data, so the style system won't generate a reflow
491
// hint for us. We need to do that manually.
492
493
// FIXME We could probably also handle changes to
494
// browser.display.auto_quality_min_font_size here, but that
495
// probably also requires clearing the text run cache, so don't
496
// bother (yet, anyway).
497
mPrefChangePendingNeedsReflow = true;
498
}
499
if (StringBeginsWith(prefName, NS_LITERAL_CSTRING("bidi."))) {
500
// Changes to bidi prefs need to trigger a reflow (see bug 443629)
501
mPrefChangePendingNeedsReflow = true;
502
503
// Changes to bidi.numeral also needs to empty the text run cache.
504
// This is handled in gfxTextRunWordCache.cpp.
505
}
506
if (StringBeginsWith(prefName, NS_LITERAL_CSTRING("gfx.font_rendering."))) {
507
// Changes to font_rendering prefs need to trigger a reflow
508
mPrefChangePendingNeedsReflow = true;
509
}
510
511
// We will end up calling InvalidatePreferenceSheets one from each pres
512
// context, but all it's doing is clearing its cached sheet pointers, so it
513
// won't be wastefully recreating the sheet multiple times.
514
//
515
// The first pres context that has its pref changed runnable called will
516
// be the one to cause the reconstruction of the pref style sheet.
517
nsLayoutStylesheetCache::InvalidatePreferenceSheets();
518
PreferenceSheet::Refresh();
519
DispatchPrefChangedRunnableIfNeeded();
520
521
if (prefName.EqualsLiteral("nglayout.debug.paint_flashing") ||
522
prefName.EqualsLiteral("nglayout.debug.paint_flashing_chrome")) {
523
mPaintFlashingInitialized = false;
524
return;
525
}
526
}
527
528
void nsPresContext::DispatchPrefChangedRunnableIfNeeded() {
529
if (mPostedPrefChangedRunnable) {
530
return;
531
}
532
533
nsCOMPtr<nsIRunnable> runnable =
534
NewRunnableMethod("nsPresContext::UpdateAfterPreferencesChanged", this,
535
&nsPresContext::UpdateAfterPreferencesChanged);
536
nsresult rv = Document()->Dispatch(TaskCategory::Other, runnable.forget());
537
if (NS_SUCCEEDED(rv)) {
538
mPostedPrefChangedRunnable = true;
539
}
540
}
541
542
void nsPresContext::UpdateAfterPreferencesChanged() {
543
mPostedPrefChangedRunnable = false;
544
if (!mPresShell) {
545
return;
546
}
547
548
if (mDocument->IsInChromeDocShell()) {
549
// FIXME(emilio): Do really all these prefs not affect chrome docs? I
550
// suspect we should move this check somewhere else.
551
return;
552
}
553
554
StaticPresData::Get()->InvalidateFontPrefs();
555
556
// Initialize our state from the user preferences
557
GetUserPreferences();
558
559
// update the presShell: tell it to set the preference style rules up
560
mPresShell->UpdatePreferenceStyles();
561
562
InvalidatePaintedLayers();
563
mDeviceContext->FlushFontCache();
564
565
nsChangeHint hint = nsChangeHint(0);
566
567
if (mPrefChangePendingNeedsReflow) {
568
hint |= NS_STYLE_HINT_REFLOW;
569
}
570
571
// Preferences require rerunning selector matching because we rebuild
572
// the pref style sheet for some preference changes.
573
RebuildAllStyleData(hint, RestyleHint::RestyleSubtree());
574
}
575
576
nsresult nsPresContext::Init(nsDeviceContext* aDeviceContext) {
577
NS_ASSERTION(!mInitialized, "attempt to reinit pres context");
578
NS_ENSURE_ARG(aDeviceContext);
579
580
mDeviceContext = aDeviceContext;
581
582
// In certain rare cases (such as changing page mode), we tear down layout
583
// state and re-initialize a new prescontext for a document. Given that we
584
// hang style state off the DOM, we detect that re-initialization case and
585
// lazily drop the servo data. We don't do this eagerly during layout teardown
586
// because that would incur an extra whole-tree traversal that's unnecessary
587
// most of the time.
588
//
589
// FIXME(emilio): I'm pretty sure this doesn't happen after bug 1414999.
590
Element* root = mDocument->GetRootElement();
591
if (root && root->HasServoData()) {
592
RestyleManager::ClearServoDataFromSubtree(root);
593
}
594
595
if (mDeviceContext->SetFullZoom(mFullZoom)) mDeviceContext->FlushFontCache();
596
mCurAppUnitsPerDevPixel = mDeviceContext->AppUnitsPerDevPixel();
597
598
mEventManager = new mozilla::EventStateManager();
599
600
mAnimationEventDispatcher = new mozilla::AnimationEventDispatcher(this);
601
mEffectCompositor = new mozilla::EffectCompositor(this);
602
mTransitionManager = MakeUnique<nsTransitionManager>(this);
603
mAnimationManager = MakeUnique<nsAnimationManager>(this);
604
605
if (mDocument->GetDisplayDocument()) {
606
NS_ASSERTION(mDocument->GetDisplayDocument()->GetPresContext(),
607
"Why are we being initialized?");
608
mRefreshDriver =
609
mDocument->GetDisplayDocument()->GetPresContext()->RefreshDriver();
610
} else {
611
dom::Document* parent = mDocument->GetParentDocument();
612
// Unfortunately, sometimes |parent| here has no presshell because
613
// printing screws up things. Assert that in other cases it does,
614
// but whenever the shell is null just fall back on using our own
615
// refresh driver.
616
NS_ASSERTION(
617
!parent || mDocument->IsStaticDocument() || parent->GetPresShell(),
618
"How did we end up with a presshell if our parent doesn't "
619
"have one?");
620
if (parent && parent->GetPresContext()) {
621
nsCOMPtr<nsIDocShellTreeItem> ourItem = mDocument->GetDocShell();
622
if (ourItem) {
623
nsCOMPtr<nsIDocShellTreeItem> parentItem;
624
ourItem->GetSameTypeParent(getter_AddRefs(parentItem));
625
if (parentItem) {
626
Element* containingElement =
627
parent->FindContentForSubDocument(mDocument);
628
if (!containingElement->IsXULElement() ||
629
!containingElement->HasAttr(kNameSpaceID_None,
630
nsGkAtoms::forceOwnRefreshDriver)) {
631
mRefreshDriver = parent->GetPresContext()->RefreshDriver();
632
}
633
}
634
}
635
}
636
637
if (!mRefreshDriver) {
638
mRefreshDriver = new nsRefreshDriver(this);
639
if (XRE_IsContentProcess()) {
640
mRefreshDriver->InitializeTimer();
641
}
642
}
643
}
644
645
// Register callbacks so we're notified when the preferences change
646
Preferences::RegisterPrefixCallbacks(
647
PREF_CHANGE_METHOD(nsPresContext::PreferenceChanged),
648
gPrefixCallbackPrefs, this);
649
Preferences::RegisterCallbacks(
650
PREF_CHANGE_METHOD(nsPresContext::PreferenceChanged), gExactCallbackPrefs,
651
this);
652
653
nsresult rv = mEventManager->Init();
654
NS_ENSURE_SUCCESS(rv, rv);
655
656
mEventManager->SetPresContext(this);
657
658
#ifdef DEBUG
659
mInitialized = true;
660
#endif
661
662
return NS_OK;
663
}
664
665
// Note: We don't hold a reference on the shell; it has a reference to
666
// us
667
void nsPresContext::AttachPresShell(mozilla::PresShell* aPresShell) {
668
MOZ_ASSERT(!mPresShell);
669
mPresShell = aPresShell;
670
671
mRestyleManager = MakeUnique<mozilla::RestyleManager>(this);
672
673
// Since CounterStyleManager is also the name of a method of
674
// nsPresContext, it is necessary to prefix the class with the mozilla
675
// namespace here.
676
mCounterStyleManager = new mozilla::CounterStyleManager(this);
677
678
dom::Document* doc = mPresShell->GetDocument();
679
MOZ_ASSERT(doc);
680
// Have to update PresContext's mDocument before calling any other methods.
681
mDocument = doc;
682
// Initialize our state from the user preferences, now that we
683
// have a presshell, and hence a document.
684
GetUserPreferences();
685
686
nsIURI* docURI = doc->GetDocumentURI();
687
688
if (IsDynamic() && docURI) {
689
bool isChrome = false;
690
bool isRes = false;
691
docURI->SchemeIs("chrome", &isChrome);
692
docURI->SchemeIs("resource", &isRes);
693
694
if (!isChrome && !isRes)
695
mImageAnimationMode = mImageAnimationModePref;
696
else
697
mImageAnimationMode = imgIContainer::kNormalAnimMode;
698
}
699
700
UpdateCharSet(doc->GetDocumentCharacterSet());
701
}
702
703
void nsPresContext::DetachPresShell() {
704
// The counter style manager's destructor needs to deallocate with the
705
// presshell arena. Disconnect it before nulling out the shell.
706
//
707
// XXXbholley: Given recent refactorings, it probably makes more sense to
708
// just null our mPresShell at the bottom of this function. I'm leaving it
709
// this way to preserve the old ordering, but I doubt anything would break.
710
if (mCounterStyleManager) {
711
mCounterStyleManager->Disconnect();
712
mCounterStyleManager = nullptr;
713
}
714
715
mPresShell = nullptr;
716
717
if (mAnimationEventDispatcher) {
718
mAnimationEventDispatcher->Disconnect();
719
mAnimationEventDispatcher = nullptr;
720
}
721
if (mEffectCompositor) {
722
mEffectCompositor->Disconnect();
723
mEffectCompositor = nullptr;
724
}
725
if (mTransitionManager) {
726
mTransitionManager->Disconnect();
727
mTransitionManager = nullptr;
728
}
729
if (mAnimationManager) {
730
mAnimationManager->Disconnect();
731
mAnimationManager = nullptr;
732
}
733
if (mRestyleManager) {
734
mRestyleManager->Disconnect();
735
mRestyleManager = nullptr;
736
}
737
if (mRefreshDriver && mRefreshDriver->GetPresContext() == this) {
738
mRefreshDriver->Disconnect();
739
// Can't null out the refresh driver here.
740
}
741
742
if (IsRoot()) {
743
nsRootPresContext* thisRoot = static_cast<nsRootPresContext*>(this);
744
745
// Have to cancel our plugin geometry timer, because the
746
// callback for that depends on a non-null presshell.
747
thisRoot->CancelApplyPluginGeometryTimer();
748
}
749
}
750
751
void nsPresContext::DoChangeCharSet(NotNull<const Encoding*> aCharSet) {
752
UpdateCharSet(aCharSet);
753
mDeviceContext->FlushFontCache();
754
755
// If a document contains one or more <script> elements, frame construction
756
// might happen earlier than the UpdateCharSet(), so we need to restyle
757
// descendants to make their style data up-to-date.
758
//
759
// FIXME(emilio): Revisit whether this is true after bug 1438911.
760
RebuildAllStyleData(NS_STYLE_HINT_REFLOW, RestyleHint::RecascadeSubtree());
761
}
762
763
void nsPresContext::UpdateCharSet(NotNull<const Encoding*> aCharSet) {
764
switch (GET_BIDI_OPTION_TEXTTYPE(GetBidi())) {
765
case IBMBIDI_TEXTTYPE_LOGICAL:
766
SetVisualMode(false);
767
break;
768
769
case IBMBIDI_TEXTTYPE_VISUAL:
770
SetVisualMode(true);
771
break;
772
773
case IBMBIDI_TEXTTYPE_CHARSET:
774
default:
775
SetVisualMode(IsVisualCharset(aCharSet));
776
}
777
}
778
779
void nsPresContext::DispatchCharSetChange(NotNull<const Encoding*> aEncoding) {
780
// In Servo RebuildAllStyleData is async, so no need to do the runnable dance.
781
DoChangeCharSet(aEncoding);
782
}
783
784
nsPresContext* nsPresContext::GetParentPresContext() {
785
mozilla::PresShell* presShell = GetPresShell();
786
if (presShell) {
787
nsViewManager* viewManager = presShell->GetViewManager();
788
if (viewManager) {
789
nsView* view = viewManager->GetRootView();
790
if (view) {
791
view = view->GetParent(); // anonymous inner view
792
if (view) {
793
view = view->GetParent(); // subdocumentframe's view
794
if (view) {
795
nsIFrame* f = view->GetFrame();
796
if (f) {
797
return f->PresContext();
798
}
799
}
800
}
801
}
802
}
803
}
804
return nullptr;
805
}
806
807
nsPresContext* nsPresContext::GetToplevelContentDocumentPresContext() {
808
if (IsChrome()) return nullptr;
809
nsPresContext* pc = this;
810
for (;;) {
811
nsPresContext* parent = pc->GetParentPresContext();
812
if (!parent || parent->IsChrome()) return pc;
813
pc = parent;
814
}
815
}
816
817
nsIWidget* nsPresContext::GetNearestWidget(nsPoint* aOffset) {
818
NS_ENSURE_TRUE(mPresShell, nullptr);
819
nsIFrame* frame = mPresShell->GetRootFrame();
820
NS_ENSURE_TRUE(frame, nullptr);
821
return frame->GetView()->GetNearestWidget(aOffset);
822
}
823
824
nsIWidget* nsPresContext::GetRootWidget() {
825
NS_ENSURE_TRUE(mPresShell, nullptr);
826
nsViewManager* vm = mPresShell->GetViewManager();
827
if (!vm) {
828
return nullptr;
829
}
830
nsCOMPtr<nsIWidget> widget;
831
vm->GetRootWidget(getter_AddRefs(widget));
832
return widget.get();
833
}
834
835
// We may want to replace this with something faster, maybe caching the root
836
// prescontext
837
nsRootPresContext* nsPresContext::GetRootPresContext() {
838
nsPresContext* pc = this;
839
for (;;) {
840
nsPresContext* parent = pc->GetParentPresContext();
841
if (!parent) break;
842
pc = parent;
843
}
844
return pc->IsRoot() ? static_cast<nsRootPresContext*>(pc) : nullptr;
845
}
846
847
// Helper function for setting Anim Mode on image
848
static void SetImgAnimModeOnImgReq(imgIRequest* aImgReq, uint16_t aMode) {
849
if (aImgReq) {
850
nsCOMPtr<imgIContainer> imgCon;
851
aImgReq->GetImage(getter_AddRefs(imgCon));
852
if (imgCon) {
853
imgCon->SetAnimationMode(aMode);
854
}
855
}
856
}
857
858
// IMPORTANT: Assumption is that all images for a Presentation
859
// have the same Animation Mode (pavlov said this was OK)
860
//
861
// Walks content and set the animation mode
862
// this is a way to turn on/off image animations
863
void nsPresContext::SetImgAnimations(nsIContent* aParent, uint16_t aMode) {
864
nsCOMPtr<nsIImageLoadingContent> imgContent(do_QueryInterface(aParent));
865
if (imgContent) {
866
nsCOMPtr<imgIRequest> imgReq;
867
imgContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
868
getter_AddRefs(imgReq));
869
SetImgAnimModeOnImgReq(imgReq, aMode);
870
}
871
872
for (nsIContent* childContent = aParent->GetFirstChild(); childContent;
873
childContent = childContent->GetNextSibling()) {
874
SetImgAnimations(childContent, aMode);
875
}
876
}
877
878
void nsPresContext::SetSMILAnimations(dom::Document* aDoc, uint16_t aNewMode,
879
uint16_t aOldMode) {
880
if (aDoc->HasAnimationController()) {
881
SMILAnimationController* controller = aDoc->GetAnimationController();
882
switch (aNewMode) {
883
case imgIContainer::kNormalAnimMode:
884
case imgIContainer::kLoopOnceAnimMode:
885
if (aOldMode == imgIContainer::kDontAnimMode)
886
controller->Resume(SMILTimeContainer::PAUSE_USERPREF);
887
break;
888
889
case imgIContainer::kDontAnimMode:
890
if (aOldMode != imgIContainer::kDontAnimMode)
891
controller->Pause(SMILTimeContainer::PAUSE_USERPREF);
892
break;
893
}
894
}
895
}
896
897
void nsPresContext::SetImageAnimationMode(uint16_t aMode) {
898
NS_ASSERTION(aMode == imgIContainer::kNormalAnimMode ||
899
aMode == imgIContainer::kDontAnimMode ||
900
aMode == imgIContainer::kLoopOnceAnimMode,
901
"Wrong Animation Mode is being set!");
902
903
// Image animation mode cannot be changed when rendering to a printer.
904
if (!IsDynamic()) return;
905
906
// Now walk the content tree and set the animation mode
907
// on all the images.
908
if (mPresShell) {
909
dom::Document* doc = mPresShell->GetDocument();
910
if (doc) {
911
doc->StyleImageLoader()->SetAnimationMode(aMode);
912
913
Element* rootElement = doc->GetRootElement();
914
if (rootElement) {
915
SetImgAnimations(rootElement, aMode);
916
}
917
SetSMILAnimations(doc, aMode, mImageAnimationMode);
918
}
919
}
920
921
mImageAnimationMode = aMode;
922
}
923
924
void nsPresContext::UpdateEffectiveTextZoom() {
925
float newZoom = mSystemFontScale * mTextZoom;
926
float minZoom = nsLayoutUtils::MinZoom();
927
float maxZoom = nsLayoutUtils::MaxZoom();
928
929
if (newZoom < minZoom) {
930
newZoom = minZoom;
931
} else if (newZoom > maxZoom) {
932
newZoom = maxZoom;
933
}
934
935
mEffectiveTextZoom = newZoom;
936
937
// Media queries could have changed, since we changed the meaning
938
// of 'em' units in them.
939
MediaFeatureValuesChanged({RestyleHint::RecascadeSubtree(),
940
NS_STYLE_HINT_REFLOW,
941
MediaFeatureChangeReason::ZoomChange});
942
}
943
944
float nsPresContext::GetDeviceFullZoom() {
945
return mDeviceContext->GetFullZoom();
946
}
947
948
void nsPresContext::SetFullZoom(float aZoom) {
949
if (!mPresShell || mFullZoom == aZoom) {
950
return;
951
}
952
953
// Re-fetch the view manager's window dimensions in case there's a deferred
954
// resize which hasn't affected our mVisibleArea yet
955
nscoord oldWidthAppUnits, oldHeightAppUnits;
956
mPresShell->GetViewManager()->GetWindowDimensions(&oldWidthAppUnits,
957
&oldHeightAppUnits);
958
float oldWidthDevPixels = oldWidthAppUnits / float(mCurAppUnitsPerDevPixel);
959
float oldHeightDevPixels = oldHeightAppUnits / float(mCurAppUnitsPerDevPixel);
960
mDeviceContext->SetFullZoom(aZoom);
961
962
NS_ASSERTION(!mSuppressResizeReflow,
963
"two zooms happening at the same time? impossible!");
964
mSuppressResizeReflow = true;
965
966
mFullZoom = aZoom;
967
968
AppUnitsPerDevPixelChanged();
969
970
mPresShell->GetViewManager()->SetWindowDimensions(
971
NSToCoordRound(oldWidthDevPixels * AppUnitsPerDevPixel()),
972
NSToCoordRound(oldHeightDevPixels * AppUnitsPerDevPixel()));
973
974
mSuppressResizeReflow = false;
975
}
976
977
void nsPresContext::SetOverrideDPPX(float aDPPX) {
978
// SetOverrideDPPX is called during navigations, including history
979
// traversals. In that case, it's typically called with our current value,
980
// and we don't need to actually do anything.
981
if (aDPPX == mOverrideDPPX) {
982
return;
983
}
984
985
mOverrideDPPX = aDPPX;
986
MediaFeatureValuesChanged({MediaFeatureChangeReason::ResolutionChange});
987
}
988
989
gfxSize nsPresContext::ScreenSizeInchesForFontInflation(bool* aChanged) {
990
if (aChanged) {
991
*aChanged = false;
992
}
993
994
nsDeviceContext* dx = DeviceContext();
995
nsRect clientRect;
996
dx->GetClientRect(clientRect); // FIXME: GetClientRect looks expensive
997
float unitsPerInch = dx->AppUnitsPerPhysicalInch();
998
gfxSize deviceSizeInches(float(clientRect.width) / unitsPerInch,
999
float(clientRect.height) / unitsPerInch);
1000
1001
if (mLastFontInflationScreenSize == gfxSize(-1.0, -1.0)) {
1002
mLastFontInflationScreenSize = deviceSizeInches;
1003
}
1004
1005
if (deviceSizeInches != mLastFontInflationScreenSize && aChanged) {
1006
*aChanged = true;
1007
mLastFontInflationScreenSize = deviceSizeInches;
1008
}
1009
1010
return deviceSizeInches;
1011
}
1012
1013
static bool CheckOverflow(ComputedStyle* aComputedStyle,
1014
ScrollStyles* aStyles) {
1015
const nsStyleDisplay* display = aComputedStyle->StyleDisplay();
1016
if (display->mOverflowX == StyleOverflow::Visible &&
1017
display->mOverscrollBehaviorX == StyleOverscrollBehavior::Auto &&
1018
display->mOverscrollBehaviorY == StyleOverscrollBehavior::Auto &&
1019
display->mScrollSnapType.strictness == StyleScrollSnapStrictness::None) {
1020
return false;
1021
}
1022
1023
WritingMode writingMode = WritingMode(aComputedStyle);
1024
if (display->mOverflowX == StyleOverflow::MozHiddenUnscrollable) {
1025
*aStyles = ScrollStyles(writingMode, StyleOverflow::Hidden,
1026
StyleOverflow::Hidden, display);
1027
} else {
1028
*aStyles = ScrollStyles(writingMode, display);
1029
}
1030
return true;
1031
}
1032
1034
static Element* GetPropagatedScrollStylesForViewport(
1035
nsPresContext* aPresContext, ScrollStyles* aStyles) {
1036
Document* document = aPresContext->Document();
1037
Element* docElement = document->GetRootElement();
1038
1039
// docElement might be null if we're doing this after removing it.
1040
if (!docElement) {
1041
return nullptr;
1042
}
1043
1044
// Check the style on the document root element
1045
ServoStyleSet* styleSet = aPresContext->StyleSet();
1046
RefPtr<ComputedStyle> rootStyle = styleSet->ResolveStyleLazily(*docElement);
1047
if (CheckOverflow(rootStyle, aStyles)) {
1048
// tell caller we stole the overflow style from the root element
1049
return docElement;
1050
}
1051
1052
// Don't look in the BODY for non-HTML documents or HTML documents
1053
// with non-HTML roots.
1054
// XXX this should be earlier; we shouldn't even look at the document root
1055
// for non-HTML documents. Fix this once we support explicit CSS styling
1056
// of the viewport
1057
if (!document->IsHTMLOrXHTML() || !docElement->IsHTMLElement()) {
1058
return nullptr;
1059
}
1060
1061
Element* bodyElement = document->AsHTMLDocument()->GetBodyElement();
1062
if (!bodyElement) {
1063
// No body, nothing to do here.
1064
return nullptr;
1065
}
1066
1067
MOZ_ASSERT(bodyElement->IsHTMLElement(nsGkAtoms::body),
1068
"GetBodyElement returned something bogus");
1069
1070
// FIXME(emilio): We could make these just a ResolveServoStyle call if we
1071
// looked at `display` on the root, and updated styles properly before doing
1072
// this on first construction:
1073
//
1075
RefPtr<ComputedStyle> bodyStyle = styleSet->ResolveStyleLazily(*bodyElement);
1076
1077
if (CheckOverflow(bodyStyle, aStyles)) {
1078
// tell caller we stole the overflow style from the body element
1079
return bodyElement;
1080
}
1081
1082
return nullptr;
1083
}
1084
1085
Element* nsPresContext::UpdateViewportScrollStylesOverride() {
1086
// Start off with our default styles, and then update them as needed.
1087
mViewportScrollStyles =
1088
ScrollStyles(StyleOverflow::Auto, StyleOverflow::Auto);
1089
mViewportScrollOverrideElement = nullptr;
1090
// Don't propagate the scrollbar state in printing or print preview.
1091
if (!IsPaginated()) {
1092
mViewportScrollOverrideElement =
1093
GetPropagatedScrollStylesForViewport(this, &mViewportScrollStyles);
1094
}
1095
1096
dom::Document* document = Document();
1097
if (Element* fullscreenElement = document->GetFullscreenElement()) {
1098
// If the document is in fullscreen, but the fullscreen element is
1099
// not the root element, we should explicitly suppress the scrollbar
1100
// here. Note that, we still need to return the original element
1101
// the styles are from, so that the state of those elements is not
1102
// affected across fullscreen change.
1103
if (fullscreenElement != document->GetRootElement() &&
1104
fullscreenElement != mViewportScrollOverrideElement) {
1105
mViewportScrollStyles =
1106
ScrollStyles(StyleOverflow::Hidden, StyleOverflow::Hidden);
1107
}
1108
}
1109
return mViewportScrollOverrideElement;
1110
}
1111
1112
bool nsPresContext::ElementWouldPropagateScrollStyles(const Element& aElement) {
1113
MOZ_ASSERT(IsPaginated(), "Should only be called on paginated contexts");
1114
if (aElement.GetParent() && !aElement.IsHTMLElement(nsGkAtoms::body)) {
1115
// We certainly won't be propagating from this element.
1116
return false;
1117
}
1118
1119
// Go ahead and just call GetPropagatedScrollStylesForViewport, but update
1120
// a dummy ScrollStyles we don't care about. It'll do a bit of extra work,
1121
// but saves us having to have more complicated code or more code duplication;
1122
// in practice we will make this call quite rarely, because we checked for all
1123
// the common cases above.
1124
ScrollStyles dummy(StyleOverflow::Auto, StyleOverflow::Auto);
1125
return GetPropagatedScrollStylesForViewport(this, &dummy) == &aElement;
1126
}
1127
1128
nsISupports* nsPresContext::GetContainerWeak() const { return GetDocShell(); }
1129
1130
nsIDocShell* nsPresContext::GetDocShell() const {
1131
return mDocument->GetDocShell();
1132
}
1133
1134
bool nsPresContext::BidiEnabled() const { return Document()->GetBidiEnabled(); }
1135
1136
void nsPresContext::SetBidiEnabled() const { Document()->SetBidiEnabled(); }
1137
1138
void nsPresContext::SetBidi(uint32_t aSource) {
1139
// Don't do all this stuff unless the options have changed.
1140
if (aSource == GetBidi()) {
1141
return;
1142
}
1143
1144
Document()->SetBidiOptions(aSource);
1145
if (IBMBIDI_TEXTDIRECTION_RTL == GET_BIDI_OPTION_DIRECTION(aSource) ||
1146
IBMBIDI_NUMERAL_HINDI == GET_BIDI_OPTION_NUMERAL(aSource)) {
1147
SetBidiEnabled();
1148
}
1149
if (IBMBIDI_TEXTTYPE_VISUAL == GET_BIDI_OPTION_TEXTTYPE(aSource)) {
1150
SetVisualMode(true);
1151
} else if (IBMBIDI_TEXTTYPE_LOGICAL == GET_BIDI_OPTION_TEXTTYPE(aSource)) {
1152
SetVisualMode(false);
1153
} else {
1154
SetVisualMode(IsVisualCharset(Document()->GetDocumentCharacterSet()));
1155
}
1156
}
1157
1158
uint32_t nsPresContext::GetBidi() const { return Document()->GetBidiOptions(); }
1159
1160
bool nsPresContext::IsTopLevelWindowInactive() {
1161
return Document()->IsTopLevelWindowInactive();
1162
}
1163
1164
void nsPresContext::RecordInteractionTime(InteractionType aType,
1165
const TimeStamp& aTimeStamp) {
1166
if (!mInteractionTimeEnabled || aTimeStamp.IsNull()) {
1167
return;
1168
}
1169
1170
// Array of references to the member variable of each time stamp
1171
// for the different interaction types, keyed by InteractionType.
1172
TimeStamp nsPresContext::*interactionTimes[] = {
1173
&nsPresContext::mFirstClickTime, &nsPresContext::mFirstKeyTime,
1174
&nsPresContext::mFirstMouseMoveTime, &nsPresContext::mFirstScrollTime};
1175
1176
// Array of histogram IDs for the different interaction types,
1177
// keyed by InteractionType.
1178
Telemetry::HistogramID histogramIds[] = {
1179
Telemetry::TIME_TO_FIRST_CLICK_MS, Telemetry::TIME_TO_FIRST_KEY_INPUT_MS,
1180
Telemetry::TIME_TO_FIRST_MOUSE_MOVE_MS,
1181
Telemetry::TIME_TO_FIRST_SCROLL_MS};
1182
1183
TimeStamp& interactionTime =
1184
this->*(interactionTimes[static_cast<uint32_t>(aType)]);
1185
if (!interactionTime.IsNull()) {
1186
// We have already recorded an interaction time.
1187
return;
1188
}
1189
1190
// Record the interaction time if it occurs after the first paint
1191
// of the top level content document.
1192
nsPresContext* topContentPresContext =
1193
GetToplevelContentDocumentPresContext();
1194
1195
if (!topContentPresContext) {
1196
// There is no top content pres context so we don't care
1197
// about the interaction time. Record a value anyways to avoid
1198
// trying to find the top content pres context in future interactions.
1199
interactionTime = TimeStamp::Now();
1200
return;
1201
}
1202
1203
if (topContentPresContext->mFirstNonBlankPaintTime.IsNull() ||
1204
topContentPresContext->mFirstNonBlankPaintTime > aTimeStamp) {
1205
// Top content pres context has not had a non-blank paint yet
1206
// or the event timestamp is before the first non-blank paint,
1207
// so don't record interaction time.
1208
return;
1209
}
1210
1211
// Check if we are recording the first of any of the interaction types.
1212
bool isFirstInteraction = true;
1213
for (TimeStamp nsPresContext::*memberPtr : interactionTimes) {
1214
TimeStamp& timeStamp = this->*(memberPtr);
1215
if (!timeStamp.IsNull()) {
1216
isFirstInteraction = false;
1217
break;
1218
}
1219
}
1220
1221
interactionTime = TimeStamp::Now();
1222
// Only the top level content pres context reports first interaction
1223
// time to telemetry (if it hasn't already done so).
1224
if (this == topContentPresContext) {
1225
if (Telemetry::CanRecordExtended()) {
1226
double millis =
1227
(interactionTime - mFirstNonBlankPaintTime).ToMilliseconds();
1228
Telemetry::Accumulate(histogramIds[static_cast<uint32_t>(aType)], millis);
1229
1230
if (isFirstInteraction) {
1231
Telemetry::Accumulate(Telemetry::TIME_TO_FIRST_INTERACTION_MS, millis);
1232
}
1233
}
1234
} else {
1235
topContentPresContext->RecordInteractionTime(aType, aTimeStamp);
1236
}
1237
}
1238
1239
nsITheme* nsPresContext::GetTheme() {
1240
if (!sNoTheme && !mTheme) {
1241
mTheme = do_GetNativeTheme();
1242
if (!mTheme) sNoTheme = true;
1243
}
1244
1245
return mTheme;
1246
}
1247
1248
void nsPresContext::ThemeChanged() {
1249
if (!mPendingThemeChanged) {
1250
sLookAndFeelChanged = true;
1251
sThemeChanged = true;
1252
1253
nsCOMPtr<nsIRunnable> ev =
1254
NewRunnableMethod("nsPresContext::ThemeChangedInternal", this,
1255
&nsPresContext::ThemeChangedInternal);
1256
nsresult rv = Document()->Dispatch(TaskCategory::Other, ev.forget());
1257
if (NS_SUCCEEDED(rv)) {
1258
mPendingThemeChanged = true;
1259
}
1260
}
1261
}
1262
1263
static bool NotifyThemeChanged(BrowserParent* aBrowserParent, void* aArg) {
1264
aBrowserParent->ThemeChanged();
1265
return false;
1266
}
1267
1268
void nsPresContext::ThemeChangedInternal() {
1269
mPendingThemeChanged = false;
1270
1271
// Tell the theme that it changed, so it can flush any handles to stale theme
1272
// data.
1273
if (mTheme && sThemeChanged) {
1274
mTheme->ThemeChanged();
1275
sThemeChanged = false;
1276
}
1277
1278
if (sLookAndFeelChanged) {
1279
// Clear all cached LookAndFeel colors.
1280
LookAndFeel::Refresh();
1281
sLookAndFeelChanged = false;
1282
1283
// Vector images (SVG) may be using theme colors so we discard all cached
1284
// surfaces. (We could add a vector image only version of DiscardAll, but
1285
// in bug 940625 we decided theme changes are rare enough not to bother.)
1286
image::SurfaceCacheUtils::DiscardAll();
1287
}
1288
1289
RefreshSystemMetrics();
1290
1291
// Recursively notify all remote leaf descendants that the
1292
// system theme has changed.
1293
if (nsPIDOMWindowOuter* window = mDocument->GetWindow()) {
1294
nsContentUtils::CallOnAllRemoteChildren(window, NotifyThemeChanged,
1295
nullptr);
1296
}
1297
}
1298
1299
void nsPresContext::SysColorChanged() {
1300
if (!mPendingSysColorChanged) {
1301
sLookAndFeelChanged = true;
1302
nsCOMPtr<nsIRunnable> ev =
1303
NewRunnableMethod("nsPresContext::SysColorChangedInternal", this,
1304
&nsPresContext::SysColorChangedInternal);
1305
nsresult rv = Document()->Dispatch(TaskCategory::Other, ev.forget());
1306
if (NS_SUCCEEDED(rv)) {
1307
mPendingSysColorChanged = true;
1308
}
1309
}
1310
}
1311
1312
void nsPresContext::SysColorChangedInternal() {
1313
mPendingSysColorChanged = false;
1314
1315
if (sLookAndFeelChanged) {
1316
// Don't use the cached values for the system colors
1317
LookAndFeel::Refresh();
1318
sLookAndFeelChanged = false;
1319
}
1320
1321
// Invalidate cached '-moz-windows-accent-color-applies' media query:
1322
RefreshSystemMetrics();
1323
1324
PreferenceSheet::Refresh();
1325
1326
// Reset default background and foreground colors for the document since
1327
// they may be using system colors
1328
GetDocumentColorPreferences();
1329
1330
// The system color values are computed to colors in the style data,
1331
// so normal style data comparison is sufficient here.
1332
RebuildAllStyleData(nsChangeHint(0), RestyleHint{0});
1333
}
1334
1335
void nsPresContext::RefreshSystemMetrics() {
1336
// This will force the system metrics to be generated the next time they're
1337
// used.
1338
nsMediaFeatures::FreeSystemMetrics();
1339
1340
// Changes to system metrics can change media queries on them.
1341
//
1342
// Changes in theme can change system colors (whose changes are
1343
// properly reflected in computed style data), system fonts (whose
1344
// changes are not), and -moz-appearance (whose changes likewise are
1345
// not), so we need to recascade for the first, and reflow for the rest.
1346
MediaFeatureValuesChangedAllDocuments({
1347
RestyleHint::RecascadeSubtree(),
1348
NS_STYLE_HINT_REFLOW,
1349
MediaFeatureChangeReason::SystemMetricsChange,
1350
});
1351
}
1352
1353
void nsPresContext::UIResolutionChanged() {
1354
if (!mPendingUIResolutionChanged) {
1355
nsCOMPtr<nsIRunnable> ev =
1356
NewRunnableMethod("nsPresContext::UIResolutionChangedInternal", this,
1357
&nsPresContext::UIResolutionChangedInternal);
1358
nsresult rv = Document()->Dispatch(TaskCategory::Other, ev.forget());
1359
if (NS_SUCCEEDED(rv)) {
1360
mPendingUIResolutionChanged = true;
1361
}
1362
}
1363
}
1364
1365
void nsPresContext::UIResolutionChangedSync() {
1366
if (!mPendingUIResolutionChanged) {
1367
mPendingUIResolutionChanged = true;
1368
UIResolutionChangedInternalScale(0.0);
1369
}
1370
}
1371
1372
/*static*/
1373
bool nsPresContext::UIResolutionChangedSubdocumentCallback(
1374
dom::Document* aDocument, void* aData) {
1375
nsPresContext* pc = aDocument->GetPresContext();
1376
if (pc) {
1377
// For subdocuments, we want to apply the parent's scale, because there
1378
// are cases where the subdoc's device context is connected to a widget
1379
// that has an out-of-date resolution (it's on a different screen, but
1380
// currently hidden, and will not be updated until shown): bug 1249279.
1381
double scale = *static_cast<double*>(aData);
1382
pc->UIResolutionChangedInternalScale(scale);
1383
}
1384
return true;
1385
}
1386
1387
static void NotifyTabUIResolutionChanged(nsIRemoteTab* aTab, void* aArg) {
1388
aTab->NotifyResolutionChanged();
1389
}
1390
1391
static void NotifyChildrenUIResolutionChanged(nsPIDOMWindowOuter* aWindow) {
1392
nsCOMPtr<Document> doc = aWindow->GetExtantDoc();
1393
RefPtr<nsPIWindowRoot> topLevelWin = nsContentUtils::GetWindowRoot(doc);
1394
if (!topLevelWin) {
1395
return;
1396
}
1397
topLevelWin->EnumerateBrowsers(NotifyTabUIResolutionChanged, nullptr);
1398
}
1399
1400
void nsPresContext::UIResolutionChangedInternal() {
1401
UIResolutionChangedInternalScale(0.0);
1402
}
1403
1404
void nsPresContext::UIResolutionChangedInternalScale(double aScale) {
1405
mPendingUIResolutionChanged = false;
1406
1407
mDeviceContext->CheckDPIChange(&aScale);
1408
if (mCurAppUnitsPerDevPixel != mDeviceContext->AppUnitsPerDevPixel()) {
1409
AppUnitsPerDevPixelChanged();
1410
}
1411
1412
// Recursively notify all remote leaf descendants of the change.
1413
if (nsPIDOMWindowOuter* window = mDocument->GetWindow()) {
1414
NotifyChildrenUIResolutionChanged(window);
1415
}
1416
1417
mDocument->EnumerateSubDocuments(UIResolutionChangedSubdocumentCallback,
1418
&aScale);
1419
}
1420
1421
void nsPresContext::EmulateMedium(const nsAString& aMediaType) {
1422
nsAtom* previousMedium = Medium();
1423
mIsEmulatingMedia = true;
1424
1425
nsAutoString mediaType;
1426
nsContentUtils::ASCIIToLower(aMediaType, mediaType);
1427
1428
mMediaEmulated = NS_Atomize(mediaType);
1429
if (mMediaEmulated != previousMedium && mPresShell) {
1430
MediaFeatureValuesChanged({MediaFeatureChangeReason::MediumChange});
1431
}
1432
}
1433
1434
void nsPresContext::StopEmulatingMedium() {
1435
nsAtom* previousMedium = Medium();
1436
mIsEmulatingMedia = false;
1437
if (Medium() != previousMedium) {
1438
MediaFeatureValuesChanged({MediaFeatureChangeReason::MediumChange});
1439
}
1440
}
1441
1442
void nsPresContext::ContentLanguageChanged() {
1443
PostRebuildAllStyleDataEvent(nsChangeHint(0),
1444
RestyleHint::RecascadeSubtree());
1445
}
1446
1447
void nsPresContext::MediaFeatureValuesChanged(
1448
const MediaFeatureChange& aChange) {
1449
if (mPresShell) {
1450
mPresShell->EnsureStyleFlush();
1451
}
1452
1453
if (!mPendingMediaFeatureValuesChange) {
1454
mPendingMediaFeatureValuesChange.emplace(aChange);
1455
return;
1456
}
1457
1458
*mPendingMediaFeatureValuesChange |= aChange;
1459
}
1460
1461
void nsPresContext::RebuildAllStyleData(nsChangeHint aExtraHint,
1462
RestyleHint aRestyleHint) {
1463
if (!mPresShell) {
1464
// We must have been torn down. Nothing to do here.
1465
return;
1466
}
1467
1468
// FIXME(emilio): Why is it safe to reset mUsesRootEMUnits / mUsesEXChUnits
1469
// here if there's no restyle hint? That looks pretty bogus.
1470
mUsesRootEMUnits = false;
1471
mUsesExChUnits = false;
1472
1473
// TODO(emilio): It's unclear to me why would these three calls below be
1474
// needed. In particular, RebuildAllStyleData doesn't rebuild rules or
1475
// specified style information and such (note the comment in
1476
// RestyleManager::RebuildAllStyleData re. the funny semantics), so I
1477
// don't know why should we rebuild the user font set / counter styles /
1478
// etc...
1479
mDocument->MarkUserFontSetDirty();
1480
MarkCounterStylesDirty();
1481
MarkFontFeatureValuesDirty();
1482
1483
RestyleManager()->RebuildAllStyleData(aExtraHint, aRestyleHint);
1484
}
1485
1486
void nsPresContext::PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint,
1487
RestyleHint aRestyleHint) {
1488
if (!mPresShell) {
1489
// We must have been torn down. Nothing to do here.
1490
return;
1491
}
1492
RestyleManager()->PostRebuildAllStyleDataEvent(aExtraHint, aRestyleHint);
1493
}
1494
1495
static bool MediaFeatureValuesChangedAllDocumentsCallback(Document* aDocument,
1496
void* aChange) {
1497
auto* change = static_cast<const MediaFeatureChange*>(aChange);
1498
if (nsPresContext* pc = aDocument->GetPresContext()) {
1499
pc->MediaFeatureValuesChangedAllDocuments(*change);
1500
}
1501
return true;
1502
}
1503
1504
void nsPresContext::MediaFeatureValuesChangedAllDocuments(
1505
const MediaFeatureChange& aChange) {
1506
MediaFeatureValuesChanged(aChange);
1507
mDocument->EnumerateSubDocuments(
1508
MediaFeatureValuesChangedAllDocumentsCallback,
1509
const_cast<MediaFeatureChange*>(&aChange));
1510
}
1511
1512
void nsPresContext::FlushPendingMediaFeatureValuesChanged() {
1513
if (!mPendingMediaFeatureValuesChange) {
1514
return;
1515
}
1516
1517
MediaFeatureChange change = *mPendingMediaFeatureValuesChange;
1518
mPendingMediaFeatureValuesChange.reset();
1519
1520
// MediumFeaturesChanged updates the applied rules, so it always gets called.
1521
if (mPresShell) {
1522
change.mRestyleHint |=
1523
mPresShell->StyleSet()->MediumFeaturesChanged(change.mReason);
1524
}
1525
1526
if (change.mRestyleHint || change.mChangeHint) {
1527
RebuildAllStyleData(change.mChangeHint, change.mRestyleHint);
1528
}
1529
1530
if (!mPresShell || !mPresShell->DidInitialize()) {
1531
return;
1532
}
1533
1534
if (mDocument->IsBeingUsedAsImage()) {
1535
MOZ_ASSERT(mDocument->MediaQueryLists().isEmpty());
1536
return;
1537
}
1538
1539
mDocument->NotifyMediaFeatureValuesChanged();
1540
1541
MOZ_DIAGNOSTIC_ASSERT(nsContentUtils::IsSafeToRunScript());
1542
1543
// Media query list listeners should be notified from a queued task
1544
// (in HTML5 terms), although we also want to notify them on certain
1545
// flushes. (We're already running off an event.)
1546
//
1547
// Note that we do this after the new style from media queries in
1548
// style sheets has been computed.
1549
1550
if (mDocument->MediaQueryLists().isEmpty()) {
1551
return;
1552
}
1553
1554
// We build a list of all the notifications we're going to send
1555
// before we send any of them.
1556
1557
// Copy pointers to all the lists into a new array, in case one of our
1558
// notifications modifies the list.
1559
nsTArray<RefPtr<mozilla::dom::MediaQueryList>> localMediaQueryLists;
1560
for (MediaQueryList* mql = mDocument->MediaQueryLists().getFirst(); mql;
1561
mql = static_cast<LinkedListElement<MediaQueryList>*>(mql)->getNext()) {
1562
localMediaQueryLists.AppendElement(mql);
1563
}
1564
1565
// Now iterate our local array of the lists.
1566
for (const auto& mql : localMediaQueryLists) {
1567
nsAutoMicroTask mt;
1568
mql->MaybeNotify();
1569
}
1570
}
1571
1572
static bool NotifyTabSizeModeChanged(BrowserParent* aTab, void* aArg) {
1573
nsSizeMode* sizeMode = static_cast<nsSizeMode*>(aArg);
1574
aTab->SizeModeChanged(*sizeMode);
1575
return false;
1576
}
1577
1578
void nsPresContext::SizeModeChanged(nsSizeMode aSizeMode) {
1579
if (nsPIDOMWindowOuter* window = mDocument->GetWindow()) {
1580
nsContentUtils::CallOnAllRemoteChildren(window, NotifyTabSizeModeChanged,
1581
&aSizeMode);
1582
}
1583
MediaFeatureValuesChangedAllDocuments(
1584
{MediaFeatureChangeReason::SizeModeChange});
1585
}
1586
1587
nsCompatibility nsPresContext::CompatibilityMode() const {
1588
return Document()->GetCompatibilityMode();
1589
}
1590
1591
void nsPresContext::SetPaginatedScrolling(bool aPaginated) {
1592
if (mType == eContext_PrintPreview || mType == eContext_PageLayout)
1593
mCanPaginatedScroll = aPaginated;
1594
}
1595
1596
void nsPresContext::SetPrintSettings(nsIPrintSettings* aPrintSettings) {
1597
if (mMedium == nsGkAtoms::print) mPrintSettings = aPrintSettings;
1598
}
1599
1600
bool nsPresContext::EnsureVisible() {
1601
nsCOMPtr<nsIDocShell> docShell(GetDocShell());
1602
if (!docShell) {
1603
return false;
1604
}
1605
nsCOMPtr<nsIContentViewer> cv;
1606
docShell->GetContentViewer(getter_AddRefs(cv));
1607
// Make sure this is the content viewer we belong with
1608
if (!cv || cv->GetPresContext() != this) {
1609
return false;
1610
}
1611
// OK, this is us. We want to call Show() on the content viewer.
1612
nsresult result = cv->Show();
1613
return NS_SUCCEEDED(result);
1614
}
1615
1616
#ifdef MOZ_REFLOW_PERF
1617
void nsPresContext::CountReflows(const char* aName, nsIFrame* aFrame) {
1618
if (mPresShell) {
1619
mPresShell->CountReflows(aName, aFrame);
1620
}
1621
}
1622
#endif
1623
1624
bool nsPresContext::HasAuthorSpecifiedRules(const nsIFrame* aFrame,
1625
uint32_t aRuleTypeMask) const {
1626
Element* elem = aFrame->GetContent()->AsElement();
1627
1628
// We need to handle non-generated content pseudos too, so we use
1629
// the parent of generated content pseudo to be consistent.
1630
if (elem->GetPseudoElementType() != PseudoStyleType::NotPseudo) {
1631
MOZ_ASSERT(elem->GetParent(), "Pseudo element has no parent element?");
1632
elem = elem->GetParent()->AsElement();
1633
}
1634
if (MOZ_UNLIKELY(!elem->HasServoData())) {
1635
// Probably shouldn't happen, but does. See bug 1387953
1636
return false;
1637
}
1638
1639
// Anonymous boxes are more complicated, and we just assume that they
1640
// cannot have any author-specified rules here.
1641
if (aFrame->Style()->IsAnonBox()) {
1642
return false;
1643
}
1644
1645
auto* set = PresShell()->StyleSet()->RawSet();
1646
return Servo_HasAuthorSpecifiedRules(set, aFrame->Style(), elem,
1647
aRuleTypeMask);
1648
}
1649
1650
gfxUserFontSet* nsPresContext::GetUserFontSet() {
1651
return mDocument->GetUserFontSet();
1652
}
1653
1654
void nsPresContext::UserFontSetUpdated(gfxUserFontEntry* aUpdatedFont) {
1655
if (!mPresShell) {
1656
return;
1657
}
1658
1659
// Note: this method is called without a font when rules in the userfont set
1660
// are updated.
1661
//
1662
// We can avoid a full restyle if font-metric-dependent units are not in use,
1663
// since we know there's no style resolution that would depend on this font
1664
// and trigger its load.
1665
//
1666
// TODO(emilio): We could be more granular if we knew which families have
1667
// potentially changed.
1668
if (!aUpdatedFont) {
1669
auto hint =
1670
UsesExChUnits() ? RestyleHint::RecascadeSubtree() : RestyleHint{0};
1671
PostRebuildAllStyleDataEvent(NS_STYLE_HINT_REFLOW, hint);
1672
return;
1673
}
1674
1675
// Iterate over the frame tree looking for frames associated with the
1676
// downloadable font family in question. If a frame's nsStyleFont has
1677
// the name, check the font group associated with the metrics to see if
1678
// it contains that specific font (i.e. the one chosen within the family
1679
// given the weight, width, and slant from the nsStyleFont). If it does,
1680
// mark that frame dirty and skip inspecting its descendants.
1681
if (nsIFrame* root = mPresShell->GetRootFrame()) {
1682
nsFontFaceUtils::MarkDirtyForFontChange(root, aUpdatedFont);
1683
}
1684
}
1685
1686
class CounterStyleCleaner final : public nsAPostRefreshObserver {
1687
public:
1688
CounterStyleCleaner(nsRefreshDriver* aRefreshDriver,
1689
CounterStyleManager* aCounterStyleManager)
1690
: mRefreshDriver(aRefreshDriver),
1691
mCounterStyleManager(aCounterStyleManager) {}
1692
virtual ~CounterStyleCleaner() {}
1693
1694
void DidRefresh() final {
1695
mRefreshDriver->RemovePostRefreshObserver(this);
1696
mCounterStyleManager->CleanRetiredStyles();
1697
delete this;
1698
}
1699
1700
private:
1701
RefPtr<nsRefreshDriver> mRefreshDriver;
1702
RefPtr<CounterStyleManager> mCounterStyleManager;
1703
};
1704
1705
void nsPresContext::FlushCounterStyles() {
1706
if (!mPresShell) {
1707
return; // we've been torn down
1708
}
1709
if (mCounterStyleManager->IsInitial()) {
1710
// Still in its initial state, no need to clean.
1711
return;
1712
}
1713
1714
if (mCounterStylesDirty) {
1715
bool changed = mCounterStyleManager->NotifyRuleChanged();
1716
if (changed) {
1717
PresShell()->NotifyCounterStylesAreDirty();
1718
PostRebuildAllStyleDataEvent(NS_STYLE_HINT_REFLOW, RestyleHint{0});
1719
RefreshDriver()->AddPostRefreshObserver(
1720
new CounterStyleCleaner(RefreshDriver(), mCounterStyleManager));
1721
}
1722
mCounterStylesDirty = false;
1723
}
1724
}
1725
1726
void nsPresContext::MarkCounterStylesDirty() {
1727
if (mCounterStyleManager->IsInitial()) {
1728
// Still in its initial state, no need to touch anything.
1729
return;
1730
}
1731
1732
mCounterStylesDirty = true;
1733
}
1734
1735
void nsPresContext::NotifyMissingFonts() {
1736
if (mMissingFonts) {
1737
mMissingFonts->Flush();
1738
}
1739
}
1740
1741
void nsPresContext::EnsureSafeToHandOutCSSRules() {
1742
if (!mPresShell->StyleSet()->EnsureUniqueInnerOnCSSSheets()) {
1743
// Nothing to do.
1744
return;
1745
}
1746
1747
RebuildAllStyleData(nsChangeHint(0), RestyleHint::RestyleSubtree());
1748
}
1749
1750
void nsPresContext::FireDOMPaintEvent(
1751
nsTArray<nsRect>* aList, TransactionId aTransactionId,
1752
mozilla::TimeStamp aTimeStamp /* = mozilla::TimeStamp() */) {
1753
nsPIDOMWindowInner* ourWindow = mDocument->GetInnerWindow();
1754
if (!ourWindow) return;
1755
1756
nsCOMPtr<EventTarget> dispatchTarget = do_QueryInterface(ourWindow);
1757
nsCOMPtr<EventTarget> eventTarget = dispatchTarget;
1758
if (!IsChrome() && !mSendAfterPaintToContent) {
1759
// Don't tell the window about this event, it should not know that
1760
// something happened in a subdocument. Tell only the chrome event handler.
1761
// (Events sent to the window get propagated to the chrome event handler
1762
// automatically.)
1763
dispatchTarget = ourWindow->GetParentTarget();
1764
if (!dispatchTarget) {
1765
return;
1766
}
1767
}
1768
1769
if (aTimeStamp.IsNull()) {
1770
aTimeStamp = mozilla::TimeStamp::Now();
1771
}
1772
DOMHighResTimeStamp timeStamp = 0;
1773
if (ourWindow) {
1774
mozilla::dom::Performance* perf = ourWindow->GetPerformance();
1775
if (perf) {
1776
timeStamp = perf->GetDOMTiming()->TimeStampToDOMHighRes(aTimeStamp);
1777
}
1778
}
1779
1780
// Events sent to the window get propagated to the chrome event handler
1781
// automatically.
1782
//
1783
// This will empty our list in case dispatching the event causes more damage
1784
// (hopefully it won't, or we're likely to get an infinite loop! At least
1785
// it won't be blocking app execution though).
1786
RefPtr<NotifyPaintEvent> event =
1787
NS_NewDOMNotifyPaintEvent(eventTarget, this, nullptr, eAfterPaint, aList,
1788
uint64_t(aTransactionId), timeStamp);
1789
1790
// Even if we're not telling the window about the event (so eventTarget is
1791
// the chrome event handler, not the window), the window is still
1792
// logically the event target.
1793
event->SetTarget(eventTarget);
1794
event->SetTrusted(true);
1795
EventDispatcher::DispatchDOMEvent(dispatchTarget, nullptr,
1796
static_cast<Event*>(event), this, nullptr);
1797
}
1798
1799
static bool MayHavePaintEventListenerSubdocumentCallback(Document* aDocument,
1800
void* aData) {
1801
bool* result = static_cast<bool*>(aData);
1802
nsPresContext* pc = aDocument->GetPresContext();
1803
if (pc) {
1804
*result = pc->MayHavePaintEventListenerInSubDocument();
1805
1806
// If we found a paint event listener, then we can stop enumerating
1807
// sub documents.
1808
return !*result;
1809
}
1810
return true;
1811
}
1812
1813
static bool MayHavePaintEventListener(nsPIDOMWindowInner* aInnerWindow) {
1814
if (!aInnerWindow) return false;
1815
if (aInnerWindow->HasPaintEventListeners()) return true;
1816
1817
EventTarget* parentTarget = aInnerWindow->GetParentTarget();
1818
if (!parentTarget) return false;
1819
1820
EventListenerManager* manager = nullptr;
1821
if ((manager = parentTarget->GetExistingListenerManager()) &&
1822
manager->MayHavePaintEventListener()) {
1823
return true;
1824
}
1825
1826
nsCOMPtr<nsINode> node;
1827
if (parentTarget != aInnerWindow->GetChromeEventHandler()) {
1828
nsCOMPtr<nsIInProcessContentFrameMessageManager> mm =
1829
do_QueryInterface(parentTarget);
1830
if (mm) {
1831
node = mm->GetOwnerContent();
1832
}
1833
}
1834
1835
if (!node) {
1836
node = do_QueryInterface(parentTarget);
1837
}
1838
if (node)
1839
return MayHavePaintEventListener(node->OwnerDoc()->GetInnerWindow());
1840
1841
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(parentTarget);
1842
if (window) return MayHavePaintEventListener(window);
1843
1844
nsCOMPtr<nsPIWindowRoot> root = do_QueryInterface(parentTarget);
1845
EventTarget* browserChildGlobal;
1846
return root && (browserChildGlobal = root->GetParentTarget()) &&
1847
(manager = browserChildGlobal->GetExistingListenerManager()) &&
1848
manager->MayHavePaintEventListener();
1849
}
1850
1851
bool nsPresContext::MayHavePaintEventListener() {
1852
return ::MayHavePaintEventListener(mDocument->GetInnerWindow());
1853
}
1854
1855
bool nsPresContext::MayHavePaintEventListenerInSubDocument() {
1856
if (MayHavePaintEventListener()) {
1857
return true;
1858
}
1859
1860
bool result = false;
1861
mDocument->EnumerateSubDocuments(MayHavePaintEventListenerSubdocumentCallback,
1862
&result);
1863
return result;
1864
}
1865
1866
void nsPresContext::NotifyInvalidation(TransactionId aTransactionId,
1867
const nsIntRect& aRect) {
1868
// Prevent values from overflow after DevPixelsToAppUnits().
1869
//
1870
// DevPixelsTopAppUnits() will multiple a factor (60) to the value,
1871
// it may make the result value over the edge (overflow) of max or
1872
// min value of int32_t. Compute the max sized dev pixel rect that
1873
// we can support and intersect with it.
1874
nsIntRect clampedRect = nsIntRect::MaxIntRect();
1875
clampedRect.ScaleInverseRoundIn(AppUnitsPerDevPixel());
1876
1877
clampedRect = clampedRect.Intersect(aRect);
1878
1879
nsRect rect(DevPixelsToAppUnits(clampedRect.x),
1880
DevPixelsToAppUnits(clampedRect.y),
1881
DevPixelsToAppUnits(clampedRect.width),
1882
DevPixelsToAppUnits(clampedRect.height));
1883
NotifyInvalidation(aTransactionId, rect);
1884
}
1885
1886
nsPresContext::TransactionInvalidations* nsPresContext::GetInvalidations(
1887
TransactionId aTransactionId) {
1888
for (TransactionInvalidations& t : mTransactions) {
1889
if (t.mTransactionId == aTransactionId) {
1890
return &t;
1891
}
1892
}
1893
return nullptr;
1894
}
1895
1896
void nsPresContext::NotifyInvalidation(TransactionId aTransactionId,
1897
const nsRect& aRect) {
1898
MOZ_ASSERT(GetContainerWeak(), "Invalidation in detached pres context");
1899
1900
// If there is no paint event listener, then we don't need to fire
1901
// the asynchronous event. We don't even need to record invalidation.
1902
// MayHavePaintEventListener is pretty cheap and we could make it
1903
// even cheaper by providing a more efficient
1904
// nsPIDOMWindow::GetListenerManager.
1905
1906
nsPresContext* pc;
1907
for (pc = this; pc; pc = pc->GetParentPresContext()) {
1908
TransactionInvalidations* transaction =
1909
pc->GetInvalidations(aTransactionId);
1910
if (transaction) {
1911
break;
1912
} else {
1913
transaction = pc->mTransactions.AppendElement();
1914
transaction->mTransactionId = aTransactionId;
1915
}
1916
}
1917
1918
TransactionInvalidations* transaction = GetInvalidations(aTransactionId);
1919
MOZ_ASSERT(transaction);
1920
transaction->mInvalidations.AppendElement(aRect);
1921
}
1922
1923
/* static */
1924
void nsPresContext::NotifySubDocInvalidation(