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/. */
7 /* a presentation of a document, part 2 */
9 #include "mozilla/PresShell.h"
12 #include "mozilla/dom/FontFaceSet.h"
13 #include "mozilla/ArrayUtils.h"
14 #include "mozilla/Attributes.h"
15 #include "mozilla/AutoRestore.h"
16 #include "mozilla/ContentIterator.h"
17 #include "mozilla/DisplayPortUtils.h"
18 #include "mozilla/EventDispatcher.h"
19 #include "mozilla/EventStateManager.h"
20 #include "mozilla/EventStates.h"
21 #include "mozilla/GeckoMVMContext.h"
22 #include "mozilla/IMEStateManager.h"
23 #include "mozilla/MemoryReporting.h"
24 #include "mozilla/dom/BrowserChild.h"
25 #include "mozilla/Likely.h"
26 #include "mozilla/Logging.h"
27 #include "mozilla/MouseEvents.h"
28 #include "mozilla/PerfStats.h"
29 #include "mozilla/PointerLockManager.h"
30 #include "mozilla/PresShellInlines.h"
31 #include "mozilla/RangeUtils.h"
32 #include "mozilla/ScopeExit.h"
33 #include "mozilla/Sprintf.h"
34 #include "mozilla/StaticAnalysisFunctions.h"
35 #include "mozilla/StaticPrefs_apz.h"
36 #include "mozilla/StaticPrefs_browser.h"
37 #include "mozilla/StaticPrefs_dom.h"
38 #include "mozilla/StaticPrefs_font.h"
39 #include "mozilla/StaticPrefs_layout.h"
40 #include "mozilla/TextEvents.h"
41 #include "mozilla/TimeStamp.h"
42 #include "mozilla/TouchEvents.h"
43 #include "mozilla/UniquePtr.h"
44 #include "mozilla/Unused.h"
45 #include "mozilla/ViewportUtils.h"
52 #include "gfxContext.h"
53 #include "gfxUserFontSet.h"
54 #include "nsContentList.h"
55 #include "nsPresContext.h"
56 #include "nsIContent.h"
57 #include "mozilla/dom/BrowserBridgeChild.h"
58 #include "mozilla/dom/BrowsingContext.h"
59 #include "mozilla/dom/CanonicalBrowsingContext.h"
60 #include "mozilla/dom/ContentChild.h"
61 #include "mozilla/dom/ContentParent.h"
62 #include "mozilla/dom/Element.h"
63 #include "mozilla/dom/PointerEventHandler.h"
64 #include "mozilla/dom/PopupBlocker.h"
65 #include "mozilla/dom/Document.h"
66 #include "mozilla/dom/DocumentInlines.h"
67 #include "mozilla/dom/UserActivation.h"
68 #include "nsAnimationManager.h"
69 #include "nsNameSpaceManager.h" // for Pref-related rule management (bugs 22963,20760,31816)
70 #include "nsFlexContainerFrame.h"
72 #include "FrameLayerBuilder.h"
73 #include "nsViewManager.h"
75 #include "nsCRTGlue.h"
78 #include "nsCOMArray.h"
79 #include "nsContainerFrame.h"
80 #include "mozilla/dom/Selection.h"
81 #include "nsGkAtoms.h"
83 #include "nsWindowSizes.h"
85 #include "nsReadableUtils.h"
86 #include "nsPageSequenceFrame.h"
88 #include "mozilla/AccessibleCaretEventHub.h"
89 #include "nsFrameManager.h"
91 #include "nsILayoutHistoryState.h"
92 #include "nsILineIterator.h" // for ScrollContentIntoView
93 #include "PLDHashTable.h"
94 #include "mozilla/dom/Touch.h"
95 #include "mozilla/dom/TouchEvent.h"
96 #include "mozilla/dom/PointerEventBinding.h"
97 #include "mozilla/dom/ShadowIncludingTreeIterator.h"
98 #include "nsIObserverService.h"
99 #include "nsDocShell.h" // for reflow observation
100 #include "nsIBaseWindow.h"
102 #include "nsLayoutUtils.h"
103 #include "nsViewportInfo.h"
104 #include "nsCSSRendering.h"
105 // for |#ifdef DEBUG| code
107 #include "nsDisplayList.h"
108 #include "nsRegion.h"
109 #include "nsAutoLayoutPhase.h"
110 #include "AutoProfilerStyleMarker.h"
111 #ifdef MOZ_REFLOW_PERF
112 # include "nsFontMetrics.h"
114 #include "MobileViewportManager.h"
115 #include "OverflowChangedTracker.h"
116 #include "PositionedEventTargeting.h"
118 #include "nsIReflowCallback.h"
120 #include "nsPIDOMWindow.h"
121 #include "nsFocusManager.h"
122 #include "nsNetUtil.h"
123 #include "nsThreadUtils.h"
124 #include "nsStyleSheetService.h"
125 #include "gfxUtils.h"
126 #include "mozilla/SMILAnimationController.h"
127 #include "mozilla/dom/SVGAnimationElement.h"
128 #include "mozilla/SVGObserverUtils.h"
129 #include "mozilla/SVGFragmentIdentifier.h"
130 #include "nsFrameSelection.h"
132 #include "mozilla/dom/Performance.h"
133 #include "nsRefreshDriver.h"
134 #include "nsDOMNavigationTiming.h"
136 // Drag & Drop, Clipboard
137 #include "nsIDocShellTreeItem.h"
139 #include "nsIScrollableFrame.h"
140 #include "nsITimer.h"
142 # include "nsAccessibilityService.h"
143 # include "mozilla/a11y/DocAccessible.h"
145 # include "mozilla/a11y/Logging.h"
149 // For style data reconstruction
150 #include "nsStyleChangeList.h"
151 #include "nsCSSFrameConstructor.h"
153 # include "nsMenuFrame.h"
154 # include "nsTreeBodyFrame.h"
155 # include "XULTreeElement.h"
156 # include "nsMenuPopupFrame.h"
157 # include "nsTreeColumns.h"
158 # include "nsIDOMXULMultSelectCntrlEl.h"
159 # include "nsIDOMXULSelectCntrlItemEl.h"
160 # include "nsIDOMXULMenuListElement.h"
161 # include "nsXULElement.h"
164 #include "mozilla/layers/CompositorBridgeChild.h"
165 #include "ClientLayerManager.h"
166 #include "gfxPlatform.h"
168 #include "LayerTreeInvalidation.h"
169 #include "mozilla/css/ImageLoader.h"
170 #include "mozilla/dom/DocumentTimeline.h"
171 #include "mozilla/dom/ScriptSettings.h"
172 #include "mozilla/ErrorResult.h"
173 #include "mozilla/Preferences.h"
174 #include "mozilla/Telemetry.h"
175 #include "nsCanvasFrame.h"
176 #include "nsImageFrame.h"
177 #include "nsIScreen.h"
178 #include "nsIScreenManager.h"
179 #include "nsPlaceholderFrame.h"
180 #include "nsTransitionManager.h"
181 #include "ChildIterator.h"
182 #include "mozilla/RestyleManager.h"
183 #include "nsIDragSession.h"
184 #include "nsIFrameInlines.h"
185 #include "mozilla/gfx/2D.h"
186 #include "nsNetUtil.h"
187 #include "nsSubDocumentFrame.h"
188 #include "nsQueryObject.h"
189 #include "mozilla/GlobalStyleSheetCache.h"
190 #include "mozilla/layers/InputAPZContext.h"
191 #include "mozilla/layers/FocusTarget.h"
192 #include "mozilla/layers/WebRenderLayerManager.h"
193 #include "mozilla/layers/WebRenderUserData.h"
194 #include "mozilla/layout/ScrollAnchorContainer.h"
195 #include "mozilla/ProfilerLabels.h"
196 #include "mozilla/ProfilerMarkers.h"
197 #include "mozilla/ScrollTypes.h"
198 #include "mozilla/ServoBindings.h"
199 #include "mozilla/ServoStyleSet.h"
200 #include "mozilla/StyleSheet.h"
201 #include "mozilla/StyleSheetInlines.h"
202 #include "mozilla/InputTaskManager.h"
203 #include "mozilla/dom/ImageTracker.h"
204 #include "nsIDocShellTreeOwner.h"
205 #include "nsClassHashtable.h"
206 #include "nsHashKeys.h"
207 #include "VisualViewport.h"
208 #include "ZoomConstraintsClient.h"
210 // define the scalfactor of drag and drop images
211 // relative to the max screen height/width
212 #define RELATIVE_SCALEFACTOR 0.0925f
214 using namespace mozilla
;
215 using namespace mozilla::css
;
216 using namespace mozilla::dom
;
217 using namespace mozilla::gfx
;
218 using namespace mozilla::layers
;
219 using namespace mozilla::gfx
;
220 using namespace mozilla::layout
;
221 using PaintFrameFlags
= nsLayoutUtils::PaintFrameFlags
;
222 typedef ScrollableLayerGuid::ViewID ViewID
;
224 PresShell::CapturingContentInfo
PresShell::sCapturingContentInfo
;
226 // RangePaintInfo is used to paint ranges to offscreen buffers
227 struct RangePaintInfo
{
228 RefPtr
<nsRange
> mRange
;
229 nsDisplayListBuilder mBuilder
;
232 // offset of builder's reference frame to the root frame
235 // Resolution at which the items are normally painted. So if we're painting
236 // these items in a range separately from the "full display list", we may want
237 // to paint them at this resolution.
238 float mResolution
= 1.0;
240 RangePaintInfo(nsRange
* aRange
, nsIFrame
* aFrame
)
242 mBuilder(aFrame
, nsDisplayListBuilderMode::Painting
, false) {
243 MOZ_COUNT_CTOR(RangePaintInfo
);
244 mBuilder
.BeginFrame();
248 mList
.DeleteAll(&mBuilder
);
250 MOZ_COUNT_DTOR(RangePaintInfo
);
256 // ----------------------------------------------------------------------
259 // Set the environment variable GECKO_VERIFY_REFLOW_FLAGS to one or
260 // more of the following flags (comma separated) for handy debug
262 static VerifyReflowFlags gVerifyReflowFlags
;
264 struct VerifyReflowFlagData
{
266 VerifyReflowFlags bit
;
269 static const VerifyReflowFlagData gFlags
[] = {
271 { "verify", VerifyReflowFlags::On
},
272 { "reflow", VerifyReflowFlags::Noisy
},
273 { "all", VerifyReflowFlags::All
},
274 { "list-commands", VerifyReflowFlags::DumpCommands
},
275 { "noisy-commands", VerifyReflowFlags::NoisyCommands
},
276 { "really-noisy-commands", VerifyReflowFlags::ReallyNoisyCommands
},
277 { "resize", VerifyReflowFlags::DuringResizeReflow
},
281 # define NUM_VERIFY_REFLOW_FLAGS (sizeof(gFlags) / sizeof(gFlags[0]))
283 static void ShowVerifyReflowFlags() {
284 printf("Here are the available GECKO_VERIFY_REFLOW_FLAGS:\n");
285 const VerifyReflowFlagData
* flag
= gFlags
;
286 const VerifyReflowFlagData
* limit
= gFlags
+ NUM_VERIFY_REFLOW_FLAGS
;
287 while (flag
< limit
) {
288 printf(" %s\n", flag
->name
);
291 printf("Note: GECKO_VERIFY_REFLOW_FLAGS is a comma separated list of flag\n");
292 printf("names (no whitespace)\n");
296 //========================================================================
297 //========================================================================
298 //========================================================================
299 #ifdef MOZ_REFLOW_PERF
300 class ReflowCountMgr
;
302 static const char kGrandTotalsStr
[] = "Grand Totals";
305 class ReflowCounter
{
307 explicit ReflowCounter(ReflowCountMgr
* aMgr
= nullptr);
311 void DisplayTotals(const char* aStr
);
312 void DisplayDiffTotals(const char* aStr
);
313 void DisplayHTMLTotals(const char* aStr
);
315 void Add() { mTotal
++; }
316 void Add(uint32_t aTotal
) { mTotal
+= aTotal
; }
318 void CalcDiffInTotals();
319 void SetTotalsCache();
321 void SetMgr(ReflowCountMgr
* aMgr
) { mMgr
= aMgr
; }
323 uint32_t GetTotal() { return mTotal
; }
326 void DisplayTotals(uint32_t aTotal
, const char* aTitle
);
327 void DisplayHTMLTotals(uint32_t aTotal
, const char* aTitle
);
330 uint32_t mCacheTotal
;
332 ReflowCountMgr
* mMgr
; // weak reference (don't delete)
336 class IndiReflowCounter
{
338 explicit IndiReflowCounter(ReflowCountMgr
* aMgr
= nullptr)
343 mHasBeenOutput(false) {}
344 virtual ~IndiReflowCounter() = default;
347 nsIFrame
* mFrame
; // weak reference (don't delete)
350 ReflowCountMgr
* mMgr
; // weak reference (don't delete)
352 ReflowCounter mCounter
;
356 //--------------------
358 //--------------------
359 class ReflowCountMgr
{
362 virtual ~ReflowCountMgr();
365 void ClearGrandTotals();
366 void DisplayTotals(const char* aStr
);
367 void DisplayHTMLTotals(const char* aStr
);
368 void DisplayDiffsInTotals();
370 void Add(const char* aName
, nsIFrame
* aFrame
);
371 ReflowCounter
* LookUp(const char* aName
);
373 void PaintCount(const char* aName
, gfxContext
* aRenderingContext
,
374 nsPresContext
* aPresContext
, nsIFrame
* aFrame
,
375 const nsPoint
& aOffset
, uint32_t aColor
);
377 FILE* GetOutFile() { return mFD
; }
379 void SetPresContext(nsPresContext
* aPresContext
) {
380 mPresContext
= aPresContext
; // weak reference
382 void SetPresShell(PresShell
* aPresShell
) {
383 mPresShell
= aPresShell
; // weak reference
386 void SetDumpFrameCounts(bool aVal
) { mDumpFrameCounts
= aVal
; }
387 void SetDumpFrameByFrameCounts(bool aVal
) { mDumpFrameByFrameCounts
= aVal
; }
388 void SetPaintFrameCounts(bool aVal
) { mPaintFrameByFrameCounts
= aVal
; }
390 bool IsPaintingFrameCounts() { return mPaintFrameByFrameCounts
; }
393 void DisplayTotals(uint32_t aTotal
, uint32_t* aDupArray
, char* aTitle
);
394 void DisplayHTMLTotals(uint32_t aTotal
, uint32_t* aDupArray
, char* aTitle
);
396 void DoGrandTotals();
397 void DoIndiTotalsTree();
399 // HTML Output Methods
400 void DoGrandHTMLTotals();
402 nsClassHashtable
<nsCharPtrHashKey
, ReflowCounter
> mCounts
;
403 nsClassHashtable
<nsCharPtrHashKey
, IndiReflowCounter
> mIndiFrameCounts
;
406 bool mDumpFrameCounts
;
407 bool mDumpFrameByFrameCounts
;
408 bool mPaintFrameByFrameCounts
;
412 // Root Frame for Individual Tracking
413 nsPresContext
* mPresContext
;
414 PresShell
* mPresShell
;
416 // ReflowCountMgr gReflowCountMgr;
419 //========================================================================
421 // comment out to hide caret
424 // The upper bound on the amount of time to spend reflowing, in
425 // microseconds. When this bound is exceeded and reflow commands are
426 // still queued up, a reflow event is posted. The idea is for reflow
427 // to not hog the processor beyond the time specifed in
428 // gMaxRCProcessingTime. This data member is initialized from the
429 // layout.reflow.timeslice pref.
430 #define NS_MAX_REFLOW_TIME 1000000
431 static int32_t gMaxRCProcessingTime
= -1;
433 struct nsCallbackEventRequest
{
434 nsIReflowCallback
* callback
;
435 nsCallbackEventRequest
* next
;
438 // ----------------------------------------------------------------------------
440 // NOTE(emilio): It'd be nice for this to assert that our document isn't in the
441 // bfcache, but font pref changes don't care about that, and maybe / probably
444 # define ASSERT_REFLOW_SCHEDULED_STATE() \
446 if (ObservingLayoutFlushes()) { \
448 mDocument->GetBFCacheEntry() || \
449 mPresContext->RefreshDriver()->IsLayoutFlushObserver(this), \
450 "Unexpected state"); \
453 !mPresContext->RefreshDriver()->IsLayoutFlushObserver(this), \
454 "Unexpected state"); \
458 # define ASSERT_REFLOW_SCHEDULED_STATE() /* nothing */
461 class nsAutoCauseReflowNotifier
{
463 MOZ_CAN_RUN_SCRIPT
explicit nsAutoCauseReflowNotifier(PresShell
* aPresShell
)
464 : mPresShell(aPresShell
) {
465 mPresShell
->WillCauseReflow();
467 MOZ_CAN_RUN_SCRIPT
~nsAutoCauseReflowNotifier() {
468 // This check should not be needed. Currently the only place that seem
469 // to need it is the code that deals with bug 337586.
470 if (!mPresShell
->mHaveShutDown
) {
471 RefPtr
<PresShell
> presShell(mPresShell
);
472 presShell
->DidCauseReflow();
474 nsContentUtils::RemoveScriptBlocker();
478 PresShell
* mPresShell
;
481 class MOZ_STACK_CLASS nsPresShellEventCB
: public EventDispatchingCallback
{
483 explicit nsPresShellEventCB(PresShell
* aPresShell
) : mPresShell(aPresShell
) {}
486 virtual void HandleEvent(EventChainPostVisitor
& aVisitor
) override
{
487 if (aVisitor
.mPresContext
&& aVisitor
.mEvent
->mClass
!= eBasicEventClass
) {
488 if (aVisitor
.mEvent
->mMessage
== eMouseDown
||
489 aVisitor
.mEvent
->mMessage
== eMouseUp
) {
490 // Mouse-up and mouse-down events call nsIFrame::HandlePress/Release
491 // which call GetContentOffsetsFromPoint which requires up-to-date
492 // layout. Bring layout up-to-date now so that GetCurrentEventFrame()
493 // below will return a real frame and we don't have to worry about
494 // destroying it by flushing later.
495 MOZ_KnownLive(mPresShell
)->FlushPendingNotifications(FlushType::Layout
);
496 } else if (aVisitor
.mEvent
->mMessage
== eWheel
&&
497 aVisitor
.mEventStatus
!= nsEventStatus_eConsumeNoDefault
) {
498 nsIFrame
* frame
= mPresShell
->GetCurrentEventFrame();
500 // chrome (including addons) should be able to know if content
501 // handles both D3E "wheel" event and legacy mouse scroll events.
502 // We should dispatch legacy mouse events before dispatching the
503 // "wheel" event into system group.
504 RefPtr
<EventStateManager
> esm
=
505 aVisitor
.mPresContext
->EventStateManager();
506 esm
->DispatchLegacyMouseScrollEvents(
507 frame
, aVisitor
.mEvent
->AsWheelEvent(), &aVisitor
.mEventStatus
);
510 nsIFrame
* frame
= mPresShell
->GetCurrentEventFrame();
511 if (!frame
&& (aVisitor
.mEvent
->mMessage
== eMouseUp
||
512 aVisitor
.mEvent
->mMessage
== eTouchEnd
)) {
513 // Redirect BUTTON_UP and TOUCH_END events to the root frame to ensure
514 // that capturing is released.
515 frame
= mPresShell
->GetRootFrame();
518 frame
->HandleEvent(aVisitor
.mPresContext
, aVisitor
.mEvent
->AsGUIEvent(),
519 &aVisitor
.mEventStatus
);
524 RefPtr
<PresShell
> mPresShell
;
527 class nsBeforeFirstPaintDispatcher
: public Runnable
{
529 explicit nsBeforeFirstPaintDispatcher(Document
* aDocument
)
530 : mozilla::Runnable("nsBeforeFirstPaintDispatcher"),
531 mDocument(aDocument
) {}
533 // Fires the "before-first-paint" event so that interested parties (right now,
534 // the mobile browser) are aware of it.
535 NS_IMETHOD
Run() override
{
536 nsCOMPtr
<nsIObserverService
> observerService
=
537 mozilla::services::GetObserverService();
538 if (observerService
) {
539 observerService
->NotifyObservers(ToSupports(mDocument
),
540 "before-first-paint", nullptr);
546 RefPtr
<Document
> mDocument
;
549 // This is a helper class to track whether the targeted frame is destroyed after
550 // dispatching pointer events. In that case, we need the original targeted
551 // content so that we can dispatch the mouse events to it.
552 class MOZ_STACK_CLASS AutoPointerEventTargetUpdater final
{
554 AutoPointerEventTargetUpdater(PresShell
* aShell
, WidgetEvent
* aEvent
,
555 nsIFrame
* aFrame
, nsIContent
** aTargetContent
) {
557 if (!aTargetContent
|| aEvent
->mClass
!= ePointerEventClass
) {
558 // Make the destructor happy.
559 mTargetContent
= nullptr;
564 MOZ_ASSERT(!aFrame
->GetContent() ||
565 aShell
->GetDocument() == aFrame
->GetContent()->OwnerDoc());
569 mTargetContent
= aTargetContent
;
570 aShell
->mPointerEventTarget
= aFrame
->GetContent();
573 ~AutoPointerEventTargetUpdater() {
574 if (!mTargetContent
|| !mShell
|| mWeakFrame
.IsAlive()) {
577 mShell
->mPointerEventTarget
.swap(*mTargetContent
);
581 RefPtr
<PresShell
> mShell
;
582 AutoWeakFrame mWeakFrame
;
583 nsIContent
** mTargetContent
;
586 void PresShell::DirtyRootsList::Add(nsIFrame
* aFrame
) {
587 // Is this root already scheduled for reflow?
588 // FIXME: This could possibly be changed to a uniqueness assertion, with some
589 // work in ResizeReflowIgnoreOverride (and maybe others?)
590 if (mList
.Contains(aFrame
)) {
591 // We don't expect frame to change depths.
592 MOZ_ASSERT(aFrame
->GetDepthInFrameTree() ==
593 mList
[mList
.IndexOf(aFrame
)].mDepth
);
597 mList
.InsertElementSorted(
598 FrameAndDepth
{aFrame
, aFrame
->GetDepthInFrameTree()},
599 FrameAndDepth::CompareByReverseDepth
{});
602 void PresShell::DirtyRootsList::Remove(nsIFrame
* aFrame
) {
603 mList
.RemoveElement(aFrame
);
606 nsIFrame
* PresShell::DirtyRootsList::PopShallowestRoot() {
607 // List is sorted in order of decreasing depth, so there are no deeper
608 // frames than the last one.
609 const FrameAndDepth
& lastFAD
= mList
.PopLastElement();
610 nsIFrame
* frame
= lastFAD
.mFrame
;
611 // We don't expect frame to change depths.
612 MOZ_ASSERT(frame
->GetDepthInFrameTree() == lastFAD
.mDepth
);
616 void PresShell::DirtyRootsList::Clear() { mList
.Clear(); }
618 bool PresShell::DirtyRootsList::Contains(nsIFrame
* aFrame
) const {
619 return mList
.Contains(aFrame
);
622 bool PresShell::DirtyRootsList::IsEmpty() const { return mList
.IsEmpty(); }
624 bool PresShell::DirtyRootsList::FrameIsAncestorOfDirtyRoot(
625 nsIFrame
* aFrame
) const {
628 // Look for a path from any dirty roots to aFrame, following GetParent().
629 // This check mirrors what FrameNeedsReflow() would have done if the reflow
630 // root didn't get in the way.
631 for (nsIFrame
* dirtyFrame
: mList
) {
633 if (dirtyFrame
== aFrame
) {
636 dirtyFrame
= dirtyFrame
->GetParent();
637 } while (dirtyFrame
);
643 bool PresShell::sDisableNonTestMouseEvents
= false;
645 LazyLogModule
PresShell::gLog("PresShell");
647 TimeStamp
PresShell::EventHandler::sLastInputCreated
;
648 TimeStamp
PresShell::EventHandler::sLastInputProcessed
;
649 StaticRefPtr
<Element
> PresShell::EventHandler::sLastKeyDownEventTargetElement
;
651 bool PresShell::sProcessInteractable
= false;
653 static bool gVerifyReflowEnabled
;
654 extern mozilla::LazyLogModule sApzMvmLog
;
656 bool PresShell::GetVerifyReflowEnable() {
658 static bool firstTime
= true;
661 char* flags
= PR_GetEnv("GECKO_VERIFY_REFLOW_FLAGS");
666 char* comma
= PL_strchr(flags
, ',');
667 if (comma
) *comma
= '\0';
670 const VerifyReflowFlagData
* flag
= gFlags
;
671 const VerifyReflowFlagData
* limit
= gFlags
+ NUM_VERIFY_REFLOW_FLAGS
;
672 while (flag
< limit
) {
673 if (PL_strcasecmp(flag
->name
, flags
) == 0) {
674 gVerifyReflowFlags
|= flag
->bit
;
681 if (!found
) error
= true;
689 if (error
) ShowVerifyReflowFlags();
692 if (VerifyReflowFlags::On
& gVerifyReflowFlags
) {
693 gVerifyReflowEnabled
= true;
695 printf("Note: verifyreflow is enabled");
696 if (VerifyReflowFlags::Noisy
& gVerifyReflowFlags
) {
699 if (VerifyReflowFlags::All
& gVerifyReflowFlags
) {
702 if (VerifyReflowFlags::DumpCommands
& gVerifyReflowFlags
) {
703 printf(" (show reflow commands)");
705 if (VerifyReflowFlags::NoisyCommands
& gVerifyReflowFlags
) {
706 printf(" (noisy reflow commands)");
707 if (VerifyReflowFlags::ReallyNoisyCommands
& gVerifyReflowFlags
) {
708 printf(" (REALLY noisy reflow commands)");
715 return gVerifyReflowEnabled
;
718 void PresShell::SetVerifyReflowEnable(bool aEnabled
) {
719 gVerifyReflowEnabled
= aEnabled
;
722 void PresShell::AddAutoWeakFrame(AutoWeakFrame
* aWeakFrame
) {
723 if (aWeakFrame
->GetFrame()) {
724 aWeakFrame
->GetFrame()->AddStateBits(NS_FRAME_EXTERNAL_REFERENCE
);
726 aWeakFrame
->SetPreviousWeakFrame(mAutoWeakFrames
);
727 mAutoWeakFrames
= aWeakFrame
;
730 void PresShell::AddWeakFrame(WeakFrame
* aWeakFrame
) {
731 if (aWeakFrame
->GetFrame()) {
732 aWeakFrame
->GetFrame()->AddStateBits(NS_FRAME_EXTERNAL_REFERENCE
);
734 MOZ_ASSERT(!mWeakFrames
.Contains(aWeakFrame
));
735 mWeakFrames
.Insert(aWeakFrame
);
738 void PresShell::RemoveAutoWeakFrame(AutoWeakFrame
* aWeakFrame
) {
739 if (mAutoWeakFrames
== aWeakFrame
) {
740 mAutoWeakFrames
= aWeakFrame
->GetPreviousWeakFrame();
743 AutoWeakFrame
* nextWeak
= mAutoWeakFrames
;
744 while (nextWeak
&& nextWeak
->GetPreviousWeakFrame() != aWeakFrame
) {
745 nextWeak
= nextWeak
->GetPreviousWeakFrame();
748 nextWeak
->SetPreviousWeakFrame(aWeakFrame
->GetPreviousWeakFrame());
752 void PresShell::RemoveWeakFrame(WeakFrame
* aWeakFrame
) {
753 MOZ_ASSERT(mWeakFrames
.Contains(aWeakFrame
));
754 mWeakFrames
.Remove(aWeakFrame
);
757 already_AddRefed
<nsFrameSelection
> PresShell::FrameSelection() {
758 RefPtr
<nsFrameSelection
> ret
= mSelection
;
762 //----------------------------------------------------------------------
764 static uint32_t sNextPresShellId
;
767 bool PresShell::AccessibleCaretEnabled(nsIDocShell
* aDocShell
) {
768 // If the pref forces it on, then enable it.
769 if (StaticPrefs::layout_accessiblecaret_enabled()) {
772 // If the touch pref is on, and touch events are enabled (this depends
773 // on the specific device running), then enable it.
774 if (StaticPrefs::layout_accessiblecaret_enabled_on_touch() &&
775 dom::TouchEvent::PrefEnabled(aDocShell
)) {
778 // Otherwise, disabled.
782 PresShell::PresShell(Document
* aDocument
)
783 : mDocument(aDocument
),
784 mViewManager(nullptr),
785 mFrameManager(nullptr),
786 mAutoWeakFrames(nullptr),
788 mDocAccessible(nullptr),
789 #endif // #ifdef ACCESSIBILITY
790 mCurrentEventFrame(nullptr),
791 mMouseLocation(NS_UNCONSTRAINEDSIZE
, NS_UNCONSTRAINEDSIZE
),
792 mLastResolutionChangeOrigin(ResolutionChangeOrigin::Apz
),
794 mAPZFocusSequenceNumber(0),
795 mCanvasBackgroundColor(NS_RGBA(0, 0, 0, 0)),
796 mActiveSuppressDisplayport(0),
797 mPresShellId(sNextPresShellId
++),
798 mFontSizeInflationEmPerLine(0),
799 mFontSizeInflationMinTwips(0),
800 mFontSizeInflationLineThreshold(0),
801 mSelectionFlags(nsISelectionDisplay::DISPLAY_TEXT
|
802 nsISelectionDisplay::DISPLAY_IMAGES
),
804 mRenderingStateFlags(RenderingStateFlags::None
),
806 mCaretEnabled(false),
807 mNeedLayoutFlush(true),
808 mNeedStyleFlush(true),
809 mNeedThrottledAnimationFlush(true),
810 mVisualViewportSizeSet(false),
811 mDidInitialize(false),
812 mIsDestroying(false),
814 mIsObservingDocument(false),
815 mForbiddenToFlush(false),
816 mIsDocumentGone(false),
817 mHaveShutDown(false),
818 mPaintingSuppressed(false),
819 mLastRootReflowHadUnconstrainedBSize(false),
820 mShouldUnsuppressPainting(false),
821 mIgnoreFrameDestruction(false),
825 mObservesMutationsForPrint(false),
826 mWasLastReflowInterrupted(false),
827 mObservingStyleFlushes(false),
828 mObservingLayoutFlushes(false),
829 mResizeEventPending(false),
830 mFontSizeInflationForceEnabled(false),
831 mFontSizeInflationDisabledInMasterProcess(false),
832 mFontSizeInflationEnabled(false),
833 mIsNeverPainting(false),
834 mResolutionUpdated(false),
835 mResolutionUpdatedByApz(false),
836 mUnderHiddenEmbedderElement(false),
837 mDocumentLoading(false),
838 mNoDelayedMouseEvents(false),
839 mNoDelayedKeyEvents(false),
840 mApproximateFrameVisibilityVisited(false),
841 mNextPaintCompressed(false),
842 mHasCSSBackgroundColor(true),
843 mIsLastChromeOnlyEscapeKeyConsumed(false),
844 mHasReceivedPaintMessage(false),
845 mIsLastKeyDownCanceled(false),
846 mHasHandledUserInput(false),
847 mForceDispatchKeyPressEventsForNonPrintableKeys(false),
848 mForceUseLegacyKeyCodeAndCharCodeValues(false),
849 mInitializedWithKeyPressEventDispatchingBlacklist(false),
850 mForceUseLegacyNonPrimaryDispatch(false),
851 mInitializedWithClickEventDispatchingBlacklist(false),
852 mMouseLocationWasSetBySynthesizedMouseEventForTests(false) {
853 MOZ_LOG(gLog
, LogLevel::Debug
, ("PresShell::PresShell this=%p", this));
854 MOZ_ASSERT(aDocument
);
856 #ifdef MOZ_REFLOW_PERF
857 mReflowCountMgr
= MakeUnique
<ReflowCountMgr
>();
858 mReflowCountMgr
->SetPresContext(mPresContext
);
859 mReflowCountMgr
->SetPresShell(this);
861 mLastOSWake
= mLoadBegin
= TimeStamp::Now();
864 NS_INTERFACE_TABLE_HEAD(PresShell
)
865 NS_INTERFACE_TABLE_BEGIN
866 // In most cases, PresShell should be treated as concrete class, but need to
867 // QI for weak reference. Therefore, the case needed by do_QueryReferent()
868 // should be tested first.
869 NS_INTERFACE_TABLE_ENTRY(PresShell
, PresShell
)
870 NS_INTERFACE_TABLE_ENTRY(PresShell
, nsIDocumentObserver
)
871 NS_INTERFACE_TABLE_ENTRY(PresShell
, nsISelectionController
)
872 NS_INTERFACE_TABLE_ENTRY(PresShell
, nsISelectionDisplay
)
873 NS_INTERFACE_TABLE_ENTRY(PresShell
, nsIObserver
)
874 NS_INTERFACE_TABLE_ENTRY(PresShell
, nsISupportsWeakReference
)
875 NS_INTERFACE_TABLE_ENTRY(PresShell
, nsIMutationObserver
)
876 NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(PresShell
, nsISupports
, nsIObserver
)
877 NS_INTERFACE_TABLE_END
878 NS_INTERFACE_TABLE_TO_MAP_SEGUE
881 NS_IMPL_ADDREF(PresShell
)
882 NS_IMPL_RELEASE(PresShell
)
884 PresShell::~PresShell() {
885 MOZ_RELEASE_ASSERT(!mForbiddenToFlush
,
886 "Flag should only be set temporarily, while doing things "
887 "that shouldn't cause destruction");
888 MOZ_LOG(gLog
, LogLevel::Debug
, ("PresShell::~PresShell this=%p", this));
890 if (!mHaveShutDown
) {
891 MOZ_ASSERT_UNREACHABLE("Someone did not call PresShell::Destroy()");
895 NS_ASSERTION(mCurrentEventContentStack
.Count() == 0,
896 "Huh, event content left on the stack in pres shell dtor!");
897 NS_ASSERTION(mFirstCallbackEventRequest
== nullptr &&
898 mLastCallbackEventRequest
== nullptr,
899 "post-reflow queues not empty. This means we're leaking");
901 MOZ_ASSERT(mAllocatedPointers
.IsEmpty(),
902 "Some pres arena objects were not freed");
904 mFrameManager
= nullptr;
905 mFrameConstructor
= nullptr;
907 mCurrentEventContent
= nullptr;
911 * Initialize the presentation shell. Create view manager and style
913 * Note this can't be merged into our constructor because caret initialization
914 * calls AddRef() on us.
916 void PresShell::Init(nsPresContext
* aPresContext
, nsViewManager
* aViewManager
) {
917 MOZ_ASSERT(mDocument
);
918 MOZ_ASSERT(aPresContext
);
919 MOZ_ASSERT(aViewManager
);
920 MOZ_ASSERT(!mViewManager
, "already initialized");
922 mViewManager
= aViewManager
;
924 // mDocument is now set. It might have a display document whose "need layout/
925 // style" flush flags are not set, but ours will be set. To keep these
926 // consistent, call the flag setting functions to propagate those flags up
927 // to the display document.
928 SetNeedLayoutFlush();
931 // Create our frame constructor.
932 mFrameConstructor
= MakeUnique
<nsCSSFrameConstructor
>(mDocument
, this);
934 mFrameManager
= mFrameConstructor
.get();
936 // The document viewer owns both view manager and pres shell.
937 mViewManager
->SetPresShell(this);
939 // Bind the context to the presentation shell.
940 // FYI: We cannot initialize mPresContext in the constructor because we
941 // cannot call AttachPresShell() in it and once we initialize
942 // mPresContext, other objects may refer refresh driver or restyle
943 // manager via mPresContext and that causes hitting MOZ_ASSERT in some
944 // places. Therefore, we should initialize mPresContext here with
945 // const_cast hack since we want to guarantee that mPresContext lives
946 // as long as the PresShell.
947 const_cast<RefPtr
<nsPresContext
>&>(mPresContext
) = aPresContext
;
948 mPresContext
->AttachPresShell(this);
950 mPresContext
->DeviceContext()->InitFontCache();
952 // FIXME(emilio, bug 1544185): Some Android code somehow depends on the shell
953 // being eagerly registered as a style flush observer. This shouldn't be
957 // Add the preference style sheet.
958 UpdatePreferenceStyles();
960 bool accessibleCaretEnabled
=
961 AccessibleCaretEnabled(mDocument
->GetDocShell());
962 if (accessibleCaretEnabled
) {
963 // Need to happen before nsFrameSelection has been set up.
964 mAccessibleCaretEventHub
= new AccessibleCaretEventHub(this);
967 mSelection
= new nsFrameSelection(this, nullptr, accessibleCaretEnabled
);
969 // Important: this has to happen after the selection has been set up
972 mCaret
= new nsCaret();
974 mOriginalCaret
= mCaret
;
976 // SetCaretEnabled(true); // make it show in browser windows
978 // set up selection to be displayed in document
979 // Don't enable selection for print media
980 nsPresContext::nsPresContextType type
= mPresContext
->Type();
981 if (type
!= nsPresContext::eContext_PrintPreview
&&
982 type
!= nsPresContext::eContext_Print
) {
983 SetDisplaySelection(nsISelectionController::SELECTION_DISABLED
);
986 if (gMaxRCProcessingTime
== -1) {
987 gMaxRCProcessingTime
=
988 Preferences::GetInt("layout.reflow.timeslice", NS_MAX_REFLOW_TIME
);
991 if (nsStyleSheetService
* ss
= nsStyleSheetService::GetInstance()) {
992 ss
->RegisterPresShell(this);
996 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
998 os
->AddObserver(this, "memory-pressure", false);
999 os
->AddObserver(this, NS_WIDGET_WAKE_OBSERVER_TOPIC
, false);
1000 if (XRE_IsParentProcess() && !sProcessInteractable
) {
1001 os
->AddObserver(this, "sessionstore-one-or-no-tab-restored", false);
1003 os
->AddObserver(this, "font-info-updated", false);
1004 os
->AddObserver(this, "look-and-feel-changed", false);
1008 #ifdef MOZ_REFLOW_PERF
1009 if (mReflowCountMgr
) {
1010 bool paintFrameCounts
=
1011 Preferences::GetBool("layout.reflow.showframecounts");
1013 bool dumpFrameCounts
=
1014 Preferences::GetBool("layout.reflow.dumpframecounts");
1016 bool dumpFrameByFrameCounts
=
1017 Preferences::GetBool("layout.reflow.dumpframebyframecounts");
1019 mReflowCountMgr
->SetDumpFrameCounts(dumpFrameCounts
);
1020 mReflowCountMgr
->SetDumpFrameByFrameCounts(dumpFrameByFrameCounts
);
1021 mReflowCountMgr
->SetPaintFrameCounts(paintFrameCounts
);
1025 if (mDocument
->HasAnimationController()) {
1026 SMILAnimationController
* animCtrl
= mDocument
->GetAnimationController();
1027 animCtrl
->NotifyRefreshDriverCreated(GetPresContext()->RefreshDriver());
1030 for (DocumentTimeline
* timeline
: mDocument
->Timelines()) {
1031 timeline
->NotifyRefreshDriverCreated(GetPresContext()->RefreshDriver());
1034 // Get our activeness from the docShell.
1035 ActivenessMaybeChanged();
1037 // Setup our font inflation preferences.
1038 mFontSizeInflationEmPerLine
= StaticPrefs::font_size_inflation_emPerLine();
1039 mFontSizeInflationMinTwips
= StaticPrefs::font_size_inflation_minTwips();
1040 mFontSizeInflationLineThreshold
=
1041 StaticPrefs::font_size_inflation_lineThreshold();
1042 mFontSizeInflationForceEnabled
=
1043 StaticPrefs::font_size_inflation_forceEnabled();
1044 mFontSizeInflationDisabledInMasterProcess
=
1045 StaticPrefs::font_size_inflation_disabledInMasterProcess();
1046 // We'll compute the font size inflation state in Initialize(), when we know
1047 // the document type.
1049 mTouchManager
.Init(this, mDocument
);
1051 if (mPresContext
->IsRootContentDocumentCrossProcess()) {
1052 mZoomConstraintsClient
= new ZoomConstraintsClient();
1053 mZoomConstraintsClient
->Init(this, mDocument
);
1055 // We call this to create mMobileViewportManager, if it is needed.
1056 MaybeRecreateMobileViewportManager(false);
1059 if (nsCOMPtr
<nsIDocShell
> docShell
= mPresContext
->GetDocShell()) {
1060 BrowsingContext
* bc
= docShell
->GetBrowsingContext();
1061 bool embedderFrameIsHidden
= true;
1062 if (Element
* embedderElement
= bc
->GetEmbedderElement()) {
1063 if (auto embedderFrame
= embedderElement
->GetPrimaryFrame()) {
1064 embedderFrameIsHidden
= !embedderFrame
->StyleVisibility()->IsVisible();
1068 if (BrowsingContext
* parent
= bc
->GetParent()) {
1069 if (nsCOMPtr
<nsIDocShell
> parentDocShell
= parent
->GetDocShell()) {
1070 if (PresShell
* parentPresShell
= parentDocShell
->GetPresShell()) {
1071 mUnderHiddenEmbedderElement
=
1072 parentPresShell
->IsUnderHiddenEmbedderElement() ||
1073 embedderFrameIsHidden
;
1080 enum TextPerfLogType
{ eLog_reflow
, eLog_loaddone
, eLog_totals
};
1082 static void LogTextPerfStats(gfxTextPerfMetrics
* aTextPerf
,
1083 PresShell
* aPresShell
,
1084 const gfxTextPerfMetrics::TextCounts
& aCounts
,
1085 float aTime
, TextPerfLogType aLogType
,
1087 LogModule
* tpLog
= gfxPlatform::GetLog(eGfxLog_textperf
);
1089 // ignore XUL contexts unless at debug level
1090 mozilla::LogLevel logLevel
= LogLevel::Warning
;
1091 if (aCounts
.numContentTextRuns
== 0) {
1092 logLevel
= LogLevel::Debug
;
1095 if (!MOZ_LOG_TEST(tpLog
, logLevel
)) {
1103 SprintfLiteral(prefix
, "(textperf-reflow) %p time-ms: %7.0f", aPresShell
,
1107 SprintfLiteral(prefix
, "(textperf-loaddone) %p time-ms: %7.0f",
1111 MOZ_ASSERT(aLogType
== eLog_totals
, "unknown textperf log type");
1112 SprintfLiteral(prefix
, "(textperf-totals) %p", aPresShell
);
1115 double hitRatio
= 0.0;
1116 uint32_t lookups
= aCounts
.wordCacheHit
+ aCounts
.wordCacheMiss
;
1118 hitRatio
= double(aCounts
.wordCacheHit
) / double(lookups
);
1121 if (aLogType
== eLog_loaddone
) {
1124 ("%s reflow: %d chars: %d "
1126 "content-textruns: %d chrome-textruns: %d "
1127 "max-textrun-len: %d "
1128 "word-cache-lookups: %d word-cache-hit-ratio: %4.3f "
1129 "word-cache-space: %d word-cache-long: %d "
1130 "pref-fallbacks: %d system-fallbacks: %d "
1131 "textruns-const: %d textruns-destr: %d "
1132 "generic-lookups: %d "
1133 "cumulative-textruns-destr: %d\n",
1134 prefix
, aTextPerf
->reflowCount
, aCounts
.numChars
, (aURL
? aURL
: ""),
1135 aCounts
.numContentTextRuns
, aCounts
.numChromeTextRuns
,
1136 aCounts
.maxTextRunLen
, lookups
, hitRatio
, aCounts
.wordCacheSpaceRules
,
1137 aCounts
.wordCacheLong
, aCounts
.fallbackPrefs
, aCounts
.fallbackSystem
,
1138 aCounts
.textrunConst
, aCounts
.textrunDestr
, aCounts
.genericLookups
,
1139 aTextPerf
->cumulative
.textrunDestr
));
1143 ("%s reflow: %d chars: %d "
1144 "content-textruns: %d chrome-textruns: %d "
1145 "max-textrun-len: %d "
1146 "word-cache-lookups: %d word-cache-hit-ratio: %4.3f "
1147 "word-cache-space: %d word-cache-long: %d "
1148 "pref-fallbacks: %d system-fallbacks: %d "
1149 "textruns-const: %d textruns-destr: %d "
1150 "generic-lookups: %d "
1151 "cumulative-textruns-destr: %d\n",
1152 prefix
, aTextPerf
->reflowCount
, aCounts
.numChars
,
1153 aCounts
.numContentTextRuns
, aCounts
.numChromeTextRuns
,
1154 aCounts
.maxTextRunLen
, lookups
, hitRatio
, aCounts
.wordCacheSpaceRules
,
1155 aCounts
.wordCacheLong
, aCounts
.fallbackPrefs
, aCounts
.fallbackSystem
,
1156 aCounts
.textrunConst
, aCounts
.textrunDestr
, aCounts
.genericLookups
,
1157 aTextPerf
->cumulative
.textrunDestr
));
1161 bool PresShell::InRDMPane() {
1162 if (Document
* doc
= GetDocument()) {
1163 if (BrowsingContext
* bc
= doc
->GetBrowsingContext()) {
1164 return bc
->InRDMPane();
1170 void PresShell::Destroy() {
1171 // Do not add code before this line please!
1172 if (mHaveShutDown
) {
1176 NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
1177 "destroy called on presshell while scripts not blocked");
1179 AUTO_PROFILER_LABEL("PresShell::Destroy", LAYOUT
);
1181 // Try to determine if the page is the user had a meaningful opportunity to
1182 // zoom this page. This is not 100% accurate but should be "good enough" for
1183 // telemetry purposes.
1184 auto isUserZoomablePage
= [&]() -> bool {
1185 if (mIsFirstPaint
) {
1186 // Page was never painted, so it wasn't zoomable by the user. We get a
1187 // handful of these "transient" presShells.
1190 if (!mPresContext
->IsRootContentDocumentCrossProcess()) {
1191 // Not a root content document, so APZ doesn't support zooming it.
1195 // Responsive design mode is a special case that we want to ignore here.
1198 if (mDocument
&& mDocument
->IsInitialDocument()) {
1199 // Ignore initial about:blank page loads
1202 if (XRE_IsContentProcess() &&
1203 IsExtensionRemoteType(ContentChild::GetSingleton()->GetRemoteType())) {
1204 // Also omit presShells from the extension process because they sometimes
1205 // can't be zoomed by the user.
1208 // Otherwise assume the page is user-zoomable.
1211 if (isUserZoomablePage()) {
1212 Telemetry::Accumulate(Telemetry::APZ_ZOOM_ACTIVITY
,
1213 IsResolutionUpdatedByApz());
1216 // dump out cumulative text perf metrics
1217 gfxTextPerfMetrics
* tp
;
1218 if (mPresContext
&& (tp
= mPresContext
->GetTextPerfMetrics())) {
1220 if (tp
->cumulative
.numChars
> 0) {
1221 LogTextPerfStats(tp
, this, tp
->cumulative
, 0.0, eLog_totals
, nullptr);
1225 if (gfxUserFontSet
* fs
= mPresContext
->GetUserFontSet()) {
1228 fs
->GetLoadStatistics(fontCount
, fontSize
);
1229 Telemetry::Accumulate(Telemetry::WEBFONT_PER_PAGE
, fontCount
);
1230 Telemetry::Accumulate(Telemetry::WEBFONT_SIZE_PER_PAGE
,
1231 uint32_t(fontSize
/ 1024));
1233 Telemetry::Accumulate(Telemetry::WEBFONT_PER_PAGE
, 0);
1234 Telemetry::Accumulate(Telemetry::WEBFONT_SIZE_PER_PAGE
, 0);
1236 const auto* stats
= mPresContext
->GetFontMatchingStats();
1238 Document
* doc
= GetDocument();
1239 if (doc
&& doc
->IsContentDocument()) {
1240 nsIURI
* uri
= doc
->GetDocumentURI();
1242 if (uri
&& !uri
->SchemeIs("about") && !uri
->SchemeIs("chrome") &&
1243 !uri
->SchemeIs("resource") &&
1244 !(uri
->SchemeIs("moz-extension") &&
1245 (NS_SUCCEEDED(uri
->GetFilePath(path
)) &&
1246 StringEndsWith(path
, "/_generated_background_page.html"_ns
)))) {
1247 Telemetry::Accumulate(Telemetry::BASE_FONT_FAMILIES_PER_PAGE
,
1249 Telemetry::Accumulate(Telemetry::LANGPACK_FONT_FAMILIES_PER_PAGE
,
1250 stats
->mLangPackFonts
);
1251 Telemetry::Accumulate(Telemetry::USER_FONT_FAMILIES_PER_PAGE
,
1253 Telemetry::Accumulate(Telemetry::WEB_FONT_FAMILIES_PER_PAGE
,
1255 Telemetry::Accumulate(
1256 Telemetry::FALLBACK_TO_PREFS_FONT
,
1257 bool(stats
->mFallbacks
& FallbackTypes::FallbackToPrefsFont
));
1258 Telemetry::Accumulate(
1259 Telemetry::FALLBACK_TO_BASE_FONT
,
1260 bool(stats
->mFallbacks
& FallbackTypes::FallbackToBaseFont
));
1261 Telemetry::Accumulate(
1262 Telemetry::FALLBACK_TO_LANGPACK_FONT
,
1263 bool(stats
->mFallbacks
& FallbackTypes::FallbackToLangPackFont
));
1264 Telemetry::Accumulate(
1265 Telemetry::FALLBACK_TO_USER_FONT
,
1266 bool(stats
->mFallbacks
& FallbackTypes::FallbackToUserFont
));
1267 Telemetry::Accumulate(
1268 Telemetry::MISSING_FONT
,
1269 bool(stats
->mFallbacks
& FallbackTypes::MissingFont
));
1270 Telemetry::Accumulate(
1271 Telemetry::MISSING_FONT_LANGPACK
,
1272 bool(stats
->mFallbacks
& FallbackTypes::MissingFontLangPack
));
1273 Telemetry::Accumulate(
1274 Telemetry::MISSING_FONT_USER
,
1275 bool(stats
->mFallbacks
& FallbackTypes::MissingFontUser
));
1279 mPresContext
->CancelManagedPostRefreshObservers();
1282 #ifdef MOZ_REFLOW_PERF
1284 mReflowCountMgr
= nullptr;
1287 if (mZoomConstraintsClient
) {
1288 mZoomConstraintsClient
->Destroy();
1289 mZoomConstraintsClient
= nullptr;
1291 if (mMobileViewportManager
) {
1292 mMobileViewportManager
->Destroy();
1293 mMobileViewportManager
= nullptr;
1294 mMVMContext
= nullptr;
1297 #ifdef ACCESSIBILITY
1298 if (mDocAccessible
) {
1300 if (a11y::logging::IsEnabled(a11y::logging::eDocDestroy
))
1301 a11y::logging::DocDestroy("presshell destroyed", mDocument
);
1304 mDocAccessible
->Shutdown();
1305 mDocAccessible
= nullptr;
1307 #endif // ACCESSIBILITY
1309 MaybeReleaseCapturingContent();
1311 EventHandler::OnPresShellDestroy(mDocument
);
1313 if (mContentToScrollTo
) {
1314 mContentToScrollTo
->RemoveProperty(nsGkAtoms::scrolling
);
1315 mContentToScrollTo
= nullptr;
1319 // We need to notify the destroying the nsPresContext to ESM for
1320 // suppressing to use from ESM.
1321 mPresContext
->EventStateManager()->NotifyDestroyPresContext(mPresContext
);
1324 if (nsStyleSheetService
* ss
= nsStyleSheetService::GetInstance()) {
1325 ss
->UnregisterPresShell(this);
1329 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
1331 os
->RemoveObserver(this, "memory-pressure");
1332 os
->RemoveObserver(this, NS_WIDGET_WAKE_OBSERVER_TOPIC
);
1333 if (XRE_IsParentProcess()) {
1334 os
->RemoveObserver(this, "sessionstore-one-or-no-tab-restored");
1336 os
->RemoveObserver(this, "font-info-updated");
1337 os
->RemoveObserver(this, "look-and-feel-changed");
1341 // If our paint suppression timer is still active, kill it.
1342 CancelPaintSuppressionTimer();
1344 // Same for our reflow continuation timer
1345 if (mReflowContinueTimer
) {
1346 mReflowContinueTimer
->Cancel();
1347 mReflowContinueTimer
= nullptr;
1350 if (mDelayedPaintTimer
) {
1351 mDelayedPaintTimer
->Cancel();
1352 mDelayedPaintTimer
= nullptr;
1355 mSynthMouseMoveEvent
.Revoke();
1357 mUpdateApproximateFrameVisibilityEvent
.Revoke();
1359 ClearApproximatelyVisibleFramesList(Some(OnNonvisible::DiscardImages
));
1362 mCaret
->Terminate();
1366 mFocusedFrameSelection
= nullptr;
1369 RefPtr
<nsFrameSelection
> frameSelection
= mSelection
;
1370 frameSelection
->DisconnectFromPresShell();
1373 // release our pref style sheet, if we have one still
1375 // TODO(emilio): Should we move the preference sheet tracking to the Document?
1376 RemovePreferenceStyles();
1378 mIsDestroying
= true;
1380 // We can't release all the event content in
1381 // mCurrentEventContentStack here since there might be code on the
1382 // stack that will release the event content too. Double release
1385 // The frames will be torn down, so remove them from the current
1386 // event frame stack (since they'd be dangling references if we'd
1387 // leave them in) and null out the mCurrentEventFrame pointer as
1390 mCurrentEventFrame
= nullptr;
1392 int32_t i
, count
= mCurrentEventFrameStack
.Length();
1393 for (i
= 0; i
< count
; i
++) {
1394 mCurrentEventFrameStack
[i
] = nullptr;
1397 mFramesToDirty
.Clear();
1398 mPendingScrollAnchorSelection
.Clear();
1399 mPendingScrollAnchorAdjustment
.Clear();
1402 // Clear the view manager's weak pointer back to |this| in case it
1404 mViewManager
->SetPresShell(nullptr);
1405 mViewManager
= nullptr;
1408 nsRefreshDriver
* rd
= GetPresContext()->RefreshDriver();
1410 // This shell must be removed from the document before the frame
1411 // hierarchy is torn down to avoid finding deleted frames through
1412 // this presshell while the frames are being torn down
1414 NS_ASSERTION(mDocument
->GetPresShell() == this, "Wrong shell?");
1415 mDocument
->ClearServoRestyleRoot();
1416 mDocument
->DeletePresShell();
1418 if (mDocument
->HasAnimationController()) {
1419 mDocument
->GetAnimationController()->NotifyRefreshDriverDestroying(rd
);
1421 for (DocumentTimeline
* timeline
: mDocument
->Timelines()) {
1422 timeline
->NotifyRefreshDriverDestroying(rd
);
1427 rd
->CancelPendingAnimationEvents(mPresContext
->AnimationEventDispatcher());
1430 // Revoke any pending events. We need to do this and cancel pending reflows
1431 // before we destroy the frame manager, since apparently frame destruction
1432 // sometimes spins the event queue when plug-ins are involved(!).
1433 // XXXmats is this still needed now that plugins are gone?
1434 StopObservingRefreshDriver();
1436 if (rd
->GetPresContext() == GetPresContext()) {
1437 rd
->RevokeViewManagerFlush();
1438 rd
->ClearHasScheduleFlush();
1441 CancelAllPendingReflows();
1442 CancelPostedReflowCallbacks();
1444 // Destroy the frame manager. This will destroy the frame hierarchy
1445 mFrameConstructor
->WillDestroyFrameTree();
1447 NS_WARNING_ASSERTION(!mAutoWeakFrames
&& mWeakFrames
.IsEmpty(),
1448 "Weak frames alive after destroying FrameManager");
1449 while (mAutoWeakFrames
) {
1450 mAutoWeakFrames
->Clear(this);
1452 const nsTArray
<WeakFrame
*> weakFrames
= ToArray(mWeakFrames
);
1453 for (WeakFrame
* weakFrame
: weakFrames
) {
1454 weakFrame
->Clear(this);
1457 // Terminate AccessibleCaretEventHub after tearing down the frame tree so that
1458 // we don't need to remove caret element's frame in
1459 // AccessibleCaret::RemoveCaretElement().
1460 if (mAccessibleCaretEventHub
) {
1461 mAccessibleCaretEventHub
->Terminate();
1462 mAccessibleCaretEventHub
= nullptr;
1466 // We hold a reference to the pres context, and it holds a weak link back
1467 // to us. To avoid the pres context having a dangling reference, set its
1468 // pres shell to nullptr
1469 mPresContext
->DetachPresShell();
1472 mHaveShutDown
= true;
1474 mTouchManager
.Destroy();
1477 void PresShell::StopObservingRefreshDriver() {
1478 nsRefreshDriver
* rd
= mPresContext
->RefreshDriver();
1479 if (mResizeEventPending
) {
1480 rd
->RemoveResizeEventFlushObserver(this);
1482 if (mObservingLayoutFlushes
) {
1483 rd
->RemoveLayoutFlushObserver(this);
1485 if (mObservingStyleFlushes
) {
1486 rd
->RemoveStyleFlushObserver(this);
1490 void PresShell::StartObservingRefreshDriver() {
1491 nsRefreshDriver
* rd
= mPresContext
->RefreshDriver();
1492 if (mResizeEventPending
) {
1493 rd
->AddResizeEventFlushObserver(this);
1495 if (mObservingLayoutFlushes
) {
1496 rd
->AddLayoutFlushObserver(this);
1498 if (mObservingStyleFlushes
) {
1499 rd
->AddStyleFlushObserver(this);
1503 nsRefreshDriver
* PresShell::GetRefreshDriver() const {
1504 return mPresContext
? mPresContext
->RefreshDriver() : nullptr;
1507 void PresShell::SetAuthorStyleDisabled(bool aStyleDisabled
) {
1508 if (aStyleDisabled
!= StyleSet()->GetAuthorStyleDisabled()) {
1509 StyleSet()->SetAuthorStyleDisabled(aStyleDisabled
);
1510 mDocument
->ApplicableStylesChanged();
1512 nsCOMPtr
<nsIObserverService
> observerService
=
1513 mozilla::services::GetObserverService();
1514 if (observerService
) {
1515 observerService
->NotifyObservers(
1516 ToSupports(mDocument
), "author-style-disabled-changed", nullptr);
1521 bool PresShell::GetAuthorStyleDisabled() const {
1522 return StyleSet()->GetAuthorStyleDisabled();
1525 void PresShell::UpdatePreferenceStyles() {
1530 // If the document doesn't have a window there's no need to notify
1531 // its presshell about changes to preferences since the document is
1532 // in a state where it doesn't matter any more (see
1533 // nsDocumentViewer::Close()).
1534 if (!mDocument
->GetWindow()) {
1538 // Documents in chrome shells do not have any preference style rules applied.
1539 if (nsContentUtils::IsInChromeDocshell(mDocument
)) {
1543 PreferenceSheet::EnsureInitialized();
1544 auto* cache
= GlobalStyleSheetCache::Singleton();
1546 RefPtr
<StyleSheet
> newPrefSheet
=
1547 PreferenceSheet::ShouldUseChromePrefs(*mDocument
)
1548 ? cache
->ChromePreferenceSheet()
1549 : cache
->ContentPreferenceSheet();
1551 if (mPrefStyleSheet
== newPrefSheet
) {
1555 RemovePreferenceStyles();
1557 // NOTE(emilio): This sheet is added as an agent sheet, because we don't want
1558 // it to be modifiable from devtools and similar, see bugs 1239336 and
1559 // 1436782. I think it conceptually should be a user sheet, and could be
1560 // without too much trouble I'd think.
1561 StyleSet()->AppendStyleSheet(*newPrefSheet
);
1562 mPrefStyleSheet
= newPrefSheet
;
1565 void PresShell::RemovePreferenceStyles() {
1566 if (mPrefStyleSheet
) {
1567 StyleSet()->RemoveStyleSheet(*mPrefStyleSheet
);
1568 mPrefStyleSheet
= nullptr;
1572 void PresShell::AddUserSheet(StyleSheet
* aSheet
) {
1573 // Make sure this does what nsDocumentViewer::CreateStyleSet does wrt
1574 // ordering. We want this new sheet to come after all the existing stylesheet
1575 // service sheets (which are at the start), but before other user sheets; see
1576 // nsIStyleSheetService.idl for the ordering.
1578 nsStyleSheetService
* sheetService
= nsStyleSheetService::GetInstance();
1579 nsTArray
<RefPtr
<StyleSheet
>>& userSheets
= *sheetService
->UserStyleSheets();
1581 // Search for the place to insert the new user sheet. Since all of the
1582 // stylesheet service provided user sheets should be at the start of the style
1583 // set's list, and aSheet should be at the end of userSheets. Given that, we
1584 // can find the right place to insert the new sheet based on the length of
1587 MOZ_ASSERT(userSheets
.LastElement() == aSheet
);
1589 size_t index
= userSheets
.Length() - 1;
1591 // Assert that all of userSheets (except for the last, new element) matches up
1592 // with what's in the style set.
1593 for (size_t i
= 0; i
< index
; ++i
) {
1594 MOZ_ASSERT(StyleSet()->SheetAt(StyleOrigin::User
, i
) == userSheets
[i
]);
1597 if (index
== static_cast<size_t>(StyleSet()->SheetCount(StyleOrigin::User
))) {
1598 StyleSet()->AppendStyleSheet(*aSheet
);
1600 StyleSheet
* ref
= StyleSet()->SheetAt(StyleOrigin::User
, index
);
1601 StyleSet()->InsertStyleSheetBefore(*aSheet
, *ref
);
1604 mDocument
->ApplicableStylesChanged();
1607 void PresShell::AddAgentSheet(StyleSheet
* aSheet
) {
1608 // Make sure this does what nsDocumentViewer::CreateStyleSet does
1610 StyleSet()->AppendStyleSheet(*aSheet
);
1611 mDocument
->ApplicableStylesChanged();
1614 void PresShell::AddAuthorSheet(StyleSheet
* aSheet
) {
1615 // Document specific "additional" Author sheets should be stronger than the
1616 // ones added with the StyleSheetService.
1617 StyleSheet
* firstAuthorSheet
= mDocument
->GetFirstAdditionalAuthorSheet();
1618 if (firstAuthorSheet
) {
1619 StyleSet()->InsertStyleSheetBefore(*aSheet
, *firstAuthorSheet
);
1621 StyleSet()->AppendStyleSheet(*aSheet
);
1624 mDocument
->ApplicableStylesChanged();
1627 void PresShell::SelectionWillTakeFocus() {
1629 FrameSelectionWillTakeFocus(*mSelection
);
1633 void PresShell::SelectionWillLoseFocus() {
1634 // Do nothing, the main selection is the default focused selection.
1637 // Selection repainting code relies on selection offsets being properly
1638 // adjusted (see bug 1626291), so we need to wait until the DOM is finished
1640 static void RepaintNormalSelectionWhenSafe(nsFrameSelection
& aFrameSelection
) {
1641 if (nsContentUtils::IsSafeToRunScript()) {
1642 aFrameSelection
.RepaintSelection(SelectionType::eNormal
);
1646 // Note that importantly we don't defer changing the DisplaySelection. That'd
1647 // be potentially racy with other code that may change it.
1648 nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
1649 "RepaintNormalSelectionWhenSafe",
1650 [sel
= RefPtr
<nsFrameSelection
>(&aFrameSelection
)] {
1651 sel
->RepaintSelection(SelectionType::eNormal
);
1655 void PresShell::FrameSelectionWillLoseFocus(nsFrameSelection
& aFrameSelection
) {
1656 if (mFocusedFrameSelection
!= &aFrameSelection
) {
1660 // Do nothing, the main selection is the default focused selection.
1661 if (&aFrameSelection
== mSelection
) {
1665 RefPtr
<nsFrameSelection
> old
= std::move(mFocusedFrameSelection
);
1666 MOZ_ASSERT(!mFocusedFrameSelection
);
1668 if (old
->GetDisplaySelection() != nsISelectionController::SELECTION_HIDDEN
) {
1669 old
->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN
);
1670 RepaintNormalSelectionWhenSafe(*old
);
1674 FrameSelectionWillTakeFocus(*mSelection
);
1678 void PresShell::FrameSelectionWillTakeFocus(nsFrameSelection
& aFrameSelection
) {
1679 if (mFocusedFrameSelection
== &aFrameSelection
) {
1681 // FIXME: Mac needs to update the global selection cache, even if the
1682 // document's focused selection doesn't change, and this is currently done
1683 // from RepaintSelection. Maybe we should move part of the global selection
1684 // handling here, or something of that sort, unclear.
1685 RepaintNormalSelectionWhenSafe(aFrameSelection
);
1690 RefPtr
<nsFrameSelection
> old
= std::move(mFocusedFrameSelection
);
1691 mFocusedFrameSelection
= &aFrameSelection
;
1694 old
->GetDisplaySelection() != nsISelectionController::SELECTION_HIDDEN
) {
1695 old
->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN
);
1696 RepaintNormalSelectionWhenSafe(*old
);
1699 if (aFrameSelection
.GetDisplaySelection() !=
1700 nsISelectionController::SELECTION_ON
) {
1701 aFrameSelection
.SetDisplaySelection(nsISelectionController::SELECTION_ON
);
1702 RepaintNormalSelectionWhenSafe(aFrameSelection
);
1707 PresShell::SetDisplaySelection(int16_t aToggle
) {
1708 RefPtr
<nsFrameSelection
> frameSelection
= mSelection
;
1709 frameSelection
->SetDisplaySelection(aToggle
);
1714 PresShell::GetDisplaySelection(int16_t* aToggle
) {
1715 RefPtr
<nsFrameSelection
> frameSelection
= mSelection
;
1716 *aToggle
= frameSelection
->GetDisplaySelection();
1721 PresShell::GetSelectionFromScript(RawSelectionType aRawSelectionType
,
1722 Selection
** aSelection
) {
1723 if (!aSelection
|| !mSelection
) return NS_ERROR_NULL_POINTER
;
1725 RefPtr
<nsFrameSelection
> frameSelection
= mSelection
;
1726 RefPtr
<Selection
> selection
=
1727 frameSelection
->GetSelection(ToSelectionType(aRawSelectionType
));
1730 return NS_ERROR_INVALID_ARG
;
1733 selection
.forget(aSelection
);
1737 Selection
* PresShell::GetSelection(RawSelectionType aRawSelectionType
) {
1742 RefPtr
<nsFrameSelection
> frameSelection
= mSelection
;
1743 return frameSelection
->GetSelection(ToSelectionType(aRawSelectionType
));
1746 Selection
* PresShell::GetCurrentSelection(SelectionType aSelectionType
) {
1751 RefPtr
<nsFrameSelection
> frameSelection
= mSelection
;
1752 return frameSelection
->GetSelection(aSelectionType
);
1755 nsFrameSelection
* PresShell::GetLastFocusedFrameSelection() {
1756 return mFocusedFrameSelection
? mFocusedFrameSelection
: mSelection
;
1760 PresShell::ScrollSelectionIntoView(RawSelectionType aRawSelectionType
,
1761 SelectionRegion aRegion
, int16_t aFlags
) {
1762 if (!mSelection
) return NS_ERROR_NULL_POINTER
;
1764 RefPtr
<nsFrameSelection
> frameSelection
= mSelection
;
1765 return frameSelection
->ScrollSelectionIntoView(
1766 ToSelectionType(aRawSelectionType
), aRegion
, aFlags
);
1770 PresShell::RepaintSelection(RawSelectionType aRawSelectionType
) {
1772 return NS_ERROR_NULL_POINTER
;
1775 if (MOZ_UNLIKELY(mIsDestroying
)) {
1779 RefPtr
<nsFrameSelection
> frameSelection
= mSelection
;
1780 return frameSelection
->RepaintSelection(ToSelectionType(aRawSelectionType
));
1783 // Make shell be a document observer
1784 void PresShell::BeginObservingDocument() {
1785 if (mDocument
&& !mIsDestroying
) {
1786 mIsObservingDocument
= true;
1787 if (mIsDocumentGone
) {
1789 "Adding a presshell that was disconnected from the document "
1790 "as a document observer? Sounds wrong...");
1791 mIsDocumentGone
= false;
1796 // Make shell stop being a document observer
1797 void PresShell::EndObservingDocument() {
1798 // XXXbz do we need to tell the frame constructor that the document
1799 // is gone, perhaps? Except for printing it's NOT gone, sometimes.
1800 mIsDocumentGone
= true;
1801 mIsObservingDocument
= false;
1805 char* nsPresShell_ReflowStackPointerTop
;
1808 void PresShell::InitPaintSuppressionTimer() {
1809 // Default to PAINTLOCK_EVENT_DELAY if we can't get the pref value.
1810 Document
* doc
= mDocument
->GetDisplayDocument()
1811 ? mDocument
->GetDisplayDocument()
1813 const bool inProcess
= !doc
->GetBrowsingContext() ||
1814 doc
->GetBrowsingContext()->Top()->IsInProcess();
1815 int32_t delay
= inProcess
1816 ? StaticPrefs::nglayout_initialpaint_delay()
1817 : StaticPrefs::nglayout_initialpaint_delay_in_oopif();
1818 mPaintSuppressionTimer
->InitWithNamedFuncCallback(
1819 [](nsITimer
* aTimer
, void* aPresShell
) {
1820 RefPtr
<PresShell
> self
= static_cast<PresShell
*>(aPresShell
);
1821 self
->UnsuppressPainting();
1823 this, delay
, nsITimer::TYPE_ONE_SHOT
,
1824 "PresShell::sPaintSuppressionCallback");
1827 nsresult
PresShell::Initialize() {
1828 if (mIsDestroying
) {
1837 MOZ_LOG(gLog
, LogLevel::Debug
, ("PresShell::Initialize this=%p", this));
1839 NS_ASSERTION(!mDidInitialize
, "Why are we being called?");
1841 RefPtr
<PresShell
> kungFuDeathGrip(this);
1843 RecomputeFontSizeInflationEnabled();
1844 MOZ_DIAGNOSTIC_ASSERT(!mIsDestroying
);
1846 // Ensure the pres context doesn't think it has changed, since we haven't even
1847 // started layout. This avoids spurious restyles / reflows afterwards.
1849 // Note that this is very intentionally before setting mDidInitialize so it
1850 // doesn't notify the document, or run media query change events.
1851 mPresContext
->FlushPendingMediaFeatureValuesChanged();
1852 MOZ_DIAGNOSTIC_ASSERT(!mIsDestroying
);
1854 mDidInitialize
= true;
1857 if (VerifyReflowFlags::NoisyCommands
& gVerifyReflowFlags
) {
1859 nsIURI
* uri
= mDocument
->GetDocumentURI();
1861 printf("*** PresShell::Initialize (this=%p, url='%s')\n", (void*)this,
1862 uri
->GetSpecOrDefault().get());
1868 // Get the root frame from the frame manager
1869 // XXXbz it would be nice to move this somewhere else... like frame manager
1870 // Init(), say. But we need to make sure our views are all set up by the
1872 nsIFrame
* rootFrame
= mFrameConstructor
->GetRootFrame();
1873 NS_ASSERTION(!rootFrame
, "How did that happen, exactly?");
1876 nsAutoScriptBlocker scriptBlocker
;
1877 rootFrame
= mFrameConstructor
->ConstructRootFrame();
1878 mFrameConstructor
->SetRootFrame(rootFrame
);
1881 NS_ENSURE_STATE(!mHaveShutDown
);
1884 return NS_ERROR_OUT_OF_MEMORY
;
1887 if (Element
* root
= mDocument
->GetRootElement()) {
1889 nsAutoCauseReflowNotifier
reflowNotifier(this);
1890 // Have the style sheet processor construct frame for the root
1891 // content object down
1892 mFrameConstructor
->ContentInserted(
1893 root
, nsCSSFrameConstructor::InsertionKind::Sync
);
1895 // Something in mFrameConstructor->ContentInserted may have caused
1896 // Destroy() to get called, bug 337586. Or, nsAutoCauseReflowNotifier
1897 // (which sets up a script blocker) going out of scope may have killed us
1899 NS_ENSURE_STATE(!mHaveShutDown
);
1902 mDocument
->TriggerAutoFocus();
1904 NS_ASSERTION(rootFrame
, "How did that happen?");
1906 // Note: when the frame was created above it had the NS_FRAME_IS_DIRTY bit
1907 // set, but XBL processing could have caused a reflow which clears it.
1908 if (MOZ_LIKELY(rootFrame
->HasAnyStateBits(NS_FRAME_IS_DIRTY
))) {
1909 // Unset the DIRTY bits so that FrameNeedsReflow() will work right.
1910 rootFrame
->RemoveStateBits(NS_FRAME_IS_DIRTY
| NS_FRAME_HAS_DIRTY_CHILDREN
);
1911 NS_ASSERTION(!mDirtyRoots
.Contains(rootFrame
),
1912 "Why is the root in mDirtyRoots already?");
1913 FrameNeedsReflow(rootFrame
, IntrinsicDirty::Resize
, NS_FRAME_IS_DIRTY
);
1914 NS_ASSERTION(mDirtyRoots
.Contains(rootFrame
),
1915 "Should be in mDirtyRoots now");
1916 NS_ASSERTION(mObservingLayoutFlushes
, "Why no reflow scheduled?");
1919 // Restore our root scroll position now if we're getting here after EndLoad
1920 // got called, since this is our one chance to do it. Note that we need not
1921 // have reflowed for this to work; when the scrollframe is finally reflowed
1922 // it'll pick up the position we store in it here.
1923 if (!mDocumentLoading
) {
1924 RestoreRootScrollPosition();
1927 // For printing, we just immediately unsuppress.
1928 if (!mPresContext
->IsPaginated()) {
1929 // Kick off a one-shot timer based off our pref value. When this timer
1930 // fires, if painting is still locked down, then we will go ahead and
1931 // trigger a full invalidate and allow painting to proceed normally.
1932 mPaintingSuppressed
= true;
1933 // Don't suppress painting if the document isn't loading.
1934 Document::ReadyState readyState
= mDocument
->GetReadyStateEnum();
1935 if (readyState
!= Document::READYSTATE_COMPLETE
) {
1936 mPaintSuppressionTimer
= NS_NewTimer();
1938 if (!mPaintSuppressionTimer
) {
1939 mPaintingSuppressed
= false;
1941 // Initialize the timer.
1942 mPaintSuppressionTimer
->SetTarget(
1943 mDocument
->EventTargetFor(TaskCategory::Other
));
1944 InitPaintSuppressionTimer();
1948 // If we get here and painting is not suppressed, we still want to run the
1949 // unsuppression logic, so set mShouldUnsuppressPainting to true.
1950 if (!mPaintingSuppressed
) {
1951 mShouldUnsuppressPainting
= true;
1954 return NS_OK
; // XXX this needs to be real. MMP
1957 nsresult
PresShell::ResizeReflow(nscoord aWidth
, nscoord aHeight
,
1958 ResizeReflowOptions aOptions
) {
1959 if (mZoomConstraintsClient
) {
1960 // If we have a ZoomConstraintsClient and the available screen area
1961 // changed, then we might need to disable double-tap-to-zoom, so notify
1962 // the ZCC to update itself.
1963 mZoomConstraintsClient
->ScreenSizeChanged();
1965 if (UsesMobileViewportSizing()) {
1966 // If we are using mobile viewport sizing, request a reflow from the MVM.
1967 // It can recompute the final CSS viewport and trigger a call to
1968 // ResizeReflowIgnoreOverride if it changed. We don't force adjusting
1969 // of resolution, because that is only necessary when we are destroying
1971 MOZ_ASSERT(mMobileViewportManager
);
1972 mMobileViewportManager
->RequestReflow(false);
1976 return ResizeReflowIgnoreOverride(aWidth
, aHeight
, aOptions
);
1979 void PresShell::SimpleResizeReflow(nscoord aWidth
, nscoord aHeight
,
1980 ResizeReflowOptions aOptions
) {
1981 MOZ_ASSERT(aWidth
!= NS_UNCONSTRAINEDSIZE
);
1982 MOZ_ASSERT(aHeight
!= NS_UNCONSTRAINEDSIZE
);
1983 nsSize oldSize
= mPresContext
->GetVisibleArea().Size();
1984 mPresContext
->SetVisibleArea(nsRect(0, 0, aWidth
, aHeight
));
1985 nsIFrame
* rootFrame
= GetRootFrame();
1989 WritingMode wm
= rootFrame
->GetWritingMode();
1990 bool isBSizeChanging
=
1991 wm
.IsVertical() ? oldSize
.width
!= aWidth
: oldSize
.height
!= aHeight
;
1992 if (isBSizeChanging
) {
1993 nsLayoutUtils::MarkIntrinsicISizesDirtyIfDependentOnBSize(rootFrame
);
1995 FrameNeedsReflow(rootFrame
, IntrinsicDirty::Resize
,
1996 NS_FRAME_HAS_DIRTY_CHILDREN
);
1998 // For compat with the old code path which always reflowed as long as there
1999 // was a root frame.
2000 bool suppressReflow
= (aOptions
& ResizeReflowOptions::SuppressReflow
) ||
2001 mPresContext
->SuppressingResizeReflow();
2002 if (!suppressReflow
) {
2003 mDocument
->FlushPendingNotifications(FlushType::InterruptibleLayout
);
2007 void PresShell::AddResizeEventFlushObserverIfNeeded() {
2008 if (!mIsDestroying
&& !mResizeEventPending
&&
2009 MOZ_LIKELY(!mDocument
->GetBFCacheEntry())) {
2010 mResizeEventPending
= true;
2011 mPresContext
->RefreshDriver()->AddResizeEventFlushObserver(this);
2015 nsresult
PresShell::ResizeReflowIgnoreOverride(nscoord aWidth
, nscoord aHeight
,
2016 ResizeReflowOptions aOptions
) {
2017 MOZ_ASSERT(!mIsReflowing
, "Shouldn't be in reflow here!");
2019 // Historically we never fired resize events if there was no root frame by the
2020 // time this function got called.
2021 const bool initialized
= mDidInitialize
;
2022 RefPtr
<PresShell
> kungFuDeathGrip(this);
2024 auto postResizeEventIfNeeded
= [this, initialized
]() {
2026 AddResizeEventFlushObserverIfNeeded();
2030 if (!(aOptions
& ResizeReflowOptions::BSizeLimit
)) {
2031 nsSize oldSize
= mPresContext
->GetVisibleArea().Size();
2032 if (oldSize
== nsSize(aWidth
, aHeight
)) {
2036 SimpleResizeReflow(aWidth
, aHeight
, aOptions
);
2037 postResizeEventIfNeeded();
2041 MOZ_ASSERT(!mPresContext
->SuppressingResizeReflow() &&
2042 !(aOptions
& ResizeReflowOptions::SuppressReflow
),
2043 "Can't suppress resize reflow and shrink-wrap at the same time");
2045 // Make sure that style is flushed before setting the pres context
2048 // Otherwise we may end up with bogus viewport units resolved against the
2049 // unconstrained bsize, or restyling the whole document resolving viewport
2050 // units against targetWidth, which may end up doing wasteful work.
2051 mDocument
->FlushPendingNotifications(FlushType::Frames
);
2053 nsIFrame
* rootFrame
= GetRootFrame();
2054 if (mIsDestroying
|| !rootFrame
) {
2055 // If we don't have a root frame yet, that means we haven't had our initial
2056 // reflow... If that's the case, and aWidth or aHeight is unconstrained,
2057 // ignore them altogether.
2058 if (aHeight
== NS_UNCONSTRAINEDSIZE
|| aWidth
== NS_UNCONSTRAINEDSIZE
) {
2059 // We can't do the work needed for SizeToContent without a root
2060 // frame, and we want to return before setting the visible area.
2061 return NS_ERROR_NOT_AVAILABLE
;
2064 mPresContext
->SetVisibleArea(nsRect(0, 0, aWidth
, aHeight
));
2065 // There isn't anything useful we can do if the initial reflow hasn't
2070 WritingMode wm
= rootFrame
->GetWritingMode();
2071 MOZ_ASSERT((wm
.IsVertical() ? aHeight
: aWidth
) != NS_UNCONSTRAINEDSIZE
,
2072 "unconstrained isize not allowed");
2074 nscoord targetWidth
= aWidth
;
2075 nscoord targetHeight
= aHeight
;
2076 if (wm
.IsVertical()) {
2077 targetWidth
= NS_UNCONSTRAINEDSIZE
;
2079 targetHeight
= NS_UNCONSTRAINEDSIZE
;
2082 mPresContext
->SetVisibleArea(nsRect(0, 0, targetWidth
, targetHeight
));
2083 // XXX Do a full invalidate at the beginning so that invalidates along
2084 // the way don't have region accumulation issues?
2086 // For height:auto BSizes (i.e. layout-controlled), descendant
2087 // intrinsic sizes can't depend on them. So the only other case is
2088 // viewport-controlled BSizes which we handle here.
2089 nsLayoutUtils::MarkIntrinsicISizesDirtyIfDependentOnBSize(rootFrame
);
2092 nsAutoCauseReflowNotifier
crNotifier(this);
2095 // Kick off a top-down reflow
2096 AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Reflow
);
2097 nsViewManager::AutoDisableRefresh
refreshBlocker(mViewManager
);
2099 mDirtyRoots
.Remove(rootFrame
);
2100 DoReflow(rootFrame
, true, nullptr);
2102 const bool reflowAgain
=
2103 wm
.IsVertical() ? mPresContext
->GetVisibleArea().width
> aWidth
2104 : mPresContext
->GetVisibleArea().height
> aHeight
;
2107 mPresContext
->SetVisibleArea(nsRect(0, 0, aWidth
, aHeight
));
2108 DoReflow(rootFrame
, true, nullptr);
2112 // Now, we may have been destroyed by the destructor of
2113 // `nsAutoCauseReflowNotifier`.
2117 // the reflow above should've set our bsize if it was NS_UNCONSTRAINEDSIZE,
2118 // and the isize shouldn't be NS_UNCONSTRAINEDSIZE anyway.
2119 MOZ_DIAGNOSTIC_ASSERT(
2120 mPresContext
->GetVisibleArea().width
!= NS_UNCONSTRAINEDSIZE
,
2121 "width should not be NS_UNCONSTRAINEDSIZE after reflow");
2122 MOZ_DIAGNOSTIC_ASSERT(
2123 mPresContext
->GetVisibleArea().height
!= NS_UNCONSTRAINEDSIZE
,
2124 "height should not be NS_UNCONSTRAINEDSIZE after reflow");
2126 postResizeEventIfNeeded();
2127 return NS_OK
; // XXX this needs to be real. MMP
2130 void PresShell::FireResizeEvent() {
2131 if (mIsDocumentGone
) {
2135 // If event handling is suppressed, repost the resize event to the refresh
2136 // driver. The event is marked as delayed so that the refresh driver does not
2137 // continue ticking.
2138 if (mDocument
->EventHandlingSuppressed()) {
2139 if (MOZ_LIKELY(!mDocument
->GetBFCacheEntry())) {
2140 mDocument
->SetHasDelayedRefreshEvent();
2141 mPresContext
->RefreshDriver()->AddResizeEventFlushObserver(
2142 this, /* aDelayed = */ true);
2147 mResizeEventPending
= false;
2149 // Send resize event from here.
2150 WidgetEvent
event(true, mozilla::eResize
);
2151 nsEventStatus status
= nsEventStatus_eIgnore
;
2153 if (nsPIDOMWindowOuter
* window
= mDocument
->GetWindow()) {
2154 EventDispatcher::Dispatch(window
, mPresContext
, &event
, nullptr, &status
);
2158 static nsIContent
* GetNativeAnonymousSubtreeRoot(nsIContent
* aContent
) {
2162 return aContent
->GetClosestNativeAnonymousSubtreeRoot();
2165 void PresShell::NativeAnonymousContentRemoved(nsIContent
* aAnonContent
) {
2166 MOZ_ASSERT(aAnonContent
->IsRootOfNativeAnonymousSubtree());
2167 if (nsIContent
* root
= GetNativeAnonymousSubtreeRoot(mCurrentEventContent
)) {
2168 if (aAnonContent
== root
) {
2169 mCurrentEventContent
= aAnonContent
->GetFlattenedTreeParent();
2170 mCurrentEventFrame
= nullptr;
2174 for (unsigned int i
= 0; i
< mCurrentEventContentStack
.Length(); i
++) {
2176 GetNativeAnonymousSubtreeRoot(mCurrentEventContentStack
.ElementAt(i
));
2177 if (aAnonContent
== anon
) {
2178 mCurrentEventContentStack
.ReplaceObjectAt(
2179 aAnonContent
->GetFlattenedTreeParent(), i
);
2180 mCurrentEventFrameStack
[i
] = nullptr;
2185 void PresShell::SetIgnoreFrameDestruction(bool aIgnore
) {
2187 // We need to tell the ImageLoader to drop all its references to frames
2188 // because they're about to go away and it won't get notifications of that.
2189 mDocument
->StyleImageLoader()->ClearFrames(mPresContext
);
2191 mIgnoreFrameDestruction
= aIgnore
;
2194 void PresShell::NotifyDestroyingFrame(nsIFrame
* aFrame
) {
2195 // We must remove these from FrameLayerBuilder::DisplayItemData::mFrameList
2196 // here, otherwise the DisplayItemData destructor will use the destroyed frame
2197 // when it tries to remove it from the (array) value of this property.
2198 aFrame
->RemoveDisplayItemDataForDeletion();
2200 if (!mIgnoreFrameDestruction
) {
2201 if (aFrame
->HasImageRequest()) {
2202 mDocument
->StyleImageLoader()->DropRequestsForFrame(aFrame
);
2205 mFrameConstructor
->NotifyDestroyingFrame(aFrame
);
2207 mDirtyRoots
.Remove(aFrame
);
2209 // Remove frame properties
2210 aFrame
->RemoveAllProperties();
2212 if (aFrame
== mCurrentEventFrame
) {
2213 mCurrentEventContent
= aFrame
->GetContent();
2214 mCurrentEventFrame
= nullptr;
2218 if (aFrame
== mDrawEventTargetFrame
) {
2219 mDrawEventTargetFrame
= nullptr;
2223 for (unsigned int i
= 0; i
< mCurrentEventFrameStack
.Length(); i
++) {
2224 if (aFrame
== mCurrentEventFrameStack
.ElementAt(i
)) {
2225 // One of our stack frames was deleted. Get its content so that when we
2226 // pop it we can still get its new frame from its content
2227 nsIContent
* currentEventContent
= aFrame
->GetContent();
2228 mCurrentEventContentStack
.ReplaceObjectAt(currentEventContent
, i
);
2229 mCurrentEventFrameStack
[i
] = nullptr;
2233 mFramesToDirty
.Remove(aFrame
);
2235 nsIScrollableFrame
* scrollableFrame
= do_QueryFrame(aFrame
);
2236 if (scrollableFrame
) {
2237 mPendingScrollAnchorSelection
.Remove(scrollableFrame
);
2238 mPendingScrollAnchorAdjustment
.Remove(scrollableFrame
);
2243 already_AddRefed
<nsCaret
> PresShell::GetCaret() const {
2244 RefPtr
<nsCaret
> caret
= mCaret
;
2245 return caret
.forget();
2248 already_AddRefed
<AccessibleCaretEventHub
>
2249 PresShell::GetAccessibleCaretEventHub() const {
2250 RefPtr
<AccessibleCaretEventHub
> eventHub
= mAccessibleCaretEventHub
;
2251 return eventHub
.forget();
2254 void PresShell::SetCaret(nsCaret
* aNewCaret
) { mCaret
= aNewCaret
; }
2256 void PresShell::RestoreCaret() { mCaret
= mOriginalCaret
; }
2258 NS_IMETHODIMP
PresShell::SetCaretEnabled(bool aInEnable
) {
2259 bool oldEnabled
= mCaretEnabled
;
2261 mCaretEnabled
= aInEnable
;
2263 if (mCaretEnabled
!= oldEnabled
) {
2266 mCaret
->SetVisible(mCaretEnabled
);
2273 NS_IMETHODIMP
PresShell::SetCaretReadOnly(bool aReadOnly
) {
2274 if (mCaret
) mCaret
->SetCaretReadOnly(aReadOnly
);
2278 NS_IMETHODIMP
PresShell::GetCaretEnabled(bool* aOutEnabled
) {
2279 NS_ENSURE_ARG_POINTER(aOutEnabled
);
2280 *aOutEnabled
= mCaretEnabled
;
2284 NS_IMETHODIMP
PresShell::SetCaretVisibilityDuringSelection(bool aVisibility
) {
2285 if (mCaret
) mCaret
->SetVisibilityDuringSelection(aVisibility
);
2289 NS_IMETHODIMP
PresShell::GetCaretVisible(bool* aOutIsVisible
) {
2290 *aOutIsVisible
= false;
2292 *aOutIsVisible
= mCaret
->IsVisible();
2297 NS_IMETHODIMP
PresShell::SetSelectionFlags(int16_t aFlags
) {
2298 mSelectionFlags
= aFlags
;
2302 NS_IMETHODIMP
PresShell::GetSelectionFlags(int16_t* aFlags
) {
2304 return NS_ERROR_INVALID_ARG
;
2307 *aFlags
= mSelectionFlags
;
2311 // implementation of nsISelectionController
2314 PresShell::PhysicalMove(int16_t aDirection
, int16_t aAmount
, bool aExtend
) {
2315 RefPtr
<nsFrameSelection
> frameSelection
= mSelection
;
2316 return frameSelection
->PhysicalMove(aDirection
, aAmount
, aExtend
);
2320 PresShell::CharacterMove(bool aForward
, bool aExtend
) {
2321 RefPtr
<nsFrameSelection
> frameSelection
= mSelection
;
2322 return frameSelection
->CharacterMove(aForward
, aExtend
);
2326 PresShell::WordMove(bool aForward
, bool aExtend
) {
2327 RefPtr
<nsFrameSelection
> frameSelection
= mSelection
;
2328 nsresult result
= frameSelection
->WordMove(aForward
, aExtend
);
2329 // if we can't go down/up any more we must then move caret completely to
2330 // end/beginning respectively.
2331 if (NS_FAILED(result
)) result
= CompleteMove(aForward
, aExtend
);
2336 PresShell::LineMove(bool aForward
, bool aExtend
) {
2337 RefPtr
<nsFrameSelection
> frameSelection
= mSelection
;
2338 nsresult result
= frameSelection
->LineMove(aForward
, aExtend
);
2339 // if we can't go down/up any more we must then move caret completely to
2340 // end/beginning respectively.
2341 if (NS_FAILED(result
)) result
= CompleteMove(aForward
, aExtend
);
2346 PresShell::IntraLineMove(bool aForward
, bool aExtend
) {
2347 RefPtr
<nsFrameSelection
> frameSelection
= mSelection
;
2348 return frameSelection
->IntraLineMove(aForward
, aExtend
);
2352 PresShell::PageMove(bool aForward
, bool aExtend
) {
2353 nsIFrame
* frame
= nullptr;
2355 frame
= do_QueryFrame(GetScrollableFrameToScroll(VerticalScrollDirection
));
2356 // If there is no scrollable frame, get the frame to move caret instead.
2358 if (!frame
|| frame
->PresContext() != mPresContext
) {
2359 frame
= mSelection
->GetFrameToPageSelect();
2364 // We may scroll parent scrollable element of current selection limiter.
2365 // In such case, we don't want to scroll selection into view unless
2366 // selection is changed.
2367 RefPtr
<nsFrameSelection
> frameSelection
= mSelection
;
2368 return frameSelection
->PageMove(
2369 aForward
, aExtend
, frame
, nsFrameSelection::SelectionIntoView::IfChanged
);
2373 PresShell::ScrollPage(bool aForward
) {
2374 nsIScrollableFrame
* scrollFrame
=
2375 GetScrollableFrameToScroll(VerticalScrollDirection
);
2377 scrollFrame
->ScrollBy(
2378 nsIntPoint(0, aForward
? 1 : -1), ScrollUnit::PAGES
, ScrollMode::Smooth
,
2379 nullptr, mozilla::ScrollOrigin::NotSpecified
,
2380 nsIScrollableFrame::NOT_MOMENTUM
, nsIScrollableFrame::ENABLE_SNAP
);
2386 PresShell::ScrollLine(bool aForward
) {
2387 nsIScrollableFrame
* scrollFrame
=
2388 GetScrollableFrameToScroll(VerticalScrollDirection
);
2391 Preferences::GetInt("toolkit.scrollbox.verticalScrollDistance",
2392 NS_DEFAULT_VERTICAL_SCROLL_DISTANCE
);
2393 scrollFrame
->ScrollBy(
2394 nsIntPoint(0, aForward
? lineCount
: -lineCount
), ScrollUnit::LINES
,
2395 ScrollMode::Smooth
, nullptr, mozilla::ScrollOrigin::NotSpecified
,
2396 nsIScrollableFrame::NOT_MOMENTUM
, nsIScrollableFrame::ENABLE_SNAP
);
2402 PresShell::ScrollCharacter(bool aRight
) {
2403 nsIScrollableFrame
* scrollFrame
=
2404 GetScrollableFrameToScroll(HorizontalScrollDirection
);
2407 Preferences::GetInt("toolkit.scrollbox.horizontalScrollDistance",
2408 NS_DEFAULT_HORIZONTAL_SCROLL_DISTANCE
);
2409 scrollFrame
->ScrollBy(
2410 nsIntPoint(aRight
? h
: -h
, 0), ScrollUnit::LINES
, ScrollMode::Smooth
,
2411 nullptr, mozilla::ScrollOrigin::NotSpecified
,
2412 nsIScrollableFrame::NOT_MOMENTUM
, nsIScrollableFrame::ENABLE_SNAP
);
2418 PresShell::CompleteScroll(bool aForward
) {
2419 nsIScrollableFrame
* scrollFrame
=
2420 GetScrollableFrameToScroll(VerticalScrollDirection
);
2422 scrollFrame
->ScrollBy(
2423 nsIntPoint(0, aForward
? 1 : -1), ScrollUnit::WHOLE
, ScrollMode::Smooth
,
2424 nullptr, mozilla::ScrollOrigin::NotSpecified
,
2425 nsIScrollableFrame::NOT_MOMENTUM
, nsIScrollableFrame::ENABLE_SNAP
);
2431 PresShell::CompleteMove(bool aForward
, bool aExtend
) {
2432 // Beware! This may flush notifications via synchronous
2433 // ScrollSelectionIntoView.
2434 RefPtr
<nsFrameSelection
> frameSelection
= mSelection
;
2435 nsIContent
* limiter
= frameSelection
->GetAncestorLimiter();
2436 nsIFrame
* frame
= limiter
? limiter
->GetPrimaryFrame()
2437 : FrameConstructor()->GetRootElementFrame();
2438 if (!frame
) return NS_ERROR_FAILURE
;
2439 nsIFrame::CaretPosition pos
= frame
->GetExtremeCaretPosition(!aForward
);
2441 const nsFrameSelection::FocusMode focusMode
=
2442 aExtend
? nsFrameSelection::FocusMode::kExtendSelection
2443 : nsFrameSelection::FocusMode::kCollapseToNewPoint
;
2444 frameSelection
->HandleClick(
2445 MOZ_KnownLive(pos
.mResultContent
) /* bug 1636889 */, pos
.mContentOffset
,
2446 pos
.mContentOffset
, focusMode
,
2447 aForward
? CARET_ASSOCIATE_AFTER
: CARET_ASSOCIATE_BEFORE
);
2449 // HandleClick resets ancestorLimiter, so set it again.
2450 frameSelection
->SetAncestorLimiter(limiter
);
2453 // After ScrollSelectionIntoView(), the pending notifications might be
2454 // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
2455 return ScrollSelectionIntoView(
2456 nsISelectionController::SELECTION_NORMAL
,
2457 nsISelectionController::SELECTION_FOCUS_REGION
,
2458 nsISelectionController::SCROLL_SYNCHRONOUS
|
2459 nsISelectionController::SCROLL_FOR_CARET_MOVE
);
2462 static void DoCheckVisibility(nsPresContext
* aPresContext
, nsIContent
* aNode
,
2463 int16_t aStartOffset
, int16_t aEndOffset
,
2465 nsIFrame
* frame
= aNode
->GetPrimaryFrame();
2467 // No frame to look at so it must not be visible.
2471 // Start process now to go through all frames to find startOffset. Then check
2472 // chars after that to see if anything until EndOffset is visible.
2473 bool finished
= false;
2474 frame
->CheckVisibility(aPresContext
, aStartOffset
, aEndOffset
, true,
2475 &finished
, aRetval
);
2476 // Don't worry about other return value.
2480 PresShell::CheckVisibility(nsINode
* node
, int16_t startOffset
,
2481 int16_t EndOffset
, bool* _retval
) {
2482 if (!node
|| startOffset
> EndOffset
|| !_retval
|| startOffset
< 0 ||
2484 return NS_ERROR_INVALID_ARG
;
2485 *_retval
= false; // initialize return parameter
2486 nsCOMPtr
<nsIContent
> content(do_QueryInterface(node
));
2487 if (!content
) return NS_ERROR_FAILURE
;
2489 DoCheckVisibility(mPresContext
, content
, startOffset
, EndOffset
, _retval
);
2493 nsresult
PresShell::CheckVisibilityContent(nsIContent
* aNode
,
2494 int16_t aStartOffset
,
2495 int16_t aEndOffset
, bool* aRetval
) {
2496 if (!aNode
|| aStartOffset
> aEndOffset
|| !aRetval
|| aStartOffset
< 0 ||
2498 return NS_ERROR_INVALID_ARG
;
2502 DoCheckVisibility(mPresContext
, aNode
, aStartOffset
, aEndOffset
, aRetval
);
2506 // end implementations nsISelectionController
2508 nsIFrame
* PresShell::GetRootScrollFrame() const {
2509 nsIFrame
* rootFrame
= mFrameConstructor
->GetRootFrame();
2510 // Ensure root frame is a viewport frame
2511 if (!rootFrame
|| !rootFrame
->IsViewportFrame()) return nullptr;
2512 nsIFrame
* theFrame
= rootFrame
->PrincipalChildList().FirstChild();
2513 if (!theFrame
|| !theFrame
->IsScrollFrame()) return nullptr;
2517 nsIScrollableFrame
* PresShell::GetRootScrollFrameAsScrollable() const {
2518 nsIFrame
* frame
= GetRootScrollFrame();
2519 if (!frame
) return nullptr;
2520 nsIScrollableFrame
* scrollableFrame
= do_QueryFrame(frame
);
2521 NS_ASSERTION(scrollableFrame
,
2522 "All scroll frames must implement nsIScrollableFrame");
2523 return scrollableFrame
;
2526 nsPageSequenceFrame
* PresShell::GetPageSequenceFrame() const {
2527 return mFrameConstructor
->GetPageSequenceFrame();
2530 nsCanvasFrame
* PresShell::GetCanvasFrame() const {
2531 nsIFrame
* frame
= mFrameConstructor
->GetDocElementContainingBlock();
2532 return do_QueryFrame(frame
);
2535 void PresShell::RestoreRootScrollPosition() {
2536 nsIScrollableFrame
* scrollableFrame
= GetRootScrollFrameAsScrollable();
2537 if (scrollableFrame
) {
2538 scrollableFrame
->ScrollToRestoredPosition();
2542 void PresShell::MaybeReleaseCapturingContent() {
2543 RefPtr
<nsFrameSelection
> frameSelection
= FrameSelection();
2544 if (frameSelection
) {
2545 frameSelection
->SetDragState(false);
2547 if (sCapturingContentInfo
.mContent
&&
2548 sCapturingContentInfo
.mContent
->OwnerDoc() == mDocument
) {
2549 PresShell::ReleaseCapturingContent();
2553 void PresShell::BeginLoad(Document
* aDocument
) {
2554 mDocumentLoading
= true;
2556 gfxTextPerfMetrics
* tp
= nullptr;
2558 tp
= mPresContext
->GetTextPerfMetrics();
2561 bool shouldLog
= MOZ_LOG_TEST(gLog
, LogLevel::Debug
);
2562 if (shouldLog
|| tp
) {
2563 mLoadBegin
= TimeStamp::Now();
2567 nsIURI
* uri
= mDocument
->GetDocumentURI();
2568 MOZ_LOG(gLog
, LogLevel::Debug
,
2569 ("(presshell) %p load begin [%s]\n", this,
2570 uri
? uri
->GetSpecOrDefault().get() : ""));
2574 void PresShell::EndLoad(Document
* aDocument
) {
2575 MOZ_ASSERT(aDocument
== mDocument
, "Wrong document");
2577 RestoreRootScrollPosition();
2579 mDocumentLoading
= false;
2582 bool PresShell::IsLayoutFlushObserver() {
2583 return GetPresContext()->RefreshDriver()->IsLayoutFlushObserver(this);
2586 void PresShell::LoadComplete() {
2587 gfxTextPerfMetrics
* tp
= nullptr;
2589 tp
= mPresContext
->GetTextPerfMetrics();
2593 bool shouldLog
= MOZ_LOG_TEST(gLog
, LogLevel::Debug
);
2594 if (shouldLog
|| tp
) {
2595 TimeDuration loadTime
= TimeStamp::Now() - mLoadBegin
;
2596 nsIURI
* uri
= mDocument
->GetDocumentURI();
2599 spec
= uri
->GetSpecOrDefault();
2602 MOZ_LOG(gLog
, LogLevel::Debug
,
2603 ("(presshell) %p load done time-ms: %9.2f [%s]\n", this,
2604 loadTime
.ToMilliseconds(), spec
.get()));
2608 if (tp
->cumulative
.numChars
> 0) {
2609 LogTextPerfStats(tp
, this, tp
->cumulative
, loadTime
.ToMilliseconds(),
2610 eLog_loaddone
, spec
.get());
2617 void PresShell::VerifyHasDirtyRootAncestor(nsIFrame
* aFrame
) {
2618 // XXXbz due to bug 372769, can't actually assert anything here...
2621 // XXXbz shouldn't need this part; remove it once FrameNeedsReflow
2622 // handles the root frame correctly.
2623 if (!aFrame
->GetParent()) {
2627 // Make sure that there is a reflow root ancestor of |aFrame| that's
2628 // in mDirtyRoots already.
2629 while (aFrame
&& aFrame
->HasAnyStateBits(NS_FRAME_HAS_DIRTY_CHILDREN
)) {
2630 if ((aFrame
->HasAnyStateBits(NS_FRAME_REFLOW_ROOT
|
2631 NS_FRAME_DYNAMIC_REFLOW_ROOT
) ||
2632 !aFrame
->GetParent()) &&
2633 mDirtyRoots
.Contains(aFrame
)) {
2637 aFrame
= aFrame
->GetParent();
2640 MOZ_ASSERT_UNREACHABLE(
2641 "Frame has dirty bits set but isn't scheduled to be "
2646 void PresShell::PostPendingScrollAnchorSelection(
2647 mozilla::layout::ScrollAnchorContainer
* aContainer
) {
2648 mPendingScrollAnchorSelection
.Insert(aContainer
->ScrollableFrame());
2651 void PresShell::FlushPendingScrollAnchorSelections() {
2652 for (nsIScrollableFrame
* scroll
: mPendingScrollAnchorSelection
) {
2653 scroll
->Anchor()->SelectAnchor();
2655 mPendingScrollAnchorSelection
.Clear();
2658 void PresShell::PostPendingScrollAnchorAdjustment(
2659 ScrollAnchorContainer
* aContainer
) {
2660 mPendingScrollAnchorAdjustment
.Insert(aContainer
->ScrollableFrame());
2663 void PresShell::FlushPendingScrollAnchorAdjustments() {
2664 for (nsIScrollableFrame
* scroll
: mPendingScrollAnchorAdjustment
) {
2665 scroll
->Anchor()->ApplyAdjustments();
2667 mPendingScrollAnchorAdjustment
.Clear();
2670 void PresShell::FrameNeedsReflow(nsIFrame
* aFrame
,
2671 IntrinsicDirty aIntrinsicDirty
,
2672 nsFrameState aBitToAdd
,
2673 ReflowRootHandling aRootHandling
) {
2674 MOZ_ASSERT(aBitToAdd
== NS_FRAME_IS_DIRTY
||
2675 aBitToAdd
== NS_FRAME_HAS_DIRTY_CHILDREN
|| !aBitToAdd
,
2676 "Unexpected bits being added");
2679 NS_ASSERTION(!(aIntrinsicDirty
== IntrinsicDirty::StyleChange
&&
2680 aBitToAdd
== NS_FRAME_HAS_DIRTY_CHILDREN
),
2681 "bits don't correspond to style change reason");
2684 NS_ASSERTION(!mIsReflowing
, "can't mark frame dirty during reflow");
2686 // If we've not yet done the initial reflow, then don't bother
2687 // enqueuing a reflow command yet.
2688 if (!mDidInitialize
) return;
2690 // If we're already destroying, don't bother with this either.
2691 if (mIsDestroying
) return;
2694 // printf("gShellCounter: %d\n", gShellCounter++);
2695 if (mInVerifyReflow
) return;
2697 if (VerifyReflowFlags::NoisyCommands
& gVerifyReflowFlags
) {
2698 printf("\nPresShell@%p: frame %p needs reflow\n", (void*)this,
2700 if (VerifyReflowFlags::ReallyNoisyCommands
& gVerifyReflowFlags
) {
2701 printf("Current content model:\n");
2702 Element
* rootElement
= mDocument
->GetRootElement();
2704 rootElement
->List(stdout
, 0);
2710 AutoTArray
<nsIFrame
*, 4> subtrees
;
2711 subtrees
.AppendElement(aFrame
);
2714 nsIFrame
* subtreeRoot
= subtrees
.PopLastElement();
2716 // Grab |wasDirty| now so we can go ahead and update the bits on
2718 bool wasDirty
= subtreeRoot
->IsSubtreeDirty();
2719 subtreeRoot
->AddStateBits(aBitToAdd
);
2721 // Determine whether we need to keep looking for the next ancestor
2722 // reflow root if subtreeRoot itself is a reflow root.
2723 bool targetNeedsReflowFromParent
;
2724 switch (aRootHandling
) {
2725 case ReflowRootHandling::PositionOrSizeChange
:
2726 targetNeedsReflowFromParent
= true;
2728 case ReflowRootHandling::NoPositionOrSizeChange
:
2729 targetNeedsReflowFromParent
= false;
2731 case ReflowRootHandling::InferFromBitToAdd
:
2732 targetNeedsReflowFromParent
= (aBitToAdd
== NS_FRAME_IS_DIRTY
);
2736 #define FRAME_IS_REFLOW_ROOT(_f) \
2737 ((_f)->HasAnyStateBits(NS_FRAME_REFLOW_ROOT | \
2738 NS_FRAME_DYNAMIC_REFLOW_ROOT) && \
2739 ((_f) != subtreeRoot || !targetNeedsReflowFromParent))
2741 // Mark the intrinsic widths as dirty on the frame, all of its ancestors,
2742 // and all of its descendants, if needed:
2744 if (aIntrinsicDirty
!= IntrinsicDirty::Resize
) {
2745 // Mark argument and all ancestors dirty. (Unless we hit a reflow
2746 // root that should contain the reflow. That root could be
2747 // subtreeRoot itself if it's not dirty, or it could be some
2748 // ancestor of subtreeRoot.)
2749 for (nsIFrame
* a
= subtreeRoot
; a
&& !FRAME_IS_REFLOW_ROOT(a
);
2750 a
= a
->GetParent()) {
2751 a
->MarkIntrinsicISizesDirty();
2752 if (a
->IsAbsolutelyPositioned()) {
2753 // If we get here, 'a' is abspos, so its subtree's intrinsic sizing
2754 // has no effect on its ancestors' intrinsic sizing. So, don't loop
2755 // upwards any further.
2761 const bool styleChange
= (aIntrinsicDirty
== IntrinsicDirty::StyleChange
);
2762 const bool dirty
= (aBitToAdd
== NS_FRAME_IS_DIRTY
);
2763 if (styleChange
|| dirty
) {
2764 // Mark all descendants dirty (using an nsTArray stack rather than
2766 // Note that ReflowInput::InitResizeFlags has some similar
2767 // code; see comments there for how and why it differs.
2768 AutoTArray
<nsIFrame
*, 32> stack
;
2769 stack
.AppendElement(subtreeRoot
);
2772 nsIFrame
* f
= stack
.PopLastElement();
2774 if (styleChange
&& f
->IsPlaceholderFrame()) {
2775 // Call `GetOutOfFlowFrame` directly because we can get here from
2776 // frame destruction and the placeholder might be already torn down.
2778 static_cast<nsPlaceholderFrame
*>(f
)->GetOutOfFlowFrame()) {
2779 if (!nsLayoutUtils::IsProperAncestorFrame(subtreeRoot
, oof
)) {
2780 // We have another distinct subtree we need to mark.
2781 subtrees
.AppendElement(oof
);
2786 for (const auto& childList
: f
->ChildLists()) {
2787 for (nsIFrame
* kid
: childList
.mList
) {
2789 kid
->MarkIntrinsicISizesDirty();
2792 kid
->AddStateBits(NS_FRAME_IS_DIRTY
);
2794 stack
.AppendElement(kid
);
2797 } while (stack
.Length() != 0);
2800 // Skip setting dirty bits up the tree if we weren't given a bit to add.
2805 // Set NS_FRAME_HAS_DIRTY_CHILDREN bits (via nsIFrame::ChildIsDirty)
2806 // up the tree until we reach either a frame that's already dirty or
2808 nsIFrame
* f
= subtreeRoot
;
2810 if (FRAME_IS_REFLOW_ROOT(f
) || !f
->GetParent()) {
2811 // we've hit a reflow root or the root frame
2814 SetNeedLayoutFlush();
2818 VerifyHasDirtyRootAncestor(f
);
2825 nsIFrame
* child
= f
;
2827 wasDirty
= f
->IsSubtreeDirty();
2828 f
->ChildIsDirty(child
);
2829 NS_ASSERTION(f
->HasAnyStateBits(NS_FRAME_HAS_DIRTY_CHILDREN
),
2830 "ChildIsDirty didn't do its job");
2832 // This frame was already marked dirty.
2834 VerifyHasDirtyRootAncestor(f
);
2839 } while (subtrees
.Length() != 0);
2841 MaybeScheduleReflow();
2844 void PresShell::FrameNeedsToContinueReflow(nsIFrame
* aFrame
) {
2845 NS_ASSERTION(mIsReflowing
, "Must be in reflow when marking path dirty.");
2846 MOZ_ASSERT(mCurrentReflowRoot
, "Must have a current reflow root here");
2848 aFrame
== mCurrentReflowRoot
||
2849 nsLayoutUtils::IsProperAncestorFrame(mCurrentReflowRoot
, aFrame
),
2850 "Frame passed in is not the descendant of mCurrentReflowRoot");
2851 NS_ASSERTION(aFrame
->HasAnyStateBits(NS_FRAME_IN_REFLOW
),
2852 "Frame passed in not in reflow?");
2854 mFramesToDirty
.Insert(aFrame
);
2857 already_AddRefed
<nsIContent
> PresShell::GetContentForScrolling() const {
2858 if (nsCOMPtr
<nsIContent
> focused
= GetFocusedContentInOurWindow()) {
2859 return focused
.forget();
2861 return GetSelectedContentForScrolling();
2864 already_AddRefed
<nsIContent
> PresShell::GetSelectedContentForScrolling() const {
2865 nsCOMPtr
<nsIContent
> selectedContent
;
2867 Selection
* domSelection
= mSelection
->GetSelection(SelectionType::eNormal
);
2870 nsIContent::FromNodeOrNull(domSelection
->GetFocusNode());
2873 return selectedContent
.forget();
2876 nsIScrollableFrame
* PresShell::GetScrollableFrameToScrollForContent(
2877 nsIContent
* aContent
, ScrollDirections aDirections
) {
2878 nsIScrollableFrame
* scrollFrame
= nullptr;
2880 nsIFrame
* startFrame
= aContent
->GetPrimaryFrame();
2882 scrollFrame
= startFrame
->GetScrollTargetFrame();
2884 startFrame
= scrollFrame
->GetScrolledFrame();
2886 scrollFrame
= nsLayoutUtils::GetNearestScrollableFrameForDirection(
2887 startFrame
, aDirections
);
2891 scrollFrame
= GetRootScrollFrameAsScrollable();
2892 if (!scrollFrame
|| !scrollFrame
->GetScrolledFrame()) {
2895 scrollFrame
= nsLayoutUtils::GetNearestScrollableFrameForDirection(
2896 scrollFrame
->GetScrolledFrame(), aDirections
);
2901 nsIScrollableFrame
* PresShell::GetScrollableFrameToScroll(
2902 ScrollDirections aDirections
) {
2903 nsCOMPtr
<nsIContent
> content
= GetContentForScrolling();
2904 return GetScrollableFrameToScrollForContent(content
.get(), aDirections
);
2907 void PresShell::CancelAllPendingReflows() {
2908 mDirtyRoots
.Clear();
2910 if (mObservingLayoutFlushes
) {
2911 GetPresContext()->RefreshDriver()->RemoveLayoutFlushObserver(this);
2912 mObservingLayoutFlushes
= false;
2915 ASSERT_REFLOW_SCHEDULED_STATE();
2918 static bool DestroyFramesAndStyleDataFor(
2919 Element
* aElement
, nsPresContext
& aPresContext
,
2920 RestyleManager::IncludeRoot aIncludeRoot
) {
2921 bool didReconstruct
=
2922 aPresContext
.FrameConstructor()->DestroyFramesFor(aElement
);
2923 RestyleManager::ClearServoDataFromSubtree(aElement
, aIncludeRoot
);
2924 return didReconstruct
;
2927 void PresShell::SlotAssignmentWillChange(Element
& aElement
,
2928 HTMLSlotElement
* aOldSlot
,
2929 HTMLSlotElement
* aNewSlot
) {
2930 MOZ_ASSERT(aOldSlot
!= aNewSlot
);
2932 if (MOZ_UNLIKELY(!mDidInitialize
)) {
2936 // If the old slot is about to become empty and show fallback, let layout know
2937 // that it needs to do work.
2938 if (aOldSlot
&& aOldSlot
->AssignedNodes().Length() == 1 &&
2939 aOldSlot
->HasChildren()) {
2940 DestroyFramesForAndRestyle(aOldSlot
);
2943 // Ensure the new element starts off clean.
2944 DestroyFramesAndStyleDataFor(&aElement
, *mPresContext
,
2945 RestyleManager::IncludeRoot::Yes
);
2948 // If the new slot will stop showing fallback content, we need to reframe it
2950 if (aNewSlot
->AssignedNodes().IsEmpty() && aNewSlot
->HasChildren()) {
2951 DestroyFramesForAndRestyle(aNewSlot
);
2952 // Otherwise we just care about the element, but we need to ensure that
2953 // something takes care of traversing to the relevant slot, if needed.
2954 } else if (aNewSlot
->HasServoData() &&
2955 !Servo_Element_IsDisplayNone(aNewSlot
)) {
2956 // Set the reframe bits...
2957 aNewSlot
->NoteDescendantsNeedFramesForServo();
2958 aElement
.SetFlags(NODE_NEEDS_FRAME
);
2959 // Now the style dirty bits. Note that we can't just do
2960 // aElement.NoteDirtyForServo(), because the new slot is not setup yet.
2961 aNewSlot
->SetHasDirtyDescendantsForServo();
2962 aNewSlot
->NoteDirtySubtreeForServo();
2968 static void AssertNoFramesInSubtree(nsIContent
* aContent
) {
2969 for (nsINode
* node
: ShadowIncludingTreeIterator(*aContent
)) {
2970 nsIContent
* c
= nsIContent::FromNode(node
);
2971 MOZ_ASSERT(!c
->GetPrimaryFrame());
2976 void PresShell::DestroyFramesForAndRestyle(Element
* aElement
) {
2978 auto postCondition
=
2979 mozilla::MakeScopeExit([&]() { AssertNoFramesInSubtree(aElement
); });
2982 MOZ_ASSERT(aElement
);
2983 if (MOZ_UNLIKELY(!mDidInitialize
)) {
2987 if (!aElement
->GetFlattenedTreeParentNode()) {
2988 // Nothing to do here, the element already is out of the frame tree.
2992 nsAutoScriptBlocker scriptBlocker
;
2994 // Mark ourselves as not safe to flush while we're doing frame destruction.
2997 const bool didReconstruct
= FrameConstructor()->DestroyFramesFor(aElement
);
2999 // Clear the style data from all the flattened tree descendants, but _not_
3000 // from us, since otherwise we wouldn't see the reframe.
3001 RestyleManager::ClearServoDataFromSubtree(aElement
,
3002 RestyleManager::IncludeRoot::No
);
3005 didReconstruct
? nsChangeHint(0) : nsChangeHint_ReconstructFrame
;
3007 mPresContext
->RestyleManager()->PostRestyleEvent(
3008 aElement
, RestyleHint::RestyleSubtree(), changeHint
);
3013 void PresShell::PostRecreateFramesFor(Element
* aElement
) {
3014 if (MOZ_UNLIKELY(!mDidInitialize
)) {
3015 // Nothing to do here. In fact, if we proceed and aElement is the root, we
3020 mPresContext
->RestyleManager()->PostRestyleEvent(
3021 aElement
, RestyleHint
{0}, nsChangeHint_ReconstructFrame
);
3024 void PresShell::RestyleForAnimation(Element
* aElement
, RestyleHint aHint
) {
3025 // Now that we no longer have separate non-animation and animation
3026 // restyles, this method having a distinct identity is less important,
3027 // but it still seems useful to offer as a "more public" API and as a
3028 // chokepoint for these restyles to go through.
3029 mPresContext
->RestyleManager()->PostRestyleEvent(aElement
, aHint
,
3033 void PresShell::SetForwardingContainer(const WeakPtr
<nsDocShell
>& aContainer
) {
3034 mForwardingContainer
= aContainer
;
3037 void PresShell::ClearFrameRefs(nsIFrame
* aFrame
) {
3038 mPresContext
->EventStateManager()->ClearFrameRefs(aFrame
);
3040 AutoWeakFrame
* weakFrame
= mAutoWeakFrames
;
3042 AutoWeakFrame
* prev
= weakFrame
->GetPreviousWeakFrame();
3043 if (weakFrame
->GetFrame() == aFrame
) {
3044 // This removes weakFrame from mAutoWeakFrames.
3045 weakFrame
->Clear(this);
3050 AutoTArray
<WeakFrame
*, 4> toRemove
;
3051 for (WeakFrame
* weakFrame
: mWeakFrames
) {
3052 if (weakFrame
->GetFrame() == aFrame
) {
3053 toRemove
.AppendElement(weakFrame
);
3056 for (WeakFrame
* weakFrame
: toRemove
) {
3057 weakFrame
->Clear(this);
3061 already_AddRefed
<gfxContext
> PresShell::CreateReferenceRenderingContext() {
3062 nsDeviceContext
* devCtx
= mPresContext
->DeviceContext();
3063 RefPtr
<gfxContext
> rc
;
3064 if (mPresContext
->IsScreen()) {
3065 rc
= gfxContext::CreateOrNull(
3066 gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget().get());
3068 // We assume the devCtx has positive width and height for this call.
3069 // However, width and height, may be outside of the reasonable range
3070 // so rc may still be null.
3071 rc
= devCtx
->CreateReferenceRenderingContext();
3074 return rc
? rc
.forget() : nullptr;
3077 nsresult
PresShell::GoToAnchor(const nsAString
& aAnchorName
, bool aScroll
,
3078 ScrollFlags aAdditionalScrollFlags
) {
3080 return NS_ERROR_FAILURE
;
3083 const Element
* root
= mDocument
->GetRootElement();
3084 if (root
&& root
->IsSVGElement(nsGkAtoms::svg
)) {
3085 // We need to execute this even if there is an empty anchor name
3086 // so that any existing SVG fragment identifier effect is removed
3087 if (SVGFragmentIdentifier::ProcessFragmentIdentifier(mDocument
,
3093 // Hold a reference to the ESM in case event dispatch tears us down.
3094 RefPtr
<EventStateManager
> esm
= mPresContext
->EventStateManager();
3096 if (aAnchorName
.IsEmpty()) {
3097 NS_ASSERTION(!aScroll
, "can't scroll to empty anchor name");
3098 esm
->SetContentState(nullptr, NS_EVENT_STATE_URLTARGET
);
3102 nsresult rv
= NS_OK
;
3103 nsCOMPtr
<nsIContent
> content
;
3105 // Search for an element with a matching "id" attribute
3107 content
= mDocument
->GetElementById(aAnchorName
);
3110 // Search for an anchor element with a matching "name" attribute
3111 if (!content
&& mDocument
->IsHTMLDocument()) {
3112 // Find a matching list of named nodes
3113 nsCOMPtr
<nsINodeList
> list
= mDocument
->GetElementsByName(aAnchorName
);
3115 // Loop through the named nodes looking for the first anchor
3116 uint32_t length
= list
->Length();
3117 for (uint32_t i
= 0; i
< length
; i
++) {
3118 nsIContent
* node
= list
->Item(i
);
3119 if (node
->IsHTMLElement(nsGkAtoms::a
)) {
3127 // Search for anchor in the HTML namespace with a matching name
3128 if (!content
&& !mDocument
->IsHTMLDocument()) {
3129 constexpr auto nameSpace
= u
"http://www.w3.org/1999/xhtml"_ns
;
3130 // Get the list of anchor elements
3131 nsCOMPtr
<nsINodeList
> list
=
3132 mDocument
->GetElementsByTagNameNS(nameSpace
, u
"a"_ns
);
3133 // Loop through the anchors looking for the first one with the given name.
3134 for (uint32_t i
= 0; true; i
++) {
3135 nsIContent
* node
= list
->Item(i
);
3136 if (!node
) { // End of list
3140 // Compare the name attribute
3141 if (node
->IsElement() &&
3142 node
->AsElement()->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::name
,
3143 aAnchorName
, eCaseMatters
)) {
3150 esm
->SetContentState(content
, NS_EVENT_STATE_URLTARGET
);
3152 #ifdef ACCESSIBILITY
3153 nsIContent
* anchorTarget
= content
;
3156 nsIScrollableFrame
* rootScroll
= GetRootScrollFrameAsScrollable();
3157 if (rootScroll
&& rootScroll
->DidHistoryRestore()) {
3158 // Scroll position restored from history trumps scrolling to anchor.
3160 rootScroll
->ClearDidHistoryRestore();
3165 rv
= ScrollContentIntoView(
3166 content
, ScrollAxis(kScrollToTop
, WhenToScroll::Always
), ScrollAxis(),
3167 ScrollFlags::AnchorScrollFlags
| aAdditionalScrollFlags
);
3168 NS_ENSURE_SUCCESS(rv
, rv
);
3170 nsIScrollableFrame
* rootScroll
= GetRootScrollFrameAsScrollable();
3172 mLastAnchorScrolledTo
= content
;
3173 mLastAnchorScrollPositionY
= rootScroll
->GetScrollPosition().y
;
3177 // Should we select the target? This action is controlled by a
3178 // preference: the default is to not select.
3179 bool selectAnchor
= Preferences::GetBool("layout.selectanchor");
3181 // Even if select anchor pref is false, we must still move the
3182 // caret there. That way tabbing will start from the new
3184 RefPtr
<nsRange
> jumpToRange
= nsRange::Create(mDocument
);
3185 while (content
&& content
->GetFirstChild()) {
3186 content
= content
->GetFirstChild();
3188 jumpToRange
->SelectNodeContents(*content
, IgnoreErrors());
3189 // Select the anchor
3190 RefPtr
<Selection
> sel
= mSelection
->GetSelection(SelectionType::eNormal
);
3192 sel
->RemoveAllRanges(IgnoreErrors());
3193 sel
->AddRangeAndSelectFramesAndNotifyListeners(*jumpToRange
,
3195 if (!selectAnchor
) {
3196 // Use a caret (collapsed selection) at the start of the anchor
3197 sel
->CollapseToStart(IgnoreErrors());
3200 // Selection is at anchor.
3201 // Now focus the document itself if focus is on an element within it.
3202 nsPIDOMWindowOuter
* win
= mDocument
->GetWindow();
3204 nsFocusManager
* fm
= nsFocusManager::GetFocusManager();
3206 nsCOMPtr
<mozIDOMWindowProxy
> focusedWindow
;
3207 fm
->GetFocusedWindow(getter_AddRefs(focusedWindow
));
3208 if (SameCOMIdentity(win
, focusedWindow
)) {
3209 fm
->ClearFocus(focusedWindow
);
3213 // If the target is an animation element, activate the animation
3214 nsCOMPtr
<SVGAnimationElement
> animationElement
= do_QueryInterface(content
);
3215 if (animationElement
) {
3216 animationElement
->ActivateByHyperlink();
3219 rv
= NS_ERROR_FAILURE
;
3220 if (nsContentUtils::EqualsIgnoreASCIICase(aAnchorName
, u
"top"_ns
)) {
3221 // Scroll to the top/left if aAnchorName is "top" and there is no element
3222 // with such a name or id.
3224 nsIScrollableFrame
* sf
= GetRootScrollFrameAsScrollable();
3225 // Check |aScroll| after setting |rv| so we set |rv| to the same
3226 // thing whether or not |aScroll| is true.
3227 if (aScroll
&& sf
) {
3228 ScrollMode scrollMode
=
3229 sf
->IsSmoothScroll() ? ScrollMode::SmoothMsd
: ScrollMode::Instant
;
3230 // Scroll to the top of the page
3231 sf
->ScrollTo(nsPoint(0, 0), scrollMode
);
3236 #ifdef ACCESSIBILITY
3238 if (nsAccessibilityService
* accService
= GetAccessibilityService()) {
3239 accService
->NotifyOfAnchorJumpTo(anchorTarget
);
3242 #endif // #ifdef ACCESSIBILITY
3247 nsresult
PresShell::ScrollToAnchor() {
3248 nsCOMPtr
<nsIContent
> lastAnchor
= std::move(mLastAnchorScrolledTo
);
3253 NS_ASSERTION(mDidInitialize
, "should have done initial reflow by now");
3254 nsIScrollableFrame
* rootScroll
= GetRootScrollFrameAsScrollable();
3256 mLastAnchorScrollPositionY
!= rootScroll
->GetScrollPosition().y
) {
3259 return ScrollContentIntoView(lastAnchor
,
3260 ScrollAxis(kScrollToTop
, WhenToScroll::Always
),
3261 ScrollAxis(), ScrollFlags::AnchorScrollFlags
);
3265 * Helper (per-continuation) for ScrollContentIntoView.
3267 * @param aContainerFrame [in] the frame which aRect is relative to
3268 * @param aFrame [in] Frame whose bounds should be unioned
3269 * @param aUseWholeLineHeightForInlines [in] if true, then for inline frames
3270 * we should include the top of the line in the added rectangle
3271 * @param aRect [inout] rect into which its bounds should be unioned
3272 * @param aHaveRect [inout] whether aRect contains data yet
3273 * @param aPrevBlock [inout] the block aLines is a line iterator for
3274 * @param aLines [inout] the line iterator we're using
3275 * @param aCurLine [inout] the line to start looking from in this iterator
3277 static void AccumulateFrameBounds(nsIFrame
* aContainerFrame
, nsIFrame
* aFrame
,
3278 bool aUseWholeLineHeightForInlines
,
3279 nsRect
& aRect
, bool& aHaveRect
,
3280 nsIFrame
*& aPrevBlock
,
3281 nsAutoLineIterator
& aLines
,
3282 int32_t& aCurLine
) {
3283 nsIFrame
* frame
= aFrame
;
3284 nsRect frameBounds
= nsRect(nsPoint(0, 0), aFrame
->GetSize());
3286 // If this is an inline frame and either the bounds height is 0 (quirks
3287 // layout model) or aUseWholeLineHeightForInlines is set, we need to
3288 // change the top of the bounds to include the whole line.
3289 if (frameBounds
.height
== 0 || aUseWholeLineHeightForInlines
) {
3290 nsIFrame
* prevFrame
= aFrame
;
3291 nsIFrame
* f
= aFrame
;
3293 while (f
&& f
->IsFrameOfType(nsIFrame::eLineParticipant
) &&
3294 !f
->IsTransformed() && !f
->IsAbsPosContainingBlock()) {
3296 f
= prevFrame
->GetParent();
3299 if (f
!= aFrame
&& f
&& f
->IsBlockFrame()) {
3300 // find the line containing aFrame and increase the top of |offset|.
3301 if (f
!= aPrevBlock
) {
3302 aLines
= f
->GetLineIterator();
3307 int32_t index
= aLines
->FindLineContaining(prevFrame
, aCurLine
);
3309 auto line
= aLines
->GetLine(index
).unwrap();
3310 frameBounds
+= frame
->GetOffsetTo(f
);
3312 if (line
.mLineBounds
.y
< frameBounds
.y
) {
3313 frameBounds
.height
= frameBounds
.YMost() - line
.mLineBounds
.y
;
3314 frameBounds
.y
= line
.mLineBounds
.y
;
3321 nsRect transformedBounds
= nsLayoutUtils::TransformFrameRectToAncestor(
3322 frame
, frameBounds
, aContainerFrame
);
3325 // We can't use nsRect::UnionRect since it drops empty rects on
3326 // the floor, and we need to include them. (Thus we need
3327 // aHaveRect to know when to drop the initial value on the floor.)
3328 aRect
= aRect
.UnionEdges(transformedBounds
);
3331 aRect
= transformedBounds
;
3335 static bool ComputeNeedToScroll(WhenToScroll aWhenToScroll
, nscoord aLineSize
,
3336 nscoord aRectMin
, nscoord aRectMax
,
3337 nscoord aViewMin
, nscoord aViewMax
) {
3338 // See how the rect should be positioned in a given axis.
3339 switch (aWhenToScroll
) {
3340 case WhenToScroll::Always
:
3341 // The caller wants the frame as visible as possible
3343 case WhenToScroll::IfNotVisible
:
3344 // Scroll only if no part of the frame is visible in this view.
3345 return aRectMax
- aLineSize
<= aViewMin
||
3346 aRectMin
+ aLineSize
>= aViewMax
;
3347 case WhenToScroll::IfNotFullyVisible
:
3348 // Scroll only if part of the frame is hidden and more can fit in view
3349 return !(aRectMin
>= aViewMin
&& aRectMax
<= aViewMax
) &&
3350 std::min(aViewMax
, aRectMax
) - std::max(aRectMin
, aViewMin
) <
3351 aViewMax
- aViewMin
;
3356 static nscoord
ComputeWhereToScroll(WhereToScroll aWhereToScroll
,
3357 nscoord aOriginalCoord
, nscoord aRectMin
,
3358 nscoord aRectMax
, nscoord aViewMin
,
3359 nscoord aViewMax
, nscoord
* aRangeMin
,
3360 nscoord
* aRangeMax
) {
3361 nscoord resultCoord
= aOriginalCoord
;
3362 nscoord scrollPortLength
= aViewMax
- aViewMin
;
3363 if (kScrollMinimum
== aWhereToScroll
) {
3364 // Scroll the minimum amount necessary to show as much as possible of the
3365 // frame. If the frame is too large, don't hide any initially visible part
3367 nscoord min
= std::min(aRectMin
, aRectMax
- scrollPortLength
);
3368 nscoord max
= std::max(aRectMin
, aRectMax
- scrollPortLength
);
3369 resultCoord
= std::min(std::max(aOriginalCoord
, min
), max
);
3371 nscoord frameAlignCoord
= NSToCoordRound(
3372 aRectMin
+ (aRectMax
- aRectMin
) * (aWhereToScroll
/ 100.0f
));
3373 resultCoord
= NSToCoordRound(frameAlignCoord
-
3374 scrollPortLength
* (aWhereToScroll
/ 100.0f
));
3376 // Force the scroll range to extend to include resultCoord.
3377 *aRangeMin
= std::min(resultCoord
, aRectMax
- scrollPortLength
);
3378 *aRangeMax
= std::max(resultCoord
, aRectMin
);
3383 * This function takes a scrollable frame, a rect in the coordinate system
3384 * of the scrolled frame, and a desired percentage-based scroll
3385 * position and attempts to scroll the rect to that position in the
3388 * This needs to work even if aRect has a width or height of zero.
3390 static void ScrollToShowRect(nsIScrollableFrame
* aFrameAsScrollable
,
3391 const nsRect
& aRect
, const nsMargin
& aMargin
,
3392 ScrollAxis aVertical
, ScrollAxis aHorizontal
,
3393 ScrollFlags aScrollFlags
) {
3394 nsPoint scrollPt
= aFrameAsScrollable
->GetVisualViewportOffset();
3395 const nsPoint originalScrollPt
= scrollPt
;
3396 const nsRect
visibleRect(scrollPt
,
3397 aFrameAsScrollable
->GetVisualViewportSize());
3399 const nsMargin padding
= aFrameAsScrollable
->GetScrollPadding() + aMargin
;
3401 const nsRect rectToScrollIntoView
= [&] {
3404 return r
.Intersect(aFrameAsScrollable
->GetScrolledRect());
3408 // Don't call GetLineScrollAmount unless we actually need it. Not only
3409 // does this save time, but it's not safe to call GetLineScrollAmount
3410 // during reflow (because it depends on font size inflation and doesn't
3411 // use the in-reflow-safe font-size inflation path). If we did call it,
3412 // it would assert and possible give the wrong result.
3413 if (aVertical
.mWhenToScroll
== WhenToScroll::IfNotVisible
||
3414 aHorizontal
.mWhenToScroll
== WhenToScroll::IfNotVisible
) {
3415 lineSize
= aFrameAsScrollable
->GetLineScrollAmount();
3417 ScrollStyles ss
= aFrameAsScrollable
->GetScrollStyles();
3418 nsRect
allowedRange(scrollPt
, nsSize(0, 0));
3419 ScrollDirections directions
=
3420 aFrameAsScrollable
->GetAvailableScrollingDirections();
3422 if (((aScrollFlags
& ScrollFlags::ScrollOverflowHidden
) ||
3423 ss
.mVertical
!= StyleOverflow::Hidden
) &&
3424 (!aVertical
.mOnlyIfPerceivedScrollableDirection
||
3425 (directions
.contains(ScrollDirection::eVertical
)))) {
3426 if (ComputeNeedToScroll(aVertical
.mWhenToScroll
, lineSize
.height
, aRect
.y
,
3427 aRect
.YMost(), visibleRect
.y
+ padding
.top
,
3428 visibleRect
.YMost() - padding
.bottom
)) {
3430 scrollPt
.y
= ComputeWhereToScroll(
3431 aVertical
.mWhereToScroll
, scrollPt
.y
, rectToScrollIntoView
.y
,
3432 rectToScrollIntoView
.YMost(), visibleRect
.y
, visibleRect
.YMost(),
3433 &allowedRange
.y
, &maxHeight
);
3434 allowedRange
.height
= maxHeight
- allowedRange
.y
;
3438 if (((aScrollFlags
& ScrollFlags::ScrollOverflowHidden
) ||
3439 ss
.mHorizontal
!= StyleOverflow::Hidden
) &&
3440 (!aHorizontal
.mOnlyIfPerceivedScrollableDirection
||
3441 (directions
.contains(ScrollDirection::eHorizontal
)))) {
3442 if (ComputeNeedToScroll(aHorizontal
.mWhenToScroll
, lineSize
.width
, aRect
.x
,
3443 aRect
.XMost(), visibleRect
.x
+ padding
.left
,
3444 visibleRect
.XMost() - padding
.right
)) {
3446 scrollPt
.x
= ComputeWhereToScroll(
3447 aHorizontal
.mWhereToScroll
, scrollPt
.x
, rectToScrollIntoView
.x
,
3448 rectToScrollIntoView
.XMost(), visibleRect
.x
, visibleRect
.XMost(),
3449 &allowedRange
.x
, &maxWidth
);
3450 allowedRange
.width
= maxWidth
- allowedRange
.x
;
3454 // If we don't need to scroll, then don't try since it might cancel
3455 // a current smooth scroll operation.
3456 if (scrollPt
== originalScrollPt
) {
3460 ScrollMode scrollMode
= ScrollMode::Instant
;
3461 bool autoBehaviorIsSmooth
= aFrameAsScrollable
->IsSmoothScroll();
3463 (aScrollFlags
& ScrollFlags::ScrollSmooth
) ||
3464 ((aScrollFlags
& ScrollFlags::ScrollSmoothAuto
) && autoBehaviorIsSmooth
);
3465 if (StaticPrefs::layout_css_scroll_behavior_enabled() && smoothScroll
) {
3466 scrollMode
= ScrollMode::SmoothMsd
;
3468 nsIFrame
* frame
= do_QueryFrame(aFrameAsScrollable
);
3469 AutoWeakFrame
weakFrame(frame
);
3470 aFrameAsScrollable
->ScrollTo(scrollPt
, scrollMode
, &allowedRange
,
3471 aScrollFlags
& ScrollFlags::ScrollSnap
3472 ? nsIScrollbarMediator::ENABLE_SNAP
3473 : nsIScrollbarMediator::DISABLE_SNAP
);
3474 if (!weakFrame
.IsAlive()) {
3478 // If this is the RCD-RSF, also call ScrollToVisual() since we want to
3479 // scroll the rect into view visually, and that may require scrolling
3480 // the visual viewport in scenarios where there is not enough layout
3482 if (aFrameAsScrollable
->IsRootScrollFrameOfDocument() &&
3483 frame
->PresContext()->IsRootContentDocumentCrossProcess()) {
3484 frame
->PresShell()->ScrollToVisual(scrollPt
, FrameMetrics::eMainThread
,
3489 nsresult
PresShell::ScrollContentIntoView(nsIContent
* aContent
,
3490 ScrollAxis aVertical
,
3491 ScrollAxis aHorizontal
,
3492 ScrollFlags aScrollFlags
) {
3493 NS_ENSURE_TRUE(aContent
, NS_ERROR_NULL_POINTER
);
3494 RefPtr
<Document
> composedDoc
= aContent
->GetComposedDoc();
3495 NS_ENSURE_STATE(composedDoc
);
3497 NS_ASSERTION(mDidInitialize
, "should have done initial reflow by now");
3499 if (mContentToScrollTo
) {
3500 mContentToScrollTo
->RemoveProperty(nsGkAtoms::scrolling
);
3502 mContentToScrollTo
= aContent
;
3503 ScrollIntoViewData
* data
= new ScrollIntoViewData();
3504 data
->mContentScrollVAxis
= aVertical
;
3505 data
->mContentScrollHAxis
= aHorizontal
;
3506 data
->mContentToScrollToFlags
= aScrollFlags
;
3507 if (NS_FAILED(mContentToScrollTo
->SetProperty(
3508 nsGkAtoms::scrolling
, data
,
3509 nsINode::DeleteProperty
<PresShell::ScrollIntoViewData
>))) {
3510 mContentToScrollTo
= nullptr;
3513 // Flush layout and attempt to scroll in the process.
3514 if (PresShell
* presShell
= composedDoc
->GetPresShell()) {
3515 presShell
->SetNeedLayoutFlush();
3517 composedDoc
->FlushPendingNotifications(FlushType::InterruptibleLayout
);
3519 // If mContentToScrollTo is non-null, that means we interrupted the reflow
3520 // (or suppressed it altogether because we're suppressing interruptible
3521 // flushes right now) and won't necessarily get the position correct, but do
3522 // a best-effort scroll here. The other option would be to do this inside
3523 // FlushPendingNotifications, but I'm not sure the repeated scrolling that
3524 // could trigger if reflows keep getting interrupted would be more desirable
3525 // than a single best-effort scroll followed by one final scroll on the first
3526 // completed reflow.
3527 if (mContentToScrollTo
) {
3528 DoScrollContentIntoView();
3533 void PresShell::DoScrollContentIntoView() {
3534 NS_ASSERTION(mDidInitialize
, "should have done initial reflow by now");
3536 nsIFrame
* frame
= mContentToScrollTo
->GetPrimaryFrame();
3538 mContentToScrollTo
->RemoveProperty(nsGkAtoms::scrolling
);
3539 mContentToScrollTo
= nullptr;
3543 if (frame
->HasAnyStateBits(NS_FRAME_FIRST_REFLOW
)) {
3544 // The reflow flush before this scroll got interrupted, and this frame's
3545 // coords and size are all zero, and it has no content showing anyway.
3546 // Don't bother scrolling to it. We'll try again when we finish up layout.
3550 // Make sure we skip 'frame' ... if it's scrollable, we should use its
3551 // scrollable ancestor as the container.
3552 nsIFrame
* container
= nsLayoutUtils::GetClosestFrameOfType(
3553 frame
->GetParent(), LayoutFrameType::Scroll
);
3555 // nothing can be scrolled
3559 ScrollIntoViewData
* data
= static_cast<ScrollIntoViewData
*>(
3560 mContentToScrollTo
->GetProperty(nsGkAtoms::scrolling
));
3561 if (MOZ_UNLIKELY(!data
)) {
3562 mContentToScrollTo
= nullptr;
3566 // Get the scroll-margin here since |frame| is going to be changed to iterate
3567 // over all continuation frames below.
3568 const nsMargin scrollMargin
= frame
->StyleMargin()->GetScrollMargin();
3570 // This is a two-step process.
3571 // Step 1: Find the bounds of the rect we want to scroll into view. For
3572 // example, for an inline frame we may want to scroll in the whole
3573 // line, or we may want to scroll multiple lines into view.
3574 // Step 2: Walk container frame and its ancestors and scroll them
3576 // frameBounds is relative to container. We're assuming
3577 // that scrollframes don't split so every continuation of frame will
3578 // be a descendant of container. (Things would still mostly work
3579 // even if that assumption was false.)
3581 bool haveRect
= false;
3582 bool useWholeLineHeightForInlines
= data
->mContentScrollVAxis
.mWhenToScroll
!=
3583 WhenToScroll::IfNotFullyVisible
;
3584 // Reuse the same line iterator across calls to AccumulateFrameBounds. We set
3585 // it every time we detect a new block (stored in prevBlock).
3586 nsIFrame
* prevBlock
= nullptr;
3587 nsAutoLineIterator lines
;
3588 // The last line we found a continuation on in |lines|. We assume that later
3589 // continuations cannot come on earlier lines.
3590 int32_t curLine
= 0;
3592 AccumulateFrameBounds(container
, frame
, useWholeLineHeightForInlines
,
3593 frameBounds
, haveRect
, prevBlock
, lines
, curLine
);
3594 } while ((frame
= frame
->GetNextContinuation()));
3596 ScrollFrameRectIntoView(container
, frameBounds
, scrollMargin
,
3597 data
->mContentScrollVAxis
, data
->mContentScrollHAxis
,
3598 data
->mContentToScrollToFlags
);
3601 bool PresShell::ScrollFrameRectIntoView(nsIFrame
* aFrame
, const nsRect
& aRect
,
3602 const nsMargin
& aMargin
,
3603 ScrollAxis aVertical
,
3604 ScrollAxis aHorizontal
,
3605 ScrollFlags aScrollFlags
) {
3606 bool didScroll
= false;
3607 // This function needs to work even if rect has a width or height of 0.
3608 nsRect rect
= aRect
;
3609 nsIFrame
* container
= aFrame
;
3610 // Walk up the frame hierarchy scrolling the rect into view and
3611 // keeping rect relative to container
3613 nsIScrollableFrame
* sf
= do_QueryFrame(container
);
3615 nsPoint oldPosition
= sf
->GetScrollPosition();
3616 nsRect targetRect
= rect
;
3617 // Inflate the scrolled rect by the container's padding in each dimension,
3618 // unless we have 'overflow-clip-box-*: content-box' in that dimension.
3619 auto* disp
= container
->StyleDisplay();
3620 if (disp
->mOverflowClipBoxBlock
== StyleOverflowClipBox::ContentBox
||
3621 disp
->mOverflowClipBoxInline
== StyleOverflowClipBox::ContentBox
) {
3622 WritingMode wm
= container
->GetWritingMode();
3623 bool cbH
= (wm
.IsVertical() ? disp
->mOverflowClipBoxBlock
3624 : disp
->mOverflowClipBoxInline
) ==
3625 StyleOverflowClipBox::ContentBox
;
3626 bool cbV
= (wm
.IsVertical() ? disp
->mOverflowClipBoxInline
3627 : disp
->mOverflowClipBoxBlock
) ==
3628 StyleOverflowClipBox::ContentBox
;
3629 nsMargin padding
= container
->GetUsedPadding();
3631 padding
.left
= padding
.right
= nscoord(0);
3634 padding
.top
= padding
.bottom
= nscoord(0);
3636 targetRect
.Inflate(padding
);
3639 targetRect
-= sf
->GetScrolledFrame()->GetPosition();
3642 AutoWeakFrame
wf(container
);
3643 ScrollToShowRect(sf
, targetRect
, aMargin
, aVertical
, aHorizontal
,
3645 if (!wf
.IsAlive()) {
3650 nsPoint newPosition
= sf
->LastScrollDestination();
3651 // If the scroll position increased, that means our content moved up,
3652 // so our rect's offset should decrease
3653 rect
+= oldPosition
- newPosition
;
3655 if (oldPosition
!= newPosition
) {
3659 // only scroll one container when this flag is set
3660 if (aScrollFlags
& ScrollFlags::ScrollFirstAncestorOnly
) {
3665 if (container
->IsTransformed()) {
3666 container
->GetTransformMatrix(ViewportType::Layout
, RelativeTo
{nullptr},
3669 nsLayoutUtils::TransformFrameRectToAncestor(container
, rect
, parent
);
3671 rect
+= container
->GetPosition();
3672 parent
= container
->GetParent();
3674 if (!parent
&& !(aScrollFlags
& ScrollFlags::ScrollNoParentFrames
)) {
3675 nsPoint
extraOffset(0, 0);
3676 int32_t APD
= container
->PresContext()->AppUnitsPerDevPixel();
3677 parent
= nsLayoutUtils::GetCrossDocParentFrameInProcess(container
,
3680 int32_t parentAPD
= parent
->PresContext()->AppUnitsPerDevPixel();
3681 rect
= rect
.ScaleToOtherAppUnitsRoundOut(APD
, parentAPD
);
3682 rect
+= extraOffset
;
3684 nsCOMPtr
<nsIDocShell
> docShell
=
3685 container
->PresContext()->GetDocShell();
3686 if (BrowserChild
* browserChild
= BrowserChild::GetFrom(docShell
)) {
3687 // Defer to the parent document if this is an out-of-process iframe.
3688 Unused
<< browserChild
->SendScrollRectIntoView(
3689 rect
, aVertical
, aHorizontal
, aScrollFlags
, APD
);
3694 } while (container
);
3699 void PresShell::ScheduleViewManagerFlush(PaintType aType
) {
3700 if (MOZ_UNLIKELY(mIsDestroying
)) {
3704 if (aType
== PaintType::DelayedCompress
) {
3705 // Delay paint for 1 second.
3706 static const uint32_t kPaintDelayPeriod
= 1000;
3707 if (!mDelayedPaintTimer
) {
3708 nsTimerCallbackFunc PaintTimerCallBack
= [](nsITimer
* aTimer
,
3710 // The passed-in PresShell is always alive here. Because if PresShell
3711 // died, mDelayedPaintTimer->Cancel() would be called during the
3712 // destruction and this callback would never be invoked.
3713 auto self
= static_cast<PresShell
*>(aClosure
);
3714 self
->SetNextPaintCompressed();
3715 self
->ScheduleViewManagerFlush();
3718 NS_NewTimerWithFuncCallback(
3719 getter_AddRefs(mDelayedPaintTimer
), PaintTimerCallBack
, this,
3720 kPaintDelayPeriod
, nsITimer::TYPE_ONE_SHOT
, "PaintTimerCallBack",
3721 mDocument
->EventTargetFor(TaskCategory::Other
));
3726 nsPresContext
* presContext
= GetPresContext();
3728 presContext
->RefreshDriver()->ScheduleViewManagerFlush();
3730 SetNeedLayoutFlush();
3733 void PresShell::DispatchSynthMouseMove(WidgetGUIEvent
* aEvent
) {
3734 AUTO_PROFILER_TRACING_MARKER_DOCSHELL("Paint", "DispatchSynthMouseMove",
3735 GRAPHICS
, mPresContext
->GetDocShell());
3736 nsEventStatus status
= nsEventStatus_eIgnore
;
3737 nsView
* targetView
= nsView::GetViewFor(aEvent
->mWidget
);
3738 if (!targetView
) return;
3739 RefPtr
<nsViewManager
> viewManager
= targetView
->GetViewManager();
3740 viewManager
->DispatchEvent(aEvent
, targetView
, &status
);
3743 void PresShell::ClearMouseCaptureOnView(nsView
* aView
) {
3744 if (nsIContent
* capturingContent
= GetCapturingContent()) {
3746 // if a view was specified, ensure that the captured content is within
3748 nsIFrame
* frame
= capturingContent
->GetPrimaryFrame();
3750 nsView
* view
= frame
->GetClosestView();
3751 // if there is no view, capturing won't be handled any more, so
3752 // just release the capture.
3755 if (view
== aView
) {
3756 ReleaseCapturingContent();
3757 // the view containing the captured content likely disappeared so
3758 // disable capture for now.
3759 AllowMouseCapture(false);
3763 view
= view
->GetParent();
3765 // return if the view wasn't found
3771 ReleaseCapturingContent();
3774 // disable mouse capture until the next mousedown as a dialog has opened
3775 // or a drag has started. Otherwise, someone could start capture during
3776 // the modal dialog or drag.
3777 AllowMouseCapture(false);
3780 void PresShell::ClearMouseCapture() {
3781 nsIContent
* capturingContent
= GetCapturingContent();
3782 if (!capturingContent
) {
3783 AllowMouseCapture(false);
3787 ReleaseCapturingContent();
3788 AllowMouseCapture(false);
3791 void PresShell::ClearMouseCapture(nsIFrame
* aFrame
) {
3793 aFrame
&& aFrame
->GetParent() &&
3794 aFrame
->GetParent()->Type() == LayoutFrameType::Deck
,
3795 "This function should only be called with a child frame of <deck>");
3797 nsIContent
* capturingContent
= GetCapturingContent();
3798 if (!capturingContent
) {
3799 AllowMouseCapture(false);
3803 nsIFrame
* capturingFrame
= capturingContent
->GetPrimaryFrame();
3804 if (!capturingFrame
) {
3805 ReleaseCapturingContent();
3806 AllowMouseCapture(false);
3810 if (nsLayoutUtils::IsAncestorFrameCrossDocInProcess(aFrame
, capturingFrame
)) {
3811 ReleaseCapturingContent();
3812 AllowMouseCapture(false);
3816 nsresult
PresShell::CaptureHistoryState(nsILayoutHistoryState
** aState
) {
3817 MOZ_ASSERT(nullptr != aState
, "null state pointer");
3819 // We actually have to mess with the docshell here, since we want to
3820 // store the state back in it.
3821 // XXXbz this isn't really right, since this is being called in the
3822 // content viewer's Hide() method... by that point the docshell's
3823 // state could be wrong. We should sort out a better ownership
3824 // model for the layout history state.
3825 nsCOMPtr
<nsIDocShell
> docShell(mPresContext
->GetDocShell());
3826 if (!docShell
) return NS_ERROR_FAILURE
;
3828 nsCOMPtr
<nsILayoutHistoryState
> historyState
;
3829 docShell
->GetLayoutHistoryState(getter_AddRefs(historyState
));
3830 if (!historyState
) {
3831 // Create the document state object
3832 historyState
= NS_NewLayoutHistoryState();
3833 docShell
->SetLayoutHistoryState(historyState
);
3836 *aState
= historyState
;
3837 NS_IF_ADDREF(*aState
);
3839 // Capture frame state for the entire frame hierarchy
3840 nsIFrame
* rootFrame
= mFrameConstructor
->GetRootFrame();
3841 if (!rootFrame
) return NS_OK
;
3843 mFrameConstructor
->CaptureFrameState(rootFrame
, historyState
);
3848 void PresShell::ScheduleBeforeFirstPaint() {
3849 if (!mDocument
->IsResourceDoc()) {
3850 // Notify observers that a new page is about to be drawn. Execute this
3851 // as soon as it is safe to run JS, which is guaranteed to be before we
3852 // go back to the event loop and actually draw the page.
3853 MOZ_LOG(gLog
, LogLevel::Debug
,
3854 ("PresShell::ScheduleBeforeFirstPaint this=%p", this));
3856 nsContentUtils::AddScriptRunner(
3857 new nsBeforeFirstPaintDispatcher(mDocument
));
3861 void PresShell::UnsuppressAndInvalidate() {
3862 // Note: We ignore the EnsureVisible check for resource documents, because
3863 // they won't have a docshell, so they'll always fail EnsureVisible.
3864 if ((!mDocument
->IsResourceDoc() && !mPresContext
->EnsureVisible()) ||
3866 // No point; we're about to be torn down anyway.
3870 ScheduleBeforeFirstPaint();
3872 mPaintingSuppressed
= false;
3873 nsIFrame
* rootFrame
= mFrameConstructor
->GetRootFrame();
3875 // let's assume that outline on a root frame is not supported
3876 rootFrame
->InvalidateFrame();
3879 // now that painting is unsuppressed, focus may be set on the document
3880 if (nsPIDOMWindowOuter
* win
= mDocument
->GetWindow()) {
3881 win
->SetReadyForFocus();
3884 if (!mHaveShutDown
) {
3885 SynthesizeMouseMove(false);
3886 ScheduleApproximateFrameVisibilityUpdateNow();
3890 void PresShell::CancelPaintSuppressionTimer() {
3891 if (mPaintSuppressionTimer
) {
3892 mPaintSuppressionTimer
->Cancel();
3893 mPaintSuppressionTimer
= nullptr;
3897 void PresShell::UnsuppressPainting() {
3898 CancelPaintSuppressionTimer();
3900 if (mIsDocumentGone
|| !mPaintingSuppressed
) {
3904 // If we have reflows pending, just wait until we process
3905 // the reflows and get all the frames where we want them
3906 // before actually unlocking the painting. Otherwise
3907 // go ahead and unlock now.
3908 if (!mDirtyRoots
.IsEmpty())
3909 mShouldUnsuppressPainting
= true;
3911 UnsuppressAndInvalidate();
3914 // Post a request to handle an arbitrary callback after reflow has finished.
3915 nsresult
PresShell::PostReflowCallback(nsIReflowCallback
* aCallback
) {
3916 void* result
= AllocateByObjectID(eArenaObjectID_nsCallbackEventRequest
,
3917 sizeof(nsCallbackEventRequest
));
3918 nsCallbackEventRequest
* request
= (nsCallbackEventRequest
*)result
;
3920 request
->callback
= aCallback
;
3921 request
->next
= nullptr;
3923 if (mLastCallbackEventRequest
) {
3924 mLastCallbackEventRequest
= mLastCallbackEventRequest
->next
= request
;
3926 mFirstCallbackEventRequest
= request
;
3927 mLastCallbackEventRequest
= request
;
3933 void PresShell::CancelReflowCallback(nsIReflowCallback
* aCallback
) {
3934 nsCallbackEventRequest
* before
= nullptr;
3935 nsCallbackEventRequest
* node
= mFirstCallbackEventRequest
;
3937 nsIReflowCallback
* callback
= node
->callback
;
3939 if (callback
== aCallback
) {
3940 nsCallbackEventRequest
* toFree
= node
;
3941 if (node
== mFirstCallbackEventRequest
) {
3943 mFirstCallbackEventRequest
= node
;
3944 NS_ASSERTION(before
== nullptr, "impossible");
3947 before
->next
= node
;
3950 if (toFree
== mLastCallbackEventRequest
) {
3951 mLastCallbackEventRequest
= before
;
3954 FreeByObjectID(eArenaObjectID_nsCallbackEventRequest
, toFree
);
3962 void PresShell::CancelPostedReflowCallbacks() {
3963 while (mFirstCallbackEventRequest
) {
3964 nsCallbackEventRequest
* node
= mFirstCallbackEventRequest
;
3965 mFirstCallbackEventRequest
= node
->next
;
3966 if (!mFirstCallbackEventRequest
) {
3967 mLastCallbackEventRequest
= nullptr;
3969 nsIReflowCallback
* callback
= node
->callback
;
3970 FreeByObjectID(eArenaObjectID_nsCallbackEventRequest
, node
);
3972 callback
->ReflowCallbackCanceled();
3977 void PresShell::HandlePostedReflowCallbacks(bool aInterruptible
) {
3978 bool shouldFlush
= false;
3980 while (mFirstCallbackEventRequest
) {
3981 nsCallbackEventRequest
* node
= mFirstCallbackEventRequest
;
3982 mFirstCallbackEventRequest
= node
->next
;
3983 if (!mFirstCallbackEventRequest
) {
3984 mLastCallbackEventRequest
= nullptr;
3986 nsIReflowCallback
* callback
= node
->callback
;
3987 FreeByObjectID(eArenaObjectID_nsCallbackEventRequest
, node
);
3989 if (callback
->ReflowFinished()) {
3995 FlushType flushType
=
3996 aInterruptible
? FlushType::InterruptibleLayout
: FlushType::Layout
;
3997 if (shouldFlush
&& !mIsDestroying
) {
3998 FlushPendingNotifications(flushType
);
4002 bool PresShell::IsSafeToFlush() const {
4003 // Not safe if we are getting torn down, reflowing, or in the middle of frame
4005 if (mIsReflowing
|| mChangeNestCount
|| mIsDestroying
) {
4009 // Not safe if we are painting
4010 if (nsViewManager
* viewManager
= GetViewManager()) {
4011 bool isPainting
= false;
4012 viewManager
->IsPainting(isPainting
);
4021 void PresShell::NotifyFontFaceSetOnRefresh() {
4022 if (FontFaceSet
* set
= mDocument
->GetFonts()) {
4027 void PresShell::DoFlushPendingNotifications(FlushType aType
) {
4028 // by default, flush animations if aType >= FlushType::Style
4029 mozilla::ChangesToFlush
flush(aType
, aType
>= FlushType::Style
);
4030 FlushPendingNotifications(flush
);
4034 static void AssertFrameSubtreeIsSane(const nsIFrame
& aRoot
) {
4035 if (const nsIContent
* content
= aRoot
.GetContent()) {
4036 MOZ_ASSERT(content
->GetFlattenedTreeParentNodeForStyle(),
4037 "Node not in the flattened tree still has a frame?");
4040 for (const auto& childList
: aRoot
.ChildLists()) {
4041 for (const nsIFrame
* child
: childList
.mList
) {
4042 AssertFrameSubtreeIsSane(*child
);
4048 static inline void AssertFrameTreeIsSane(const PresShell
& aPresShell
) {
4050 if (const nsIFrame
* root
= aPresShell
.GetRootFrame()) {
4051 AssertFrameSubtreeIsSane(*root
);
4056 void PresShell::DoFlushPendingNotifications(mozilla::ChangesToFlush aFlush
) {
4057 // FIXME(emilio, bug 1530177): Turn into a release assert when bug 1530188 and
4058 // bug 1530190 are fixed.
4059 MOZ_DIAGNOSTIC_ASSERT(!mForbiddenToFlush
, "This is bad!");
4061 // Per our API contract, hold a strong ref to ourselves until we return.
4062 RefPtr
<PresShell
> kungFuDeathGrip
= this;
4065 * VERY IMPORTANT: If you add some sort of new flushing to this
4066 * method, make sure to add the relevant SetNeedLayoutFlush or
4067 * SetNeedStyleFlush calls on the shell.
4069 FlushType flushType
= aFlush
.mFlushType
;
4071 MOZ_ASSERT(NeedFlush(flushType
), "Why did we get called?");
4073 AUTO_PROFILER_MARKER_TEXT(
4074 "DoFlushPendingNotifications", LAYOUT
,
4075 MarkerOptions(MarkerStack::Capture(), MarkerInnerWindowIdFromDocShell(
4076 mPresContext
->GetDocShell())),
4077 nsDependentCString(kFlushTypeNames
[flushType
]));
4078 AUTO_PROFILER_LABEL_DYNAMIC_CSTR_NONSENSITIVE(
4079 "PresShell::DoFlushPendingNotifications", LAYOUT
,
4080 kFlushTypeNames
[flushType
]);
4082 #ifdef ACCESSIBILITY
4084 if (nsAccessibilityService
* accService
= GetAccService()) {
4085 NS_ASSERTION(!accService
->IsProcessingRefreshDriverNotification(),
4086 "Flush during accessible tree update!");
4091 NS_ASSERTION(flushType
>= FlushType::Style
, "Why did we get called?");
4093 mNeedStyleFlush
= false;
4094 mNeedThrottledAnimationFlush
=
4095 mNeedThrottledAnimationFlush
&& !aFlush
.mFlushAnimations
;
4097 mNeedLayoutFlush
&& (flushType
< FlushType::InterruptibleLayout
);
4099 bool isSafeToFlush
= IsSafeToFlush();
4101 // If layout could possibly trigger scripts, then it's only safe to flush if
4102 // it's safe to run script.
4103 bool hasHadScriptObject
;
4104 if (mDocument
->GetScriptHandlingObject(hasHadScriptObject
) ||
4105 hasHadScriptObject
) {
4106 isSafeToFlush
= isSafeToFlush
&& nsContentUtils::IsSafeToRunScript();
4109 // Don't flush if the doc is already in the bfcache.
4110 if (MOZ_UNLIKELY(mDocument
->GetPresShell() != this)) {
4111 MOZ_DIAGNOSTIC_ASSERT(!mDocument
->GetPresShell(),
4112 "Where did this shell come from?");
4113 isSafeToFlush
= false;
4116 MOZ_DIAGNOSTIC_ASSERT(!mIsDestroying
|| !isSafeToFlush
);
4117 MOZ_DIAGNOSTIC_ASSERT(mIsDestroying
|| mViewManager
);
4118 MOZ_DIAGNOSTIC_ASSERT(mIsDestroying
|| mDocument
->HasShellOrBFCacheEntry());
4120 // Make sure the view manager stays alive.
4121 RefPtr
<nsViewManager
> viewManager
= mViewManager
;
4122 bool didStyleFlush
= false;
4123 bool didLayoutFlush
= false;
4124 if (isSafeToFlush
) {
4125 // Record that we are in a flush, so that our optimization in
4126 // Document::FlushPendingNotifications doesn't skip any re-entrant
4127 // calls to us. Otherwise, we might miss some needed flushes, since
4128 // we clear mNeedStyleFlush / mNeedLayoutFlush here at the top of
4129 // the function but we might not have done the work yet.
4130 AutoRestore
<bool> guard(mInFlush
);
4133 // We need to make sure external resource documents are flushed too (for
4134 // example, svg filters that reference a filter in an external document
4135 // need the frames in the external document to be constructed for the
4136 // filter to work). We only need external resources to be flushed when the
4137 // main document is flushing >= FlushType::Frames, so we flush external
4138 // resources here instead of Document::FlushPendingNotifications.
4139 mDocument
->FlushExternalResources(flushType
);
4141 // Force flushing of any pending content notifications that might have
4142 // queued up while our event was pending. That will ensure that we don't
4143 // construct frames for content right now that's still waiting to be
4145 mDocument
->FlushPendingNotifications(FlushType::ContentAndNotify
);
4147 mDocument
->UpdateSVGUseElementShadowTrees();
4149 // Process pending restyles, since any flush of the presshell wants
4150 // up-to-date style data.
4151 if (MOZ_LIKELY(!mIsDestroying
)) {
4152 viewManager
->FlushDelayedResize(false);
4153 mPresContext
->FlushPendingMediaFeatureValuesChanged();
4156 if (MOZ_LIKELY(!mIsDestroying
)) {
4157 // Now that we have flushed media queries, update the rules before looking
4158 // up @font-face / @counter-style / @font-feature-values rules.
4159 StyleSet()->UpdateStylistIfNeeded();
4161 // Flush any pending update of the user font set, since that could
4162 // cause style changes (for updating ex/ch units, and to cause a
4164 mDocument
->FlushUserFontSet();
4166 mPresContext
->FlushCounterStyles();
4168 mPresContext
->FlushFontFeatureValues();
4170 // Flush any requested SMIL samples.
4171 if (mDocument
->HasAnimationController()) {
4172 mDocument
->GetAnimationController()->FlushResampleRequests();
4175 if (aFlush
.mFlushAnimations
&& mPresContext
->EffectCompositor()) {
4176 mPresContext
->EffectCompositor()->PostRestyleForThrottledAnimations();
4180 // The FlushResampleRequests() above flushed style changes.
4181 if (MOZ_LIKELY(!mIsDestroying
)) {
4182 nsAutoScriptBlocker scriptBlocker
;
4183 Maybe
<uint64_t> innerWindowID
;
4184 if (auto* window
= mDocument
->GetInnerWindow()) {
4185 innerWindowID
= Some(window
->WindowID());
4187 AutoProfilerStyleMarker
tracingStyleFlush(std::move(mStyleCause
),
4189 PerfStats::AutoMetricRecording
<PerfStats::Metric::Styling
> autoRecording
;
4190 LAYOUT_TELEMETRY_RECORD_BASE(Restyle
);
4192 mPresContext
->RestyleManager()->ProcessPendingRestyles();
4195 // Now those constructors or events might have posted restyle
4196 // events. At the same time, we still need up-to-date style data.
4197 // In particular, reflow depends on style being completely up to
4198 // date. If it's not, then style reparenting, which can
4199 // happen during reflow, might suddenly pick up the new rules and
4200 // we'll end up with frames whose style doesn't match the frame
4202 if (MOZ_LIKELY(!mIsDestroying
)) {
4203 nsAutoScriptBlocker scriptBlocker
;
4204 Maybe
<uint64_t> innerWindowID
;
4205 if (auto* window
= mDocument
->GetInnerWindow()) {
4206 innerWindowID
= Some(window
->WindowID());
4208 AutoProfilerStyleMarker
tracingStyleFlush(std::move(mStyleCause
),
4210 PerfStats::AutoMetricRecording
<PerfStats::Metric::Styling
> autoRecording
;
4211 LAYOUT_TELEMETRY_RECORD_BASE(Restyle
);
4213 mPresContext
->RestyleManager()->ProcessPendingRestyles();
4214 // Clear mNeedStyleFlush here agagin to make this flag work properly for
4215 // optimization since the flag might have set in ProcessPendingRestyles().
4216 mNeedStyleFlush
= false;
4219 AssertFrameTreeIsSane(*this);
4221 didStyleFlush
= true;
4223 // There might be more pending constructors now, but we're not going to
4224 // worry about them. They can't be triggered during reflow, so we should
4227 if (flushType
>= (SuppressInterruptibleReflows()
4229 : FlushType::InterruptibleLayout
) &&
4231 didLayoutFlush
= true;
4232 mFrameConstructor
->RecalcQuotesAndCounters();
4233 if (ProcessReflowCommands(flushType
< FlushType::Layout
)) {
4234 if (mContentToScrollTo
) {
4235 DoScrollContentIntoView();
4236 if (mContentToScrollTo
) {
4237 mContentToScrollTo
->RemoveProperty(nsGkAtoms::scrolling
);
4238 mContentToScrollTo
= nullptr;
4244 if (flushType
>= FlushType::Layout
) {
4245 if (!mIsDestroying
) {
4246 viewManager
->UpdateWidgetGeometry();
4251 if (!didStyleFlush
&& flushType
>= FlushType::Style
&& !mIsDestroying
) {
4252 SetNeedStyleFlush();
4253 if (aFlush
.mFlushAnimations
) {
4254 SetNeedThrottledAnimationFlush();
4258 if (!didLayoutFlush
&& flushType
>= FlushType::InterruptibleLayout
&&
4260 // We suppressed this flush either due to it not being safe to flush,
4261 // or due to SuppressInterruptibleReflows(). Either way, the
4262 // mNeedLayoutFlush flag needs to be re-set.
4263 SetNeedLayoutFlush();
4266 // Update flush counters
4267 if (didStyleFlush
) {
4268 mLayoutTelemetry
.IncReqsPerFlush(FlushType::Style
);
4271 if (didLayoutFlush
) {
4272 mLayoutTelemetry
.IncReqsPerFlush(FlushType::Layout
);
4275 // Record telemetry for the number of requests per each flush type.
4277 // Flushes happen as style or style+layout. This depends upon the `flushType`
4278 // where flushType >= InterruptibleLayout means flush layout and flushType >=
4279 // Style means flush style. We only report if didLayoutFlush or didStyleFlush
4280 // is true because we care if a flush really did take place. (Flush is guarded
4281 // by `isSafeToFlush == true`.)
4282 if (flushType
>= FlushType::InterruptibleLayout
&& didLayoutFlush
) {
4283 MOZ_ASSERT(didLayoutFlush
== didStyleFlush
);
4284 mLayoutTelemetry
.PingReqsPerFlushTelemetry(FlushType::Layout
);
4285 } else if (flushType
>= FlushType::Style
&& didStyleFlush
) {
4286 MOZ_ASSERT(!didLayoutFlush
);
4287 mLayoutTelemetry
.PingReqsPerFlushTelemetry(FlushType::Style
);
4291 MOZ_CAN_RUN_SCRIPT_BOUNDARY
void PresShell::CharacterDataChanged(
4292 nsIContent
* aContent
, const CharacterDataChangeInfo
& aInfo
) {
4293 MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript());
4294 MOZ_ASSERT(!mIsDocumentGone
, "Unexpected CharacterDataChanged");
4295 MOZ_ASSERT(aContent
->OwnerDoc() == mDocument
, "Unexpected document");
4297 nsAutoCauseReflowNotifier
crNotifier(this);
4299 mPresContext
->RestyleManager()->CharacterDataChanged(aContent
, aInfo
);
4300 mFrameConstructor
->CharacterDataChanged(aContent
, aInfo
);
4303 MOZ_CAN_RUN_SCRIPT_BOUNDARY
void PresShell::ContentStateChanged(
4304 Document
* aDocument
, nsIContent
* aContent
, EventStates aStateMask
) {
4305 MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript());
4306 MOZ_ASSERT(!mIsDocumentGone
, "Unexpected ContentStateChanged");
4307 MOZ_ASSERT(aDocument
== mDocument
, "Unexpected aDocument");
4309 if (mDidInitialize
) {
4310 nsAutoCauseReflowNotifier
crNotifier(this);
4311 mPresContext
->RestyleManager()->ContentStateChanged(aContent
, aStateMask
);
4315 void PresShell::DocumentStatesChanged(EventStates aStateMask
) {
4316 MOZ_ASSERT(!mIsDocumentGone
, "Unexpected DocumentStatesChanged");
4317 MOZ_ASSERT(mDocument
);
4318 MOZ_ASSERT(!aStateMask
.IsEmpty());
4320 if (mDidInitialize
) {
4321 StyleSet()->InvalidateStyleForDocumentStateChanges(aStateMask
);
4324 if (aStateMask
.HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE
)) {
4325 if (nsIFrame
* root
= mFrameConstructor
->GetRootFrame()) {
4326 root
->SchedulePaint();
4331 MOZ_CAN_RUN_SCRIPT_BOUNDARY
void PresShell::AttributeWillChange(
4332 Element
* aElement
, int32_t aNameSpaceID
, nsAtom
* aAttribute
,
4334 MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript());
4335 MOZ_ASSERT(!mIsDocumentGone
, "Unexpected AttributeWillChange");
4336 MOZ_ASSERT(aElement
->OwnerDoc() == mDocument
, "Unexpected document");
4338 // XXXwaterson it might be more elegant to wait until after the
4339 // initial reflow to begin observing the document. That would
4340 // squelch any other inappropriate notifications as well.
4341 if (mDidInitialize
) {
4342 nsAutoCauseReflowNotifier
crNotifier(this);
4343 mPresContext
->RestyleManager()->AttributeWillChange(aElement
, aNameSpaceID
,
4344 aAttribute
, aModType
);
4348 MOZ_CAN_RUN_SCRIPT_BOUNDARY
void PresShell::AttributeChanged(
4349 Element
* aElement
, int32_t aNameSpaceID
, nsAtom
* aAttribute
,
4350 int32_t aModType
, const nsAttrValue
* aOldValue
) {
4351 MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript());
4352 MOZ_ASSERT(!mIsDocumentGone
, "Unexpected AttributeChanged");
4353 MOZ_ASSERT(aElement
->OwnerDoc() == mDocument
, "Unexpected document");
4355 // XXXwaterson it might be more elegant to wait until after the
4356 // initial reflow to begin observing the document. That would
4357 // squelch any other inappropriate notifications as well.
4358 if (mDidInitialize
) {
4359 nsAutoCauseReflowNotifier
crNotifier(this);
4360 mPresContext
->RestyleManager()->AttributeChanged(
4361 aElement
, aNameSpaceID
, aAttribute
, aModType
, aOldValue
);
4365 MOZ_CAN_RUN_SCRIPT_BOUNDARY
void PresShell::ContentAppended(
4366 nsIContent
* aFirstNewContent
) {
4367 MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript());
4368 MOZ_ASSERT(!mIsDocumentGone
, "Unexpected ContentAppended");
4369 MOZ_ASSERT(aFirstNewContent
->OwnerDoc() == mDocument
, "Unexpected document");
4371 // We never call ContentAppended with a document as the container, so we can
4372 // assert that we have an nsIContent parent.
4373 MOZ_ASSERT(aFirstNewContent
->GetParent());
4374 MOZ_ASSERT(aFirstNewContent
->GetParent()->IsElement() ||
4375 aFirstNewContent
->GetParent()->IsShadowRoot());
4377 if (!mDidInitialize
) {
4381 nsAutoCauseReflowNotifier
crNotifier(this);
4383 // Call this here so it only happens for real content mutations and
4384 // not cases when the frame constructor calls its own methods to force
4385 // frame reconstruction.
4386 mPresContext
->RestyleManager()->ContentAppended(aFirstNewContent
);
4388 mFrameConstructor
->ContentAppended(
4389 aFirstNewContent
, nsCSSFrameConstructor::InsertionKind::Async
);
4392 MOZ_CAN_RUN_SCRIPT_BOUNDARY
void PresShell::ContentInserted(
4393 nsIContent
* aChild
) {
4394 MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript());
4395 MOZ_ASSERT(!mIsDocumentGone
, "Unexpected ContentInserted");
4396 MOZ_ASSERT(aChild
->OwnerDoc() == mDocument
, "Unexpected document");
4398 if (!mDidInitialize
) {
4402 nsAutoCauseReflowNotifier
crNotifier(this);
4404 // Call this here so it only happens for real content mutations and
4405 // not cases when the frame constructor calls its own methods to force
4406 // frame reconstruction.
4407 mPresContext
->RestyleManager()->ContentInserted(aChild
);
4409 mFrameConstructor
->ContentInserted(
4410 aChild
, nsCSSFrameConstructor::InsertionKind::Async
);
4413 MOZ_CAN_RUN_SCRIPT_BOUNDARY
void PresShell::ContentRemoved(
4414 nsIContent
* aChild
, nsIContent
* aPreviousSibling
) {
4415 MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript());
4416 MOZ_ASSERT(!mIsDocumentGone
, "Unexpected ContentRemoved");
4417 MOZ_ASSERT(aChild
->OwnerDoc() == mDocument
, "Unexpected document");
4418 nsINode
* container
= aChild
->GetParentNode();
4420 // Notify the ESM that the content has been removed, so that
4421 // it can clean up any state related to the content.
4423 mPresContext
->EventStateManager()->ContentRemoved(mDocument
, aChild
);
4425 nsAutoCauseReflowNotifier
crNotifier(this);
4427 // Call this here so it only happens for real content mutations and
4428 // not cases when the frame constructor calls its own methods to force
4429 // frame reconstruction.
4430 nsIContent
* oldNextSibling
= nullptr;
4432 // Editor calls into here with NAC via HTMLEditor::DeleteRefToAnonymousNode.
4433 // This could be asserted if that caller is fixed.
4434 if (MOZ_LIKELY(!aChild
->IsRootOfNativeAnonymousSubtree())) {
4435 oldNextSibling
= aPreviousSibling
? aPreviousSibling
->GetNextSibling()
4436 : container
->GetFirstChild();
4439 // After removing aChild from tree we should save information about live
4441 if (mPointerEventTarget
&&
4442 mPointerEventTarget
->IsInclusiveDescendantOf(aChild
)) {
4443 mPointerEventTarget
= aChild
->GetParent();
4446 mFrameConstructor
->ContentRemoved(aChild
, oldNextSibling
,
4447 nsCSSFrameConstructor::REMOVE_CONTENT
);
4449 // NOTE(emilio): It's important that this goes after the frame constructor
4450 // stuff, otherwise the frame constructor can't see elements which are
4451 // display: contents / display: none, because we'd have cleared all the style
4453 mPresContext
->RestyleManager()->ContentRemoved(aChild
, oldNextSibling
);
4456 void PresShell::NotifyCounterStylesAreDirty() {
4457 // TODO: Looks like that nsFrameConstructor::NotifyCounterStylesAreDirty()
4458 // does not run script. If so, we don't need to block script with
4459 // nsAutoCauseReflowNotifier here. Instead, there should be methods
4460 // and stack only class which manages only mChangeNestCount for
4461 // avoiding unnecessary `MOZ_CAN_RUN_SCRIPT` marking.
4462 nsAutoCauseReflowNotifier
reflowNotifier(this);
4463 mFrameConstructor
->NotifyCounterStylesAreDirty();
4466 bool PresShell::FrameIsAncestorOfDirtyRoot(nsIFrame
* aFrame
) const {
4467 return mDirtyRoots
.FrameIsAncestorOfDirtyRoot(aFrame
);
4470 void PresShell::ReconstructFrames() {
4471 MOZ_ASSERT(!mFrameConstructor
->GetRootFrame() || mDidInitialize
,
4472 "Must not have root frame before initial reflow");
4473 if (!mDidInitialize
|| mIsDestroying
) {
4474 // Nothing to do here
4478 // Have to make sure that the content notifications are flushed before we
4479 // start messing with the frame model; otherwise we can get content doubling.
4481 // Also make sure that styles are flushed before calling into the frame
4482 // constructor, since that's what it expects.
4483 mDocument
->FlushPendingNotifications(FlushType::Style
);
4485 if (mIsDestroying
) {
4489 nsAutoCauseReflowNotifier
crNotifier(this);
4490 mFrameConstructor
->ReconstructDocElementHierarchy(
4491 nsCSSFrameConstructor::InsertionKind::Sync
);
4494 nsresult
PresShell::RenderDocument(const nsRect
& aRect
,
4495 RenderDocumentFlags aFlags
,
4496 nscolor aBackgroundColor
,
4497 gfxContext
* aThebesContext
) {
4498 NS_ENSURE_TRUE(!(aFlags
& RenderDocumentFlags::IsUntrusted
),
4499 NS_ERROR_NOT_IMPLEMENTED
);
4501 nsRootPresContext
* rootPresContext
= mPresContext
->GetRootPresContext();
4502 if (rootPresContext
) {
4503 rootPresContext
->FlushWillPaintObservers();
4504 if (mIsDestroying
) return NS_OK
;
4507 nsAutoScriptBlocker blockScripts
;
4509 // Set up the rectangle as the path in aThebesContext
4510 gfxRect
r(0, 0, nsPresContext::AppUnitsToFloatCSSPixels(aRect
.width
),
4511 nsPresContext::AppUnitsToFloatCSSPixels(aRect
.height
));
4512 aThebesContext
->NewPath();
4513 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
4514 aThebesContext
->SnappedRectangle(r
);
4516 aThebesContext
->Rectangle(r
);
4519 nsIFrame
* rootFrame
= mFrameConstructor
->GetRootFrame();
4521 // Nothing to paint, just fill the rect
4522 aThebesContext
->SetColor(sRGBColor::FromABGR(aBackgroundColor
));
4523 aThebesContext
->Fill();
4527 gfxContextAutoSaveRestore
save(aThebesContext
);
4529 MOZ_ASSERT(aThebesContext
->CurrentOp() == CompositionOp::OP_OVER
);
4531 aThebesContext
->Clip();
4533 nsDeviceContext
* devCtx
= mPresContext
->DeviceContext();
4535 gfxPoint
offset(-nsPresContext::AppUnitsToFloatCSSPixels(aRect
.x
),
4536 -nsPresContext::AppUnitsToFloatCSSPixels(aRect
.y
));
4538 gfxFloat(devCtx
->AppUnitsPerDevPixel()) / AppUnitsPerCSSPixel();
4540 // Since canvas APIs use floats to set up their matrices, we may have some
4541 // slight rounding errors here. We use NudgeToIntegers() here to adjust
4542 // matrix components that are integers up to the accuracy of floats to be
4544 gfxMatrix newTM
= aThebesContext
->CurrentMatrixDouble()
4545 .PreTranslate(offset
)
4546 .PreScale(scale
, scale
)
4548 aThebesContext
->SetMatrixDouble(newTM
);
4550 AutoSaveRestoreRenderingState
_(this);
4552 bool wouldFlushRetainedLayers
= false;
4553 PaintFrameFlags flags
= PaintFrameFlags::IgnoreSuppression
;
4554 if (aThebesContext
->CurrentMatrix().HasNonIntegerTranslation()) {
4555 flags
|= PaintFrameFlags::InTransform
;
4557 if (!(aFlags
& RenderDocumentFlags::AsyncDecodeImages
)) {
4558 flags
|= PaintFrameFlags::SyncDecodeImages
;
4560 if (aFlags
& RenderDocumentFlags::UseHighQualityScaling
) {
4561 flags
|= PaintFrameFlags::UseHighQualityScaling
;
4563 if (aFlags
& RenderDocumentFlags::UseWidgetLayers
) {
4564 // We only support using widget layers on display root's with widgets.
4565 nsView
* view
= rootFrame
->GetView();
4566 if (view
&& view
->GetWidget() &&
4567 nsLayoutUtils::GetDisplayRootFrame(rootFrame
) == rootFrame
) {
4568 LayerManager
* layerManager
= view
->GetWidget()->GetLayerManager();
4569 // ClientLayerManagers or WebRenderLayerManagers in content processes
4570 // don't support taking snapshots.
4572 (!layerManager
->AsKnowsCompositor() || XRE_IsParentProcess())) {
4573 flags
|= PaintFrameFlags::WidgetLayers
;
4577 if (!(aFlags
& RenderDocumentFlags::DrawCaret
)) {
4578 wouldFlushRetainedLayers
= true;
4579 flags
|= PaintFrameFlags::HideCaret
;
4581 if (aFlags
& RenderDocumentFlags::IgnoreViewportScrolling
) {
4582 wouldFlushRetainedLayers
= !IgnoringViewportScrolling();
4583 mRenderingStateFlags
|= RenderingStateFlags::IgnoringViewportScrolling
;
4585 if (aFlags
& RenderDocumentFlags::ResetViewportScrolling
) {
4586 wouldFlushRetainedLayers
= true;
4587 flags
|= PaintFrameFlags::ResetViewportScrolling
;
4589 if (aFlags
& RenderDocumentFlags::DrawWindowNotFlushing
) {
4590 mRenderingStateFlags
|= RenderingStateFlags::DrawWindowNotFlushing
;
4592 if (aFlags
& RenderDocumentFlags::DocumentRelative
) {
4593 // XXX be smarter about this ... drawWindow might want a rect
4594 // that's "pretty close" to what our retained layer tree covers.
4595 // In that case, it wouldn't disturb normal rendering too much,
4596 // and we should allow it.
4597 wouldFlushRetainedLayers
= true;
4598 flags
|= PaintFrameFlags::DocumentRelative
;
4601 // Don't let drawWindow blow away our retained layer tree
4602 if ((flags
& PaintFrameFlags::WidgetLayers
) && wouldFlushRetainedLayers
) {
4603 flags
&= ~PaintFrameFlags::WidgetLayers
;
4606 nsLayoutUtils::PaintFrame(aThebesContext
, rootFrame
, nsRegion(aRect
),
4608 nsDisplayListBuilderMode::Painting
, flags
);
4614 * Clip the display list aList to a range. Returns the clipped
4615 * rectangle surrounding the range.
4617 nsRect
PresShell::ClipListToRange(nsDisplayListBuilder
* aBuilder
,
4618 nsDisplayList
* aList
, nsRange
* aRange
) {
4619 // iterate though the display items and add up the bounding boxes of each.
4620 // This will allow the total area of the frames within the range to be
4621 // determined. To do this, remove an item from the bottom of the list, check
4622 // whether it should be part of the range, and if so, append it to the top
4623 // of the temporary list tmpList. If the item is a text frame at the end of
4624 // the selection range, clip it to the portion of the text frame that is
4625 // part of the selection. Then, append the wrapper to the top of the list.
4626 // Otherwise, just delete the item and don't append it.
4628 nsDisplayList tmpList
;
4631 while ((i
= aList
->RemoveBottom())) {
4632 if (i
->GetType() == DisplayItemType::TYPE_CONTAINER
) {
4633 tmpList
.AppendToTop(i
);
4634 surfaceRect
.UnionRect(
4635 surfaceRect
, ClipListToRange(aBuilder
, i
->GetChildren(), aRange
));
4639 // itemToInsert indiciates the item that should be inserted into the
4640 // temporary list. If null, no item should be inserted.
4641 nsDisplayItem
* itemToInsert
= nullptr;
4642 nsIFrame
* frame
= i
->Frame();
4643 nsIContent
* content
= frame
->GetContent();
4645 bool atStart
= (content
== aRange
->GetStartContainer());
4646 bool atEnd
= (content
== aRange
->GetEndContainer());
4647 if ((atStart
|| atEnd
) && frame
->IsTextFrame()) {
4648 auto [frameStartOffset
, frameEndOffset
] = frame
->GetOffsets();
4650 int32_t hilightStart
=
4651 atStart
? std::max(static_cast<int32_t>(aRange
->StartOffset()),
4654 int32_t hilightEnd
=
4655 atEnd
? std::min(static_cast<int32_t>(aRange
->EndOffset()),
4658 if (hilightStart
< hilightEnd
) {
4659 // determine the location of the start and end edges of the range.
4660 nsPoint startPoint
, endPoint
;
4661 frame
->GetPointFromOffset(hilightStart
, &startPoint
);
4662 frame
->GetPointFromOffset(hilightEnd
, &endPoint
);
4664 // The clip rectangle is determined by taking the the start and
4665 // end points of the range, offset from the reference frame.
4666 // Because of rtl, the end point may be to the left of (or above,
4667 // in vertical mode) the start point, so x (or y) is set to the
4668 // lower of the values.
4669 nsRect
textRect(aBuilder
->ToReferenceFrame(frame
), frame
->GetSize());
4670 if (frame
->GetWritingMode().IsVertical()) {
4671 nscoord y
= std::min(startPoint
.y
, endPoint
.y
);
4673 textRect
.height
= std::max(startPoint
.y
, endPoint
.y
) - y
;
4675 nscoord x
= std::min(startPoint
.x
, endPoint
.x
);
4677 textRect
.width
= std::max(startPoint
.x
, endPoint
.x
) - x
;
4679 surfaceRect
.UnionRect(surfaceRect
, textRect
);
4681 const ActiveScrolledRoot
* asr
= i
->GetActiveScrolledRoot();
4683 DisplayItemClip newClip
;
4684 newClip
.SetTo(textRect
);
4686 const DisplayItemClipChain
* newClipChain
=
4687 aBuilder
->AllocateDisplayItemClipChain(newClip
, asr
, nullptr);
4689 i
->IntersectClip(aBuilder
, newClipChain
, true);
4693 // Don't try to descend into subdocuments.
4694 // If this ever changes we'd need to add handling for subdocuments with
4695 // different zoom levels.
4696 else if (content
->GetUncomposedDoc() ==
4697 aRange
->GetStartContainer()->GetUncomposedDoc()) {
4698 // if the node is within the range, append it to the temporary list
4701 RangeUtils::CompareNodeToRange(content
, aRange
, &before
, &after
);
4702 if (NS_SUCCEEDED(rv
) && !before
&& !after
) {
4705 surfaceRect
.UnionRect(surfaceRect
, i
->GetBounds(aBuilder
, &snap
));
4710 // insert the item into the list if necessary. If the item has a child
4711 // list, insert that as well
4712 nsDisplayList
* sublist
= i
->GetSameCoordinateSystemChildren();
4713 if (itemToInsert
|| sublist
) {
4714 tmpList
.AppendToTop(itemToInsert
? itemToInsert
: i
);
4715 // if the item is a list, iterate over it as well
4717 surfaceRect
.UnionRect(surfaceRect
,
4718 ClipListToRange(aBuilder
, sublist
, aRange
));
4720 // otherwise, just delete the item and don't readd it to the list
4721 i
->Destroy(aBuilder
);
4725 // now add all the items back onto the original list again
4726 aList
->AppendToTop(&tmpList
);
4734 static bool gDumpRangePaintList
= false;
4737 UniquePtr
<RangePaintInfo
> PresShell::CreateRangePaintInfo(
4738 nsRange
* aRange
, nsRect
& aSurfaceRect
, bool aForPrimarySelection
) {
4739 nsIFrame
* ancestorFrame
= nullptr;
4740 nsIFrame
* rootFrame
= GetRootFrame();
4742 // If the start or end of the range is the document, just use the root
4743 // frame, otherwise get the common ancestor of the two endpoints of the
4745 nsINode
* startContainer
= aRange
->GetStartContainer();
4746 nsINode
* endContainer
= aRange
->GetEndContainer();
4747 Document
* doc
= startContainer
->GetComposedDoc();
4748 if (startContainer
== doc
|| endContainer
== doc
) {
4749 ancestorFrame
= rootFrame
;
4751 nsINode
* ancestor
= nsContentUtils::GetClosestCommonInclusiveAncestor(
4752 startContainer
, endContainer
);
4753 NS_ASSERTION(!ancestor
|| ancestor
->IsContent(),
4754 "common ancestor is not content");
4756 while (ancestor
&& ancestor
->IsContent()) {
4757 ancestorFrame
= ancestor
->AsContent()->GetPrimaryFrame();
4758 if (ancestorFrame
) {
4762 ancestor
= ancestor
->GetParentOrShadowHostNode();
4765 // use the nearest ancestor frame that includes all continuations as the
4766 // root for building the display list
4767 while (ancestorFrame
&&
4768 nsLayoutUtils::GetNextContinuationOrIBSplitSibling(ancestorFrame
))
4769 ancestorFrame
= ancestorFrame
->GetParent();
4772 if (!ancestorFrame
) {
4776 // get a display list containing the range
4777 auto info
= MakeUnique
<RangePaintInfo
>(aRange
, ancestorFrame
);
4778 info
->mBuilder
.SetIncludeAllOutOfFlows();
4779 if (aForPrimarySelection
) {
4780 info
->mBuilder
.SetSelectedFramesOnly();
4782 info
->mBuilder
.EnterPresShell(ancestorFrame
);
4784 ContentSubtreeIterator subtreeIter
;
4785 nsresult rv
= subtreeIter
.Init(aRange
);
4786 if (NS_FAILED(rv
)) {
4790 auto BuildDisplayListForNode
= [&](nsINode
* aNode
) {
4791 if (MOZ_UNLIKELY(!aNode
->IsContent())) {
4794 nsIFrame
* frame
= aNode
->AsContent()->GetPrimaryFrame();
4795 // XXX deal with frame being null due to display:contents
4797 frame
= nsLayoutUtils::GetNextContinuationOrIBSplitSibling(frame
)) {
4798 info
->mBuilder
.SetVisibleRect(frame
->InkOverflowRect());
4799 info
->mBuilder
.SetDirtyRect(frame
->InkOverflowRect());
4800 frame
->BuildDisplayListForStackingContext(&info
->mBuilder
, &info
->mList
);
4803 if (startContainer
->NodeType() == nsINode::TEXT_NODE
) {
4804 BuildDisplayListForNode(startContainer
);
4806 for (; !subtreeIter
.IsDone(); subtreeIter
.Next()) {
4807 nsCOMPtr
<nsINode
> node
= subtreeIter
.GetCurrentNode();
4808 BuildDisplayListForNode(node
);
4810 if (endContainer
!= startContainer
&&
4811 endContainer
->NodeType() == nsINode::TEXT_NODE
) {
4812 BuildDisplayListForNode(endContainer
);
4815 // If one of the ancestor presShells (including this one) has a resolution
4816 // set, we may have some APZ zoom applied. That means we may want to rasterize
4817 // the nodes at that zoom level. Populate `info` with the relevant information
4818 // so that the caller can decide what to do. Also wrap the display list in
4819 // appropriate nsDisplayAsyncZoom display items. This code handles the general
4820 // case with nested async zooms (even though that never actually happens),
4821 // because it fell out of the implementation for free.
4823 // TODO: Do we need to do the same for ancestor transforms?
4824 for (nsPresContext
* ctx
= GetPresContext(); ctx
;
4825 ctx
= ctx
->GetParentPresContext()) {
4826 PresShell
* shell
= ctx
->PresShell();
4827 float resolution
= shell
->GetResolution();
4829 // If we are at the root document in the process, try to see if documents
4830 // in enclosing processes have a resolution and include that as well.
4831 if (!ctx
->GetParentPresContext()) {
4832 // xScale is an arbitrary choice. Outside of edge cases involving CSS
4833 // transforms, xScale == yScale so it doesn't matter.
4834 resolution
*= ViewportUtils::TryInferEnclosingResolution(shell
).xScale
;
4837 if (resolution
== 1.0) {
4841 info
->mResolution
*= resolution
;
4842 nsIFrame
* rootScrollFrame
= shell
->GetRootScrollFrame();
4844 nsLayoutUtils::FindOrCreateIDFor(rootScrollFrame
->GetContent());
4846 nsDisplayList wrapped
;
4847 wrapped
.AppendNewToTop
<nsDisplayAsyncZoom
>(&info
->mBuilder
, rootScrollFrame
,
4848 &info
->mList
, nullptr, zoomedId
);
4849 info
->mList
.AppendToTop(&wrapped
);
4853 if (gDumpRangePaintList
) {
4854 fprintf(stderr
, "CreateRangePaintInfo --- before ClipListToRange:\n");
4855 nsIFrame::PrintDisplayList(&(info
->mBuilder
), info
->mList
);
4859 nsRect rangeRect
= ClipListToRange(&info
->mBuilder
, &info
->mList
, aRange
);
4861 info
->mBuilder
.LeavePresShell(ancestorFrame
, &info
->mList
);
4864 if (gDumpRangePaintList
) {
4865 fprintf(stderr
, "CreateRangePaintInfo --- after ClipListToRange:\n");
4866 nsIFrame::PrintDisplayList(&(info
->mBuilder
), info
->mList
);
4870 // determine the offset of the reference frame for the display list
4871 // to the root frame. This will allow the coordinates used when painting
4872 // to all be offset from the same point
4873 info
->mRootOffset
= ancestorFrame
->GetBoundingClientRect().TopLeft();
4874 rangeRect
.MoveBy(info
->mRootOffset
);
4875 aSurfaceRect
.UnionRect(aSurfaceRect
, rangeRect
);
4880 already_AddRefed
<SourceSurface
> PresShell::PaintRangePaintInfo(
4881 const nsTArray
<UniquePtr
<RangePaintInfo
>>& aItems
, Selection
* aSelection
,
4882 const Maybe
<CSSIntRegion
>& aRegion
, nsRect aArea
,
4883 const LayoutDeviceIntPoint aPoint
, LayoutDeviceIntRect
* aScreenRect
,
4884 RenderImageFlags aFlags
) {
4885 nsPresContext
* pc
= GetPresContext();
4886 if (!pc
|| aArea
.width
== 0 || aArea
.height
== 0) return nullptr;
4888 // use the rectangle to create the surface
4889 LayoutDeviceIntRect pixelArea
= LayoutDeviceIntRect::FromAppUnitsToOutside(
4890 aArea
, pc
->AppUnitsPerDevPixel());
4892 // if the image should not be resized, scale must be 1
4896 pc
->DeviceContext()->GetClientRect(maxSize
);
4898 // check if the image should be resized
4899 bool resize
= !!(aFlags
& RenderImageFlags::AutoScale
);
4902 // check if image-resizing-algorithm should be used
4903 if (aFlags
& RenderImageFlags::IsImage
) {
4904 // get max screensize
4905 int32_t maxWidth
= pc
->AppUnitsToDevPixels(maxSize
.width
);
4906 int32_t maxHeight
= pc
->AppUnitsToDevPixels(maxSize
.height
);
4907 // resize image relative to the screensize
4908 // get best height/width relative to screensize
4909 float bestHeight
= float(maxHeight
) * RELATIVE_SCALEFACTOR
;
4910 float bestWidth
= float(maxWidth
) * RELATIVE_SCALEFACTOR
;
4911 // calculate scale for bestWidth
4912 float adjustedScale
= bestWidth
/ float(pixelArea
.width
);
4913 // get the worst height (height when width is perfect)
4914 float worstHeight
= float(pixelArea
.height
) * adjustedScale
;
4915 // get the difference of best and worst height
4916 float difference
= bestHeight
- worstHeight
;
4917 // halve the difference and add it to worstHeight to get
4918 // the best compromise between bestHeight and bestWidth,
4919 // then calculate the corresponding scale factor
4920 adjustedScale
= (worstHeight
+ difference
/ 2) / float(pixelArea
.height
);
4921 // prevent upscaling
4922 scale
= std::min(scale
, adjustedScale
);
4924 // get half of max screensize
4925 int32_t maxWidth
= pc
->AppUnitsToDevPixels(maxSize
.width
>> 1);
4926 int32_t maxHeight
= pc
->AppUnitsToDevPixels(maxSize
.height
>> 1);
4927 if (pixelArea
.width
> maxWidth
|| pixelArea
.height
> maxHeight
) {
4928 // divide the maximum size by the image size in both directions.
4929 // Whichever direction produces the smallest result determines how much
4930 // should be scaled.
4931 if (pixelArea
.width
> maxWidth
)
4932 scale
= std::min(scale
, float(maxWidth
) / pixelArea
.width
);
4933 if (pixelArea
.height
> maxHeight
)
4934 scale
= std::min(scale
, float(maxHeight
) / pixelArea
.height
);
4938 // Pick a resolution scale factor that is the highest we need for any of
4939 // the items. This means some items may get rendered at a higher-than-needed
4940 // resolution but at least nothing will be avoidably blurry.
4941 float resolutionScale
= 1.0;
4942 for (const UniquePtr
<RangePaintInfo
>& rangeInfo
: aItems
) {
4943 resolutionScale
= std::max(resolutionScale
, rangeInfo
->mResolution
);
4945 float unclampedResolution
= resolutionScale
;
4946 // Clamp the resolution scale so that `pixelArea` when scaled by `scale` and
4947 // `resolutionScale` isn't bigger than `maxSize`. This prevents creating
4948 // giant/unbounded images.
4950 std::min(resolutionScale
, maxSize
.width
/ (scale
* pixelArea
.width
));
4952 std::min(resolutionScale
, maxSize
.height
/ (scale
* pixelArea
.height
));
4953 // The following assert should only get hit if pixelArea scaled by `scale`
4954 // alone would already have been bigger than `maxSize`, which should never
4955 // be the case. For release builds we handle gracefully by reverting
4956 // resolutionScale to 1.0 to avoid unexpected consequences.
4957 MOZ_ASSERT(resolutionScale
>= 1.0);
4958 resolutionScale
= std::max(1.0f
, resolutionScale
);
4960 scale
*= resolutionScale
;
4962 // Now we need adjust the output screen position of the surface based on the
4963 // scaling factor and any APZ zoom that may be in effect. The goal is here
4964 // to set `aScreenRect`'s top-left corner (in screen-relative LD pixels)
4965 // such that the scaling effect on the surface appears anchored at `aPoint`
4966 // ("anchor" here is like "transform-origin"). When this code is e.g. used
4967 // to generate a drag image for dragging operations, `aPoint` refers to the
4968 // position of the mouse cursor (also in screen-relative LD pixels), and the
4969 // user-visible effect of doing this is that the point at which the user
4970 // clicked to start the drag remains under the mouse during the drag.
4972 // In order to do this we first compute the top-left corner of the
4973 // pixelArea is screen-relative LD pixels.
4974 LayoutDevicePoint visualPoint
= ViewportUtils::ToScreenRelativeVisual(
4975 LayoutDevicePoint(pixelArea
.TopLeft()), pc
);
4976 // And then adjust the output screen position based on that, which we can do
4977 // since everything here is screen-relative LD pixels. Note that the scale
4978 // factor we use here is the effective "transform" scale applied to the
4979 // content we're painting, relative to the scale at which it would normally
4980 // get painted at as part of page rendering (`unclampedResolution`).
4981 float scaleRelativeToNormalContent
= scale
/ unclampedResolution
;
4982 aScreenRect
->x
= NSToIntFloor(aPoint
.x
- float(aPoint
.x
- visualPoint
.x
) *
4983 scaleRelativeToNormalContent
);
4984 aScreenRect
->y
= NSToIntFloor(aPoint
.y
- float(aPoint
.y
- visualPoint
.y
) *
4985 scaleRelativeToNormalContent
);
4987 pixelArea
.width
= NSToIntFloor(float(pixelArea
.width
) * scale
);
4988 pixelArea
.height
= NSToIntFloor(float(pixelArea
.height
) * scale
);
4989 if (!pixelArea
.width
|| !pixelArea
.height
) {
4993 // move aScreenRect to the position of the surface in screen coordinates
4994 LayoutDevicePoint visualPoint
= ViewportUtils::ToScreenRelativeVisual(
4995 LayoutDevicePoint(pixelArea
.TopLeft()), pc
);
4996 aScreenRect
->MoveTo(RoundedToInt(visualPoint
));
4998 aScreenRect
->width
= pixelArea
.width
;
4999 aScreenRect
->height
= pixelArea
.height
;
5001 RefPtr
<DrawTarget
> dt
=
5002 gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
5003 IntSize(pixelArea
.width
, pixelArea
.height
), SurfaceFormat::B8G8R8A8
);
5004 if (!dt
|| !dt
->IsValid()) {
5008 RefPtr
<gfxContext
> ctx
= gfxContext::CreateOrNull(dt
);
5009 MOZ_ASSERT(ctx
); // already checked the draw target above
5012 RefPtr
<PathBuilder
> builder
= dt
->CreatePathBuilder(FillRule::FILL_WINDING
);
5014 // Convert aRegion from CSS pixels to dev pixels
5015 nsIntRegion region
= aRegion
->ToAppUnits(AppUnitsPerCSSPixel())
5016 .ToOutsidePixels(pc
->AppUnitsPerDevPixel());
5017 for (auto iter
= region
.RectIter(); !iter
.Done(); iter
.Next()) {
5018 const IntRect
& rect
= iter
.Get();
5020 builder
->MoveTo(rect
.TopLeft());
5021 builder
->LineTo(rect
.TopRight());
5022 builder
->LineTo(rect
.BottomRight());
5023 builder
->LineTo(rect
.BottomLeft());
5024 builder
->LineTo(rect
.TopLeft());
5027 RefPtr
<Path
> path
= builder
->Finish();
5031 gfxMatrix initialTM
= ctx
->CurrentMatrixDouble();
5034 initialTM
.PreScale(scale
, scale
);
5037 // translate so that points are relative to the surface area
5038 gfxPoint surfaceOffset
= nsLayoutUtils::PointToGfxPoint(
5039 -aArea
.TopLeft(), pc
->AppUnitsPerDevPixel());
5040 initialTM
.PreTranslate(surfaceOffset
);
5042 // temporarily hide the selection so that text is drawn normally. If a
5043 // selection is being rendered, use that, otherwise use the presshell's
5045 RefPtr
<nsFrameSelection
> frameSelection
;
5047 frameSelection
= aSelection
->GetFrameSelection();
5049 frameSelection
= FrameSelection();
5051 int16_t oldDisplaySelection
= frameSelection
->GetDisplaySelection();
5052 frameSelection
->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN
);
5054 // next, paint each range in the selection
5055 for (const UniquePtr
<RangePaintInfo
>& rangeInfo
: aItems
) {
5056 // the display lists paint relative to the offset from the reference
5057 // frame, so account for that translation too:
5058 gfxPoint rootOffset
= nsLayoutUtils::PointToGfxPoint(
5059 rangeInfo
->mRootOffset
, pc
->AppUnitsPerDevPixel());
5060 ctx
->SetMatrixDouble(initialTM
.PreTranslate(rootOffset
));
5061 aArea
.MoveBy(-rangeInfo
->mRootOffset
.x
, -rangeInfo
->mRootOffset
.y
);
5062 nsRegion
visible(aArea
);
5063 RefPtr
<LayerManager
> layerManager
= rangeInfo
->mList
.PaintRoot(
5064 &rangeInfo
->mBuilder
, ctx
, nsDisplayList::PAINT_DEFAULT
, Nothing());
5065 aArea
.MoveBy(rangeInfo
->mRootOffset
.x
, rangeInfo
->mRootOffset
.y
);
5068 // restore the old selection display state
5069 frameSelection
->SetDisplaySelection(oldDisplaySelection
);
5071 return dt
->Snapshot();
5074 already_AddRefed
<SourceSurface
> PresShell::RenderNode(
5075 nsINode
* aNode
, const Maybe
<CSSIntRegion
>& aRegion
,
5076 const LayoutDeviceIntPoint aPoint
, LayoutDeviceIntRect
* aScreenRect
,
5077 RenderImageFlags aFlags
) {
5078 // area will hold the size of the surface needed to draw the node, measured
5079 // from the root frame.
5081 nsTArray
<UniquePtr
<RangePaintInfo
>> rangeItems
;
5083 // nothing to draw if the node isn't in a document
5084 if (!aNode
->IsInComposedDoc()) {
5088 RefPtr
<nsRange
> range
= nsRange::Create(aNode
);
5089 IgnoredErrorResult rv
;
5090 range
->SelectNode(*aNode
, rv
);
5095 UniquePtr
<RangePaintInfo
> info
= CreateRangePaintInfo(range
, area
, false);
5097 // XXX(Bug 1631371) Check if this should use a fallible operation as it
5098 // pretended earlier, or change the return type to void.
5099 rangeItems
.AppendElement(std::move(info
));
5102 Maybe
<CSSIntRegion
> region
= aRegion
;
5104 // combine the area with the supplied region
5105 CSSIntRect rrectPixels
= region
->GetBounds();
5107 nsRect rrect
= ToAppUnits(rrectPixels
, AppUnitsPerCSSPixel());
5108 area
.IntersectRect(area
, rrect
);
5110 nsPresContext
* pc
= GetPresContext();
5111 if (!pc
) return nullptr;
5113 // move the region so that it is offset from the topleft corner of the
5115 region
->MoveBy(-nsPresContext::AppUnitsToIntCSSPixels(area
.x
),
5116 -nsPresContext::AppUnitsToIntCSSPixels(area
.y
));
5119 return PaintRangePaintInfo(rangeItems
, nullptr, region
, area
, aPoint
,
5120 aScreenRect
, aFlags
);
5123 already_AddRefed
<SourceSurface
> PresShell::RenderSelection(
5124 Selection
* aSelection
, const LayoutDeviceIntPoint aPoint
,
5125 LayoutDeviceIntRect
* aScreenRect
, RenderImageFlags aFlags
) {
5126 // area will hold the size of the surface needed to draw the selection,
5127 // measured from the root frame.
5129 nsTArray
<UniquePtr
<RangePaintInfo
>> rangeItems
;
5131 // iterate over each range and collect them into the rangeItems array.
5132 // This is done so that the size of selection can be determined so as
5133 // to allocate a surface area
5134 uint32_t numRanges
= aSelection
->RangeCount();
5135 NS_ASSERTION(numRanges
> 0, "RenderSelection called with no selection");
5137 for (uint32_t r
= 0; r
< numRanges
; r
++) {
5138 RefPtr
<nsRange
> range
= aSelection
->GetRangeAt(r
);
5140 UniquePtr
<RangePaintInfo
> info
= CreateRangePaintInfo(range
, area
, true);
5142 // XXX(Bug 1631371) Check if this should use a fallible operation as it
5143 // pretended earlier.
5144 rangeItems
.AppendElement(std::move(info
));
5148 return PaintRangePaintInfo(rangeItems
, aSelection
, Nothing(), area
, aPoint
,
5149 aScreenRect
, aFlags
);
5152 void PresShell::AddPrintPreviewBackgroundItem(nsDisplayListBuilder
* aBuilder
,
5153 nsDisplayList
* aList
,
5155 const nsRect
& aBounds
) {
5156 aList
->AppendNewToBottom
<nsDisplaySolidColor
>(aBuilder
, aFrame
, aBounds
,
5157 NS_RGB(115, 115, 115));
5160 static bool AddCanvasBackgroundColor(const nsDisplayList
* aList
,
5161 nsIFrame
* aCanvasFrame
, nscolor aColor
,
5162 bool aCSSBackgroundColor
) {
5163 for (nsDisplayItem
* i
: *aList
) {
5164 const DisplayItemType type
= i
->GetType();
5166 if (i
->Frame() == aCanvasFrame
&&
5167 type
== DisplayItemType::TYPE_CANVAS_BACKGROUND_COLOR
) {
5168 auto* bg
= static_cast<nsDisplayCanvasBackgroundColor
*>(i
);
5169 bg
->SetExtraBackgroundColor(aColor
);
5173 const bool isBlendContainer
=
5174 type
== DisplayItemType::TYPE_BLEND_CONTAINER
||
5175 type
== DisplayItemType::TYPE_TABLE_BLEND_CONTAINER
;
5177 nsDisplayList
* sublist
= i
->GetSameCoordinateSystemChildren();
5178 if (sublist
&& !(isBlendContainer
&& !aCSSBackgroundColor
) &&
5179 AddCanvasBackgroundColor(sublist
, aCanvasFrame
, aColor
,
5180 aCSSBackgroundColor
))
5186 void PresShell::AddCanvasBackgroundColorItem(
5187 nsDisplayListBuilder
* aBuilder
, nsDisplayList
* aList
, nsIFrame
* aFrame
,
5188 const nsRect
& aBounds
, nscolor aBackstopColor
,
5189 AddCanvasBackgroundColorFlags aFlags
) {
5190 if (aBounds
.IsEmpty()) {
5193 // We don't want to add an item for the canvas background color if the frame
5194 // (sub)tree we are painting doesn't include any canvas frames. There isn't
5195 // an easy way to check this directly, but if we check if the root of the
5196 // (sub)tree we are painting is a canvas frame that should cover us in all
5197 // cases (it will usually be a viewport frame when we have a canvas frame in
5199 if (!(aFlags
& AddCanvasBackgroundColorFlags::ForceDraw
) &&
5200 !nsCSSRendering::IsCanvasFrame(aFrame
)) {
5204 nscolor bgcolor
= NS_ComposeColors(aBackstopColor
, mCanvasBackgroundColor
);
5205 if (NS_GET_A(bgcolor
) == 0) return;
5207 // To make layers work better, we want to avoid having a big non-scrolled
5208 // color background behind a scrolled transparent background. Instead,
5209 // we'll try to move the color background into the scrolled content
5210 // by making nsDisplayCanvasBackground paint it.
5211 // If we're only adding an unscrolled item, then pretend that we've
5213 bool addedScrollingBackgroundColor
=
5214 !!(aFlags
& AddCanvasBackgroundColorFlags::AppendUnscrolledOnly
);
5215 if (!aFrame
->GetParent() && !addedScrollingBackgroundColor
) {
5216 nsIScrollableFrame
* sf
=
5217 aFrame
->PresShell()->GetRootScrollFrameAsScrollable();
5219 nsCanvasFrame
* canvasFrame
= do_QueryFrame(sf
->GetScrolledFrame());
5220 if (canvasFrame
&& canvasFrame
->IsVisibleForPainting()) {
5221 addedScrollingBackgroundColor
= AddCanvasBackgroundColor(
5222 aList
, canvasFrame
, bgcolor
, mHasCSSBackgroundColor
);
5227 // With async scrolling, we'd like to have two instances of the background
5228 // color: one that scrolls with the content (for the reasons stated above),
5229 // and one underneath which does not scroll with the content, but which can
5230 // be shown during checkerboarding and overscroll.
5231 // We can only do that if the color is opaque.
5232 bool forceUnscrolledItem
=
5233 nsLayoutUtils::UsesAsyncScrolling(aFrame
) && NS_GET_A(bgcolor
) == 255;
5235 if (!addedScrollingBackgroundColor
|| forceUnscrolledItem
) {
5236 aList
->AppendNewToBottom
<nsDisplaySolidColor
>(aBuilder
, aFrame
, aBounds
,
5241 static bool IsTransparentContainerElement(nsPresContext
* aPresContext
) {
5242 nsCOMPtr
<nsIDocShell
> docShell
= aPresContext
->GetDocShell();
5247 nsCOMPtr
<nsPIDOMWindowOuter
> pwin
= docShell
->GetWindow();
5248 if (!pwin
) return false;
5249 nsCOMPtr
<Element
> containerElement
= pwin
->GetFrameElementInternal();
5251 BrowserChild
* tab
= BrowserChild::GetFrom(docShell
);
5253 // Check if presShell is the top PresShell. Only the top can
5254 // influence the canvas background color.
5255 if (aPresContext
->GetPresShell() != tab
->GetTopLevelPresShell()) {
5260 return (containerElement
&& containerElement
->HasAttr(
5261 kNameSpaceID_None
, nsGkAtoms::transparent
)) ||
5262 (tab
&& tab
->IsTransparent());
5265 nscolor
PresShell::GetDefaultBackgroundColorToDraw() {
5266 if (!mPresContext
|| !mPresContext
->GetBackgroundColorDraw()) {
5267 return NS_RGB(255, 255, 255);
5270 nscolor backgroundColor
= mPresContext
->DefaultBackgroundColor();
5271 if (backgroundColor
!= NS_RGB(255, 255, 255)) {
5272 // Return non-default color.
5273 return backgroundColor
;
5276 // Use a dark background for top-level about:blank that is inaccessible to
5278 Document
* doc
= GetDocument();
5279 BrowsingContext
* bc
= doc
->GetBrowsingContext();
5280 if (bc
&& bc
->IsTop() && !bc
->HasOpener() && doc
->GetDocumentURI() &&
5281 NS_IsAboutBlank(doc
->GetDocumentURI()) &&
5282 doc
->PrefersColorScheme(Document::IgnoreRFP::Yes
) ==
5283 StylePrefersColorScheme::Dark
) {
5284 // Use --in-content-page-background for prefers-color-scheme: dark.
5285 return StaticPrefs::browser_proton_enabled() ? NS_RGB(0x1C, 0x1B, 0x22)
5286 : NS_RGB(0x2A, 0x2A, 0x2E);
5289 return backgroundColor
;
5292 void PresShell::UpdateCanvasBackground() {
5293 // If we have a frame tree and it has style information that
5294 // specifies the background color of the canvas, update our local
5295 // cache of that color.
5296 nsIFrame
* rootStyleFrame
= FrameConstructor()->GetRootElementStyleFrame();
5297 if (rootStyleFrame
) {
5298 ComputedStyle
* bgStyle
=
5299 nsCSSRendering::FindRootFrameBackground(rootStyleFrame
);
5300 // XXX We should really be passing the canvasframe, not the root element
5301 // style frame but we don't have access to the canvasframe here. It isn't
5302 // a problem because only a few frames can return something other than true
5303 // and none of them would be a canvas frame or root element style frame.
5304 bool drawBackgroundImage
= false;
5305 bool drawBackgroundColor
= false;
5306 const nsStyleDisplay
* disp
= rootStyleFrame
->StyleDisplay();
5307 StyleAppearance appearance
= disp
->EffectiveAppearance();
5308 if (rootStyleFrame
->IsThemed(disp
) &&
5309 appearance
!= StyleAppearance::MozWinGlass
&&
5310 appearance
!= StyleAppearance::MozWinBorderlessGlass
) {
5311 // Ignore the CSS background-color if -moz-appearance is used and it is
5312 // not one of the glass values. (Windows 7 Glass has traditionally not
5313 // overridden background colors, so we preserve that behavior for now.)
5314 mCanvasBackgroundColor
= NS_RGBA(0, 0, 0, 0);
5316 mCanvasBackgroundColor
= nsCSSRendering::DetermineBackgroundColor(
5317 mPresContext
, bgStyle
, rootStyleFrame
, drawBackgroundImage
,
5318 drawBackgroundColor
);
5320 mHasCSSBackgroundColor
= drawBackgroundColor
;
5321 if (mPresContext
->IsRootContentDocumentCrossProcess() &&
5322 !IsTransparentContainerElement(mPresContext
)) {
5323 mCanvasBackgroundColor
= NS_ComposeColors(
5324 GetDefaultBackgroundColorToDraw(), mCanvasBackgroundColor
);
5328 // If the root element of the document (ie html) has style 'display: none'
5329 // then the document's background color does not get drawn; cache the
5330 // color we actually draw.
5331 if (!FrameConstructor()->GetRootElementFrame()) {
5332 mCanvasBackgroundColor
= GetDefaultBackgroundColorToDraw();
5336 nscolor
PresShell::ComputeBackstopColor(nsView
* aDisplayRoot
) {
5337 nsIWidget
* widget
= aDisplayRoot
->GetWidget();
5338 if (widget
&& (widget
->GetTransparencyMode() != eTransparencyOpaque
||
5339 widget
->WidgetPaintsBackground())) {
5340 // Within a transparent widget, so the backstop color must be
5341 // totally transparent.
5342 return NS_RGBA(0, 0, 0, 0);
5344 // Within an opaque widget (or no widget at all), so the backstop
5345 // color must be totally opaque. The user's default background
5346 // as reported by the prescontext is guaranteed to be opaque.
5347 return GetDefaultBackgroundColorToDraw();
5350 struct PaintParams
{
5351 nscolor mBackgroundColor
;
5354 LayerManager
* PresShell::GetLayerManager() {
5355 NS_ASSERTION(mViewManager
, "Should have view manager");
5357 nsView
* rootView
= mViewManager
->GetRootView();
5359 if (nsIWidget
* widget
= rootView
->GetWidget()) {
5360 return widget
->GetLayerManager();
5366 bool PresShell::AsyncPanZoomEnabled() {
5367 NS_ASSERTION(mViewManager
, "Should have view manager");
5368 nsView
* rootView
= mViewManager
->GetRootView();
5370 if (nsIWidget
* widget
= rootView
->GetWidget()) {
5371 return widget
->AsyncPanZoomEnabled();
5374 return gfxPlatform::AsyncPanZoomEnabled();
5377 nsresult
PresShell::SetResolutionAndScaleTo(float aResolution
,
5378 ResolutionChangeOrigin aOrigin
) {
5379 if (!(aResolution
> 0.0)) {
5380 return NS_ERROR_ILLEGAL_VALUE
;
5382 if (aResolution
== mResolution
.valueOr(0.0)) {
5383 MOZ_ASSERT(mResolution
.isSome());
5387 // GetResolution handles mResolution being nothing by returning 1 so this
5388 // is checking that the resolution is actually changing.
5389 bool resolutionUpdated
= (aResolution
!= GetResolution());
5391 mLastResolutionChangeOrigin
= aOrigin
;
5393 RenderingState
state(this);
5394 state
.mResolution
= Some(aResolution
);
5395 SetRenderingState(state
);
5396 if (mMobileViewportManager
) {
5397 mMobileViewportManager
->ResolutionUpdated(aOrigin
);
5399 if (aOrigin
== ResolutionChangeOrigin::Apz
) {
5400 mResolutionUpdatedByApz
= true;
5401 } else if (resolutionUpdated
) {
5402 mResolutionUpdated
= true;
5405 if (auto* window
= nsGlobalWindowInner::Cast(mDocument
->GetInnerWindow())) {
5406 window
->VisualViewport()->PostResizeEvent();
5412 float PresShell::GetCumulativeResolution() const {
5413 float resolution
= GetResolution();
5414 nsPresContext
* parentCtx
= GetPresContext()->GetParentPresContext();
5416 resolution
*= parentCtx
->PresShell()->GetCumulativeResolution();
5421 void PresShell::SetRestoreResolution(float aResolution
,
5422 LayoutDeviceIntSize aDisplaySize
) {
5423 if (mMobileViewportManager
) {
5424 mMobileViewportManager
->SetRestoreResolution(aResolution
, aDisplaySize
);
5428 void PresShell::SetRenderingState(const RenderingState
& aState
) {
5429 if (mRenderingStateFlags
!= aState
.mRenderingStateFlags
) {
5430 // Rendering state changed in a way that forces us to flush any
5431 // retained layers we might already have.
5432 LayerManager
* manager
= GetLayerManager();
5434 FrameLayerBuilder::InvalidateAllLayers(manager
);
5438 if (GetResolution() != aState
.mResolution
.valueOr(1.f
)) {
5439 if (nsIFrame
* frame
= GetRootFrame()) {
5440 frame
->SchedulePaint();
5444 mRenderingStateFlags
= aState
.mRenderingStateFlags
;
5445 mResolution
= aState
.mResolution
;
5448 void PresShell::SynthesizeMouseMove(bool aFromScroll
) {
5449 if (!StaticPrefs::layout_reflow_synthMouseMove()) return;
5451 if (mPaintingSuppressed
|| !mIsActive
|| !mPresContext
) {
5455 if (!mPresContext
->IsRoot()) {
5456 if (PresShell
* rootPresShell
= GetRootPresShell()) {
5457 rootPresShell
->SynthesizeMouseMove(aFromScroll
);
5462 if (mMouseLocation
== nsPoint(NS_UNCONSTRAINEDSIZE
, NS_UNCONSTRAINEDSIZE
))
5465 if (!mSynthMouseMoveEvent
.IsPending()) {
5466 RefPtr
<nsSynthMouseMoveEvent
> ev
=
5467 new nsSynthMouseMoveEvent(this, aFromScroll
);
5469 GetPresContext()->RefreshDriver()->AddRefreshObserver(
5470 ev
, FlushType::Display
, "Synthetic mouse move event");
5471 mSynthMouseMoveEvent
= std::move(ev
);
5476 * Find the first floating view with a frame and a widget in a postorder
5477 * traversal of the view tree that contains the point. Thus more deeply nested
5478 * floating views are preferred over their ancestors, and floating views earlier
5479 * in the view hierarchy (i.e., added later) are preferred over their siblings.
5480 * This is adequate for finding the "topmost" floating view under a point, given
5481 * that floating views don't supporting having a specific z-index.
5483 * We cannot exit early when aPt is outside the view bounds, because floating
5484 * views aren't necessarily included in their parent's bounds, so this could
5485 * traverse the entire view hierarchy --- use carefully.
5487 * aPt is relative aRelativeToView with the viewport type
5488 * aRelativeToViewportType. aRelativeToView will always have a frame. If aView
5489 * has a frame then aRelativeToView will be aView. (The reason aRelativeToView
5490 * and aView are separate is because we need to traverse into views without
5491 * frames (ie the inner view of a subdocument frame) but we can only easily
5492 * transform between views using TransformPoint which takes frames.)
5494 static nsView
* FindFloatingViewContaining(nsView
* aRelativeToView
,
5495 ViewportType aRelativeToViewportType
,
5496 nsView
* aView
, nsPoint aPt
) {
5497 MOZ_ASSERT(aRelativeToView
->GetFrame());
5499 if (aView
->GetVisibility() == nsViewVisibility_kHide
) {
5500 // No need to look into descendants.
5504 bool crossingZoomBoundary
= false;
5506 nsIFrame
* frame
= aView
->GetFrame();
5508 if (!frame
->IsVisibleConsideringAncestors(
5509 nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY
) ||
5510 !frame
->PresShell()->IsActive()) {
5514 // We start out in visual coords and then if we cross the zoom boundary we
5515 // become in layout coords. The zoom boundary always occurs in a document
5516 // with IsRootContentDocumentCrossProcess. The root view of such a document
5517 // is outside the zoom boundary and any child view must be inside the zoom
5518 // boundary because we only create views for certain kinds of frames and
5519 // none of them can be between the root frame and the zoom boundary.
5520 if (aRelativeToViewportType
== ViewportType::Visual
) {
5521 if (!aRelativeToView
->GetParent() ||
5522 aRelativeToView
->GetViewManager() !=
5523 aRelativeToView
->GetParent()->GetViewManager()) {
5524 if (aRelativeToView
->GetFrame()
5526 ->IsRootContentDocumentCrossProcess()) {
5527 crossingZoomBoundary
= true;
5532 ViewportType nextRelativeToViewportType
= aRelativeToViewportType
;
5533 if (crossingZoomBoundary
) {
5534 nextRelativeToViewportType
= ViewportType::Layout
;
5537 nsLayoutUtils::TransformResult result
= nsLayoutUtils::TransformPoint(
5538 RelativeTo
{aRelativeToView
->GetFrame(), aRelativeToViewportType
},
5539 RelativeTo
{frame
, nextRelativeToViewportType
}, aPt
);
5540 if (result
!= nsLayoutUtils::TRANSFORM_SUCCEEDED
) {
5544 aRelativeToView
= aView
;
5545 aRelativeToViewportType
= nextRelativeToViewportType
;
5548 for (nsView
* v
= aView
->GetFirstChild(); v
; v
= v
->GetNextSibling()) {
5549 nsView
* r
= FindFloatingViewContaining(aRelativeToView
,
5550 aRelativeToViewportType
, v
, aPt
);
5554 if (!frame
|| !aView
->GetFloating() || !aView
->HasWidget()) {
5558 // Even though aPt is in visual coordinates until we cross the zoom boundary
5559 // it is valid to compare it to view coords (which are in layout coords)
5560 // because visual coords are the same as layout coords for every view outside
5561 // of the zoom boundary except for the root view of the root content document.
5562 // For the root view of the root content document, its bounds don't actually
5563 // correspond to what is visible when we have a MobileViewportManager. So we
5564 // skip the hit test. This is okay because the point has already been hit
5565 // test: 1) if we are the root view in the process then the point comes from a
5566 // real mouse event so it must have been over our widget, or 2) if we are the
5567 // root of a subdocument then hittesting against the view of the subdocument
5568 // frame that contains us already happened and succeeded before getting here.
5569 if (!crossingZoomBoundary
) {
5570 if (aView
->GetDimensions().Contains(aPt
)) {
5579 * This finds the first view with a frame that contains the given point in a
5580 * postorder traversal of the view tree, assuming that the point is not in a
5581 * floating view. It assumes that only floating views extend outside the bounds
5584 * This methods should only be called if FindFloatingViewContaining returns
5587 * aPt is relative aRelativeToView with the viewport type
5588 * aRelativeToViewportType. aRelativeToView will always have a frame. If aView
5589 * has a frame then aRelativeToView will be aView. (The reason aRelativeToView
5590 * and aView are separate is because we need to traverse into views without
5591 * frames (ie the inner view of a subdocument frame) but we can only easily
5592 * transform between views using TransformPoint which takes frames.)
5594 static nsView
* FindViewContaining(nsView
* aRelativeToView
,
5595 ViewportType aRelativeToViewportType
,
5596 nsView
* aView
, nsPoint aPt
) {
5597 MOZ_ASSERT(aRelativeToView
->GetFrame());
5599 if (aView
->GetVisibility() == nsViewVisibility_kHide
) {
5603 nsIFrame
* frame
= aView
->GetFrame();
5605 if (!frame
->IsVisibleConsideringAncestors(
5606 nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY
) ||
5607 !frame
->PresShell()->IsActive()) {
5611 // We start out in visual coords and then if we cross the zoom boundary we
5612 // become in layout coords. The zoom boundary always occurs in a document
5613 // with IsRootContentDocumentCrossProcess. The root view of such a document
5614 // is outside the zoom boundary and any child view must be inside the zoom
5615 // boundary because we only create views for certain kinds of frames and
5616 // none of them can be between the root frame and the zoom boundary.
5617 bool crossingZoomBoundary
= false;
5618 if (aRelativeToViewportType
== ViewportType::Visual
) {
5619 if (!aRelativeToView
->GetParent() ||
5620 aRelativeToView
->GetViewManager() !=
5621 aRelativeToView
->GetParent()->GetViewManager()) {
5622 if (aRelativeToView
->GetFrame()
5624 ->IsRootContentDocumentCrossProcess()) {
5625 crossingZoomBoundary
= true;
5630 ViewportType nextRelativeToViewportType
= aRelativeToViewportType
;
5631 if (crossingZoomBoundary
) {
5632 nextRelativeToViewportType
= ViewportType::Layout
;
5635 nsLayoutUtils::TransformResult result
= nsLayoutUtils::TransformPoint(
5636 RelativeTo
{aRelativeToView
->GetFrame(), aRelativeToViewportType
},
5637 RelativeTo
{frame
, nextRelativeToViewportType
}, aPt
);
5638 if (result
!= nsLayoutUtils::TRANSFORM_SUCCEEDED
) {
5642 // Even though aPt is in visual coordinates until we cross the zoom boundary
5643 // it is valid to compare it to view coords (which are in layout coords)
5644 // because visual coords are the same as layout coords for every view
5645 // outside of the zoom boundary except for the root view of the root content
5647 // For the root view of the root content document, its bounds don't
5648 // actually correspond to what is visible when we have a
5649 // MobileViewportManager. So we skip the hit test. This is okay because the
5650 // point has already been hit test: 1) if we are the root view in the
5651 // process then the point comes from a real mouse event so it must have been
5652 // over our widget, or 2) if we are the root of a subdocument then
5653 // hittesting against the view of the subdocument frame that contains us
5654 // already happened and succeeded before getting here.
5655 if (!crossingZoomBoundary
) {
5656 if (!aView
->GetDimensions().Contains(aPt
)) {
5661 aRelativeToView
= aView
;
5662 aRelativeToViewportType
= nextRelativeToViewportType
;
5665 for (nsView
* v
= aView
->GetFirstChild(); v
; v
= v
->GetNextSibling()) {
5667 FindViewContaining(aRelativeToView
, aRelativeToViewportType
, v
, aPt
);
5671 return frame
? aView
: nullptr;
5674 static BrowserBridgeChild
* GetChildBrowser(nsView
* aView
) {
5678 nsIFrame
* frame
= aView
->GetFrame();
5679 if (!frame
&& aView
->GetParent()) {
5680 // If frame is null then view is an anonymous inner view, and we want
5681 // the frame from the corresponding outer view.
5682 frame
= aView
->GetParent()->GetFrame();
5684 if (!frame
|| !frame
->GetContent()) {
5687 return BrowserBridgeChild::GetFrom(frame
->GetContent());
5690 void PresShell::ProcessSynthMouseMoveEvent(bool aFromScroll
) {
5691 // If drag session has started, we shouldn't synthesize mousemove event.
5692 nsCOMPtr
<nsIDragSession
> dragSession
= nsContentUtils::GetDragSession();
5694 mSynthMouseMoveEvent
.Forget();
5698 // allow new event to be posted while handling this one only if the
5699 // source of the event is a scroll (to prevent infinite reflow loops)
5701 mSynthMouseMoveEvent
.Forget();
5704 nsView
* rootView
= mViewManager
? mViewManager
->GetRootView() : nullptr;
5705 if (mMouseLocation
== nsPoint(NS_UNCONSTRAINEDSIZE
, NS_UNCONSTRAINEDSIZE
) ||
5706 !rootView
|| !rootView
->HasWidget() || !mPresContext
) {
5707 mSynthMouseMoveEvent
.Forget();
5711 NS_ASSERTION(mPresContext
->IsRoot(), "Only a root pres shell should be here");
5713 // Hold a ref to ourselves so DispatchEvent won't destroy us (since
5714 // we need to access members after we call DispatchEvent).
5715 RefPtr
<PresShell
> kungFuDeathGrip(this);
5717 #ifdef DEBUG_MOUSE_LOCATION
5718 printf("[ps=%p]synthesizing mouse move to (%d,%d)\n", this, mMouseLocation
.x
,
5722 int32_t APD
= mPresContext
->AppUnitsPerDevPixel();
5724 // We need a widget to put in the event we are going to dispatch so we look
5725 // for a view that has a widget and the mouse location is over. We first look
5726 // for floating views, if there isn't one we use the root view. |view| holds
5728 nsView
* view
= nullptr;
5730 // The appunits per devpixel ratio of |view|.
5733 // mRefPoint will be mMouseLocation relative to the widget of |view|, the
5734 // widget we will put in the event we dispatch, in viewAPD appunits
5735 nsPoint
refpoint(0, 0);
5737 // We always dispatch the event to the pres shell that contains the view that
5738 // the mouse is over. pointVM is the VM of that pres shell.
5739 nsViewManager
* pointVM
= nullptr;
5741 // This could be a bit slow (traverses entire view hierarchy)
5742 // but it's OK to do it once per synthetic mouse event
5743 if (rootView
->GetFrame()) {
5744 view
= FindFloatingViewContaining(rootView
, ViewportType::Visual
, rootView
,
5747 nsView
* pointView
= view
;
5750 if (rootView
->GetFrame()) {
5751 pointView
= FindViewContaining(rootView
, ViewportType::Visual
, rootView
,
5754 pointView
= rootView
;
5756 // pointView can be null in situations related to mouse capture
5757 pointVM
= (pointView
? pointView
: view
)->GetViewManager();
5758 refpoint
= mMouseLocation
+ rootView
->ViewToWidgetOffset();
5761 pointVM
= view
->GetViewManager();
5762 nsIFrame
* frame
= view
->GetFrame();
5763 NS_ASSERTION(frame
, "floating views can't be anonymous");
5764 viewAPD
= frame
->PresContext()->AppUnitsPerDevPixel();
5765 refpoint
= mMouseLocation
;
5766 DebugOnly
<nsLayoutUtils::TransformResult
> result
=
5767 nsLayoutUtils::TransformPoint(
5768 RelativeTo
{rootView
->GetFrame(), ViewportType::Visual
},
5769 RelativeTo
{frame
, ViewportType::Layout
}, refpoint
);
5770 MOZ_ASSERT(result
== nsLayoutUtils::TRANSFORM_SUCCEEDED
);
5771 refpoint
+= view
->ViewToWidgetOffset();
5773 NS_ASSERTION(view
->GetWidget(), "view should have a widget here");
5774 WidgetMouseEvent
event(true, eMouseMove
, view
->GetWidget(),
5775 WidgetMouseEvent::eSynthesized
);
5777 LayoutDeviceIntPoint::FromAppUnitsToNearest(refpoint
, viewAPD
);
5778 event
.mTime
= PR_IntervalNow();
5779 // XXX set event.mModifiers ?
5780 // XXX mnakano I think that we should get the latest information from widget.
5782 if (BrowserBridgeChild
* bbc
= GetChildBrowser(pointView
)) {
5783 // If we have a BrowserBridgeChild, we're going to be dispatching this
5784 // mouse event into an OOP iframe of the current document.
5785 event
.mLayersId
= bbc
->GetLayersId();
5786 bbc
->SendDispatchSynthesizedMouseEvent(event
);
5787 } else if (RefPtr
<PresShell
> presShell
= pointVM
->GetPresShell()) {
5788 // Since this gets run in a refresh tick there isn't an InputAPZContext on
5789 // the stack from the nsBaseWidget. We need to simulate one with at least
5790 // the correct target guid, so that the correct callback transform gets
5791 // applied if this event goes to a child process. The input block id is set
5792 // to 0 because this is a synthetic event which doesn't really belong to any
5793 // input block. Same for the APZ response field.
5794 InputAPZContext
apzContext(mMouseEventTargetGuid
, 0, nsEventStatus_eIgnore
);
5795 presShell
->DispatchSynthMouseMove(&event
);
5799 mSynthMouseMoveEvent
.Forget();
5804 void PresShell::MarkFramesInListApproximatelyVisible(
5805 const nsDisplayList
& aList
) {
5806 for (nsDisplayItem
* item
: aList
) {
5807 nsDisplayList
* sublist
= item
->GetChildren();
5809 MarkFramesInListApproximatelyVisible(*sublist
);
5813 nsIFrame
* frame
= item
->Frame();
5816 if (!frame
->TrackingVisibility()) {
5820 // Use the presshell containing the frame.
5821 PresShell
* presShell
= frame
->PresShell();
5822 MOZ_ASSERT(!presShell
->AssumeAllFramesVisible());
5823 if (presShell
->mApproximatelyVisibleFrames
.EnsureInserted(frame
)) {
5824 // The frame was added to mApproximatelyVisibleFrames, so increment its
5826 frame
->IncApproximateVisibleCount();
5832 void PresShell::DecApproximateVisibleCount(
5833 VisibleFrames
& aFrames
, const Maybe
<OnNonvisible
>& aNonvisibleAction
5834 /* = Nothing() */) {
5835 for (nsIFrame
* frame
: aFrames
) {
5836 // Decrement the frame's visible count if we're still tracking its
5837 // visibility. (We may not be, if the frame disabled visibility tracking
5838 // after we added it to the visible frames list.)
5839 if (frame
->TrackingVisibility()) {
5840 frame
->DecApproximateVisibleCount(aNonvisibleAction
);
5845 void PresShell::RebuildApproximateFrameVisibilityDisplayList(
5846 const nsDisplayList
& aList
) {
5847 MOZ_ASSERT(!mApproximateFrameVisibilityVisited
, "already visited?");
5848 mApproximateFrameVisibilityVisited
= true;
5850 // Remove the entries of the mApproximatelyVisibleFrames hashtable and put
5851 // them in oldApproxVisibleFrames.
5852 VisibleFrames oldApproximatelyVisibleFrames
=
5853 std::move(mApproximatelyVisibleFrames
);
5855 MarkFramesInListApproximatelyVisible(aList
);
5857 DecApproximateVisibleCount(oldApproximatelyVisibleFrames
);
5861 void PresShell::ClearApproximateFrameVisibilityVisited(nsView
* aView
,
5863 nsViewManager
* vm
= aView
->GetViewManager();
5865 PresShell
* presShell
= vm
->GetPresShell();
5866 if (!presShell
->mApproximateFrameVisibilityVisited
) {
5867 presShell
->ClearApproximatelyVisibleFramesList();
5869 presShell
->mApproximateFrameVisibilityVisited
= false;
5871 for (nsView
* v
= aView
->GetFirstChild(); v
; v
= v
->GetNextSibling()) {
5872 ClearApproximateFrameVisibilityVisited(v
, v
->GetViewManager() != vm
);
5876 void PresShell::ClearApproximatelyVisibleFramesList(
5877 const Maybe
<OnNonvisible
>& aNonvisibleAction
5878 /* = Nothing() */) {
5879 DecApproximateVisibleCount(mApproximatelyVisibleFrames
, aNonvisibleAction
);
5880 mApproximatelyVisibleFrames
.Clear();
5883 void PresShell::MarkFramesInSubtreeApproximatelyVisible(
5884 nsIFrame
* aFrame
, const nsRect
& aRect
, bool aRemoveOnly
/* = false */) {
5885 MOZ_ASSERT(aFrame
->PresShell() == this, "wrong presshell");
5887 if (aFrame
->TrackingVisibility() && aFrame
->StyleVisibility()->IsVisible() &&
5889 aFrame
->GetVisibility() == Visibility::ApproximatelyVisible
)) {
5890 MOZ_ASSERT(!AssumeAllFramesVisible());
5891 if (mApproximatelyVisibleFrames
.EnsureInserted(aFrame
)) {
5892 // The frame was added to mApproximatelyVisibleFrames, so increment its
5894 aFrame
->IncApproximateVisibleCount();
5898 nsSubDocumentFrame
* subdocFrame
= do_QueryFrame(aFrame
);
5900 PresShell
* presShell
= subdocFrame
->GetSubdocumentPresShellForPainting(
5901 nsSubDocumentFrame::IGNORE_PAINT_SUPPRESSION
);
5902 if (presShell
&& !presShell
->AssumeAllFramesVisible()) {
5903 nsRect rect
= aRect
;
5904 nsIFrame
* root
= presShell
->GetRootFrame();
5906 rect
.MoveBy(aFrame
->GetOffsetToCrossDoc(root
));
5908 rect
.MoveBy(-aFrame
->GetContentRectRelativeToSelf().TopLeft());
5910 rect
= rect
.ScaleToOtherAppUnitsRoundOut(
5911 aFrame
->PresContext()->AppUnitsPerDevPixel(),
5912 presShell
->GetPresContext()->AppUnitsPerDevPixel());
5914 presShell
->RebuildApproximateFrameVisibility(&rect
);
5919 nsRect rect
= aRect
;
5921 nsIScrollableFrame
* scrollFrame
= do_QueryFrame(aFrame
);
5923 bool ignoreDisplayPort
= false;
5924 if (DisplayPortUtils::IsMissingDisplayPortBaseRect(aFrame
->GetContent())) {
5925 // We can properly set the base rect for root scroll frames on top level
5926 // and root content documents. Otherwise the base rect we compute might
5927 // be way too big without the limiting that
5928 // ScrollFrameHelper::DecideScrollableLayer does, so we just ignore the
5929 // displayport in that case.
5930 nsPresContext
* pc
= aFrame
->PresContext();
5931 if (scrollFrame
->IsRootScrollFrameOfDocument() &&
5932 (pc
->IsRootContentDocumentCrossProcess() ||
5933 (pc
->IsChrome() && !pc
->GetParentPresContext()))) {
5935 nsPoint(), nsLayoutUtils::CalculateCompositionSizeForFrame(aFrame
));
5936 DisplayPortUtils::SetDisplayPortBase(aFrame
->GetContent(), baseRect
);
5938 ignoreDisplayPort
= true;
5943 bool usingDisplayport
=
5944 !ignoreDisplayPort
&&
5945 DisplayPortUtils::GetDisplayPortForVisibilityTesting(
5946 aFrame
->GetContent(), &displayPort
);
5948 scrollFrame
->NotifyApproximateFrameVisibilityUpdate(!usingDisplayport
);
5950 if (usingDisplayport
) {
5953 rect
= rect
.Intersect(scrollFrame
->GetScrollPortRect());
5955 rect
= scrollFrame
->ExpandRectToNearlyVisible(rect
);
5958 bool preserves3DChildren
= aFrame
->Extend3DContext();
5960 // We assume all frames in popups are visible, so we skip them here.
5961 const nsIFrame::ChildListIDs skip
= {nsIFrame::kPopupList
,
5962 nsIFrame::kSelectPopupList
};
5963 for (const auto& [list
, listID
] : aFrame
->ChildLists()) {
5964 if (skip
.contains(listID
)) {
5968 for (nsIFrame
* child
: list
) {
5969 nsRect r
= rect
- child
->GetPosition();
5970 if (!r
.IntersectRect(r
, child
->InkOverflowRect())) {
5973 if (child
->IsTransformed()) {
5974 // for children of a preserve3d element we just pass down the same dirty
5976 if (!preserves3DChildren
||
5977 !child
->Combines3DTransformWithAncestors()) {
5978 const nsRect overflow
= child
->InkOverflowRectRelativeToSelf();
5980 if (nsDisplayTransform::UntransformRect(r
, overflow
, child
, &out
)) {
5987 MarkFramesInSubtreeApproximatelyVisible(child
, r
);
5992 void PresShell::RebuildApproximateFrameVisibility(
5993 nsRect
* aRect
, bool aRemoveOnly
/* = false */) {
5994 MOZ_ASSERT(!mApproximateFrameVisibilityVisited
, "already visited?");
5995 mApproximateFrameVisibilityVisited
= true;
5997 nsIFrame
* rootFrame
= GetRootFrame();
6002 // Remove the entries of the mApproximatelyVisibleFrames hashtable and put
6003 // them in oldApproximatelyVisibleFrames.
6004 VisibleFrames oldApproximatelyVisibleFrames
=
6005 std::move(mApproximatelyVisibleFrames
);
6007 nsRect
vis(nsPoint(0, 0), rootFrame
->GetSize());
6012 MarkFramesInSubtreeApproximatelyVisible(rootFrame
, vis
, aRemoveOnly
);
6014 DecApproximateVisibleCount(oldApproximatelyVisibleFrames
);
6017 void PresShell::UpdateApproximateFrameVisibility() {
6018 DoUpdateApproximateFrameVisibility(/* aRemoveOnly = */ false);
6021 void PresShell::DoUpdateApproximateFrameVisibility(bool aRemoveOnly
) {
6023 !mPresContext
|| mPresContext
->IsRootContentDocumentInProcess(),
6024 "Updating approximate frame visibility on a non-root content document?");
6026 mUpdateApproximateFrameVisibilityEvent
.Revoke();
6028 if (mHaveShutDown
|| mIsDestroying
) {
6032 // call update on that frame
6033 nsIFrame
* rootFrame
= GetRootFrame();
6035 ClearApproximatelyVisibleFramesList(Some(OnNonvisible::DiscardImages
));
6039 RebuildApproximateFrameVisibility(/* aRect = */ nullptr, aRemoveOnly
);
6040 ClearApproximateFrameVisibilityVisited(rootFrame
->GetView(), true);
6042 #ifdef DEBUG_FRAME_VISIBILITY_DISPLAY_LIST
6043 // This can be used to debug the frame walker by comparing beforeFrameList
6044 // and mApproximatelyVisibleFrames in RebuildFrameVisibilityDisplayList to see
6045 // if they produce the same results (mApproximatelyVisibleFrames holds the
6046 // frames the display list thinks are visible, beforeFrameList holds the
6047 // frames the frame walker thinks are visible).
6048 nsDisplayListBuilder
builder(
6049 rootFrame
, nsDisplayListBuilderMode::FRAME_VISIBILITY
, false);
6050 nsRect
updateRect(nsPoint(0, 0), rootFrame
->GetSize());
6051 nsIFrame
* rootScroll
= GetRootScrollFrame();
6053 nsIContent
* content
= rootScroll
->GetContent();
6055 Unused
<< nsLayoutUtils::GetDisplayPortForVisibilityTesting(
6056 content
, &updateRect
, RelativeTo::ScrollFrame
);
6059 if (IgnoringViewportScrolling()) {
6060 builder
.SetIgnoreScrollFrame(rootScroll
);
6063 builder
.IgnorePaintSuppression();
6064 builder
.EnterPresShell(rootFrame
);
6066 rootFrame
->BuildDisplayListForStackingContext(&builder
, updateRect
, &list
);
6067 builder
.LeavePresShell(rootFrame
, &list
);
6069 RebuildApproximateFrameVisibilityDisplayList(list
);
6071 ClearApproximateFrameVisibilityVisited(rootFrame
->GetView(), true);
6073 list
.DeleteAll(&builder
);
6077 bool PresShell::AssumeAllFramesVisible() {
6078 if (!StaticPrefs::layout_framevisibility_enabled() || !mPresContext
||
6083 // We assume all frames are visible in print, print preview, chrome, and
6084 // resource docs and don't keep track of them.
6085 if (mPresContext
->Type() == nsPresContext::eContext_PrintPreview
||
6086 mPresContext
->Type() == nsPresContext::eContext_Print
||
6087 mPresContext
->IsChrome() || mDocument
->IsResourceDoc()) {
6091 // If we're assuming all frames are visible in the top level content
6092 // document, we need to in subdocuments as well. Otherwise we can get in a
6093 // situation where things like animations won't work in subdocuments because
6094 // their frames appear not to be visible, since we won't schedule an image
6095 // visibility update if the top level content document is assuming all
6096 // frames are visible.
6098 // Note that it's not safe to call IsRootContentDocument() if we're
6099 // currently being destroyed, so we have to check that first.
6100 if (!mHaveShutDown
&& !mIsDestroying
&&
6101 !mPresContext
->IsRootContentDocumentInProcess()) {
6102 nsPresContext
* presContext
=
6103 mPresContext
->GetInProcessRootContentDocumentPresContext();
6104 if (presContext
&& presContext
->PresShell()->AssumeAllFramesVisible()) {
6112 void PresShell::ScheduleApproximateFrameVisibilityUpdateSoon() {
6113 if (AssumeAllFramesVisible()) {
6117 if (!mPresContext
) {
6121 nsRefreshDriver
* refreshDriver
= mPresContext
->RefreshDriver();
6122 if (!refreshDriver
) {
6126 // Ask the refresh driver to update frame visibility soon.
6127 refreshDriver
->ScheduleFrameVisibilityUpdate();
6130 void PresShell::ScheduleApproximateFrameVisibilityUpdateNow() {
6131 if (AssumeAllFramesVisible()) {
6135 if (!mPresContext
->IsRootContentDocumentInProcess()) {
6136 nsPresContext
* presContext
=
6137 mPresContext
->GetInProcessRootContentDocumentPresContext();
6138 if (!presContext
) return;
6139 MOZ_ASSERT(presContext
->IsRootContentDocumentInProcess(),
6140 "Didn't get a root prescontext from "
6141 "GetInProcessRootContentDocumentPresContext?");
6142 presContext
->PresShell()->ScheduleApproximateFrameVisibilityUpdateNow();
6146 if (mHaveShutDown
|| mIsDestroying
) {
6150 if (mUpdateApproximateFrameVisibilityEvent
.IsPending()) {
6154 RefPtr
<nsRunnableMethod
<PresShell
>> event
=
6155 NewRunnableMethod("PresShell::UpdateApproximateFrameVisibility", this,
6156 &PresShell::UpdateApproximateFrameVisibility
);
6157 nsresult rv
= mDocument
->Dispatch(TaskCategory::Other
, do_AddRef(event
));
6159 if (NS_SUCCEEDED(rv
)) {
6160 mUpdateApproximateFrameVisibilityEvent
= std::move(event
);
6164 void PresShell::EnsureFrameInApproximatelyVisibleList(nsIFrame
* aFrame
) {
6165 if (!aFrame
->TrackingVisibility()) {
6169 if (AssumeAllFramesVisible()) {
6170 aFrame
->IncApproximateVisibleCount();
6175 // Make sure it's in this pres shell.
6176 nsCOMPtr
<nsIContent
> content
= aFrame
->GetContent();
6178 PresShell
* presShell
= content
->OwnerDoc()->GetPresShell();
6179 MOZ_ASSERT(!presShell
|| presShell
== this, "wrong shell");
6183 if (mApproximatelyVisibleFrames
.EnsureInserted(aFrame
)) {
6184 // We inserted a new entry.
6185 aFrame
->IncApproximateVisibleCount();
6189 void PresShell::RemoveFrameFromApproximatelyVisibleList(nsIFrame
* aFrame
) {
6191 // Make sure it's in this pres shell.
6192 nsCOMPtr
<nsIContent
> content
= aFrame
->GetContent();
6194 PresShell
* presShell
= content
->OwnerDoc()->GetPresShell();
6195 MOZ_ASSERT(!presShell
|| presShell
== this, "wrong shell");
6199 if (AssumeAllFramesVisible()) {
6200 MOZ_ASSERT(mApproximatelyVisibleFrames
.Count() == 0,
6201 "Shouldn't have any frames in the table");
6205 if (mApproximatelyVisibleFrames
.EnsureRemoved(aFrame
) &&
6206 aFrame
->TrackingVisibility()) {
6207 // aFrame was in the hashtable, and we're still tracking its visibility,
6208 // so we need to decrement its visible count.
6209 aFrame
->DecApproximateVisibleCount();
6213 class nsAutoNotifyDidPaint
{
6215 nsAutoNotifyDidPaint(PresShell
* aShell
, PaintFlags aFlags
)
6216 : mShell(aShell
), mFlags(aFlags
) {}
6217 ~nsAutoNotifyDidPaint() {
6218 if (!!(mFlags
& PaintFlags::PaintComposite
)) {
6219 mShell
->GetPresContext()->NotifyDidPaintForSubtree();
6228 void PresShell::Paint(nsView
* aViewToPaint
, const nsRegion
& aDirtyRegion
,
6229 PaintFlags aFlags
) {
6231 nsIURI
* uri
= mDocument
->GetDocumentURI();
6232 Document
* contentRoot
= GetPrimaryContentDocument();
6234 uri
= contentRoot
->GetDocumentURI();
6236 url
= uri
? uri
->GetSpecOrDefault() : "N/A"_ns
;
6237 AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING("PresShell::Paint", GRAPHICS
, url
);
6239 Maybe
<js::AutoAssertNoContentJS
> nojs
;
6241 // On Android, Flash can call into content JS during painting, so we can't
6242 // assert there. However, we don't rely on this assertion on Android because
6243 // we don't paint while JS is running.
6244 #if !defined(MOZ_WIDGET_ANDROID)
6245 if (!(aFlags
& PaintFlags::PaintComposite
)) {
6246 // We need to allow content JS when the flag is set since we may trigger
6247 // MozAfterPaint events in content in those cases.
6248 nojs
.emplace(dom::danger::GetJSContext());
6252 NS_ASSERTION(!mIsDestroying
, "painting a destroyed PresShell");
6253 NS_ASSERTION(aViewToPaint
, "null view");
6255 MOZ_ASSERT(!mApproximateFrameVisibilityVisited
, "Should have been cleared");
6261 if (StaticPrefs::apz_keyboard_enabled_AtStartup()) {
6262 // Update the focus target for async keyboard scrolling. This will be
6263 // forwarded to APZ by nsDisplayList::PaintRoot. We need to to do this
6264 // before we enter the paint phase because dispatching eVoid events can
6265 // cause layout to happen.
6266 mAPZFocusTarget
= FocusTarget(this, mAPZFocusSequenceNumber
);
6269 nsPresContext
* presContext
= GetPresContext();
6270 AUTO_LAYOUT_PHASE_ENTRY_POINT(presContext
, Paint
);
6272 nsIFrame
* frame
= aViewToPaint
->GetFrame();
6274 LayerManager
* layerManager
= aViewToPaint
->GetWidget()->GetLayerManager();
6275 NS_ASSERTION(layerManager
, "Must be in paint event");
6276 bool shouldInvalidate
= layerManager
->NeedsWidgetInvalidation();
6278 nsAutoNotifyDidPaint
notifyDidPaint(this, aFlags
);
6280 // Whether or not we should set first paint when painting is suppressed
6281 // is debatable. For now we'll do it because B2G relied on first paint
6282 // to configure the viewport and we only want to do that when we have
6283 // real content to paint. See Bug 798245
6284 if (mIsFirstPaint
&& !mPaintingSuppressed
) {
6285 MOZ_LOG(gLog
, LogLevel::Debug
,
6286 ("PresShell::Paint, first paint, this=%p", this));
6288 layerManager
->SetIsFirstPaint();
6289 mIsFirstPaint
= false;
6292 if (!layerManager
->BeginTransaction(url
)) {
6296 // Send an updated focus target with this transaction. Be sure to do this
6297 // before we paint in the case this is an empty transaction.
6298 layerManager
->SetFocusTarget(mAPZFocusTarget
);
6301 // Try to do an empty transaction, if the frame tree does not
6302 // need to be updated. Do not try to do an empty transaction on
6303 // a non-retained layer manager (like the BasicLayerManager that
6304 // draws the window title bar on Mac), because a) it won't work
6305 // and b) below we don't want to clear NS_FRAME_UPDATE_LAYER_TREE,
6306 // that will cause us to forget to update the real layer manager!
6308 if (!(aFlags
& PaintFlags::PaintLayers
)) {
6309 if (layerManager
->EndEmptyTransaction()) {
6312 NS_WARNING("Must complete empty transaction when compositing!");
6315 if (!(aFlags
& PaintFlags::PaintSyncDecodeImages
) &&
6316 !frame
->HasAnyStateBits(NS_FRAME_UPDATE_LAYER_TREE
) &&
6317 !mNextPaintCompressed
) {
6318 NotifySubDocInvalidationFunc computeInvalidFunc
=
6319 presContext
->MayHavePaintEventListenerInSubDocument()
6320 ? nsPresContext::NotifySubDocInvalidation
6322 bool computeInvalidRect
=
6323 computeInvalidFunc
||
6324 (layerManager
->GetBackendType() == LayersBackend::LAYERS_BASIC
);
6326 UniquePtr
<LayerProperties
> props
;
6327 // For WR, the layermanager has no root layer. We want to avoid
6328 // calling ComputeDifferences in that case because it assumes non-null
6330 if (computeInvalidRect
&& layerManager
->GetRoot()) {
6331 props
= LayerProperties::CloneFrom(layerManager
->GetRoot());
6334 MaybeSetupTransactionIdAllocator(layerManager
, presContext
);
6336 if (layerManager
->EndEmptyTransaction(
6337 (aFlags
& PaintFlags::PaintComposite
)
6338 ? LayerManager::END_DEFAULT
6339 : LayerManager::END_NO_COMPOSITE
)) {
6340 nsIntRegion invalid
;
6341 bool areaOverflowed
= false;
6343 if (!props
->ComputeDifferences(layerManager
->GetRoot(), invalid
,
6344 computeInvalidFunc
)) {
6345 areaOverflowed
= true;
6348 LayerProperties::ClearInvalidations(layerManager
->GetRoot());
6350 if (props
&& !areaOverflowed
) {
6351 if (!invalid
.IsEmpty()) {
6352 nsIntRect bounds
= invalid
.GetBounds();
6353 nsRect
rect(presContext
->DevPixelsToAppUnits(bounds
.x
),
6354 presContext
->DevPixelsToAppUnits(bounds
.y
),
6355 presContext
->DevPixelsToAppUnits(bounds
.width
),
6356 presContext
->DevPixelsToAppUnits(bounds
.height
));
6357 if (shouldInvalidate
) {
6358 aViewToPaint
->GetViewManager()->InvalidateViewNoSuppression(
6359 aViewToPaint
, rect
);
6361 presContext
->NotifyInvalidation(
6362 layerManager
->GetLastTransactionId(), bounds
);
6364 } else if (shouldInvalidate
) {
6365 aViewToPaint
->GetViewManager()->InvalidateView(aViewToPaint
);
6368 frame
->UpdatePaintCountForPaintedPresShells();
6372 frame
->RemoveStateBits(NS_FRAME_UPDATE_LAYER_TREE
);
6375 frame
->ClearPresShellsFromLastPaint();
6378 nscolor bgcolor
= ComputeBackstopColor(aViewToPaint
);
6379 PaintFrameFlags flags
=
6380 PaintFrameFlags::WidgetLayers
| PaintFrameFlags::ExistingTransaction
;
6381 if (!(aFlags
& PaintFlags::PaintComposite
)) {
6382 flags
|= PaintFrameFlags::NoComposite
;
6384 // We force sync-decode for printing / print-preview (printing already does
6385 // this from nsPageSequenceFrame::PrintNextSheet).
6386 if (aFlags
& PaintFlags::PaintSyncDecodeImages
||
6387 mDocument
->IsStaticDocument()) {
6388 flags
|= PaintFrameFlags::SyncDecodeImages
;
6390 if (mNextPaintCompressed
) {
6391 flags
|= PaintFrameFlags::Compressed
;
6392 mNextPaintCompressed
= false;
6394 if (layerManager
->GetBackendType() == layers::LayersBackend::LAYERS_WR
) {
6395 flags
|= PaintFrameFlags::ForWebRender
;
6399 // We can paint directly into the widget using its layer manager.
6400 nsLayoutUtils::PaintFrame(nullptr, frame
, aDirtyRegion
, bgcolor
,
6401 nsDisplayListBuilderMode::Painting
, flags
);
6405 if (layerManager
->GetBackendType() == layers::LayersBackend::LAYERS_WR
) {
6406 nsPresContext
* pc
= GetPresContext();
6407 LayoutDeviceRect bounds
= LayoutDeviceRect::FromAppUnits(
6408 pc
->GetVisibleArea(), pc
->AppUnitsPerDevPixel());
6409 bgcolor
= NS_ComposeColors(bgcolor
, mCanvasBackgroundColor
);
6410 WebRenderBackgroundData
data(wr::ToLayoutRect(bounds
),
6411 wr::ToColorF(ToDeviceColor(bgcolor
)));
6412 WrFiltersHolder wrFilters
;
6414 MaybeSetupTransactionIdAllocator(layerManager
, presContext
);
6415 layerManager
->AsWebRenderLayerManager()->EndTransactionWithoutLayer(
6416 nullptr, nullptr, std::move(wrFilters
), &data
, 0);
6420 RefPtr
<ColorLayer
> root
= layerManager
->CreateColorLayer();
6422 nsPresContext
* pc
= GetPresContext();
6424 pc
->GetVisibleArea().ToOutsidePixels(pc
->AppUnitsPerDevPixel());
6425 bgcolor
= NS_ComposeColors(bgcolor
, mCanvasBackgroundColor
);
6426 root
->SetColor(ToDeviceColor(bgcolor
));
6427 root
->SetVisibleRegion(LayerIntRegion::FromUnknownRegion(bounds
));
6428 layerManager
->SetRoot(root
);
6430 MaybeSetupTransactionIdAllocator(layerManager
, presContext
);
6431 layerManager
->EndTransaction(nullptr, nullptr,
6432 (aFlags
& PaintFlags::PaintComposite
)
6433 ? LayerManager::END_DEFAULT
6434 : LayerManager::END_NO_COMPOSITE
);
6438 void PresShell::SetCapturingContent(nsIContent
* aContent
, CaptureFlags aFlags
,
6439 WidgetEvent
* aEvent
) {
6440 // If capture was set for pointer lock, don't unlock unless we are coming
6441 // out of pointer lock explicitly.
6442 if (!aContent
&& sCapturingContentInfo
.mPointerLock
&&
6443 !(aFlags
& CaptureFlags::PointerLock
)) {
6447 sCapturingContentInfo
.mContent
= nullptr;
6448 sCapturingContentInfo
.mRemoteTarget
= nullptr;
6450 // only set capturing content if allowed or the
6451 // CaptureFlags::IgnoreAllowedState or CaptureFlags::PointerLock are used.
6452 if ((aFlags
& CaptureFlags::IgnoreAllowedState
) ||
6453 sCapturingContentInfo
.mAllowed
|| (aFlags
& CaptureFlags::PointerLock
)) {
6455 sCapturingContentInfo
.mContent
= aContent
;
6458 MOZ_ASSERT(XRE_IsParentProcess());
6459 MOZ_ASSERT(aEvent
->mMessage
== eMouseDown
);
6460 MOZ_ASSERT(aEvent
->HasBeenPostedToRemoteProcess());
6461 sCapturingContentInfo
.mRemoteTarget
=
6462 BrowserParent::GetLastMouseRemoteTarget();
6463 MOZ_ASSERT(sCapturingContentInfo
.mRemoteTarget
);
6465 // CaptureFlags::PointerLock is the same as
6466 // CaptureFlags::RetargetToElement & CaptureFlags::IgnoreAllowedState.
6467 sCapturingContentInfo
.mRetargetToElement
=
6468 !!(aFlags
& CaptureFlags::RetargetToElement
) ||
6469 !!(aFlags
& CaptureFlags::PointerLock
);
6470 sCapturingContentInfo
.mPreventDrag
=
6471 !!(aFlags
& CaptureFlags::PreventDragStart
);
6472 sCapturingContentInfo
.mPointerLock
= !!(aFlags
& CaptureFlags::PointerLock
);
6476 nsIContent
* PresShell::GetCurrentEventContent() {
6477 if (mCurrentEventContent
&&
6478 mCurrentEventContent
->GetComposedDoc() != mDocument
) {
6479 mCurrentEventContent
= nullptr;
6480 mCurrentEventFrame
= nullptr;
6482 return mCurrentEventContent
;
6485 nsIFrame
* PresShell::GetCurrentEventFrame() {
6486 if (MOZ_UNLIKELY(mIsDestroying
)) {
6490 // GetCurrentEventContent() makes sure the content is still in the
6491 // same document that this pres shell belongs to. If not, then the
6492 // frame shouldn't get an event, nor should we even assume its safe
6493 // to try and find the frame.
6494 nsIContent
* content
= GetCurrentEventContent();
6495 if (!mCurrentEventFrame
&& content
) {
6496 mCurrentEventFrame
= content
->GetPrimaryFrame();
6497 MOZ_ASSERT(!mCurrentEventFrame
||
6498 mCurrentEventFrame
->PresContext()->GetPresShell() == this);
6500 return mCurrentEventFrame
;
6503 already_AddRefed
<nsIContent
> PresShell::GetEventTargetContent(
6504 WidgetEvent
* aEvent
) {
6505 nsCOMPtr
<nsIContent
> content
= GetCurrentEventContent();
6507 nsIFrame
* currentEventFrame
= GetCurrentEventFrame();
6508 if (currentEventFrame
) {
6509 currentEventFrame
->GetContentForEvent(aEvent
, getter_AddRefs(content
));
6510 NS_ASSERTION(!content
|| content
->GetComposedDoc() == mDocument
,
6511 "handing out content from a different doc");
6514 return content
.forget();
6517 void PresShell::PushCurrentEventInfo(nsIFrame
* aFrame
, nsIContent
* aContent
) {
6518 if (mCurrentEventFrame
|| mCurrentEventContent
) {
6519 mCurrentEventFrameStack
.InsertElementAt(0, mCurrentEventFrame
);
6520 mCurrentEventContentStack
.InsertObjectAt(mCurrentEventContent
, 0);
6522 mCurrentEventFrame
= aFrame
;
6523 mCurrentEventContent
= aContent
;
6526 void PresShell::PopCurrentEventInfo() {
6527 mCurrentEventFrame
= nullptr;
6528 mCurrentEventContent
= nullptr;
6530 if (0 != mCurrentEventFrameStack
.Length()) {
6531 mCurrentEventFrame
= mCurrentEventFrameStack
.ElementAt(0);
6532 mCurrentEventFrameStack
.RemoveElementAt(0);
6533 mCurrentEventContent
= mCurrentEventContentStack
.ObjectAt(0);
6534 mCurrentEventContentStack
.RemoveObjectAt(0);
6536 // Don't use it if it has moved to a different document.
6537 if (mCurrentEventContent
&&
6538 mCurrentEventContent
->GetComposedDoc() != mDocument
) {
6539 mCurrentEventContent
= nullptr;
6540 mCurrentEventFrame
= nullptr;
6546 bool PresShell::EventHandler::InZombieDocument(nsIContent
* aContent
) {
6547 // If a content node points to a null document, or the document is not
6548 // attached to a window, then it is possibly in a zombie document,
6549 // about to be replaced by a newly loading document.
6550 // Such documents cannot handle DOM events.
6551 // It might actually be in a node not attached to any document,
6552 // in which case there is not parent presshell to retarget it to.
6553 Document
* doc
= aContent
->GetComposedDoc();
6554 return !doc
|| !doc
->GetWindow();
6557 already_AddRefed
<nsPIDOMWindowOuter
> PresShell::GetRootWindow() {
6558 nsCOMPtr
<nsPIDOMWindowOuter
> window
= mDocument
->GetWindow();
6560 nsCOMPtr
<nsPIDOMWindowOuter
> rootWindow
= window
->GetPrivateRoot();
6561 NS_ASSERTION(rootWindow
, "nsPIDOMWindow::GetPrivateRoot() returns NULL");
6562 return rootWindow
.forget();
6565 // If we don't have DOM window, we're zombie, we should find the root window
6566 // with our parent shell.
6567 RefPtr
<PresShell
> parentPresShell
= GetParentPresShellForEventHandling();
6568 NS_ENSURE_TRUE(parentPresShell
, nullptr);
6569 return parentPresShell
->GetRootWindow();
6572 already_AddRefed
<nsPIDOMWindowOuter
>
6573 PresShell::GetFocusedDOMWindowInOurWindow() {
6574 nsCOMPtr
<nsPIDOMWindowOuter
> rootWindow
= GetRootWindow();
6575 NS_ENSURE_TRUE(rootWindow
, nullptr);
6576 nsCOMPtr
<nsPIDOMWindowOuter
> focusedWindow
;
6577 nsFocusManager::GetFocusedDescendant(rootWindow
,
6578 nsFocusManager::eIncludeAllDescendants
,
6579 getter_AddRefs(focusedWindow
));
6580 return focusedWindow
.forget();
6583 already_AddRefed
<nsIContent
> PresShell::GetFocusedContentInOurWindow() const {
6584 nsFocusManager
* fm
= nsFocusManager::GetFocusManager();
6585 if (fm
&& mDocument
) {
6586 RefPtr
<Element
> focusedElement
;
6587 fm
->GetFocusedElementForWindow(mDocument
->GetWindow(), false, nullptr,
6588 getter_AddRefs(focusedElement
));
6589 return focusedElement
.forget();
6594 already_AddRefed
<PresShell
> PresShell::GetParentPresShellForEventHandling() {
6595 if (!mPresContext
) {
6599 // Now, find the parent pres shell and send the event there
6600 RefPtr
<nsDocShell
> docShell
= mPresContext
->GetDocShell();
6602 docShell
= mForwardingContainer
.get();
6605 // Might have gone away, or never been around to start with
6610 BrowsingContext
* bc
= docShell
->GetBrowsingContext();
6615 RefPtr
<BrowsingContext
> parentBC
;
6616 if (XRE_IsParentProcess()) {
6617 parentBC
= bc
->Canonical()->GetParentCrossChromeBoundary();
6619 parentBC
= bc
->GetParent();
6622 RefPtr
<nsIDocShell
> parentDocShell
=
6623 parentBC
? parentBC
->GetDocShell() : nullptr;
6624 if (!parentDocShell
) {
6628 RefPtr
<PresShell
> parentPresShell
= parentDocShell
->GetPresShell();
6629 return parentPresShell
.forget();
6632 nsresult
PresShell::EventHandler::RetargetEventToParent(
6633 WidgetGUIEvent
* aGUIEvent
, nsEventStatus
* aEventStatus
) {
6634 // Send this events straight up to the parent pres shell.
6635 // We do this for keystroke events in zombie documents or if either a frame
6636 // or a root content is not present.
6637 // That way at least the UI key bindings can work.
6639 RefPtr
<PresShell
> parentPresShell
= GetParentPresShellForEventHandling();
6640 NS_ENSURE_TRUE(parentPresShell
, NS_ERROR_FAILURE
);
6642 // Fake the event as though it's from the parent pres shell's root frame.
6643 return parentPresShell
->HandleEvent(parentPresShell
->GetRootFrame(),
6644 aGUIEvent
, true, aEventStatus
);
6647 void PresShell::DisableNonTestMouseEvents(bool aDisable
) {
6648 sDisableNonTestMouseEvents
= aDisable
;
6651 bool PresShell::MouseLocationWasSetBySynthesizedMouseEventForTests() const {
6652 if (!mPresContext
) {
6655 if (mPresContext
->IsRoot()) {
6656 return mMouseLocationWasSetBySynthesizedMouseEventForTests
;
6658 PresShell
* rootPresShell
= GetRootPresShell();
6659 return rootPresShell
&&
6660 rootPresShell
->mMouseLocationWasSetBySynthesizedMouseEventForTests
;
6663 void PresShell::RecordMouseLocation(WidgetGUIEvent
* aEvent
) {
6664 if (!mPresContext
) return;
6666 if (!mPresContext
->IsRoot()) {
6667 PresShell
* rootPresShell
= GetRootPresShell();
6668 if (rootPresShell
) {
6669 rootPresShell
->RecordMouseLocation(aEvent
);
6674 if ((aEvent
->mMessage
== eMouseMove
&&
6675 aEvent
->AsMouseEvent()->mReason
== WidgetMouseEvent::eReal
) ||
6676 aEvent
->mMessage
== eMouseEnterIntoWidget
||
6677 aEvent
->mMessage
== eMouseDown
|| aEvent
->mMessage
== eMouseUp
) {
6678 nsIFrame
* rootFrame
= GetRootFrame();
6680 nsView
* rootView
= mViewManager
->GetRootView();
6681 mMouseLocation
= nsLayoutUtils::TranslateWidgetToView(
6682 mPresContext
, aEvent
->mWidget
, aEvent
->mRefPoint
, rootView
);
6683 mMouseEventTargetGuid
= InputAPZContext::GetTargetLayerGuid();
6685 RelativeTo relativeTo
{rootFrame
};
6686 if (rootFrame
->PresContext()->IsRootContentDocumentCrossProcess()) {
6687 relativeTo
.mViewportType
= ViewportType::Visual
;
6690 nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent
, relativeTo
);
6691 mMouseEventTargetGuid
= InputAPZContext::GetTargetLayerGuid();
6693 mMouseLocationWasSetBySynthesizedMouseEventForTests
=
6694 aEvent
->mFlags
.mIsSynthesizedForTests
;
6695 #ifdef DEBUG_MOUSE_LOCATION
6696 if (aEvent
->mMessage
== eMouseEnterIntoWidget
) {
6697 printf("[ps=%p]got mouse enter for %p\n", this, aEvent
->mWidget
);
6699 printf("[ps=%p]setting mouse location to (%d,%d)\n", this, mMouseLocation
.x
,
6702 if (aEvent
->mMessage
== eMouseEnterIntoWidget
) {
6703 SynthesizeMouseMove(false);
6705 } else if (aEvent
->mMessage
== eMouseExitFromWidget
) {
6706 // Although we only care about the mouse moving into an area for which this
6707 // pres shell doesn't receive mouse move events, we don't check which widget
6708 // the mouse exit was for since this seems to vary by platform. Hopefully
6709 // this won't matter at all since we'll get the mouse move or enter after
6710 // the mouse exit when the mouse moves from one of our widgets into another.
6711 mMouseLocation
= nsPoint(NS_UNCONSTRAINEDSIZE
, NS_UNCONSTRAINEDSIZE
);
6712 mMouseEventTargetGuid
= InputAPZContext::GetTargetLayerGuid();
6713 mMouseLocationWasSetBySynthesizedMouseEventForTests
=
6714 aEvent
->mFlags
.mIsSynthesizedForTests
;
6715 #ifdef DEBUG_MOUSE_LOCATION
6716 printf("[ps=%p]got mouse exit for %p\n", this, aEvent
->mWidget
);
6717 printf("[ps=%p]clearing mouse location\n", this);
6722 void PresShell::nsSynthMouseMoveEvent::Revoke() {
6724 mPresShell
->GetPresContext()->RefreshDriver()->RemoveRefreshObserver(
6725 this, FlushType::Display
);
6726 mPresShell
= nullptr;
6731 nsIFrame
* PresShell::EventHandler::GetNearestFrameContainingPresShell(
6732 PresShell
* aPresShell
) {
6733 nsView
* view
= aPresShell
->GetViewManager()->GetRootView();
6734 while (view
&& !view
->GetFrame()) {
6735 view
= view
->GetParent();
6738 nsIFrame
* frame
= nullptr;
6740 frame
= view
->GetFrame();
6746 static CallState
FlushThrottledStyles(Document
& aDocument
) {
6747 PresShell
* presShell
= aDocument
.GetPresShell();
6748 if (presShell
&& presShell
->IsVisible()) {
6749 if (nsPresContext
* presContext
= presShell
->GetPresContext()) {
6750 presContext
->RestyleManager()->UpdateOnlyAnimationStyles();
6754 aDocument
.EnumerateSubDocuments(FlushThrottledStyles
);
6755 return CallState::Continue
;
6758 bool PresShell::CanDispatchEvent(const WidgetGUIEvent
* aEvent
) const {
6760 mPresContext
&& !mHaveShutDown
&& nsContentUtils::IsSafeToRunScript();
6762 rv
&= (aEvent
&& aEvent
->mWidget
&& !aEvent
->mWidget
->Destroyed());
6768 PresShell
* PresShell::GetShellForEventTarget(nsIFrame
* aFrame
,
6769 nsIContent
* aContent
) {
6771 return aFrame
->PresShell();
6774 Document
* doc
= aContent
->GetComposedDoc();
6778 return doc
->GetPresShell();
6784 PresShell
* PresShell::GetShellForTouchEvent(WidgetGUIEvent
* aEvent
) {
6785 switch (aEvent
->mMessage
) {
6789 // get the correct shell to dispatch to
6790 WidgetTouchEvent
* touchEvent
= aEvent
->AsTouchEvent();
6791 for (dom::Touch
* touch
: touchEvent
->mTouches
) {
6796 RefPtr
<dom::Touch
> oldTouch
=
6797 TouchManager::GetCapturedTouch(touch
->Identifier());
6802 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(oldTouch
->GetTarget());
6807 nsIFrame
* contentFrame
= content
->GetPrimaryFrame();
6808 if (!contentFrame
) {
6812 PresShell
* presShell
= contentFrame
->PresContext()->GetPresShell();
6824 nsresult
PresShell::HandleEvent(nsIFrame
* aFrameForPresShell
,
6825 WidgetGUIEvent
* aGUIEvent
,
6826 bool aDontRetargetEvents
,
6827 nsEventStatus
* aEventStatus
) {
6828 MOZ_ASSERT(aGUIEvent
);
6829 // If it's synthesized in the parent process and our mouse location was set
6830 // by a mouse event which was synthesized for tests because the test does not
6831 // want to change `:hover` state with the synthesized mouse event for native
6832 // mouse cursor position.
6833 if (aGUIEvent
->mMessage
== eMouseMove
&&
6834 aGUIEvent
->CameFromAnotherProcess() && XRE_IsContentProcess() &&
6835 !aGUIEvent
->mFlags
.mIsSynthesizedForTests
&&
6836 MouseLocationWasSetBySynthesizedMouseEventForTests() &&
6837 aGUIEvent
->AsMouseEvent()->mReason
== WidgetMouseEvent::eSynthesized
) {
6840 EventHandler
eventHandler(*this);
6841 return eventHandler
.HandleEvent(aFrameForPresShell
, aGUIEvent
,
6842 aDontRetargetEvents
, aEventStatus
);
6845 nsresult
PresShell::EventHandler::HandleEvent(nsIFrame
* aFrameForPresShell
,
6846 WidgetGUIEvent
* aGUIEvent
,
6847 bool aDontRetargetEvents
,
6848 nsEventStatus
* aEventStatus
) {
6849 MOZ_ASSERT(aGUIEvent
);
6850 MOZ_DIAGNOSTIC_ASSERT(aGUIEvent
->IsTrusted());
6851 MOZ_ASSERT(aEventStatus
);
6853 NS_ASSERTION(aFrameForPresShell
, "aFrameForPresShell should be not null");
6855 // Update the latest focus sequence number with this new sequence number;
6856 // the next transasction that gets sent to the compositor will carry this over
6857 if (mPresShell
->mAPZFocusSequenceNumber
< aGUIEvent
->mFocusSequenceNumber
) {
6858 mPresShell
->mAPZFocusSequenceNumber
= aGUIEvent
->mFocusSequenceNumber
;
6861 if (mPresShell
->IsDestroying() ||
6862 (PresShell::sDisableNonTestMouseEvents
&&
6863 !aGUIEvent
->mFlags
.mIsSynthesizedForTests
&&
6864 aGUIEvent
->HasMouseEventMessage())) {
6868 mPresShell
->RecordMouseLocation(aGUIEvent
);
6870 if (MaybeHandleEventWithAccessibleCaret(aFrameForPresShell
, aGUIEvent
,
6872 // Handled by AccessibleCaretEventHub.
6876 if (MaybeDiscardEvent(aGUIEvent
)) {
6877 // Cannot handle the event for now.
6881 if (!aDontRetargetEvents
) {
6882 // If aGUIEvent should be handled in another PresShell, we should call its
6883 // HandleEvent() and do nothing here.
6884 nsresult rv
= NS_OK
;
6885 if (MaybeHandleEventWithAnotherPresShell(aFrameForPresShell
, aGUIEvent
,
6886 aEventStatus
, &rv
)) {
6887 // Handled by another PresShell or nobody can handle the event.
6892 if (MaybeDiscardOrDelayKeyboardEvent(aGUIEvent
)) {
6893 // The event is discarded or put into the delayed event queue.
6897 if (aGUIEvent
->IsUsingCoordinates()) {
6898 return HandleEventUsingCoordinates(aFrameForPresShell
, aGUIEvent
,
6899 aEventStatus
, aDontRetargetEvents
);
6902 // Activation events need to be dispatched even if no frame was found, since
6903 // we don't want the focus to be out of sync.
6904 if (!aFrameForPresShell
) {
6905 if (!NS_EVENT_NEEDS_FRAME(aGUIEvent
)) {
6906 // Push nullptr for both current event target content and frame since
6907 // there is no frame but the event does not require a frame.
6908 AutoCurrentEventInfoSetter
eventInfoSetter(*this);
6909 return HandleEventWithCurrentEventInfo(aGUIEvent
, aEventStatus
, true,
6913 if (aGUIEvent
->HasKeyEventMessage()) {
6914 // Keypress events in new blank tabs should not be completely thrown away.
6915 // Retarget them -- the parent chrome shell might make use of them.
6916 return RetargetEventToParent(aGUIEvent
, aEventStatus
);
6922 if (aGUIEvent
->IsTargetedAtFocusedContent()) {
6923 return HandleEventAtFocusedContent(aGUIEvent
, aEventStatus
);
6926 return HandleEventWithFrameForPresShell(aFrameForPresShell
, aGUIEvent
,
6930 nsresult
PresShell::EventHandler::HandleEventUsingCoordinates(
6931 nsIFrame
* aFrameForPresShell
, WidgetGUIEvent
* aGUIEvent
,
6932 nsEventStatus
* aEventStatus
, bool aDontRetargetEvents
) {
6933 MOZ_ASSERT(aGUIEvent
);
6934 MOZ_ASSERT(aGUIEvent
->IsUsingCoordinates());
6935 MOZ_ASSERT(aEventStatus
);
6937 // Flush pending notifications to handle the event with the latest layout.
6938 // But if it causes destroying the frame for mPresShell, stop handling the
6940 AutoWeakFrame
weakFrame(aFrameForPresShell
);
6941 MaybeFlushPendingNotifications(aGUIEvent
);
6942 if (!weakFrame
.IsAlive()) {
6943 *aEventStatus
= nsEventStatus_eIgnore
;
6947 // XXX Retrieving capturing content here. However, some of the following
6948 // methods allow to run script. So, isn't it possible the capturing
6949 // content outdated?
6950 nsCOMPtr
<nsIContent
> capturingContent
=
6951 EventHandler::GetCapturingContentFor(aGUIEvent
);
6953 if (GetDocument() && aGUIEvent
->mClass
== eTouchEventClass
) {
6954 PointerLockManager::Unlock();
6957 nsIFrame
* frameForPresShell
= MaybeFlushThrottledStyles(aFrameForPresShell
);
6958 if (NS_WARN_IF(!frameForPresShell
)) {
6962 bool isCapturingContentIgnored
= false;
6963 bool isCaptureRetargeted
= false;
6964 nsIFrame
* rootFrameToHandleEvent
= ComputeRootFrameToHandleEvent(
6965 frameForPresShell
, aGUIEvent
, capturingContent
,
6966 &isCapturingContentIgnored
, &isCaptureRetargeted
);
6967 if (isCapturingContentIgnored
) {
6968 capturingContent
= nullptr;
6971 // The order to generate pointer event is
6972 // 1. check pending pointer capture.
6973 // 2. check if there is a capturing content.
6975 // 4. dispatch pointer events
6976 // 5. check whether the targets of all Touch instances are in the same
6977 // document and suppress invalid instances.
6978 // 6. dispatch mouse or touch events.
6980 // Try to keep frame for following check, because frame can be damaged
6981 // during MaybeProcessPointerCapture.
6983 AutoWeakFrame
frameKeeper(rootFrameToHandleEvent
);
6984 PointerEventHandler::MaybeProcessPointerCapture(aGUIEvent
);
6985 // Prevent application crashes, in case damaged frame.
6986 if (!frameKeeper
.IsAlive()) {
6987 NS_WARNING("Nothing to handle this event!");
6992 // Only capture mouse events and pointer events.
6993 RefPtr
<Element
> pointerCapturingElement
=
6994 PointerEventHandler::GetPointerCapturingElement(aGUIEvent
);
6996 if (pointerCapturingElement
) {
6997 rootFrameToHandleEvent
= pointerCapturingElement
->GetPrimaryFrame();
6998 if (!rootFrameToHandleEvent
) {
6999 return HandleEventWithPointerCapturingContentWithoutItsFrame(
7000 aFrameForPresShell
, aGUIEvent
, pointerCapturingElement
, aEventStatus
);
7004 WidgetMouseEvent
* mouseEvent
= aGUIEvent
->AsMouseEvent();
7005 bool isWindowLevelMouseExit
=
7006 (aGUIEvent
->mMessage
== eMouseExitFromWidget
) &&
7008 (mouseEvent
->mExitFrom
.value() == WidgetMouseEvent::ePlatformTopLevel
||
7009 mouseEvent
->mExitFrom
.value() == WidgetMouseEvent::ePuppet
));
7011 // Get the frame at the event point. However, don't do this if we're
7012 // capturing and retargeting the event because the captured frame will
7013 // be used instead below. Also keep using the root frame if we're dealing
7014 // with a window-level mouse exit event since we want to start sending
7015 // mouse out events at the root EventStateManager.
7016 EventTargetData
eventTargetData(rootFrameToHandleEvent
);
7017 if (!isCaptureRetargeted
&& !isWindowLevelMouseExit
&&
7018 !pointerCapturingElement
) {
7019 if (!ComputeEventTargetFrameAndPresShellAtEventPoint(
7020 rootFrameToHandleEvent
, aGUIEvent
, &eventTargetData
)) {
7021 *aEventStatus
= nsEventStatus_eIgnore
;
7026 // if a node is capturing the mouse, check if the event needs to be
7027 // retargeted at the capturing content instead. This will be the case when
7028 // capture retargeting is being used, no frame was found or the frame's
7029 // content is not a descendant of the capturing content.
7030 if (capturingContent
&& !pointerCapturingElement
&&
7031 (PresShell::sCapturingContentInfo
.mRetargetToElement
||
7032 !eventTargetData
.mFrame
->GetContent() ||
7033 !nsContentUtils::ContentIsCrossDocDescendantOf(
7034 eventTargetData
.mFrame
->GetContent(), capturingContent
))) {
7035 // A check was already done above to ensure that capturingContent is
7036 // in this presshell.
7037 NS_ASSERTION(capturingContent
->OwnerDoc() == GetDocument(),
7038 "Unexpected document");
7039 nsIFrame
* capturingFrame
= capturingContent
->GetPrimaryFrame();
7040 if (capturingFrame
) {
7041 eventTargetData
.SetFrameAndComputePresShell(capturingFrame
);
7045 if (NS_WARN_IF(!eventTargetData
.mFrame
)) {
7049 // Suppress mouse event if it's being targeted at an element inside
7050 // a document which needs events suppressed
7051 if (MaybeDiscardOrDelayMouseEvent(eventTargetData
.mFrame
, aGUIEvent
)) {
7055 // Check if we have an active EventStateManager which isn't the
7056 // EventStateManager of the current PresContext. If that is the case, and
7057 // mouse is over some ancestor document, forward event handling to the
7058 // active document. This way content can get mouse events even when mouse
7059 // is over the chrome or outside the window.
7060 if (eventTargetData
.MaybeRetargetToActiveDocument(aGUIEvent
) &&
7061 NS_WARN_IF(!eventTargetData
.mFrame
)) {
7065 if (!eventTargetData
.ComputeElementFromFrame(aGUIEvent
)) {
7068 // Note that even if ComputeElementFromFrame() returns true,
7069 // eventTargetData.mContent can be nullptr here.
7071 // Dispatch a pointer event if Pointer Events is enabled. Note that if
7072 // pointer event listeners change the layout, eventTargetData is
7073 // automatically updated.
7074 if (!DispatchPrecedingPointerEvent(
7075 aFrameForPresShell
, aGUIEvent
, pointerCapturingElement
,
7076 aDontRetargetEvents
, &eventTargetData
, aEventStatus
)) {
7080 // frame could be null after dispatching pointer events.
7081 // XXX Despite of this comment, we update the event target data outside
7082 // DispatchPrecedingPointerEvent(). Can we make it call
7083 // UpdateTouchEventTarget()?
7084 eventTargetData
.UpdateTouchEventTarget(aGUIEvent
);
7086 // Handle the event in the correct shell.
7087 // We pass the subshell's root frame as the frame to start from. This is
7088 // the only correct alternative; if the event was captured then it
7089 // must have been captured by us or some ancestor shell and we
7090 // now ask the subshell to dispatch it normally.
7091 EventHandler
eventHandler(*eventTargetData
.mPresShell
);
7092 AutoCurrentEventInfoSetter
eventInfoSetter(eventHandler
, eventTargetData
);
7093 // eventTargetData is on the stack and is guaranteed to keep its
7094 // mOverrideClickTarget alive, so we can just use MOZ_KnownLive here.
7095 nsresult rv
= eventHandler
.HandleEventWithCurrentEventInfo(
7096 aGUIEvent
, aEventStatus
, true,
7097 MOZ_KnownLive(eventTargetData
.mOverrideClickTarget
));
7099 eventTargetData
.mPresShell
->ShowEventTargetDebug();
7104 bool PresShell::EventHandler::MaybeFlushPendingNotifications(
7105 WidgetGUIEvent
* aGUIEvent
) {
7106 MOZ_ASSERT(aGUIEvent
);
7108 switch (aGUIEvent
->mMessage
) {
7111 RefPtr
<nsPresContext
> presContext
= mPresShell
->GetPresContext();
7112 if (NS_WARN_IF(!presContext
)) {
7115 uint64_t framesConstructedCount
= presContext
->FramesConstructedCount();
7116 uint64_t framesReflowedCount
= presContext
->FramesReflowedCount();
7118 MOZ_KnownLive(mPresShell
)->FlushPendingNotifications(FlushType::Layout
);
7119 return framesConstructedCount
!= presContext
->FramesConstructedCount() ||
7120 framesReflowedCount
!= presContext
->FramesReflowedCount();
7127 // The type of coordinates to use for hit-testing input events
7128 // that are relative to the RCD's viewport frame.
7129 // On most platforms, use visual coordinates so that scrollbars
7131 // On mobile, use layout coordinates because hit-testing in
7132 // visual coordinates clashes with mobile viewport sizing, where
7133 // the ViewportFrame is sized to the initial containing block
7134 // (ICB) size, which is in layout coordinates. This is fine
7135 // because we don't need to be able to target scrollbars on mobile
7136 // (scrollbar dragging isn't supported).
7137 static ViewportType
ViewportTypeForInputEventsRelativeToRoot() {
7138 #ifdef MOZ_WIDGET_ANDROID
7139 return ViewportType::Layout
;
7141 return ViewportType::Visual
;
7145 nsIFrame
* PresShell::EventHandler::GetFrameToHandleNonTouchEvent(
7146 nsIFrame
* aRootFrameToHandleEvent
, WidgetGUIEvent
* aGUIEvent
) {
7147 MOZ_ASSERT(aGUIEvent
);
7148 MOZ_ASSERT(aGUIEvent
->mClass
!= eTouchEventClass
);
7150 ViewportType viewportType
= ViewportType::Layout
;
7151 if (aRootFrameToHandleEvent
->Type() == LayoutFrameType::Viewport
) {
7152 nsPresContext
* pc
= aRootFrameToHandleEvent
->PresContext();
7153 if (pc
->IsChrome()) {
7154 viewportType
= ViewportType::Visual
;
7155 } else if (pc
->IsRootContentDocument()) {
7156 viewportType
= ViewportTypeForInputEventsRelativeToRoot();
7159 RelativeTo relativeTo
{aRootFrameToHandleEvent
, viewportType
};
7160 nsPoint eventPoint
=
7161 nsLayoutUtils::GetEventCoordinatesRelativeTo(aGUIEvent
, relativeTo
);
7164 if (aGUIEvent
->mClass
== eMouseEventClass
) {
7165 WidgetMouseEvent
* mouseEvent
= aGUIEvent
->AsMouseEvent();
7166 if (mouseEvent
&& mouseEvent
->mIgnoreRootScrollFrame
) {
7167 flags
|= INPUT_IGNORE_ROOT_SCROLL_FRAME
;
7171 nsIFrame
* targetFrame
=
7172 FindFrameTargetedByInputEvent(aGUIEvent
, relativeTo
, eventPoint
, flags
);
7174 return aRootFrameToHandleEvent
;
7177 if (targetFrame
->PresShell() == mPresShell
) {
7178 // If found target is in mPresShell, we've already found it in the latest
7179 // layout so that we can use it.
7183 // If target is in a child document, we've not flushed its layout yet.
7184 PresShell
* childPresShell
= targetFrame
->PresShell();
7185 EventHandler
childEventHandler(*childPresShell
);
7186 AutoWeakFrame
weakFrame(aRootFrameToHandleEvent
);
7187 bool layoutChanged
=
7188 childEventHandler
.MaybeFlushPendingNotifications(aGUIEvent
);
7189 if (!weakFrame
.IsAlive()) {
7190 // Stop handling the event if the root frame to handle event is destroyed
7191 // by the reflow. (but why?)
7194 if (!layoutChanged
) {
7195 // If the layout in the child PresShell hasn't been changed, we don't
7196 // need to recompute the target.
7200 // Finally, we need to recompute the target with the latest layout.
7202 FindFrameTargetedByInputEvent(aGUIEvent
, relativeTo
, eventPoint
, flags
);
7204 return targetFrame
? targetFrame
: aRootFrameToHandleEvent
;
7207 bool PresShell::EventHandler::ComputeEventTargetFrameAndPresShellAtEventPoint(
7208 nsIFrame
* aRootFrameToHandleEvent
, WidgetGUIEvent
* aGUIEvent
,
7209 EventTargetData
* aEventTargetData
) {
7210 MOZ_ASSERT(aRootFrameToHandleEvent
);
7211 MOZ_ASSERT(aGUIEvent
);
7212 MOZ_ASSERT(aEventTargetData
);
7214 if (aGUIEvent
->mClass
== eTouchEventClass
) {
7215 nsIFrame
* targetFrameAtTouchEvent
= TouchManager::SetupTarget(
7216 aGUIEvent
->AsTouchEvent(), aRootFrameToHandleEvent
);
7217 aEventTargetData
->SetFrameAndComputePresShell(targetFrameAtTouchEvent
);
7221 nsIFrame
* targetFrame
=
7222 GetFrameToHandleNonTouchEvent(aRootFrameToHandleEvent
, aGUIEvent
);
7223 aEventTargetData
->SetFrameAndComputePresShell(targetFrame
);
7224 return !!aEventTargetData
->mFrame
;
7227 bool PresShell::EventHandler::DispatchPrecedingPointerEvent(
7228 nsIFrame
* aFrameForPresShell
, WidgetGUIEvent
* aGUIEvent
,
7229 nsIContent
* aPointerCapturingContent
, bool aDontRetargetEvents
,
7230 EventTargetData
* aEventTargetData
, nsEventStatus
* aEventStatus
) {
7231 MOZ_ASSERT(aFrameForPresShell
);
7232 MOZ_ASSERT(aGUIEvent
);
7233 MOZ_ASSERT(aEventTargetData
);
7234 MOZ_ASSERT(aEventStatus
);
7236 // Dispatch pointer events from the mouse or touch events. Regarding
7237 // pointer events from mouse, we should dispatch those pointer events to
7238 // the same target as the source mouse events. We pass the frame found
7239 // in hit test to PointerEventHandler and dispatch pointer events to it.
7241 // Regarding pointer events from touch, the behavior is different. Touch
7242 // events are dispatched to the same target as the target of touchstart.
7243 // Multiple touch points must be dispatched to the same document. Pointer
7244 // events from touch can be dispatched to different documents. We Pass the
7245 // original frame to PointerEventHandler, reentry PresShell::HandleEvent,
7246 // and do hit test for each point.
7247 nsIFrame
* targetFrame
= aGUIEvent
->mClass
== eTouchEventClass
7248 ? aFrameForPresShell
7249 : aEventTargetData
->mFrame
;
7251 if (aPointerCapturingContent
) {
7252 aEventTargetData
->mOverrideClickTarget
=
7253 GetOverrideClickTarget(aGUIEvent
, aFrameForPresShell
);
7254 aEventTargetData
->mPresShell
=
7255 PresShell::GetShellForEventTarget(nullptr, aPointerCapturingContent
);
7256 if (!aEventTargetData
->mPresShell
) {
7257 // If we can't process event for the capturing content, release
7259 PointerEventHandler::ReleaseIfCaptureByDescendant(
7260 aPointerCapturingContent
);
7264 targetFrame
= aPointerCapturingContent
->GetPrimaryFrame();
7265 aEventTargetData
->mFrame
= targetFrame
;
7268 AutoWeakFrame
weakTargetFrame(targetFrame
);
7269 AutoWeakFrame
weakFrame(aEventTargetData
->mFrame
);
7270 nsCOMPtr
<nsIContent
> content(aEventTargetData
->mContent
);
7271 RefPtr
<PresShell
> presShell(aEventTargetData
->mPresShell
);
7272 nsCOMPtr
<nsIContent
> targetContent
;
7273 PointerEventHandler::DispatchPointerFromMouseOrTouch(
7274 presShell
, aEventTargetData
->mFrame
, content
, aGUIEvent
,
7275 aDontRetargetEvents
, aEventStatus
, getter_AddRefs(targetContent
));
7277 // If the target frame is alive, the caller should keep handling the event
7278 // unless event target frame is destroyed.
7279 if (weakTargetFrame
.IsAlive()) {
7280 return weakFrame
.IsAlive();
7283 // If the event is not a mouse event, the caller should keep handling the
7284 // event unless event target frame is destroyed. Note that this case is
7285 // not defined by the spec.
7286 if (aGUIEvent
->mClass
!= eMouseEventClass
) {
7287 return weakFrame
.IsAlive();
7290 // Spec defines that mouse events must be dispatched to the same target as
7291 // the pointer event. If the target is no longer participating in its
7292 // ownerDocument's tree, fire the event at the original target's nearest
7294 if (!targetContent
) {
7298 // XXX Why don't we reset aEventTargetData->mContent here?
7299 aEventTargetData
->mFrame
= targetContent
->GetPrimaryFrame();
7300 aEventTargetData
->mPresShell
= PresShell::GetShellForEventTarget(
7301 aEventTargetData
->mFrame
, targetContent
);
7303 // If new target PresShel is not found, we cannot keep handling the event.
7304 return !!aEventTargetData
->mPresShell
;
7307 bool PresShell::EventHandler::MaybeHandleEventWithAccessibleCaret(
7308 nsIFrame
* aFrameForPresShell
, WidgetGUIEvent
* aGUIEvent
,
7309 nsEventStatus
* aEventStatus
) {
7310 MOZ_ASSERT(aGUIEvent
);
7311 MOZ_ASSERT(aEventStatus
);
7313 // Don't dispatch event to AccessibleCaretEventHub when the event status
7314 // is nsEventStatus_eConsumeNoDefault. This might be happened when content
7315 // preventDefault on the pointer events. In such case, we also call
7316 // preventDefault on mouse events to stop default behaviors.
7317 if (*aEventStatus
== nsEventStatus_eConsumeNoDefault
) {
7321 if (!AccessibleCaretEnabled(GetDocument()->GetDocShell())) {
7325 // AccessibleCaretEventHub handles only mouse, touch, and keyboard events.
7326 if (aGUIEvent
->mClass
!= eMouseEventClass
&&
7327 aGUIEvent
->mClass
!= eTouchEventClass
&&
7328 aGUIEvent
->mClass
!= eKeyboardEventClass
) {
7332 // First, try the event hub at the event point to handle a long press to
7333 // select a word in an unfocused window.
7335 EventTargetData
eventTargetData(nullptr);
7336 if (!ComputeEventTargetFrameAndPresShellAtEventPoint(
7337 aFrameForPresShell
, aGUIEvent
, &eventTargetData
)) {
7341 if (!eventTargetData
.mPresShell
) {
7345 RefPtr
<AccessibleCaretEventHub
> eventHub
=
7346 eventTargetData
.mPresShell
->GetAccessibleCaretEventHub();
7351 *aEventStatus
= eventHub
->HandleEvent(aGUIEvent
);
7352 if (*aEventStatus
!= nsEventStatus_eConsumeNoDefault
) {
7356 // If the event is consumed, cancel APZC panning by setting
7357 // mMultipleActionsPrevented.
7358 aGUIEvent
->mFlags
.mMultipleActionsPrevented
= true;
7362 // Then, we target the event to the event hub at the focused window.
7363 nsCOMPtr
<nsPIDOMWindowOuter
> window
= GetFocusedDOMWindowInOurWindow();
7367 RefPtr
<Document
> retargetEventDoc
= window
->GetExtantDoc();
7368 if (!retargetEventDoc
) {
7371 RefPtr
<PresShell
> presShell
= retargetEventDoc
->GetPresShell();
7376 RefPtr
<AccessibleCaretEventHub
> eventHub
=
7377 presShell
->GetAccessibleCaretEventHub();
7381 *aEventStatus
= eventHub
->HandleEvent(aGUIEvent
);
7382 if (*aEventStatus
!= nsEventStatus_eConsumeNoDefault
) {
7385 // If the event is consumed, cancel APZC panning by setting
7386 // mMultipleActionsPrevented.
7387 aGUIEvent
->mFlags
.mMultipleActionsPrevented
= true;
7391 bool PresShell::EventHandler::MaybeDiscardEvent(WidgetGUIEvent
* aGUIEvent
) {
7392 MOZ_ASSERT(aGUIEvent
);
7394 // If it is safe to dispatch events now, don't discard the event.
7395 if (nsContentUtils::IsSafeToRunScript()) {
7399 // If the event does not cause dispatching DOM event (i.e., internal event),
7400 // we can keep handling it even when it's not safe to run script.
7401 if (!aGUIEvent
->IsAllowedToDispatchDOMEvent()) {
7405 // If the event is a composition event, we need to let IMEStateManager know
7406 // it's discarded because it needs to listen all composition events to manage
7407 // TextComposition instance.
7408 if (aGUIEvent
->mClass
== eCompositionEventClass
) {
7409 IMEStateManager::OnCompositionEventDiscarded(
7410 aGUIEvent
->AsCompositionEvent());
7414 if (aGUIEvent
->IsIMERelatedEvent()) {
7415 nsPrintfCString
warning("%s event is discarded",
7416 ToChar(aGUIEvent
->mMessage
));
7417 NS_WARNING(warning
.get());
7419 #endif // #ifdef DEBUG
7421 nsContentUtils::WarnScriptWasIgnored(GetDocument());
7426 nsIContent
* PresShell::EventHandler::GetCapturingContentFor(
7427 WidgetGUIEvent
* aGUIEvent
) {
7428 return (aGUIEvent
->mClass
== ePointerEventClass
||
7429 aGUIEvent
->mClass
== eWheelEventClass
||
7430 aGUIEvent
->HasMouseEventMessage())
7431 ? PresShell::GetCapturingContent()
7435 bool PresShell::EventHandler::GetRetargetEventDocument(
7436 WidgetGUIEvent
* aGUIEvent
, Document
** aRetargetEventDocument
) {
7437 MOZ_ASSERT(aGUIEvent
);
7438 MOZ_ASSERT(aRetargetEventDocument
);
7440 *aRetargetEventDocument
= nullptr;
7442 // key and IME related events should not cross top level window boundary.
7443 // Basically, such input events should be fired only on focused widget.
7444 // However, some IMEs might need to clean up composition after focused
7445 // window is deactivated. And also some tests on MozMill want to test key
7446 // handling on deactivated window because MozMill window can be activated
7447 // during tests. So, there is no merit the events should be redirected to
7448 // active window. So, the events should be handled on the last focused
7449 // content in the last focused DOM window in same top level window.
7450 // Note, if no DOM window has been focused yet, we can discard the events.
7451 if (aGUIEvent
->IsTargetedAtFocusedWindow()) {
7452 nsCOMPtr
<nsPIDOMWindowOuter
> window
= GetFocusedDOMWindowInOurWindow();
7453 // No DOM window in same top level window has not been focused yet,
7454 // discard the events.
7459 RefPtr
<Document
> retargetEventDoc
= window
->GetExtantDoc();
7460 if (!retargetEventDoc
) {
7463 retargetEventDoc
.forget(aRetargetEventDocument
);
7467 nsIContent
* capturingContent
=
7468 EventHandler::GetCapturingContentFor(aGUIEvent
);
7469 if (capturingContent
) {
7470 // if the mouse is being captured then retarget the mouse event at the
7471 // document that is being captured.
7472 RefPtr
<Document
> retargetEventDoc
= capturingContent
->GetComposedDoc();
7473 retargetEventDoc
.forget(aRetargetEventDocument
);
7478 if (aGUIEvent
->mClass
== eTouchEventClass
||
7479 aGUIEvent
->mClass
== eMouseEventClass
||
7480 aGUIEvent
->mClass
== eWheelEventClass
) {
7481 RefPtr
<Document
> retargetEventDoc
= mPresShell
->GetPrimaryContentDocument();
7482 retargetEventDoc
.forget(aRetargetEventDocument
);
7485 #endif // #ifdef ANDROID
7487 // When we don't find another document to handle the event, we need to keep
7488 // handling the event by ourselves.
7492 nsIFrame
* PresShell::EventHandler::GetFrameForHandlingEventWith(
7493 WidgetGUIEvent
* aGUIEvent
, Document
* aRetargetDocument
,
7494 nsIFrame
* aFrameForPresShell
) {
7495 MOZ_ASSERT(aGUIEvent
);
7496 MOZ_ASSERT(aRetargetDocument
);
7498 RefPtr
<PresShell
> retargetPresShell
= aRetargetDocument
->GetPresShell();
7499 // Even if the document doesn't have PresShell, i.e., it's invisible, we
7500 // need to dispatch only KeyboardEvent in its nearest visible document
7501 // because key focus shouldn't be caught by invisible document.
7502 if (!retargetPresShell
) {
7503 if (!aGUIEvent
->HasKeyEventMessage()) {
7506 Document
* retargetEventDoc
= aRetargetDocument
;
7507 while (!retargetPresShell
) {
7508 retargetEventDoc
= retargetEventDoc
->GetInProcessParentDocument();
7509 if (!retargetEventDoc
) {
7512 retargetPresShell
= retargetEventDoc
->GetPresShell();
7516 // If the found PresShell is this instance, caller needs to keep handling
7517 // aGUIEvent by itself. Therefore, return the given frame which was set
7518 // to aFrame of HandleEvent().
7519 if (retargetPresShell
== mPresShell
) {
7520 return aFrameForPresShell
;
7523 // Use root frame of the new PresShell if there is.
7524 nsIFrame
* rootFrame
= retargetPresShell
->GetRootFrame();
7529 // Otherwise, and if aGUIEvent requires content of PresShell, caller should
7530 // stop handling the event.
7531 if (aGUIEvent
->mMessage
== eQueryTextContent
||
7532 aGUIEvent
->IsContentCommandEvent()) {
7536 // Otherwise, use nearest ancestor frame which includes the PresShell.
7537 return GetNearestFrameContainingPresShell(retargetPresShell
);
7540 bool PresShell::EventHandler::MaybeHandleEventWithAnotherPresShell(
7541 nsIFrame
* aFrameForPresShell
, WidgetGUIEvent
* aGUIEvent
,
7542 nsEventStatus
* aEventStatus
, nsresult
* aRv
) {
7543 MOZ_ASSERT(aGUIEvent
);
7544 MOZ_ASSERT(aEventStatus
);
7549 RefPtr
<Document
> retargetEventDoc
;
7550 if (!GetRetargetEventDocument(aGUIEvent
, getter_AddRefs(retargetEventDoc
))) {
7551 // Nobody can handle this event. So, treat as handled by somebody to make
7552 // caller do nothing anymore.
7556 // If there is no proper retarget document, the caller should handle the
7558 if (!retargetEventDoc
) {
7562 nsIFrame
* frame
= GetFrameForHandlingEventWith(aGUIEvent
, retargetEventDoc
,
7563 aFrameForPresShell
);
7565 // Nobody can handle this event. So, treat as handled by somebody to make
7566 // caller do nothing anymore.
7570 // If we reached same frame as set to HandleEvent(), the caller should handle
7571 // the event by itself.
7572 if (frame
== aFrameForPresShell
) {
7576 // We need to handle aGUIEvent with another PresShell.
7577 RefPtr
<PresShell
> presShell
= frame
->PresContext()->PresShell();
7578 *aRv
= presShell
->HandleEvent(frame
, aGUIEvent
, true, aEventStatus
);
7582 bool PresShell::EventHandler::MaybeDiscardOrDelayKeyboardEvent(
7583 WidgetGUIEvent
* aGUIEvent
) {
7584 MOZ_ASSERT(aGUIEvent
);
7586 if (aGUIEvent
->mClass
!= eKeyboardEventClass
) {
7590 Document
* document
= GetDocument();
7591 if (!document
|| !document
->EventHandlingSuppressed()) {
7595 MOZ_ASSERT_IF(InputTaskManager::CanSuspendInputEvent(),
7596 !InputTaskManager::Get()->IsSuspended());
7598 if (aGUIEvent
->mMessage
== eKeyDown
) {
7599 mPresShell
->mNoDelayedKeyEvents
= true;
7600 } else if (!mPresShell
->mNoDelayedKeyEvents
) {
7601 UniquePtr
<DelayedKeyEvent
> delayedKeyEvent
=
7602 MakeUnique
<DelayedKeyEvent
>(aGUIEvent
->AsKeyboardEvent());
7603 PushDelayedEventIntoQueue(std::move(delayedKeyEvent
));
7605 aGUIEvent
->mFlags
.mIsSuppressedOrDelayed
= true;
7609 bool PresShell::EventHandler::MaybeDiscardOrDelayMouseEvent(
7610 nsIFrame
* aFrameToHandleEvent
, WidgetGUIEvent
* aGUIEvent
) {
7611 MOZ_ASSERT(aFrameToHandleEvent
);
7612 MOZ_ASSERT(aGUIEvent
);
7614 if (aGUIEvent
->mClass
!= eMouseEventClass
) {
7618 if (!aFrameToHandleEvent
->PresContext()
7620 ->EventHandlingSuppressed()) {
7624 MOZ_ASSERT_IF(InputTaskManager::CanSuspendInputEvent() &&
7625 aGUIEvent
->mMessage
!= eMouseMove
,
7626 !InputTaskManager::Get()->IsSuspended());
7628 if (aGUIEvent
->mMessage
== eMouseDown
) {
7629 mPresShell
->mNoDelayedMouseEvents
= true;
7630 } else if (!mPresShell
->mNoDelayedMouseEvents
&&
7631 (aGUIEvent
->mMessage
== eMouseUp
||
7632 // contextmenu is triggered after right mouseup on Windows and
7633 // right mousedown on other platforms.
7634 aGUIEvent
->mMessage
== eContextMenu
||
7635 aGUIEvent
->mMessage
== eMouseExitFromWidget
)) {
7636 UniquePtr
<DelayedMouseEvent
> delayedMouseEvent
=
7637 MakeUnique
<DelayedMouseEvent
>(aGUIEvent
->AsMouseEvent());
7638 PushDelayedEventIntoQueue(std::move(delayedMouseEvent
));
7641 // If there is a suppressed event listener associated with the document,
7642 // notify it about the suppressed mouse event. This allows devtools
7643 // features to continue receiving mouse events even when the devtools
7644 // debugger has paused execution in a page.
7645 RefPtr
<EventListener
> suppressedListener
= aFrameToHandleEvent
->PresContext()
7647 ->GetSuppressedEventListener();
7648 if (!suppressedListener
||
7649 aGUIEvent
->AsMouseEvent()->mReason
== WidgetMouseEvent::eSynthesized
) {
7653 nsCOMPtr
<nsIContent
> targetContent
;
7654 aFrameToHandleEvent
->GetContentForEvent(aGUIEvent
,
7655 getter_AddRefs(targetContent
));
7656 if (targetContent
) {
7657 aGUIEvent
->mTarget
= targetContent
;
7660 nsCOMPtr
<EventTarget
> eventTarget
= aGUIEvent
->mTarget
;
7661 RefPtr
<Event
> event
= EventDispatcher::CreateEvent(
7662 eventTarget
, aFrameToHandleEvent
->PresContext(), aGUIEvent
, u
""_ns
);
7664 suppressedListener
->HandleEvent(*event
);
7668 nsIFrame
* PresShell::EventHandler::MaybeFlushThrottledStyles(
7669 nsIFrame
* aFrameForPresShell
) {
7670 if (!GetDocument()) {
7671 // XXX Only when mPresShell has document, we'll try to look for a frame
7672 // containing mPresShell even if given frame is nullptr. Does this
7674 return aFrameForPresShell
;
7677 PresShell
* rootPresShell
= mPresShell
->GetRootPresShell();
7678 if (NS_WARN_IF(!rootPresShell
)) {
7681 Document
* rootDocument
= rootPresShell
->GetDocument();
7682 if (NS_WARN_IF(!rootDocument
)) {
7686 AutoWeakFrame
weakFrameForPresShell(aFrameForPresShell
);
7687 { // scope for scriptBlocker.
7688 nsAutoScriptBlocker scriptBlocker
;
7689 FlushThrottledStyles(*rootDocument
);
7692 if (weakFrameForPresShell
.IsAlive()) {
7693 return aFrameForPresShell
;
7696 return GetNearestFrameContainingPresShell(mPresShell
);
7699 nsIFrame
* PresShell::EventHandler::ComputeRootFrameToHandleEvent(
7700 nsIFrame
* aFrameForPresShell
, WidgetGUIEvent
* aGUIEvent
,
7701 nsIContent
* aCapturingContent
, bool* aIsCapturingContentIgnored
,
7702 bool* aIsCaptureRetargeted
) {
7703 MOZ_ASSERT(aFrameForPresShell
);
7704 MOZ_ASSERT(aGUIEvent
);
7705 MOZ_ASSERT(aIsCapturingContentIgnored
);
7706 MOZ_ASSERT(aIsCaptureRetargeted
);
7708 nsIFrame
* rootFrameToHandleEvent
= ComputeRootFrameToHandleEventWithPopup(
7709 aFrameForPresShell
, aGUIEvent
, aCapturingContent
,
7710 aIsCapturingContentIgnored
);
7711 if (*aIsCapturingContentIgnored
) {
7712 // If the capturing content is ignored, we don't need to respect it.
7713 return rootFrameToHandleEvent
;
7716 if (!aCapturingContent
) {
7717 return rootFrameToHandleEvent
;
7720 // If we have capturing content, let's compute root frame with it again.
7721 return ComputeRootFrameToHandleEventWithCapturingContent(
7722 rootFrameToHandleEvent
, aCapturingContent
, aIsCapturingContentIgnored
,
7723 aIsCaptureRetargeted
);
7726 nsIFrame
* PresShell::EventHandler::ComputeRootFrameToHandleEventWithPopup(
7727 nsIFrame
* aRootFrameToHandleEvent
, WidgetGUIEvent
* aGUIEvent
,
7728 nsIContent
* aCapturingContent
, bool* aIsCapturingContentIgnored
) {
7729 MOZ_ASSERT(aRootFrameToHandleEvent
);
7730 MOZ_ASSERT(aGUIEvent
);
7731 MOZ_ASSERT(aIsCapturingContentIgnored
);
7733 *aIsCapturingContentIgnored
= false;
7735 nsPresContext
* framePresContext
= aRootFrameToHandleEvent
->PresContext();
7736 nsPresContext
* rootPresContext
= framePresContext
->GetRootPresContext();
7737 NS_ASSERTION(rootPresContext
== GetPresContext()->GetRootPresContext(),
7738 "How did we end up outside the connected "
7739 "prescontext/viewmanager hierarchy?");
7740 nsIFrame
* popupFrame
= nsLayoutUtils::GetPopupFrameForEventCoordinates(
7741 rootPresContext
, aGUIEvent
);
7743 return aRootFrameToHandleEvent
;
7746 // If a remote browser is currently capturing input break out if we
7747 // detect a chrome generated popup.
7748 // XXXedgar, do we need to check fission OOP iframe?
7749 if (aCapturingContent
&&
7750 EventStateManager::IsTopLevelRemoteTarget(aCapturingContent
)) {
7751 *aIsCapturingContentIgnored
= true;
7754 // If the popupFrame is an ancestor of the 'frame', the frame should
7755 // handle the event, otherwise, the popup should handle it.
7756 if (nsContentUtils::ContentIsCrossDocDescendantOf(
7757 framePresContext
->GetPresShell()->GetDocument(),
7758 popupFrame
->GetContent())) {
7759 return aRootFrameToHandleEvent
;
7762 // If we aren't starting our event dispatch from the root frame of the
7763 // root prescontext, then someone must be capturing the mouse. In that
7764 // case we only want to use the popup list if the capture is
7765 // inside the popup.
7766 if (framePresContext
== rootPresContext
&&
7767 aRootFrameToHandleEvent
== FrameConstructor()->GetRootFrame()) {
7771 if (aCapturingContent
&& !*aIsCapturingContentIgnored
&&
7772 aCapturingContent
->IsInclusiveDescendantOf(popupFrame
->GetContent())) {
7776 return aRootFrameToHandleEvent
;
7780 PresShell::EventHandler::ComputeRootFrameToHandleEventWithCapturingContent(
7781 nsIFrame
* aRootFrameToHandleEvent
, nsIContent
* aCapturingContent
,
7782 bool* aIsCapturingContentIgnored
, bool* aIsCaptureRetargeted
) {
7783 MOZ_ASSERT(aRootFrameToHandleEvent
);
7784 MOZ_ASSERT(aCapturingContent
);
7785 MOZ_ASSERT(aIsCapturingContentIgnored
);
7786 MOZ_ASSERT(aIsCaptureRetargeted
);
7788 *aIsCapturingContentIgnored
= false;
7789 *aIsCaptureRetargeted
= false;
7791 // If a capture is active, determine if the BrowsingContext is active. If
7792 // not, clear the capture and target the mouse event normally instead. This
7793 // would occur if the mouse button is held down while a tab change occurs.
7794 // If the BrowsingContext is active, look for a scrolling container.
7795 BrowsingContext
* bc
= GetPresContext()->Document()->GetBrowsingContext();
7796 if (!bc
|| !bc
->IsActive()) {
7797 ClearMouseCapture();
7798 *aIsCapturingContentIgnored
= true;
7799 return aRootFrameToHandleEvent
;
7802 if (PresShell::sCapturingContentInfo
.mRetargetToElement
) {
7803 *aIsCaptureRetargeted
= true;
7804 return aRootFrameToHandleEvent
;
7807 // A check was already done above to ensure that aCapturingContent is
7808 // in this presshell.
7809 NS_ASSERTION(aCapturingContent
->OwnerDoc() == GetDocument(),
7810 "Unexpected document");
7811 nsIFrame
* captureFrame
= aCapturingContent
->GetPrimaryFrame();
7812 if (!captureFrame
) {
7813 return aRootFrameToHandleEvent
;
7816 if (aCapturingContent
->IsHTMLElement(nsGkAtoms::select
)) {
7817 // a dropdown <select> has a child in its selectPopupList and we should
7818 // capture on that instead.
7819 nsIFrame
* childFrame
=
7820 captureFrame
->GetChildList(nsIFrame::kSelectPopupList
).FirstChild();
7822 captureFrame
= childFrame
;
7826 // scrollable frames should use the scrolling container as the root instead
7828 nsIScrollableFrame
* scrollFrame
= do_QueryFrame(captureFrame
);
7829 return scrollFrame
? scrollFrame
->GetScrolledFrame()
7830 : aRootFrameToHandleEvent
;
7834 PresShell::EventHandler::HandleEventWithPointerCapturingContentWithoutItsFrame(
7835 nsIFrame
* aFrameForPresShell
, WidgetGUIEvent
* aGUIEvent
,
7836 nsIContent
* aPointerCapturingContent
, nsEventStatus
* aEventStatus
) {
7837 MOZ_ASSERT(aGUIEvent
);
7838 MOZ_ASSERT(aPointerCapturingContent
);
7839 MOZ_ASSERT(!aPointerCapturingContent
->GetPrimaryFrame(),
7840 "Handle the event with frame rather than only with the content");
7841 MOZ_ASSERT(aEventStatus
);
7843 RefPtr
<PresShell
> presShellForCapturingContent
=
7844 PresShell::GetShellForEventTarget(nullptr, aPointerCapturingContent
);
7845 if (!presShellForCapturingContent
) {
7846 // If we can't process event for the capturing content, release
7848 PointerEventHandler::ReleaseIfCaptureByDescendant(aPointerCapturingContent
);
7852 nsCOMPtr
<nsIContent
> overrideClickTarget
=
7853 GetOverrideClickTarget(aGUIEvent
, aFrameForPresShell
);
7855 // Dispatch events to the capturing content even it's frame is
7857 PointerEventHandler::DispatchPointerFromMouseOrTouch(
7858 presShellForCapturingContent
, nullptr, aPointerCapturingContent
,
7859 aGUIEvent
, false, aEventStatus
, nullptr);
7861 if (presShellForCapturingContent
== mPresShell
) {
7862 return HandleEventWithTarget(aGUIEvent
, nullptr, aPointerCapturingContent
,
7863 aEventStatus
, true, nullptr,
7864 overrideClickTarget
);
7867 EventHandler
eventHandlerForCapturingContent(
7868 std::move(presShellForCapturingContent
));
7869 return eventHandlerForCapturingContent
.HandleEventWithTarget(
7870 aGUIEvent
, nullptr, aPointerCapturingContent
, aEventStatus
, true, nullptr,
7871 overrideClickTarget
);
7874 nsresult
PresShell::EventHandler::HandleEventAtFocusedContent(
7875 WidgetGUIEvent
* aGUIEvent
, nsEventStatus
* aEventStatus
) {
7876 MOZ_ASSERT(aGUIEvent
);
7877 MOZ_ASSERT(aGUIEvent
->IsTargetedAtFocusedContent());
7878 MOZ_ASSERT(aEventStatus
);
7880 AutoCurrentEventInfoSetter
eventInfoSetter(*this);
7882 RefPtr
<Element
> eventTargetElement
=
7883 ComputeFocusedEventTargetElement(aGUIEvent
);
7885 mPresShell
->mCurrentEventFrame
= nullptr;
7886 if (eventTargetElement
) {
7887 nsresult rv
= NS_OK
;
7888 if (MaybeHandleEventWithAnotherPresShell(eventTargetElement
, aGUIEvent
,
7889 aEventStatus
, &rv
)) {
7894 // If we cannot handle the event with mPresShell, let's try to handle it
7895 // with parent PresShell.
7896 mPresShell
->mCurrentEventContent
= eventTargetElement
;
7897 if (!mPresShell
->GetCurrentEventContent() ||
7898 !mPresShell
->GetCurrentEventFrame() ||
7899 InZombieDocument(mPresShell
->mCurrentEventContent
)) {
7900 return RetargetEventToParent(aGUIEvent
, aEventStatus
);
7904 HandleEventWithCurrentEventInfo(aGUIEvent
, aEventStatus
, true, nullptr);
7907 mPresShell
->ShowEventTargetDebug();
7913 Element
* PresShell::EventHandler::ComputeFocusedEventTargetElement(
7914 WidgetGUIEvent
* aGUIEvent
) {
7915 MOZ_ASSERT(aGUIEvent
);
7916 MOZ_ASSERT(aGUIEvent
->IsTargetedAtFocusedContent());
7918 // key and IME related events go to the focused frame in this DOM window.
7919 nsPIDOMWindowOuter
* window
= GetDocument()->GetWindow();
7920 nsCOMPtr
<nsPIDOMWindowOuter
> focusedWindow
;
7921 Element
* eventTargetElement
= nsFocusManager::GetFocusedDescendant(
7922 window
, nsFocusManager::eOnlyCurrentWindow
,
7923 getter_AddRefs(focusedWindow
));
7925 // otherwise, if there is no focused content or the focused content has
7926 // no frame, just use the root content. This ensures that key events
7927 // still get sent to the window properly if nothing is focused or if a
7928 // frame goes away while it is focused.
7929 if (!eventTargetElement
|| !eventTargetElement
->GetPrimaryFrame()) {
7930 eventTargetElement
= GetDocument()->GetUnfocusedKeyEventTarget();
7933 switch (aGUIEvent
->mMessage
) {
7935 sLastKeyDownEventTargetElement
= eventTargetElement
;
7936 return eventTargetElement
;
7939 if (!sLastKeyDownEventTargetElement
) {
7940 return eventTargetElement
;
7942 // If a different element is now focused for the keypress/keyup event
7943 // than what was focused during the keydown event, check if the new
7944 // focused element is not in a chrome document any more, and if so,
7945 // retarget the event back at the keydown target. This prevents a
7946 // content area from grabbing the focus from chrome in-between key
7948 if (eventTargetElement
) {
7949 bool keyDownIsChrome
= nsContentUtils::IsChromeDoc(
7950 sLastKeyDownEventTargetElement
->GetComposedDoc());
7951 if (keyDownIsChrome
!= nsContentUtils::IsChromeDoc(
7952 eventTargetElement
->GetComposedDoc()) ||
7953 (keyDownIsChrome
&& BrowserParent::GetFrom(eventTargetElement
))) {
7954 eventTargetElement
= sLastKeyDownEventTargetElement
;
7958 if (aGUIEvent
->mMessage
== eKeyUp
) {
7959 sLastKeyDownEventTargetElement
= nullptr;
7963 return eventTargetElement
;
7967 bool PresShell::EventHandler::MaybeHandleEventWithAnotherPresShell(
7968 Element
* aEventTargetElement
, WidgetGUIEvent
* aGUIEvent
,
7969 nsEventStatus
* aEventStatus
, nsresult
* aRv
) {
7970 MOZ_ASSERT(aEventTargetElement
);
7971 MOZ_ASSERT(aGUIEvent
);
7972 MOZ_ASSERT(!aGUIEvent
->IsUsingCoordinates());
7973 MOZ_ASSERT(aEventStatus
);
7976 Document
* eventTargetDocument
= aEventTargetElement
->OwnerDoc();
7977 if (!eventTargetDocument
|| eventTargetDocument
== GetDocument()) {
7982 RefPtr
<PresShell
> eventTargetPresShell
= eventTargetDocument
->GetPresShell();
7983 if (!eventTargetPresShell
) {
7985 return true; // No PresShell can handle the event.
7988 EventHandler
eventHandler(std::move(eventTargetPresShell
));
7989 *aRv
= eventHandler
.HandleRetargetedEvent(aGUIEvent
, aEventStatus
,
7990 aEventTargetElement
);
7994 nsresult
PresShell::EventHandler::HandleEventWithFrameForPresShell(
7995 nsIFrame
* aFrameForPresShell
, WidgetGUIEvent
* aGUIEvent
,
7996 nsEventStatus
* aEventStatus
) {
7997 MOZ_ASSERT(aGUIEvent
);
7998 MOZ_ASSERT(!aGUIEvent
->IsUsingCoordinates());
7999 MOZ_ASSERT(!aGUIEvent
->IsTargetedAtFocusedContent());
8000 MOZ_ASSERT(aEventStatus
);
8002 AutoCurrentEventInfoSetter
eventInfoSetter(*this, aFrameForPresShell
,
8005 nsresult rv
= NS_OK
;
8006 if (mPresShell
->GetCurrentEventFrame()) {
8008 HandleEventWithCurrentEventInfo(aGUIEvent
, aEventStatus
, true, nullptr);
8012 mPresShell
->ShowEventTargetDebug();
8018 Document
* PresShell::GetPrimaryContentDocument() {
8019 nsPresContext
* context
= GetPresContext();
8020 if (!context
|| !context
->IsRoot()) {
8024 nsCOMPtr
<nsIDocShellTreeItem
> shellAsTreeItem
= context
->GetDocShell();
8025 if (!shellAsTreeItem
) {
8029 nsCOMPtr
<nsIDocShellTreeOwner
> owner
;
8030 shellAsTreeItem
->GetTreeOwner(getter_AddRefs(owner
));
8035 // now get the primary content shell (active tab)
8036 nsCOMPtr
<nsIDocShellTreeItem
> item
;
8037 owner
->GetPrimaryContentShell(getter_AddRefs(item
));
8038 nsCOMPtr
<nsIDocShell
> childDocShell
= do_QueryInterface(item
);
8039 if (!childDocShell
) {
8043 return childDocShell
->GetExtantDocument();
8047 void PresShell::ShowEventTargetDebug() {
8048 if (nsIFrame::GetShowEventTargetFrameBorder() && GetCurrentEventFrame()) {
8049 if (mDrawEventTargetFrame
) {
8050 mDrawEventTargetFrame
->InvalidateFrame();
8053 mDrawEventTargetFrame
= mCurrentEventFrame
;
8054 mDrawEventTargetFrame
->InvalidateFrame();
8059 nsresult
PresShell::EventHandler::HandleEventWithTarget(
8060 WidgetEvent
* aEvent
, nsIFrame
* aNewEventFrame
, nsIContent
* aNewEventContent
,
8061 nsEventStatus
* aEventStatus
, bool aIsHandlingNativeEvent
,
8062 nsIContent
** aTargetContent
, nsIContent
* aOverrideClickTarget
) {
8064 MOZ_DIAGNOSTIC_ASSERT(aEvent
->IsTrusted());
8067 MOZ_ASSERT(!aNewEventFrame
||
8068 aNewEventFrame
->PresContext()->GetPresShell() == mPresShell
,
8070 if (aNewEventContent
) {
8071 Document
* doc
= aNewEventContent
->GetComposedDoc();
8072 NS_ASSERTION(doc
, "event for content that isn't in a document");
8073 // NOTE: We don't require that the document still have a PresShell.
8077 NS_ENSURE_STATE(!aNewEventContent
||
8078 aNewEventContent
->GetComposedDoc() == GetDocument());
8079 AutoPointerEventTargetUpdater
updater(mPresShell
, aEvent
, aNewEventFrame
,
8081 AutoCurrentEventInfoSetter
eventInfoSetter(*this, aNewEventFrame
,
8083 nsresult rv
= HandleEventWithCurrentEventInfo(aEvent
, aEventStatus
, false,
8084 aOverrideClickTarget
);
8090 class MOZ_RAII AutoEventHandler final
{
8092 AutoEventHandler(WidgetEvent
* aEvent
, Document
* aDocument
) : mEvent(aEvent
) {
8094 MOZ_ASSERT(mEvent
->IsTrusted());
8096 if (mEvent
->mMessage
== eMouseDown
) {
8097 PresShell::ReleaseCapturingContent();
8098 PresShell::AllowMouseCapture(true);
8100 if (NeedsToUpdateCurrentMouseBtnState()) {
8101 WidgetMouseEvent
* mouseEvent
= mEvent
->AsMouseEvent();
8103 EventStateManager::sCurrentMouseBtn
= mouseEvent
->mButton
;
8108 ~AutoEventHandler() {
8109 if (mEvent
->mMessage
== eMouseDown
) {
8110 PresShell::AllowMouseCapture(false);
8112 if (NeedsToUpdateCurrentMouseBtnState()) {
8113 EventStateManager::sCurrentMouseBtn
= MouseButton::eNotPressed
;
8118 bool NeedsToUpdateCurrentMouseBtnState() const {
8119 return mEvent
->mMessage
== eMouseDown
|| mEvent
->mMessage
== eMouseUp
||
8120 mEvent
->mMessage
== ePointerDown
|| mEvent
->mMessage
== ePointerUp
;
8123 WidgetEvent
* mEvent
;
8126 } // anonymous namespace
8128 nsresult
PresShell::EventHandler::HandleEventWithCurrentEventInfo(
8129 WidgetEvent
* aEvent
, nsEventStatus
* aEventStatus
,
8130 bool aIsHandlingNativeEvent
, nsIContent
* aOverrideClickTarget
) {
8132 MOZ_ASSERT(aEventStatus
);
8134 RefPtr
<EventStateManager
> manager
= GetPresContext()->EventStateManager();
8136 // If we cannot handle the event with mPresShell because of no target,
8137 // just record the response time.
8138 // XXX Is this intentional? In such case, the score is really good because
8139 // of nothing to do. So, it may make average and median better.
8140 if (NS_EVENT_NEEDS_FRAME(aEvent
) && !mPresShell
->GetCurrentEventFrame() &&
8141 !mPresShell
->GetCurrentEventContent()) {
8142 RecordEventHandlingResponsePerformance(aEvent
);
8146 if (mPresShell
->mCurrentEventContent
&& aEvent
->IsTargetedAtFocusedWindow()) {
8147 nsFocusManager
* fm
= nsFocusManager::GetFocusManager();
8149 // This may run script now. So, mPresShell might be destroyed after here.
8150 fm
->FlushBeforeEventHandlingIfNeeded(mPresShell
->mCurrentEventContent
);
8154 bool touchIsNew
= false;
8155 if (!PrepareToDispatchEvent(aEvent
, aEventStatus
, &touchIsNew
)) {
8159 // We finished preparing to dispatch the event. So, let's record the
8161 RecordEventPreparationPerformance(aEvent
);
8163 AutoHandlingUserInputStatePusher
userInpStatePusher(
8164 UserActivation::IsUserInteractionEvent(aEvent
), aEvent
);
8165 AutoEventHandler
eventHandler(aEvent
, GetDocument());
8166 AutoPopupStatePusher
popupStatePusher(
8167 PopupBlocker::GetEventPopupControlState(aEvent
));
8169 // FIXME. If the event was reused, we need to clear the old target,
8171 aEvent
->mTarget
= nullptr;
8173 HandlingTimeAccumulator
handlingTimeAccumulator(*this, aEvent
);
8175 nsresult rv
= DispatchEvent(manager
, aEvent
, touchIsNew
, aEventStatus
,
8176 aOverrideClickTarget
);
8178 if (!mPresShell
->IsDestroying() && aIsHandlingNativeEvent
) {
8179 // Ensure that notifications to IME should be sent before getting next
8180 // native event from the event queue.
8181 // XXX Should we check the event message or event class instead of
8182 // using aIsHandlingNativeEvent?
8183 manager
->TryToFlushPendingNotificationsToIME();
8186 FinalizeHandlingEvent(aEvent
);
8188 RecordEventHandlingResponsePerformance(aEvent
);
8190 return rv
; // Result of DispatchEvent()
8193 nsresult
PresShell::EventHandler::DispatchEvent(
8194 EventStateManager
* aEventStateManager
, WidgetEvent
* aEvent
,
8195 bool aTouchIsNew
, nsEventStatus
* aEventStatus
,
8196 nsIContent
* aOverrideClickTarget
) {
8197 MOZ_ASSERT(aEventStateManager
);
8199 MOZ_ASSERT(aEventStatus
);
8201 // 1. Give event to event manager for pre event state changes and
8202 // generation of synthetic events.
8203 { // Scope for presContext
8204 RefPtr
<nsPresContext
> presContext
= GetPresContext();
8205 nsCOMPtr
<nsIContent
> eventContent
= mPresShell
->mCurrentEventContent
;
8206 nsresult rv
= aEventStateManager
->PreHandleEvent(
8207 presContext
, aEvent
, mPresShell
->mCurrentEventFrame
, eventContent
,
8208 aEventStatus
, aOverrideClickTarget
);
8209 if (NS_FAILED(rv
)) {
8214 // 2. Give event to the DOM for third party and JS use.
8215 bool wasHandlingKeyBoardEvent
= nsContentUtils::IsHandlingKeyBoardEvent();
8216 if (aEvent
->mClass
== eKeyboardEventClass
) {
8217 nsContentUtils::SetIsHandlingKeyBoardEvent(true);
8219 // If EventStateManager or something wants reply from remote process and
8220 // needs to win any other event listeners in chrome, the event is both
8221 // stopped its propagation and marked as "waiting reply from remote
8222 // process". In this case, PresShell shouldn't dispatch the event into
8223 // the DOM tree because they don't have a chance to stop propagation in
8224 // the system event group. On the other hand, if its propagation is not
8225 // stopped, that means that the event may be reserved by chrome. If it's
8226 // reserved by chrome, the event shouldn't be sent to any remote
8227 // processes. In this case, PresShell needs to dispatch the event to
8228 // the DOM tree for checking if it's reserved.
8229 if (aEvent
->IsAllowedToDispatchDOMEvent() &&
8230 !(aEvent
->PropagationStopped() &&
8231 aEvent
->IsWaitingReplyFromRemoteProcess())) {
8232 MOZ_ASSERT(nsContentUtils::IsSafeToRunScript(),
8233 "Somebody changed aEvent to cause a DOM event!");
8234 nsPresShellEventCB
eventCB(mPresShell
);
8235 if (nsIFrame
* target
= mPresShell
->GetCurrentEventFrame()) {
8236 if (target
->OnlySystemGroupDispatch(aEvent
->mMessage
)) {
8237 aEvent
->StopPropagation();
8240 if (aEvent
->mClass
== eTouchEventClass
) {
8241 DispatchTouchEventToDOM(aEvent
, aEventStatus
, &eventCB
, aTouchIsNew
);
8243 DispatchEventToDOM(aEvent
, aEventStatus
, &eventCB
);
8247 nsContentUtils::SetIsHandlingKeyBoardEvent(wasHandlingKeyBoardEvent
);
8249 if (mPresShell
->IsDestroying()) {
8253 // 3. Give event to event manager for post event state changes and
8254 // generation of synthetic events.
8255 // Refetch the prescontext, in case it changed.
8256 RefPtr
<nsPresContext
> presContext
= GetPresContext();
8257 return aEventStateManager
->PostHandleEvent(
8258 presContext
, aEvent
, mPresShell
->GetCurrentEventFrame(), aEventStatus
,
8259 aOverrideClickTarget
);
8262 bool PresShell::EventHandler::PrepareToDispatchEvent(
8263 WidgetEvent
* aEvent
, nsEventStatus
* aEventStatus
, bool* aTouchIsNew
) {
8264 MOZ_ASSERT(aEvent
->IsTrusted());
8265 MOZ_ASSERT(aEventStatus
);
8266 MOZ_ASSERT(aTouchIsNew
);
8268 *aTouchIsNew
= false;
8269 if (aEvent
->IsUserAction()) {
8270 mPresShell
->mHasHandledUserInput
= true;
8273 switch (aEvent
->mMessage
) {
8277 WidgetKeyboardEvent
* keyboardEvent
= aEvent
->AsKeyboardEvent();
8278 MaybeHandleKeyboardEventBeforeDispatch(keyboardEvent
);
8282 bool allowCapture
= EventStateManager::GetActiveEventStateManager() &&
8284 GetPresContext()->EventStateManager() ==
8285 EventStateManager::GetActiveEventStateManager();
8286 PresShell::AllowMouseCapture(allowCapture
);
8290 nsCOMPtr
<nsIDragSession
> session
= nsContentUtils::GetDragSession();
8292 bool onlyChromeDrop
= false;
8293 session
->GetOnlyChromeDrop(&onlyChromeDrop
);
8294 if (onlyChromeDrop
) {
8295 aEvent
->mFlags
.mOnlyChromeDispatch
= true;
8300 case eContextMenu
: {
8301 // If we cannot open context menu even though eContextMenu is fired, we
8302 // should stop dispatching it into the DOM.
8303 WidgetMouseEvent
* mouseEvent
= aEvent
->AsMouseEvent();
8304 if (mouseEvent
->IsContextMenuKeyEvent() &&
8305 !AdjustContextMenuKeyEvent(mouseEvent
)) {
8309 // If "Shift" state is active, context menu should be forcibly opened even
8310 // if web apps want to prevent it since we respect our users' intention.
8311 // In this case, we don't fire "contextmenu" event on web content because
8312 // of not cancelable.
8313 if (mouseEvent
->IsShift()) {
8314 aEvent
->mFlags
.mOnlyChromeDispatch
= true;
8315 aEvent
->mFlags
.mRetargetToNonNativeAnonymous
= true;
8323 case eTouchPointerCancel
:
8324 return mPresShell
->mTouchManager
.PreHandleEvent(
8325 aEvent
, aEventStatus
, *aTouchIsNew
, mPresShell
->mCurrentEventContent
);
8331 void PresShell::EventHandler::FinalizeHandlingEvent(WidgetEvent
* aEvent
) {
8332 switch (aEvent
->mMessage
) {
8336 if (aEvent
->AsKeyboardEvent()->mKeyCode
== NS_VK_ESCAPE
) {
8337 if (aEvent
->mMessage
== eKeyUp
) {
8338 // Reset this flag after key up is handled.
8339 mPresShell
->mIsLastChromeOnlyEscapeKeyConsumed
= false;
8341 if (aEvent
->mFlags
.mOnlyChromeDispatch
&&
8342 aEvent
->mFlags
.mDefaultPreventedByChrome
) {
8343 mPresShell
->mIsLastChromeOnlyEscapeKeyConsumed
= true;
8345 if (aEvent
->mMessage
== eKeyDown
&&
8346 !aEvent
->mFlags
.mDefaultPrevented
) {
8347 if (Document
* doc
= GetDocument()) {
8348 doc
->TryCancelDialog();
8353 if (aEvent
->mMessage
== eKeyDown
) {
8354 mPresShell
->mIsLastKeyDownCanceled
= aEvent
->mFlags
.mDefaultPrevented
;
8359 // reset the capturing content now that the mouse button is up
8360 PresShell::ReleaseCapturingContent();
8363 PresShell::AllowMouseCapture(false);
8372 // After any drag event other than dragstart (which is handled
8373 // separately, as we need to collect the data first), the DataTransfer
8374 // needs to be made protected, and then disconnected.
8375 DataTransfer
* dataTransfer
= aEvent
->AsDragEvent()->mDataTransfer
;
8377 dataTransfer
->Disconnect();
8386 void PresShell::EventHandler::MaybeHandleKeyboardEventBeforeDispatch(
8387 WidgetKeyboardEvent
* aKeyboardEvent
) {
8388 MOZ_ASSERT(aKeyboardEvent
);
8390 if (aKeyboardEvent
->mKeyCode
!= NS_VK_ESCAPE
) {
8394 // If we're in fullscreen mode, exit from it forcibly when Escape key is
8396 Document
* doc
= mPresShell
->GetCurrentEventContent()
8397 ? mPresShell
->mCurrentEventContent
->OwnerDoc()
8399 Document
* root
= nsContentUtils::GetInProcessSubtreeRootDocument(doc
);
8400 if (root
&& root
->GetFullscreenElement()) {
8401 // Prevent default action on ESC key press when exiting
8402 // DOM fullscreen mode. This prevents the browser ESC key
8403 // handler from stopping all loads in the document, which
8404 // would cause <video> loads to stop.
8405 // XXX We need to claim the Escape key event which will be
8406 // dispatched only into chrome is already consumed by
8407 // content because we need to prevent its default here
8408 // for some reasons (not sure) but we need to detect
8409 // if a chrome event handler will call PreventDefault()
8410 // again and check it later.
8411 aKeyboardEvent
->PreventDefaultBeforeDispatch(CrossProcessForwarding::eStop
);
8412 aKeyboardEvent
->mFlags
.mOnlyChromeDispatch
= true;
8414 // The event listeners in chrome can prevent this ESC behavior by
8415 // calling prevent default on the preceding keydown/press events.
8416 if (!mPresShell
->mIsLastChromeOnlyEscapeKeyConsumed
&&
8417 aKeyboardEvent
->mMessage
== eKeyUp
) {
8418 // ESC key released while in DOM fullscreen mode.
8419 // Fully exit all browser windows and documents from
8421 Document::AsyncExitFullscreen(nullptr);
8425 nsCOMPtr
<Document
> pointerLockedDoc
= PointerLockManager::GetLockedDocument();
8426 if (!mPresShell
->mIsLastChromeOnlyEscapeKeyConsumed
&& pointerLockedDoc
) {
8427 // XXX See above comment to understand the reason why this needs
8428 // to claim that the Escape key event is consumed by content
8429 // even though it will be dispatched only into chrome.
8430 aKeyboardEvent
->PreventDefaultBeforeDispatch(CrossProcessForwarding::eStop
);
8431 aKeyboardEvent
->mFlags
.mOnlyChromeDispatch
= true;
8432 if (aKeyboardEvent
->mMessage
== eKeyUp
) {
8433 PointerLockManager::Unlock();
8438 void PresShell::EventHandler::RecordEventPreparationPerformance(
8439 const WidgetEvent
* aEvent
) {
8442 switch (aEvent
->mMessage
) {
8446 if (aEvent
->AsKeyboardEvent()->ShouldInteractionTimeRecorded()) {
8447 GetPresContext()->RecordInteractionTime(
8448 nsPresContext::InteractionType::KeyInteraction
, aEvent
->mTimeStamp
);
8450 Telemetry::AccumulateTimeDelta(Telemetry::INPUT_EVENT_QUEUED_KEYBOARD_MS
,
8451 aEvent
->mTimeStamp
);
8456 Telemetry::AccumulateTimeDelta(Telemetry::INPUT_EVENT_QUEUED_CLICK_MS
,
8457 aEvent
->mTimeStamp
);
8461 GetPresContext()->RecordInteractionTime(
8462 nsPresContext::InteractionType::ClickInteraction
, aEvent
->mTimeStamp
);
8466 if (aEvent
->mFlags
.mHandledByAPZ
) {
8467 Telemetry::AccumulateTimeDelta(
8468 Telemetry::INPUT_EVENT_QUEUED_APZ_MOUSE_MOVE_MS
,
8469 aEvent
->mTimeStamp
);
8471 GetPresContext()->RecordInteractionTime(
8472 nsPresContext::InteractionType::MouseMoveInteraction
,
8473 aEvent
->mTimeStamp
);
8477 if (aEvent
->mFlags
.mHandledByAPZ
) {
8478 Telemetry::AccumulateTimeDelta(
8479 Telemetry::INPUT_EVENT_QUEUED_APZ_WHEEL_MS
, aEvent
->mTimeStamp
);
8484 if (aEvent
->mFlags
.mHandledByAPZ
) {
8485 Telemetry::AccumulateTimeDelta(
8486 Telemetry::INPUT_EVENT_QUEUED_APZ_TOUCH_MOVE_MS
,
8487 aEvent
->mTimeStamp
);
8496 void PresShell::EventHandler::RecordEventHandlingResponsePerformance(
8497 const WidgetEvent
* aEvent
) {
8498 if (!Telemetry::CanRecordBase() || aEvent
->mTimeStamp
.IsNull() ||
8499 aEvent
->mTimeStamp
<= mPresShell
->mLastOSWake
||
8500 !aEvent
->AsInputEvent()) {
8504 TimeStamp now
= TimeStamp::Now();
8505 double millis
= (now
- aEvent
->mTimeStamp
).ToMilliseconds();
8506 Telemetry::Accumulate(Telemetry::INPUT_EVENT_RESPONSE_MS
, millis
);
8507 if (GetDocument() &&
8508 GetDocument()->GetReadyStateEnum() != Document::READYSTATE_COMPLETE
) {
8509 Telemetry::Accumulate(Telemetry::LOAD_INPUT_EVENT_RESPONSE_MS
, millis
);
8512 if (!sLastInputProcessed
|| sLastInputProcessed
< aEvent
->mTimeStamp
) {
8513 if (sLastInputProcessed
) {
8514 // This input event was created after we handled the last one.
8515 // Accumulate the previous events' coalesced duration.
8517 (sLastInputProcessed
- sLastInputCreated
).ToMilliseconds();
8518 Telemetry::Accumulate(Telemetry::INPUT_EVENT_RESPONSE_COALESCED_MS
,
8521 if (MOZ_UNLIKELY(!PresShell::sProcessInteractable
)) {
8522 // For content process, we use the ready state of
8523 // top-level-content-document to know if the process has finished the
8525 // For parent process, see the topic
8526 // 'sessionstore-one-or-no-tab-restored' in PresShell::Observe.
8527 if (XRE_IsContentProcess() && GetDocument() &&
8528 GetDocument()->IsTopLevelContentDocument()) {
8529 switch (GetDocument()->GetReadyStateEnum()) {
8530 case Document::READYSTATE_INTERACTIVE
:
8531 case Document::READYSTATE_COMPLETE
:
8532 PresShell::sProcessInteractable
= true;
8539 if (MOZ_LIKELY(PresShell::sProcessInteractable
)) {
8540 Telemetry::Accumulate(Telemetry::INPUT_EVENT_RESPONSE_POST_STARTUP_MS
,
8543 Telemetry::Accumulate(Telemetry::INPUT_EVENT_RESPONSE_STARTUP_MS
,
8547 sLastInputCreated
= aEvent
->mTimeStamp
;
8548 } else if (aEvent
->mTimeStamp
< sLastInputCreated
) {
8549 // This event was created before the last input. May be processing out
8550 // of order, so coalesce backwards, too.
8551 sLastInputCreated
= aEvent
->mTimeStamp
;
8553 sLastInputProcessed
= now
;
8558 PresShell::EventHandler::GetDocumentPrincipalToCompareWithBlacklist(
8559 PresShell
& aPresShell
) {
8560 nsPresContext
* presContext
= aPresShell
.GetPresContext();
8561 if (NS_WARN_IF(!presContext
)) {
8564 return presContext
->Document()->GetPrincipalForPrefBasedHacks();
8567 nsresult
PresShell::EventHandler::DispatchEventToDOM(
8568 WidgetEvent
* aEvent
, nsEventStatus
* aEventStatus
,
8569 nsPresShellEventCB
* aEventCB
) {
8570 nsresult rv
= NS_OK
;
8571 nsCOMPtr
<nsINode
> eventTarget
= mPresShell
->mCurrentEventContent
;
8572 nsPresShellEventCB
* eventCBPtr
= aEventCB
;
8574 nsCOMPtr
<nsIContent
> targetContent
;
8575 if (mPresShell
->mCurrentEventFrame
) {
8576 rv
= mPresShell
->mCurrentEventFrame
->GetContentForEvent(
8577 aEvent
, getter_AddRefs(targetContent
));
8579 if (NS_SUCCEEDED(rv
) && targetContent
) {
8580 eventTarget
= targetContent
;
8581 } else if (GetDocument()) {
8582 eventTarget
= GetDocument();
8583 // If we don't have any content, the callback wouldn't probably
8585 eventCBPtr
= nullptr;
8589 if (aEvent
->IsBlockedForFingerprintingResistance()) {
8590 aEvent
->mFlags
.mOnlySystemGroupDispatchInContent
= true;
8591 } else if (aEvent
->mMessage
== eKeyPress
) {
8592 // If eKeyPress event is marked as not dispatched in the default event
8593 // group in web content, it's caused by non-printable key or key
8594 // combination. In this case, UI Events declares that browsers
8595 // shouldn't dispatch keypress event. However, some web apps may be
8596 // broken with this strict behavior due to historical issue.
8597 // Therefore, we need to keep dispatching keypress event for such keys
8598 // even with breaking the standard.
8599 // Similarly, the other browsers sets non-zero value of keyCode or
8600 // charCode of keypress event to the other. Therefore, we should
8601 // behave so, however, some web apps may be broken. On such web apps,
8602 // we should keep using legacy our behavior.
8603 if (!mPresShell
->mInitializedWithKeyPressEventDispatchingBlacklist
) {
8604 mPresShell
->mInitializedWithKeyPressEventDispatchingBlacklist
= true;
8605 nsCOMPtr
<nsIPrincipal
> principal
=
8606 GetDocumentPrincipalToCompareWithBlacklist(*mPresShell
);
8608 mPresShell
->mForceDispatchKeyPressEventsForNonPrintableKeys
=
8609 principal
->IsURIInPrefList(
8610 "dom.keyboardevent.keypress.hack.dispatch_non_printable_"
8612 principal
->IsURIInPrefList(
8613 "dom.keyboardevent.keypress.hack."
8614 "dispatch_non_printable_keys.addl");
8616 mPresShell
->mForceUseLegacyKeyCodeAndCharCodeValues
|=
8617 principal
->IsURIInPrefList(
8618 "dom.keyboardevent.keypress.hack."
8619 "use_legacy_keycode_and_charcode") ||
8620 principal
->IsURIInPrefList(
8621 "dom.keyboardevent.keypress.hack."
8622 "use_legacy_keycode_and_charcode.addl");
8625 if (mPresShell
->mForceDispatchKeyPressEventsForNonPrintableKeys
) {
8626 aEvent
->mFlags
.mOnlySystemGroupDispatchInContent
= false;
8628 if (mPresShell
->mForceUseLegacyKeyCodeAndCharCodeValues
) {
8629 aEvent
->AsKeyboardEvent()->mUseLegacyKeyCodeAndCharCodeValues
= true;
8631 } else if (aEvent
->mMessage
== eMouseUp
) {
8632 // Historically Firefox has dispatched click events for non-primary
8633 // buttons, but only on window and document (and inside input/textarea),
8634 // not on elements in general. The UI events spec forbids click (and
8635 // dblclick) for non-primary mouse buttons, and specifies auxclick
8636 // instead. In case of some websites that rely on non-primary click to
8637 // prevent new tab etc. and don't have auxclick code to do the same, we
8638 // need to revert to the historial non-standard behaviour
8639 if (!mPresShell
->mInitializedWithClickEventDispatchingBlacklist
) {
8640 mPresShell
->mInitializedWithClickEventDispatchingBlacklist
= true;
8642 nsCOMPtr
<nsIPrincipal
> principal
=
8643 GetDocumentPrincipalToCompareWithBlacklist(*mPresShell
);
8646 mPresShell
->mForceUseLegacyNonPrimaryDispatch
=
8647 principal
->IsURIInPrefList(
8648 "dom.mouseevent.click.hack.use_legacy_non-primary_dispatch");
8651 if (mPresShell
->mForceUseLegacyNonPrimaryDispatch
) {
8652 aEvent
->AsMouseEvent()->mUseLegacyNonPrimaryDispatch
= true;
8656 if (aEvent
->mClass
== eCompositionEventClass
) {
8657 RefPtr
<nsPresContext
> presContext
= GetPresContext();
8658 RefPtr
<BrowserParent
> browserParent
=
8659 IMEStateManager::GetActiveBrowserParent();
8660 IMEStateManager::DispatchCompositionEvent(
8661 eventTarget
, presContext
, browserParent
, aEvent
->AsCompositionEvent(),
8662 aEventStatus
, eventCBPtr
);
8664 RefPtr
<nsPresContext
> presContext
= GetPresContext();
8665 EventDispatcher::Dispatch(eventTarget
, presContext
, aEvent
, nullptr,
8666 aEventStatus
, eventCBPtr
);
8672 void PresShell::EventHandler::DispatchTouchEventToDOM(
8673 WidgetEvent
* aEvent
, nsEventStatus
* aEventStatus
,
8674 nsPresShellEventCB
* aEventCB
, bool aTouchIsNew
) {
8675 // calling preventDefault on touchstart or the first touchmove for a
8676 // point prevents mouse events. calling it on the touchend should
8677 // prevent click dispatching.
8678 bool canPrevent
= (aEvent
->mMessage
== eTouchStart
) ||
8679 (aEvent
->mMessage
== eTouchMove
&& aTouchIsNew
) ||
8680 (aEvent
->mMessage
== eTouchEnd
);
8681 bool preventDefault
= false;
8682 nsEventStatus tmpStatus
= nsEventStatus_eIgnore
;
8683 WidgetTouchEvent
* touchEvent
= aEvent
->AsTouchEvent();
8685 // loop over all touches and dispatch events on any that have changed
8686 for (dom::Touch
* touch
: touchEvent
->mTouches
) {
8687 // We should remove all suppressed touch instances in
8688 // TouchManager::PreHandleEvent.
8689 MOZ_ASSERT(!touch
->mIsTouchEventSuppressed
);
8691 if (!touch
|| !touch
->mChanged
) {
8695 nsCOMPtr
<EventTarget
> targetPtr
= touch
->mTarget
;
8696 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(targetPtr
);
8701 Document
* doc
= content
->OwnerDoc();
8702 nsIContent
* capturingContent
= PresShell::GetCapturingContent();
8703 if (capturingContent
) {
8704 if (capturingContent
->OwnerDoc() != doc
) {
8705 // Wrong document, don't dispatch anything.
8708 content
= capturingContent
;
8711 MOZ_ASSERT(touchEvent
->IsTrusted());
8712 WidgetTouchEvent
newEvent(true, touchEvent
->mMessage
, touchEvent
->mWidget
);
8713 newEvent
.AssignTouchEventData(*touchEvent
, false);
8714 newEvent
.mTarget
= targetPtr
;
8715 newEvent
.mFlags
.mHandledByAPZ
= touchEvent
->mFlags
.mHandledByAPZ
;
8717 RefPtr
<PresShell
> contentPresShell
;
8718 if (doc
== GetDocument()) {
8719 contentPresShell
= doc
->GetPresShell();
8720 if (contentPresShell
) {
8721 // XXXsmaug huge hack. Pushing possibly capturing content,
8722 // even though event target is something else.
8723 contentPresShell
->PushCurrentEventInfo(content
->GetPrimaryFrame(),
8728 nsPresContext
* context
= doc
->GetPresContext();
8730 if (contentPresShell
) {
8731 contentPresShell
->PopCurrentEventInfo();
8736 tmpStatus
= nsEventStatus_eIgnore
;
8737 EventDispatcher::Dispatch(targetPtr
, context
, &newEvent
, nullptr,
8738 &tmpStatus
, aEventCB
);
8739 if (nsEventStatus_eConsumeNoDefault
== tmpStatus
||
8740 newEvent
.mFlags
.mMultipleActionsPrevented
) {
8741 preventDefault
= true;
8744 if (newEvent
.mFlags
.mMultipleActionsPrevented
) {
8745 touchEvent
->mFlags
.mMultipleActionsPrevented
= true;
8748 if (contentPresShell
) {
8749 contentPresShell
->PopCurrentEventInfo();
8753 if (preventDefault
&& canPrevent
) {
8754 *aEventStatus
= nsEventStatus_eConsumeNoDefault
;
8756 *aEventStatus
= nsEventStatus_eIgnore
;
8760 // Dispatch event to content only (NOT full processing)
8761 // See also HandleEventWithTarget which does full event processing.
8762 nsresult
PresShell::HandleDOMEventWithTarget(nsIContent
* aTargetContent
,
8763 WidgetEvent
* aEvent
,
8764 nsEventStatus
* aStatus
) {
8765 nsresult rv
= NS_OK
;
8767 PushCurrentEventInfo(nullptr, aTargetContent
);
8769 // Bug 41013: Check if the event should be dispatched to content.
8770 // It's possible that we are in the middle of destroying the window
8771 // and the js context is out of date. This check detects the case
8772 // that caused a crash in bug 41013, but there may be a better way
8773 // to handle this situation!
8774 nsCOMPtr
<nsISupports
> container
= mPresContext
->GetContainerWeak();
8776 // Dispatch event to content
8777 rv
= EventDispatcher::Dispatch(aTargetContent
, mPresContext
, aEvent
,
8781 PopCurrentEventInfo();
8785 // See the method above.
8786 nsresult
PresShell::HandleDOMEventWithTarget(nsIContent
* aTargetContent
,
8788 nsEventStatus
* aStatus
) {
8789 nsresult rv
= NS_OK
;
8791 PushCurrentEventInfo(nullptr, aTargetContent
);
8792 nsCOMPtr
<nsISupports
> container
= mPresContext
->GetContainerWeak();
8794 rv
= EventDispatcher::DispatchDOMEvent(aTargetContent
, nullptr, aEvent
,
8795 mPresContext
, aStatus
);
8798 PopCurrentEventInfo();
8802 bool PresShell::EventHandler::AdjustContextMenuKeyEvent(
8803 WidgetMouseEvent
* aMouseEvent
) {
8805 // if a menu is open, open the context menu relative to the active item on the
8807 nsXULPopupManager
* pm
= nsXULPopupManager::GetInstance();
8809 nsIFrame
* popupFrame
= pm
->GetTopPopup(ePopupTypeMenu
);
8811 nsIFrame
* itemFrame
=
8812 (static_cast<nsMenuPopupFrame
*>(popupFrame
))->GetCurrentMenuItem();
8813 if (!itemFrame
) itemFrame
= popupFrame
;
8815 nsCOMPtr
<nsIWidget
> widget
= popupFrame
->GetNearestWidget();
8816 aMouseEvent
->mWidget
= widget
;
8817 LayoutDeviceIntPoint widgetPoint
= widget
->WidgetToScreenOffset();
8818 aMouseEvent
->mRefPoint
=
8819 LayoutDeviceIntPoint::FromAppUnitsToNearest(
8820 itemFrame
->GetScreenRectInAppUnits().BottomLeft(),
8821 itemFrame
->PresContext()->AppUnitsPerDevPixel()) -
8824 mPresShell
->mCurrentEventContent
= itemFrame
->GetContent();
8825 mPresShell
->mCurrentEventFrame
= itemFrame
;
8832 // If we're here because of the key-equiv for showing context menus, we
8833 // have to twiddle with the NS event to make sure the context menu comes
8834 // up in the upper left of the relevant content area before we create
8835 // the DOM event. Since we never call InitMouseEvent() on the event,
8836 // the client X/Y will be 0,0. We can make use of that if the widget is null.
8837 // Use the root view manager's widget since it's most likely to have one,
8838 // and the coordinates returned by GetCurrentItemAndPositionForElement
8839 // are relative to the widget of the root of the root view manager.
8840 nsRootPresContext
* rootPC
= GetPresContext()->GetRootPresContext();
8841 aMouseEvent
->mRefPoint
= LayoutDeviceIntPoint(0, 0);
8843 rootPC
->PresShell()->GetViewManager()->GetRootWidget(
8844 getter_AddRefs(aMouseEvent
->mWidget
));
8846 if (aMouseEvent
->mWidget
) {
8847 // default the refpoint to the topleft of our document
8848 nsPoint
offset(0, 0);
8849 nsIFrame
* rootFrame
= FrameConstructor()->GetRootFrame();
8851 nsView
* view
= rootFrame
->GetClosestView(&offset
);
8852 offset
+= view
->GetOffsetToWidget(aMouseEvent
->mWidget
);
8853 aMouseEvent
->mRefPoint
= LayoutDeviceIntPoint::FromAppUnitsToNearest(
8854 offset
, GetPresContext()->AppUnitsPerDevPixel());
8858 aMouseEvent
->mWidget
= nullptr;
8861 // see if we should use the caret position for the popup
8862 LayoutDeviceIntPoint caretPoint
;
8863 // Beware! This may flush notifications via synchronous
8864 // ScrollSelectionIntoView.
8865 if (PrepareToUseCaretPosition(MOZ_KnownLive(aMouseEvent
->mWidget
),
8867 // caret position is good
8868 int32_t devPixelRatio
= GetPresContext()->AppUnitsPerDevPixel();
8869 caretPoint
= LayoutDeviceIntPoint::FromAppUnitsToNearest(
8870 ViewportUtils::LayoutToVisual(
8871 LayoutDeviceIntPoint::ToAppUnits(caretPoint
, devPixelRatio
),
8872 GetPresContext()->PresShell()),
8874 aMouseEvent
->mRefPoint
= caretPoint
;
8878 // If we're here because of the key-equiv for showing context menus, we
8879 // have to reset the event target to the currently focused element. Get it
8880 // from the focus controller.
8881 RefPtr
<Element
> currentFocus
;
8882 nsFocusManager
* fm
= nsFocusManager::GetFocusManager();
8884 currentFocus
= fm
->GetFocusedElement();
8887 // Reset event coordinates relative to focused frame in view
8889 nsCOMPtr
<nsIContent
> currentPointElement
;
8890 GetCurrentItemAndPositionForElement(
8891 currentFocus
, getter_AddRefs(currentPointElement
),
8892 aMouseEvent
->mRefPoint
, MOZ_KnownLive(aMouseEvent
->mWidget
));
8893 if (currentPointElement
) {
8894 mPresShell
->mCurrentEventContent
= currentPointElement
;
8895 mPresShell
->mCurrentEventFrame
= nullptr;
8896 mPresShell
->GetCurrentEventFrame();
8903 // PresShell::EventHandler::PrepareToUseCaretPosition
8905 // This checks to see if we should use the caret position for popup context
8906 // menus. Returns true if the caret position should be used, and the
8907 // coordinates of that position is returned in aTargetPt. This function
8908 // will also scroll the window as needed to make the caret visible.
8910 // The event widget should be the widget that generated the event, and
8911 // whose coordinate system the resulting event's mRefPoint should be
8912 // relative to. The returned point is in device pixels realtive to the
8913 // widget passed in.
8914 bool PresShell::EventHandler::PrepareToUseCaretPosition(
8915 nsIWidget
* aEventWidget
, LayoutDeviceIntPoint
& aTargetPt
) {
8918 // check caret visibility
8919 RefPtr
<nsCaret
> caret
= mPresShell
->GetCaret();
8920 NS_ENSURE_TRUE(caret
, false);
8922 bool caretVisible
= caret
->IsVisible();
8923 if (!caretVisible
) return false;
8925 // caret selection, this is a temporary weak reference, so no refcounting is
8927 Selection
* domSelection
= caret
->GetSelection();
8928 NS_ENSURE_TRUE(domSelection
, false);
8930 // since the match could be an anonymous textnode inside a
8931 // <textarea> or text <input>, we need to get the outer frame
8932 // note: frames are not refcounted
8933 nsIFrame
* frame
= nullptr; // may be nullptr
8934 nsINode
* node
= domSelection
->GetFocusNode();
8935 NS_ENSURE_TRUE(node
, false);
8936 nsCOMPtr
<nsIContent
> content
= nsIContent::FromNode(node
);
8938 nsIContent
* nonNative
= content
->FindFirstNonChromeOnlyAccessContent();
8939 content
= nonNative
;
8943 // It seems like ScrollSelectionIntoView should be enough, but it's
8944 // not. The problem is that scrolling the selection into view when it is
8945 // below the current viewport will align the top line of the frame exactly
8946 // with the bottom of the window. This is fine, BUT, the popup event causes
8947 // the control to be re-focused which does this exact call to
8948 // ScrollContentIntoView, which has a one-pixel disagreement of whether the
8949 // frame is actually in view. The result is that the frame is aligned with
8950 // the top of the window, but the menu is still at the bottom.
8952 // Doing this call first forces the frame to be in view, eliminating the
8953 // problem. The only difference in the result is that if your cursor is in
8954 // an edit box below the current view, you'll get the edit box aligned with
8955 // the top of the window. This is arguably better behavior anyway.
8957 MOZ_KnownLive(mPresShell
)
8958 ->ScrollContentIntoView(
8959 content
, ScrollAxis(kScrollMinimum
, WhenToScroll::IfNotVisible
),
8960 ScrollAxis(kScrollMinimum
, WhenToScroll::IfNotVisible
),
8961 ScrollFlags::ScrollOverflowHidden
);
8962 NS_ENSURE_SUCCESS(rv
, false);
8963 frame
= content
->GetPrimaryFrame();
8964 NS_WARNING_ASSERTION(frame
, "No frame for focused content?");
8967 // Actually scroll the selection (ie caret) into view. Note that this must
8968 // be synchronous since we will be checking the caret position on the screen.
8970 // Be easy about errors, and just don't scroll in those cases. Better to have
8971 // the correct menu at a weird place than the wrong menu.
8972 // After ScrollSelectionIntoView(), the pending notifications might be
8973 // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
8974 nsCOMPtr
<nsISelectionController
> selCon
;
8976 frame
->GetSelectionController(GetPresContext(), getter_AddRefs(selCon
));
8978 selCon
= static_cast<nsISelectionController
*>(mPresShell
);
8980 rv
= selCon
->ScrollSelectionIntoView(
8981 nsISelectionController::SELECTION_NORMAL
,
8982 nsISelectionController::SELECTION_FOCUS_REGION
,
8983 nsISelectionController::SCROLL_SYNCHRONOUS
);
8984 NS_ENSURE_SUCCESS(rv
, false);
8987 nsPresContext
* presContext
= GetPresContext();
8989 // get caret position relative to the closest view
8991 nsIFrame
* caretFrame
= caret
->GetGeometry(&caretCoords
);
8992 if (!caretFrame
) return false;
8994 nsView
* view
= caretFrame
->GetClosestView(&viewOffset
);
8995 if (!view
) return false;
8996 // and then get the caret coords relative to the event widget
8998 viewOffset
+= view
->GetOffsetToWidget(aEventWidget
);
9000 caretCoords
.MoveBy(viewOffset
);
9002 // caret coordinates are in app units, convert to pixels
9004 presContext
->AppUnitsToDevPixels(caretCoords
.x
+ caretCoords
.width
);
9006 presContext
->AppUnitsToDevPixels(caretCoords
.y
+ caretCoords
.height
);
9008 // make sure rounding doesn't return a pixel which is outside the caret
9009 // (e.g. one line lower)
9015 void PresShell::EventHandler::GetCurrentItemAndPositionForElement(
9016 Element
* aFocusedElement
, nsIContent
** aTargetToUse
,
9017 LayoutDeviceIntPoint
& aTargetPt
, nsIWidget
* aRootWidget
) {
9018 nsCOMPtr
<nsIContent
> focusedContent
= aFocusedElement
;
9019 MOZ_KnownLive(mPresShell
)
9020 ->ScrollContentIntoView(focusedContent
, ScrollAxis(), ScrollAxis(),
9021 ScrollFlags::ScrollOverflowHidden
);
9023 nsPresContext
* presContext
= GetPresContext();
9025 bool istree
= false, checkLineHeight
= true;
9026 nscoord extraTreeY
= 0;
9029 // Set the position to just underneath the current item for multi-select
9030 // lists or just underneath the selected item for single-select lists. If
9031 // the element is not a list, or there is no selection, leave the position
9033 nsCOMPtr
<Element
> item
;
9034 nsCOMPtr
<nsIDOMXULMultiSelectControlElement
> multiSelect
=
9035 aFocusedElement
->AsXULMultiSelectControl();
9037 checkLineHeight
= false;
9039 int32_t currentIndex
;
9040 multiSelect
->GetCurrentIndex(¤tIndex
);
9041 if (currentIndex
>= 0) {
9042 RefPtr
<XULTreeElement
> tree
= XULTreeElement::FromNode(focusedContent
);
9043 // Tree view special case (tree items have no frames)
9044 // Get the focused row and add its coordinates, which are already in
9046 // XXX Boris, should we create a new interface so that this doesn't
9047 // need to know about trees? Something like nsINodelessChildCreator
9048 // which could provide the current focus coordinates?
9050 tree
->EnsureRowIsVisible(currentIndex
);
9051 int32_t firstVisibleRow
= tree
->GetFirstVisibleRow();
9052 int32_t rowHeight
= tree
->RowHeight();
9054 extraTreeY
+= nsPresContext::CSSPixelsToAppUnits(
9055 (currentIndex
- firstVisibleRow
+ 1) * rowHeight
);
9058 RefPtr
<nsTreeColumns
> cols
= tree
->GetColumns();
9060 nsTreeColumn
* col
= cols
->GetFirstColumn();
9062 RefPtr
<Element
> colElement
= col
->Element();
9063 nsIFrame
* frame
= colElement
->GetPrimaryFrame();
9065 extraTreeY
+= frame
->GetSize().height
;
9070 multiSelect
->GetCurrentItem(getter_AddRefs(item
));
9074 // don't check menulists as the selected item will be inside a popup.
9075 nsCOMPtr
<nsIDOMXULMenuListElement
> menulist
=
9076 aFocusedElement
->AsXULMenuList();
9078 nsCOMPtr
<nsIDOMXULSelectControlElement
> select
=
9079 aFocusedElement
->AsXULSelectControl();
9081 checkLineHeight
= false;
9082 select
->GetSelectedItem(getter_AddRefs(item
));
9088 focusedContent
= item
;
9092 nsIFrame
* frame
= focusedContent
->GetPrimaryFrame();
9095 frame
->PresContext() == GetPresContext(),
9096 "handling event for focused content that is not in our document?");
9098 nsPoint
frameOrigin(0, 0);
9100 // Get the frame's origin within its view
9101 nsView
* view
= frame
->GetClosestView(&frameOrigin
);
9102 NS_ASSERTION(view
, "No view for frame");
9104 // View's origin relative the widget
9106 frameOrigin
+= view
->GetOffsetToWidget(aRootWidget
);
9109 // Start context menu down and to the right from top left of frame
9110 // use the lineheight. This is a good distance to move the context
9111 // menu away from the top left corner of the frame. If we always
9112 // used the frame height, the context menu could end up far away,
9113 // for example when we're focused on linked images.
9114 // On the other hand, we want to use the frame height if it's less
9115 // than the current line height, so that the context menu appears
9116 // associated with the correct frame.
9119 extra
= frame
->GetSize().height
;
9120 if (checkLineHeight
) {
9121 nsIScrollableFrame
* scrollFrame
=
9122 nsLayoutUtils::GetNearestScrollableFrame(
9123 frame
, nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN
|
9124 nsLayoutUtils::SCROLLABLE_FIXEDPOS_FINDS_ROOT
);
9126 nsSize scrollAmount
= scrollFrame
->GetLineScrollAmount();
9127 nsIFrame
* f
= do_QueryFrame(scrollFrame
);
9128 int32_t APD
= presContext
->AppUnitsPerDevPixel();
9129 int32_t scrollAPD
= f
->PresContext()->AppUnitsPerDevPixel();
9130 scrollAmount
= scrollAmount
.ScaleToOtherAppUnits(scrollAPD
, APD
);
9131 if (extra
> scrollAmount
.height
) {
9132 extra
= scrollAmount
.height
;
9138 aTargetPt
.x
= presContext
->AppUnitsToDevPixels(frameOrigin
.x
);
9140 presContext
->AppUnitsToDevPixels(frameOrigin
.y
+ extra
+ extraTreeY
);
9143 NS_IF_ADDREF(*aTargetToUse
= focusedContent
);
9146 bool PresShell::ShouldIgnoreInvalidation() {
9147 return mPaintingSuppressed
|| !mIsActive
|| mIsNeverPainting
;
9150 void PresShell::WillPaint() {
9151 // Check the simplest things first. In particular, it's important to
9152 // check mIsActive before making any of the more expensive calls such
9153 // as GetRootPresContext, for the case of a browser with a large
9155 // Don't bother doing anything if some viewmanager in our tree is painting
9156 // while we still have painting suppressed or we are not active.
9157 if (!mIsActive
|| mPaintingSuppressed
|| !IsVisible()) {
9161 nsRootPresContext
* rootPresContext
= mPresContext
->GetRootPresContext();
9162 if (!rootPresContext
) {
9163 // In some edge cases, such as when we don't have a root frame yet,
9164 // we can't find the root prescontext. There's nothing to do in that
9169 rootPresContext
->FlushWillPaintObservers();
9170 if (mIsDestroying
) return;
9172 // Process reflows, if we have them, to reduce flicker due to invalidates and
9173 // reflow being interspersed. Note that we _do_ allow this to be
9174 // interruptible; if we can't do all the reflows it's better to flicker a bit
9175 // than to freeze up.
9176 FlushPendingNotifications(
9177 ChangesToFlush(FlushType::InterruptibleLayout
, false));
9180 void PresShell::DidPaintWindow() {
9181 nsRootPresContext
* rootPresContext
= mPresContext
->GetRootPresContext();
9182 if (rootPresContext
!= mPresContext
) {
9183 // This could be a popup's presshell. No point in notifying XPConnect
9184 // about compositing of popups.
9188 if (!mHasReceivedPaintMessage
) {
9189 mHasReceivedPaintMessage
= true;
9191 nsCOMPtr
<nsIObserverService
> obsvc
= services::GetObserverService();
9192 if (obsvc
&& mDocument
) {
9193 nsPIDOMWindowOuter
* window
= mDocument
->GetWindow();
9194 nsCOMPtr
<nsIDOMChromeWindow
> chromeWin(do_QueryInterface(window
));
9196 obsvc
->NotifyObservers(chromeWin
, "widget-first-paint", nullptr);
9202 bool PresShell::IsVisible() const {
9203 if (!mIsActive
|| !mViewManager
) return false;
9205 nsView
* view
= mViewManager
->GetRootView();
9206 if (!view
) return true;
9208 // inner view of subdoc frame
9209 view
= view
->GetParent();
9210 if (!view
) return true;
9213 view
= view
->GetParent();
9214 if (!view
) return true;
9216 nsIFrame
* frame
= view
->GetFrame();
9217 if (!frame
) return true;
9219 return frame
->IsVisibleConsideringAncestors(
9220 nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY
);
9223 void PresShell::SuppressDisplayport(bool aEnabled
) {
9225 mActiveSuppressDisplayport
++;
9226 } else if (mActiveSuppressDisplayport
> 0) {
9227 bool isSuppressed
= IsDisplayportSuppressed();
9228 mActiveSuppressDisplayport
--;
9229 if (isSuppressed
&& !IsDisplayportSuppressed()) {
9230 // We unsuppressed the displayport, trigger a paint
9231 if (nsIFrame
* rootFrame
= mFrameConstructor
->GetRootFrame()) {
9232 rootFrame
->SchedulePaint();
9238 static bool sDisplayPortSuppressionRespected
= true;
9240 void PresShell::RespectDisplayportSuppression(bool aEnabled
) {
9241 bool isSuppressed
= IsDisplayportSuppressed();
9242 sDisplayPortSuppressionRespected
= aEnabled
;
9243 if (isSuppressed
&& !IsDisplayportSuppressed()) {
9244 // We unsuppressed the displayport, trigger a paint
9245 if (nsIFrame
* rootFrame
= mFrameConstructor
->GetRootFrame()) {
9246 rootFrame
->SchedulePaint();
9251 bool PresShell::IsDisplayportSuppressed() {
9252 return sDisplayPortSuppressionRespected
&& mActiveSuppressDisplayport
> 0;
9255 static CallState
FreezeSubDocument(Document
& aDocument
) {
9256 if (PresShell
* presShell
= aDocument
.GetPresShell()) {
9257 presShell
->Freeze();
9259 return CallState::Continue
;
9262 void PresShell::Freeze(bool aIncludeSubDocuments
) {
9263 mUpdateApproximateFrameVisibilityEvent
.Revoke();
9265 MaybeReleaseCapturingContent();
9268 SetCaretEnabled(false);
9271 mPaintingSuppressed
= true;
9273 if (aIncludeSubDocuments
&& mDocument
) {
9274 mDocument
->EnumerateSubDocuments(FreezeSubDocument
);
9277 nsPresContext
* presContext
= GetPresContext();
9279 presContext
->DisableInteractionTimeRecording();
9280 if (presContext
->RefreshDriver()->GetPresContext() == presContext
) {
9281 presContext
->RefreshDriver()->Freeze();
9287 UpdateImageLockingState();
9291 void PresShell::FireOrClearDelayedEvents(bool aFireEvents
) {
9292 mNoDelayedMouseEvents
= false;
9293 mNoDelayedKeyEvents
= false;
9295 mDelayedEvents
.Clear();
9300 RefPtr
<Document
> doc
= mDocument
;
9301 while (!mIsDestroying
&& mDelayedEvents
.Length() &&
9302 !doc
->EventHandlingSuppressed()) {
9303 UniquePtr
<DelayedEvent
> ev
= std::move(mDelayedEvents
[0]);
9304 mDelayedEvents
.RemoveElementAt(0);
9305 if (ev
->IsKeyPressEvent() && mIsLastKeyDownCanceled
) {
9310 if (!doc
->EventHandlingSuppressed()) {
9311 mDelayedEvents
.Clear();
9316 void PresShell::Thaw(bool aIncludeSubDocuments
) {
9317 nsPresContext
* presContext
= GetPresContext();
9319 presContext
->RefreshDriver()->GetPresContext() == presContext
) {
9320 presContext
->RefreshDriver()->Thaw();
9323 if (aIncludeSubDocuments
&& mDocument
) {
9324 mDocument
->EnumerateSubDocuments([](Document
& aSubDoc
) {
9325 if (PresShell
* presShell
= aSubDoc
.GetPresShell()) {
9328 return CallState::Continue
;
9332 // Get the activeness of our presshell, as this might have changed
9333 // while we were in the bfcache
9334 ActivenessMaybeChanged();
9336 // We're now unfrozen
9338 UpdateImageLockingState();
9340 UnsuppressPainting();
9343 //--------------------------------------------------------
9344 // Start of protected and private methods on the PresShell
9345 //--------------------------------------------------------
9347 void PresShell::MaybeScheduleReflow() {
9348 ASSERT_REFLOW_SCHEDULED_STATE();
9349 if (mObservingLayoutFlushes
|| mIsDestroying
|| mIsReflowing
||
9350 mDirtyRoots
.IsEmpty())
9353 if (!mPresContext
->HasPendingInterrupt() || !ScheduleReflowOffTimer()) {
9357 ASSERT_REFLOW_SCHEDULED_STATE();
9360 void PresShell::ScheduleReflow() {
9361 ASSERT_REFLOW_SCHEDULED_STATE();
9362 DoObserveLayoutFlushes();
9363 ASSERT_REFLOW_SCHEDULED_STATE();
9366 void PresShell::WillCauseReflow() {
9367 nsContentUtils::AddScriptBlocker();
9371 void PresShell::DidCauseReflow() {
9372 NS_ASSERTION(mChangeNestCount
!= 0, "Unexpected call to DidCauseReflow()");
9374 nsContentUtils::RemoveScriptBlocker();
9377 void PresShell::WillDoReflow() {
9378 mDocument
->FlushUserFontSet();
9380 mPresContext
->FlushCounterStyles();
9382 mPresContext
->FlushFontFeatureValues();
9384 mLastReflowStart
= GetPerformanceNowUnclamped();
9387 void PresShell::DidDoReflow(bool aInterruptible
) {
9388 HandlePostedReflowCallbacks(aInterruptible
);
9389 if (mIsDestroying
) {
9393 nsAutoScriptBlocker scriptBlocker
;
9394 AutoAssertNoFlush
noReentrantFlush(*this);
9395 if (nsCOMPtr
<nsIDocShell
> docShell
= mPresContext
->GetDocShell()) {
9396 DOMHighResTimeStamp now
= GetPerformanceNowUnclamped();
9397 docShell
->NotifyReflowObservers(aInterruptible
, mLastReflowStart
, now
);
9400 if (!mPresContext
->HasPendingInterrupt()) {
9401 mDocument
->ScheduleResizeObserversNotification();
9404 if (StaticPrefs::layout_reflow_synthMouseMove()) {
9405 SynthesizeMouseMove(false);
9408 mPresContext
->NotifyMissingFonts();
9411 DOMHighResTimeStamp
PresShell::GetPerformanceNowUnclamped() {
9412 DOMHighResTimeStamp now
= 0;
9414 if (nsPIDOMWindowInner
* window
= mDocument
->GetInnerWindow()) {
9415 Performance
* perf
= window
->GetPerformance();
9418 now
= perf
->NowUnclamped();
9425 void PresShell::sReflowContinueCallback(nsITimer
* aTimer
, void* aPresShell
) {
9426 RefPtr
<PresShell
> self
= static_cast<PresShell
*>(aPresShell
);
9428 MOZ_ASSERT(aTimer
== self
->mReflowContinueTimer
, "Unexpected timer");
9429 self
->mReflowContinueTimer
= nullptr;
9430 self
->ScheduleReflow();
9433 bool PresShell::ScheduleReflowOffTimer() {
9434 MOZ_ASSERT(!mObservingLayoutFlushes
, "Shouldn't get here");
9435 ASSERT_REFLOW_SCHEDULED_STATE();
9437 if (!mReflowContinueTimer
) {
9438 nsresult rv
= NS_NewTimerWithFuncCallback(
9439 getter_AddRefs(mReflowContinueTimer
), sReflowContinueCallback
, this, 30,
9440 nsITimer::TYPE_ONE_SHOT
, "sReflowContinueCallback",
9441 mDocument
->EventTargetFor(TaskCategory::Other
));
9442 return NS_SUCCEEDED(rv
);
9447 bool PresShell::DoReflow(nsIFrame
* target
, bool aInterruptible
,
9448 OverflowChangedTracker
* aOverflowTracker
) {
9449 [[maybe_unused
]] nsIURI
* uri
= mDocument
->GetDocumentURI();
9450 AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING(
9451 "Reflow", LAYOUT_Reflow
, uri
? uri
->GetSpecOrDefault() : "N/A"_ns
);
9453 LAYOUT_TELEMETRY_RECORD_BASE(Reflow
);
9455 PerfStats::AutoMetricRecording
<PerfStats::Metric::Reflowing
> autoRecording
;
9457 gfxTextPerfMetrics
* tp
= mPresContext
->GetTextPerfMetrics();
9458 TimeStamp timeStart
;
9462 timeStart
= TimeStamp::Now();
9465 if (mMobileViewportManager
) {
9466 mMobileViewportManager
->UpdateSizesBeforeReflow();
9469 // Schedule a paint, but don't actually mark this frame as changed for
9470 // retained DL building purposes. If any child frames get moved, then
9471 // they will schedule paint again. We could probaby skip this, and just
9472 // schedule a similar paint when a frame is deleted.
9473 target
->SchedulePaint(nsIFrame::PAINT_DEFAULT
, false);
9475 nsDocShell
* docShell
=
9476 static_cast<nsDocShell
*>(GetPresContext()->GetDocShell());
9477 RefPtr
<TimelineConsumers
> timelines
= TimelineConsumers::Get();
9478 bool isTimelineRecording
= timelines
&& timelines
->HasConsumer(docShell
);
9480 if (isTimelineRecording
) {
9481 timelines
->AddMarkerForDocShell(docShell
, "Reflow",
9482 MarkerTracingType::START
);
9485 Maybe
<uint64_t> innerWindowID
;
9486 if (auto* window
= mDocument
->GetInnerWindow()) {
9487 innerWindowID
= Some(window
->WindowID());
9489 AutoProfilerTracing
tracingLayoutFlush(
9490 "Paint", "Reflow", geckoprofiler::category::LAYOUT
,
9491 std::move(mReflowCause
), innerWindowID
);
9492 mReflowCause
= nullptr;
9494 FlushPendingScrollAnchorSelections();
9496 if (mReflowContinueTimer
) {
9497 mReflowContinueTimer
->Cancel();
9498 mReflowContinueTimer
= nullptr;
9501 const bool isRoot
= target
== mFrameConstructor
->GetRootFrame();
9503 MOZ_ASSERT(isRoot
|| aOverflowTracker
,
9504 "caller must provide overflow tracker when reflowing "
9507 // CreateReferenceRenderingContext can return nullptr
9508 RefPtr
<gfxContext
> rcx(CreateReferenceRenderingContext());
9511 mCurrentReflowRoot
= target
;
9514 // If the target frame is the root of the frame hierarchy, then
9515 // use all the available space. If it's simply a `reflow root',
9516 // then use the target frame's size as the available space.
9517 WritingMode wm
= target
->GetWritingMode();
9518 LogicalSize
size(wm
);
9520 size
= LogicalSize(wm
, mPresContext
->GetVisibleArea().Size());
9522 size
= target
->GetLogicalSize();
9525 OverflowAreas oldOverflow
; // initialized and used only when !isRoot
9527 oldOverflow
= target
->GetOverflowAreas();
9530 NS_ASSERTION(!target
->GetNextInFlow() && !target
->GetPrevInFlow(),
9531 "reflow roots should never split");
9533 // Don't pass size directly to the reflow input, since a
9534 // constrained height implies page/column breaking.
9535 LogicalSize
reflowSize(wm
, size
.ISize(wm
), NS_UNCONSTRAINEDSIZE
);
9536 ReflowInput
reflowInput(mPresContext
, target
, rcx
, reflowSize
,
9537 ReflowInput::InitFlag::CallerWillInit
);
9538 reflowInput
.mOrthogonalLimit
= size
.BSize(wm
);
9541 reflowInput
.Init(mPresContext
);
9543 // When the root frame is being reflowed with unconstrained block-size
9544 // (which happens when we're called from
9545 // nsDocumentViewer::SizeToContent), we're effectively doing a
9546 // resize in the block direction, since it changes the meaning of
9547 // percentage block-sizes even if no block-sizes actually changed.
9548 // The same applies when we reflow again after that computation. This is
9549 // an unusual case, and isn't caught by ReflowInput::InitResizeFlags.
9550 bool hasUnconstrainedBSize
= size
.BSize(wm
) == NS_UNCONSTRAINEDSIZE
;
9552 if (hasUnconstrainedBSize
|| mLastRootReflowHadUnconstrainedBSize
) {
9553 reflowInput
.SetBResize(true);
9556 mLastRootReflowHadUnconstrainedBSize
= hasUnconstrainedBSize
;
9558 // Initialize reflow input with current used border and padding,
9559 // in case this was set specially by the parent frame when the reflow root
9560 // was reflowed by its parent.
9561 reflowInput
.Init(mPresContext
, Nothing(),
9562 Some(target
->GetLogicalUsedBorder(wm
)),
9563 Some(target
->GetLogicalUsedPadding(wm
)));
9566 // fix the computed height
9567 NS_ASSERTION(reflowInput
.ComputedPhysicalMargin() == nsMargin(0, 0, 0, 0),
9568 "reflow input should not set margin for reflow roots");
9569 if (size
.BSize(wm
) != NS_UNCONSTRAINEDSIZE
) {
9570 nscoord computedBSize
=
9572 reflowInput
.ComputedLogicalBorderPadding(wm
).BStartEnd(wm
);
9573 computedBSize
= std::max(computedBSize
, 0);
9574 reflowInput
.SetComputedBSize(computedBSize
);
9577 reflowInput
.ComputedISize() ==
9579 reflowInput
.ComputedLogicalBorderPadding(wm
).IStartEnd(wm
),
9580 "reflow input computed incorrect inline size");
9582 mPresContext
->ReflowStarted(aInterruptible
);
9583 mIsReflowing
= true;
9585 nsReflowStatus status
;
9586 ReflowOutput
desiredSize(reflowInput
);
9587 target
->Reflow(mPresContext
, desiredSize
, reflowInput
, status
);
9589 // If an incremental reflow is initiated at a frame other than the
9590 // root frame, then its desired size had better not change! If it's
9591 // initiated at the root, then the size better not change unless its
9592 // height was unconstrained to start with.
9593 nsRect boundsRelativeToTarget
=
9594 nsRect(0, 0, desiredSize
.Width(), desiredSize
.Height());
9595 NS_ASSERTION((isRoot
&& size
.BSize(wm
) == NS_UNCONSTRAINEDSIZE
) ||
9596 (desiredSize
.ISize(wm
) == size
.ISize(wm
) &&
9597 desiredSize
.BSize(wm
) == size
.BSize(wm
)),
9598 "non-root frame's desired size changed during an "
9599 "incremental reflow");
9600 NS_ASSERTION(status
.IsEmpty(), "reflow roots should never split");
9602 target
->SetSize(boundsRelativeToTarget
.Size());
9604 // Always use boundsRelativeToTarget here, not
9605 // desiredSize.InkOverflowRect(), because for root frames (where they
9606 // could be different, since root frames are allowed to have overflow) the
9607 // root view bounds need to match the viewport bounds; the view manager
9608 // "window dimensions" code depends on it.
9609 nsContainerFrame::SyncFrameViewAfterReflow(
9610 mPresContext
, target
, target
->GetView(), boundsRelativeToTarget
);
9611 nsContainerFrame::SyncWindowProperties(mPresContext
, target
,
9612 target
->GetView(), rcx
,
9613 nsContainerFrame::SET_ASYNC
);
9615 target
->DidReflow(mPresContext
, nullptr);
9616 if (target
->IsInScrollAnchorChain()) {
9617 ScrollAnchorContainer
* container
= ScrollAnchorContainer::FindFor(target
);
9618 PostPendingScrollAnchorAdjustment(container
);
9620 if (isRoot
&& size
.BSize(wm
) == NS_UNCONSTRAINEDSIZE
) {
9621 mPresContext
->SetVisibleArea(boundsRelativeToTarget
);
9625 mCurrentReflowRoot
= nullptr;
9628 if (!isRoot
&& oldOverflow
!= target
->GetOverflowAreas()) {
9629 // The overflow area changed. Propagate this change to ancestors.
9630 aOverflowTracker
->AddFrame(target
->GetParent(),
9631 OverflowChangedTracker::CHILDREN_CHANGED
);
9635 mPresContext
->HasPendingInterrupt() || mFramesToDirty
.Count() == 0,
9636 "Why do we need to dirty anything if not interrupted?");
9638 mIsReflowing
= false;
9639 bool interrupted
= mPresContext
->HasPendingInterrupt();
9641 // Make sure target gets reflowed again.
9642 for (const auto& key
: mFramesToDirty
) {
9643 // Mark frames dirty until target frame.
9644 for (nsIFrame
* f
= key
; f
&& !f
->IsSubtreeDirty(); f
= f
->GetParent()) {
9645 f
->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN
);
9646 if (f
->IsFlexItem()) {
9647 nsFlexContainerFrame::MarkCachedFlexMeasurementsDirty(f
);
9656 NS_ASSERTION(target
->IsSubtreeDirty(), "Why is the target not dirty?");
9657 mDirtyRoots
.Add(target
);
9658 SetNeedLayoutFlush();
9660 // Clear mFramesToDirty after we've done the target->IsSubtreeDirty()
9661 // assertion so that if it fails it's easier to see what's going on.
9662 #ifdef NOISY_INTERRUPTIBLE_REFLOW
9663 printf("mFramesToDirty.Count() == %u\n", mFramesToDirty
.Count());
9664 #endif /* NOISY_INTERRUPTIBLE_REFLOW */
9665 mFramesToDirty
.Clear();
9667 // Any FlushPendingNotifications with interruptible reflows
9668 // should be suppressed now. We don't want to do extra reflow work
9669 // before our reflow event happens.
9670 mWasLastReflowInterrupted
= true;
9671 MaybeScheduleReflow();
9674 // dump text perf metrics for reflows with significant text processing
9676 if (tp
->current
.numChars
> 100) {
9677 TimeDuration reflowTime
= TimeStamp::Now() - timeStart
;
9678 LogTextPerfStats(tp
, this, tp
->current
, reflowTime
.ToMilliseconds(),
9679 eLog_reflow
, nullptr);
9684 if (isTimelineRecording
) {
9685 timelines
->AddMarkerForDocShell(docShell
, "Reflow", MarkerTracingType::END
);
9688 return !interrupted
;
9692 void PresShell::DoVerifyReflow() {
9693 if (GetVerifyReflowEnable()) {
9694 // First synchronously render what we have so far so that we can
9696 nsView
* rootView
= mViewManager
->GetRootView();
9697 mViewManager
->InvalidateView(rootView
);
9699 FlushPendingNotifications(FlushType::Layout
);
9700 mInVerifyReflow
= true;
9701 bool ok
= VerifyIncrementalReflow();
9702 mInVerifyReflow
= false;
9703 if (VerifyReflowFlags::All
& gVerifyReflowFlags
) {
9704 printf("ProcessReflowCommands: finished (%s)\n", ok
? "ok" : "failed");
9707 if (!mDirtyRoots
.IsEmpty()) {
9708 printf("XXX yikes! reflow commands queued during verify-reflow\n");
9714 // used with Telemetry metrics
9715 #define NS_LONG_REFLOW_TIME_MS 5000
9717 bool PresShell::ProcessReflowCommands(bool aInterruptible
) {
9718 if (mDirtyRoots
.IsEmpty() && !mShouldUnsuppressPainting
) {
9719 // Nothing to do; bail out
9723 mozilla::TimeStamp timerStart
= mozilla::TimeStamp::Now();
9724 bool interrupted
= false;
9725 if (!mDirtyRoots
.IsEmpty()) {
9727 if (VerifyReflowFlags::DumpCommands
& gVerifyReflowFlags
) {
9728 printf("ProcessReflowCommands: begin incremental reflow\n");
9732 // If reflow is interruptible, then make a note of our deadline.
9733 const PRIntervalTime deadline
=
9735 ? PR_IntervalNow() + PR_MicrosecondsToInterval(gMaxRCProcessingTime
)
9736 : (PRIntervalTime
)0;
9738 // Scope for the reflow entry point
9740 nsAutoScriptBlocker scriptBlocker
;
9742 AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Reflow
);
9743 nsViewManager::AutoDisableRefresh
refreshBlocker(mViewManager
);
9745 OverflowChangedTracker overflowTracker
;
9748 // Send an incremental reflow notification to the target frame.
9749 nsIFrame
* target
= mDirtyRoots
.PopShallowestRoot();
9751 if (!target
->IsSubtreeDirty()) {
9752 // It's not dirty anymore, which probably means the notification
9753 // was posted in the middle of a reflow (perhaps with a reflow
9754 // root in the middle). Don't do anything.
9758 interrupted
= !DoReflow(target
, aInterruptible
, &overflowTracker
);
9760 // Keep going until we're out of reflow commands, or we've run
9761 // past our deadline, or we're interrupted.
9762 } while (!interrupted
&& !mDirtyRoots
.IsEmpty() &&
9763 (!aInterruptible
|| PR_IntervalNow() < deadline
));
9765 interrupted
= !mDirtyRoots
.IsEmpty();
9767 overflowTracker
.Flush();
9770 // We didn't get interrupted. Go ahead and perform scroll anchor
9772 FlushPendingScrollAnchorAdjustments();
9776 // Exiting the scriptblocker might have killed us
9777 if (!mIsDestroying
) {
9778 DidDoReflow(aInterruptible
);
9781 // DidDoReflow might have killed us
9782 if (!mIsDestroying
) {
9784 if (VerifyReflowFlags::DumpCommands
& gVerifyReflowFlags
) {
9785 printf("\nPresShell::ProcessReflowCommands() finished: this=%p\n",
9791 // If any new reflow commands were enqueued during the reflow, schedule
9792 // another reflow event to process them. Note that we want to do this
9793 // after DidDoReflow(), since that method can change whether there are
9794 // dirty roots around by flushing, and there's no point in posting a
9795 // reflow event just to have the flush revoke it.
9796 if (!mDirtyRoots
.IsEmpty()) {
9797 MaybeScheduleReflow();
9798 // And record that we might need flushing
9799 SetNeedLayoutFlush();
9804 if (!mIsDestroying
&& mShouldUnsuppressPainting
&& mDirtyRoots
.IsEmpty()) {
9805 // We only unlock if we're out of reflows. It's pointless
9806 // to unlock if reflows are still pending, since reflows
9807 // are just going to thrash the frames around some more. By
9808 // waiting we avoid an overeager "jitter" effect.
9809 mShouldUnsuppressPainting
= false;
9810 UnsuppressAndInvalidate();
9813 if (mDocument
->GetRootElement()) {
9814 TimeDuration elapsed
= TimeStamp::Now() - timerStart
;
9815 int32_t intElapsed
= int32_t(elapsed
.ToMilliseconds());
9817 if (intElapsed
> NS_LONG_REFLOW_TIME_MS
) {
9818 Telemetry::Accumulate(Telemetry::LONG_REFLOW_INTERRUPTIBLE
,
9819 aInterruptible
? 1 : 0);
9823 return !interrupted
;
9826 void PresShell::WindowSizeMoveDone() {
9828 EventStateManager::ClearGlobalActiveContent(nullptr);
9829 ClearMouseCapture();
9834 PresShell::Observe(nsISupports
* aSubject
, const char* aTopic
,
9835 const char16_t
* aData
) {
9836 if (mIsDestroying
) {
9837 NS_WARNING("our observers should have been unregistered by now");
9841 if (!nsCRT::strcmp(aTopic
, "memory-pressure")) {
9842 if (!AssumeAllFramesVisible() && mPresContext
->IsRootContentDocument()) {
9843 DoUpdateApproximateFrameVisibility(/* aRemoveOnly = */ true);
9848 if (!nsCRT::strcmp(aTopic
, NS_WIDGET_WAKE_OBSERVER_TOPIC
)) {
9849 mLastOSWake
= TimeStamp::Now();
9853 // For parent process, user may expect the UI is interactable after a
9854 // tab (previously opened page or home page) has restored.
9855 if (!nsCRT::strcmp(aTopic
, "sessionstore-one-or-no-tab-restored")) {
9856 MOZ_ASSERT(XRE_IsParentProcess());
9857 sProcessInteractable
= true;
9859 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
9861 os
->RemoveObserver(this, "sessionstore-one-or-no-tab-restored");
9866 if (!nsCRT::strcmp(aTopic
, "font-info-updated")) {
9867 mPresContext
->ForceReflowForFontInfoUpdate();
9871 if (!nsCRT::strcmp(aTopic
, "look-and-feel-changed")) {
9872 // See how LookAndFeel::NotifyChangedAllWindows encodes this.
9873 auto kind
= widget::ThemeChangeKind(aData
[0]);
9878 NS_WARNING("unrecognized topic in PresShell::Observe");
9879 return NS_ERROR_FAILURE
;
9882 bool PresShell::AddRefreshObserver(nsARefreshObserver
* aObserver
,
9883 FlushType aFlushType
,
9884 const char* aObserverDescription
) {
9885 nsPresContext
* presContext
= GetPresContext();
9886 if (MOZ_UNLIKELY(!presContext
)) {
9889 presContext
->RefreshDriver()->AddRefreshObserver(aObserver
, aFlushType
,
9890 aObserverDescription
);
9894 bool PresShell::RemoveRefreshObserver(nsARefreshObserver
* aObserver
,
9895 FlushType aFlushType
) {
9896 nsPresContext
* presContext
= GetPresContext();
9897 return presContext
&& presContext
->RefreshDriver()->RemoveRefreshObserver(
9898 aObserver
, aFlushType
);
9901 bool PresShell::AddPostRefreshObserver(nsAPostRefreshObserver
* aObserver
) {
9902 nsPresContext
* presContext
= GetPresContext();
9906 presContext
->RefreshDriver()->AddPostRefreshObserver(aObserver
);
9910 bool PresShell::RemovePostRefreshObserver(nsAPostRefreshObserver
* aObserver
) {
9911 nsPresContext
* presContext
= GetPresContext();
9915 presContext
->RefreshDriver()->RemovePostRefreshObserver(aObserver
);
9919 void PresShell::DoObserveStyleFlushes() {
9920 MOZ_ASSERT(!ObservingStyleFlushes());
9921 mObservingStyleFlushes
= true;
9923 if (MOZ_LIKELY(!mDocument
->GetBFCacheEntry())) {
9924 mPresContext
->RefreshDriver()->AddStyleFlushObserver(this);
9928 void PresShell::DoObserveLayoutFlushes() {
9929 MOZ_ASSERT(!ObservingLayoutFlushes());
9930 mObservingLayoutFlushes
= true;
9932 if (MOZ_LIKELY(!mDocument
->GetBFCacheEntry())) {
9933 mPresContext
->RefreshDriver()->AddLayoutFlushObserver(this);
9937 //------------------------------------------------------
9938 // End of protected and private methods on the PresShell
9939 //------------------------------------------------------
9941 //------------------------------------------------------------------
9942 //-- Delayed event Classes Impls
9943 //------------------------------------------------------------------
9945 PresShell::DelayedInputEvent::DelayedInputEvent()
9946 : DelayedEvent(), mEvent(nullptr) {}
9948 PresShell::DelayedInputEvent::~DelayedInputEvent() { delete mEvent
; }
9950 void PresShell::DelayedInputEvent::Dispatch() {
9951 if (!mEvent
|| !mEvent
->mWidget
) {
9954 nsCOMPtr
<nsIWidget
> widget
= mEvent
->mWidget
;
9955 nsEventStatus status
;
9956 widget
->DispatchEvent(mEvent
, status
);
9959 PresShell::DelayedMouseEvent::DelayedMouseEvent(WidgetMouseEvent
* aEvent
)
9960 : DelayedInputEvent() {
9961 MOZ_DIAGNOSTIC_ASSERT(aEvent
->IsTrusted());
9962 WidgetMouseEvent
* mouseEvent
=
9963 new WidgetMouseEvent(true, aEvent
->mMessage
, aEvent
->mWidget
,
9964 aEvent
->mReason
, aEvent
->mContextMenuTrigger
);
9965 mouseEvent
->AssignMouseEventData(*aEvent
, false);
9966 mEvent
= mouseEvent
;
9969 PresShell::DelayedKeyEvent::DelayedKeyEvent(WidgetKeyboardEvent
* aEvent
)
9970 : DelayedInputEvent() {
9971 MOZ_DIAGNOSTIC_ASSERT(aEvent
->IsTrusted());
9972 WidgetKeyboardEvent
* keyEvent
=
9973 new WidgetKeyboardEvent(true, aEvent
->mMessage
, aEvent
->mWidget
);
9974 keyEvent
->AssignKeyEventData(*aEvent
, false);
9975 keyEvent
->mFlags
.mIsSynthesizedForTests
=
9976 aEvent
->mFlags
.mIsSynthesizedForTests
;
9977 keyEvent
->mFlags
.mIsSuppressedOrDelayed
= true;
9981 bool PresShell::DelayedKeyEvent::IsKeyPressEvent() {
9982 return mEvent
->mMessage
== eKeyPress
;
9985 // Start of DEBUG only code
9989 static void LogVerifyMessage(nsIFrame
* k1
, nsIFrame
* k2
, const char* aMsg
) {
9990 nsAutoString n1
, n2
;
9992 k1
->GetFrameName(n1
);
9994 n1
.AssignLiteral(u
"(null)");
9998 k2
->GetFrameName(n2
);
10000 n2
.AssignLiteral(u
"(null)");
10003 printf("verifyreflow: %s %p != %s %p %s\n",
10004 NS_LossyConvertUTF16toASCII(n1
).get(), (void*)k1
,
10005 NS_LossyConvertUTF16toASCII(n2
).get(), (void*)k2
, aMsg
);
10008 static void LogVerifyMessage(nsIFrame
* k1
, nsIFrame
* k2
, const char* aMsg
,
10009 const nsRect
& r1
, const nsRect
& r2
) {
10010 printf("VerifyReflow Error:\n");
10014 k1
->GetFrameName(name
);
10015 printf(" %s %p ", NS_LossyConvertUTF16toASCII(name
).get(), (void*)k1
);
10017 printf("{%d, %d, %d, %d} != \n", r1
.x
, r1
.y
, r1
.width
, r1
.height
);
10020 k2
->GetFrameName(name
);
10021 printf(" %s %p ", NS_LossyConvertUTF16toASCII(name
).get(), (void*)k2
);
10023 printf("{%d, %d, %d, %d}\n %s\n", r2
.x
, r2
.y
, r2
.width
, r2
.height
, aMsg
);
10026 static void LogVerifyMessage(nsIFrame
* k1
, nsIFrame
* k2
, const char* aMsg
,
10027 const nsIntRect
& r1
, const nsIntRect
& r2
) {
10028 printf("VerifyReflow Error:\n");
10032 k1
->GetFrameName(name
);
10033 printf(" %s %p ", NS_LossyConvertUTF16toASCII(name
).get(), (void*)k1
);
10035 printf("{%d, %d, %d, %d} != \n", r1
.x
, r1
.y
, r1
.width
, r1
.height
);
10038 k2
->GetFrameName(name
);
10039 printf(" %s %p ", NS_LossyConvertUTF16toASCII(name
).get(), (void*)k2
);
10041 printf("{%d, %d, %d, %d}\n %s\n", r2
.x
, r2
.y
, r2
.width
, r2
.height
, aMsg
);
10044 static bool CompareTrees(nsPresContext
* aFirstPresContext
,
10045 nsIFrame
* aFirstFrame
,
10046 nsPresContext
* aSecondPresContext
,
10047 nsIFrame
* aSecondFrame
) {
10048 if (!aFirstPresContext
|| !aFirstFrame
|| !aSecondPresContext
||
10051 // XXX Evil hack to reduce false positives; I can't seem to figure
10052 // out how to flush scrollbar changes correctly
10053 // if (aFirstFrame->IsScrollbarFrame())
10056 const auto& childLists1
= aFirstFrame
->ChildLists();
10057 const auto& childLists2
= aSecondFrame
->ChildLists();
10058 auto iterLists1
= childLists1
.begin();
10059 auto iterLists2
= childLists2
.begin();
10061 const nsFrameList
& kids1
=
10062 iterLists1
!= childLists1
.end() ? iterLists1
->mList
: nsFrameList();
10063 const nsFrameList
& kids2
=
10064 iterLists2
!= childLists2
.end() ? iterLists2
->mList
: nsFrameList();
10065 int32_t l1
= kids1
.GetLength();
10066 int32_t l2
= kids2
.GetLength();
10069 LogVerifyMessage(kids1
.FirstChild(), kids2
.FirstChild(),
10070 "child counts don't match: ");
10071 printf("%d != %d\n", l1
, l2
);
10072 if (!(VerifyReflowFlags::All
& gVerifyReflowFlags
)) {
10077 LayoutDeviceIntRect r1
, r2
;
10080 for (nsFrameList::Enumerator
e1(kids1
), e2(kids2
);; e1
.Next(), e2
.Next()) {
10081 nsIFrame
* k1
= e1
.get();
10082 nsIFrame
* k2
= e2
.get();
10083 if (((nullptr == k1
) && (nullptr != k2
)) ||
10084 ((nullptr != k1
) && (nullptr == k2
))) {
10086 LogVerifyMessage(k1
, k2
, "child lists are different\n");
10088 } else if (nullptr != k1
) {
10089 // Verify that the frames are the same size
10090 if (!k1
->GetRect().IsEqualInterior(k2
->GetRect())) {
10092 LogVerifyMessage(k1
, k2
, "(frame rects)", k1
->GetRect(),
10096 // Make sure either both have views or neither have views; if they
10097 // do have views, make sure the views are the same size. If the
10098 // views have widgets, make sure they both do or neither does. If
10099 // they do, make sure the widgets are the same size.
10100 v1
= k1
->GetView();
10101 v2
= k2
->GetView();
10102 if (((nullptr == v1
) && (nullptr != v2
)) ||
10103 ((nullptr != v1
) && (nullptr == v2
))) {
10105 LogVerifyMessage(k1
, k2
, "child views are not matched\n");
10106 } else if (nullptr != v1
) {
10107 if (!v1
->GetBounds().IsEqualInterior(v2
->GetBounds())) {
10108 LogVerifyMessage(k1
, k2
, "(view rects)", v1
->GetBounds(),
10112 nsIWidget
* w1
= v1
->GetWidget();
10113 nsIWidget
* w2
= v2
->GetWidget();
10114 if (((nullptr == w1
) && (nullptr != w2
)) ||
10115 ((nullptr != w1
) && (nullptr == w2
))) {
10117 LogVerifyMessage(k1
, k2
, "child widgets are not matched\n");
10118 } else if (nullptr != w1
) {
10119 r1
= w1
->GetBounds();
10120 r2
= w2
->GetBounds();
10121 if (!r1
.IsEqualEdges(r2
)) {
10122 LogVerifyMessage(k1
, k2
, "(widget rects)", r1
.ToUnknownRect(),
10123 r2
.ToUnknownRect());
10127 if (!ok
&& !(VerifyReflowFlags::All
& gVerifyReflowFlags
)) {
10131 // XXX Should perhaps compare their float managers.
10133 // Compare the sub-trees too
10134 if (!CompareTrees(aFirstPresContext
, k1
, aSecondPresContext
, k2
)) {
10136 if (!(VerifyReflowFlags::All
& gVerifyReflowFlags
)) {
10144 if (!ok
&& (!(VerifyReflowFlags::All
& gVerifyReflowFlags
))) {
10150 const bool lists1Done
= iterLists1
== childLists1
.end();
10151 const bool lists2Done
= iterLists2
== childLists2
.end();
10152 if (lists1Done
!= lists2Done
||
10153 (!lists1Done
&& iterLists1
->mID
!= iterLists2
->mID
)) {
10154 if (!(VerifyReflowFlags::All
& gVerifyReflowFlags
)) {
10157 LogVerifyMessage(kids1
.FirstChild(), kids2
.FirstChild(),
10158 "child list names are not matched: ");
10159 fprintf(stdout
, "%s != %s\n",
10160 !lists1Done
? ChildListName(iterLists1
->mID
) : "(null)",
10161 !lists2Done
? ChildListName(iterLists2
->mID
) : "(null)");
10164 } while (ok
&& iterLists1
!= childLists1
.end());
10172 FindTopFrame(nsIFrame
* aRoot
)
10175 nsIContent
* content
= aRoot
->GetContent();
10178 content
->GetTag(tag
);
10179 if (nullptr != tag
) {
10185 // Try one of the children
10186 for (nsIFrame
* kid
: aRoot
->PrincipalChildList()) {
10187 nsIFrame
* result
= FindTopFrame(kid
);
10188 if (nullptr != result
) {
10199 // After an incremental reflow, we verify the correctness by doing a
10200 // full reflow into a fresh frame tree.
10201 bool PresShell::VerifyIncrementalReflow() {
10202 if (VerifyReflowFlags::Noisy
& gVerifyReflowFlags
) {
10203 printf("Building Verification Tree...\n");
10206 // Create a presentation context to view the new frame tree
10207 RefPtr
<nsPresContext
> cx
= new nsRootPresContext(
10208 mDocument
, mPresContext
->IsPaginated()
10209 ? nsPresContext::eContext_PrintPreview
10210 : nsPresContext::eContext_Galley
);
10211 NS_ENSURE_TRUE(cx
, false);
10213 nsDeviceContext
* dc
= mPresContext
->DeviceContext();
10214 nsresult rv
= cx
->Init(dc
);
10215 NS_ENSURE_SUCCESS(rv
, false);
10217 // Get our scrolling preference
10218 nsView
* rootView
= mViewManager
->GetRootView();
10219 NS_ENSURE_TRUE(rootView
->HasWidget(), false);
10220 nsIWidget
* parentWidget
= rootView
->GetWidget();
10222 // Create a new view manager.
10223 RefPtr
<nsViewManager
> vm
= new nsViewManager();
10224 NS_ENSURE_TRUE(vm
, false);
10226 NS_ENSURE_SUCCESS(rv
, false);
10228 // Create a child window of the parent that is our "root view/window"
10230 nsRect tbounds
= mPresContext
->GetVisibleArea();
10231 nsView
* view
= vm
->CreateView(tbounds
, nullptr);
10232 NS_ENSURE_TRUE(view
, false);
10234 // now create the widget for the view
10235 rv
= view
->CreateWidgetForParent(parentWidget
, nullptr, true);
10236 NS_ENSURE_SUCCESS(rv
, false);
10238 // Setup hierarchical relationship in view manager
10239 vm
->SetRootView(view
);
10241 // Make the new presentation context the same size as our
10242 // presentation context.
10243 cx
->SetVisibleArea(mPresContext
->GetVisibleArea());
10245 RefPtr
<PresShell
> presShell
= mDocument
->CreatePresShell(cx
, vm
);
10246 NS_ENSURE_TRUE(presShell
, false);
10248 // Note that after we create the shell, we must make sure to destroy it
10249 presShell
->SetVerifyReflowEnable(
10250 false); // turn off verify reflow while we're
10251 // reflowing the test frame tree
10252 vm
->SetPresShell(presShell
);
10254 nsAutoCauseReflowNotifier
crNotifier(this);
10255 presShell
->Initialize();
10257 presShell
->FlushPendingNotifications(FlushType::Layout
);
10258 presShell
->SetVerifyReflowEnable(
10259 true); // turn on verify reflow again now that
10260 // we're done reflowing the test frame tree
10261 // Force the non-primary presshell to unsuppress; it doesn't want to normally
10262 // because it thinks it's hidden
10263 presShell
->mPaintingSuppressed
= false;
10264 if (VerifyReflowFlags::Noisy
& gVerifyReflowFlags
) {
10265 printf("Verification Tree built, comparing...\n");
10268 // Now that the document has been reflowed, use its frame tree to
10269 // compare against our frame tree.
10270 nsIFrame
* root1
= mFrameConstructor
->GetRootFrame();
10271 nsIFrame
* root2
= presShell
->GetRootFrame();
10272 bool ok
= CompareTrees(mPresContext
, root1
, cx
, root2
);
10273 if (!ok
&& (VerifyReflowFlags::Noisy
& gVerifyReflowFlags
)) {
10274 printf("Verify reflow failed, primary tree:\n");
10275 root1
->List(stdout
);
10276 printf("Verification tree:\n");
10277 root2
->List(stdout
);
10281 // Sample code for dumping page to png
10282 // XXX Needs to be made more flexible
10285 static int num
= 0;
10286 stra
.AppendLiteral("C:\\mozilla\\mozilla\\debug\\filea");
10287 stra
.AppendInt(num
);
10288 stra
.AppendLiteral(".png");
10289 gfxUtils::WriteAsPNG(presShell
, stra
);
10291 strb
.AppendLiteral("C:\\mozilla\\mozilla\\debug\\fileb");
10292 strb
.AppendInt(num
);
10293 strb
.AppendLiteral(".png");
10294 gfxUtils::WriteAsPNG(presShell
, strb
);
10299 presShell
->EndObservingDocument();
10300 presShell
->Destroy();
10301 if (VerifyReflowFlags::Noisy
& gVerifyReflowFlags
) {
10302 printf("Finished Verifying Reflow...\n");
10308 // Layout debugging hooks
10309 void PresShell::ListComputedStyles(FILE* out
, int32_t aIndent
) {
10310 nsIFrame
* rootFrame
= GetRootFrame();
10312 rootFrame
->Style()->List(out
, aIndent
);
10315 // The root element's frame's ComputedStyle is the root of a separate tree.
10316 Element
* rootElement
= mDocument
->GetRootElement();
10318 nsIFrame
* rootElementFrame
= rootElement
->GetPrimaryFrame();
10319 if (rootElementFrame
) {
10320 rootElementFrame
->Style()->List(out
, aIndent
);
10326 #if defined(DEBUG) || defined(MOZ_LAYOUT_DEBUGGER)
10327 void PresShell::ListStyleSheets(FILE* out
, int32_t aIndent
) {
10328 auto ListStyleSheetsAtOrigin
= [this, out
, aIndent
](StyleOrigin origin
) {
10329 int32_t sheetCount
= StyleSet()->SheetCount(origin
);
10330 for (int32_t i
= 0; i
< sheetCount
; ++i
) {
10331 StyleSet()->SheetAt(origin
, i
)->List(out
, aIndent
);
10335 ListStyleSheetsAtOrigin(StyleOrigin::UserAgent
);
10336 ListStyleSheetsAtOrigin(StyleOrigin::User
);
10337 ListStyleSheetsAtOrigin(StyleOrigin::Author
);
10341 //=============================================================
10342 //=============================================================
10343 //-- Debug Reflow Counts
10344 //=============================================================
10345 //=============================================================
10346 #ifdef MOZ_REFLOW_PERF
10347 //-------------------------------------------------------------
10348 void PresShell::DumpReflows() {
10349 if (mReflowCountMgr
) {
10350 nsAutoCString uriStr
;
10352 nsIURI
* uri
= mDocument
->GetDocumentURI();
10354 uri
->GetPathQueryRef(uriStr
);
10357 mReflowCountMgr
->DisplayTotals(uriStr
.get());
10358 mReflowCountMgr
->DisplayHTMLTotals(uriStr
.get());
10359 mReflowCountMgr
->DisplayDiffsInTotals();
10363 //-------------------------------------------------------------
10364 void PresShell::CountReflows(const char* aName
, nsIFrame
* aFrame
) {
10365 if (mReflowCountMgr
) {
10366 mReflowCountMgr
->Add(aName
, aFrame
);
10370 //-------------------------------------------------------------
10371 void PresShell::PaintCount(const char* aName
, gfxContext
* aRenderingContext
,
10372 nsPresContext
* aPresContext
, nsIFrame
* aFrame
,
10373 const nsPoint
& aOffset
, uint32_t aColor
) {
10374 if (mReflowCountMgr
) {
10375 mReflowCountMgr
->PaintCount(aName
, aRenderingContext
, aPresContext
, aFrame
,
10380 //-------------------------------------------------------------
10381 void PresShell::SetPaintFrameCount(bool aPaintFrameCounts
) {
10382 if (mReflowCountMgr
) {
10383 mReflowCountMgr
->SetPaintFrameCounts(aPaintFrameCounts
);
10387 bool PresShell::IsPaintingFrameCounts() {
10388 if (mReflowCountMgr
) return mReflowCountMgr
->IsPaintingFrameCounts();
10392 //------------------------------------------------------------------
10393 //-- Reflow Counter Classes Impls
10394 //------------------------------------------------------------------
10396 //------------------------------------------------------------------
10397 ReflowCounter::ReflowCounter(ReflowCountMgr
* aMgr
) : mMgr(aMgr
) {
10402 //------------------------------------------------------------------
10403 ReflowCounter::~ReflowCounter() = default;
10405 //------------------------------------------------------------------
10406 void ReflowCounter::ClearTotals() { mTotal
= 0; }
10408 //------------------------------------------------------------------
10409 void ReflowCounter::SetTotalsCache() { mCacheTotal
= mTotal
; }
10411 //------------------------------------------------------------------
10412 void ReflowCounter::CalcDiffInTotals() { mCacheTotal
= mTotal
- mCacheTotal
; }
10414 //------------------------------------------------------------------
10415 void ReflowCounter::DisplayTotals(const char* aStr
) {
10416 DisplayTotals(mTotal
, aStr
? aStr
: "Totals");
10419 //------------------------------------------------------------------
10420 void ReflowCounter::DisplayDiffTotals(const char* aStr
) {
10421 DisplayTotals(mCacheTotal
, aStr
? aStr
: "Diff Totals");
10424 //------------------------------------------------------------------
10425 void ReflowCounter::DisplayHTMLTotals(const char* aStr
) {
10426 DisplayHTMLTotals(mTotal
, aStr
? aStr
: "Totals");
10429 //------------------------------------------------------------------
10430 void ReflowCounter::DisplayTotals(uint32_t aTotal
, const char* aTitle
) {
10435 ReflowCounter
* gTots
= (ReflowCounter
*)mMgr
->LookUp(kGrandTotalsStr
);
10437 printf("%25s\t", aTitle
);
10438 printf("%d\t", aTotal
);
10439 if (gTots
!= this && aTotal
> 0) {
10440 gTots
->Add(aTotal
);
10444 //------------------------------------------------------------------
10445 void ReflowCounter::DisplayHTMLTotals(uint32_t aTotal
, const char* aTitle
) {
10450 ReflowCounter
* gTots
= (ReflowCounter
*)mMgr
->LookUp(kGrandTotalsStr
);
10451 FILE* fd
= mMgr
->GetOutFile();
10456 fprintf(fd
, "<tr><td><center>%s</center></td>", aTitle
);
10457 fprintf(fd
, "<td><center>%d</center></td></tr>\n", aTotal
);
10459 if (gTots
!= this && aTotal
> 0) {
10460 gTots
->Add(aTotal
);
10464 //------------------------------------------------------------------
10465 //-- ReflowCountMgr
10466 //------------------------------------------------------------------
10468 # define KEY_BUF_SIZE_FOR_PTR \
10469 24 // adequate char[] buffer to sprintf a pointer
10471 ReflowCountMgr::ReflowCountMgr() : mCounts(10), mIndiFrameCounts(10) {
10472 mCycledOnce
= false;
10473 mDumpFrameCounts
= false;
10474 mDumpFrameByFrameCounts
= false;
10475 mPaintFrameByFrameCounts
= false;
10478 //------------------------------------------------------------------
10479 ReflowCountMgr::~ReflowCountMgr() = default;
10481 //------------------------------------------------------------------
10482 ReflowCounter
* ReflowCountMgr::LookUp(const char* aName
) {
10483 return mCounts
.Get(aName
);
10486 //------------------------------------------------------------------
10487 void ReflowCountMgr::Add(const char* aName
, nsIFrame
* aFrame
) {
10488 NS_ASSERTION(aName
!= nullptr, "Name shouldn't be null!");
10490 if (mDumpFrameCounts
) {
10491 auto* const counter
= mCounts
.GetOrInsertNew(aName
, this);
10495 if ((mDumpFrameByFrameCounts
|| mPaintFrameByFrameCounts
) &&
10496 aFrame
!= nullptr) {
10497 char key
[KEY_BUF_SIZE_FOR_PTR
];
10498 SprintfLiteral(key
, "%p", (void*)aFrame
);
10499 auto* const counter
=
10501 .LookupOrInsertWith(key
,
10502 [&aName
, &aFrame
, this]() {
10504 MakeUnique
<IndiReflowCounter
>(this);
10505 counter
->mFrame
= aFrame
;
10506 counter
->mName
.AssignASCII(aName
);
10510 // this eliminates extra counts from super classes
10511 if (counter
&& counter
->mName
.EqualsASCII(aName
)) {
10513 counter
->mCounter
.Add(1);
10518 //------------------------------------------------------------------
10519 void ReflowCountMgr::PaintCount(const char* aName
,
10520 gfxContext
* aRenderingContext
,
10521 nsPresContext
* aPresContext
, nsIFrame
* aFrame
,
10522 const nsPoint
& aOffset
, uint32_t aColor
) {
10523 if (mPaintFrameByFrameCounts
&& aFrame
!= nullptr) {
10524 char key
[KEY_BUF_SIZE_FOR_PTR
];
10525 SprintfLiteral(key
, "%p", (void*)aFrame
);
10526 IndiReflowCounter
* counter
= mIndiFrameCounts
.Get(key
);
10527 if (counter
!= nullptr && counter
->mName
.EqualsASCII(aName
)) {
10528 DrawTarget
* drawTarget
= aRenderingContext
->GetDrawTarget();
10529 int32_t appUnitsPerDevPixel
= aPresContext
->AppUnitsPerDevPixel();
10531 aRenderingContext
->Save();
10532 gfxPoint devPixelOffset
=
10533 nsLayoutUtils::PointToGfxPoint(aOffset
, appUnitsPerDevPixel
);
10534 aRenderingContext
->SetMatrixDouble(
10535 aRenderingContext
->CurrentMatrixDouble().PreTranslate(
10538 // We don't care about the document language or user fonts here;
10539 // just get a default Latin font.
10540 nsFont
font(StyleGenericFontFamily::Serif
, Length::FromPixels(11));
10541 nsFontMetrics::Params params
;
10542 params
.language
= nsGkAtoms::x_western
;
10543 params
.textPerf
= aPresContext
->GetTextPerfMetrics();
10544 params
.fontStats
= nullptr; // not interested here
10545 params
.featureValueLookup
= aPresContext
->GetFontFeatureValuesLookup();
10546 RefPtr
<nsFontMetrics
> fm
=
10547 aPresContext
->DeviceContext()->GetMetricsFor(font
, params
);
10550 int len
= SprintfLiteral(buf
, "%d", counter
->mCount
);
10551 nscoord x
= 0, y
= fm
->MaxAscent();
10552 nscoord width
, height
= fm
->MaxHeight();
10553 fm
->SetTextRunRTL(false);
10554 width
= fm
->GetWidth(buf
, len
, drawTarget
);
10559 color
= sRGBColor::FromABGR(aColor
);
10560 color2
= sRGBColor(0.f
, 0.f
, 0.f
);
10562 gfx::Float rc
= 0.f
, gc
= 0.f
, bc
= 0.f
;
10563 if (counter
->mCount
< 5) {
10566 } else if (counter
->mCount
< 11) {
10571 color
= sRGBColor(rc
, gc
, bc
);
10572 color2
= sRGBColor(rc
/ 2, gc
/ 2, bc
/ 2);
10575 nsRect
rect(0, 0, width
+ 15, height
+ 15);
10577 NSRectToSnappedRect(rect
, appUnitsPerDevPixel
, *drawTarget
);
10578 ColorPattern
black(ToDeviceColor(sRGBColor::OpaqueBlack()));
10579 drawTarget
->FillRect(devPxRect
, black
);
10581 aRenderingContext
->SetColor(color2
);
10582 fm
->DrawString(buf
, len
, x
+ 15, y
+ 15, aRenderingContext
);
10583 aRenderingContext
->SetColor(color
);
10584 fm
->DrawString(buf
, len
, x
, y
, aRenderingContext
);
10586 aRenderingContext
->Restore();
10591 //------------------------------------------------------------------
10592 void ReflowCountMgr::DoGrandTotals() {
10593 mCounts
.WithEntryHandle(kGrandTotalsStr
, [this](auto&& entry
) {
10595 entry
.Insert(MakeUnique
<ReflowCounter
>(this));
10597 entry
.Data()->ClearTotals();
10601 printf("\t\t\t\tTotal\n");
10602 for (uint32_t i
= 0; i
< 78; i
++) {
10606 for (const auto& entry
: mCounts
) {
10607 entry
.GetData()->DisplayTotals(entry
.GetKey());
10611 static void RecurseIndiTotals(
10612 nsPresContext
* aPresContext
,
10613 nsClassHashtable
<nsCharPtrHashKey
, IndiReflowCounter
>& aHT
,
10614 nsIFrame
* aParentFrame
, int32_t aLevel
) {
10615 if (aParentFrame
== nullptr) {
10619 char key
[KEY_BUF_SIZE_FOR_PTR
];
10620 SprintfLiteral(key
, "%p", (void*)aParentFrame
);
10621 IndiReflowCounter
* counter
= aHT
.Get(key
);
10623 counter
->mHasBeenOutput
= true;
10624 char* name
= ToNewCString(counter
->mName
);
10625 for (int32_t i
= 0; i
< aLevel
; i
++) printf(" ");
10626 printf("%s - %p [%d][", name
, (void*)aParentFrame
, counter
->mCount
);
10627 printf("%d", counter
->mCounter
.GetTotal());
10632 for (nsIFrame
* child
: aParentFrame
->PrincipalChildList()) {
10633 RecurseIndiTotals(aPresContext
, aHT
, child
, aLevel
+ 1);
10637 //------------------------------------------------------------------
10638 void ReflowCountMgr::DoIndiTotalsTree() {
10639 printf("\n------------------------------------------------\n");
10640 printf("-- Individual Frame Counts\n");
10641 printf("------------------------------------------------\n");
10644 nsIFrame
* rootFrame
= mPresShell
->GetRootFrame();
10645 RecurseIndiTotals(mPresContext
, mIndiFrameCounts
, rootFrame
, 0);
10646 printf("------------------------------------------------\n");
10647 printf("-- Individual Counts of Frames not in Root Tree\n");
10648 printf("------------------------------------------------\n");
10649 for (const auto& counter
: mIndiFrameCounts
.Values()) {
10650 if (!counter
->mHasBeenOutput
) {
10651 char* name
= ToNewCString(counter
->mName
);
10652 printf("%s - %p [%d][", name
, (void*)counter
->mFrame
,
10654 printf("%d", counter
->mCounter
.GetTotal());
10662 //------------------------------------------------------------------
10663 void ReflowCountMgr::DoGrandHTMLTotals() {
10664 mCounts
.WithEntryHandle(kGrandTotalsStr
, [this](auto&& entry
) {
10666 entry
.Insert(MakeUnique
<ReflowCounter
>(this));
10668 entry
.Data()->ClearTotals();
10672 static const char* title
[] = {"Class", "Reflows"};
10673 fprintf(mFD
, "<tr>");
10674 for (uint32_t i
= 0; i
< ArrayLength(title
); i
++) {
10675 fprintf(mFD
, "<td><center><b>%s<b></center></td>", title
[i
]);
10677 fprintf(mFD
, "</tr>\n");
10679 for (const auto& entry
: mCounts
) {
10680 entry
.GetData()->DisplayHTMLTotals(entry
.GetKey());
10684 //------------------------------------
10685 void ReflowCountMgr::DisplayTotals(const char* aStr
) {
10687 printf("%s\n", aStr
? aStr
: "No name");
10689 if (mDumpFrameCounts
) {
10692 if (mDumpFrameByFrameCounts
) {
10693 DoIndiTotalsTree();
10696 //------------------------------------
10697 void ReflowCountMgr::DisplayHTMLTotals(const char* aStr
) {
10698 # ifdef WIN32x // XXX NOT XP!
10701 char* sptr
= strrchr(aStr
, '/');
10704 strcpy(name
, sptr
);
10705 char* eptr
= strrchr(name
, '.');
10709 strcat(name
, "_stats.html");
10711 mFD
= fopen(name
, "w");
10713 fprintf(mFD
, "<html><head><title>Reflow Stats</title></head><body>\n");
10714 const char* title
= aStr
? aStr
: "No name";
10716 "<center><b>%s</b><br><table border=1 "
10717 "style=\"background-color:#e0e0e0\">",
10719 DoGrandHTMLTotals();
10720 fprintf(mFD
, "</center></table>\n");
10721 fprintf(mFD
, "</body></html>\n");
10728 //------------------------------------------------------------------
10729 void ReflowCountMgr::ClearTotals() {
10730 for (const auto& data
: mCounts
.Values()) {
10731 data
->ClearTotals();
10735 //------------------------------------------------------------------
10736 void ReflowCountMgr::ClearGrandTotals() {
10737 mCounts
.WithEntryHandle(kGrandTotalsStr
, [&](auto&& entry
) {
10739 entry
.Insert(MakeUnique
<ReflowCounter
>(this));
10741 entry
.Data()->ClearTotals();
10742 entry
.Data()->SetTotalsCache();
10747 //------------------------------------------------------------------
10748 void ReflowCountMgr::DisplayDiffsInTotals() {
10750 printf("Differences\n");
10751 for (int32_t i
= 0; i
< 78; i
++) {
10755 ClearGrandTotals();
10758 for (const auto& entry
: mCounts
) {
10760 entry
.GetData()->CalcDiffInTotals();
10761 entry
.GetData()->DisplayDiffTotals(entry
.GetKey());
10763 entry
.GetData()->SetTotalsCache();
10766 mCycledOnce
= true;
10769 #endif // MOZ_REFLOW_PERF
10771 nsIFrame
* PresShell::GetAbsoluteContainingBlock(nsIFrame
* aFrame
) {
10772 return FrameConstructor()->GetAbsoluteContainingBlock(
10773 aFrame
, nsCSSFrameConstructor::ABS_POS
);
10776 #ifdef ACCESSIBILITY
10779 bool PresShell::IsAccessibilityActive() { return GetAccService() != nullptr; }
10782 nsAccessibilityService
* PresShell::GetAccessibilityService() {
10783 return GetAccService();
10786 #endif // #ifdef ACCESSIBILITY
10788 void PresShell::ActivenessMaybeChanged() {
10792 SetIsActive(ShouldBeActive());
10795 bool PresShell::ShouldBeActive() const {
10796 MOZ_LOG(gLog
, LogLevel::Debug
,
10797 ("PresShell::ShouldBeActive(%s, %d)\n",
10798 mDocument
->GetDocumentURI()
10799 ? mDocument
->GetDocumentURI()->GetSpecOrDefault().get()
10803 Document
* doc
= mDocument
;
10805 if (doc
->IsBeingUsedAsImage()) {
10806 // Documents used as an image can remain active. They do not tick their
10807 // refresh driver if not painted, and they can't run script or such so they
10808 // can't really observe much else.
10812 if (Document
* displayDoc
= doc
->GetDisplayDocument()) {
10813 // Ok, we're an external resource document -- we need to use our display
10814 // document's docshell to determine "IsActive" status, since we lack
10815 // a browsing context of our own.
10816 MOZ_ASSERT(!doc
->GetBrowsingContext(),
10817 "external resource doc shouldn't have its own BC");
10821 Document
* root
= nsContentUtils::GetInProcessSubtreeRootDocument(doc
);
10822 if (auto* browserChild
= BrowserChild::GetFrom(root
->GetDocShell())) {
10823 // We might want to activate a tab even though the browsing-context is not
10824 // active if the BrowserChild is considered visible. This serves two
10827 // * For top-level tabs, we use this for tab warming. The browsing-context
10828 // might still be inactive, but we want to activate the pres shell and
10829 // the refresh driver.
10831 // * For oop iframes, we do want to throttle them if they're not visible.
10833 // TODO(emilio): Consider unifying the in-process vs. fission iframe
10834 // throttling code (in-process throttling for non-visible iframes lives
10835 // right now in Document::ShouldThrottleFrameRequests(), but that only
10837 if (!browserChild
->IsVisible()) {
10838 MOZ_LOG(gLog
, LogLevel::Debug
,
10839 (" > BrowserChild %p is not visible", browserChild
));
10843 // If the browser is visible but just due to be preserving layers
10844 // artificially, we do want to fall back to the browsing context activeness
10845 // instead. Otherwise we do want to be active for the use cases above.
10846 if (!browserChild
->IsPreservingLayers()) {
10847 MOZ_LOG(gLog
, LogLevel::Debug
,
10848 (" > BrowserChild %p is visible and not preserving layers",
10853 gLog
, LogLevel::Debug
,
10854 (" > BrowserChild %p is visible and preserving layers", browserChild
));
10857 BrowsingContext
* bc
= doc
->GetBrowsingContext();
10858 MOZ_LOG(gLog
, LogLevel::Debug
,
10859 (" > BrowsingContext %p active: %d", bc
, bc
&& bc
->IsActive()));
10860 return bc
&& bc
->IsActive();
10863 void PresShell::SetIsActive(bool aIsActive
) {
10864 MOZ_ASSERT(mDocument
, "should only be called with a document");
10866 const bool changed
= mIsActive
!= aIsActive
;
10868 mIsActive
= aIsActive
;
10870 nsPresContext
* presContext
= GetPresContext();
10872 presContext
->RefreshDriver()->GetPresContext() == presContext
) {
10873 presContext
->RefreshDriver()->SetThrottled(!mIsActive
);
10877 // Propagate state-change to my resource documents' PresShells and other
10880 // Note that it is fine to not propagate to fission iframes. Those will
10881 // become active / inactive as needed as a result of they getting painted /
10882 // not painted eventually.
10883 auto recurse
= [aIsActive
](Document
& aSubDoc
) {
10884 if (PresShell
* presShell
= aSubDoc
.GetPresShell()) {
10885 presShell
->SetIsActive(aIsActive
);
10887 return CallState::Continue
;
10889 mDocument
->EnumerateExternalResources(recurse
);
10890 mDocument
->EnumerateSubDocuments(recurse
);
10893 UpdateImageLockingState();
10894 #ifdef ACCESSIBILITY
10896 if (nsAccessibilityService
* accService
=
10897 PresShell::GetAccessibilityService()) {
10898 accService
->PresShellActivated(this);
10901 #endif // #ifdef ACCESSIBILITY
10903 #if defined(MOZ_WIDGET_ANDROID)
10904 if (changed
&& !aIsActive
&& presContext
&&
10905 presContext
->IsRootContentDocumentCrossProcess()) {
10906 if (BrowserChild
* browserChild
= BrowserChild::GetFrom(this)) {
10907 // Reset the dynamic toolbar offset state.
10908 presContext
->UpdateDynamicToolbarOffset(0);
10914 if (nsIFrame
* rootFrame
= GetRootFrame()) {
10915 rootFrame
->SchedulePaint();
10920 RefPtr
<MobileViewportManager
> PresShell::GetMobileViewportManager() const {
10921 return mMobileViewportManager
;
10924 Maybe
<MobileViewportManager::ManagerType
> UseMobileViewportManager(
10925 PresShell
* aPresShell
, Document
* aDocument
) {
10926 // If we're not using APZ, we won't be able to zoom, so there is no
10927 // point in having an MVM.
10928 if (nsPresContext
* presContext
= aPresShell
->GetPresContext()) {
10929 if (nsIWidget
* widget
= presContext
->GetNearestWidget()) {
10930 if (!widget
->AsyncPanZoomEnabled()) {
10935 if (nsLayoutUtils::ShouldHandleMetaViewport(aDocument
)) {
10936 return Some(MobileViewportManager::ManagerType::VisualAndMetaViewport
);
10938 if (StaticPrefs::apz_mvm_force_enabled() ||
10939 nsLayoutUtils::AllowZoomingForDocument(aDocument
)) {
10940 return Some(MobileViewportManager::ManagerType::VisualViewportOnly
);
10945 void PresShell::MaybeRecreateMobileViewportManager(bool aAfterInitialization
) {
10946 // Determine if we require a MobileViewportManager, and what kind if so. We
10947 // need one any time we allow resolution zooming for a document, and any time
10948 // we want to obey <meta name="viewport"> tags for it.
10949 Maybe
<MobileViewportManager::ManagerType
> mvmType
=
10950 UseMobileViewportManager(this, mDocument
);
10952 if (mvmType
.isNothing() && !mMobileViewportManager
) {
10953 // We don't need one and don't have it. So we're done.
10956 if (mvmType
&& mMobileViewportManager
&&
10957 *mvmType
== mMobileViewportManager
->GetManagerType()) {
10958 // We need one and we have one of the correct type, so we're done.
10962 if (mMobileViewportManager
) {
10963 // We have one, but we need to either destroy it completely to replace it
10964 // with another one of the correct type. So either way, let's destroy the
10966 mMobileViewportManager
->Destroy();
10967 mMobileViewportManager
= nullptr;
10968 mMVMContext
= nullptr;
10970 ResetVisualViewportSize();
10972 // After we clear out the MVM and the MVMContext, also reset the
10973 // resolution to its pre-MVM value.
10974 SetResolutionAndScaleTo(mDocument
->GetSavedResolutionBeforeMVM(),
10975 ResolutionChangeOrigin::MainThreadRestore
);
10977 if (aAfterInitialization
) {
10978 // Force a reflow to our correct size by going back to the docShell
10979 // and asking it to reassert its size. This is necessary because
10980 // everything underneath the docShell, like the ViewManager, has been
10981 // altered by the MobileViewportManager in an irreversible way.
10982 nsDocShell
* docShell
=
10983 static_cast<nsDocShell
*>(GetPresContext()->GetDocShell());
10984 int32_t width
, height
;
10985 docShell
->GetSize(&width
, &height
);
10986 docShell
->SetSize(width
, height
, false);
10991 // Let's create the MVM of the type that we need. At this point we shouldn't
10993 MOZ_ASSERT(!mMobileViewportManager
);
10995 if (mPresContext
->IsRootContentDocumentCrossProcess()) {
10996 // Store the resolution so we can restore to this resolution when
10997 // the MVM is destroyed.
10998 mDocument
->SetSavedResolutionBeforeMVM(mResolution
.valueOr(1.0f
));
11000 mMVMContext
= new GeckoMVMContext(mDocument
, this);
11001 mMobileViewportManager
= new MobileViewportManager(mMVMContext
, *mvmType
);
11002 if (MOZ_UNLIKELY(MOZ_LOG_TEST(sApzMvmLog
, LogLevel::Debug
))) {
11003 nsIURI
* uri
= mDocument
->GetDocumentURI();
11004 MOZ_LOG(sApzMvmLog
, LogLevel::Debug
,
11005 ("Created MVM %p (type %d) for URI %s",
11006 mMobileViewportManager
.get(), (int)*mvmType
,
11007 uri
? uri
->GetSpecOrDefault().get() : "(null)"));
11010 if (aAfterInitialization
) {
11011 // Setting the initial viewport will trigger a reflow.
11012 mMobileViewportManager
->SetInitialViewport();
11018 bool PresShell::UsesMobileViewportSizing() const {
11019 return mMobileViewportManager
!= nullptr &&
11020 nsLayoutUtils::ShouldHandleMetaViewport(mDocument
);
11024 * Determines the current image locking state. Called when one of the
11025 * dependent factors changes.
11027 void PresShell::UpdateImageLockingState() {
11028 // We're locked if we're both thawed and active.
11029 bool locked
= !mFrozen
&& mIsActive
;
11031 mDocument
->ImageTracker()->SetLockingState(locked
);
11034 // Request decodes for visible image frames; we want to start decoding as
11035 // quickly as possible when we get foregrounded to minimize flashing.
11036 for (const auto& key
: mApproximatelyVisibleFrames
) {
11037 if (nsImageFrame
* imageFrame
= do_QueryFrame(key
)) {
11038 imageFrame
->MaybeDecodeForPredictedSize();
11044 PresShell
* PresShell::GetRootPresShell() const {
11045 if (mPresContext
) {
11046 nsPresContext
* rootPresContext
= mPresContext
->GetRootPresContext();
11047 if (rootPresContext
) {
11048 return rootPresContext
->PresShell();
11054 void PresShell::AddSizeOfIncludingThis(nsWindowSizes
& aSizes
) const {
11055 MallocSizeOf mallocSizeOf
= aSizes
.mState
.mMallocSizeOf
;
11056 mFrameArena
.AddSizeOfExcludingThis(aSizes
, Arena::ArenaKind::PresShell
);
11057 aSizes
.mLayoutPresShellSize
+= mallocSizeOf(this);
11059 aSizes
.mLayoutPresShellSize
+= mCaret
->SizeOfIncludingThis(mallocSizeOf
);
11061 aSizes
.mLayoutPresShellSize
+=
11062 mApproximatelyVisibleFrames
.ShallowSizeOfExcludingThis(mallocSizeOf
) +
11063 mFramesToDirty
.ShallowSizeOfExcludingThis(mallocSizeOf
) +
11064 mPendingScrollAnchorSelection
.ShallowSizeOfExcludingThis(mallocSizeOf
) +
11065 mPendingScrollAnchorAdjustment
.ShallowSizeOfExcludingThis(mallocSizeOf
);
11067 aSizes
.mLayoutTextRunsSize
+= SizeOfTextRuns(mallocSizeOf
);
11069 aSizes
.mLayoutPresContextSize
+=
11070 mPresContext
->SizeOfIncludingThis(mallocSizeOf
);
11072 mFrameConstructor
->AddSizeOfIncludingThis(aSizes
);
11075 size_t PresShell::SizeOfTextRuns(MallocSizeOf aMallocSizeOf
) const {
11076 nsIFrame
* rootFrame
= mFrameConstructor
->GetRootFrame();
11081 // clear the TEXT_RUN_MEMORY_ACCOUNTED flags
11082 nsLayoutUtils::SizeOfTextRunsForFrames(rootFrame
, nullptr,
11083 /* clear = */ true);
11085 // collect the total memory in use for textruns
11086 return nsLayoutUtils::SizeOfTextRunsForFrames(rootFrame
, aMallocSizeOf
,
11087 /* clear = */ false);
11090 void PresShell::MarkFixedFramesForReflow(IntrinsicDirty aIntrinsicDirty
) {
11091 nsIFrame
* rootFrame
= mFrameConstructor
->GetRootFrame();
11093 const nsFrameList
& childList
=
11094 rootFrame
->GetChildList(nsIFrame::kFixedList
);
11095 for (nsIFrame
* childFrame
: childList
) {
11096 FrameNeedsReflow(childFrame
, aIntrinsicDirty
, NS_FRAME_IS_DIRTY
);
11101 static void AppendSubtree(nsIDocShell
* aDocShell
,
11102 nsTArray
<nsCOMPtr
<nsIContentViewer
>>& aArray
) {
11103 if (nsCOMPtr
<nsIContentViewer
> cv
= aDocShell
->GetContentViewer()) {
11104 aArray
.AppendElement(cv
);
11107 int32_t n
= aDocShell
->GetInProcessChildCount();
11108 for (int32_t i
= 0; i
< n
; i
++) {
11109 nsCOMPtr
<nsIDocShellTreeItem
> childItem
;
11110 aDocShell
->GetInProcessChildAt(i
, getter_AddRefs(childItem
));
11112 nsCOMPtr
<nsIDocShell
> child(do_QueryInterface(childItem
));
11113 AppendSubtree(child
, aArray
);
11118 void PresShell::MaybeReflowForInflationScreenSizeChange() {
11119 nsPresContext
* pc
= GetPresContext();
11120 const bool fontInflationWasEnabled
= FontSizeInflationEnabled();
11121 RecomputeFontSizeInflationEnabled();
11122 bool changed
= false;
11123 if (FontSizeInflationEnabled() && FontSizeInflationMinTwips() != 0) {
11124 pc
->ScreenSizeInchesForFontInflation(&changed
);
11127 changed
= changed
|| fontInflationWasEnabled
!= FontSizeInflationEnabled();
11131 if (nsCOMPtr
<nsIDocShell
> docShell
= pc
->GetDocShell()) {
11132 nsTArray
<nsCOMPtr
<nsIContentViewer
>> array
;
11133 AppendSubtree(docShell
, array
);
11134 for (uint32_t i
= 0, iEnd
= array
.Length(); i
< iEnd
; ++i
) {
11135 nsCOMPtr
<nsIContentViewer
> cv
= array
[i
];
11136 if (RefPtr
<PresShell
> descendantPresShell
= cv
->GetPresShell()) {
11137 nsIFrame
* rootFrame
= descendantPresShell
->GetRootFrame();
11139 descendantPresShell
->FrameNeedsReflow(
11140 rootFrame
, IntrinsicDirty::StyleChange
, NS_FRAME_IS_DIRTY
);
11147 void PresShell::CompleteChangeToVisualViewportSize() {
11148 // This can get called during reflow, if the caller wants to get the latest
11149 // visual viewport size after scrollbars have been added/removed. In such a
11150 // case, we don't need to mark things as dirty because the things that we
11151 // would mark dirty either just got updated (the root scrollframe's
11152 // scrollbars), or will be laid out later during this reflow cycle (fixed-pos
11153 // items). Callers that update the visual viewport during a reflow are
11154 // responsible for maintaining these invariants.
11155 if (!mIsReflowing
) {
11156 if (nsIScrollableFrame
* rootScrollFrame
=
11157 GetRootScrollFrameAsScrollable()) {
11158 rootScrollFrame
->MarkScrollbarsDirtyForReflow();
11160 MarkFixedFramesForReflow(IntrinsicDirty::Resize
);
11163 MaybeReflowForInflationScreenSizeChange();
11165 if (auto* window
= nsGlobalWindowInner::Cast(mDocument
->GetInnerWindow())) {
11166 window
->VisualViewport()->PostResizeEvent();
11170 void PresShell::SetVisualViewportSize(nscoord aWidth
, nscoord aHeight
) {
11171 MOZ_ASSERT(aWidth
>= 0.0 && aHeight
>= 0.0);
11173 if (!mVisualViewportSizeSet
|| mVisualViewportSize
.width
!= aWidth
||
11174 mVisualViewportSize
.height
!= aHeight
) {
11175 mVisualViewportSizeSet
= true;
11176 mVisualViewportSize
.width
= aWidth
;
11177 mVisualViewportSize
.height
= aHeight
;
11179 CompleteChangeToVisualViewportSize();
11183 void PresShell::ResetVisualViewportSize() {
11184 if (mVisualViewportSizeSet
) {
11185 mVisualViewportSizeSet
= false;
11186 mVisualViewportSize
.width
= 0;
11187 mVisualViewportSize
.height
= 0;
11189 CompleteChangeToVisualViewportSize();
11193 bool PresShell::SetVisualViewportOffset(const nsPoint
& aScrollOffset
,
11194 const nsPoint
& aPrevLayoutScrollPos
) {
11195 nsPoint newOffset
= aScrollOffset
;
11196 nsIScrollableFrame
* rootScrollFrame
= GetRootScrollFrameAsScrollable();
11197 if (rootScrollFrame
) {
11198 // See the comment in nsHTMLScrollFrame::Reflow above the call to
11199 // SetVisualViewportOffset for why we need to do this.
11200 nsRect scrollRange
= rootScrollFrame
->GetScrollRangeForUserInputEvents();
11201 if (!scrollRange
.Contains(newOffset
)) {
11202 newOffset
.x
= std::min(newOffset
.x
, scrollRange
.XMost());
11203 newOffset
.x
= std::max(newOffset
.x
, scrollRange
.x
);
11204 newOffset
.y
= std::min(newOffset
.y
, scrollRange
.YMost());
11205 newOffset
.y
= std::max(newOffset
.y
, scrollRange
.y
);
11209 nsPoint prevOffset
= GetVisualViewportOffset();
11210 if (prevOffset
== newOffset
) {
11214 mVisualViewportOffset
= Some(newOffset
);
11216 if (auto* window
= nsGlobalWindowInner::Cast(mDocument
->GetInnerWindow())) {
11217 window
->VisualViewport()->PostScrollEvent(prevOffset
, aPrevLayoutScrollPos
);
11220 if (IsVisualViewportSizeSet() && rootScrollFrame
) {
11221 rootScrollFrame
->Anchor()->UserScrolled();
11224 if (gfxPlatform::UseDesktopZoomingScrollbars()) {
11225 if (nsIScrollableFrame
* rootScrollFrame
=
11226 GetRootScrollFrameAsScrollable()) {
11227 rootScrollFrame
->UpdateScrollbarPosition();
11234 void PresShell::ScrollToVisual(const nsPoint
& aVisualViewportOffset
,
11235 FrameMetrics::ScrollOffsetUpdateType aUpdateType
,
11236 ScrollMode aMode
) {
11237 MOZ_ASSERT(aMode
== ScrollMode::Instant
|| aMode
== ScrollMode::SmoothMsd
);
11239 if (aMode
== ScrollMode::SmoothMsd
) {
11240 if (nsIScrollableFrame
* sf
= GetRootScrollFrameAsScrollable()) {
11241 if (sf
->SmoothScrollVisual(aVisualViewportOffset
, aUpdateType
)) {
11247 // If the caller asked for instant scroll, or if we failed
11248 // to do a smooth scroll, do an instant scroll.
11249 SetPendingVisualScrollUpdate(aVisualViewportOffset
, aUpdateType
);
11252 void PresShell::SetPendingVisualScrollUpdate(
11253 const nsPoint
& aVisualViewportOffset
,
11254 FrameMetrics::ScrollOffsetUpdateType aUpdateType
) {
11255 mPendingVisualScrollUpdate
=
11256 Some(VisualScrollUpdate
{aVisualViewportOffset
, aUpdateType
});
11258 // The pending update is picked up during the next paint.
11259 // Schedule a paint to make sure one will happen.
11260 if (nsIFrame
* rootFrame
= GetRootFrame()) {
11261 rootFrame
->SchedulePaint();
11265 void PresShell::ClearPendingVisualScrollUpdate() {
11266 if (mPendingVisualScrollUpdate
&& mPendingVisualScrollUpdate
->mAcknowledged
) {
11267 mPendingVisualScrollUpdate
= mozilla::Nothing();
11271 void PresShell::AcknowledgePendingVisualScrollUpdate() {
11272 MOZ_ASSERT(mPendingVisualScrollUpdate
);
11273 mPendingVisualScrollUpdate
->mAcknowledged
= true;
11276 nsPoint
PresShell::GetVisualViewportOffsetRelativeToLayoutViewport() const {
11277 return GetVisualViewportOffset() - GetLayoutViewportOffset();
11280 nsPoint
PresShell::GetLayoutViewportOffset() const {
11282 if (nsIScrollableFrame
* sf
= GetRootScrollFrameAsScrollable()) {
11283 result
= sf
->GetScrollPosition();
11288 nsSize
PresShell::GetLayoutViewportSize() const {
11290 if (nsIScrollableFrame
* sf
= GetRootScrollFrameAsScrollable()) {
11291 result
= sf
->GetScrollPortRect().Size();
11296 nsSize
PresShell::GetVisualViewportSizeUpdatedByDynamicToolbar() const {
11297 NS_ASSERTION(mVisualViewportSizeSet
,
11298 "asking for visual viewport size when its not set?");
11299 if (!mMobileViewportManager
) {
11300 return mVisualViewportSize
;
11303 MOZ_ASSERT(GetDynamicToolbarState() == DynamicToolbarState::InTransition
||
11304 GetDynamicToolbarState() == DynamicToolbarState::Collapsed
);
11306 nsSize sizeUpdatedByDynamicToolbar
=
11307 mMobileViewportManager
->GetVisualViewportSizeUpdatedByDynamicToolbar();
11308 return sizeUpdatedByDynamicToolbar
== nsSize() ? mVisualViewportSize
11309 : sizeUpdatedByDynamicToolbar
;
11312 void PresShell::RecomputeFontSizeInflationEnabled() {
11313 mFontSizeInflationEnabled
= DetermineFontSizeInflationState();
11315 // Divide by 100 to convert the pref from a percentage to a fraction.
11316 float fontScale
= StaticPrefs::font_size_systemFontScale() / 100.0f
;
11317 if (fontScale
== 0.0f
) {
11321 MOZ_ASSERT(mDocument
);
11322 MOZ_ASSERT(mPresContext
);
11323 if (mFontSizeInflationEnabled
|| mDocument
->IsSyntheticDocument()) {
11324 mPresContext
->SetSystemFontScale(1.0f
);
11326 mPresContext
->SetSystemFontScale(fontScale
);
11330 bool PresShell::DetermineFontSizeInflationState() {
11331 MOZ_ASSERT(mPresContext
, "our pres context should not be null");
11332 if (mPresContext
->IsChrome()) {
11336 if (FontSizeInflationEmPerLine() == 0 && FontSizeInflationMinTwips() == 0) {
11340 // Force-enabling font inflation always trumps the heuristics here.
11341 if (!FontSizeInflationForceEnabled()) {
11342 if (BrowserChild
* tab
= BrowserChild::GetFrom(this)) {
11343 // We're in a child process. Cancel inflation if we're not
11344 // async-pan zoomed.
11345 if (!tab
->AsyncPanZoomEnabled()) {
11348 } else if (XRE_IsParentProcess()) {
11349 // We're in the master process. Cancel inflation if it's been
11350 // explicitly disabled.
11351 if (FontSizeInflationDisabledInMasterProcess()) {
11358 // See bug 706918, comment 23 for more information on this particular section
11359 // of the code. We're using "screen size" in place of the size of the content
11360 // area, because on mobile, these are close or equal. This will work for our
11361 // purposes (bug 706198), but it will need to be changed in the future to be
11362 // more correct when we bring the rest of the viewport code into platform.
11363 // We actually want the size of the content area, in the event that we don't
11364 // have any metadata about the width and/or height. On mobile, the screen size
11365 // and the size of the content area are very close, or the same value.
11366 // In XUL fennec, the content area is the size of the <browser> widget, but
11367 // in native fennec, the content area is the size of the Gecko LayerView
11371 // Once bug 716575 has been resolved, this code should be changed so that it
11372 // does the right thing on all platforms.
11374 nsCOMPtr
<nsIScreenManager
> screenMgr
=
11375 do_GetService("@mozilla.org/gfx/screenmanager;1", &rv
);
11376 if (!NS_SUCCEEDED(rv
)) {
11380 nsCOMPtr
<nsIScreen
> screen
;
11381 screenMgr
->GetPrimaryScreen(getter_AddRefs(screen
));
11383 int32_t screenLeft
, screenTop
, screenWidth
, screenHeight
;
11384 screen
->GetRect(&screenLeft
, &screenTop
, &screenWidth
, &screenHeight
);
11386 nsViewportInfo vInf
= GetDocument()->GetViewportInfo(
11387 ScreenIntSize(screenWidth
, screenHeight
));
11389 if (vInf
.GetDefaultZoom() >= CSSToScreenScale(1.0f
) ||
11390 vInf
.IsAutoSizeEnabled()) {
11398 void PresShell::SyncWindowProperties(nsView
* aView
) {
11399 nsIFrame
* frame
= aView
->GetFrame();
11400 if (frame
&& mPresContext
) {
11401 // CreateReferenceRenderingContext can return nullptr
11402 RefPtr
<gfxContext
> rcx(CreateReferenceRenderingContext());
11403 nsContainerFrame::SyncWindowProperties(mPresContext
, frame
, aView
, rcx
, 0);
11407 nsresult
PresShell::HasRuleProcessorUsedByMultipleStyleSets(uint32_t aSheetType
,
11413 void PresShell::NotifyStyleSheetServiceSheetAdded(StyleSheet
* aSheet
,
11414 uint32_t aSheetType
) {
11415 switch (aSheetType
) {
11416 case nsIStyleSheetService::AGENT_SHEET
:
11417 AddAgentSheet(aSheet
);
11419 case nsIStyleSheetService::USER_SHEET
:
11420 AddUserSheet(aSheet
);
11422 case nsIStyleSheetService::AUTHOR_SHEET
:
11423 AddAuthorSheet(aSheet
);
11426 MOZ_ASSERT_UNREACHABLE("unexpected aSheetType value");
11431 void PresShell::NotifyStyleSheetServiceSheetRemoved(StyleSheet
* aSheet
,
11432 uint32_t aSheetType
) {
11433 StyleSet()->RemoveStyleSheet(*aSheet
);
11434 mDocument
->ApplicableStylesChanged();
11437 void PresShell::SetIsUnderHiddenEmbedderElement(
11438 bool aUnderHiddenEmbedderElement
) {
11439 if (mUnderHiddenEmbedderElement
== aUnderHiddenEmbedderElement
) {
11443 mUnderHiddenEmbedderElement
= aUnderHiddenEmbedderElement
;
11445 if (nsCOMPtr
<nsIDocShell
> docShell
= mPresContext
->GetDocShell()) {
11446 BrowsingContext
* bc
= docShell
->GetBrowsingContext();
11448 // Propagate to children.
11449 for (BrowsingContext
* child
: bc
->Children()) {
11450 Element
* embedderElement
= child
->GetEmbedderElement();
11451 if (!embedderElement
) {
11452 // TODO: We shouldn't need to null check here since `child` and the
11453 // element returned by `child->GetEmbedderElement()` are in our
11454 // process (the actual browsing context represented by `child` may not
11455 // be, but that doesn't matter). However, there are currently a very
11456 // small number of crashes due to `embedderElement` being null, somehow
11457 // - see bug 1551241. For now we wallpaper the crash.
11461 bool embedderFrameIsHidden
= true;
11462 if (auto embedderFrame
= embedderElement
->GetPrimaryFrame()) {
11463 embedderFrameIsHidden
= !embedderFrame
->StyleVisibility()->IsVisible();
11466 if (nsIDocShell
* childDocShell
= child
->GetDocShell()) {
11467 PresShell
* presShell
= childDocShell
->GetPresShell();
11471 presShell
->SetIsUnderHiddenEmbedderElement(
11472 aUnderHiddenEmbedderElement
|| embedderFrameIsHidden
);
11474 BrowserBridgeChild
* bridgeChild
=
11475 BrowserBridgeChild::GetFrom(embedderElement
);
11476 bridgeChild
->SetIsUnderHiddenEmbedderElement(
11477 aUnderHiddenEmbedderElement
|| embedderFrameIsHidden
);
11483 nsIContent
* PresShell::EventHandler::GetOverrideClickTarget(
11484 WidgetGUIEvent
* aGUIEvent
, nsIFrame
* aFrame
) {
11485 if (aGUIEvent
->mMessage
!= eMouseUp
) {
11489 MOZ_ASSERT(aGUIEvent
->mClass
== eMouseEventClass
);
11490 WidgetMouseEvent
* mouseEvent
= aGUIEvent
->AsMouseEvent();
11492 uint32_t flags
= 0;
11493 RelativeTo relativeTo
{aFrame
};
11494 nsPoint eventPoint
=
11495 nsLayoutUtils::GetEventCoordinatesRelativeTo(aGUIEvent
, relativeTo
);
11496 if (mouseEvent
->mIgnoreRootScrollFrame
) {
11497 flags
|= INPUT_IGNORE_ROOT_SCROLL_FRAME
;
11501 FindFrameTargetedByInputEvent(aGUIEvent
, relativeTo
, eventPoint
, flags
);
11506 nsIContent
* overrideClickTarget
= target
->GetContent();
11507 while (overrideClickTarget
&& !overrideClickTarget
->IsElement()) {
11508 overrideClickTarget
= overrideClickTarget
->GetFlattenedTreeParent();
11510 return overrideClickTarget
;
11513 /******************************************************************************
11514 * PresShell::EventHandler::EventTargetData
11515 ******************************************************************************/
11517 void PresShell::EventHandler::EventTargetData::SetFrameAndComputePresShell(
11518 nsIFrame
* aFrameToHandleEvent
) {
11519 if (aFrameToHandleEvent
) {
11520 mFrame
= aFrameToHandleEvent
;
11521 mPresShell
= aFrameToHandleEvent
->PresShell();
11524 mPresShell
= nullptr;
11528 void PresShell::EventHandler::EventTargetData::
11529 SetFrameAndComputePresShellAndContent(nsIFrame
* aFrameToHandleEvent
,
11530 WidgetGUIEvent
* aGUIEvent
) {
11531 MOZ_ASSERT(aFrameToHandleEvent
);
11532 MOZ_ASSERT(aGUIEvent
);
11534 SetFrameAndComputePresShell(aFrameToHandleEvent
);
11535 SetContentForEventFromFrame(aGUIEvent
);
11538 void PresShell::EventHandler::EventTargetData::SetContentForEventFromFrame(
11539 WidgetGUIEvent
* aGUIEvent
) {
11540 MOZ_ASSERT(mFrame
);
11541 mContent
= nullptr;
11542 mFrame
->GetContentForEvent(aGUIEvent
, getter_AddRefs(mContent
));
11545 nsIContent
* PresShell::EventHandler::EventTargetData::GetFrameContent() const {
11546 return mFrame
? mFrame
->GetContent() : nullptr;
11549 bool PresShell::EventHandler::EventTargetData::MaybeRetargetToActiveDocument(
11550 WidgetGUIEvent
* aGUIEvent
) {
11551 MOZ_ASSERT(aGUIEvent
);
11552 MOZ_ASSERT(mFrame
);
11553 MOZ_ASSERT(mPresShell
);
11554 MOZ_ASSERT(!mContent
, "Doesn't support to retarget the content");
11556 EventStateManager
* activeESM
=
11557 EventStateManager::GetActiveEventStateManager();
11562 if (aGUIEvent
->mClass
!= ePointerEventClass
&&
11563 !aGUIEvent
->HasMouseEventMessage()) {
11567 if (activeESM
== GetEventStateManager()) {
11571 nsPresContext
* activePresContext
= activeESM
->GetPresContext();
11572 if (!activePresContext
) {
11576 PresShell
* activePresShell
= activePresContext
->GetPresShell();
11577 if (!activePresShell
) {
11581 // Note, currently for backwards compatibility we don't forward mouse events
11582 // to the active document when mouse is over some subdocument.
11583 if (!nsContentUtils::ContentIsCrossDocDescendantOf(
11584 activePresShell
->GetDocument(), GetDocument())) {
11588 SetFrameAndComputePresShell(activePresShell
->GetRootFrame());
11592 bool PresShell::EventHandler::EventTargetData::ComputeElementFromFrame(
11593 WidgetGUIEvent
* aGUIEvent
) {
11594 MOZ_ASSERT(aGUIEvent
);
11595 MOZ_ASSERT(aGUIEvent
->IsUsingCoordinates());
11596 MOZ_ASSERT(mPresShell
);
11597 MOZ_ASSERT(mFrame
);
11599 SetContentForEventFromFrame(aGUIEvent
);
11601 // If there is no content for this frame, target it anyway. Some frames can
11602 // be targeted but do not have content, particularly windows with scrolling
11608 // Bug 103055, bug 185889: mouse events apply to *elements*, not all nodes.
11609 // Thus we get the nearest element parent here.
11610 // XXX we leave the frame the same even if we find an element parent, so that
11611 // the text frame will receive the event (selection and friends are the ones
11612 // who care about that anyway)
11614 // We use weak pointers because during this tight loop, the node
11615 // will *not* go away. And this happens on every mousemove.
11616 nsIContent
* content
= mContent
;
11617 while (content
&& !content
->IsElement()) {
11618 content
= content
->GetFlattenedTreeParent();
11620 mContent
= content
;
11622 // If we found an element, target it. Otherwise, target *nothing*.
11626 void PresShell::EventHandler::EventTargetData::UpdateTouchEventTarget(
11627 WidgetGUIEvent
* aGUIEvent
) {
11628 MOZ_ASSERT(aGUIEvent
);
11630 if (aGUIEvent
->mClass
!= eTouchEventClass
) {
11634 if (aGUIEvent
->mMessage
== eTouchStart
) {
11635 WidgetTouchEvent
* touchEvent
= aGUIEvent
->AsTouchEvent();
11636 nsIFrame
* newFrame
=
11637 TouchManager::SuppressInvalidPointsAndGetTargetedFrame(touchEvent
);
11639 return; // XXX Why don't we stop handling the event in this case?
11641 SetFrameAndComputePresShellAndContent(newFrame
, aGUIEvent
);
11645 PresShell
* newPresShell
= PresShell::GetShellForTouchEvent(aGUIEvent
);
11646 if (!newPresShell
) {
11647 return; // XXX Why don't we stop handling the event in this case?
11650 // Touch events (except touchstart) are dispatching to the captured
11651 // element. Get correct shell from it.
11652 mPresShell
= newPresShell
;
11655 /******************************************************************************
11656 * PresShell::EventHandler::HandlingTimeAccumulator
11657 ******************************************************************************/
11659 PresShell::EventHandler::HandlingTimeAccumulator::HandlingTimeAccumulator(
11660 const PresShell::EventHandler
& aEventHandler
, const WidgetEvent
* aEvent
)
11661 : mEventHandler(aEventHandler
),
11663 mHandlingStartTime(TimeStamp::Now()) {
11664 MOZ_ASSERT(mEvent
);
11665 MOZ_ASSERT(mEvent
->IsTrusted());
11668 PresShell::EventHandler::HandlingTimeAccumulator::~HandlingTimeAccumulator() {
11669 if (mEvent
->mTimeStamp
<= mEventHandler
.mPresShell
->mLastOSWake
) {
11673 switch (mEvent
->mMessage
) {
11677 Telemetry::AccumulateTimeDelta(Telemetry::INPUT_EVENT_HANDLED_KEYBOARD_MS
,
11678 mHandlingStartTime
);
11681 Telemetry::AccumulateTimeDelta(
11682 Telemetry::INPUT_EVENT_HANDLED_MOUSE_DOWN_MS
, mHandlingStartTime
);
11685 Telemetry::AccumulateTimeDelta(Telemetry::INPUT_EVENT_HANDLED_MOUSE_UP_MS
,
11686 mHandlingStartTime
);
11689 if (mEvent
->mFlags
.mHandledByAPZ
) {
11690 Telemetry::AccumulateTimeDelta(
11691 Telemetry::INPUT_EVENT_HANDLED_APZ_MOUSE_MOVE_MS
,
11692 mHandlingStartTime
);
11696 if (mEvent
->mFlags
.mHandledByAPZ
) {
11697 Telemetry::AccumulateTimeDelta(
11698 Telemetry::INPUT_EVENT_HANDLED_APZ_WHEEL_MS
, mHandlingStartTime
);
11702 if (mEvent
->mFlags
.mHandledByAPZ
) {
11703 Telemetry::AccumulateTimeDelta(
11704 Telemetry::INPUT_EVENT_HANDLED_APZ_TOUCH_MOVE_MS
,
11705 mHandlingStartTime
);
11713 void PresShell::EndPaint() {
11714 ClearPendingVisualScrollUpdate();
11717 mDocument
->EnumerateSubDocuments([](Document
& aSubDoc
) {
11718 if (PresShell
* presShell
= aSubDoc
.GetPresShell()) {
11719 presShell
->EndPaint();
11721 return CallState::Continue
;
11726 void PresShell::PingPerTickTelemetry(FlushType aFlushType
) {
11727 mLayoutTelemetry
.PingPerTickTelemetry(aFlushType
);
11730 bool PresShell::GetZoomableByAPZ() const {
11731 return mZoomConstraintsClient
&& mZoomConstraintsClient
->GetAllowZoom();