Bug 1584173 [wpt PR 19325] - Update interfaces/fullscreen.idl, a=testonly
[gecko.git] / layout / base / PresShell.cpp
blob83e890fc9fdfc297212c3e95dc012ffa2b187a95
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"
11 #include "mozilla/dom/FontFaceSet.h"
12 #include "mozilla/ArrayUtils.h"
13 #include "mozilla/Attributes.h"
14 #include "mozilla/AutoRestore.h"
15 #include "mozilla/ContentIterator.h"
16 #include "mozilla/EventDispatcher.h"
17 #include "mozilla/EventStateManager.h"
18 #include "mozilla/EventStates.h"
19 #include "mozilla/GeckoMVMContext.h"
20 #include "mozilla/IMEStateManager.h"
21 #include "mozilla/MemoryReporting.h"
22 #include "mozilla/dom/BrowserChild.h"
23 #include "mozilla/Likely.h"
24 #include "mozilla/Logging.h"
25 #include "mozilla/MouseEvents.h"
26 #include "mozilla/PerfStats.h"
27 #include "mozilla/PresShellInlines.h"
28 #include "mozilla/RangeUtils.h"
29 #include "mozilla/Sprintf.h"
30 #include "mozilla/StaticPrefs_apz.h"
31 #include "mozilla/StaticPrefs_dom.h"
32 #include "mozilla/StaticPrefs_font.h"
33 #include "mozilla/StaticPrefs_layout.h"
34 #include "mozilla/TextEvents.h"
35 #include "mozilla/TimeStamp.h"
36 #include "mozilla/TouchEvents.h"
37 #include "mozilla/UniquePtr.h"
38 #include "mozilla/Unused.h"
39 #include <algorithm>
41 #ifdef XP_WIN
42 # include "winuser.h"
43 #endif
45 #include "gfxContext.h"
46 #include "gfxUserFontSet.h"
47 #include "nsContentList.h"
48 #include "nsPresContext.h"
49 #include "nsIContent.h"
50 #include "mozilla/dom/BrowserBridgeChild.h"
51 #include "mozilla/dom/BrowsingContext.h"
52 #include "mozilla/dom/Element.h"
53 #include "mozilla/dom/PointerEventHandler.h"
54 #include "mozilla/dom/PopupBlocker.h"
55 #include "mozilla/dom/Document.h"
56 #include "mozilla/dom/DocumentInlines.h"
57 #include "mozilla/dom/UserActivation.h"
58 #include "nsAnimationManager.h"
59 #include "nsNameSpaceManager.h" // for Pref-related rule management (bugs 22963,20760,31816)
60 #include "nsFrame.h"
61 #include "FrameLayerBuilder.h"
62 #include "nsViewManager.h"
63 #include "nsView.h"
64 #include "nsCRTGlue.h"
65 #include "prinrval.h"
66 #include "nsTArray.h"
67 #include "nsCOMArray.h"
68 #include "nsContainerFrame.h"
69 #include "mozilla/dom/Selection.h"
70 #include "nsGkAtoms.h"
71 #include "nsRange.h"
72 #include "nsWindowSizes.h"
73 #include "nsCOMPtr.h"
74 #include "nsReadableUtils.h"
75 #include "nsPageSequenceFrame.h"
76 #include "nsIPermissionManager.h"
77 #include "nsIMozBrowserFrame.h"
78 #include "nsCaret.h"
79 #include "mozilla/AccessibleCaretEventHub.h"
80 #include "nsFrameManager.h"
81 #include "nsXPCOM.h"
82 #include "nsILayoutHistoryState.h"
83 #include "nsILineIterator.h" // for ScrollContentIntoView
84 #include "PLDHashTable.h"
85 #include "mozilla/dom/Touch.h"
86 #include "mozilla/dom/TouchEvent.h"
87 #include "mozilla/dom/PointerEventBinding.h"
88 #include "mozilla/dom/ShadowIncludingTreeIterator.h"
89 #include "nsIObserverService.h"
90 #include "nsDocShell.h" // for reflow observation
91 #include "nsIBaseWindow.h"
92 #include "nsError.h"
93 #include "nsLayoutUtils.h"
94 #include "nsViewportInfo.h"
95 #include "nsCSSRendering.h"
96 // for |#ifdef DEBUG| code
97 #include "prenv.h"
98 #include "nsDisplayList.h"
99 #include "nsRegion.h"
100 #include "nsAutoLayoutPhase.h"
101 #ifdef MOZ_GECKO_PROFILER
102 # include "AutoProfilerStyleMarker.h"
103 #endif
104 #ifdef MOZ_REFLOW_PERF
105 # include "nsFontMetrics.h"
106 #endif
107 #include "MobileViewportManager.h"
108 #include "OverflowChangedTracker.h"
109 #include "PositionedEventTargeting.h"
111 #include "nsIReflowCallback.h"
113 #include "nsPIDOMWindow.h"
114 #include "nsFocusManager.h"
115 #include "nsIObjectFrame.h"
116 #include "nsIObjectLoadingContent.h"
117 #include "nsNetUtil.h"
118 #include "nsThreadUtils.h"
119 #include "nsStyleSheetService.h"
120 #include "gfxUtils.h"
121 #include "mozilla/SMILAnimationController.h"
122 #include "SVGContentUtils.h"
123 #include "SVGObserverUtils.h"
124 #include "SVGFragmentIdentifier.h"
125 #include "nsFrameSelection.h"
127 #include "mozilla/dom/Performance.h"
128 #include "nsRefreshDriver.h"
129 #include "nsDOMNavigationTiming.h"
131 // Drag & Drop, Clipboard
132 #include "nsIDocShellTreeItem.h"
133 #include "nsIURI.h"
134 #include "nsIScrollableFrame.h"
135 #include "nsITimer.h"
136 #ifdef ACCESSIBILITY
137 # include "nsAccessibilityService.h"
138 # include "mozilla/a11y/DocAccessible.h"
139 # ifdef DEBUG
140 # include "mozilla/a11y/Logging.h"
141 # endif
142 #endif
144 // For style data reconstruction
145 #include "nsStyleChangeList.h"
146 #include "nsCSSFrameConstructor.h"
147 #ifdef MOZ_XUL
148 # include "nsMenuFrame.h"
149 # include "nsTreeBodyFrame.h"
150 # include "XULTreeElement.h"
151 # include "nsMenuPopupFrame.h"
152 # include "nsTreeColumns.h"
153 # include "nsIDOMXULMultSelectCntrlEl.h"
154 # include "nsIDOMXULSelectCntrlItemEl.h"
155 # include "nsIDOMXULMenuListElement.h"
156 # include "nsXULElement.h"
157 #endif // MOZ_XUL
159 #include "mozilla/layers/CompositorBridgeChild.h"
160 #include "ClientLayerManager.h"
161 #include "GeckoProfiler.h"
162 #include "gfxPlatform.h"
163 #include "Layers.h"
164 #include "LayerTreeInvalidation.h"
165 #include "mozilla/css/ImageLoader.h"
166 #include "mozilla/dom/DocumentTimeline.h"
167 #include "mozilla/dom/ScriptSettings.h"
168 #include "mozilla/ErrorResult.h"
169 #include "mozilla/Preferences.h"
170 #include "mozilla/Telemetry.h"
171 #include "nsCanvasFrame.h"
172 #include "nsIImageLoadingContent.h"
173 #include "nsImageFrame.h"
174 #include "nsIScreen.h"
175 #include "nsIScreenManager.h"
176 #include "nsPlaceholderFrame.h"
177 #include "nsTransitionManager.h"
178 #include "ChildIterator.h"
179 #include "mozilla/RestyleManager.h"
180 #include "nsIDragSession.h"
181 #include "nsIFrameInlines.h"
182 #include "mozilla/gfx/2D.h"
183 #include "nsSubDocumentFrame.h"
184 #include "nsQueryObject.h"
185 #include "nsLayoutStylesheetCache.h"
186 #include "mozilla/layers/InputAPZContext.h"
187 #include "mozilla/layers/FocusTarget.h"
188 #include "mozilla/layers/WebRenderLayerManager.h"
189 #include "mozilla/layers/WebRenderUserData.h"
190 #include "mozilla/layout/ScrollAnchorContainer.h"
191 #include "mozilla/ServoBindings.h"
192 #include "mozilla/ServoStyleSet.h"
193 #include "mozilla/StyleSheet.h"
194 #include "mozilla/StyleSheetInlines.h"
195 #include "mozilla/dom/ImageTracker.h"
196 #include "nsIDocShellTreeOwner.h"
197 #include "nsBindingManager.h"
198 #include "nsClassHashtable.h"
199 #include "nsHashKeys.h"
200 #include "VisualViewport.h"
202 #ifdef MOZ_TASK_TRACER
203 # include "GeckoTaskTracer.h"
204 using namespace mozilla::tasktracer;
205 #endif
207 // define the scalfactor of drag and drop images
208 // relative to the max screen height/width
209 #define RELATIVE_SCALEFACTOR 0.0925f
211 using namespace mozilla;
212 using namespace mozilla::css;
213 using namespace mozilla::dom;
214 using namespace mozilla::gfx;
215 using namespace mozilla::layers;
216 using namespace mozilla::gfx;
217 using namespace mozilla::layout;
218 using PaintFrameFlags = nsLayoutUtils::PaintFrameFlags;
219 typedef ScrollableLayerGuid::ViewID ViewID;
221 PresShell::CapturingContentInfo PresShell::sCapturingContentInfo;
223 // RangePaintInfo is used to paint ranges to offscreen buffers
224 struct RangePaintInfo {
225 RefPtr<nsRange> mRange;
226 nsDisplayListBuilder mBuilder;
227 nsDisplayList mList;
229 // offset of builder's reference frame to the root frame
230 nsPoint mRootOffset;
232 RangePaintInfo(nsRange* aRange, nsIFrame* aFrame)
233 : mRange(aRange),
234 mBuilder(aFrame, nsDisplayListBuilderMode::Painting, false) {
235 MOZ_COUNT_CTOR(RangePaintInfo);
236 mBuilder.BeginFrame();
239 ~RangePaintInfo() {
240 mList.DeleteAll(&mBuilder);
241 mBuilder.EndFrame();
242 MOZ_COUNT_DTOR(RangePaintInfo);
246 #undef NOISY
248 // ----------------------------------------------------------------------
250 #ifdef DEBUG
251 // Set the environment variable GECKO_VERIFY_REFLOW_FLAGS to one or
252 // more of the following flags (comma separated) for handy debug
253 // output.
254 static VerifyReflowFlags gVerifyReflowFlags;
256 struct VerifyReflowFlagData {
257 const char* name;
258 VerifyReflowFlags bit;
261 static const VerifyReflowFlagData gFlags[] = {
262 // clang-format off
263 { "verify", VerifyReflowFlags::On },
264 { "reflow", VerifyReflowFlags::Noisy },
265 { "all", VerifyReflowFlags::All },
266 { "list-commands", VerifyReflowFlags::DumpCommands },
267 { "noisy-commands", VerifyReflowFlags::NoisyCommands },
268 { "really-noisy-commands", VerifyReflowFlags::ReallyNoisyCommands },
269 { "resize", VerifyReflowFlags::DuringResizeReflow },
270 // clang-format on
273 # define NUM_VERIFY_REFLOW_FLAGS (sizeof(gFlags) / sizeof(gFlags[0]))
275 static void ShowVerifyReflowFlags() {
276 printf("Here are the available GECKO_VERIFY_REFLOW_FLAGS:\n");
277 const VerifyReflowFlagData* flag = gFlags;
278 const VerifyReflowFlagData* limit = gFlags + NUM_VERIFY_REFLOW_FLAGS;
279 while (flag < limit) {
280 printf(" %s\n", flag->name);
281 ++flag;
283 printf("Note: GECKO_VERIFY_REFLOW_FLAGS is a comma separated list of flag\n");
284 printf("names (no whitespace)\n");
286 #endif
288 //========================================================================
289 //========================================================================
290 //========================================================================
291 #ifdef MOZ_REFLOW_PERF
292 class ReflowCountMgr;
294 static const char kGrandTotalsStr[] = "Grand Totals";
296 // Counting Class
297 class ReflowCounter {
298 public:
299 explicit ReflowCounter(ReflowCountMgr* aMgr = nullptr);
300 ~ReflowCounter();
302 void ClearTotals();
303 void DisplayTotals(const char* aStr);
304 void DisplayDiffTotals(const char* aStr);
305 void DisplayHTMLTotals(const char* aStr);
307 void Add() { mTotal++; }
308 void Add(uint32_t aTotal) { mTotal += aTotal; }
310 void CalcDiffInTotals();
311 void SetTotalsCache();
313 void SetMgr(ReflowCountMgr* aMgr) { mMgr = aMgr; }
315 uint32_t GetTotal() { return mTotal; }
317 protected:
318 void DisplayTotals(uint32_t aTotal, const char* aTitle);
319 void DisplayHTMLTotals(uint32_t aTotal, const char* aTitle);
321 uint32_t mTotal;
322 uint32_t mCacheTotal;
324 ReflowCountMgr* mMgr; // weak reference (don't delete)
327 // Counting Class
328 class IndiReflowCounter {
329 public:
330 explicit IndiReflowCounter(ReflowCountMgr* aMgr = nullptr)
331 : mFrame(nullptr),
332 mCount(0),
333 mMgr(aMgr),
334 mCounter(aMgr),
335 mHasBeenOutput(false) {}
336 virtual ~IndiReflowCounter() {}
338 nsAutoString mName;
339 nsIFrame* mFrame; // weak reference (don't delete)
340 int32_t mCount;
342 ReflowCountMgr* mMgr; // weak reference (don't delete)
344 ReflowCounter mCounter;
345 bool mHasBeenOutput;
348 //--------------------
349 // Manager Class
350 //--------------------
351 class ReflowCountMgr {
352 public:
353 ReflowCountMgr();
354 virtual ~ReflowCountMgr();
356 void ClearTotals();
357 void ClearGrandTotals();
358 void DisplayTotals(const char* aStr);
359 void DisplayHTMLTotals(const char* aStr);
360 void DisplayDiffsInTotals();
362 void Add(const char* aName, nsIFrame* aFrame);
363 ReflowCounter* LookUp(const char* aName);
365 void PaintCount(const char* aName, gfxContext* aRenderingContext,
366 nsPresContext* aPresContext, nsIFrame* aFrame,
367 const nsPoint& aOffset, uint32_t aColor);
369 FILE* GetOutFile() { return mFD; }
371 void SetPresContext(nsPresContext* aPresContext) {
372 mPresContext = aPresContext; // weak reference
374 void SetPresShell(PresShell* aPresShell) {
375 mPresShell = aPresShell; // weak reference
378 void SetDumpFrameCounts(bool aVal) { mDumpFrameCounts = aVal; }
379 void SetDumpFrameByFrameCounts(bool aVal) { mDumpFrameByFrameCounts = aVal; }
380 void SetPaintFrameCounts(bool aVal) { mPaintFrameByFrameCounts = aVal; }
382 bool IsPaintingFrameCounts() { return mPaintFrameByFrameCounts; }
384 protected:
385 void DisplayTotals(uint32_t aTotal, uint32_t* aDupArray, char* aTitle);
386 void DisplayHTMLTotals(uint32_t aTotal, uint32_t* aDupArray, char* aTitle);
388 void DoGrandTotals();
389 void DoIndiTotalsTree();
391 // HTML Output Methods
392 void DoGrandHTMLTotals();
394 nsClassHashtable<nsCharPtrHashKey, ReflowCounter> mCounts;
395 nsClassHashtable<nsCharPtrHashKey, IndiReflowCounter> mIndiFrameCounts;
396 FILE* mFD;
398 bool mDumpFrameCounts;
399 bool mDumpFrameByFrameCounts;
400 bool mPaintFrameByFrameCounts;
402 bool mCycledOnce;
404 // Root Frame for Individual Tracking
405 nsPresContext* mPresContext;
406 PresShell* mPresShell;
408 // ReflowCountMgr gReflowCountMgr;
410 #endif
411 //========================================================================
413 // comment out to hide caret
414 #define SHOW_CARET
416 // The upper bound on the amount of time to spend reflowing, in
417 // microseconds. When this bound is exceeded and reflow commands are
418 // still queued up, a reflow event is posted. The idea is for reflow
419 // to not hog the processor beyond the time specifed in
420 // gMaxRCProcessingTime. This data member is initialized from the
421 // layout.reflow.timeslice pref.
422 #define NS_MAX_REFLOW_TIME 1000000
423 static int32_t gMaxRCProcessingTime = -1;
425 struct nsCallbackEventRequest {
426 nsIReflowCallback* callback;
427 nsCallbackEventRequest* next;
430 // ----------------------------------------------------------------------------
432 // NOTE(emilio): It'd be nice for this to assert that our document isn't in the
433 // bfcache, but font pref changes don't care about that, and maybe / probably
434 // shouldn't.
435 #ifdef DEBUG
436 # define ASSERT_REFLOW_SCHEDULED_STATE() \
438 if (ObservingLayoutFlushes()) { \
439 MOZ_ASSERT( \
440 mDocument->GetBFCacheEntry() || \
441 mPresContext->RefreshDriver()->IsLayoutFlushObserver(this), \
442 "Unexpected state"); \
443 } else { \
444 MOZ_ASSERT( \
445 !mPresContext->RefreshDriver()->IsLayoutFlushObserver(this), \
446 "Unexpected state"); \
449 #else
450 # define ASSERT_REFLOW_SCHEDULED_STATE() /* nothing */
451 #endif
453 class nsAutoCauseReflowNotifier {
454 public:
455 explicit nsAutoCauseReflowNotifier(PresShell* aPresShell)
456 : mPresShell(aPresShell) {
457 mPresShell->WillCauseReflow();
459 ~nsAutoCauseReflowNotifier() {
460 // This check should not be needed. Currently the only place that seem
461 // to need it is the code that deals with bug 337586.
462 if (!mPresShell->mHaveShutDown) {
463 mPresShell->DidCauseReflow();
464 } else {
465 nsContentUtils::RemoveScriptBlocker();
469 PresShell* mPresShell;
472 class MOZ_STACK_CLASS nsPresShellEventCB : public EventDispatchingCallback {
473 public:
474 explicit nsPresShellEventCB(PresShell* aPresShell) : mPresShell(aPresShell) {}
476 MOZ_CAN_RUN_SCRIPT
477 virtual void HandleEvent(EventChainPostVisitor& aVisitor) override {
478 if (aVisitor.mPresContext && aVisitor.mEvent->mClass != eBasicEventClass) {
479 if (aVisitor.mEvent->mMessage == eMouseDown ||
480 aVisitor.mEvent->mMessage == eMouseUp) {
481 // Mouse-up and mouse-down events call nsFrame::HandlePress/Release
482 // which call GetContentOffsetsFromPoint which requires up-to-date
483 // layout. Bring layout up-to-date now so that GetCurrentEventFrame()
484 // below will return a real frame and we don't have to worry about
485 // destroying it by flushing later.
486 MOZ_KnownLive(mPresShell)->FlushPendingNotifications(FlushType::Layout);
487 } else if (aVisitor.mEvent->mMessage == eWheel &&
488 aVisitor.mEventStatus != nsEventStatus_eConsumeNoDefault) {
489 nsIFrame* frame = mPresShell->GetCurrentEventFrame();
490 if (frame) {
491 // chrome (including addons) should be able to know if content
492 // handles both D3E "wheel" event and legacy mouse scroll events.
493 // We should dispatch legacy mouse events before dispatching the
494 // "wheel" event into system group.
495 RefPtr<EventStateManager> esm =
496 aVisitor.mPresContext->EventStateManager();
497 esm->DispatchLegacyMouseScrollEvents(
498 frame, aVisitor.mEvent->AsWheelEvent(), &aVisitor.mEventStatus);
501 nsIFrame* frame = mPresShell->GetCurrentEventFrame();
502 if (!frame && (aVisitor.mEvent->mMessage == eMouseUp ||
503 aVisitor.mEvent->mMessage == eTouchEnd)) {
504 // Redirect BUTTON_UP and TOUCH_END events to the root frame to ensure
505 // that capturing is released.
506 frame = mPresShell->GetRootFrame();
508 if (frame) {
509 frame->HandleEvent(MOZ_KnownLive(aVisitor.mPresContext),
510 aVisitor.mEvent->AsGUIEvent(),
511 &aVisitor.mEventStatus);
516 RefPtr<PresShell> mPresShell;
519 class nsBeforeFirstPaintDispatcher : public Runnable {
520 public:
521 explicit nsBeforeFirstPaintDispatcher(Document* aDocument)
522 : mozilla::Runnable("nsBeforeFirstPaintDispatcher"),
523 mDocument(aDocument) {}
525 // Fires the "before-first-paint" event so that interested parties (right now,
526 // the mobile browser) are aware of it.
527 NS_IMETHOD Run() override {
528 nsCOMPtr<nsIObserverService> observerService =
529 mozilla::services::GetObserverService();
530 if (observerService) {
531 observerService->NotifyObservers(ToSupports(mDocument),
532 "before-first-paint", nullptr);
534 return NS_OK;
537 private:
538 RefPtr<Document> mDocument;
541 // This is a helper class to track whether the targeted frame is destroyed after
542 // dispatching pointer events. In that case, we need the original targeted
543 // content so that we can dispatch the mouse events to it.
544 class MOZ_STACK_CLASS AutoPointerEventTargetUpdater final {
545 public:
546 AutoPointerEventTargetUpdater(PresShell* aShell, WidgetEvent* aEvent,
547 nsIFrame* aFrame, nsIContent** aTargetContent) {
548 MOZ_ASSERT(aEvent);
549 if (!aTargetContent || aEvent->mClass != ePointerEventClass) {
550 // Make the destructor happy.
551 mTargetContent = nullptr;
552 return;
554 MOZ_ASSERT(aShell);
555 MOZ_ASSERT(aFrame);
556 MOZ_ASSERT(!aFrame->GetContent() ||
557 aShell->GetDocument() == aFrame->GetContent()->OwnerDoc());
559 MOZ_ASSERT(StaticPrefs::dom_w3c_pointer_events_enabled());
560 mShell = aShell;
561 mWeakFrame = aFrame;
562 mTargetContent = aTargetContent;
563 aShell->mPointerEventTarget = aFrame->GetContent();
566 ~AutoPointerEventTargetUpdater() {
567 if (!mTargetContent || !mShell || mWeakFrame.IsAlive()) {
568 return;
570 mShell->mPointerEventTarget.swap(*mTargetContent);
573 private:
574 RefPtr<PresShell> mShell;
575 AutoWeakFrame mWeakFrame;
576 nsIContent** mTargetContent;
579 void PresShell::DirtyRootsList::Add(nsIFrame* aFrame) {
580 // Is this root already scheduled for reflow?
581 // FIXME: This could possibly be changed to a uniqueness assertion, with some
582 // work in ResizeReflowIgnoreOverride (and maybe others?)
583 if (mList.Contains(aFrame)) {
584 // We don't expect frame to change depths.
585 MOZ_ASSERT(aFrame->GetDepthInFrameTree() ==
586 mList[mList.IndexOf(aFrame)].mDepth);
587 return;
590 mList.InsertElementSorted(
591 FrameAndDepth{aFrame, aFrame->GetDepthInFrameTree()},
592 FrameAndDepth::CompareByReverseDepth{});
595 void PresShell::DirtyRootsList::Remove(nsIFrame* aFrame) {
596 mList.RemoveElement(aFrame);
599 nsIFrame* PresShell::DirtyRootsList::PopShallowestRoot() {
600 // List is sorted in order of decreasing depth, so there are no deeper
601 // frames than the last one.
602 const FrameAndDepth& lastFAD = mList.LastElement();
603 nsIFrame* frame = lastFAD.mFrame;
604 // We don't expect frame to change depths.
605 MOZ_ASSERT(frame->GetDepthInFrameTree() == lastFAD.mDepth);
606 mList.RemoveLastElement();
607 return frame;
610 void PresShell::DirtyRootsList::Clear() { mList.Clear(); }
612 bool PresShell::DirtyRootsList::Contains(nsIFrame* aFrame) const {
613 return mList.Contains(aFrame);
616 bool PresShell::DirtyRootsList::IsEmpty() const { return mList.IsEmpty(); }
618 bool PresShell::DirtyRootsList::FrameIsAncestorOfDirtyRoot(
619 nsIFrame* aFrame) const {
620 MOZ_ASSERT(aFrame);
622 // Look for a path from any dirty roots to aFrame, following GetParent().
623 // This check mirrors what FrameNeedsReflow() would have done if the reflow
624 // root didn't get in the way.
625 for (nsIFrame* dirtyFrame : mList) {
626 do {
627 if (dirtyFrame == aFrame) {
628 return true;
630 dirtyFrame = dirtyFrame->GetParent();
631 } while (dirtyFrame);
634 return false;
637 bool PresShell::sDisableNonTestMouseEvents = false;
639 LazyLogModule PresShell::gLog("PresShell");
641 TimeStamp PresShell::EventHandler::sLastInputCreated;
642 TimeStamp PresShell::EventHandler::sLastInputProcessed;
643 StaticRefPtr<Element> PresShell::EventHandler::sLastKeyDownEventTargetElement;
645 bool PresShell::sProcessInteractable = false;
647 static bool gVerifyReflowEnabled;
649 bool PresShell::GetVerifyReflowEnable() {
650 #ifdef DEBUG
651 static bool firstTime = true;
652 if (firstTime) {
653 firstTime = false;
654 char* flags = PR_GetEnv("GECKO_VERIFY_REFLOW_FLAGS");
655 if (flags) {
656 bool error = false;
658 for (;;) {
659 char* comma = PL_strchr(flags, ',');
660 if (comma) *comma = '\0';
662 bool found = false;
663 const VerifyReflowFlagData* flag = gFlags;
664 const VerifyReflowFlagData* limit = gFlags + NUM_VERIFY_REFLOW_FLAGS;
665 while (flag < limit) {
666 if (PL_strcasecmp(flag->name, flags) == 0) {
667 gVerifyReflowFlags |= flag->bit;
668 found = true;
669 break;
671 ++flag;
674 if (!found) error = true;
676 if (!comma) break;
678 *comma = ',';
679 flags = comma + 1;
682 if (error) ShowVerifyReflowFlags();
685 if (VerifyReflowFlags::On & gVerifyReflowFlags) {
686 gVerifyReflowEnabled = true;
688 printf("Note: verifyreflow is enabled");
689 if (VerifyReflowFlags::Noisy & gVerifyReflowFlags) {
690 printf(" (noisy)");
692 if (VerifyReflowFlags::All & gVerifyReflowFlags) {
693 printf(" (all)");
695 if (VerifyReflowFlags::DumpCommands & gVerifyReflowFlags) {
696 printf(" (show reflow commands)");
698 if (VerifyReflowFlags::NoisyCommands & gVerifyReflowFlags) {
699 printf(" (noisy reflow commands)");
700 if (VerifyReflowFlags::ReallyNoisyCommands & gVerifyReflowFlags) {
701 printf(" (REALLY noisy reflow commands)");
704 printf("\n");
707 #endif
708 return gVerifyReflowEnabled;
711 void PresShell::SetVerifyReflowEnable(bool aEnabled) {
712 gVerifyReflowEnabled = aEnabled;
715 void PresShell::AddAutoWeakFrame(AutoWeakFrame* aWeakFrame) {
716 if (aWeakFrame->GetFrame()) {
717 aWeakFrame->GetFrame()->AddStateBits(NS_FRAME_EXTERNAL_REFERENCE);
719 aWeakFrame->SetPreviousWeakFrame(mAutoWeakFrames);
720 mAutoWeakFrames = aWeakFrame;
723 void PresShell::AddWeakFrame(WeakFrame* aWeakFrame) {
724 if (aWeakFrame->GetFrame()) {
725 aWeakFrame->GetFrame()->AddStateBits(NS_FRAME_EXTERNAL_REFERENCE);
727 MOZ_ASSERT(!mWeakFrames.GetEntry(aWeakFrame));
728 mWeakFrames.PutEntry(aWeakFrame);
731 void PresShell::RemoveAutoWeakFrame(AutoWeakFrame* aWeakFrame) {
732 if (mAutoWeakFrames == aWeakFrame) {
733 mAutoWeakFrames = aWeakFrame->GetPreviousWeakFrame();
734 return;
736 AutoWeakFrame* nextWeak = mAutoWeakFrames;
737 while (nextWeak && nextWeak->GetPreviousWeakFrame() != aWeakFrame) {
738 nextWeak = nextWeak->GetPreviousWeakFrame();
740 if (nextWeak) {
741 nextWeak->SetPreviousWeakFrame(aWeakFrame->GetPreviousWeakFrame());
745 void PresShell::RemoveWeakFrame(WeakFrame* aWeakFrame) {
746 MOZ_ASSERT(mWeakFrames.GetEntry(aWeakFrame));
747 mWeakFrames.RemoveEntry(aWeakFrame);
750 already_AddRefed<nsFrameSelection> PresShell::FrameSelection() {
751 RefPtr<nsFrameSelection> ret = mSelection;
752 return ret.forget();
755 //----------------------------------------------------------------------
757 static uint32_t sNextPresShellId;
759 /* static */
760 bool PresShell::AccessibleCaretEnabled(nsIDocShell* aDocShell) {
761 // If the pref forces it on, then enable it.
762 if (StaticPrefs::layout_accessiblecaret_enabled()) {
763 return true;
765 // If the touch pref is on, and touch events are enabled (this depends
766 // on the specific device running), then enable it.
767 if (StaticPrefs::layout_accessiblecaret_enabled_on_touch() &&
768 dom::TouchEvent::PrefEnabled(aDocShell)) {
769 return true;
771 // Otherwise, disabled.
772 return false;
775 PresShell::PresShell()
776 : mViewManager(nullptr),
777 mFrameManager(nullptr),
778 mAutoWeakFrames(nullptr),
779 #ifdef ACCESSIBILITY
780 mDocAccessible(nullptr),
781 #endif // #ifdef ACCESSIBILITY
782 mCurrentEventFrame(nullptr),
783 mMouseLocation(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE),
784 mPaintCount(0),
785 mAPZFocusSequenceNumber(0),
786 mCanvasBackgroundColor(NS_RGBA(0, 0, 0, 0)),
787 mActiveSuppressDisplayport(0),
788 mPresShellId(sNextPresShellId++),
789 mFontSizeInflationEmPerLine(0),
790 mFontSizeInflationMinTwips(0),
791 mFontSizeInflationLineThreshold(0),
792 mSelectionFlags(nsISelectionDisplay::DISPLAY_TEXT |
793 nsISelectionDisplay::DISPLAY_IMAGES),
794 mChangeNestCount(0),
795 mRenderingStateFlags(RenderingStateFlags::None),
796 mInFlush(false),
797 mCaretEnabled(false),
798 mNeedLayoutFlush(true),
799 mNeedStyleFlush(true),
800 mNeedThrottledAnimationFlush(true),
801 mVisualViewportSizeSet(false),
802 mDidInitialize(false),
803 mIsDestroying(false),
804 mIsReflowing(false),
805 mIsObservingDocument(false),
806 mForbiddenToFlush(false),
807 mIsDocumentGone(false),
808 mHaveShutDown(false),
809 mPaintingSuppressed(false),
810 mLastRootReflowHadUnconstrainedBSize(false),
811 mShouldUnsuppressPainting(false),
812 mIgnoreFrameDestruction(false),
813 mIsActive(true),
814 mFrozen(false),
815 mIsFirstPaint(true), // FIXME/bug 735029: find a better solution
816 mObservesMutationsForPrint(false),
817 mWasLastReflowInterrupted(false),
818 mObservingStyleFlushes(false),
819 mObservingLayoutFlushes(false),
820 mResizeEventPending(false),
821 mFontSizeInflationForceEnabled(false),
822 mFontSizeInflationDisabledInMasterProcess(false),
823 mFontSizeInflationEnabled(false),
824 mPaintingIsFrozen(false),
825 mIsNeverPainting(false),
826 mResolutionUpdated(false),
827 mResolutionUpdatedByApz(false),
828 mUnderHiddenEmbedderElement(false),
829 mDocumentLoading(false),
830 mNoDelayedMouseEvents(false),
831 mNoDelayedKeyEvents(false),
832 mApproximateFrameVisibilityVisited(false),
833 mNextPaintCompressed(false),
834 mHasCSSBackgroundColor(true),
835 mIsLastChromeOnlyEscapeKeyConsumed(false),
836 mHasReceivedPaintMessage(false),
837 mIsLastKeyDownCanceled(false),
838 mHasHandledUserInput(false),
839 mForceDispatchKeyPressEventsForNonPrintableKeys(false),
840 mForceUseLegacyKeyCodeAndCharCodeValues(false),
841 mInitializedWithKeyPressEventDispatchingBlacklist(false),
842 mForceUseLegacyNonPrimaryDispatch(false),
843 mInitializedWithClickEventDispatchingBlacklist(false) {
844 MOZ_LOG(gLog, LogLevel::Debug, ("PresShell::PresShell this=%p", this));
846 #ifdef MOZ_REFLOW_PERF
847 mReflowCountMgr = MakeUnique<ReflowCountMgr>();
848 mReflowCountMgr->SetPresContext(mPresContext);
849 mReflowCountMgr->SetPresShell(this);
850 #endif
851 mLastOSWake = mLoadBegin = TimeStamp::Now();
852 PodZero(&mReqsPerFlush);
853 PodZero(&mFlushesPerTick);
855 PointerEventHandler::Initialize();
858 NS_INTERFACE_TABLE_HEAD(PresShell)
859 NS_INTERFACE_TABLE_BEGIN
860 // In most cases, PresShell should be treated as concrete class, but need to
861 // QI for weak reference. Therefore, the case needed by do_QueryReferent()
862 // should be tested first.
863 NS_INTERFACE_TABLE_ENTRY(PresShell, PresShell)
864 NS_INTERFACE_TABLE_ENTRY(PresShell, nsIDocumentObserver)
865 NS_INTERFACE_TABLE_ENTRY(PresShell, nsISelectionController)
866 NS_INTERFACE_TABLE_ENTRY(PresShell, nsISelectionDisplay)
867 NS_INTERFACE_TABLE_ENTRY(PresShell, nsIObserver)
868 NS_INTERFACE_TABLE_ENTRY(PresShell, nsISupportsWeakReference)
869 NS_INTERFACE_TABLE_ENTRY(PresShell, nsIMutationObserver)
870 NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(PresShell, nsISupports, nsIObserver)
871 NS_INTERFACE_TABLE_END
872 NS_INTERFACE_TABLE_TO_MAP_SEGUE
873 NS_INTERFACE_MAP_END
875 NS_IMPL_ADDREF(PresShell)
876 NS_IMPL_RELEASE(PresShell)
878 PresShell::~PresShell() {
879 MOZ_RELEASE_ASSERT(!mForbiddenToFlush,
880 "Flag should only be set temporarily, while doing things "
881 "that shouldn't cause destruction");
882 MOZ_LOG(gLog, LogLevel::Debug, ("PresShell::~PresShell this=%p", this));
884 if (!mHaveShutDown) {
885 MOZ_ASSERT_UNREACHABLE("Someone did not call PresShell::Destroy()");
886 Destroy();
889 NS_ASSERTION(mCurrentEventContentStack.Count() == 0,
890 "Huh, event content left on the stack in pres shell dtor!");
891 NS_ASSERTION(mFirstCallbackEventRequest == nullptr &&
892 mLastCallbackEventRequest == nullptr,
893 "post-reflow queues not empty. This means we're leaking");
895 // Verify that if painting was frozen, but we're being removed from the tree,
896 // that we now re-enable painting on our refresh driver, since it may need to
897 // be re-used by another presentation.
898 if (mPaintingIsFrozen) {
899 mPresContext->RefreshDriver()->Thaw();
902 MOZ_ASSERT(mAllocatedPointers.IsEmpty(),
903 "Some pres arena objects were not freed");
905 mFrameManager = nullptr;
906 mFrameConstructor = nullptr;
908 mCurrentEventContent = nullptr;
912 * Initialize the presentation shell. Create view manager and style
913 * manager.
914 * Note this can't be merged into our constructor because caret initialization
915 * calls AddRef() on us.
917 void PresShell::Init(Document* aDocument, nsPresContext* aPresContext,
918 nsViewManager* aViewManager) {
919 MOZ_ASSERT(aDocument, "null ptr");
920 MOZ_ASSERT(aPresContext, "null ptr");
921 MOZ_ASSERT(aViewManager, "null ptr");
922 MOZ_ASSERT(!mDocument, "already initialized");
924 if (!aDocument || !aPresContext || !aViewManager || mDocument) {
925 return;
928 mDocument = aDocument;
929 mViewManager = aViewManager;
931 // mDocument is now set. It might have a display document whose "need layout/
932 // style" flush flags are not set, but ours will be set. To keep these
933 // consistent, call the flag setting functions to propagate those flags up
934 // to the display document.
935 SetNeedLayoutFlush();
936 SetNeedStyleFlush();
938 // Create our frame constructor.
939 mFrameConstructor = MakeUnique<nsCSSFrameConstructor>(mDocument, this);
941 mFrameManager = mFrameConstructor.get();
943 // The document viewer owns both view manager and pres shell.
944 mViewManager->SetPresShell(this);
946 // Bind the context to the presentation shell.
947 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
954 // needed otherwise.
955 EnsureStyleFlush();
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();
969 RefPtr<nsFrameSelection> frameSelection = mSelection;
970 frameSelection->Init(this, nullptr, accessibleCaretEnabled);
972 // Important: this has to happen after the selection has been set up
973 #ifdef SHOW_CARET
974 // make the caret
975 mCaret = new nsCaret();
976 mCaret->Init(this);
977 mOriginalCaret = mCaret;
979 // SetCaretEnabled(true); // make it show in browser windows
980 #endif
981 // set up selection to be displayed in document
982 // Don't enable selection for print media
983 nsPresContext::nsPresContextType type = aPresContext->Type();
984 if (type != nsPresContext::eContext_PrintPreview &&
985 type != nsPresContext::eContext_Print)
986 SetDisplaySelection(nsISelectionController::SELECTION_DISABLED);
988 if (gMaxRCProcessingTime == -1) {
989 gMaxRCProcessingTime =
990 Preferences::GetInt("layout.reflow.timeslice", NS_MAX_REFLOW_TIME);
993 if (nsStyleSheetService* ss = nsStyleSheetService::GetInstance()) {
994 ss->RegisterPresShell(this);
998 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
999 if (os) {
1000 os->AddObserver(this, "memory-pressure", false);
1001 os->AddObserver(this, NS_WIDGET_WAKE_OBSERVER_TOPIC, false);
1002 if (XRE_IsParentProcess() && !sProcessInteractable) {
1003 os->AddObserver(this, "sessionstore-one-or-no-tab-restored", false);
1005 os->AddObserver(this, "font-info-updated", false);
1006 os->AddObserver(this, "look-and-feel-pref-changed", false);
1010 #ifdef MOZ_REFLOW_PERF
1011 if (mReflowCountMgr) {
1012 bool paintFrameCounts =
1013 Preferences::GetBool("layout.reflow.showframecounts");
1015 bool dumpFrameCounts =
1016 Preferences::GetBool("layout.reflow.dumpframecounts");
1018 bool dumpFrameByFrameCounts =
1019 Preferences::GetBool("layout.reflow.dumpframebyframecounts");
1021 mReflowCountMgr->SetDumpFrameCounts(dumpFrameCounts);
1022 mReflowCountMgr->SetDumpFrameByFrameCounts(dumpFrameByFrameCounts);
1023 mReflowCountMgr->SetPaintFrameCounts(paintFrameCounts);
1025 #endif
1027 if (mDocument->HasAnimationController()) {
1028 SMILAnimationController* animCtrl = mDocument->GetAnimationController();
1029 animCtrl->NotifyRefreshDriverCreated(GetPresContext()->RefreshDriver());
1032 for (DocumentTimeline* timeline : mDocument->Timelines()) {
1033 timeline->NotifyRefreshDriverCreated(GetPresContext()->RefreshDriver());
1036 // Get our activeness from the docShell.
1037 QueryIsActive();
1039 // Setup our font inflation preferences.
1040 mFontSizeInflationEmPerLine = StaticPrefs::font_size_inflation_emPerLine();
1041 mFontSizeInflationMinTwips = StaticPrefs::font_size_inflation_minTwips();
1042 mFontSizeInflationLineThreshold =
1043 StaticPrefs::font_size_inflation_lineThreshold();
1044 mFontSizeInflationForceEnabled =
1045 StaticPrefs::font_size_inflation_forceEnabled();
1046 mFontSizeInflationDisabledInMasterProcess =
1047 StaticPrefs::font_size_inflation_disabledInMasterProcess();
1048 // We'll compute the font size inflation state in Initialize(), when we know
1049 // the document type.
1051 mTouchManager.Init(this, mDocument);
1053 if (mPresContext->IsRootContentDocumentCrossProcess()) {
1054 mZoomConstraintsClient = new ZoomConstraintsClient();
1055 mZoomConstraintsClient->Init(this, mDocument);
1057 // We call this to create mMobileViewportManager, if it is needed.
1058 UpdateViewportOverridden(false);
1061 if (nsCOMPtr<nsIDocShell> docShell = mPresContext->GetDocShell()) {
1062 BrowsingContext* bc = docShell->GetBrowsingContext();
1063 bool embedderFrameIsHidden = true;
1064 if (Element* embedderElement = bc->GetEmbedderElement()) {
1065 if (auto embedderFrame = embedderElement->GetPrimaryFrame()) {
1066 embedderFrameIsHidden = !embedderFrame->StyleVisibility()->IsVisible();
1070 if (BrowsingContext* parent = bc->GetParent()) {
1071 if (nsCOMPtr<nsIDocShell> parentDocShell = parent->GetDocShell()) {
1072 if (PresShell* parentPresShell = parentDocShell->GetPresShell()) {
1073 mUnderHiddenEmbedderElement =
1074 parentPresShell->IsUnderHiddenEmbedderElement() ||
1075 embedderFrameIsHidden;
1082 enum TextPerfLogType { eLog_reflow, eLog_loaddone, eLog_totals };
1084 static void LogTextPerfStats(gfxTextPerfMetrics* aTextPerf,
1085 PresShell* aPresShell,
1086 const gfxTextPerfMetrics::TextCounts& aCounts,
1087 float aTime, TextPerfLogType aLogType,
1088 const char* aURL) {
1089 LogModule* tpLog = gfxPlatform::GetLog(eGfxLog_textperf);
1091 // ignore XUL contexts unless at debug level
1092 mozilla::LogLevel logLevel = LogLevel::Warning;
1093 if (aCounts.numContentTextRuns == 0) {
1094 logLevel = LogLevel::Debug;
1097 if (!MOZ_LOG_TEST(tpLog, logLevel)) {
1098 return;
1101 char prefix[256];
1103 switch (aLogType) {
1104 case eLog_reflow:
1105 SprintfLiteral(prefix, "(textperf-reflow) %p time-ms: %7.0f", aPresShell,
1106 aTime);
1107 break;
1108 case eLog_loaddone:
1109 SprintfLiteral(prefix, "(textperf-loaddone) %p time-ms: %7.0f",
1110 aPresShell, aTime);
1111 break;
1112 default:
1113 MOZ_ASSERT(aLogType == eLog_totals, "unknown textperf log type");
1114 SprintfLiteral(prefix, "(textperf-totals) %p", aPresShell);
1117 double hitRatio = 0.0;
1118 uint32_t lookups = aCounts.wordCacheHit + aCounts.wordCacheMiss;
1119 if (lookups) {
1120 hitRatio = double(aCounts.wordCacheHit) / double(lookups);
1123 if (aLogType == eLog_loaddone) {
1124 MOZ_LOG(
1125 tpLog, logLevel,
1126 ("%s reflow: %d chars: %d "
1127 "[%s] "
1128 "content-textruns: %d chrome-textruns: %d "
1129 "max-textrun-len: %d "
1130 "word-cache-lookups: %d word-cache-hit-ratio: %4.3f "
1131 "word-cache-space: %d word-cache-long: %d "
1132 "pref-fallbacks: %d system-fallbacks: %d "
1133 "textruns-const: %d textruns-destr: %d "
1134 "generic-lookups: %d "
1135 "cumulative-textruns-destr: %d\n",
1136 prefix, aTextPerf->reflowCount, aCounts.numChars, (aURL ? aURL : ""),
1137 aCounts.numContentTextRuns, aCounts.numChromeTextRuns,
1138 aCounts.maxTextRunLen, lookups, hitRatio, aCounts.wordCacheSpaceRules,
1139 aCounts.wordCacheLong, aCounts.fallbackPrefs, aCounts.fallbackSystem,
1140 aCounts.textrunConst, aCounts.textrunDestr, aCounts.genericLookups,
1141 aTextPerf->cumulative.textrunDestr));
1142 } else {
1143 MOZ_LOG(
1144 tpLog, logLevel,
1145 ("%s reflow: %d chars: %d "
1146 "content-textruns: %d chrome-textruns: %d "
1147 "max-textrun-len: %d "
1148 "word-cache-lookups: %d word-cache-hit-ratio: %4.3f "
1149 "word-cache-space: %d word-cache-long: %d "
1150 "pref-fallbacks: %d system-fallbacks: %d "
1151 "textruns-const: %d textruns-destr: %d "
1152 "generic-lookups: %d "
1153 "cumulative-textruns-destr: %d\n",
1154 prefix, aTextPerf->reflowCount, aCounts.numChars,
1155 aCounts.numContentTextRuns, aCounts.numChromeTextRuns,
1156 aCounts.maxTextRunLen, lookups, hitRatio, aCounts.wordCacheSpaceRules,
1157 aCounts.wordCacheLong, aCounts.fallbackPrefs, aCounts.fallbackSystem,
1158 aCounts.textrunConst, aCounts.textrunDestr, aCounts.genericLookups,
1159 aTextPerf->cumulative.textrunDestr));
1163 void PresShell::Destroy() {
1164 // Do not add code before this line please!
1165 if (mHaveShutDown) {
1166 return;
1169 NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
1170 "destroy called on presshell while scripts not blocked");
1172 AUTO_PROFILER_LABEL("PresShell::Destroy", LAYOUT);
1174 // dump out cumulative text perf metrics
1175 gfxTextPerfMetrics* tp;
1176 if (mPresContext && (tp = mPresContext->GetTextPerfMetrics())) {
1177 tp->Accumulate();
1178 if (tp->cumulative.numChars > 0) {
1179 LogTextPerfStats(tp, this, tp->cumulative, 0.0, eLog_totals, nullptr);
1182 if (mPresContext) {
1183 if (gfxUserFontSet* fs = mPresContext->GetUserFontSet()) {
1184 uint32_t fontCount;
1185 uint64_t fontSize;
1186 fs->GetLoadStatistics(fontCount, fontSize);
1187 Telemetry::Accumulate(Telemetry::WEBFONT_PER_PAGE, fontCount);
1188 Telemetry::Accumulate(Telemetry::WEBFONT_SIZE_PER_PAGE,
1189 uint32_t(fontSize / 1024));
1190 } else {
1191 Telemetry::Accumulate(Telemetry::WEBFONT_PER_PAGE, 0);
1192 Telemetry::Accumulate(Telemetry::WEBFONT_SIZE_PER_PAGE, 0);
1196 #ifdef MOZ_REFLOW_PERF
1197 DumpReflows();
1198 mReflowCountMgr = nullptr;
1199 #endif
1201 if (mZoomConstraintsClient) {
1202 mZoomConstraintsClient->Destroy();
1203 mZoomConstraintsClient = nullptr;
1205 if (mMobileViewportManager) {
1206 mMobileViewportManager->Destroy();
1207 mMobileViewportManager = nullptr;
1208 mMVMContext = nullptr;
1211 #ifdef ACCESSIBILITY
1212 if (mDocAccessible) {
1213 # ifdef DEBUG
1214 if (a11y::logging::IsEnabled(a11y::logging::eDocDestroy))
1215 a11y::logging::DocDestroy("presshell destroyed", mDocument);
1216 # endif
1218 mDocAccessible->Shutdown();
1219 mDocAccessible = nullptr;
1221 #endif // ACCESSIBILITY
1223 MaybeReleaseCapturingContent();
1225 EventHandler::OnPresShellDestroy(mDocument);
1227 if (mContentToScrollTo) {
1228 mContentToScrollTo->DeleteProperty(nsGkAtoms::scrolling);
1229 mContentToScrollTo = nullptr;
1232 if (mPresContext) {
1233 // We need to notify the destroying the nsPresContext to ESM for
1234 // suppressing to use from ESM.
1235 mPresContext->EventStateManager()->NotifyDestroyPresContext(mPresContext);
1238 if (nsStyleSheetService* ss = nsStyleSheetService::GetInstance()) {
1239 ss->UnregisterPresShell(this);
1243 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
1244 if (os) {
1245 os->RemoveObserver(this, "memory-pressure");
1246 os->RemoveObserver(this, NS_WIDGET_WAKE_OBSERVER_TOPIC);
1247 if (XRE_IsParentProcess()) {
1248 os->RemoveObserver(this, "sessionstore-one-or-no-tab-restored");
1250 os->RemoveObserver(this, "font-info-updated");
1251 os->RemoveObserver(this, "look-and-feel-pref-changed");
1255 // If our paint suppression timer is still active, kill it.
1256 if (mPaintSuppressionTimer) {
1257 mPaintSuppressionTimer->Cancel();
1258 mPaintSuppressionTimer = nullptr;
1261 // Same for our reflow continuation timer
1262 if (mReflowContinueTimer) {
1263 mReflowContinueTimer->Cancel();
1264 mReflowContinueTimer = nullptr;
1267 if (mDelayedPaintTimer) {
1268 mDelayedPaintTimer->Cancel();
1269 mDelayedPaintTimer = nullptr;
1272 mSynthMouseMoveEvent.Revoke();
1274 mUpdateApproximateFrameVisibilityEvent.Revoke();
1276 ClearApproximatelyVisibleFramesList(Some(OnNonvisible::DiscardImages));
1278 if (mCaret) {
1279 mCaret->Terminate();
1280 mCaret = nullptr;
1283 if (mSelection) {
1284 RefPtr<nsFrameSelection> frameSelection = mSelection;
1285 frameSelection->DisconnectFromPresShell();
1288 if (mAccessibleCaretEventHub) {
1289 mAccessibleCaretEventHub->Terminate();
1290 mAccessibleCaretEventHub = nullptr;
1293 // release our pref style sheet, if we have one still
1295 // TODO(emilio): Should we move the preference sheet tracking to the Document?
1296 RemovePreferenceStyles();
1298 mIsDestroying = true;
1300 // We can't release all the event content in
1301 // mCurrentEventContentStack here since there might be code on the
1302 // stack that will release the event content too. Double release
1303 // bad!
1305 // The frames will be torn down, so remove them from the current
1306 // event frame stack (since they'd be dangling references if we'd
1307 // leave them in) and null out the mCurrentEventFrame pointer as
1308 // well.
1310 mCurrentEventFrame = nullptr;
1312 int32_t i, count = mCurrentEventFrameStack.Length();
1313 for (i = 0; i < count; i++) {
1314 mCurrentEventFrameStack[i] = nullptr;
1317 mFramesToDirty.Clear();
1318 mPendingScrollAnchorSelection.Clear();
1319 mPendingScrollAnchorAdjustment.Clear();
1321 if (mViewManager) {
1322 // Clear the view manager's weak pointer back to |this| in case it
1323 // was leaked.
1324 mViewManager->SetPresShell(nullptr);
1325 mViewManager = nullptr;
1328 nsRefreshDriver* rd = GetPresContext()->RefreshDriver();
1330 // This shell must be removed from the document before the frame
1331 // hierarchy is torn down to avoid finding deleted frames through
1332 // this presshell while the frames are being torn down
1333 if (mDocument) {
1334 NS_ASSERTION(mDocument->GetPresShell() == this, "Wrong shell?");
1335 mDocument->ClearServoRestyleRoot();
1336 mDocument->DeletePresShell();
1338 if (mDocument->HasAnimationController()) {
1339 mDocument->GetAnimationController()->NotifyRefreshDriverDestroying(rd);
1341 for (DocumentTimeline* timeline : mDocument->Timelines()) {
1342 timeline->NotifyRefreshDriverDestroying(rd);
1346 if (mPresContext) {
1347 rd->CancelPendingAnimationEvents(mPresContext->AnimationEventDispatcher());
1350 // Revoke any pending events. We need to do this and cancel pending reflows
1351 // before we destroy the frame manager, since apparently frame destruction
1352 // sometimes spins the event queue when plug-ins are involved(!).
1353 StopObservingRefreshDriver();
1355 if (rd->GetPresContext() == GetPresContext()) {
1356 rd->RevokeViewManagerFlush();
1359 CancelAllPendingReflows();
1360 CancelPostedReflowCallbacks();
1362 // Destroy the frame manager. This will destroy the frame hierarchy
1363 mFrameConstructor->WillDestroyFrameTree();
1365 NS_WARNING_ASSERTION(!mAutoWeakFrames && mWeakFrames.IsEmpty(),
1366 "Weak frames alive after destroying FrameManager");
1367 while (mAutoWeakFrames) {
1368 mAutoWeakFrames->Clear(this);
1370 nsTArray<WeakFrame*> toRemove(mWeakFrames.Count());
1371 for (auto iter = mWeakFrames.Iter(); !iter.Done(); iter.Next()) {
1372 toRemove.AppendElement(iter.Get()->GetKey());
1374 for (WeakFrame* weakFrame : toRemove) {
1375 weakFrame->Clear(this);
1378 if (mPresContext) {
1379 // We hold a reference to the pres context, and it holds a weak link back
1380 // to us. To avoid the pres context having a dangling reference, set its
1381 // pres shell to nullptr
1382 mPresContext->DetachPresShell();
1385 mHaveShutDown = true;
1387 mTouchManager.Destroy();
1390 void PresShell::StopObservingRefreshDriver() {
1391 nsRefreshDriver* rd = mPresContext->RefreshDriver();
1392 if (mResizeEventPending) {
1393 rd->RemoveResizeEventFlushObserver(this);
1395 if (mObservingLayoutFlushes) {
1396 rd->RemoveLayoutFlushObserver(this);
1398 if (mObservingStyleFlushes) {
1399 rd->RemoveStyleFlushObserver(this);
1403 void PresShell::StartObservingRefreshDriver() {
1404 nsRefreshDriver* rd = mPresContext->RefreshDriver();
1405 if (mResizeEventPending) {
1406 rd->AddResizeEventFlushObserver(this);
1408 if (mObservingLayoutFlushes) {
1409 rd->AddLayoutFlushObserver(this);
1411 if (mObservingStyleFlushes) {
1412 rd->AddStyleFlushObserver(this);
1416 nsRefreshDriver* PresShell::GetRefreshDriver() const {
1417 return mPresContext ? mPresContext->RefreshDriver() : nullptr;
1420 void PresShell::SetAuthorStyleDisabled(bool aStyleDisabled) {
1421 if (aStyleDisabled != StyleSet()->GetAuthorStyleDisabled()) {
1422 StyleSet()->SetAuthorStyleDisabled(aStyleDisabled);
1423 mDocument->ApplicableStylesChanged();
1425 nsCOMPtr<nsIObserverService> observerService =
1426 mozilla::services::GetObserverService();
1427 if (observerService) {
1428 observerService->NotifyObservers(
1429 ToSupports(mDocument), "author-style-disabled-changed", nullptr);
1434 bool PresShell::GetAuthorStyleDisabled() const {
1435 return StyleSet()->GetAuthorStyleDisabled();
1438 void PresShell::UpdatePreferenceStyles() {
1439 if (!mDocument) {
1440 return;
1443 // If the document doesn't have a window there's no need to notify
1444 // its presshell about changes to preferences since the document is
1445 // in a state where it doesn't matter any more (see
1446 // nsDocumentViewer::Close()).
1447 if (!mDocument->GetWindow()) {
1448 return;
1451 // Documents in chrome shells do not have any preference style rules applied.
1452 if (nsContentUtils::IsInChromeDocshell(mDocument)) {
1453 return;
1456 PreferenceSheet::EnsureInitialized();
1457 auto cache = nsLayoutStylesheetCache::Singleton();
1459 RefPtr<StyleSheet> newPrefSheet =
1460 PreferenceSheet::ShouldUseChromePrefs(*mDocument)
1461 ? cache->ChromePreferenceSheet()
1462 : cache->ContentPreferenceSheet();
1464 if (mPrefStyleSheet == newPrefSheet) {
1465 return;
1468 RemovePreferenceStyles();
1470 // NOTE(emilio): This sheet is added as an agent sheet, because we don't want
1471 // it to be modifiable from devtools and similar, see bugs 1239336 and
1472 // 1436782. I think it conceptually should be a user sheet, and could be
1473 // without too much trouble I'd think.
1474 StyleSet()->AppendStyleSheet(StyleOrigin::UserAgent, newPrefSheet);
1475 mPrefStyleSheet = newPrefSheet;
1478 void PresShell::RemovePreferenceStyles() {
1479 if (mPrefStyleSheet) {
1480 StyleSet()->RemoveStyleSheet(StyleOrigin::UserAgent, mPrefStyleSheet);
1481 mPrefStyleSheet = nullptr;
1485 void PresShell::AddUserSheet(StyleSheet* aSheet) {
1486 // Make sure this does what nsDocumentViewer::CreateStyleSet does wrt
1487 // ordering. We want this new sheet to come after all the existing stylesheet
1488 // service sheets (which are at the start), but before other user sheets; see
1489 // nsIStyleSheetService.idl for the ordering.
1491 nsStyleSheetService* sheetService = nsStyleSheetService::GetInstance();
1492 nsTArray<RefPtr<StyleSheet>>& userSheets = *sheetService->UserStyleSheets();
1494 // Search for the place to insert the new user sheet. Since all of the
1495 // stylesheet service provided user sheets should be at the start of the style
1496 // set's list, and aSheet should be at the end of userSheets. Given that, we
1497 // can find the right place to insert the new sheet based on the length of
1498 // userSheets.
1499 MOZ_ASSERT(aSheet);
1500 MOZ_ASSERT(userSheets.LastElement() == aSheet);
1502 size_t index = userSheets.Length() - 1;
1504 // Assert that all of userSheets (except for the last, new element) matches up
1505 // with what's in the style set.
1506 for (size_t i = 0; i < index; ++i) {
1507 MOZ_ASSERT(StyleSet()->SheetAt(StyleOrigin::User, i) == userSheets[i]);
1510 if (index == static_cast<size_t>(StyleSet()->SheetCount(StyleOrigin::User))) {
1511 StyleSet()->AppendStyleSheet(StyleOrigin::User, aSheet);
1512 } else {
1513 StyleSheet* ref = StyleSet()->SheetAt(StyleOrigin::User, index);
1514 StyleSet()->InsertStyleSheetBefore(StyleOrigin::User, aSheet, ref);
1517 mDocument->ApplicableStylesChanged();
1520 void PresShell::AddAgentSheet(StyleSheet* aSheet) {
1521 // Make sure this does what nsDocumentViewer::CreateStyleSet does
1522 // wrt ordering.
1523 StyleSet()->AppendStyleSheet(StyleOrigin::UserAgent, aSheet);
1524 mDocument->ApplicableStylesChanged();
1527 void PresShell::AddAuthorSheet(StyleSheet* aSheet) {
1528 // Document specific "additional" Author sheets should be stronger than the
1529 // ones added with the StyleSheetService.
1530 StyleSheet* firstAuthorSheet = mDocument->GetFirstAdditionalAuthorSheet();
1531 if (firstAuthorSheet) {
1532 StyleSet()->InsertStyleSheetBefore(StyleOrigin::Author, aSheet,
1533 firstAuthorSheet);
1534 } else {
1535 StyleSet()->AppendStyleSheet(StyleOrigin::Author, aSheet);
1538 mDocument->ApplicableStylesChanged();
1541 void PresShell::RemoveSheet(StyleOrigin aOrigin, StyleSheet* aSheet) {
1542 StyleSet()->RemoveStyleSheet(aOrigin, aSheet);
1543 mDocument->ApplicableStylesChanged();
1546 NS_IMETHODIMP
1547 PresShell::SetDisplaySelection(int16_t aToggle) {
1548 RefPtr<nsFrameSelection> frameSelection = mSelection;
1549 frameSelection->SetDisplaySelection(aToggle);
1550 return NS_OK;
1553 NS_IMETHODIMP
1554 PresShell::GetDisplaySelection(int16_t* aToggle) {
1555 RefPtr<nsFrameSelection> frameSelection = mSelection;
1556 *aToggle = frameSelection->GetDisplaySelection();
1557 return NS_OK;
1560 NS_IMETHODIMP
1561 PresShell::GetSelectionFromScript(RawSelectionType aRawSelectionType,
1562 Selection** aSelection) {
1563 if (!aSelection || !mSelection) return NS_ERROR_NULL_POINTER;
1565 RefPtr<nsFrameSelection> frameSelection = mSelection;
1566 RefPtr<Selection> selection =
1567 frameSelection->GetSelection(ToSelectionType(aRawSelectionType));
1569 if (!selection) {
1570 return NS_ERROR_INVALID_ARG;
1573 selection.forget(aSelection);
1574 return NS_OK;
1577 Selection* PresShell::GetSelection(RawSelectionType aRawSelectionType) {
1578 if (!mSelection) {
1579 return nullptr;
1582 RefPtr<nsFrameSelection> frameSelection = mSelection;
1583 return frameSelection->GetSelection(ToSelectionType(aRawSelectionType));
1586 Selection* PresShell::GetCurrentSelection(SelectionType aSelectionType) {
1587 if (!mSelection) return nullptr;
1589 RefPtr<nsFrameSelection> frameSelection = mSelection;
1590 return frameSelection->GetSelection(aSelectionType);
1593 already_AddRefed<nsISelectionController>
1594 PresShell::GetSelectionControllerForFocusedContent(
1595 nsIContent** aFocusedContent) {
1596 if (aFocusedContent) {
1597 *aFocusedContent = nullptr;
1600 if (mDocument) {
1601 nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
1602 nsCOMPtr<nsIContent> focusedContent = nsFocusManager::GetFocusedDescendant(
1603 mDocument->GetWindow(), nsFocusManager::eOnlyCurrentWindow,
1604 getter_AddRefs(focusedWindow));
1605 if (focusedContent) {
1606 nsIFrame* frame = focusedContent->GetPrimaryFrame();
1607 if (frame) {
1608 nsCOMPtr<nsISelectionController> selectionController;
1609 frame->GetSelectionController(mPresContext,
1610 getter_AddRefs(selectionController));
1611 if (selectionController) {
1612 if (aFocusedContent) {
1613 focusedContent.forget(aFocusedContent);
1615 return selectionController.forget();
1620 nsCOMPtr<nsISelectionController> self(this);
1621 return self.forget();
1624 NS_IMETHODIMP
1625 PresShell::ScrollSelectionIntoView(RawSelectionType aRawSelectionType,
1626 SelectionRegion aRegion, int16_t aFlags) {
1627 if (!mSelection) return NS_ERROR_NULL_POINTER;
1629 RefPtr<nsFrameSelection> frameSelection = mSelection;
1630 return frameSelection->ScrollSelectionIntoView(
1631 ToSelectionType(aRawSelectionType), aRegion, aFlags);
1634 NS_IMETHODIMP
1635 PresShell::RepaintSelection(RawSelectionType aRawSelectionType) {
1636 if (!mSelection) {
1637 return NS_ERROR_NULL_POINTER;
1640 if (MOZ_UNLIKELY(mIsDestroying)) {
1641 return NS_OK;
1644 RefPtr<nsFrameSelection> frameSelection = mSelection;
1645 return frameSelection->RepaintSelection(ToSelectionType(aRawSelectionType));
1648 // Make shell be a document observer
1649 void PresShell::BeginObservingDocument() {
1650 if (mDocument && !mIsDestroying) {
1651 mIsObservingDocument = true;
1652 if (mIsDocumentGone) {
1653 NS_WARNING(
1654 "Adding a presshell that was disconnected from the document "
1655 "as a document observer? Sounds wrong...");
1656 mIsDocumentGone = false;
1661 // Make shell stop being a document observer
1662 void PresShell::EndObservingDocument() {
1663 // XXXbz do we need to tell the frame constructor that the document
1664 // is gone, perhaps? Except for printing it's NOT gone, sometimes.
1665 mIsDocumentGone = true;
1666 mIsObservingDocument = false;
1669 #ifdef DEBUG_kipp
1670 char* nsPresShell_ReflowStackPointerTop;
1671 #endif
1673 class XBLConstructorRunner : public Runnable {
1674 public:
1675 explicit XBLConstructorRunner(Document* aDocument)
1676 : Runnable("XBLConstructorRunner"), mDocument(aDocument) {}
1678 NS_IMETHOD Run() override {
1679 mDocument->BindingManager()->ProcessAttachedQueue();
1680 return NS_OK;
1683 private:
1684 RefPtr<Document> mDocument;
1687 nsresult PresShell::Initialize() {
1688 if (mIsDestroying) {
1689 return NS_OK;
1692 if (!mDocument) {
1693 // Nothing to do
1694 return NS_OK;
1697 MOZ_LOG(gLog, LogLevel::Debug, ("PresShell::Initialize this=%p", this));
1699 NS_ASSERTION(!mDidInitialize, "Why are we being called?");
1701 RefPtr<PresShell> kungFuDeathGrip(this);
1703 RecomputeFontSizeInflationEnabled();
1704 MOZ_DIAGNOSTIC_ASSERT(!mIsDestroying);
1706 // Ensure the pres context doesn't think it has changed, since we haven't even
1707 // started layout. This avoids spurious restyles / reflows afterwards.
1709 // Note that this is very intentionally before setting mDidInitialize so it
1710 // doesn't notify the document, or run media query change events.
1711 mPresContext->FlushPendingMediaFeatureValuesChanged();
1712 MOZ_DIAGNOSTIC_ASSERT(!mIsDestroying);
1714 mDidInitialize = true;
1716 #ifdef DEBUG
1717 if (VerifyReflowFlags::NoisyCommands & gVerifyReflowFlags) {
1718 if (mDocument) {
1719 nsIURI* uri = mDocument->GetDocumentURI();
1720 if (uri) {
1721 printf("*** PresShell::Initialize (this=%p, url='%s')\n", (void*)this,
1722 uri->GetSpecOrDefault().get());
1726 #endif
1728 // Get the root frame from the frame manager
1729 // XXXbz it would be nice to move this somewhere else... like frame manager
1730 // Init(), say. But we need to make sure our views are all set up by the
1731 // time we do this!
1732 nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
1733 NS_ASSERTION(!rootFrame, "How did that happen, exactly?");
1735 if (!rootFrame) {
1736 nsAutoScriptBlocker scriptBlocker;
1737 rootFrame = mFrameConstructor->ConstructRootFrame();
1738 mFrameConstructor->SetRootFrame(rootFrame);
1741 NS_ENSURE_STATE(!mHaveShutDown);
1743 if (!rootFrame) {
1744 return NS_ERROR_OUT_OF_MEMORY;
1747 if (Element* root = mDocument->GetRootElement()) {
1749 nsAutoCauseReflowNotifier reflowNotifier(this);
1750 // Have the style sheet processor construct frame for the root
1751 // content object down
1752 mFrameConstructor->ContentInserted(
1753 root, nullptr, nsCSSFrameConstructor::InsertionKind::Sync);
1755 // Something in mFrameConstructor->ContentInserted may have caused
1756 // Destroy() to get called, bug 337586.
1757 NS_ENSURE_STATE(!mHaveShutDown);
1760 // nsAutoCauseReflowNotifier (which sets up a script blocker) going out of
1761 // scope may have killed us too
1762 NS_ENSURE_STATE(!mHaveShutDown);
1764 // Run the XBL binding constructors for any new frames we've constructed.
1765 // (Do this in a script runner, since our caller might have a script
1766 // blocker on the stack.)
1767 nsContentUtils::AddScriptRunner(new XBLConstructorRunner(mDocument));
1769 // XBLConstructorRunner might destroy us.
1770 NS_ENSURE_STATE(!mHaveShutDown);
1773 mDocument->TriggerAutoFocus();
1775 NS_ASSERTION(rootFrame, "How did that happen?");
1777 // Note: when the frame was created above it had the NS_FRAME_IS_DIRTY bit
1778 // set, but XBL processing could have caused a reflow which clears it.
1779 if (MOZ_LIKELY(rootFrame->GetStateBits() & NS_FRAME_IS_DIRTY)) {
1780 // Unset the DIRTY bits so that FrameNeedsReflow() will work right.
1781 rootFrame->RemoveStateBits(NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN);
1782 NS_ASSERTION(!mDirtyRoots.Contains(rootFrame),
1783 "Why is the root in mDirtyRoots already?");
1784 FrameNeedsReflow(rootFrame, IntrinsicDirty::Resize, NS_FRAME_IS_DIRTY);
1785 NS_ASSERTION(mDirtyRoots.Contains(rootFrame),
1786 "Should be in mDirtyRoots now");
1787 NS_ASSERTION(mObservingLayoutFlushes, "Why no reflow scheduled?");
1790 // Restore our root scroll position now if we're getting here after EndLoad
1791 // got called, since this is our one chance to do it. Note that we need not
1792 // have reflowed for this to work; when the scrollframe is finally reflowed
1793 // it'll pick up the position we store in it here.
1794 if (!mDocumentLoading) {
1795 RestoreRootScrollPosition();
1798 // For printing, we just immediately unsuppress.
1799 if (!mPresContext->IsPaginated()) {
1800 // Kick off a one-shot timer based off our pref value. When this timer
1801 // fires, if painting is still locked down, then we will go ahead and
1802 // trigger a full invalidate and allow painting to proceed normally.
1803 mPaintingSuppressed = true;
1804 // Don't suppress painting if the document isn't loading.
1805 Document::ReadyState readyState = mDocument->GetReadyStateEnum();
1806 if (readyState != Document::READYSTATE_COMPLETE) {
1807 mPaintSuppressionTimer = NS_NewTimer();
1809 if (!mPaintSuppressionTimer) {
1810 mPaintingSuppressed = false;
1811 } else {
1812 // Initialize the timer.
1814 // Default to PAINTLOCK_EVENT_DELAY if we can't get the pref value.
1815 int32_t delay = Preferences::GetInt("nglayout.initialpaint.delay",
1816 PAINTLOCK_EVENT_DELAY);
1818 mPaintSuppressionTimer->SetTarget(
1819 mDocument->EventTargetFor(TaskCategory::Other));
1820 mPaintSuppressionTimer->InitWithNamedFuncCallback(
1821 sPaintSuppressionCallback, this, delay, nsITimer::TYPE_ONE_SHOT,
1822 "PresShell::sPaintSuppressionCallback");
1826 // If we get here and painting is not suppressed, we still want to run the
1827 // unsuppression logic, so set mShouldUnsuppressPainting to true.
1828 if (!mPaintingSuppressed) {
1829 mShouldUnsuppressPainting = true;
1832 return NS_OK; // XXX this needs to be real. MMP
1835 void PresShell::sPaintSuppressionCallback(nsITimer* aTimer, void* aPresShell) {
1836 RefPtr<PresShell> self = static_cast<PresShell*>(aPresShell);
1837 if (self) self->UnsuppressPainting();
1840 nsresult PresShell::ResizeReflow(nscoord aWidth, nscoord aHeight,
1841 ResizeReflowOptions aOptions) {
1842 if (mZoomConstraintsClient) {
1843 // If we have a ZoomConstraintsClient and the available screen area
1844 // changed, then we might need to disable double-tap-to-zoom, so notify
1845 // the ZCC to update itself.
1846 mZoomConstraintsClient->ScreenSizeChanged();
1848 if (mMobileViewportManager) {
1849 // If we have a mobile viewport manager, request a reflow from it. It can
1850 // recompute the final CSS viewport and trigger a call to
1851 // ResizeReflowIgnoreOverride if it changed. We don't force adjusting
1852 // of resolution, because that is only necessary when we are destroying
1853 // the MVM.
1854 mMobileViewportManager->RequestReflow(false);
1855 return NS_OK;
1858 return ResizeReflowIgnoreOverride(aWidth, aHeight, aOptions);
1861 void PresShell::SimpleResizeReflow(nscoord aWidth, nscoord aHeight,
1862 ResizeReflowOptions aOptions) {
1863 MOZ_ASSERT(aWidth != NS_UNCONSTRAINEDSIZE);
1864 MOZ_ASSERT(aHeight != NS_UNCONSTRAINEDSIZE);
1865 nsSize oldSize = mPresContext->GetVisibleArea().Size();
1866 mPresContext->SetVisibleArea(nsRect(0, 0, aWidth, aHeight));
1867 nsIFrame* rootFrame = GetRootFrame();
1868 if (!rootFrame) {
1869 return;
1871 WritingMode wm = rootFrame->GetWritingMode();
1872 bool isBSizeChanging =
1873 wm.IsVertical() ? oldSize.width != aWidth : oldSize.height != aHeight;
1874 if (isBSizeChanging) {
1875 nsLayoutUtils::MarkIntrinsicISizesDirtyIfDependentOnBSize(rootFrame);
1877 FrameNeedsReflow(rootFrame, IntrinsicDirty::Resize,
1878 NS_FRAME_HAS_DIRTY_CHILDREN);
1880 // For compat with the old code path which always reflowed as long as there
1881 // was a root frame.
1882 bool suppressReflow = (aOptions & ResizeReflowOptions::SuppressReflow) ||
1883 mPresContext->SuppressingResizeReflow();
1884 if (!suppressReflow) {
1885 mDocument->FlushPendingNotifications(FlushType::InterruptibleLayout);
1889 nsresult PresShell::ResizeReflowIgnoreOverride(nscoord aWidth, nscoord aHeight,
1890 ResizeReflowOptions aOptions) {
1891 MOZ_ASSERT(!mIsReflowing, "Shouldn't be in reflow here!");
1892 nsSize oldSize = mPresContext->GetVisibleArea().Size();
1893 if (oldSize == nsSize(aWidth, aHeight)) {
1894 return NS_OK;
1897 // Historically we never fired resize events if there was no root frame by the
1898 // time this function got called.
1899 const bool initialized = mDidInitialize;
1900 RefPtr<PresShell> kungFuDeathGrip(this);
1902 auto postResizeEventIfNeeded = [this, initialized]() {
1903 if (initialized && !mIsDestroying && !mResizeEventPending) {
1904 mResizeEventPending = true;
1905 if (MOZ_LIKELY(!mDocument->GetBFCacheEntry())) {
1906 mPresContext->RefreshDriver()->AddResizeEventFlushObserver(this);
1911 if (!(aOptions & ResizeReflowOptions::BSizeLimit)) {
1912 SimpleResizeReflow(aWidth, aHeight, aOptions);
1913 postResizeEventIfNeeded();
1914 return NS_OK;
1917 MOZ_ASSERT(!mPresContext->SuppressingResizeReflow() &&
1918 !(aOptions & ResizeReflowOptions::SuppressReflow),
1919 "Can't suppress resize reflow and shrink-wrap at the same time");
1921 // Make sure that style is flushed before setting the pres context
1922 // VisibleArea.
1924 // Otherwise we may end up with bogus viewport units resolved against the
1925 // unconstrained bsize, or restyling the whole document resolving viewport
1926 // units against targetWidth, which may end up doing wasteful work.
1927 mDocument->FlushPendingNotifications(FlushType::Frames);
1929 nsIFrame* rootFrame = GetRootFrame();
1930 if (mIsDestroying || !rootFrame) {
1931 // If we don't have a root frame yet, that means we haven't had our initial
1932 // reflow... If that's the case, and aWidth or aHeight is unconstrained,
1933 // ignore them altogether.
1934 if (aHeight == NS_UNCONSTRAINEDSIZE || aWidth == NS_UNCONSTRAINEDSIZE) {
1935 // We can't do the work needed for SizeToContent without a root
1936 // frame, and we want to return before setting the visible area.
1937 return NS_ERROR_NOT_AVAILABLE;
1940 mPresContext->SetVisibleArea(nsRect(0, 0, aWidth, aHeight));
1941 // There isn't anything useful we can do if the initial reflow hasn't
1942 // happened.
1943 return NS_OK;
1946 WritingMode wm = rootFrame->GetWritingMode();
1947 MOZ_ASSERT((wm.IsVertical() ? aHeight : aWidth) != NS_UNCONSTRAINEDSIZE,
1948 "unconstrained isize not allowed");
1950 nscoord targetWidth = aWidth;
1951 nscoord targetHeight = aHeight;
1952 if (wm.IsVertical()) {
1953 targetWidth = NS_UNCONSTRAINEDSIZE;
1954 } else {
1955 targetHeight = NS_UNCONSTRAINEDSIZE;
1958 mPresContext->SetVisibleArea(nsRect(0, 0, targetWidth, targetHeight));
1959 // XXX Do a full invalidate at the beginning so that invalidates along
1960 // the way don't have region accumulation issues?
1962 // For height:auto BSizes (i.e. layout-controlled), descendant
1963 // intrinsic sizes can't depend on them. So the only other case is
1964 // viewport-controlled BSizes which we handle here.
1965 nsLayoutUtils::MarkIntrinsicISizesDirtyIfDependentOnBSize(rootFrame);
1968 nsAutoCauseReflowNotifier crNotifier(this);
1969 WillDoReflow();
1971 // Kick off a top-down reflow
1972 AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Reflow);
1973 nsViewManager::AutoDisableRefresh refreshBlocker(mViewManager);
1975 mDirtyRoots.Remove(rootFrame);
1976 DoReflow(rootFrame, true, nullptr);
1978 const bool reflowAgain =
1979 wm.IsVertical() ? mPresContext->GetVisibleArea().width > aWidth
1980 : mPresContext->GetVisibleArea().height > aHeight;
1982 if (reflowAgain) {
1983 mPresContext->SetVisibleArea(nsRect(0, 0, aWidth, aHeight));
1984 DoReflow(rootFrame, true, nullptr);
1988 DidDoReflow(true);
1990 // the reflow above should've set our bsize if it was NS_UNCONSTRAINEDSIZE,
1991 // and the isize shouldn't be NS_UNCONSTRAINEDSIZE anyway.
1992 MOZ_DIAGNOSTIC_ASSERT(
1993 mPresContext->GetVisibleArea().width != NS_UNCONSTRAINEDSIZE,
1994 "width should not be NS_UNCONSTRAINEDSIZE after reflow");
1995 MOZ_DIAGNOSTIC_ASSERT(
1996 mPresContext->GetVisibleArea().height != NS_UNCONSTRAINEDSIZE,
1997 "height should not be NS_UNCONSTRAINEDSIZE after reflow");
1999 postResizeEventIfNeeded();
2000 return NS_OK; // XXX this needs to be real. MMP
2003 void PresShell::FireResizeEvent() {
2004 if (mIsDocumentGone) {
2005 return;
2008 // If event handling is suppressed, repost the resize event to the refresh
2009 // driver. The event is marked as delayed so that the refresh driver does not
2010 // continue ticking.
2011 if (mDocument->EventHandlingSuppressed()) {
2012 if (MOZ_LIKELY(!mDocument->GetBFCacheEntry())) {
2013 mDocument->SetHasDelayedRefreshEvent();
2014 mPresContext->RefreshDriver()->AddResizeEventFlushObserver(
2015 this, /* aDelayed = */ true);
2017 return;
2020 mResizeEventPending = false;
2022 // Send resize event from here.
2023 WidgetEvent event(true, mozilla::eResize);
2024 nsEventStatus status = nsEventStatus_eIgnore;
2026 if (nsPIDOMWindowOuter* window = mDocument->GetWindow()) {
2027 EventDispatcher::Dispatch(window, mPresContext, &event, nullptr, &status);
2031 static nsIContent* GetNativeAnonymousSubtreeRoot(nsIContent* aContent) {
2032 if (!aContent || !aContent->IsInNativeAnonymousSubtree()) {
2033 return nullptr;
2035 auto* current = aContent;
2036 // FIXME(emilio): This should not need to worry about current being null, but
2037 // editor removes nodes in native anonymous subtrees, and we don't clean nodes
2038 // from the current event content stack from ContentRemoved, so it can
2039 // actually happen, see bug 1510208.
2040 while (current && !current->IsRootOfNativeAnonymousSubtree()) {
2041 current = current->GetFlattenedTreeParent();
2043 return current;
2046 void PresShell::NativeAnonymousContentRemoved(nsIContent* aAnonContent) {
2047 MOZ_ASSERT(aAnonContent->IsRootOfNativeAnonymousSubtree());
2048 if (nsIContent* root = GetNativeAnonymousSubtreeRoot(mCurrentEventContent)) {
2049 if (aAnonContent == root) {
2050 mCurrentEventContent = aAnonContent->GetFlattenedTreeParent();
2051 mCurrentEventFrame = nullptr;
2055 for (unsigned int i = 0; i < mCurrentEventContentStack.Length(); i++) {
2056 nsIContent* anon =
2057 GetNativeAnonymousSubtreeRoot(mCurrentEventContentStack.ElementAt(i));
2058 if (aAnonContent == anon) {
2059 mCurrentEventContentStack.ReplaceObjectAt(
2060 aAnonContent->GetFlattenedTreeParent(), i);
2061 mCurrentEventFrameStack[i] = nullptr;
2066 void PresShell::SetIgnoreFrameDestruction(bool aIgnore) {
2067 if (mDocument) {
2068 // We need to tell the ImageLoader to drop all its references to frames
2069 // because they're about to go away and it won't get notifications of that.
2070 mDocument->StyleImageLoader()->ClearFrames(mPresContext);
2072 mIgnoreFrameDestruction = aIgnore;
2075 void PresShell::NotifyDestroyingFrame(nsIFrame* aFrame) {
2076 // We must remove these from FrameLayerBuilder::DisplayItemData::mFrameList
2077 // here, otherwise the DisplayItemData destructor will use the destroyed frame
2078 // when it tries to remove it from the (array) value of this property.
2079 aFrame->RemoveDisplayItemDataForDeletion();
2081 if (!mIgnoreFrameDestruction) {
2082 if (aFrame->HasImageRequest()) {
2083 mDocument->StyleImageLoader()->DropRequestsForFrame(aFrame);
2086 mFrameConstructor->NotifyDestroyingFrame(aFrame);
2088 mDirtyRoots.Remove(aFrame);
2090 // Remove frame properties
2091 aFrame->DeleteAllProperties();
2093 if (aFrame == mCurrentEventFrame) {
2094 mCurrentEventContent = aFrame->GetContent();
2095 mCurrentEventFrame = nullptr;
2098 #ifdef DEBUG
2099 if (aFrame == mDrawEventTargetFrame) {
2100 mDrawEventTargetFrame = nullptr;
2102 #endif
2104 for (unsigned int i = 0; i < mCurrentEventFrameStack.Length(); i++) {
2105 if (aFrame == mCurrentEventFrameStack.ElementAt(i)) {
2106 // One of our stack frames was deleted. Get its content so that when we
2107 // pop it we can still get its new frame from its content
2108 nsIContent* currentEventContent = aFrame->GetContent();
2109 mCurrentEventContentStack.ReplaceObjectAt(currentEventContent, i);
2110 mCurrentEventFrameStack[i] = nullptr;
2114 mFramesToDirty.RemoveEntry(aFrame);
2116 nsIScrollableFrame* scrollableFrame = do_QueryFrame(aFrame);
2117 if (scrollableFrame) {
2118 mPendingScrollAnchorSelection.RemoveEntry(scrollableFrame);
2119 mPendingScrollAnchorAdjustment.RemoveEntry(scrollableFrame);
2124 already_AddRefed<nsCaret> PresShell::GetCaret() const {
2125 RefPtr<nsCaret> caret = mCaret;
2126 return caret.forget();
2129 already_AddRefed<AccessibleCaretEventHub>
2130 PresShell::GetAccessibleCaretEventHub() const {
2131 RefPtr<AccessibleCaretEventHub> eventHub = mAccessibleCaretEventHub;
2132 return eventHub.forget();
2135 void PresShell::SetCaret(nsCaret* aNewCaret) { mCaret = aNewCaret; }
2137 void PresShell::RestoreCaret() { mCaret = mOriginalCaret; }
2139 NS_IMETHODIMP PresShell::SetCaretEnabled(bool aInEnable) {
2140 bool oldEnabled = mCaretEnabled;
2142 mCaretEnabled = aInEnable;
2144 if (mCaretEnabled != oldEnabled) {
2145 MOZ_ASSERT(mCaret);
2146 if (mCaret) {
2147 mCaret->SetVisible(mCaretEnabled);
2151 return NS_OK;
2154 NS_IMETHODIMP PresShell::SetCaretReadOnly(bool aReadOnly) {
2155 if (mCaret) mCaret->SetCaretReadOnly(aReadOnly);
2156 return NS_OK;
2159 NS_IMETHODIMP PresShell::GetCaretEnabled(bool* aOutEnabled) {
2160 NS_ENSURE_ARG_POINTER(aOutEnabled);
2161 *aOutEnabled = mCaretEnabled;
2162 return NS_OK;
2165 NS_IMETHODIMP PresShell::SetCaretVisibilityDuringSelection(bool aVisibility) {
2166 if (mCaret) mCaret->SetVisibilityDuringSelection(aVisibility);
2167 return NS_OK;
2170 NS_IMETHODIMP PresShell::GetCaretVisible(bool* aOutIsVisible) {
2171 *aOutIsVisible = false;
2172 if (mCaret) {
2173 *aOutIsVisible = mCaret->IsVisible();
2175 return NS_OK;
2178 NS_IMETHODIMP PresShell::SetSelectionFlags(int16_t aInEnable) {
2179 mSelectionFlags = aInEnable;
2180 return NS_OK;
2183 NS_IMETHODIMP PresShell::GetSelectionFlags(int16_t* aOutEnable) {
2184 if (!aOutEnable) return NS_ERROR_INVALID_ARG;
2185 *aOutEnable = mSelectionFlags;
2186 return NS_OK;
2189 // implementation of nsISelectionController
2191 NS_IMETHODIMP
2192 PresShell::PhysicalMove(int16_t aDirection, int16_t aAmount, bool aExtend) {
2193 RefPtr<nsFrameSelection> frameSelection = mSelection;
2194 return frameSelection->PhysicalMove(aDirection, aAmount, aExtend);
2197 NS_IMETHODIMP
2198 PresShell::CharacterMove(bool aForward, bool aExtend) {
2199 RefPtr<nsFrameSelection> frameSelection = mSelection;
2200 return frameSelection->CharacterMove(aForward, aExtend);
2203 NS_IMETHODIMP
2204 PresShell::CharacterExtendForDelete() {
2205 RefPtr<nsFrameSelection> frameSelection = mSelection;
2206 return frameSelection->CharacterExtendForDelete();
2209 NS_IMETHODIMP
2210 PresShell::CharacterExtendForBackspace() {
2211 RefPtr<nsFrameSelection> frameSelection = mSelection;
2212 return frameSelection->CharacterExtendForBackspace();
2215 NS_IMETHODIMP
2216 PresShell::WordMove(bool aForward, bool aExtend) {
2217 RefPtr<nsFrameSelection> frameSelection = mSelection;
2218 nsresult result = frameSelection->WordMove(aForward, aExtend);
2219 // if we can't go down/up any more we must then move caret completely to
2220 // end/beginning respectively.
2221 if (NS_FAILED(result)) result = CompleteMove(aForward, aExtend);
2222 return result;
2225 NS_IMETHODIMP
2226 PresShell::WordExtendForDelete(bool aForward) {
2227 RefPtr<nsFrameSelection> frameSelection = mSelection;
2228 return frameSelection->WordExtendForDelete(aForward);
2231 NS_IMETHODIMP
2232 PresShell::LineMove(bool aForward, bool aExtend) {
2233 RefPtr<nsFrameSelection> frameSelection = mSelection;
2234 nsresult result = frameSelection->LineMove(aForward, aExtend);
2235 // if we can't go down/up any more we must then move caret completely to
2236 // end/beginning respectively.
2237 if (NS_FAILED(result)) result = CompleteMove(aForward, aExtend);
2238 return result;
2241 NS_IMETHODIMP
2242 PresShell::IntraLineMove(bool aForward, bool aExtend) {
2243 RefPtr<nsFrameSelection> frameSelection = mSelection;
2244 return frameSelection->IntraLineMove(aForward, aExtend);
2247 NS_IMETHODIMP
2248 PresShell::PageMove(bool aForward, bool aExtend) {
2249 nsIFrame* frame = nullptr;
2250 if (!aExtend) {
2251 frame = do_QueryFrame(
2252 GetScrollableFrameToScroll(ScrollableDirection::Vertical));
2253 // If there is no scrollable frame, get the frame to move caret instead.
2255 if (!frame) {
2256 frame = mSelection->GetFrameToPageSelect();
2257 if (!frame) {
2258 return NS_OK;
2261 RefPtr<nsFrameSelection> frameSelection = mSelection;
2262 frameSelection->CommonPageMove(aForward, aExtend, frame);
2263 // After ScrollSelectionIntoView(), the pending notifications might be
2264 // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
2265 return ScrollSelectionIntoView(
2266 nsISelectionController::SELECTION_NORMAL,
2267 nsISelectionController::SELECTION_FOCUS_REGION,
2268 nsISelectionController::SCROLL_SYNCHRONOUS |
2269 nsISelectionController::SCROLL_FOR_CARET_MOVE);
2272 NS_IMETHODIMP
2273 PresShell::ScrollPage(bool aForward) {
2274 nsIScrollableFrame* scrollFrame =
2275 GetScrollableFrameToScroll(ScrollableDirection::Vertical);
2276 if (scrollFrame) {
2277 scrollFrame->ScrollBy(nsIntPoint(0, aForward ? 1 : -1),
2278 nsIScrollableFrame::PAGES, ScrollMode::Smooth,
2279 nullptr, nullptr, nsIScrollableFrame::NOT_MOMENTUM,
2280 nsIScrollableFrame::ENABLE_SNAP);
2282 return NS_OK;
2285 NS_IMETHODIMP
2286 PresShell::ScrollLine(bool aForward) {
2287 nsIScrollableFrame* scrollFrame =
2288 GetScrollableFrameToScroll(ScrollableDirection::Vertical);
2289 if (scrollFrame) {
2290 int32_t lineCount =
2291 Preferences::GetInt("toolkit.scrollbox.verticalScrollDistance",
2292 NS_DEFAULT_VERTICAL_SCROLL_DISTANCE);
2293 scrollFrame->ScrollBy(nsIntPoint(0, aForward ? lineCount : -lineCount),
2294 nsIScrollableFrame::LINES, ScrollMode::Smooth,
2295 nullptr, nullptr, nsIScrollableFrame::NOT_MOMENTUM,
2296 nsIScrollableFrame::ENABLE_SNAP);
2298 return NS_OK;
2301 NS_IMETHODIMP
2302 PresShell::ScrollCharacter(bool aRight) {
2303 nsIScrollableFrame* scrollFrame =
2304 GetScrollableFrameToScroll(ScrollableDirection::Horizontal);
2305 if (scrollFrame) {
2306 int32_t h =
2307 Preferences::GetInt("toolkit.scrollbox.horizontalScrollDistance",
2308 NS_DEFAULT_HORIZONTAL_SCROLL_DISTANCE);
2309 scrollFrame->ScrollBy(nsIntPoint(aRight ? h : -h, 0),
2310 nsIScrollableFrame::LINES, ScrollMode::Smooth,
2311 nullptr, nullptr, nsIScrollableFrame::NOT_MOMENTUM,
2312 nsIScrollableFrame::ENABLE_SNAP);
2314 return NS_OK;
2317 NS_IMETHODIMP
2318 PresShell::CompleteScroll(bool aForward) {
2319 nsIScrollableFrame* scrollFrame =
2320 GetScrollableFrameToScroll(ScrollableDirection::Vertical);
2321 if (scrollFrame) {
2322 scrollFrame->ScrollBy(nsIntPoint(0, aForward ? 1 : -1),
2323 nsIScrollableFrame::WHOLE, ScrollMode::Smooth,
2324 nullptr, nullptr, nsIScrollableFrame::NOT_MOMENTUM,
2325 nsIScrollableFrame::ENABLE_SNAP);
2327 return NS_OK;
2330 NS_IMETHODIMP
2331 PresShell::CompleteMove(bool aForward, bool aExtend) {
2332 // Beware! This may flush notifications via synchronous
2333 // ScrollSelectionIntoView.
2334 RefPtr<nsFrameSelection> frameSelection = mSelection;
2335 nsIContent* limiter = frameSelection->GetAncestorLimiter();
2336 nsIFrame* frame = limiter ? limiter->GetPrimaryFrame()
2337 : FrameConstructor()->GetRootElementFrame();
2338 if (!frame) return NS_ERROR_FAILURE;
2339 nsIFrame::CaretPosition pos = frame->GetExtremeCaretPosition(!aForward);
2340 frameSelection->HandleClick(
2341 pos.mResultContent, pos.mContentOffset, pos.mContentOffset, aExtend,
2342 false, aForward ? CARET_ASSOCIATE_AFTER : CARET_ASSOCIATE_BEFORE);
2343 if (limiter) {
2344 // HandleClick resets ancestorLimiter, so set it again.
2345 frameSelection->SetAncestorLimiter(limiter);
2348 // After ScrollSelectionIntoView(), the pending notifications might be
2349 // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
2350 return ScrollSelectionIntoView(
2351 nsISelectionController::SELECTION_NORMAL,
2352 nsISelectionController::SELECTION_FOCUS_REGION,
2353 nsISelectionController::SCROLL_SYNCHRONOUS |
2354 nsISelectionController::SCROLL_FOR_CARET_MOVE);
2357 NS_IMETHODIMP
2358 PresShell::SelectAll() {
2359 RefPtr<nsFrameSelection> frameSelection = mSelection;
2360 return frameSelection->SelectAll();
2363 static void DoCheckVisibility(nsPresContext* aPresContext, nsIContent* aNode,
2364 int16_t aStartOffset, int16_t aEndOffset,
2365 bool* aRetval) {
2366 nsIFrame* frame = aNode->GetPrimaryFrame();
2367 if (!frame) {
2368 // No frame to look at so it must not be visible.
2369 return;
2372 // Start process now to go through all frames to find startOffset. Then check
2373 // chars after that to see if anything until EndOffset is visible.
2374 bool finished = false;
2375 frame->CheckVisibility(aPresContext, aStartOffset, aEndOffset, true,
2376 &finished, aRetval);
2377 // Don't worry about other return value.
2380 NS_IMETHODIMP
2381 PresShell::CheckVisibility(nsINode* node, int16_t startOffset,
2382 int16_t EndOffset, bool* _retval) {
2383 if (!node || startOffset > EndOffset || !_retval || startOffset < 0 ||
2384 EndOffset < 0)
2385 return NS_ERROR_INVALID_ARG;
2386 *_retval = false; // initialize return parameter
2387 nsCOMPtr<nsIContent> content(do_QueryInterface(node));
2388 if (!content) return NS_ERROR_FAILURE;
2390 DoCheckVisibility(mPresContext, content, startOffset, EndOffset, _retval);
2391 return NS_OK;
2394 nsresult PresShell::CheckVisibilityContent(nsIContent* aNode,
2395 int16_t aStartOffset,
2396 int16_t aEndOffset, bool* aRetval) {
2397 if (!aNode || aStartOffset > aEndOffset || !aRetval || aStartOffset < 0 ||
2398 aEndOffset < 0) {
2399 return NS_ERROR_INVALID_ARG;
2402 *aRetval = false;
2403 DoCheckVisibility(mPresContext, aNode, aStartOffset, aEndOffset, aRetval);
2404 return NS_OK;
2407 // end implementations nsISelectionController
2409 nsIFrame* PresShell::GetRootScrollFrame() const {
2410 nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
2411 // Ensure root frame is a viewport frame
2412 if (!rootFrame || !rootFrame->IsViewportFrame()) return nullptr;
2413 nsIFrame* theFrame = rootFrame->PrincipalChildList().FirstChild();
2414 if (!theFrame || !theFrame->IsScrollFrame()) return nullptr;
2415 return theFrame;
2418 nsIScrollableFrame* PresShell::GetRootScrollFrameAsScrollable() const {
2419 nsIFrame* frame = GetRootScrollFrame();
2420 if (!frame) return nullptr;
2421 nsIScrollableFrame* scrollableFrame = do_QueryFrame(frame);
2422 NS_ASSERTION(scrollableFrame,
2423 "All scroll frames must implement nsIScrollableFrame");
2424 return scrollableFrame;
2427 nsPageSequenceFrame* PresShell::GetPageSequenceFrame() const {
2428 return mFrameConstructor->GetPageSequenceFrame();
2431 nsCanvasFrame* PresShell::GetCanvasFrame() const {
2432 nsIFrame* frame = mFrameConstructor->GetDocElementContainingBlock();
2433 return do_QueryFrame(frame);
2436 void PresShell::RestoreRootScrollPosition() {
2437 nsIScrollableFrame* scrollableFrame = GetRootScrollFrameAsScrollable();
2438 if (scrollableFrame) {
2439 scrollableFrame->ScrollToRestoredPosition();
2443 void PresShell::MaybeReleaseCapturingContent() {
2444 RefPtr<nsFrameSelection> frameSelection = FrameSelection();
2445 if (frameSelection) {
2446 frameSelection->SetDragState(false);
2448 if (sCapturingContentInfo.mContent &&
2449 sCapturingContentInfo.mContent->OwnerDoc() == mDocument) {
2450 PresShell::ReleaseCapturingContent();
2454 void PresShell::BeginLoad(Document* aDocument) {
2455 mDocumentLoading = true;
2457 gfxTextPerfMetrics* tp = nullptr;
2458 if (mPresContext) {
2459 tp = mPresContext->GetTextPerfMetrics();
2462 bool shouldLog = MOZ_LOG_TEST(gLog, LogLevel::Debug);
2463 if (shouldLog || tp) {
2464 mLoadBegin = TimeStamp::Now();
2467 if (shouldLog) {
2468 nsIURI* uri = mDocument->GetDocumentURI();
2469 MOZ_LOG(gLog, LogLevel::Debug,
2470 ("(presshell) %p load begin [%s]\n", this,
2471 uri ? uri->GetSpecOrDefault().get() : ""));
2475 void PresShell::EndLoad(Document* aDocument) {
2476 MOZ_ASSERT(aDocument == mDocument, "Wrong document");
2478 RestoreRootScrollPosition();
2480 mDocumentLoading = false;
2483 void PresShell::LoadComplete() {
2484 gfxTextPerfMetrics* tp = nullptr;
2485 if (mPresContext) {
2486 tp = mPresContext->GetTextPerfMetrics();
2489 // log load
2490 bool shouldLog = MOZ_LOG_TEST(gLog, LogLevel::Debug);
2491 if (shouldLog || tp) {
2492 TimeDuration loadTime = TimeStamp::Now() - mLoadBegin;
2493 nsIURI* uri = mDocument->GetDocumentURI();
2494 nsAutoCString spec;
2495 if (uri) {
2496 spec = uri->GetSpecOrDefault();
2498 if (shouldLog) {
2499 MOZ_LOG(gLog, LogLevel::Debug,
2500 ("(presshell) %p load done time-ms: %9.2f [%s]\n", this,
2501 loadTime.ToMilliseconds(), spec.get()));
2503 if (tp) {
2504 tp->Accumulate();
2505 if (tp->cumulative.numChars > 0) {
2506 LogTextPerfStats(tp, this, tp->cumulative, loadTime.ToMilliseconds(),
2507 eLog_loaddone, spec.get());
2513 #ifdef DEBUG
2514 void PresShell::VerifyHasDirtyRootAncestor(nsIFrame* aFrame) {
2515 // XXXbz due to bug 372769, can't actually assert anything here...
2516 return;
2518 // XXXbz shouldn't need this part; remove it once FrameNeedsReflow
2519 // handles the root frame correctly.
2520 if (!aFrame->GetParent()) {
2521 return;
2524 // Make sure that there is a reflow root ancestor of |aFrame| that's
2525 // in mDirtyRoots already.
2526 while (aFrame && (aFrame->GetStateBits() & NS_FRAME_HAS_DIRTY_CHILDREN)) {
2527 if ((aFrame->HasAnyStateBits(NS_FRAME_REFLOW_ROOT |
2528 NS_FRAME_DYNAMIC_REFLOW_ROOT) ||
2529 !aFrame->GetParent()) &&
2530 mDirtyRoots.Contains(aFrame)) {
2531 return;
2534 aFrame = aFrame->GetParent();
2537 MOZ_ASSERT_UNREACHABLE(
2538 "Frame has dirty bits set but isn't scheduled to be "
2539 "reflowed?");
2541 #endif
2543 void PresShell::PostPendingScrollAnchorSelection(
2544 mozilla::layout::ScrollAnchorContainer* aContainer) {
2545 mPendingScrollAnchorSelection.PutEntry(aContainer->ScrollableFrame());
2548 void PresShell::FlushPendingScrollAnchorSelections() {
2549 for (auto iter = mPendingScrollAnchorSelection.Iter(); !iter.Done();
2550 iter.Next()) {
2551 nsIScrollableFrame* scroll = iter.Get()->GetKey();
2552 scroll->Anchor()->SelectAnchor();
2554 mPendingScrollAnchorSelection.Clear();
2557 void PresShell::PostPendingScrollAnchorAdjustment(
2558 ScrollAnchorContainer* aContainer) {
2559 mPendingScrollAnchorAdjustment.PutEntry(aContainer->ScrollableFrame());
2562 void PresShell::FlushPendingScrollAnchorAdjustments() {
2563 for (auto iter = mPendingScrollAnchorAdjustment.Iter(); !iter.Done();
2564 iter.Next()) {
2565 nsIScrollableFrame* scroll = iter.Get()->GetKey();
2566 scroll->Anchor()->ApplyAdjustments();
2568 mPendingScrollAnchorAdjustment.Clear();
2571 void PresShell::FrameNeedsReflow(nsIFrame* aFrame,
2572 IntrinsicDirty aIntrinsicDirty,
2573 nsFrameState aBitToAdd,
2574 ReflowRootHandling aRootHandling) {
2575 MOZ_ASSERT(aBitToAdd == NS_FRAME_IS_DIRTY ||
2576 aBitToAdd == NS_FRAME_HAS_DIRTY_CHILDREN || !aBitToAdd,
2577 "Unexpected bits being added");
2579 // FIXME bug 478135
2580 NS_ASSERTION(!(aIntrinsicDirty == IntrinsicDirty::StyleChange &&
2581 aBitToAdd == NS_FRAME_HAS_DIRTY_CHILDREN),
2582 "bits don't correspond to style change reason");
2584 // FIXME bug 457400
2585 NS_ASSERTION(!mIsReflowing, "can't mark frame dirty during reflow");
2587 // If we've not yet done the initial reflow, then don't bother
2588 // enqueuing a reflow command yet.
2589 if (!mDidInitialize) return;
2591 // If we're already destroying, don't bother with this either.
2592 if (mIsDestroying) return;
2594 #ifdef DEBUG
2595 // printf("gShellCounter: %d\n", gShellCounter++);
2596 if (mInVerifyReflow) return;
2598 if (VerifyReflowFlags::NoisyCommands & gVerifyReflowFlags) {
2599 printf("\nPresShell@%p: frame %p needs reflow\n", (void*)this,
2600 (void*)aFrame);
2601 if (VerifyReflowFlags::ReallyNoisyCommands & gVerifyReflowFlags) {
2602 printf("Current content model:\n");
2603 Element* rootElement = mDocument->GetRootElement();
2604 if (rootElement) {
2605 rootElement->List(stdout, 0);
2609 #endif
2611 AutoTArray<nsIFrame*, 4> subtrees;
2612 subtrees.AppendElement(aFrame);
2614 do {
2615 nsIFrame* subtreeRoot = subtrees.PopLastElement();
2617 // Grab |wasDirty| now so we can go ahead and update the bits on
2618 // subtreeRoot.
2619 bool wasDirty = NS_SUBTREE_DIRTY(subtreeRoot);
2620 subtreeRoot->AddStateBits(aBitToAdd);
2622 // Determine whether we need to keep looking for the next ancestor
2623 // reflow root if subtreeRoot itself is a reflow root.
2624 bool targetNeedsReflowFromParent;
2625 switch (aRootHandling) {
2626 case ReflowRootHandling::PositionOrSizeChange:
2627 targetNeedsReflowFromParent = true;
2628 break;
2629 case ReflowRootHandling::NoPositionOrSizeChange:
2630 targetNeedsReflowFromParent = false;
2631 break;
2632 case ReflowRootHandling::InferFromBitToAdd:
2633 targetNeedsReflowFromParent = (aBitToAdd == NS_FRAME_IS_DIRTY);
2634 break;
2637 #define FRAME_IS_REFLOW_ROOT(_f) \
2638 ((_f)->HasAnyStateBits(NS_FRAME_REFLOW_ROOT | \
2639 NS_FRAME_DYNAMIC_REFLOW_ROOT) && \
2640 ((_f) != subtreeRoot || !targetNeedsReflowFromParent))
2642 // Mark the intrinsic widths as dirty on the frame, all of its ancestors,
2643 // and all of its descendants, if needed:
2645 if (aIntrinsicDirty != IntrinsicDirty::Resize) {
2646 // Mark argument and all ancestors dirty. (Unless we hit a reflow
2647 // root that should contain the reflow. That root could be
2648 // subtreeRoot itself if it's not dirty, or it could be some
2649 // ancestor of subtreeRoot.)
2650 for (nsIFrame* a = subtreeRoot; a && !FRAME_IS_REFLOW_ROOT(a);
2651 a = a->GetParent()) {
2652 a->MarkIntrinsicISizesDirty();
2653 if (a->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) &&
2654 a->IsAbsolutelyPositioned()) {
2655 // If we get here, 'a' is abspos, so its subtree's intrinsic sizing
2656 // has no effect on its ancestors' intrinsic sizing. So, don't loop
2657 // upwards any further.
2658 break;
2663 const bool styleChange = (aIntrinsicDirty == IntrinsicDirty::StyleChange);
2664 const bool dirty = (aBitToAdd == NS_FRAME_IS_DIRTY);
2665 if (styleChange || dirty) {
2666 // Mark all descendants dirty (using an nsTArray stack rather than
2667 // recursion).
2668 // Note that ReflowInput::InitResizeFlags has some similar
2669 // code; see comments there for how and why it differs.
2670 AutoTArray<nsIFrame*, 32> stack;
2671 stack.AppendElement(subtreeRoot);
2673 do {
2674 nsIFrame* f = stack.PopLastElement();
2676 if (styleChange) {
2677 if (f->IsPlaceholderFrame()) {
2678 nsIFrame* oof = nsPlaceholderFrame::GetRealFrameForPlaceholder(f);
2679 if (!nsLayoutUtils::IsProperAncestorFrame(subtreeRoot, oof)) {
2680 // We have another distinct subtree we need to mark.
2681 subtrees.AppendElement(oof);
2686 nsIFrame::ChildListIterator lists(f);
2687 for (; !lists.IsDone(); lists.Next()) {
2688 for (nsIFrame* kid : lists.CurrentList()) {
2689 if (styleChange) {
2690 kid->MarkIntrinsicISizesDirty();
2692 if (dirty) {
2693 kid->AddStateBits(NS_FRAME_IS_DIRTY);
2695 stack.AppendElement(kid);
2698 } while (stack.Length() != 0);
2701 // Skip setting dirty bits up the tree if we weren't given a bit to add.
2702 if (!aBitToAdd) {
2703 continue;
2706 // Set NS_FRAME_HAS_DIRTY_CHILDREN bits (via nsIFrame::ChildIsDirty)
2707 // up the tree until we reach either a frame that's already dirty or
2708 // a reflow root.
2709 nsIFrame* f = subtreeRoot;
2710 for (;;) {
2711 if (FRAME_IS_REFLOW_ROOT(f) || !f->GetParent()) {
2712 // we've hit a reflow root or the root frame
2713 if (!wasDirty) {
2714 mDirtyRoots.Add(f);
2715 SetNeedLayoutFlush();
2717 #ifdef DEBUG
2718 else {
2719 VerifyHasDirtyRootAncestor(f);
2721 #endif
2723 break;
2726 nsIFrame* child = f;
2727 f = f->GetParent();
2728 wasDirty = NS_SUBTREE_DIRTY(f);
2729 f->ChildIsDirty(child);
2730 NS_ASSERTION(f->GetStateBits() & NS_FRAME_HAS_DIRTY_CHILDREN,
2731 "ChildIsDirty didn't do its job");
2732 if (wasDirty) {
2733 // This frame was already marked dirty.
2734 #ifdef DEBUG
2735 VerifyHasDirtyRootAncestor(f);
2736 #endif
2737 break;
2740 } while (subtrees.Length() != 0);
2742 MaybeScheduleReflow();
2745 void PresShell::FrameNeedsToContinueReflow(nsIFrame* aFrame) {
2746 NS_ASSERTION(mIsReflowing, "Must be in reflow when marking path dirty.");
2747 MOZ_ASSERT(mCurrentReflowRoot, "Must have a current reflow root here");
2748 NS_ASSERTION(
2749 aFrame == mCurrentReflowRoot ||
2750 nsLayoutUtils::IsProperAncestorFrame(mCurrentReflowRoot, aFrame),
2751 "Frame passed in is not the descendant of mCurrentReflowRoot");
2752 NS_ASSERTION(aFrame->GetStateBits() & NS_FRAME_IN_REFLOW,
2753 "Frame passed in not in reflow?");
2755 mFramesToDirty.PutEntry(aFrame);
2758 already_AddRefed<nsIContent> PresShell::GetContentForScrolling() const {
2759 if (nsCOMPtr<nsIContent> focused = GetFocusedContentInOurWindow()) {
2760 return focused.forget();
2762 return GetSelectedContentForScrolling();
2765 already_AddRefed<nsIContent> PresShell::GetSelectedContentForScrolling() const {
2766 nsCOMPtr<nsIContent> selectedContent;
2767 if (mSelection) {
2768 Selection* domSelection = mSelection->GetSelection(SelectionType::eNormal);
2769 if (domSelection) {
2770 selectedContent =
2771 nsIContent::FromNodeOrNull(domSelection->GetFocusNode());
2774 return selectedContent.forget();
2777 nsIScrollableFrame* PresShell::GetNearestScrollableFrame(
2778 nsIFrame* aFrame, ScrollableDirection aDirection) {
2779 if (aDirection == ScrollableDirection::Either) {
2780 return nsLayoutUtils::GetNearestScrollableFrame(aFrame);
2783 return nsLayoutUtils::GetNearestScrollableFrameForDirection(
2784 aFrame, aDirection == ScrollableDirection::Vertical
2785 ? nsLayoutUtils::eVertical
2786 : nsLayoutUtils::eHorizontal);
2789 nsIScrollableFrame* PresShell::GetScrollableFrameToScrollForContent(
2790 nsIContent* aContent, ScrollableDirection aDirection) {
2791 nsIScrollableFrame* scrollFrame = nullptr;
2792 if (aContent) {
2793 nsIFrame* startFrame = aContent->GetPrimaryFrame();
2794 if (startFrame) {
2795 scrollFrame = startFrame->GetScrollTargetFrame();
2796 if (scrollFrame) {
2797 startFrame = scrollFrame->GetScrolledFrame();
2799 scrollFrame = GetNearestScrollableFrame(startFrame, aDirection);
2802 if (!scrollFrame) {
2803 scrollFrame = GetRootScrollFrameAsScrollable();
2804 if (!scrollFrame || !scrollFrame->GetScrolledFrame()) {
2805 return nullptr;
2807 scrollFrame =
2808 GetNearestScrollableFrame(scrollFrame->GetScrolledFrame(), aDirection);
2810 return scrollFrame;
2813 nsIScrollableFrame* PresShell::GetScrollableFrameToScroll(
2814 ScrollableDirection aDirection) {
2815 nsCOMPtr<nsIContent> content = GetContentForScrolling();
2816 return GetScrollableFrameToScrollForContent(content.get(), aDirection);
2819 void PresShell::CancelAllPendingReflows() {
2820 mDirtyRoots.Clear();
2822 if (mObservingLayoutFlushes) {
2823 GetPresContext()->RefreshDriver()->RemoveLayoutFlushObserver(this);
2824 mObservingLayoutFlushes = false;
2827 ASSERT_REFLOW_SCHEDULED_STATE();
2830 static bool DestroyFramesAndStyleDataFor(
2831 Element* aElement, nsPresContext& aPresContext,
2832 RestyleManager::IncludeRoot aIncludeRoot) {
2833 bool didReconstruct =
2834 aPresContext.FrameConstructor()->DestroyFramesFor(aElement);
2835 RestyleManager::ClearServoDataFromSubtree(aElement, aIncludeRoot);
2836 return didReconstruct;
2839 void PresShell::SlotAssignmentWillChange(Element& aElement,
2840 HTMLSlotElement* aOldSlot,
2841 HTMLSlotElement* aNewSlot) {
2842 MOZ_ASSERT(aOldSlot != aNewSlot);
2844 if (MOZ_UNLIKELY(!mDidInitialize)) {
2845 return;
2848 // If the old slot is about to become empty and show fallback, let layout know
2849 // that it needs to do work.
2850 if (aOldSlot && aOldSlot->AssignedNodes().Length() == 1 &&
2851 aOldSlot->HasChildren()) {
2852 DestroyFramesForAndRestyle(aOldSlot);
2855 // Ensure the new element starts off clean.
2856 DestroyFramesAndStyleDataFor(&aElement, *mPresContext,
2857 RestyleManager::IncludeRoot::Yes);
2859 if (aNewSlot) {
2860 // If the new slot will stop showing fallback content, we need to reframe it
2861 // altogether.
2862 if (aNewSlot->AssignedNodes().IsEmpty() && aNewSlot->HasChildren()) {
2863 DestroyFramesForAndRestyle(aNewSlot);
2864 // Otherwise we just care about the element, but we need to ensure that
2865 // something takes care of traversing to the relevant slot, if needed.
2866 } else if (aNewSlot->HasServoData() &&
2867 !Servo_Element_IsDisplayNone(aNewSlot)) {
2868 // Set the reframe bits...
2869 aNewSlot->NoteDescendantsNeedFramesForServo();
2870 aElement.SetFlags(NODE_NEEDS_FRAME);
2871 // Now the style dirty bits. Note that we can't just do
2872 // aElement.NoteDirtyForServo(), because the new slot is not setup yet.
2873 aNewSlot->SetHasDirtyDescendantsForServo();
2874 aNewSlot->NoteDirtySubtreeForServo();
2879 #ifdef DEBUG
2880 static void AssertNoFramesInSubtree(nsIContent* aContent) {
2881 for (nsINode* node : ShadowIncludingTreeIterator(*aContent)) {
2882 nsIContent* c = nsIContent::FromNode(node);
2883 MOZ_ASSERT(!c->GetPrimaryFrame());
2884 if (auto* binding = c->GetXBLBinding()) {
2885 if (auto* bindingWithContent = binding->GetBindingWithContent()) {
2886 nsIContent* anonContent = bindingWithContent->GetAnonymousContent();
2887 MOZ_ASSERT(!anonContent->GetPrimaryFrame());
2889 // Need to do this instead of just AssertNoFramesInSubtree(anonContent),
2890 // because the parent of the children of the <content> element isn't the
2891 // <content> element, but the bound element, and that confuses
2892 // GetNextNode a lot.
2893 for (nsIContent* child = anonContent->GetFirstChild(); child;
2894 child = child->GetNextSibling()) {
2895 AssertNoFramesInSubtree(child);
2901 #endif
2903 void PresShell::DestroyFramesForAndRestyle(Element* aElement) {
2904 #ifdef DEBUG
2905 auto postCondition =
2906 mozilla::MakeScopeExit([&]() { AssertNoFramesInSubtree(aElement); });
2907 #endif
2909 MOZ_ASSERT(aElement);
2910 if (MOZ_UNLIKELY(!mDidInitialize)) {
2911 return;
2914 if (!aElement->GetFlattenedTreeParentNode()) {
2915 // Nothing to do here, the element already is out of the frame tree.
2916 return;
2919 nsAutoScriptBlocker scriptBlocker;
2921 // Mark ourselves as not safe to flush while we're doing frame destruction.
2922 ++mChangeNestCount;
2924 const bool didReconstruct = FrameConstructor()->DestroyFramesFor(aElement);
2926 // Clear the style data from all the flattened tree descendants, but _not_
2927 // from us, since otherwise we wouldn't see the reframe.
2928 RestyleManager::ClearServoDataFromSubtree(aElement,
2929 RestyleManager::IncludeRoot::No);
2931 auto changeHint =
2932 didReconstruct ? nsChangeHint(0) : nsChangeHint_ReconstructFrame;
2934 mPresContext->RestyleManager()->PostRestyleEvent(
2935 aElement, RestyleHint::RestyleSubtree(), changeHint);
2937 --mChangeNestCount;
2940 void PresShell::PostRecreateFramesFor(Element* aElement) {
2941 if (MOZ_UNLIKELY(!mDidInitialize)) {
2942 // Nothing to do here. In fact, if we proceed and aElement is the root, we
2943 // will crash.
2944 return;
2947 mPresContext->RestyleManager()->PostRestyleEvent(
2948 aElement, RestyleHint{0}, nsChangeHint_ReconstructFrame);
2951 void PresShell::RestyleForAnimation(Element* aElement, RestyleHint aHint) {
2952 // Now that we no longer have separate non-animation and animation
2953 // restyles, this method having a distinct identity is less important,
2954 // but it still seems useful to offer as a "more public" API and as a
2955 // chokepoint for these restyles to go through.
2956 mPresContext->RestyleManager()->PostRestyleEvent(aElement, aHint,
2957 nsChangeHint(0));
2960 void PresShell::SetForwardingContainer(const WeakPtr<nsDocShell>& aContainer) {
2961 mForwardingContainer = aContainer;
2964 void PresShell::ClearFrameRefs(nsIFrame* aFrame) {
2965 mPresContext->EventStateManager()->ClearFrameRefs(aFrame);
2967 AutoWeakFrame* weakFrame = mAutoWeakFrames;
2968 while (weakFrame) {
2969 AutoWeakFrame* prev = weakFrame->GetPreviousWeakFrame();
2970 if (weakFrame->GetFrame() == aFrame) {
2971 // This removes weakFrame from mAutoWeakFrames.
2972 weakFrame->Clear(this);
2974 weakFrame = prev;
2977 AutoTArray<WeakFrame*, 4> toRemove;
2978 for (auto iter = mWeakFrames.Iter(); !iter.Done(); iter.Next()) {
2979 WeakFrame* weakFrame = iter.Get()->GetKey();
2980 if (weakFrame->GetFrame() == aFrame) {
2981 toRemove.AppendElement(weakFrame);
2984 for (WeakFrame* weakFrame : toRemove) {
2985 weakFrame->Clear(this);
2989 already_AddRefed<gfxContext> PresShell::CreateReferenceRenderingContext() {
2990 nsDeviceContext* devCtx = mPresContext->DeviceContext();
2991 RefPtr<gfxContext> rc;
2992 if (mPresContext->IsScreen()) {
2993 rc = gfxContext::CreateOrNull(
2994 gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget().get());
2995 } else {
2996 // We assume the devCtx has positive width and height for this call.
2997 // However, width and height, may be outside of the reasonable range
2998 // so rc may still be null.
2999 rc = devCtx->CreateReferenceRenderingContext();
3002 return rc ? rc.forget() : nullptr;
3005 nsresult PresShell::GoToAnchor(const nsAString& aAnchorName, bool aScroll,
3006 ScrollFlags aAdditionalScrollFlags) {
3007 if (!mDocument) {
3008 return NS_ERROR_FAILURE;
3011 const Element* root = mDocument->GetRootElement();
3012 if (root && root->IsSVGElement(nsGkAtoms::svg)) {
3013 // We need to execute this even if there is an empty anchor name
3014 // so that any existing SVG fragment identifier effect is removed
3015 if (SVGFragmentIdentifier::ProcessFragmentIdentifier(mDocument,
3016 aAnchorName)) {
3017 return NS_OK;
3021 // Hold a reference to the ESM in case event dispatch tears us down.
3022 RefPtr<EventStateManager> esm = mPresContext->EventStateManager();
3024 if (aAnchorName.IsEmpty()) {
3025 NS_ASSERTION(!aScroll, "can't scroll to empty anchor name");
3026 esm->SetContentState(nullptr, NS_EVENT_STATE_URLTARGET);
3027 return NS_OK;
3030 nsresult rv = NS_OK;
3031 nsCOMPtr<nsIContent> content;
3033 // Search for an element with a matching "id" attribute
3034 if (mDocument) {
3035 content = mDocument->GetElementById(aAnchorName);
3038 // Search for an anchor element with a matching "name" attribute
3039 if (!content && mDocument->IsHTMLDocument()) {
3040 // Find a matching list of named nodes
3041 nsCOMPtr<nsINodeList> list = mDocument->GetElementsByName(aAnchorName);
3042 if (list) {
3043 // Loop through the named nodes looking for the first anchor
3044 uint32_t length = list->Length();
3045 for (uint32_t i = 0; i < length; i++) {
3046 nsIContent* node = list->Item(i);
3047 if (node->IsHTMLElement(nsGkAtoms::a)) {
3048 content = node;
3049 break;
3055 // Search for anchor in the HTML namespace with a matching name
3056 if (!content && !mDocument->IsHTMLDocument()) {
3057 NS_NAMED_LITERAL_STRING(nameSpace, "http://www.w3.org/1999/xhtml");
3058 // Get the list of anchor elements
3059 nsCOMPtr<nsINodeList> list =
3060 mDocument->GetElementsByTagNameNS(nameSpace, NS_LITERAL_STRING("a"));
3061 // Loop through the anchors looking for the first one with the given name.
3062 for (uint32_t i = 0; true; i++) {
3063 nsIContent* node = list->Item(i);
3064 if (!node) { // End of list
3065 break;
3068 // Compare the name attribute
3069 if (node->IsElement() &&
3070 node->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name,
3071 aAnchorName, eCaseMatters)) {
3072 content = node;
3073 break;
3078 esm->SetContentState(content, NS_EVENT_STATE_URLTARGET);
3080 #ifdef ACCESSIBILITY
3081 nsIContent* anchorTarget = content;
3082 #endif
3084 nsIScrollableFrame* rootScroll = GetRootScrollFrameAsScrollable();
3085 if (rootScroll && rootScroll->DidHistoryRestore()) {
3086 // Scroll position restored from history trumps scrolling to anchor.
3087 aScroll = false;
3088 rootScroll->ClearDidHistoryRestore();
3091 if (content) {
3092 if (aScroll) {
3093 rv = ScrollContentIntoView(
3094 content, ScrollAxis(kScrollToTop, WhenToScroll::Always), ScrollAxis(),
3095 ScrollFlags::AnchorScrollFlags | aAdditionalScrollFlags);
3096 NS_ENSURE_SUCCESS(rv, rv);
3098 nsIScrollableFrame* rootScroll = GetRootScrollFrameAsScrollable();
3099 if (rootScroll) {
3100 mLastAnchorScrolledTo = content;
3101 mLastAnchorScrollPositionY = rootScroll->GetScrollPosition().y;
3105 // Should we select the target? This action is controlled by a
3106 // preference: the default is to not select.
3107 bool selectAnchor = Preferences::GetBool("layout.selectanchor");
3109 // Even if select anchor pref is false, we must still move the
3110 // caret there. That way tabbing will start from the new
3111 // location
3112 RefPtr<nsRange> jumpToRange = new nsRange(mDocument);
3113 while (content && content->GetFirstChild()) {
3114 content = content->GetFirstChild();
3116 jumpToRange->SelectNodeContents(*content, IgnoreErrors());
3117 // Select the anchor
3118 RefPtr<Selection> sel = mSelection->GetSelection(SelectionType::eNormal);
3119 if (sel) {
3120 sel->RemoveAllRanges(IgnoreErrors());
3121 sel->AddRangeAndSelectFramesAndNotifyListeners(*jumpToRange,
3122 IgnoreErrors());
3123 if (!selectAnchor) {
3124 // Use a caret (collapsed selection) at the start of the anchor
3125 sel->CollapseToStart(IgnoreErrors());
3128 // Selection is at anchor.
3129 // Now focus the document itself if focus is on an element within it.
3130 nsPIDOMWindowOuter* win = mDocument->GetWindow();
3132 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
3133 if (fm && win) {
3134 nsCOMPtr<mozIDOMWindowProxy> focusedWindow;
3135 fm->GetFocusedWindow(getter_AddRefs(focusedWindow));
3136 if (SameCOMIdentity(win, focusedWindow)) {
3137 fm->ClearFocus(focusedWindow);
3141 // If the target is an animation element, activate the animation
3142 if (content->IsNodeOfType(nsINode::eANIMATION)) {
3143 SVGContentUtils::ActivateByHyperlink(content.get());
3145 } else {
3146 rv = NS_ERROR_FAILURE;
3147 NS_NAMED_LITERAL_STRING(top, "top");
3148 if (nsContentUtils::EqualsIgnoreASCIICase(aAnchorName, top)) {
3149 // Scroll to the top/left if aAnchorName is "top" and there is no element
3150 // with such a name or id.
3151 rv = NS_OK;
3152 nsIScrollableFrame* sf = GetRootScrollFrameAsScrollable();
3153 // Check |aScroll| after setting |rv| so we set |rv| to the same
3154 // thing whether or not |aScroll| is true.
3155 if (aScroll && sf) {
3156 // Scroll to the top of the page
3157 sf->ScrollTo(nsPoint(0, 0), ScrollMode::Instant);
3162 #ifdef ACCESSIBILITY
3163 if (anchorTarget) {
3164 if (nsAccessibilityService* accService = GetAccessibilityService()) {
3165 accService->NotifyOfAnchorJumpTo(anchorTarget);
3168 #endif // #ifdef ACCESSIBILITY
3170 return rv;
3173 nsresult PresShell::ScrollToAnchor() {
3174 nsCOMPtr<nsIContent> lastAnchor = mLastAnchorScrolledTo.forget();
3175 if (!lastAnchor) {
3176 return NS_OK;
3179 NS_ASSERTION(mDidInitialize, "should have done initial reflow by now");
3180 nsIScrollableFrame* rootScroll = GetRootScrollFrameAsScrollable();
3181 if (!rootScroll ||
3182 mLastAnchorScrollPositionY != rootScroll->GetScrollPosition().y) {
3183 return NS_OK;
3185 return ScrollContentIntoView(lastAnchor,
3186 ScrollAxis(kScrollToTop, WhenToScroll::Always),
3187 ScrollAxis(), ScrollFlags::AnchorScrollFlags);
3191 * Helper (per-continuation) for ScrollContentIntoView.
3193 * @param aContainerFrame [in] the frame which aRect is relative to
3194 * @param aFrame [in] Frame whose bounds should be unioned
3195 * @param aUseWholeLineHeightForInlines [in] if true, then for inline frames
3196 * we should include the top of the line in the added rectangle
3197 * @param aRect [inout] rect into which its bounds should be unioned
3198 * @param aHaveRect [inout] whether aRect contains data yet
3199 * @param aPrevBlock [inout] the block aLines is a line iterator for
3200 * @param aLines [inout] the line iterator we're using
3201 * @param aCurLine [inout] the line to start looking from in this iterator
3203 static void AccumulateFrameBounds(nsIFrame* aContainerFrame, nsIFrame* aFrame,
3204 bool aUseWholeLineHeightForInlines,
3205 nsRect& aRect, bool& aHaveRect,
3206 nsIFrame*& aPrevBlock,
3207 nsAutoLineIterator& aLines,
3208 int32_t& aCurLine) {
3209 nsIFrame* frame = aFrame;
3210 nsRect frameBounds = nsRect(nsPoint(0, 0), aFrame->GetSize());
3212 // If this is an inline frame and either the bounds height is 0 (quirks
3213 // layout model) or aUseWholeLineHeightForInlines is set, we need to
3214 // change the top of the bounds to include the whole line.
3215 if (frameBounds.height == 0 || aUseWholeLineHeightForInlines) {
3216 nsIFrame* prevFrame = aFrame;
3217 nsIFrame* f = aFrame;
3219 while (f && f->IsFrameOfType(nsIFrame::eLineParticipant) &&
3220 !f->IsTransformed() && !f->IsAbsPosContainingBlock()) {
3221 prevFrame = f;
3222 f = prevFrame->GetParent();
3225 if (f != aFrame && f && f->IsBlockFrame()) {
3226 // find the line containing aFrame and increase the top of |offset|.
3227 if (f != aPrevBlock) {
3228 aLines = f->GetLineIterator();
3229 aPrevBlock = f;
3230 aCurLine = 0;
3232 if (aLines) {
3233 int32_t index = aLines->FindLineContaining(prevFrame, aCurLine);
3234 if (index >= 0) {
3235 aCurLine = index;
3236 nsIFrame* trash1;
3237 int32_t trash2;
3238 nsRect lineBounds;
3240 if (NS_SUCCEEDED(
3241 aLines->GetLine(index, &trash1, &trash2, lineBounds))) {
3242 frameBounds += frame->GetOffsetTo(f);
3243 frame = f;
3244 if (lineBounds.y < frameBounds.y) {
3245 frameBounds.height = frameBounds.YMost() - lineBounds.y;
3246 frameBounds.y = lineBounds.y;
3254 nsRect transformedBounds = nsLayoutUtils::TransformFrameRectToAncestor(
3255 frame, frameBounds, aContainerFrame);
3257 if (aHaveRect) {
3258 // We can't use nsRect::UnionRect since it drops empty rects on
3259 // the floor, and we need to include them. (Thus we need
3260 // aHaveRect to know when to drop the initial value on the floor.)
3261 aRect.UnionRectEdges(aRect, transformedBounds);
3262 } else {
3263 aHaveRect = true;
3264 aRect = transformedBounds;
3268 static bool ComputeNeedToScroll(WhenToScroll aWhenToScroll, nscoord aLineSize,
3269 nscoord aRectMin, nscoord aRectMax,
3270 nscoord aViewMin, nscoord aViewMax) {
3271 // See how the rect should be positioned vertically
3272 if (WhenToScroll::Always == aWhenToScroll) {
3273 // The caller wants the frame as visible as possible
3274 return true;
3275 } else if (WhenToScroll::IfNotVisible == aWhenToScroll) {
3276 // Scroll only if no part of the frame is visible in this view
3277 return aRectMax - aLineSize <= aViewMin || aRectMin + aLineSize >= aViewMax;
3278 } else if (WhenToScroll::IfNotFullyVisible == aWhenToScroll) {
3279 // Scroll only if part of the frame is hidden and more can fit in view
3280 return !(aRectMin >= aViewMin && aRectMax <= aViewMax) &&
3281 std::min(aViewMax, aRectMax) - std::max(aRectMin, aViewMin) <
3282 aViewMax - aViewMin;
3284 return false;
3287 static nscoord ComputeWhereToScroll(WhereToScroll aWhereToScroll,
3288 nscoord aOriginalCoord, nscoord aRectMin,
3289 nscoord aRectMax, nscoord aViewMin,
3290 nscoord aViewMax, nscoord* aRangeMin,
3291 nscoord* aRangeMax) {
3292 nscoord resultCoord = aOriginalCoord;
3293 nscoord scrollPortLength = aViewMax - aViewMin;
3294 if (kScrollMinimum == aWhereToScroll) {
3295 // Scroll the minimum amount necessary to show as much as possible of the
3296 // frame. If the frame is too large, don't hide any initially visible part
3297 // of it.
3298 nscoord min = std::min(aRectMin, aRectMax - scrollPortLength);
3299 nscoord max = std::max(aRectMin, aRectMax - scrollPortLength);
3300 resultCoord = std::min(std::max(aOriginalCoord, min), max);
3301 } else {
3302 nscoord frameAlignCoord = NSToCoordRound(
3303 aRectMin + (aRectMax - aRectMin) * (aWhereToScroll / 100.0f));
3304 resultCoord = NSToCoordRound(frameAlignCoord -
3305 scrollPortLength * (aWhereToScroll / 100.0f));
3307 // Force the scroll range to extend to include resultCoord.
3308 *aRangeMin = std::min(resultCoord, aRectMax - scrollPortLength);
3309 *aRangeMax = std::max(resultCoord, aRectMin);
3310 return resultCoord;
3314 * This function takes a scrollable frame, a rect in the coordinate system
3315 * of the scrolled frame, and a desired percentage-based scroll
3316 * position and attempts to scroll the rect to that position in the
3317 * visual viewport.
3319 * This needs to work even if aRect has a width or height of zero.
3321 static void ScrollToShowRect(nsIScrollableFrame* aFrameAsScrollable,
3322 const nsRect& aRect, ScrollAxis aVertical,
3323 ScrollAxis aHorizontal, ScrollFlags aScrollFlags) {
3324 nsPoint scrollPt = aFrameAsScrollable->GetVisualViewportOffset();
3325 nsRect visibleRect(scrollPt, aFrameAsScrollable->GetVisualViewportSize());
3327 nsSize lineSize;
3328 // Don't call GetLineScrollAmount unless we actually need it. Not only
3329 // does this save time, but it's not safe to call GetLineScrollAmount
3330 // during reflow (because it depends on font size inflation and doesn't
3331 // use the in-reflow-safe font-size inflation path). If we did call it,
3332 // it would assert and possible give the wrong result.
3333 if (aVertical.mWhenToScroll == WhenToScroll::IfNotVisible ||
3334 aHorizontal.mWhenToScroll == WhenToScroll::IfNotVisible) {
3335 lineSize = aFrameAsScrollable->GetLineScrollAmount();
3337 ScrollStyles ss = aFrameAsScrollable->GetScrollStyles();
3338 nsRect allowedRange(scrollPt, nsSize(0, 0));
3339 bool needToScroll = false;
3340 uint32_t directions = aFrameAsScrollable->GetAvailableScrollingDirections();
3342 if (((aScrollFlags & ScrollFlags::ScrollOverflowHidden) ||
3343 ss.mVertical != StyleOverflow::Hidden) &&
3344 (!aVertical.mOnlyIfPerceivedScrollableDirection ||
3345 (directions & nsIScrollableFrame::VERTICAL))) {
3346 if (ComputeNeedToScroll(aVertical.mWhenToScroll, lineSize.height, aRect.y,
3347 aRect.YMost(), visibleRect.y,
3348 visibleRect.YMost())) {
3349 nscoord maxHeight;
3350 scrollPt.y = ComputeWhereToScroll(
3351 aVertical.mWhereToScroll, scrollPt.y, aRect.y, aRect.YMost(),
3352 visibleRect.y, visibleRect.YMost(), &allowedRange.y, &maxHeight);
3353 allowedRange.height = maxHeight - allowedRange.y;
3354 needToScroll = true;
3358 if (((aScrollFlags & ScrollFlags::ScrollOverflowHidden) ||
3359 ss.mHorizontal != StyleOverflow::Hidden) &&
3360 (!aHorizontal.mOnlyIfPerceivedScrollableDirection ||
3361 (directions & nsIScrollableFrame::HORIZONTAL))) {
3362 if (ComputeNeedToScroll(aHorizontal.mWhenToScroll, lineSize.width, aRect.x,
3363 aRect.XMost(), visibleRect.x,
3364 visibleRect.XMost())) {
3365 nscoord maxWidth;
3366 scrollPt.x = ComputeWhereToScroll(
3367 aHorizontal.mWhereToScroll, scrollPt.x, aRect.x, aRect.XMost(),
3368 visibleRect.x, visibleRect.XMost(), &allowedRange.x, &maxWidth);
3369 allowedRange.width = maxWidth - allowedRange.x;
3370 needToScroll = true;
3374 // If we don't need to scroll, then don't try since it might cancel
3375 // a current smooth scroll operation.
3376 if (needToScroll) {
3377 ScrollMode scrollMode = ScrollMode::Instant;
3378 bool autoBehaviorIsSmooth = aFrameAsScrollable->IsSmoothScroll();
3379 bool smoothScroll = (aScrollFlags & ScrollFlags::ScrollSmooth) ||
3380 ((aScrollFlags & ScrollFlags::ScrollSmoothAuto) &&
3381 autoBehaviorIsSmooth);
3382 if (StaticPrefs::layout_css_scroll_behavior_enabled() && smoothScroll) {
3383 scrollMode = ScrollMode::SmoothMsd;
3385 nsIFrame* frame = do_QueryFrame(aFrameAsScrollable);
3386 AutoWeakFrame weakFrame(frame);
3387 aFrameAsScrollable->ScrollTo(scrollPt, scrollMode, &allowedRange,
3388 aScrollFlags & ScrollFlags::ScrollSnap
3389 ? nsIScrollbarMediator::ENABLE_SNAP
3390 : nsIScrollbarMediator::DISABLE_SNAP);
3391 if (!weakFrame.IsAlive()) {
3392 return;
3395 // If this is the RCD-RSF, also call ScrollToVisual() since we want to
3396 // scroll the rect into view visually, and that may require scrolling
3397 // the visual viewport in scenarios where there is not enough layout
3398 // scroll range.
3399 if (aFrameAsScrollable->IsRootScrollFrameOfDocument() &&
3400 frame->PresShell()->GetPresContext()->IsRootContentDocument()) {
3401 frame->PresShell()->ScrollToVisual(scrollPt, FrameMetrics::eMainThread,
3402 scrollMode);
3407 nsresult PresShell::ScrollContentIntoView(nsIContent* aContent,
3408 ScrollAxis aVertical,
3409 ScrollAxis aHorizontal,
3410 ScrollFlags aScrollFlags) {
3411 NS_ENSURE_TRUE(aContent, NS_ERROR_NULL_POINTER);
3412 RefPtr<Document> composedDoc = aContent->GetComposedDoc();
3413 NS_ENSURE_STATE(composedDoc);
3415 NS_ASSERTION(mDidInitialize, "should have done initial reflow by now");
3417 if (mContentToScrollTo) {
3418 mContentToScrollTo->DeleteProperty(nsGkAtoms::scrolling);
3420 mContentToScrollTo = aContent;
3421 ScrollIntoViewData* data = new ScrollIntoViewData();
3422 data->mContentScrollVAxis = aVertical;
3423 data->mContentScrollHAxis = aHorizontal;
3424 data->mContentToScrollToFlags = aScrollFlags;
3425 if (NS_FAILED(mContentToScrollTo->SetProperty(
3426 nsGkAtoms::scrolling, data,
3427 nsINode::DeleteProperty<PresShell::ScrollIntoViewData>))) {
3428 mContentToScrollTo = nullptr;
3431 // Flush layout and attempt to scroll in the process.
3432 if (PresShell* presShell = composedDoc->GetPresShell()) {
3433 presShell->SetNeedLayoutFlush();
3435 composedDoc->FlushPendingNotifications(FlushType::InterruptibleLayout);
3437 // If mContentToScrollTo is non-null, that means we interrupted the reflow
3438 // (or suppressed it altogether because we're suppressing interruptible
3439 // flushes right now) and won't necessarily get the position correct, but do
3440 // a best-effort scroll here. The other option would be to do this inside
3441 // FlushPendingNotifications, but I'm not sure the repeated scrolling that
3442 // could trigger if reflows keep getting interrupted would be more desirable
3443 // than a single best-effort scroll followed by one final scroll on the first
3444 // completed reflow.
3445 if (mContentToScrollTo) {
3446 DoScrollContentIntoView();
3448 return NS_OK;
3451 void PresShell::DoScrollContentIntoView() {
3452 NS_ASSERTION(mDidInitialize, "should have done initial reflow by now");
3454 nsIFrame* frame = mContentToScrollTo->GetPrimaryFrame();
3455 if (!frame) {
3456 mContentToScrollTo->DeleteProperty(nsGkAtoms::scrolling);
3457 mContentToScrollTo = nullptr;
3458 return;
3461 if (frame->GetStateBits() & NS_FRAME_FIRST_REFLOW) {
3462 // The reflow flush before this scroll got interrupted, and this frame's
3463 // coords and size are all zero, and it has no content showing anyway.
3464 // Don't bother scrolling to it. We'll try again when we finish up layout.
3465 return;
3468 // Make sure we skip 'frame' ... if it's scrollable, we should use its
3469 // scrollable ancestor as the container.
3470 nsIFrame* container = nsLayoutUtils::GetClosestFrameOfType(
3471 frame->GetParent(), LayoutFrameType::Scroll);
3472 if (!container) {
3473 // nothing can be scrolled
3474 return;
3477 ScrollIntoViewData* data = static_cast<ScrollIntoViewData*>(
3478 mContentToScrollTo->GetProperty(nsGkAtoms::scrolling));
3479 if (MOZ_UNLIKELY(!data)) {
3480 mContentToScrollTo = nullptr;
3481 return;
3484 // Get the scroll-margin here since |frame| is going to be changed to iterate
3485 // over all continuation frames below.
3486 nsMargin scrollMargin;
3487 if (!(data->mContentToScrollToFlags & ScrollFlags::IgnoreMarginAndPadding)) {
3488 scrollMargin = frame->StyleMargin()->GetScrollMargin();
3491 // This is a two-step process.
3492 // Step 1: Find the bounds of the rect we want to scroll into view. For
3493 // example, for an inline frame we may want to scroll in the whole
3494 // line, or we may want to scroll multiple lines into view.
3495 // Step 2: Walk container frame and its ancestors and scroll them
3496 // appropriately.
3497 // frameBounds is relative to container. We're assuming
3498 // that scrollframes don't split so every continuation of frame will
3499 // be a descendant of container. (Things would still mostly work
3500 // even if that assumption was false.)
3501 nsRect frameBounds;
3502 bool haveRect = false;
3503 bool useWholeLineHeightForInlines = data->mContentScrollVAxis.mWhenToScroll !=
3504 WhenToScroll::IfNotFullyVisible;
3505 // Reuse the same line iterator across calls to AccumulateFrameBounds. We set
3506 // it every time we detect a new block (stored in prevBlock).
3507 nsIFrame* prevBlock = nullptr;
3508 nsAutoLineIterator lines;
3509 // The last line we found a continuation on in |lines|. We assume that later
3510 // continuations cannot come on earlier lines.
3511 int32_t curLine = 0;
3512 do {
3513 AccumulateFrameBounds(container, frame, useWholeLineHeightForInlines,
3514 frameBounds, haveRect, prevBlock, lines, curLine);
3515 } while ((frame = frame->GetNextContinuation()));
3517 frameBounds.Inflate(scrollMargin);
3519 ScrollFrameRectIntoView(container, frameBounds, data->mContentScrollVAxis,
3520 data->mContentScrollHAxis,
3521 data->mContentToScrollToFlags);
3524 bool PresShell::ScrollFrameRectIntoView(nsIFrame* aFrame, const nsRect& aRect,
3525 ScrollAxis aVertical,
3526 ScrollAxis aHorizontal,
3527 ScrollFlags aScrollFlags) {
3528 bool didScroll = false;
3529 // This function needs to work even if rect has a width or height of 0.
3530 nsRect rect = aRect;
3531 nsIFrame* container = aFrame;
3532 // Walk up the frame hierarchy scrolling the rect into view and
3533 // keeping rect relative to container
3534 do {
3535 nsIScrollableFrame* sf = do_QueryFrame(container);
3536 if (sf) {
3537 nsPoint oldPosition = sf->GetScrollPosition();
3538 nsRect targetRect = rect;
3539 // Inflate the scrolled rect by the container's padding in each dimension,
3540 // unless we have 'overflow-clip-box-*: content-box' in that dimension.
3541 auto* disp = container->StyleDisplay();
3542 if (disp->mOverflowClipBoxBlock == StyleOverflowClipBox::ContentBox ||
3543 disp->mOverflowClipBoxInline == StyleOverflowClipBox::ContentBox) {
3544 WritingMode wm = container->GetWritingMode();
3545 bool cbH = (wm.IsVertical() ? disp->mOverflowClipBoxBlock
3546 : disp->mOverflowClipBoxInline) ==
3547 StyleOverflowClipBox::ContentBox;
3548 bool cbV = (wm.IsVertical() ? disp->mOverflowClipBoxInline
3549 : disp->mOverflowClipBoxBlock) ==
3550 StyleOverflowClipBox::ContentBox;
3551 nsMargin padding = container->GetUsedPadding();
3552 if (!cbH) {
3553 padding.left = padding.right = nscoord(0);
3555 if (!cbV) {
3556 padding.top = padding.bottom = nscoord(0);
3558 targetRect.Inflate(padding);
3561 targetRect -= sf->GetScrolledFrame()->GetPosition();
3562 if (!(aScrollFlags & ScrollFlags::IgnoreMarginAndPadding)) {
3563 nsMargin scrollPadding = sf->GetScrollPadding();
3564 targetRect.Inflate(scrollPadding);
3565 targetRect = targetRect.Intersect(sf->GetScrolledRect());
3569 AutoWeakFrame wf(container);
3570 ScrollToShowRect(sf, targetRect, aVertical, aHorizontal, aScrollFlags);
3571 if (!wf.IsAlive()) {
3572 return didScroll;
3576 nsPoint newPosition = sf->LastScrollDestination();
3577 // If the scroll position increased, that means our content moved up,
3578 // so our rect's offset should decrease
3579 rect += oldPosition - newPosition;
3581 if (oldPosition != newPosition) {
3582 didScroll = true;
3585 // only scroll one container when this flag is set
3586 if (aScrollFlags & ScrollFlags::ScrollFirstAncestorOnly) {
3587 break;
3590 nsIFrame* parent;
3591 if (container->IsTransformed()) {
3592 container->GetTransformMatrix(nullptr, &parent);
3593 rect =
3594 nsLayoutUtils::TransformFrameRectToAncestor(container, rect, parent);
3595 } else {
3596 rect += container->GetPosition();
3597 parent = container->GetParent();
3599 if (!parent && !(aScrollFlags & ScrollFlags::ScrollNoParentFrames)) {
3600 nsPoint extraOffset(0, 0);
3601 int32_t APD = container->PresContext()->AppUnitsPerDevPixel();
3602 parent = nsLayoutUtils::GetCrossDocParentFrame(container, &extraOffset);
3603 if (parent) {
3604 int32_t parentAPD = parent->PresContext()->AppUnitsPerDevPixel();
3605 rect = rect.ScaleToOtherAppUnitsRoundOut(APD, parentAPD);
3606 rect += extraOffset;
3607 } else {
3608 nsCOMPtr<nsIDocShell> docShell =
3609 container->PresContext()->GetDocShell();
3610 if (BrowserChild* browserChild = BrowserChild::GetFrom(docShell)) {
3611 // Defer to the parent document if this is an out-of-process iframe.
3612 Unused << browserChild->SendScrollRectIntoView(
3613 rect, aVertical, aHorizontal, aScrollFlags, APD);
3617 container = parent;
3618 } while (container);
3620 return didScroll;
3623 void PresShell::ScheduleViewManagerFlush(PaintType aType) {
3624 if (MOZ_UNLIKELY(mIsDestroying)) {
3625 return;
3628 if (aType == PaintType::DelayedCompress) {
3629 // Delay paint for 1 second.
3630 static const uint32_t kPaintDelayPeriod = 1000;
3631 if (!mDelayedPaintTimer) {
3632 nsTimerCallbackFunc PaintTimerCallBack = [](nsITimer* aTimer,
3633 void* aClosure) {
3634 // The passed-in PresShell is always alive here. Because if PresShell
3635 // died, mDelayedPaintTimer->Cancel() would be called during the
3636 // destruction and this callback would never be invoked.
3637 auto self = static_cast<PresShell*>(aClosure);
3638 self->SetNextPaintCompressed();
3639 self->ScheduleViewManagerFlush();
3642 NS_NewTimerWithFuncCallback(
3643 getter_AddRefs(mDelayedPaintTimer), PaintTimerCallBack, this,
3644 kPaintDelayPeriod, nsITimer::TYPE_ONE_SHOT, "PaintTimerCallBack",
3645 mDocument->EventTargetFor(TaskCategory::Other));
3647 return;
3650 nsPresContext* presContext = GetPresContext();
3651 if (presContext) {
3652 presContext->RefreshDriver()->ScheduleViewManagerFlush();
3654 SetNeedLayoutFlush();
3657 void PresShell::DispatchSynthMouseMove(WidgetGUIEvent* aEvent) {
3658 AUTO_PROFILER_TRACING_DOCSHELL("Paint", "DispatchSynthMouseMove", GRAPHICS,
3659 mPresContext->GetDocShell());
3660 nsEventStatus status = nsEventStatus_eIgnore;
3661 nsView* targetView = nsView::GetViewFor(aEvent->mWidget);
3662 if (!targetView) return;
3663 RefPtr<nsViewManager> viewManager = targetView->GetViewManager();
3664 viewManager->DispatchEvent(aEvent, targetView, &status);
3667 void PresShell::ClearMouseCaptureOnView(nsView* aView) {
3668 if (sCapturingContentInfo.mContent) {
3669 if (aView) {
3670 // if a view was specified, ensure that the captured content is within
3671 // this view.
3672 nsIFrame* frame = sCapturingContentInfo.mContent->GetPrimaryFrame();
3673 if (frame) {
3674 nsView* view = frame->GetClosestView();
3675 // if there is no view, capturing won't be handled any more, so
3676 // just release the capture.
3677 if (view) {
3678 do {
3679 if (view == aView) {
3680 sCapturingContentInfo.mContent = nullptr;
3681 // the view containing the captured content likely disappeared so
3682 // disable capture for now.
3683 sCapturingContentInfo.mAllowed = false;
3684 break;
3687 view = view->GetParent();
3688 } while (view);
3689 // return if the view wasn't found
3690 return;
3695 sCapturingContentInfo.mContent = nullptr;
3698 // disable mouse capture until the next mousedown as a dialog has opened
3699 // or a drag has started. Otherwise, someone could start capture during
3700 // the modal dialog or drag.
3701 sCapturingContentInfo.mAllowed = false;
3704 void PresShell::ClearMouseCapture(nsIFrame* aFrame) {
3705 if (!sCapturingContentInfo.mContent) {
3706 sCapturingContentInfo.mAllowed = false;
3707 return;
3710 // null frame argument means clear the capture
3711 if (!aFrame) {
3712 sCapturingContentInfo.mContent = nullptr;
3713 sCapturingContentInfo.mAllowed = false;
3714 return;
3717 nsIFrame* capturingFrame = sCapturingContentInfo.mContent->GetPrimaryFrame();
3718 if (!capturingFrame) {
3719 sCapturingContentInfo.mContent = nullptr;
3720 sCapturingContentInfo.mAllowed = false;
3721 return;
3724 if (nsLayoutUtils::IsAncestorFrameCrossDoc(aFrame, capturingFrame)) {
3725 sCapturingContentInfo.mContent = nullptr;
3726 sCapturingContentInfo.mAllowed = false;
3730 nsresult PresShell::CaptureHistoryState(nsILayoutHistoryState** aState) {
3731 MOZ_ASSERT(nullptr != aState, "null state pointer");
3733 // We actually have to mess with the docshell here, since we want to
3734 // store the state back in it.
3735 // XXXbz this isn't really right, since this is being called in the
3736 // content viewer's Hide() method... by that point the docshell's
3737 // state could be wrong. We should sort out a better ownership
3738 // model for the layout history state.
3739 nsCOMPtr<nsIDocShell> docShell(mPresContext->GetDocShell());
3740 if (!docShell) return NS_ERROR_FAILURE;
3742 nsCOMPtr<nsILayoutHistoryState> historyState;
3743 docShell->GetLayoutHistoryState(getter_AddRefs(historyState));
3744 if (!historyState) {
3745 // Create the document state object
3746 historyState = NS_NewLayoutHistoryState();
3747 docShell->SetLayoutHistoryState(historyState);
3750 *aState = historyState;
3751 NS_IF_ADDREF(*aState);
3753 // Capture frame state for the entire frame hierarchy
3754 nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
3755 if (!rootFrame) return NS_OK;
3757 mFrameConstructor->CaptureFrameState(rootFrame, historyState);
3759 return NS_OK;
3762 void PresShell::ScheduleBeforeFirstPaint() {
3763 if (!mDocument->IsResourceDoc()) {
3764 // Notify observers that a new page is about to be drawn. Execute this
3765 // as soon as it is safe to run JS, which is guaranteed to be before we
3766 // go back to the event loop and actually draw the page.
3767 MOZ_LOG(gLog, LogLevel::Debug,
3768 ("PresShell::ScheduleBeforeFirstPaint this=%p", this));
3770 nsContentUtils::AddScriptRunner(
3771 new nsBeforeFirstPaintDispatcher(mDocument));
3775 void PresShell::UnsuppressAndInvalidate() {
3776 // Note: We ignore the EnsureVisible check for resource documents, because
3777 // they won't have a docshell, so they'll always fail EnsureVisible.
3778 if ((!mDocument->IsResourceDoc() && !mPresContext->EnsureVisible()) ||
3779 mHaveShutDown) {
3780 // No point; we're about to be torn down anyway.
3781 return;
3784 ScheduleBeforeFirstPaint();
3786 mPaintingSuppressed = false;
3787 nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
3788 if (rootFrame) {
3789 // let's assume that outline on a root frame is not supported
3790 rootFrame->InvalidateFrame();
3793 // now that painting is unsuppressed, focus may be set on the document
3794 if (nsPIDOMWindowOuter* win = mDocument->GetWindow()) win->SetReadyForFocus();
3796 if (!mHaveShutDown) {
3797 SynthesizeMouseMove(false);
3798 ScheduleApproximateFrameVisibilityUpdateNow();
3802 void PresShell::UnsuppressPainting() {
3803 if (mPaintSuppressionTimer) {
3804 mPaintSuppressionTimer->Cancel();
3805 mPaintSuppressionTimer = nullptr;
3808 if (mIsDocumentGone || !mPaintingSuppressed) return;
3810 // If we have reflows pending, just wait until we process
3811 // the reflows and get all the frames where we want them
3812 // before actually unlocking the painting. Otherwise
3813 // go ahead and unlock now.
3814 if (!mDirtyRoots.IsEmpty())
3815 mShouldUnsuppressPainting = true;
3816 else
3817 UnsuppressAndInvalidate();
3820 // Post a request to handle an arbitrary callback after reflow has finished.
3821 nsresult PresShell::PostReflowCallback(nsIReflowCallback* aCallback) {
3822 void* result = AllocateByObjectID(eArenaObjectID_nsCallbackEventRequest,
3823 sizeof(nsCallbackEventRequest));
3824 nsCallbackEventRequest* request = (nsCallbackEventRequest*)result;
3826 request->callback = aCallback;
3827 request->next = nullptr;
3829 if (mLastCallbackEventRequest) {
3830 mLastCallbackEventRequest = mLastCallbackEventRequest->next = request;
3831 } else {
3832 mFirstCallbackEventRequest = request;
3833 mLastCallbackEventRequest = request;
3836 return NS_OK;
3839 void PresShell::CancelReflowCallback(nsIReflowCallback* aCallback) {
3840 nsCallbackEventRequest* before = nullptr;
3841 nsCallbackEventRequest* node = mFirstCallbackEventRequest;
3842 while (node) {
3843 nsIReflowCallback* callback = node->callback;
3845 if (callback == aCallback) {
3846 nsCallbackEventRequest* toFree = node;
3847 if (node == mFirstCallbackEventRequest) {
3848 node = node->next;
3849 mFirstCallbackEventRequest = node;
3850 NS_ASSERTION(before == nullptr, "impossible");
3851 } else {
3852 node = node->next;
3853 before->next = node;
3856 if (toFree == mLastCallbackEventRequest) {
3857 mLastCallbackEventRequest = before;
3860 FreeByObjectID(eArenaObjectID_nsCallbackEventRequest, toFree);
3861 } else {
3862 before = node;
3863 node = node->next;
3868 void PresShell::CancelPostedReflowCallbacks() {
3869 while (mFirstCallbackEventRequest) {
3870 nsCallbackEventRequest* node = mFirstCallbackEventRequest;
3871 mFirstCallbackEventRequest = node->next;
3872 if (!mFirstCallbackEventRequest) {
3873 mLastCallbackEventRequest = nullptr;
3875 nsIReflowCallback* callback = node->callback;
3876 FreeByObjectID(eArenaObjectID_nsCallbackEventRequest, node);
3877 if (callback) {
3878 callback->ReflowCallbackCanceled();
3883 void PresShell::HandlePostedReflowCallbacks(bool aInterruptible) {
3884 bool shouldFlush = false;
3886 while (mFirstCallbackEventRequest) {
3887 nsCallbackEventRequest* node = mFirstCallbackEventRequest;
3888 mFirstCallbackEventRequest = node->next;
3889 if (!mFirstCallbackEventRequest) {
3890 mLastCallbackEventRequest = nullptr;
3892 nsIReflowCallback* callback = node->callback;
3893 FreeByObjectID(eArenaObjectID_nsCallbackEventRequest, node);
3894 if (callback) {
3895 if (callback->ReflowFinished()) {
3896 shouldFlush = true;
3901 FlushType flushType =
3902 aInterruptible ? FlushType::InterruptibleLayout : FlushType::Layout;
3903 if (shouldFlush && !mIsDestroying) {
3904 FlushPendingNotifications(flushType);
3908 bool PresShell::IsSafeToFlush() const {
3909 // Not safe if we are getting torn down, reflowing, or in the middle of frame
3910 // construction.
3911 if (mIsReflowing || mChangeNestCount || mIsDestroying) {
3912 return false;
3915 // Not safe if we are painting
3916 if (nsViewManager* viewManager = GetViewManager()) {
3917 bool isPainting = false;
3918 viewManager->IsPainting(isPainting);
3919 if (isPainting) {
3920 return false;
3924 return true;
3927 void PresShell::NotifyFontFaceSetOnRefresh() {
3928 if (FontFaceSet* set = mDocument->GetFonts()) {
3929 set->DidRefresh();
3933 void PresShell::DoFlushPendingNotifications(FlushType aType) {
3934 // by default, flush animations if aType >= FlushType::Style
3935 mozilla::ChangesToFlush flush(aType, aType >= FlushType::Style);
3936 FlushPendingNotifications(flush);
3939 #ifdef DEBUG
3940 static void AssertFrameSubtreeIsSane(const nsIFrame& aRoot) {
3941 if (const nsIContent* content = aRoot.GetContent()) {
3942 MOZ_ASSERT(content->GetFlattenedTreeParentNodeForStyle(),
3943 "Node not in the flattened tree still has a frame?");
3946 nsIFrame::ChildListIterator childLists(&aRoot);
3947 for (; !childLists.IsDone(); childLists.Next()) {
3948 for (const nsIFrame* child : childLists.CurrentList()) {
3949 AssertFrameSubtreeIsSane(*child);
3953 #endif
3955 static inline void AssertFrameTreeIsSane(const PresShell& aPresShell) {
3956 #ifdef DEBUG
3957 if (const nsIFrame* root = aPresShell.GetRootFrame()) {
3958 AssertFrameSubtreeIsSane(*root);
3960 #endif
3963 void PresShell::DoFlushPendingNotifications(mozilla::ChangesToFlush aFlush) {
3964 // FIXME(emilio, bug 1530177): Turn into a release assert when bug 1530188 and
3965 // bug 1530190 are fixed.
3966 MOZ_DIAGNOSTIC_ASSERT(!mForbiddenToFlush, "This is bad!");
3968 // Per our API contract, hold a strong ref to ourselves until we return.
3969 RefPtr<PresShell> kungFuDeathGrip = this;
3972 * VERY IMPORTANT: If you add some sort of new flushing to this
3973 * method, make sure to add the relevant SetNeedLayoutFlush or
3974 * SetNeedStyleFlush calls on the shell.
3976 FlushType flushType = aFlush.mFlushType;
3978 MOZ_ASSERT(NeedFlush(flushType), "Why did we get called?");
3980 #ifdef MOZ_GECKO_PROFILER
3981 // clang-format off
3982 static const EnumeratedArray<FlushType, FlushType::Count, const char*>
3983 flushTypeNames = {
3985 "Event",
3986 "Content",
3987 "ContentAndNotify",
3988 "Style",
3989 // As far as the profiler is concerned, EnsurePresShellInitAndFrames and
3990 // Frames are the same
3991 "Style",
3992 "Style",
3993 "InterruptibleLayout",
3994 "Layout",
3995 "Display"
3997 // clang-format on
3998 AUTO_PROFILER_LABEL_DYNAMIC_CSTR("PresShell::DoFlushPendingNotifications",
3999 LAYOUT, flushTypeNames[flushType]);
4000 #endif
4002 #ifdef ACCESSIBILITY
4003 # ifdef DEBUG
4004 if (nsAccessibilityService* accService = GetAccService()) {
4005 NS_ASSERTION(!accService->IsProcessingRefreshDriverNotification(),
4006 "Flush during accessible tree update!");
4008 # endif
4009 #endif
4011 NS_ASSERTION(flushType >= FlushType::Style, "Why did we get called?");
4013 mNeedStyleFlush = false;
4014 mNeedThrottledAnimationFlush =
4015 mNeedThrottledAnimationFlush && !aFlush.mFlushAnimations;
4016 mNeedLayoutFlush =
4017 mNeedLayoutFlush && (flushType < FlushType::InterruptibleLayout);
4019 bool isSafeToFlush = IsSafeToFlush();
4021 // If layout could possibly trigger scripts, then it's only safe to flush if
4022 // it's safe to run script.
4023 bool hasHadScriptObject;
4024 if (mDocument->GetScriptHandlingObject(hasHadScriptObject) ||
4025 hasHadScriptObject) {
4026 isSafeToFlush = isSafeToFlush && nsContentUtils::IsSafeToRunScript();
4029 // Don't flush if the doc is already in the bfcache.
4030 if (MOZ_UNLIKELY(mDocument->GetPresShell() != this)) {
4031 MOZ_DIAGNOSTIC_ASSERT(!mDocument->GetPresShell(),
4032 "Where did this shell come from?");
4033 isSafeToFlush = false;
4036 MOZ_DIAGNOSTIC_ASSERT(!mIsDestroying || !isSafeToFlush);
4037 MOZ_DIAGNOSTIC_ASSERT(mIsDestroying || mViewManager);
4038 MOZ_DIAGNOSTIC_ASSERT(mIsDestroying || mDocument->HasShellOrBFCacheEntry());
4040 // Make sure the view manager stays alive.
4041 RefPtr<nsViewManager> viewManager = mViewManager;
4042 bool didStyleFlush = false;
4043 bool didLayoutFlush = false;
4044 if (isSafeToFlush) {
4045 // Record that we are in a flush, so that our optimization in
4046 // Document::FlushPendingNotifications doesn't skip any re-entrant
4047 // calls to us. Otherwise, we might miss some needed flushes, since
4048 // we clear mNeedStyleFlush / mNeedLayoutFlush here at the top of
4049 // the function but we might not have done the work yet.
4050 AutoRestore<bool> guard(mInFlush);
4051 mInFlush = true;
4053 // We need to make sure external resource documents are flushed too (for
4054 // example, svg filters that reference a filter in an external document
4055 // need the frames in the external document to be constructed for the
4056 // filter to work). We only need external resources to be flushed when the
4057 // main document is flushing >= FlushType::Frames, so we flush external
4058 // resources here instead of Document::FlushPendingNotifications.
4059 mDocument->FlushExternalResources(flushType);
4061 // Force flushing of any pending content notifications that might have
4062 // queued up while our event was pending. That will ensure that we don't
4063 // construct frames for content right now that's still waiting to be
4064 // notified on,
4065 mDocument->FlushPendingNotifications(FlushType::ContentAndNotify);
4067 mDocument->UpdateSVGUseElementShadowTrees();
4069 // Process pending restyles, since any flush of the presshell wants
4070 // up-to-date style data.
4071 if (MOZ_LIKELY(!mIsDestroying)) {
4072 viewManager->FlushDelayedResize(false);
4073 mPresContext->FlushPendingMediaFeatureValuesChanged();
4076 if (MOZ_LIKELY(!mIsDestroying)) {
4077 // Now that we have flushed media queries, update the rules before looking
4078 // up @font-face / @counter-style / @font-feature-values rules.
4079 StyleSet()->UpdateStylistIfNeeded();
4081 // Flush any pending update of the user font set, since that could
4082 // cause style changes (for updating ex/ch units, and to cause a
4083 // reflow).
4084 mDocument->FlushUserFontSet();
4086 mPresContext->FlushCounterStyles();
4088 mPresContext->FlushFontFeatureValues();
4090 // Flush any requested SMIL samples.
4091 if (mDocument->HasAnimationController()) {
4092 mDocument->GetAnimationController()->FlushResampleRequests();
4095 if (aFlush.mFlushAnimations && mPresContext->EffectCompositor()) {
4096 mPresContext->EffectCompositor()->PostRestyleForThrottledAnimations();
4100 // The FlushResampleRequests() above flushed style changes.
4101 if (MOZ_LIKELY(!mIsDestroying)) {
4102 nsAutoScriptBlocker scriptBlocker;
4103 #ifdef MOZ_GECKO_PROFILER
4104 nsCOMPtr<nsIDocShell> docShell = mPresContext->GetDocShell();
4105 DECLARE_DOCSHELL_AND_HISTORY_ID(docShell);
4106 AutoProfilerStyleMarker tracingStyleFlush(std::move(mStyleCause),
4107 docShellId, docShellHistoryId);
4108 #endif
4109 PerfStats::AutoMetricRecording<PerfStats::Metric::Styling> autoRecording;
4111 mPresContext->RestyleManager()->ProcessPendingRestyles();
4114 // Process whatever XBL constructors those restyles queued up. This
4115 // ensures that onload doesn't fire too early and that we won't do extra
4116 // reflows after those constructors run.
4117 if (MOZ_LIKELY(!mIsDestroying)) {
4118 mDocument->BindingManager()->ProcessAttachedQueue();
4121 // Now those constructors or events might have posted restyle
4122 // events. At the same time, we still need up-to-date style data.
4123 // In particular, reflow depends on style being completely up to
4124 // date. If it's not, then style reparenting, which can
4125 // happen during reflow, might suddenly pick up the new rules and
4126 // we'll end up with frames whose style doesn't match the frame
4127 // type.
4128 if (MOZ_LIKELY(!mIsDestroying)) {
4129 nsAutoScriptBlocker scriptBlocker;
4130 #ifdef MOZ_GECKO_PROFILER
4131 nsCOMPtr<nsIDocShell> docShell = mPresContext->GetDocShell();
4132 DECLARE_DOCSHELL_AND_HISTORY_ID(docShell);
4133 AutoProfilerStyleMarker tracingStyleFlush(std::move(mStyleCause),
4134 docShellId, docShellHistoryId);
4135 #endif
4136 PerfStats::AutoMetricRecording<PerfStats::Metric::Styling> autoRecording;
4138 mPresContext->RestyleManager()->ProcessPendingRestyles();
4139 // Clear mNeedStyleFlush here agagin to make this flag work properly for
4140 // optimization since the flag might have set in ProcessPendingRestyles().
4141 mNeedStyleFlush = false;
4144 AssertFrameTreeIsSane(*this);
4146 didStyleFlush = true;
4148 // There might be more pending constructors now, but we're not going to
4149 // worry about them. They can't be triggered during reflow, so we should
4150 // be good.
4152 if (flushType >= (SuppressInterruptibleReflows()
4153 ? FlushType::Layout
4154 : FlushType::InterruptibleLayout) &&
4155 !mIsDestroying) {
4156 didLayoutFlush = true;
4157 mFrameConstructor->RecalcQuotesAndCounters();
4158 if (ProcessReflowCommands(flushType < FlushType::Layout)) {
4159 if (mContentToScrollTo) {
4160 DoScrollContentIntoView();
4161 if (mContentToScrollTo) {
4162 mContentToScrollTo->DeleteProperty(nsGkAtoms::scrolling);
4163 mContentToScrollTo = nullptr;
4169 if (flushType >= FlushType::Layout) {
4170 if (!mIsDestroying) {
4171 viewManager->UpdateWidgetGeometry();
4176 if (!didStyleFlush && flushType >= FlushType::Style && !mIsDestroying) {
4177 SetNeedStyleFlush();
4178 if (aFlush.mFlushAnimations) {
4179 SetNeedThrottledAnimationFlush();
4183 if (!didLayoutFlush && flushType >= FlushType::InterruptibleLayout &&
4184 !mIsDestroying) {
4185 // We suppressed this flush either due to it not being safe to flush,
4186 // or due to SuppressInterruptibleReflows(). Either way, the
4187 // mNeedLayoutFlush flag needs to be re-set.
4188 SetNeedLayoutFlush();
4191 // Update flush counters
4192 if (didStyleFlush) {
4193 mFlushesPerTick[FlushKind::Style]++;
4196 if (didLayoutFlush) {
4197 mFlushesPerTick[FlushKind::Layout]++;
4200 // Record telemetry for the number of requests per each flush type.
4202 // Flushes happen as style or style+layout. This depends upon the `flushType`
4203 // where flushType >= InterruptibleLayout means flush layout and flushType >=
4204 // Style means flush style. We only report if didLayoutFlush or didStyleFlush
4205 // is true because we care if a flush really did take place. (Flush is guarded
4206 // by `isSafeToFlush == true`.)
4207 if (flushType >= FlushType::InterruptibleLayout && didLayoutFlush) {
4208 MOZ_ASSERT(didLayoutFlush == didStyleFlush);
4209 PingReqsPerFlushTelemetry(FlushKind::Layout);
4210 } else if (flushType >= FlushType::Style && didStyleFlush) {
4211 MOZ_ASSERT(!didLayoutFlush);
4212 PingReqsPerFlushTelemetry(FlushKind::Style);
4216 void PresShell::CharacterDataChanged(nsIContent* aContent,
4217 const CharacterDataChangeInfo& aInfo) {
4218 MOZ_ASSERT(!mIsDocumentGone, "Unexpected CharacterDataChanged");
4219 MOZ_ASSERT(aContent->OwnerDoc() == mDocument, "Unexpected document");
4221 nsAutoCauseReflowNotifier crNotifier(this);
4223 mPresContext->RestyleManager()->CharacterDataChanged(aContent, aInfo);
4224 mFrameConstructor->CharacterDataChanged(aContent, aInfo);
4227 void PresShell::ContentStateChanged(Document* aDocument, nsIContent* aContent,
4228 EventStates aStateMask) {
4229 MOZ_ASSERT(!mIsDocumentGone, "Unexpected ContentStateChanged");
4230 MOZ_ASSERT(aDocument == mDocument, "Unexpected aDocument");
4232 if (mDidInitialize) {
4233 nsAutoCauseReflowNotifier crNotifier(this);
4234 mPresContext->RestyleManager()->ContentStateChanged(aContent, aStateMask);
4238 void PresShell::DocumentStatesChanged(EventStates aStateMask) {
4239 MOZ_ASSERT(!mIsDocumentGone, "Unexpected DocumentStatesChanged");
4240 MOZ_ASSERT(mDocument);
4241 MOZ_ASSERT(!aStateMask.IsEmpty());
4243 if (mDidInitialize) {
4244 StyleSet()->InvalidateStyleForDocumentStateChanges(aStateMask);
4247 if (aStateMask.HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE)) {
4248 if (nsIFrame* root = mFrameConstructor->GetRootFrame()) {
4249 root->SchedulePaint();
4254 void PresShell::AttributeWillChange(Element* aElement, int32_t aNameSpaceID,
4255 nsAtom* aAttribute, int32_t aModType) {
4256 MOZ_ASSERT(!mIsDocumentGone, "Unexpected AttributeWillChange");
4257 MOZ_ASSERT(aElement->OwnerDoc() == mDocument, "Unexpected document");
4259 // XXXwaterson it might be more elegant to wait until after the
4260 // initial reflow to begin observing the document. That would
4261 // squelch any other inappropriate notifications as well.
4262 if (mDidInitialize) {
4263 nsAutoCauseReflowNotifier crNotifier(this);
4264 mPresContext->RestyleManager()->AttributeWillChange(aElement, aNameSpaceID,
4265 aAttribute, aModType);
4269 void PresShell::AttributeChanged(Element* aElement, int32_t aNameSpaceID,
4270 nsAtom* aAttribute, int32_t aModType,
4271 const nsAttrValue* aOldValue) {
4272 MOZ_ASSERT(!mIsDocumentGone, "Unexpected AttributeChanged");
4273 MOZ_ASSERT(aElement->OwnerDoc() == mDocument, "Unexpected document");
4275 // XXXwaterson it might be more elegant to wait until after the
4276 // initial reflow to begin observing the document. That would
4277 // squelch any other inappropriate notifications as well.
4278 if (mDidInitialize) {
4279 nsAutoCauseReflowNotifier crNotifier(this);
4280 mPresContext->RestyleManager()->AttributeChanged(
4281 aElement, aNameSpaceID, aAttribute, aModType, aOldValue);
4285 void PresShell::ContentAppended(nsIContent* aFirstNewContent) {
4286 MOZ_ASSERT(!mIsDocumentGone, "Unexpected ContentAppended");
4287 MOZ_ASSERT(aFirstNewContent->OwnerDoc() == mDocument, "Unexpected document");
4289 // We never call ContentAppended with a document as the container, so we can
4290 // assert that we have an nsIContent parent.
4291 MOZ_ASSERT(aFirstNewContent->GetParent());
4292 MOZ_ASSERT(aFirstNewContent->GetParent()->IsElement() ||
4293 aFirstNewContent->GetParent()->IsShadowRoot());
4295 if (!mDidInitialize) {
4296 return;
4299 nsAutoCauseReflowNotifier crNotifier(this);
4301 // Call this here so it only happens for real content mutations and
4302 // not cases when the frame constructor calls its own methods to force
4303 // frame reconstruction.
4304 mPresContext->RestyleManager()->ContentAppended(aFirstNewContent);
4306 mFrameConstructor->ContentAppended(
4307 aFirstNewContent, nsCSSFrameConstructor::InsertionKind::Async);
4310 void PresShell::ContentInserted(nsIContent* aChild) {
4311 MOZ_ASSERT(!mIsDocumentGone, "Unexpected ContentInserted");
4312 MOZ_ASSERT(aChild->OwnerDoc() == mDocument, "Unexpected document");
4314 if (!mDidInitialize) {
4315 return;
4318 nsAutoCauseReflowNotifier crNotifier(this);
4320 // Call this here so it only happens for real content mutations and
4321 // not cases when the frame constructor calls its own methods to force
4322 // frame reconstruction.
4323 mPresContext->RestyleManager()->ContentInserted(aChild);
4325 mFrameConstructor->ContentInserted(
4326 aChild, nullptr, nsCSSFrameConstructor::InsertionKind::Async);
4329 void PresShell::ContentRemoved(nsIContent* aChild,
4330 nsIContent* aPreviousSibling) {
4331 MOZ_ASSERT(!mIsDocumentGone, "Unexpected ContentRemoved");
4332 MOZ_ASSERT(aChild->OwnerDoc() == mDocument, "Unexpected document");
4333 nsINode* container = aChild->GetParentNode();
4335 // Notify the ESM that the content has been removed, so that
4336 // it can clean up any state related to the content.
4338 mPresContext->EventStateManager()->ContentRemoved(mDocument, aChild);
4340 nsAutoCauseReflowNotifier crNotifier(this);
4342 // Call this here so it only happens for real content mutations and
4343 // not cases when the frame constructor calls its own methods to force
4344 // frame reconstruction.
4345 nsIContent* oldNextSibling = nullptr;
4347 // Editor calls into here with NAC via HTMLEditor::DeleteRefToAnonymousNode.
4348 // This could be asserted if that caller is fixed.
4349 if (MOZ_LIKELY(!aChild->IsRootOfAnonymousSubtree())) {
4350 oldNextSibling = aPreviousSibling ? aPreviousSibling->GetNextSibling()
4351 : container->GetFirstChild();
4354 // After removing aChild from tree we should save information about live
4355 // ancestor
4356 if (mPointerEventTarget &&
4357 mPointerEventTarget->IsInclusiveDescendantOf(aChild)) {
4358 mPointerEventTarget = aChild->GetParent();
4361 mFrameConstructor->ContentRemoved(aChild, oldNextSibling,
4362 nsCSSFrameConstructor::REMOVE_CONTENT);
4364 // NOTE(emilio): It's important that this goes after the frame constructor
4365 // stuff, otherwise the frame constructor can't see elements which are
4366 // display: contents / display: none, because we'd have cleared all the style
4367 // data from there.
4368 mPresContext->RestyleManager()->ContentRemoved(aChild, oldNextSibling);
4371 void PresShell::NotifyCounterStylesAreDirty() {
4372 nsAutoCauseReflowNotifier reflowNotifier(this);
4373 mFrameConstructor->NotifyCounterStylesAreDirty();
4376 bool PresShell::FrameIsAncestorOfDirtyRoot(nsIFrame* aFrame) const {
4377 return mDirtyRoots.FrameIsAncestorOfDirtyRoot(aFrame);
4380 void PresShell::ReconstructFrames() {
4381 MOZ_ASSERT(!mFrameConstructor->GetRootFrame() || mDidInitialize,
4382 "Must not have root frame before initial reflow");
4383 if (!mDidInitialize || mIsDestroying) {
4384 // Nothing to do here
4385 return;
4388 RefPtr<PresShell> kungFuDeathGrip(this);
4390 // Have to make sure that the content notifications are flushed before we
4391 // start messing with the frame model; otherwise we can get content doubling.
4393 // Also make sure that styles are flushed before calling into the frame
4394 // constructor, since that's what it expects.
4395 mDocument->FlushPendingNotifications(FlushType::Style);
4397 if (mIsDestroying) {
4398 return;
4401 nsAutoCauseReflowNotifier crNotifier(this);
4402 mFrameConstructor->ReconstructDocElementHierarchy(
4403 nsCSSFrameConstructor::InsertionKind::Sync);
4406 nsresult PresShell::RenderDocument(const nsRect& aRect,
4407 RenderDocumentFlags aFlags,
4408 nscolor aBackgroundColor,
4409 gfxContext* aThebesContext) {
4410 NS_ENSURE_TRUE(!(aFlags & RenderDocumentFlags::IsUntrusted),
4411 NS_ERROR_NOT_IMPLEMENTED);
4413 nsRootPresContext* rootPresContext = mPresContext->GetRootPresContext();
4414 if (rootPresContext) {
4415 rootPresContext->FlushWillPaintObservers();
4416 if (mIsDestroying) return NS_OK;
4419 nsAutoScriptBlocker blockScripts;
4421 // Set up the rectangle as the path in aThebesContext
4422 gfxRect r(0, 0, nsPresContext::AppUnitsToFloatCSSPixels(aRect.width),
4423 nsPresContext::AppUnitsToFloatCSSPixels(aRect.height));
4424 aThebesContext->NewPath();
4425 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
4426 aThebesContext->SnappedRectangle(r);
4427 #else
4428 aThebesContext->Rectangle(r);
4429 #endif
4431 nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
4432 if (!rootFrame) {
4433 // Nothing to paint, just fill the rect
4434 aThebesContext->SetColor(Color::FromABGR(aBackgroundColor));
4435 aThebesContext->Fill();
4436 return NS_OK;
4439 gfxContextAutoSaveRestore save(aThebesContext);
4441 MOZ_ASSERT(aThebesContext->CurrentOp() == CompositionOp::OP_OVER);
4443 aThebesContext->Clip();
4445 nsDeviceContext* devCtx = mPresContext->DeviceContext();
4447 gfxPoint offset(-nsPresContext::AppUnitsToFloatCSSPixels(aRect.x),
4448 -nsPresContext::AppUnitsToFloatCSSPixels(aRect.y));
4449 gfxFloat scale =
4450 gfxFloat(devCtx->AppUnitsPerDevPixel()) / AppUnitsPerCSSPixel();
4452 // Since canvas APIs use floats to set up their matrices, we may have some
4453 // slight rounding errors here. We use NudgeToIntegers() here to adjust
4454 // matrix components that are integers up to the accuracy of floats to be
4455 // those integers.
4456 gfxMatrix newTM = aThebesContext->CurrentMatrixDouble()
4457 .PreTranslate(offset)
4458 .PreScale(scale, scale)
4459 .NudgeToIntegers();
4460 aThebesContext->SetMatrixDouble(newTM);
4462 AutoSaveRestoreRenderingState _(this);
4464 bool wouldFlushRetainedLayers = false;
4465 PaintFrameFlags flags = PaintFrameFlags::IgnoreSuppression;
4466 if (aThebesContext->CurrentMatrix().HasNonIntegerTranslation()) {
4467 flags |= PaintFrameFlags::InTransform;
4469 if (!(aFlags & RenderDocumentFlags::AsyncDecodeImages)) {
4470 flags |= PaintFrameFlags::SyncDecodeImages;
4472 if (aFlags & RenderDocumentFlags::UseWidgetLayers) {
4473 // We only support using widget layers on display root's with widgets.
4474 nsView* view = rootFrame->GetView();
4475 if (view && view->GetWidget() &&
4476 nsLayoutUtils::GetDisplayRootFrame(rootFrame) == rootFrame) {
4477 LayerManager* layerManager = view->GetWidget()->GetLayerManager();
4478 // ClientLayerManagers or WebRenderLayerManagers in content processes
4479 // don't support taking snapshots.
4480 if (layerManager &&
4481 (!layerManager->AsKnowsCompositor() || XRE_IsParentProcess())) {
4482 flags |= PaintFrameFlags::WidgetLayers;
4486 if (!(aFlags & RenderDocumentFlags::DrawCaret)) {
4487 wouldFlushRetainedLayers = true;
4488 flags |= PaintFrameFlags::HideCaret;
4490 if (aFlags & RenderDocumentFlags::IgnoreViewportScrolling) {
4491 wouldFlushRetainedLayers = !IgnoringViewportScrolling();
4492 mRenderingStateFlags |= RenderingStateFlags::IgnoringViewportScrolling;
4494 if (aFlags & RenderDocumentFlags::DrawWindowNotFlushing) {
4495 mRenderingStateFlags |= RenderingStateFlags::DrawWindowNotFlushing;
4497 if (aFlags & RenderDocumentFlags::DocumentRelative) {
4498 // XXX be smarter about this ... drawWindow might want a rect
4499 // that's "pretty close" to what our retained layer tree covers.
4500 // In that case, it wouldn't disturb normal rendering too much,
4501 // and we should allow it.
4502 wouldFlushRetainedLayers = true;
4503 flags |= PaintFrameFlags::DocumentRelative;
4506 // Don't let drawWindow blow away our retained layer tree
4507 if ((flags & PaintFrameFlags::WidgetLayers) && wouldFlushRetainedLayers) {
4508 flags &= ~PaintFrameFlags::WidgetLayers;
4511 nsLayoutUtils::PaintFrame(aThebesContext, rootFrame, nsRegion(aRect),
4512 aBackgroundColor,
4513 nsDisplayListBuilderMode::Painting, flags);
4515 return NS_OK;
4519 * Clip the display list aList to a range. Returns the clipped
4520 * rectangle surrounding the range.
4522 nsRect PresShell::ClipListToRange(nsDisplayListBuilder* aBuilder,
4523 nsDisplayList* aList, nsRange* aRange) {
4524 // iterate though the display items and add up the bounding boxes of each.
4525 // This will allow the total area of the frames within the range to be
4526 // determined. To do this, remove an item from the bottom of the list, check
4527 // whether it should be part of the range, and if so, append it to the top
4528 // of the temporary list tmpList. If the item is a text frame at the end of
4529 // the selection range, clip it to the portion of the text frame that is
4530 // part of the selection. Then, append the wrapper to the top of the list.
4531 // Otherwise, just delete the item and don't append it.
4532 nsRect surfaceRect;
4533 nsDisplayList tmpList;
4535 nsDisplayItem* i;
4536 while ((i = aList->RemoveBottom())) {
4537 if (i->GetType() == DisplayItemType::TYPE_CONTAINER) {
4538 tmpList.AppendToTop(i);
4539 surfaceRect.UnionRect(
4540 surfaceRect, ClipListToRange(aBuilder, i->GetChildren(), aRange));
4541 continue;
4544 // itemToInsert indiciates the item that should be inserted into the
4545 // temporary list. If null, no item should be inserted.
4546 nsDisplayItem* itemToInsert = nullptr;
4547 nsIFrame* frame = i->Frame();
4548 nsIContent* content = frame->GetContent();
4549 if (content) {
4550 bool atStart = (content == aRange->GetStartContainer());
4551 bool atEnd = (content == aRange->GetEndContainer());
4552 if ((atStart || atEnd) && frame->IsTextFrame()) {
4553 int32_t frameStartOffset, frameEndOffset;
4554 frame->GetOffsets(frameStartOffset, frameEndOffset);
4556 int32_t hilightStart =
4557 atStart ? std::max(static_cast<int32_t>(aRange->StartOffset()),
4558 frameStartOffset)
4559 : frameStartOffset;
4560 int32_t hilightEnd =
4561 atEnd ? std::min(static_cast<int32_t>(aRange->EndOffset()),
4562 frameEndOffset)
4563 : frameEndOffset;
4564 if (hilightStart < hilightEnd) {
4565 // determine the location of the start and end edges of the range.
4566 nsPoint startPoint, endPoint;
4567 frame->GetPointFromOffset(hilightStart, &startPoint);
4568 frame->GetPointFromOffset(hilightEnd, &endPoint);
4570 // The clip rectangle is determined by taking the the start and
4571 // end points of the range, offset from the reference frame.
4572 // Because of rtl, the end point may be to the left of (or above,
4573 // in vertical mode) the start point, so x (or y) is set to the
4574 // lower of the values.
4575 nsRect textRect(aBuilder->ToReferenceFrame(frame), frame->GetSize());
4576 if (frame->GetWritingMode().IsVertical()) {
4577 nscoord y = std::min(startPoint.y, endPoint.y);
4578 textRect.y += y;
4579 textRect.height = std::max(startPoint.y, endPoint.y) - y;
4580 } else {
4581 nscoord x = std::min(startPoint.x, endPoint.x);
4582 textRect.x += x;
4583 textRect.width = std::max(startPoint.x, endPoint.x) - x;
4585 surfaceRect.UnionRect(surfaceRect, textRect);
4587 const ActiveScrolledRoot* asr = i->GetActiveScrolledRoot();
4589 DisplayItemClip newClip;
4590 newClip.SetTo(textRect);
4592 const DisplayItemClipChain* newClipChain =
4593 aBuilder->AllocateDisplayItemClipChain(newClip, asr, nullptr);
4595 i->IntersectClip(aBuilder, newClipChain, true);
4596 itemToInsert = i;
4599 // Don't try to descend into subdocuments.
4600 // If this ever changes we'd need to add handling for subdocuments with
4601 // different zoom levels.
4602 else if (content->GetUncomposedDoc() ==
4603 aRange->GetStartContainer()->GetUncomposedDoc()) {
4604 // if the node is within the range, append it to the temporary list
4605 bool before, after;
4606 nsresult rv =
4607 RangeUtils::CompareNodeToRange(content, aRange, &before, &after);
4608 if (NS_SUCCEEDED(rv) && !before && !after) {
4609 itemToInsert = i;
4610 bool snap;
4611 surfaceRect.UnionRect(surfaceRect, i->GetBounds(aBuilder, &snap));
4616 // insert the item into the list if necessary. If the item has a child
4617 // list, insert that as well
4618 nsDisplayList* sublist = i->GetSameCoordinateSystemChildren();
4619 if (itemToInsert || sublist) {
4620 tmpList.AppendToTop(itemToInsert ? itemToInsert : i);
4621 // if the item is a list, iterate over it as well
4622 if (sublist)
4623 surfaceRect.UnionRect(surfaceRect,
4624 ClipListToRange(aBuilder, sublist, aRange));
4625 } else {
4626 // otherwise, just delete the item and don't readd it to the list
4627 i->Destroy(aBuilder);
4631 // now add all the items back onto the original list again
4632 aList->AppendToTop(&tmpList);
4634 return surfaceRect;
4637 #ifdef DEBUG
4638 # include <stdio.h>
4640 static bool gDumpRangePaintList = false;
4641 #endif
4643 UniquePtr<RangePaintInfo> PresShell::CreateRangePaintInfo(
4644 nsRange* aRange, nsRect& aSurfaceRect, bool aForPrimarySelection) {
4645 nsIFrame* ancestorFrame;
4646 nsIFrame* rootFrame = GetRootFrame();
4648 // If the start or end of the range is the document, just use the root
4649 // frame, otherwise get the common ancestor of the two endpoints of the
4650 // range.
4651 nsINode* startContainer = aRange->GetStartContainer();
4652 nsINode* endContainer = aRange->GetEndContainer();
4653 Document* doc = startContainer->GetComposedDoc();
4654 if (startContainer == doc || endContainer == doc) {
4655 ancestorFrame = rootFrame;
4656 } else {
4657 nsINode* ancestor =
4658 nsContentUtils::GetCommonAncestor(startContainer, endContainer);
4659 NS_ASSERTION(!ancestor || ancestor->IsContent(),
4660 "common ancestor is not content");
4662 while (ancestor && ancestor->IsContent()) {
4663 ancestorFrame = ancestor->AsContent()->GetPrimaryFrame();
4664 if (ancestorFrame) {
4665 break;
4668 ancestor = ancestor->GetParentOrShadowHostNode();
4671 // use the nearest ancestor frame that includes all continuations as the
4672 // root for building the display list
4673 while (ancestorFrame &&
4674 nsLayoutUtils::GetNextContinuationOrIBSplitSibling(ancestorFrame))
4675 ancestorFrame = ancestorFrame->GetParent();
4678 if (!ancestorFrame) {
4679 return nullptr;
4682 // get a display list containing the range
4683 auto info = MakeUnique<RangePaintInfo>(aRange, ancestorFrame);
4684 info->mBuilder.SetIncludeAllOutOfFlows();
4685 if (aForPrimarySelection) {
4686 info->mBuilder.SetSelectedFramesOnly();
4688 info->mBuilder.EnterPresShell(ancestorFrame);
4690 ContentSubtreeIterator subtreeIter;
4691 nsresult rv = subtreeIter.Init(aRange);
4692 if (NS_FAILED(rv)) {
4693 return nullptr;
4696 auto BuildDisplayListForNode = [&](nsINode* aNode) {
4697 if (MOZ_UNLIKELY(!aNode->IsContent())) {
4698 return;
4700 nsIFrame* frame = aNode->AsContent()->GetPrimaryFrame();
4701 // XXX deal with frame being null due to display:contents
4702 for (; frame;
4703 frame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(frame)) {
4704 info->mBuilder.SetVisibleRect(frame->GetVisualOverflowRect());
4705 info->mBuilder.SetDirtyRect(frame->GetVisualOverflowRect());
4706 frame->BuildDisplayListForStackingContext(&info->mBuilder, &info->mList);
4709 if (startContainer->NodeType() == nsINode::TEXT_NODE) {
4710 BuildDisplayListForNode(startContainer);
4712 for (; !subtreeIter.IsDone(); subtreeIter.Next()) {
4713 nsCOMPtr<nsINode> node = subtreeIter.GetCurrentNode();
4714 BuildDisplayListForNode(node);
4716 if (endContainer != startContainer &&
4717 endContainer->NodeType() == nsINode::TEXT_NODE) {
4718 BuildDisplayListForNode(endContainer);
4721 #ifdef DEBUG
4722 if (gDumpRangePaintList) {
4723 fprintf(stderr, "CreateRangePaintInfo --- before ClipListToRange:\n");
4724 nsFrame::PrintDisplayList(&(info->mBuilder), info->mList);
4726 #endif
4728 nsRect rangeRect = ClipListToRange(&info->mBuilder, &info->mList, aRange);
4730 info->mBuilder.LeavePresShell(ancestorFrame, &info->mList);
4732 #ifdef DEBUG
4733 if (gDumpRangePaintList) {
4734 fprintf(stderr, "CreateRangePaintInfo --- after ClipListToRange:\n");
4735 nsFrame::PrintDisplayList(&(info->mBuilder), info->mList);
4737 #endif
4739 // determine the offset of the reference frame for the display list
4740 // to the root frame. This will allow the coordinates used when painting
4741 // to all be offset from the same point
4742 info->mRootOffset = ancestorFrame->GetOffsetTo(rootFrame);
4743 rangeRect.MoveBy(info->mRootOffset);
4744 aSurfaceRect.UnionRect(aSurfaceRect, rangeRect);
4746 return info;
4749 already_AddRefed<SourceSurface> PresShell::PaintRangePaintInfo(
4750 const nsTArray<UniquePtr<RangePaintInfo>>& aItems, Selection* aSelection,
4751 const Maybe<CSSIntRegion>& aRegion, nsRect aArea,
4752 const LayoutDeviceIntPoint aPoint, LayoutDeviceIntRect* aScreenRect,
4753 RenderImageFlags aFlags) {
4754 nsPresContext* pc = GetPresContext();
4755 if (!pc || aArea.width == 0 || aArea.height == 0) return nullptr;
4757 // use the rectangle to create the surface
4758 nsIntRect pixelArea = aArea.ToOutsidePixels(pc->AppUnitsPerDevPixel());
4760 // if the image should not be resized, scale must be 1
4761 float scale = 1.0;
4762 nsIntRect rootScreenRect =
4763 GetRootFrame()->GetScreenRectInAppUnits().ToNearestPixels(
4764 pc->AppUnitsPerDevPixel());
4766 nsRect maxSize;
4767 pc->DeviceContext()->GetClientRect(maxSize);
4769 // check if the image should be resized
4770 bool resize = !!(aFlags & RenderImageFlags::AutoScale);
4772 if (resize) {
4773 // check if image-resizing-algorithm should be used
4774 if (aFlags & RenderImageFlags::IsImage) {
4775 // get max screensize
4776 nscoord maxWidth = pc->AppUnitsToDevPixels(maxSize.width);
4777 nscoord maxHeight = pc->AppUnitsToDevPixels(maxSize.height);
4778 // resize image relative to the screensize
4779 // get best height/width relative to screensize
4780 float bestHeight = float(maxHeight) * RELATIVE_SCALEFACTOR;
4781 float bestWidth = float(maxWidth) * RELATIVE_SCALEFACTOR;
4782 // calculate scale for bestWidth
4783 float adjustedScale = bestWidth / float(pixelArea.width);
4784 // get the worst height (height when width is perfect)
4785 float worstHeight = float(pixelArea.height) * adjustedScale;
4786 // get the difference of best and worst height
4787 float difference = bestHeight - worstHeight;
4788 // halve the difference and add it to worstHeight to get
4789 // the best compromise between bestHeight and bestWidth,
4790 // then calculate the corresponding scale factor
4791 adjustedScale = (worstHeight + difference / 2) / float(pixelArea.height);
4792 // prevent upscaling
4793 scale = std::min(scale, adjustedScale);
4794 } else {
4795 // get half of max screensize
4796 nscoord maxWidth = pc->AppUnitsToDevPixels(maxSize.width >> 1);
4797 nscoord maxHeight = pc->AppUnitsToDevPixels(maxSize.height >> 1);
4798 if (pixelArea.width > maxWidth || pixelArea.height > maxHeight) {
4799 // divide the maximum size by the image size in both directions.
4800 // Whichever direction produces the smallest result determines how much
4801 // should be scaled.
4802 if (pixelArea.width > maxWidth)
4803 scale = std::min(scale, float(maxWidth) / pixelArea.width);
4804 if (pixelArea.height > maxHeight)
4805 scale = std::min(scale, float(maxHeight) / pixelArea.height);
4809 pixelArea.width = NSToIntFloor(float(pixelArea.width) * scale);
4810 pixelArea.height = NSToIntFloor(float(pixelArea.height) * scale);
4811 if (!pixelArea.width || !pixelArea.height) return nullptr;
4813 // adjust the screen position based on the rescaled size
4814 nscoord left = rootScreenRect.x + pixelArea.x;
4815 nscoord top = rootScreenRect.y + pixelArea.y;
4816 aScreenRect->x = NSToIntFloor(aPoint.x - float(aPoint.x - left) * scale);
4817 aScreenRect->y = NSToIntFloor(aPoint.y - float(aPoint.y - top) * scale);
4818 } else {
4819 // move aScreenRect to the position of the surface in screen coordinates
4820 aScreenRect->MoveTo(rootScreenRect.x + pixelArea.x,
4821 rootScreenRect.y + pixelArea.y);
4823 aScreenRect->width = pixelArea.width;
4824 aScreenRect->height = pixelArea.height;
4826 RefPtr<DrawTarget> dt =
4827 gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
4828 IntSize(pixelArea.width, pixelArea.height), SurfaceFormat::B8G8R8A8);
4829 if (!dt || !dt->IsValid()) {
4830 return nullptr;
4833 RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(dt);
4834 MOZ_ASSERT(ctx); // already checked the draw target above
4836 if (aRegion) {
4837 RefPtr<PathBuilder> builder = dt->CreatePathBuilder(FillRule::FILL_WINDING);
4839 // Convert aRegion from CSS pixels to dev pixels
4840 nsIntRegion region = aRegion->ToAppUnits(AppUnitsPerCSSPixel())
4841 .ToOutsidePixels(pc->AppUnitsPerDevPixel());
4842 for (auto iter = region.RectIter(); !iter.Done(); iter.Next()) {
4843 const IntRect& rect = iter.Get();
4845 builder->MoveTo(rect.TopLeft());
4846 builder->LineTo(rect.TopRight());
4847 builder->LineTo(rect.BottomRight());
4848 builder->LineTo(rect.BottomLeft());
4849 builder->LineTo(rect.TopLeft());
4852 RefPtr<Path> path = builder->Finish();
4853 ctx->Clip(path);
4856 gfxMatrix initialTM = ctx->CurrentMatrixDouble();
4858 if (resize) initialTM.PreScale(scale, scale);
4860 // translate so that points are relative to the surface area
4861 gfxPoint surfaceOffset = nsLayoutUtils::PointToGfxPoint(
4862 -aArea.TopLeft(), pc->AppUnitsPerDevPixel());
4863 initialTM.PreTranslate(surfaceOffset);
4865 // temporarily hide the selection so that text is drawn normally. If a
4866 // selection is being rendered, use that, otherwise use the presshell's
4867 // selection.
4868 RefPtr<nsFrameSelection> frameSelection;
4869 if (aSelection) {
4870 frameSelection = aSelection->GetFrameSelection();
4871 } else {
4872 frameSelection = FrameSelection();
4874 int16_t oldDisplaySelection = frameSelection->GetDisplaySelection();
4875 frameSelection->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN);
4877 // next, paint each range in the selection
4878 for (const UniquePtr<RangePaintInfo>& rangeInfo : aItems) {
4879 // the display lists paint relative to the offset from the reference
4880 // frame, so account for that translation too:
4881 gfxPoint rootOffset = nsLayoutUtils::PointToGfxPoint(
4882 rangeInfo->mRootOffset, pc->AppUnitsPerDevPixel());
4883 ctx->SetMatrixDouble(initialTM.PreTranslate(rootOffset));
4884 aArea.MoveBy(-rangeInfo->mRootOffset.x, -rangeInfo->mRootOffset.y);
4885 nsRegion visible(aArea);
4886 RefPtr<LayerManager> layerManager = rangeInfo->mList.PaintRoot(
4887 &rangeInfo->mBuilder, ctx, nsDisplayList::PAINT_DEFAULT);
4888 aArea.MoveBy(rangeInfo->mRootOffset.x, rangeInfo->mRootOffset.y);
4891 // restore the old selection display state
4892 frameSelection->SetDisplaySelection(oldDisplaySelection);
4894 return dt->Snapshot();
4897 already_AddRefed<SourceSurface> PresShell::RenderNode(
4898 nsINode* aNode, const Maybe<CSSIntRegion>& aRegion,
4899 const LayoutDeviceIntPoint aPoint, LayoutDeviceIntRect* aScreenRect,
4900 RenderImageFlags aFlags) {
4901 // area will hold the size of the surface needed to draw the node, measured
4902 // from the root frame.
4903 nsRect area;
4904 nsTArray<UniquePtr<RangePaintInfo>> rangeItems;
4906 // nothing to draw if the node isn't in a document
4907 if (!aNode->IsInComposedDoc()) {
4908 return nullptr;
4911 RefPtr<nsRange> range = new nsRange(aNode);
4912 IgnoredErrorResult rv;
4913 range->SelectNode(*aNode, rv);
4914 if (rv.Failed()) {
4915 return nullptr;
4918 UniquePtr<RangePaintInfo> info = CreateRangePaintInfo(range, area, false);
4919 if (info && !rangeItems.AppendElement(std::move(info))) {
4920 return nullptr;
4923 Maybe<CSSIntRegion> region = aRegion;
4924 if (region) {
4925 // combine the area with the supplied region
4926 CSSIntRect rrectPixels = region->GetBounds();
4928 nsRect rrect = ToAppUnits(rrectPixels, AppUnitsPerCSSPixel());
4929 area.IntersectRect(area, rrect);
4931 nsPresContext* pc = GetPresContext();
4932 if (!pc) return nullptr;
4934 // move the region so that it is offset from the topleft corner of the
4935 // surface
4936 region->MoveBy(-nsPresContext::AppUnitsToIntCSSPixels(area.x),
4937 -nsPresContext::AppUnitsToIntCSSPixels(area.y));
4940 return PaintRangePaintInfo(rangeItems, nullptr, region, area, aPoint,
4941 aScreenRect, aFlags);
4944 already_AddRefed<SourceSurface> PresShell::RenderSelection(
4945 Selection* aSelection, const LayoutDeviceIntPoint aPoint,
4946 LayoutDeviceIntRect* aScreenRect, RenderImageFlags aFlags) {
4947 // area will hold the size of the surface needed to draw the selection,
4948 // measured from the root frame.
4949 nsRect area;
4950 nsTArray<UniquePtr<RangePaintInfo>> rangeItems;
4952 // iterate over each range and collect them into the rangeItems array.
4953 // This is done so that the size of selection can be determined so as
4954 // to allocate a surface area
4955 uint32_t numRanges = aSelection->RangeCount();
4956 NS_ASSERTION(numRanges > 0, "RenderSelection called with no selection");
4958 for (uint32_t r = 0; r < numRanges; r++) {
4959 RefPtr<nsRange> range = aSelection->GetRangeAt(r);
4961 UniquePtr<RangePaintInfo> info = CreateRangePaintInfo(range, area, true);
4962 if (info && !rangeItems.AppendElement(std::move(info))) {
4963 return nullptr;
4967 return PaintRangePaintInfo(rangeItems, aSelection, Nothing(), area, aPoint,
4968 aScreenRect, aFlags);
4971 void PresShell::AddPrintPreviewBackgroundItem(nsDisplayListBuilder* aBuilder,
4972 nsDisplayList* aList,
4973 nsIFrame* aFrame,
4974 const nsRect& aBounds) {
4975 aList->AppendNewToBottom<nsDisplaySolidColor>(aBuilder, aFrame, aBounds,
4976 NS_RGB(115, 115, 115));
4979 static bool AddCanvasBackgroundColor(const nsDisplayList* aList,
4980 nsIFrame* aCanvasFrame, nscolor aColor,
4981 bool aCSSBackgroundColor) {
4982 for (nsDisplayItem* i = aList->GetBottom(); i; i = i->GetAbove()) {
4983 const DisplayItemType type = i->GetType();
4985 if (i->Frame() == aCanvasFrame &&
4986 type == DisplayItemType::TYPE_CANVAS_BACKGROUND_COLOR) {
4987 auto* bg = static_cast<nsDisplayCanvasBackgroundColor*>(i);
4988 bg->SetExtraBackgroundColor(aColor);
4989 return true;
4992 const bool isBlendContainer =
4993 type == DisplayItemType::TYPE_BLEND_CONTAINER ||
4994 type == DisplayItemType::TYPE_TABLE_BLEND_CONTAINER;
4996 nsDisplayList* sublist = i->GetSameCoordinateSystemChildren();
4997 if (sublist && !(isBlendContainer && !aCSSBackgroundColor) &&
4998 AddCanvasBackgroundColor(sublist, aCanvasFrame, aColor,
4999 aCSSBackgroundColor))
5000 return true;
5002 return false;
5005 void PresShell::AddCanvasBackgroundColorItem(
5006 nsDisplayListBuilder* aBuilder, nsDisplayList* aList, nsIFrame* aFrame,
5007 const nsRect& aBounds, nscolor aBackstopColor,
5008 AddCanvasBackgroundColorFlags aFlags) {
5009 if (aBounds.IsEmpty()) {
5010 return;
5012 // We don't want to add an item for the canvas background color if the frame
5013 // (sub)tree we are painting doesn't include any canvas frames. There isn't
5014 // an easy way to check this directly, but if we check if the root of the
5015 // (sub)tree we are painting is a canvas frame that should cover us in all
5016 // cases (it will usually be a viewport frame when we have a canvas frame in
5017 // the (sub)tree).
5018 if (!(aFlags & AddCanvasBackgroundColorFlags::ForceDraw) &&
5019 !nsCSSRendering::IsCanvasFrame(aFrame)) {
5020 return;
5023 nscolor bgcolor = NS_ComposeColors(aBackstopColor, mCanvasBackgroundColor);
5024 if (NS_GET_A(bgcolor) == 0) return;
5026 // To make layers work better, we want to avoid having a big non-scrolled
5027 // color background behind a scrolled transparent background. Instead,
5028 // we'll try to move the color background into the scrolled content
5029 // by making nsDisplayCanvasBackground paint it.
5030 // If we're only adding an unscrolled item, then pretend that we've
5031 // already done it.
5032 bool addedScrollingBackgroundColor =
5033 !!(aFlags & AddCanvasBackgroundColorFlags::AppendUnscrolledOnly);
5034 if (!aFrame->GetParent() && !addedScrollingBackgroundColor) {
5035 nsIScrollableFrame* sf =
5036 aFrame->PresShell()->GetRootScrollFrameAsScrollable();
5037 if (sf) {
5038 nsCanvasFrame* canvasFrame = do_QueryFrame(sf->GetScrolledFrame());
5039 if (canvasFrame && canvasFrame->IsVisibleForPainting()) {
5040 addedScrollingBackgroundColor = AddCanvasBackgroundColor(
5041 aList, canvasFrame, bgcolor, mHasCSSBackgroundColor);
5046 // With async scrolling, we'd like to have two instances of the background
5047 // color: one that scrolls with the content (for the reasons stated above),
5048 // and one underneath which does not scroll with the content, but which can
5049 // be shown during checkerboarding and overscroll.
5050 // We can only do that if the color is opaque.
5051 bool forceUnscrolledItem =
5052 nsLayoutUtils::UsesAsyncScrolling(aFrame) && NS_GET_A(bgcolor) == 255;
5054 if (!addedScrollingBackgroundColor || forceUnscrolledItem) {
5055 aList->AppendNewToBottom<nsDisplaySolidColor>(aBuilder, aFrame, aBounds,
5056 bgcolor);
5060 static bool IsTransparentContainerElement(nsPresContext* aPresContext) {
5061 nsCOMPtr<nsIDocShell> docShell = aPresContext->GetDocShell();
5062 if (!docShell) {
5063 return false;
5066 nsCOMPtr<nsPIDOMWindowOuter> pwin = docShell->GetWindow();
5067 if (!pwin) return false;
5068 nsCOMPtr<Element> containerElement = pwin->GetFrameElementInternal();
5070 BrowserChild* tab = BrowserChild::GetFrom(docShell);
5071 if (tab) {
5072 // Check if presShell is the top PresShell. Only the top can
5073 // influence the canvas background color.
5074 if (aPresContext->GetPresShell() != tab->GetTopLevelPresShell()) {
5075 tab = nullptr;
5079 return (containerElement && containerElement->HasAttr(
5080 kNameSpaceID_None, nsGkAtoms::transparent)) ||
5081 (tab && tab->IsTransparent());
5084 nscolor PresShell::GetDefaultBackgroundColorToDraw() {
5085 if (!mPresContext || !mPresContext->GetBackgroundColorDraw()) {
5086 return NS_RGB(255, 255, 255);
5088 return mPresContext->DefaultBackgroundColor();
5091 void PresShell::UpdateCanvasBackground() {
5092 // If we have a frame tree and it has style information that
5093 // specifies the background color of the canvas, update our local
5094 // cache of that color.
5095 nsIFrame* rootStyleFrame = FrameConstructor()->GetRootElementStyleFrame();
5096 if (rootStyleFrame) {
5097 ComputedStyle* bgStyle =
5098 nsCSSRendering::FindRootFrameBackground(rootStyleFrame);
5099 // XXX We should really be passing the canvasframe, not the root element
5100 // style frame but we don't have access to the canvasframe here. It isn't
5101 // a problem because only a few frames can return something other than true
5102 // and none of them would be a canvas frame or root element style frame.
5103 bool drawBackgroundImage;
5104 bool drawBackgroundColor;
5105 mCanvasBackgroundColor = nsCSSRendering::DetermineBackgroundColor(
5106 mPresContext, bgStyle, rootStyleFrame, drawBackgroundImage,
5107 drawBackgroundColor);
5108 mHasCSSBackgroundColor = drawBackgroundColor;
5109 if (mPresContext->IsRootContentDocumentCrossProcess() &&
5110 !IsTransparentContainerElement(mPresContext)) {
5111 mCanvasBackgroundColor = NS_ComposeColors(
5112 GetDefaultBackgroundColorToDraw(), mCanvasBackgroundColor);
5116 // If the root element of the document (ie html) has style 'display: none'
5117 // then the document's background color does not get drawn; cache the
5118 // color we actually draw.
5119 if (!FrameConstructor()->GetRootElementFrame()) {
5120 mCanvasBackgroundColor = GetDefaultBackgroundColorToDraw();
5124 nscolor PresShell::ComputeBackstopColor(nsView* aDisplayRoot) {
5125 nsIWidget* widget = aDisplayRoot->GetWidget();
5126 if (widget && (widget->GetTransparencyMode() != eTransparencyOpaque ||
5127 widget->WidgetPaintsBackground())) {
5128 // Within a transparent widget, so the backstop color must be
5129 // totally transparent.
5130 return NS_RGBA(0, 0, 0, 0);
5132 // Within an opaque widget (or no widget at all), so the backstop
5133 // color must be totally opaque. The user's default background
5134 // as reported by the prescontext is guaranteed to be opaque.
5135 return GetDefaultBackgroundColorToDraw();
5138 struct PaintParams {
5139 nscolor mBackgroundColor;
5142 LayerManager* PresShell::GetLayerManager() {
5143 NS_ASSERTION(mViewManager, "Should have view manager");
5145 nsView* rootView = mViewManager->GetRootView();
5146 if (rootView) {
5147 if (nsIWidget* widget = rootView->GetWidget()) {
5148 return widget->GetLayerManager();
5151 return nullptr;
5154 bool PresShell::AsyncPanZoomEnabled() {
5155 NS_ASSERTION(mViewManager, "Should have view manager");
5156 nsView* rootView = mViewManager->GetRootView();
5157 if (rootView) {
5158 if (nsIWidget* widget = rootView->GetWidget()) {
5159 return widget->AsyncPanZoomEnabled();
5162 return gfxPlatform::AsyncPanZoomEnabled();
5165 nsresult PresShell::SetResolutionAndScaleTo(float aResolution,
5166 ResolutionChangeOrigin aOrigin) {
5167 if (!(aResolution > 0.0)) {
5168 return NS_ERROR_ILLEGAL_VALUE;
5170 if (aResolution == mResolution.valueOr(0.0)) {
5171 MOZ_ASSERT(mResolution.isSome());
5172 return NS_OK;
5174 RenderingState state(this);
5175 state.mResolution = Some(aResolution);
5176 SetRenderingState(state);
5177 if (mMobileViewportManager) {
5178 mMobileViewportManager->ResolutionUpdated(aOrigin);
5180 if (aOrigin == ResolutionChangeOrigin::Apz) {
5181 mResolutionUpdatedByApz = true;
5182 } else {
5183 mResolutionUpdated = true;
5186 if (auto* window = nsGlobalWindowInner::Cast(mDocument->GetInnerWindow())) {
5187 window->VisualViewport()->PostResizeEvent();
5190 return NS_OK;
5193 float PresShell::GetCumulativeResolution() {
5194 float resolution = GetResolution();
5195 nsPresContext* parentCtx = GetPresContext()->GetParentPresContext();
5196 if (parentCtx) {
5197 resolution *= parentCtx->PresShell()->GetCumulativeResolution();
5199 return resolution;
5202 float PresShell::GetCumulativeNonRootScaleResolution() {
5203 float resolution = 1.0;
5204 PresShell* currentPresShell = this;
5205 while (currentPresShell) {
5206 nsPresContext* currentCtx = currentPresShell->GetPresContext();
5207 if (currentCtx != currentCtx->GetRootPresContext()) {
5208 resolution *= currentPresShell->GetResolution();
5210 nsPresContext* parentCtx = currentCtx->GetParentPresContext();
5211 if (parentCtx) {
5212 currentPresShell = parentCtx->PresShell();
5213 } else {
5214 currentPresShell = nullptr;
5217 return resolution;
5220 void PresShell::SetRestoreResolution(float aResolution,
5221 LayoutDeviceIntSize aDisplaySize) {
5222 if (mMobileViewportManager) {
5223 mMobileViewportManager->SetRestoreResolution(aResolution, aDisplaySize);
5227 void PresShell::SetRenderingState(const RenderingState& aState) {
5228 if (mRenderingStateFlags != aState.mRenderingStateFlags) {
5229 // Rendering state changed in a way that forces us to flush any
5230 // retained layers we might already have.
5231 LayerManager* manager = GetLayerManager();
5232 if (manager) {
5233 FrameLayerBuilder::InvalidateAllLayers(manager);
5237 // nsSubDocumentFrame uses a resolution different from 1.0 to determine if it
5238 // needs to build a nsDisplayResolution item. So if we are going from or
5239 // to 1.0 then we need to invalidate the subdoc frame so that item gets
5240 // created/removed.
5241 if (mResolution.valueOr(1.0) != aState.mResolution.valueOr(1.0) &&
5242 (mResolution.valueOr(1.0) == 1.0 ||
5243 aState.mResolution.valueOr(1.0) == 1.0)) {
5244 if (nsIFrame* frame = GetRootFrame()) {
5245 frame = nsLayoutUtils::GetCrossDocParentFrame(frame);
5246 if (frame) {
5247 frame->InvalidateFrame();
5252 mRenderingStateFlags = aState.mRenderingStateFlags;
5253 mResolution = aState.mResolution;
5256 void PresShell::SynthesizeMouseMove(bool aFromScroll) {
5257 if (!StaticPrefs::layout_reflow_synthMouseMove()) return;
5259 if (mPaintingSuppressed || !mIsActive || !mPresContext) {
5260 return;
5263 if (!mPresContext->IsRoot()) {
5264 if (PresShell* rootPresShell = GetRootPresShell()) {
5265 rootPresShell->SynthesizeMouseMove(aFromScroll);
5267 return;
5270 if (mMouseLocation == nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE))
5271 return;
5273 if (!mSynthMouseMoveEvent.IsPending()) {
5274 RefPtr<nsSynthMouseMoveEvent> ev =
5275 new nsSynthMouseMoveEvent(this, aFromScroll);
5277 GetPresContext()->RefreshDriver()->AddRefreshObserver(ev,
5278 FlushType::Display);
5279 mSynthMouseMoveEvent = std::move(ev);
5284 * Find the first floating view with a widget in a postorder traversal of the
5285 * view tree that contains the point. Thus more deeply nested floating views
5286 * are preferred over their ancestors, and floating views earlier in the
5287 * view hierarchy (i.e., added later) are preferred over their siblings.
5288 * This is adequate for finding the "topmost" floating view under a point,
5289 * given that floating views don't supporting having a specific z-index.
5291 * We cannot exit early when aPt is outside the view bounds, because floating
5292 * views aren't necessarily included in their parent's bounds, so this could
5293 * traverse the entire view hierarchy --- use carefully.
5295 static nsView* FindFloatingViewContaining(nsView* aView, nsPoint aPt) {
5296 if (aView->GetVisibility() == nsViewVisibility_kHide)
5297 // No need to look into descendants.
5298 return nullptr;
5300 nsIFrame* frame = aView->GetFrame();
5301 if (frame) {
5302 if (!frame->IsVisibleConsideringAncestors(
5303 nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY) ||
5304 !frame->PresShell()->IsActive()) {
5305 return nullptr;
5309 for (nsView* v = aView->GetFirstChild(); v; v = v->GetNextSibling()) {
5310 nsView* r = FindFloatingViewContaining(v, v->ConvertFromParentCoords(aPt));
5311 if (r) return r;
5314 if (aView->GetFloating() && aView->HasWidget() &&
5315 aView->GetDimensions().Contains(aPt))
5316 return aView;
5318 return nullptr;
5322 * This finds the first view containing the given point in a postorder
5323 * traversal of the view tree that contains the point, assuming that the
5324 * point is not in a floating view. It assumes that only floating views
5325 * extend outside the bounds of their parents.
5327 * This methods should only be called if FindFloatingViewContaining
5328 * returns null.
5330 static nsView* FindViewContaining(nsView* aView, nsPoint aPt) {
5331 if (!aView->GetDimensions().Contains(aPt) ||
5332 aView->GetVisibility() == nsViewVisibility_kHide) {
5333 return nullptr;
5336 nsIFrame* frame = aView->GetFrame();
5337 if (frame) {
5338 if (!frame->IsVisibleConsideringAncestors(
5339 nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY) ||
5340 !frame->PresShell()->IsActive()) {
5341 return nullptr;
5345 for (nsView* v = aView->GetFirstChild(); v; v = v->GetNextSibling()) {
5346 nsView* r = FindViewContaining(v, v->ConvertFromParentCoords(aPt));
5347 if (r) return r;
5350 return aView;
5353 static BrowserBridgeChild* GetChildBrowser(nsView* aView) {
5354 if (!aView) {
5355 return nullptr;
5357 nsIFrame* frame = aView->GetFrame();
5358 if (!frame && aView->GetParent()) {
5359 // If frame is null then view is an anonymous inner view, and we want
5360 // the frame from the corresponding outer view.
5361 frame = aView->GetParent()->GetFrame();
5363 if (!frame || !frame->GetContent()) {
5364 return nullptr;
5366 return BrowserBridgeChild::GetFrom(frame->GetContent());
5369 void PresShell::ProcessSynthMouseMoveEvent(bool aFromScroll) {
5370 // If drag session has started, we shouldn't synthesize mousemove event.
5371 nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
5372 if (dragSession) {
5373 mSynthMouseMoveEvent.Forget();
5374 return;
5377 // allow new event to be posted while handling this one only if the
5378 // source of the event is a scroll (to prevent infinite reflow loops)
5379 if (aFromScroll) {
5380 mSynthMouseMoveEvent.Forget();
5383 nsView* rootView = mViewManager ? mViewManager->GetRootView() : nullptr;
5384 if (mMouseLocation == nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE) ||
5385 !rootView || !rootView->HasWidget() || !mPresContext) {
5386 mSynthMouseMoveEvent.Forget();
5387 return;
5390 NS_ASSERTION(mPresContext->IsRoot(), "Only a root pres shell should be here");
5392 // Hold a ref to ourselves so DispatchEvent won't destroy us (since
5393 // we need to access members after we call DispatchEvent).
5394 RefPtr<PresShell> kungFuDeathGrip(this);
5396 #ifdef DEBUG_MOUSE_LOCATION
5397 printf("[ps=%p]synthesizing mouse move to (%d,%d)\n", this, mMouseLocation.x,
5398 mMouseLocation.y);
5399 #endif
5401 int32_t APD = mPresContext->AppUnitsPerDevPixel();
5403 // We need a widget to put in the event we are going to dispatch so we look
5404 // for a view that has a widget and the mouse location is over. We first look
5405 // for floating views, if there isn't one we use the root view. |view| holds
5406 // that view.
5407 nsView* view = nullptr;
5409 // The appunits per devpixel ratio of |view|.
5410 int32_t viewAPD;
5412 // mRefPoint will be mMouseLocation relative to the widget of |view|, the
5413 // widget we will put in the event we dispatch, in viewAPD appunits
5414 nsPoint refpoint(0, 0);
5416 // We always dispatch the event to the pres shell that contains the view that
5417 // the mouse is over. pointVM is the VM of that pres shell.
5418 nsViewManager* pointVM = nullptr;
5420 // This could be a bit slow (traverses entire view hierarchy)
5421 // but it's OK to do it once per synthetic mouse event
5422 view = FindFloatingViewContaining(rootView, mMouseLocation);
5423 nsView* pointView = view;
5424 if (!view) {
5425 view = rootView;
5426 pointView = FindViewContaining(rootView, mMouseLocation);
5427 // pointView can be null in situations related to mouse capture
5428 pointVM = (pointView ? pointView : view)->GetViewManager();
5429 refpoint = mMouseLocation + rootView->ViewToWidgetOffset();
5430 viewAPD = APD;
5431 } else {
5432 pointVM = view->GetViewManager();
5433 nsIFrame* frame = view->GetFrame();
5434 NS_ASSERTION(frame, "floating views can't be anonymous");
5435 viewAPD = frame->PresContext()->AppUnitsPerDevPixel();
5436 refpoint = mMouseLocation.ScaleToOtherAppUnits(APD, viewAPD);
5437 refpoint -= view->GetOffsetTo(rootView);
5438 refpoint += view->ViewToWidgetOffset();
5440 NS_ASSERTION(view->GetWidget(), "view should have a widget here");
5441 WidgetMouseEvent event(true, eMouseMove, view->GetWidget(),
5442 WidgetMouseEvent::eSynthesized);
5443 event.mRefPoint =
5444 LayoutDeviceIntPoint::FromAppUnitsToNearest(refpoint, viewAPD);
5445 event.mTime = PR_IntervalNow();
5446 // XXX set event.mModifiers ?
5447 // XXX mnakano I think that we should get the latest information from widget.
5449 if (BrowserBridgeChild* bbc = GetChildBrowser(pointView)) {
5450 // If we have a BrowserBridgeChild, we're going to be dispatching this
5451 // mouse event into an OOP iframe of the current document.
5452 event.mLayersId = bbc->GetLayersId();
5453 bbc->SendDispatchSynthesizedMouseEvent(event);
5454 } else if (RefPtr<PresShell> presShell = pointVM->GetPresShell()) {
5455 // Since this gets run in a refresh tick there isn't an InputAPZContext on
5456 // the stack from the nsBaseWidget. We need to simulate one with at least
5457 // the correct target guid, so that the correct callback transform gets
5458 // applied if this event goes to a child process. The input block id is set
5459 // to 0 because this is a synthetic event which doesn't really belong to any
5460 // input block. Same for the APZ response field.
5461 InputAPZContext apzContext(mMouseEventTargetGuid, 0, nsEventStatus_eIgnore);
5462 presShell->DispatchSynthMouseMove(&event);
5465 if (!aFromScroll) {
5466 mSynthMouseMoveEvent.Forget();
5470 /* static */
5471 void PresShell::MarkFramesInListApproximatelyVisible(
5472 const nsDisplayList& aList) {
5473 for (nsDisplayItem* item = aList.GetBottom(); item; item = item->GetAbove()) {
5474 nsDisplayList* sublist = item->GetChildren();
5475 if (sublist) {
5476 MarkFramesInListApproximatelyVisible(*sublist);
5477 continue;
5480 nsIFrame* frame = item->Frame();
5481 MOZ_ASSERT(frame);
5483 if (!frame->TrackingVisibility()) {
5484 continue;
5487 // Use the presshell containing the frame.
5488 PresShell* presShell = frame->PresShell();
5489 MOZ_ASSERT(!presShell->AssumeAllFramesVisible());
5490 if (presShell->mApproximatelyVisibleFrames.EnsureInserted(frame)) {
5491 // The frame was added to mApproximatelyVisibleFrames, so increment its
5492 // visible count.
5493 frame->IncApproximateVisibleCount();
5498 /* static */
5499 void PresShell::DecApproximateVisibleCount(
5500 VisibleFrames& aFrames, const Maybe<OnNonvisible>& aNonvisibleAction
5501 /* = Nothing() */) {
5502 for (auto iter = aFrames.Iter(); !iter.Done(); iter.Next()) {
5503 nsIFrame* frame = iter.Get()->GetKey();
5504 // Decrement the frame's visible count if we're still tracking its
5505 // visibility. (We may not be, if the frame disabled visibility tracking
5506 // after we added it to the visible frames list.)
5507 if (frame->TrackingVisibility()) {
5508 frame->DecApproximateVisibleCount(aNonvisibleAction);
5513 void PresShell::RebuildApproximateFrameVisibilityDisplayList(
5514 const nsDisplayList& aList) {
5515 MOZ_ASSERT(!mApproximateFrameVisibilityVisited, "already visited?");
5516 mApproximateFrameVisibilityVisited = true;
5518 // Remove the entries of the mApproximatelyVisibleFrames hashtable and put
5519 // them in oldApproxVisibleFrames.
5520 VisibleFrames oldApproximatelyVisibleFrames;
5521 mApproximatelyVisibleFrames.SwapElements(oldApproximatelyVisibleFrames);
5523 MarkFramesInListApproximatelyVisible(aList);
5525 DecApproximateVisibleCount(oldApproximatelyVisibleFrames);
5528 /* static */
5529 void PresShell::ClearApproximateFrameVisibilityVisited(nsView* aView,
5530 bool aClear) {
5531 nsViewManager* vm = aView->GetViewManager();
5532 if (aClear) {
5533 PresShell* presShell = vm->GetPresShell();
5534 if (!presShell->mApproximateFrameVisibilityVisited) {
5535 presShell->ClearApproximatelyVisibleFramesList();
5537 presShell->mApproximateFrameVisibilityVisited = false;
5539 for (nsView* v = aView->GetFirstChild(); v; v = v->GetNextSibling()) {
5540 ClearApproximateFrameVisibilityVisited(v, v->GetViewManager() != vm);
5544 void PresShell::ClearApproximatelyVisibleFramesList(
5545 const Maybe<OnNonvisible>& aNonvisibleAction
5546 /* = Nothing() */) {
5547 DecApproximateVisibleCount(mApproximatelyVisibleFrames, aNonvisibleAction);
5548 mApproximatelyVisibleFrames.Clear();
5551 void PresShell::MarkFramesInSubtreeApproximatelyVisible(
5552 nsIFrame* aFrame, const nsRect& aRect, bool aRemoveOnly /* = false */) {
5553 MOZ_ASSERT(aFrame->PresShell() == this, "wrong presshell");
5555 if (aFrame->TrackingVisibility() && aFrame->StyleVisibility()->IsVisible() &&
5556 (!aRemoveOnly ||
5557 aFrame->GetVisibility() == Visibility::ApproximatelyVisible)) {
5558 MOZ_ASSERT(!AssumeAllFramesVisible());
5559 if (mApproximatelyVisibleFrames.EnsureInserted(aFrame)) {
5560 // The frame was added to mApproximatelyVisibleFrames, so increment its
5561 // visible count.
5562 aFrame->IncApproximateVisibleCount();
5566 nsSubDocumentFrame* subdocFrame = do_QueryFrame(aFrame);
5567 if (subdocFrame) {
5568 PresShell* presShell = subdocFrame->GetSubdocumentPresShellForPainting(
5569 nsSubDocumentFrame::IGNORE_PAINT_SUPPRESSION);
5570 if (presShell && !presShell->AssumeAllFramesVisible()) {
5571 nsRect rect = aRect;
5572 nsIFrame* root = presShell->GetRootFrame();
5573 if (root) {
5574 rect.MoveBy(aFrame->GetOffsetToCrossDoc(root));
5575 } else {
5576 rect.MoveBy(-aFrame->GetContentRectRelativeToSelf().TopLeft());
5578 rect = rect.ScaleToOtherAppUnitsRoundOut(
5579 aFrame->PresContext()->AppUnitsPerDevPixel(),
5580 presShell->GetPresContext()->AppUnitsPerDevPixel());
5582 presShell->RebuildApproximateFrameVisibility(&rect);
5584 return;
5587 nsRect rect = aRect;
5589 nsIScrollableFrame* scrollFrame = do_QueryFrame(aFrame);
5590 if (scrollFrame) {
5591 bool ignoreDisplayPort = false;
5592 if (nsLayoutUtils::IsMissingDisplayPortBaseRect(aFrame->GetContent())) {
5593 // We can properly set the base rect for root scroll frames on top level
5594 // and root content documents. Otherwise the base rect we compute might
5595 // be way too big without the limiting that
5596 // ScrollFrameHelper::DecideScrollableLayer does, so we just ignore the
5597 // displayport in that case.
5598 nsPresContext* pc = aFrame->PresContext();
5599 if (scrollFrame->IsRootScrollFrameOfDocument() &&
5600 (pc->IsRootContentDocument() || !pc->GetParentPresContext())) {
5601 nsRect baseRect =
5602 nsRect(nsPoint(0, 0),
5603 nsLayoutUtils::CalculateCompositionSizeForFrame(aFrame));
5604 nsLayoutUtils::SetDisplayPortBase(aFrame->GetContent(), baseRect);
5605 } else {
5606 ignoreDisplayPort = true;
5610 nsRect displayPort;
5611 bool usingDisplayport =
5612 !ignoreDisplayPort &&
5613 nsLayoutUtils::GetDisplayPortForVisibilityTesting(
5614 aFrame->GetContent(), &displayPort, RelativeTo::ScrollFrame);
5616 scrollFrame->NotifyApproximateFrameVisibilityUpdate(!usingDisplayport);
5618 if (usingDisplayport) {
5619 rect = displayPort;
5620 } else {
5621 rect = rect.Intersect(scrollFrame->GetScrollPortRect());
5623 rect = scrollFrame->ExpandRectToNearlyVisible(rect);
5626 bool preserves3DChildren = aFrame->Extend3DContext();
5628 // We assume all frames in popups are visible, so we skip them here.
5629 const nsIFrame::ChildListIDs skip = {nsIFrame::kPopupList,
5630 nsIFrame::kSelectPopupList};
5631 for (nsIFrame::ChildListIterator childLists(aFrame); !childLists.IsDone();
5632 childLists.Next()) {
5633 if (skip.contains(childLists.CurrentID())) {
5634 continue;
5637 for (nsIFrame* child : childLists.CurrentList()) {
5638 nsRect r = rect - child->GetPosition();
5639 if (!r.IntersectRect(r, child->GetVisualOverflowRect())) {
5640 continue;
5642 if (child->IsTransformed()) {
5643 // for children of a preserve3d element we just pass down the same dirty
5644 // rect
5645 if (!preserves3DChildren ||
5646 !child->Combines3DTransformWithAncestors()) {
5647 const nsRect overflow = child->GetVisualOverflowRectRelativeToSelf();
5648 nsRect out;
5649 if (nsDisplayTransform::UntransformRect(r, overflow, child, &out)) {
5650 r = out;
5651 } else {
5652 r.SetEmpty();
5656 MarkFramesInSubtreeApproximatelyVisible(child, r);
5661 void PresShell::RebuildApproximateFrameVisibility(
5662 nsRect* aRect, bool aRemoveOnly /* = false */) {
5663 MOZ_ASSERT(!mApproximateFrameVisibilityVisited, "already visited?");
5664 mApproximateFrameVisibilityVisited = true;
5666 nsIFrame* rootFrame = GetRootFrame();
5667 if (!rootFrame) {
5668 return;
5671 // Remove the entries of the mApproximatelyVisibleFrames hashtable and put
5672 // them in oldApproximatelyVisibleFrames.
5673 VisibleFrames oldApproximatelyVisibleFrames;
5674 mApproximatelyVisibleFrames.SwapElements(oldApproximatelyVisibleFrames);
5676 nsRect vis(nsPoint(0, 0), rootFrame->GetSize());
5677 if (aRect) {
5678 vis = *aRect;
5681 MarkFramesInSubtreeApproximatelyVisible(rootFrame, vis, aRemoveOnly);
5683 DecApproximateVisibleCount(oldApproximatelyVisibleFrames);
5686 void PresShell::UpdateApproximateFrameVisibility() {
5687 DoUpdateApproximateFrameVisibility(/* aRemoveOnly = */ false);
5690 void PresShell::DoUpdateApproximateFrameVisibility(bool aRemoveOnly) {
5691 MOZ_ASSERT(
5692 !mPresContext || mPresContext->IsRootContentDocument(),
5693 "Updating approximate frame visibility on a non-root content document?");
5695 mUpdateApproximateFrameVisibilityEvent.Revoke();
5697 if (mHaveShutDown || mIsDestroying) {
5698 return;
5701 // call update on that frame
5702 nsIFrame* rootFrame = GetRootFrame();
5703 if (!rootFrame) {
5704 ClearApproximatelyVisibleFramesList(Some(OnNonvisible::DiscardImages));
5705 return;
5708 RebuildApproximateFrameVisibility(/* aRect = */ nullptr, aRemoveOnly);
5709 ClearApproximateFrameVisibilityVisited(rootFrame->GetView(), true);
5711 #ifdef DEBUG_FRAME_VISIBILITY_DISPLAY_LIST
5712 // This can be used to debug the frame walker by comparing beforeFrameList
5713 // and mApproximatelyVisibleFrames in RebuildFrameVisibilityDisplayList to see
5714 // if they produce the same results (mApproximatelyVisibleFrames holds the
5715 // frames the display list thinks are visible, beforeFrameList holds the
5716 // frames the frame walker thinks are visible).
5717 nsDisplayListBuilder builder(
5718 rootFrame, nsDisplayListBuilderMode::FRAME_VISIBILITY, false);
5719 nsRect updateRect(nsPoint(0, 0), rootFrame->GetSize());
5720 nsIFrame* rootScroll = GetRootScrollFrame();
5721 if (rootScroll) {
5722 nsIContent* content = rootScroll->GetContent();
5723 if (content) {
5724 Unused << nsLayoutUtils::GetDisplayPortForVisibilityTesting(
5725 content, &updateRect, RelativeTo::ScrollFrame);
5728 if (IgnoringViewportScrolling()) {
5729 builder.SetIgnoreScrollFrame(rootScroll);
5732 builder.IgnorePaintSuppression();
5733 builder.EnterPresShell(rootFrame);
5734 nsDisplayList list;
5735 rootFrame->BuildDisplayListForStackingContext(&builder, updateRect, &list);
5736 builder.LeavePresShell(rootFrame, &list);
5738 RebuildApproximateFrameVisibilityDisplayList(list);
5740 ClearApproximateFrameVisibilityVisited(rootFrame->GetView(), true);
5742 list.DeleteAll(&builder);
5743 #endif
5746 bool PresShell::AssumeAllFramesVisible() {
5747 static bool sFrameVisibilityEnabled = true;
5748 static bool sFrameVisibilityPrefCached = false;
5750 if (!sFrameVisibilityPrefCached) {
5751 Preferences::AddBoolVarCache(&sFrameVisibilityEnabled,
5752 "layout.framevisibility.enabled", true);
5753 sFrameVisibilityPrefCached = true;
5756 if (!sFrameVisibilityEnabled || !mPresContext || !mDocument) {
5757 return true;
5760 // We assume all frames are visible in print, print preview, chrome, and
5761 // resource docs and don't keep track of them.
5762 if (mPresContext->Type() == nsPresContext::eContext_PrintPreview ||
5763 mPresContext->Type() == nsPresContext::eContext_Print ||
5764 mPresContext->IsChrome() || mDocument->IsResourceDoc()) {
5765 return true;
5768 // If we're assuming all frames are visible in the top level content
5769 // document, we need to in subdocuments as well. Otherwise we can get in a
5770 // situation where things like animations won't work in subdocuments because
5771 // their frames appear not to be visible, since we won't schedule an image
5772 // visibility update if the top level content document is assuming all
5773 // frames are visible.
5775 // Note that it's not safe to call IsRootContentDocument() if we're
5776 // currently being destroyed, so we have to check that first.
5777 if (!mHaveShutDown && !mIsDestroying &&
5778 !mPresContext->IsRootContentDocumentInProcess()) {
5779 nsPresContext* presContext =
5780 mPresContext->GetToplevelContentDocumentPresContext();
5781 if (presContext && presContext->PresShell()->AssumeAllFramesVisible()) {
5782 return true;
5786 return false;
5789 void PresShell::ScheduleApproximateFrameVisibilityUpdateSoon() {
5790 if (AssumeAllFramesVisible()) {
5791 return;
5794 if (!mPresContext) {
5795 return;
5798 nsRefreshDriver* refreshDriver = mPresContext->RefreshDriver();
5799 if (!refreshDriver) {
5800 return;
5803 // Ask the refresh driver to update frame visibility soon.
5804 refreshDriver->ScheduleFrameVisibilityUpdate();
5807 void PresShell::ScheduleApproximateFrameVisibilityUpdateNow() {
5808 if (AssumeAllFramesVisible()) {
5809 return;
5812 if (!mPresContext->IsRootContentDocument()) {
5813 nsPresContext* presContext =
5814 mPresContext->GetToplevelContentDocumentPresContext();
5815 if (!presContext) return;
5816 MOZ_ASSERT(presContext->IsRootContentDocument(),
5817 "Didn't get a root prescontext from "
5818 "GetToplevelContentDocumentPresContext?");
5819 presContext->PresShell()->ScheduleApproximateFrameVisibilityUpdateNow();
5820 return;
5823 if (mHaveShutDown || mIsDestroying) {
5824 return;
5827 if (mUpdateApproximateFrameVisibilityEvent.IsPending()) {
5828 return;
5831 RefPtr<nsRunnableMethod<PresShell>> event =
5832 NewRunnableMethod("PresShell::UpdateApproximateFrameVisibility", this,
5833 &PresShell::UpdateApproximateFrameVisibility);
5834 nsresult rv = mDocument->Dispatch(TaskCategory::Other, do_AddRef(event));
5836 if (NS_SUCCEEDED(rv)) {
5837 mUpdateApproximateFrameVisibilityEvent = std::move(event);
5841 void PresShell::EnsureFrameInApproximatelyVisibleList(nsIFrame* aFrame) {
5842 if (!aFrame->TrackingVisibility()) {
5843 return;
5846 if (AssumeAllFramesVisible()) {
5847 aFrame->IncApproximateVisibleCount();
5848 return;
5851 #ifdef DEBUG
5852 // Make sure it's in this pres shell.
5853 nsCOMPtr<nsIContent> content = aFrame->GetContent();
5854 if (content) {
5855 PresShell* presShell = content->OwnerDoc()->GetPresShell();
5856 MOZ_ASSERT(!presShell || presShell == this, "wrong shell");
5858 #endif
5860 if (mApproximatelyVisibleFrames.EnsureInserted(aFrame)) {
5861 // We inserted a new entry.
5862 aFrame->IncApproximateVisibleCount();
5866 void PresShell::RemoveFrameFromApproximatelyVisibleList(nsIFrame* aFrame) {
5867 #ifdef DEBUG
5868 // Make sure it's in this pres shell.
5869 nsCOMPtr<nsIContent> content = aFrame->GetContent();
5870 if (content) {
5871 PresShell* presShell = content->OwnerDoc()->GetPresShell();
5872 MOZ_ASSERT(!presShell || presShell == this, "wrong shell");
5874 #endif
5876 if (AssumeAllFramesVisible()) {
5877 MOZ_ASSERT(mApproximatelyVisibleFrames.Count() == 0,
5878 "Shouldn't have any frames in the table");
5879 return;
5882 if (mApproximatelyVisibleFrames.EnsureRemoved(aFrame) &&
5883 aFrame->TrackingVisibility()) {
5884 // aFrame was in the hashtable, and we're still tracking its visibility,
5885 // so we need to decrement its visible count.
5886 aFrame->DecApproximateVisibleCount();
5890 class nsAutoNotifyDidPaint {
5891 public:
5892 nsAutoNotifyDidPaint(PresShell* aShell, PaintFlags aFlags)
5893 : mShell(aShell), mFlags(aFlags) {}
5894 ~nsAutoNotifyDidPaint() {
5895 if (!!(mFlags & PaintFlags::PaintComposite)) {
5896 mShell->GetPresContext()->NotifyDidPaintForSubtree();
5900 private:
5901 PresShell* mShell;
5902 PaintFlags mFlags;
5905 void PresShell::Paint(nsView* aViewToPaint, const nsRegion& aDirtyRegion,
5906 PaintFlags aFlags) {
5907 nsCString url;
5908 nsIURI* uri = mDocument->GetDocumentURI();
5909 Document* contentRoot = GetPrimaryContentDocument();
5910 if (contentRoot) {
5911 uri = contentRoot->GetDocumentURI();
5913 url = uri ? uri->GetSpecOrDefault() : NS_LITERAL_CSTRING("N/A");
5914 #ifdef MOZ_GECKO_PROFILER
5915 AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING("PresShell::Paint", GRAPHICS, url);
5916 #endif
5918 Maybe<js::AutoAssertNoContentJS> nojs;
5920 // On Android, Flash can call into content JS during painting, so we can't
5921 // assert there. However, we don't rely on this assertion on Android because
5922 // we don't paint while JS is running.
5923 #if !defined(MOZ_WIDGET_ANDROID)
5924 if (!(aFlags & PaintFlags::PaintComposite)) {
5925 // We need to allow content JS when the flag is set since we may trigger
5926 // MozAfterPaint events in content in those cases.
5927 nojs.emplace(dom::danger::GetJSContext());
5929 #endif
5931 NS_ASSERTION(!mIsDestroying, "painting a destroyed PresShell");
5932 NS_ASSERTION(aViewToPaint, "null view");
5934 MOZ_ASSERT(!mApproximateFrameVisibilityVisited, "Should have been cleared");
5936 if (!mIsActive) {
5937 return;
5940 if (StaticPrefs::apz_keyboard_enabled_AtStartup()) {
5941 // Update the focus target for async keyboard scrolling. This will be
5942 // forwarded to APZ by nsDisplayList::PaintRoot. We need to to do this
5943 // before we enter the paint phase because dispatching eVoid events can
5944 // cause layout to happen.
5945 mAPZFocusTarget = FocusTarget(this, mAPZFocusSequenceNumber);
5948 nsPresContext* presContext = GetPresContext();
5949 AUTO_LAYOUT_PHASE_ENTRY_POINT(presContext, Paint);
5951 nsIFrame* frame = aViewToPaint->GetFrame();
5953 LayerManager* layerManager = aViewToPaint->GetWidget()->GetLayerManager();
5954 NS_ASSERTION(layerManager, "Must be in paint event");
5955 bool shouldInvalidate = layerManager->NeedsWidgetInvalidation();
5957 nsAutoNotifyDidPaint notifyDidPaint(this, aFlags);
5959 // Whether or not we should set first paint when painting is suppressed
5960 // is debatable. For now we'll do it because B2G relied on first paint
5961 // to configure the viewport and we only want to do that when we have
5962 // real content to paint. See Bug 798245
5963 if (mIsFirstPaint && !mPaintingSuppressed) {
5964 layerManager->SetIsFirstPaint();
5965 mIsFirstPaint = false;
5968 if (!layerManager->BeginTransaction(url)) {
5969 return;
5972 // Send an updated focus target with this transaction. Be sure to do this
5973 // before we paint in the case this is an empty transaction.
5974 layerManager->SetFocusTarget(mAPZFocusTarget);
5976 if (frame) {
5977 // Try to do an empty transaction, if the frame tree does not
5978 // need to be updated. Do not try to do an empty transaction on
5979 // a non-retained layer manager (like the BasicLayerManager that
5980 // draws the window title bar on Mac), because a) it won't work
5981 // and b) below we don't want to clear NS_FRAME_UPDATE_LAYER_TREE,
5982 // that will cause us to forget to update the real layer manager!
5984 if (!(aFlags & PaintFlags::PaintLayers)) {
5985 if (layerManager->EndEmptyTransaction()) {
5986 return;
5988 NS_WARNING("Must complete empty transaction when compositing!");
5991 if (!(aFlags & PaintFlags::PaintSyncDecodeImages) &&
5992 !(frame->GetStateBits() & NS_FRAME_UPDATE_LAYER_TREE) &&
5993 !mNextPaintCompressed) {
5994 NotifySubDocInvalidationFunc computeInvalidFunc =
5995 presContext->MayHavePaintEventListenerInSubDocument()
5996 ? nsPresContext::NotifySubDocInvalidation
5997 : 0;
5998 bool computeInvalidRect =
5999 computeInvalidFunc ||
6000 (layerManager->GetBackendType() == LayersBackend::LAYERS_BASIC);
6002 UniquePtr<LayerProperties> props;
6003 // For WR, the layermanager has no root layer. We want to avoid
6004 // calling ComputeDifferences in that case because it assumes non-null
6005 // and crashes.
6006 if (computeInvalidRect && layerManager->GetRoot()) {
6007 props = LayerProperties::CloneFrom(layerManager->GetRoot());
6010 MaybeSetupTransactionIdAllocator(layerManager, presContext);
6012 if (layerManager->EndEmptyTransaction(
6013 (aFlags & PaintFlags::PaintComposite)
6014 ? LayerManager::END_DEFAULT
6015 : LayerManager::END_NO_COMPOSITE)) {
6016 nsIntRegion invalid;
6017 bool areaOverflowed = false;
6018 if (props) {
6019 if (!props->ComputeDifferences(layerManager->GetRoot(), invalid,
6020 computeInvalidFunc)) {
6021 areaOverflowed = true;
6023 } else {
6024 LayerProperties::ClearInvalidations(layerManager->GetRoot());
6026 if (props && !areaOverflowed) {
6027 if (!invalid.IsEmpty()) {
6028 nsIntRect bounds = invalid.GetBounds();
6029 nsRect rect(presContext->DevPixelsToAppUnits(bounds.x),
6030 presContext->DevPixelsToAppUnits(bounds.y),
6031 presContext->DevPixelsToAppUnits(bounds.width),
6032 presContext->DevPixelsToAppUnits(bounds.height));
6033 if (shouldInvalidate) {
6034 aViewToPaint->GetViewManager()->InvalidateViewNoSuppression(
6035 aViewToPaint, rect);
6037 presContext->NotifyInvalidation(
6038 layerManager->GetLastTransactionId(), bounds);
6040 } else if (shouldInvalidate) {
6041 aViewToPaint->GetViewManager()->InvalidateView(aViewToPaint);
6044 frame->UpdatePaintCountForPaintedPresShells();
6045 return;
6048 frame->RemoveStateBits(NS_FRAME_UPDATE_LAYER_TREE);
6050 if (frame) {
6051 frame->ClearPresShellsFromLastPaint();
6054 nscolor bgcolor = ComputeBackstopColor(aViewToPaint);
6055 PaintFrameFlags flags =
6056 PaintFrameFlags::WidgetLayers | PaintFrameFlags::ExistingTransaction;
6057 if (!(aFlags & PaintFlags::PaintComposite)) {
6058 flags |= PaintFrameFlags::NoComposite;
6060 if (aFlags & PaintFlags::PaintSyncDecodeImages) {
6061 flags |= PaintFrameFlags::SyncDecodeImages;
6063 if (mNextPaintCompressed) {
6064 flags |= PaintFrameFlags::Compressed;
6065 mNextPaintCompressed = false;
6067 if (layerManager->GetBackendType() == layers::LayersBackend::LAYERS_WR) {
6068 flags |= PaintFrameFlags::ForWebRender;
6071 if (frame) {
6072 // We can paint directly into the widget using its layer manager.
6073 nsLayoutUtils::PaintFrame(nullptr, frame, aDirtyRegion, bgcolor,
6074 nsDisplayListBuilderMode::Painting, flags);
6076 // When recording/replaying, create a checkpoint after every paint. This
6077 // can cause content JS to run, so reset |nojs|.
6078 if (recordreplay::IsRecordingOrReplaying()) {
6079 nojs.reset();
6080 recordreplay::child::CreateCheckpoint();
6083 return;
6086 if (layerManager->GetBackendType() == layers::LayersBackend::LAYERS_WR) {
6087 nsPresContext* pc = GetPresContext();
6088 LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
6089 pc->GetVisibleArea(), pc->AppUnitsPerDevPixel());
6090 bgcolor = NS_ComposeColors(bgcolor, mCanvasBackgroundColor);
6091 WebRenderBackgroundData data(wr::ToLayoutRect(bounds),
6092 wr::ToColorF(ToDeviceColor(bgcolor)));
6093 WrFiltersHolder wrFilters;
6095 MaybeSetupTransactionIdAllocator(layerManager, presContext);
6096 layerManager->AsWebRenderLayerManager()->EndTransactionWithoutLayer(
6097 nullptr, nullptr, std::move(wrFilters), &data);
6098 return;
6101 RefPtr<ColorLayer> root = layerManager->CreateColorLayer();
6102 if (root) {
6103 nsPresContext* pc = GetPresContext();
6104 nsIntRect bounds =
6105 pc->GetVisibleArea().ToOutsidePixels(pc->AppUnitsPerDevPixel());
6106 bgcolor = NS_ComposeColors(bgcolor, mCanvasBackgroundColor);
6107 root->SetColor(Color::FromABGR(bgcolor));
6108 root->SetVisibleRegion(LayerIntRegion::FromUnknownRegion(bounds));
6109 layerManager->SetRoot(root);
6111 MaybeSetupTransactionIdAllocator(layerManager, presContext);
6112 layerManager->EndTransaction(nullptr, nullptr,
6113 (aFlags & PaintFlags::PaintComposite)
6114 ? LayerManager::END_DEFAULT
6115 : LayerManager::END_NO_COMPOSITE);
6118 // static
6119 void PresShell::SetCapturingContent(nsIContent* aContent, CaptureFlags aFlags) {
6120 // If capture was set for pointer lock, don't unlock unless we are coming
6121 // out of pointer lock explicitly.
6122 if (!aContent && sCapturingContentInfo.mPointerLock &&
6123 !(aFlags & CaptureFlags::PointerLock)) {
6124 return;
6127 sCapturingContentInfo.mContent = nullptr;
6129 // only set capturing content if allowed or the
6130 // CaptureFlags::IgnoreAllowedState or CaptureFlags::PointerLock are used.
6131 if ((aFlags & CaptureFlags::IgnoreAllowedState) ||
6132 sCapturingContentInfo.mAllowed || (aFlags & CaptureFlags::PointerLock)) {
6133 if (aContent) {
6134 sCapturingContentInfo.mContent = aContent;
6136 // CaptureFlags::PointerLock is the same as
6137 // CaptureFlags::RetargetToElement & CaptureFlags::IgnoreAllowedState.
6138 sCapturingContentInfo.mRetargetToElement =
6139 !!(aFlags & CaptureFlags::RetargetToElement) ||
6140 !!(aFlags & CaptureFlags::PointerLock);
6141 sCapturingContentInfo.mPreventDrag =
6142 !!(aFlags & CaptureFlags::PreventDragStart);
6143 sCapturingContentInfo.mPointerLock = !!(aFlags & CaptureFlags::PointerLock);
6147 nsIContent* PresShell::GetCurrentEventContent() {
6148 if (mCurrentEventContent &&
6149 mCurrentEventContent->GetComposedDoc() != mDocument) {
6150 mCurrentEventContent = nullptr;
6151 mCurrentEventFrame = nullptr;
6153 return mCurrentEventContent;
6156 nsIFrame* PresShell::GetCurrentEventFrame() {
6157 if (MOZ_UNLIKELY(mIsDestroying)) {
6158 return nullptr;
6161 // GetCurrentEventContent() makes sure the content is still in the
6162 // same document that this pres shell belongs to. If not, then the
6163 // frame shouldn't get an event, nor should we even assume its safe
6164 // to try and find the frame.
6165 nsIContent* content = GetCurrentEventContent();
6166 if (!mCurrentEventFrame && content) {
6167 mCurrentEventFrame = content->GetPrimaryFrame();
6168 MOZ_ASSERT(!mCurrentEventFrame ||
6169 mCurrentEventFrame->PresContext()->GetPresShell() == this);
6171 return mCurrentEventFrame;
6174 already_AddRefed<nsIContent> PresShell::GetEventTargetContent(
6175 WidgetEvent* aEvent) {
6176 nsCOMPtr<nsIContent> content = GetCurrentEventContent();
6177 if (!content) {
6178 nsIFrame* currentEventFrame = GetCurrentEventFrame();
6179 if (currentEventFrame) {
6180 currentEventFrame->GetContentForEvent(aEvent, getter_AddRefs(content));
6181 NS_ASSERTION(!content || content->GetComposedDoc() == mDocument,
6182 "handing out content from a different doc");
6185 return content.forget();
6188 void PresShell::PushCurrentEventInfo(nsIFrame* aFrame, nsIContent* aContent) {
6189 if (mCurrentEventFrame || mCurrentEventContent) {
6190 mCurrentEventFrameStack.InsertElementAt(0, mCurrentEventFrame);
6191 mCurrentEventContentStack.InsertObjectAt(mCurrentEventContent, 0);
6193 mCurrentEventFrame = aFrame;
6194 mCurrentEventContent = aContent;
6197 void PresShell::PopCurrentEventInfo() {
6198 mCurrentEventFrame = nullptr;
6199 mCurrentEventContent = nullptr;
6201 if (0 != mCurrentEventFrameStack.Length()) {
6202 mCurrentEventFrame = mCurrentEventFrameStack.ElementAt(0);
6203 mCurrentEventFrameStack.RemoveElementAt(0);
6204 mCurrentEventContent = mCurrentEventContentStack.ObjectAt(0);
6205 mCurrentEventContentStack.RemoveObjectAt(0);
6207 // Don't use it if it has moved to a different document.
6208 if (mCurrentEventContent &&
6209 mCurrentEventContent->GetComposedDoc() != mDocument) {
6210 mCurrentEventContent = nullptr;
6211 mCurrentEventFrame = nullptr;
6216 // static
6217 bool PresShell::EventHandler::InZombieDocument(nsIContent* aContent) {
6218 // If a content node points to a null document, or the document is not
6219 // attached to a window, then it is possibly in a zombie document,
6220 // about to be replaced by a newly loading document.
6221 // Such documents cannot handle DOM events.
6222 // It might actually be in a node not attached to any document,
6223 // in which case there is not parent presshell to retarget it to.
6224 Document* doc = aContent->GetComposedDoc();
6225 return !doc || !doc->GetWindow();
6228 already_AddRefed<nsPIDOMWindowOuter> PresShell::GetRootWindow() {
6229 nsCOMPtr<nsPIDOMWindowOuter> window = mDocument->GetWindow();
6230 if (window) {
6231 nsCOMPtr<nsPIDOMWindowOuter> rootWindow = window->GetPrivateRoot();
6232 NS_ASSERTION(rootWindow, "nsPIDOMWindow::GetPrivateRoot() returns NULL");
6233 return rootWindow.forget();
6236 // If we don't have DOM window, we're zombie, we should find the root window
6237 // with our parent shell.
6238 RefPtr<PresShell> parentPresShell = GetParentPresShellForEventHandling();
6239 NS_ENSURE_TRUE(parentPresShell, nullptr);
6240 return parentPresShell->GetRootWindow();
6243 already_AddRefed<nsPIDOMWindowOuter>
6244 PresShell::GetFocusedDOMWindowInOurWindow() {
6245 nsCOMPtr<nsPIDOMWindowOuter> rootWindow = GetRootWindow();
6246 NS_ENSURE_TRUE(rootWindow, nullptr);
6247 nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
6248 nsFocusManager::GetFocusedDescendant(rootWindow,
6249 nsFocusManager::eIncludeAllDescendants,
6250 getter_AddRefs(focusedWindow));
6251 return focusedWindow.forget();
6254 already_AddRefed<nsIContent> PresShell::GetFocusedContentInOurWindow() const {
6255 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
6256 if (fm && mDocument) {
6257 RefPtr<Element> focusedElement;
6258 fm->GetFocusedElementForWindow(mDocument->GetWindow(), false, nullptr,
6259 getter_AddRefs(focusedElement));
6260 return focusedElement.forget();
6262 return nullptr;
6265 already_AddRefed<PresShell> PresShell::GetParentPresShellForEventHandling() {
6266 NS_ENSURE_TRUE(mPresContext, nullptr);
6268 // Now, find the parent pres shell and send the event there
6269 nsCOMPtr<nsIDocShellTreeItem> treeItem = mPresContext->GetDocShell();
6270 if (!treeItem) {
6271 treeItem = mForwardingContainer.get();
6274 // Might have gone away, or never been around to start with
6275 NS_ENSURE_TRUE(treeItem, nullptr);
6277 nsCOMPtr<nsIDocShellTreeItem> parentTreeItem;
6278 treeItem->GetInProcessParent(getter_AddRefs(parentTreeItem));
6279 nsCOMPtr<nsIDocShell> parentDocShell = do_QueryInterface(parentTreeItem);
6280 NS_ENSURE_TRUE(parentDocShell && treeItem != parentTreeItem, nullptr);
6282 RefPtr<PresShell> parentPresShell = parentDocShell->GetPresShell();
6283 return parentPresShell.forget();
6286 nsresult PresShell::EventHandler::RetargetEventToParent(
6287 WidgetGUIEvent* aGUIEvent, nsEventStatus* aEventStatus) {
6288 // Send this events straight up to the parent pres shell.
6289 // We do this for keystroke events in zombie documents or if either a frame
6290 // or a root content is not present.
6291 // That way at least the UI key bindings can work.
6293 RefPtr<PresShell> parentPresShell = GetParentPresShellForEventHandling();
6294 NS_ENSURE_TRUE(parentPresShell, NS_ERROR_FAILURE);
6296 // Fake the event as though it's from the parent pres shell's root frame.
6297 return parentPresShell->HandleEvent(parentPresShell->GetRootFrame(),
6298 aGUIEvent, true, aEventStatus);
6301 void PresShell::DisableNonTestMouseEvents(bool aDisable) {
6302 sDisableNonTestMouseEvents = aDisable;
6305 void PresShell::RecordMouseLocation(WidgetGUIEvent* aEvent) {
6306 if (!mPresContext) return;
6308 if (!mPresContext->IsRoot()) {
6309 PresShell* rootPresShell = GetRootPresShell();
6310 if (rootPresShell) {
6311 rootPresShell->RecordMouseLocation(aEvent);
6313 return;
6316 if ((aEvent->mMessage == eMouseMove &&
6317 aEvent->AsMouseEvent()->mReason == WidgetMouseEvent::eReal) ||
6318 aEvent->mMessage == eMouseEnterIntoWidget ||
6319 aEvent->mMessage == eMouseDown || aEvent->mMessage == eMouseUp) {
6320 nsIFrame* rootFrame = GetRootFrame();
6321 if (!rootFrame) {
6322 nsView* rootView = mViewManager->GetRootView();
6323 mMouseLocation = nsLayoutUtils::TranslateWidgetToView(
6324 mPresContext, aEvent->mWidget, aEvent->mRefPoint, rootView);
6325 mMouseEventTargetGuid = InputAPZContext::GetTargetLayerGuid();
6326 } else {
6327 mMouseLocation =
6328 nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, rootFrame);
6329 mMouseEventTargetGuid = InputAPZContext::GetTargetLayerGuid();
6331 #ifdef DEBUG_MOUSE_LOCATION
6332 if (aEvent->mMessage == eMouseEnterIntoWidget) {
6333 printf("[ps=%p]got mouse enter for %p\n", this, aEvent->mWidget);
6335 printf("[ps=%p]setting mouse location to (%d,%d)\n", this, mMouseLocation.x,
6336 mMouseLocation.y);
6337 #endif
6338 if (aEvent->mMessage == eMouseEnterIntoWidget) {
6339 SynthesizeMouseMove(false);
6341 } else if (aEvent->mMessage == eMouseExitFromWidget) {
6342 // Although we only care about the mouse moving into an area for which this
6343 // pres shell doesn't receive mouse move events, we don't check which widget
6344 // the mouse exit was for since this seems to vary by platform. Hopefully
6345 // this won't matter at all since we'll get the mouse move or enter after
6346 // the mouse exit when the mouse moves from one of our widgets into another.
6347 mMouseLocation = nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
6348 mMouseEventTargetGuid = InputAPZContext::GetTargetLayerGuid();
6349 #ifdef DEBUG_MOUSE_LOCATION
6350 printf("[ps=%p]got mouse exit for %p\n", this, aEvent->mWidget);
6351 printf("[ps=%p]clearing mouse location\n", this);
6352 #endif
6356 // static
6357 nsIFrame* PresShell::EventHandler::GetNearestFrameContainingPresShell(
6358 PresShell* aPresShell) {
6359 nsView* view = aPresShell->GetViewManager()->GetRootView();
6360 while (view && !view->GetFrame()) {
6361 view = view->GetParent();
6364 nsIFrame* frame = nullptr;
6365 if (view) {
6366 frame = view->GetFrame();
6369 return frame;
6372 static bool FlushThrottledStyles(Document* aDocument, void* aData) {
6373 PresShell* presShell = aDocument->GetPresShell();
6374 if (presShell && presShell->IsVisible()) {
6375 nsPresContext* presContext = presShell->GetPresContext();
6376 if (presContext) {
6377 presContext->RestyleManager()->UpdateOnlyAnimationStyles();
6381 aDocument->EnumerateSubDocuments(FlushThrottledStyles, nullptr);
6382 return true;
6385 bool PresShell::CanDispatchEvent(const WidgetGUIEvent* aEvent) const {
6386 bool rv =
6387 mPresContext && !mHaveShutDown && nsContentUtils::IsSafeToRunScript();
6388 if (aEvent) {
6389 rv &= (aEvent && aEvent->mWidget && !aEvent->mWidget->Destroyed());
6391 return rv;
6394 /* static */
6395 PresShell* PresShell::GetShellForEventTarget(nsIFrame* aFrame,
6396 nsIContent* aContent) {
6397 if (aFrame) {
6398 return aFrame->PresShell();
6400 if (aContent) {
6401 Document* doc = aContent->GetComposedDoc();
6402 if (!doc) {
6403 return nullptr;
6405 return doc->GetPresShell();
6407 return nullptr;
6410 /* static */
6411 PresShell* PresShell::GetShellForTouchEvent(WidgetGUIEvent* aEvent) {
6412 switch (aEvent->mMessage) {
6413 case eTouchMove:
6414 case eTouchCancel:
6415 case eTouchEnd: {
6416 // get the correct shell to dispatch to
6417 WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
6418 for (dom::Touch* touch : touchEvent->mTouches) {
6419 if (!touch) {
6420 return nullptr;
6423 RefPtr<dom::Touch> oldTouch =
6424 TouchManager::GetCapturedTouch(touch->Identifier());
6425 if (!oldTouch) {
6426 return nullptr;
6429 nsCOMPtr<nsIContent> content = do_QueryInterface(oldTouch->GetTarget());
6430 if (!content) {
6431 return nullptr;
6434 nsIFrame* contentFrame = content->GetPrimaryFrame();
6435 if (!contentFrame) {
6436 return nullptr;
6439 PresShell* presShell = contentFrame->PresContext()->GetPresShell();
6440 if (presShell) {
6441 return presShell;
6444 return nullptr;
6446 default:
6447 return nullptr;
6451 nsresult PresShell::HandleEvent(nsIFrame* aFrameForPresShell,
6452 WidgetGUIEvent* aGUIEvent,
6453 bool aDontRetargetEvents,
6454 nsEventStatus* aEventStatus) {
6455 MOZ_ASSERT(aGUIEvent);
6456 EventHandler eventHandler(*this);
6457 return eventHandler.HandleEvent(aFrameForPresShell, aGUIEvent,
6458 aDontRetargetEvents, aEventStatus);
6461 nsresult PresShell::EventHandler::HandleEvent(nsIFrame* aFrameForPresShell,
6462 WidgetGUIEvent* aGUIEvent,
6463 bool aDontRetargetEvents,
6464 nsEventStatus* aEventStatus) {
6465 MOZ_ASSERT(aGUIEvent);
6466 MOZ_DIAGNOSTIC_ASSERT(aGUIEvent->IsTrusted());
6467 MOZ_ASSERT(aEventStatus);
6469 #ifdef MOZ_TASK_TRACER
6470 Maybe<AutoSourceEvent> taskTracerEvent;
6471 if (MOZ_UNLIKELY(IsStartLogging())) {
6472 // Make touch events, mouse events and hardware key events to be
6473 // the source events of TaskTracer, and originate the rest
6474 // correlation tasks from here.
6475 SourceEventType type = SourceEventType::Unknown;
6476 if (aGUIEvent->AsTouchEvent()) {
6477 type = SourceEventType::Touch;
6478 } else if (aGUIEvent->AsMouseEvent()) {
6479 type = SourceEventType::Mouse;
6480 } else if (aGUIEvent->AsKeyboardEvent()) {
6481 type = SourceEventType::Key;
6483 taskTracerEvent.emplace(type);
6485 #endif
6487 NS_ASSERTION(aFrameForPresShell, "aFrameForPresShell should be not null");
6489 // Update the latest focus sequence number with this new sequence number;
6490 // the next transasction that gets sent to the compositor will carry this over
6491 if (mPresShell->mAPZFocusSequenceNumber < aGUIEvent->mFocusSequenceNumber) {
6492 mPresShell->mAPZFocusSequenceNumber = aGUIEvent->mFocusSequenceNumber;
6495 if (mPresShell->IsDestroying() ||
6496 (PresShell::sDisableNonTestMouseEvents &&
6497 !aGUIEvent->mFlags.mIsSynthesizedForTests &&
6498 aGUIEvent->HasMouseEventMessage())) {
6499 return NS_OK;
6502 mPresShell->RecordMouseLocation(aGUIEvent);
6504 if (MaybeHandleEventWithAccessibleCaret(aGUIEvent, aEventStatus)) {
6505 // Probably handled by AccessibleCaretEventHub.
6506 return NS_OK;
6509 if (MaybeDiscardEvent(aGUIEvent)) {
6510 // Nobody cannot handle the event for now.
6511 return NS_OK;
6514 if (!aDontRetargetEvents) {
6515 // If aGUIEvent should be handled in another PresShell, we should call its
6516 // HandleEvent() and do nothing here.
6517 nsresult rv = NS_OK;
6518 if (MaybeHandleEventWithAnotherPresShell(aFrameForPresShell, aGUIEvent,
6519 aEventStatus, &rv)) {
6520 // Handled by another PresShell or nobody can handle the event.
6521 return rv;
6525 if (MaybeDiscardOrDelayKeyboardEvent(aGUIEvent)) {
6526 // The event is discarded or put into the delayed event queue.
6527 return NS_OK;
6530 if (aGUIEvent->IsUsingCoordinates()) {
6531 return HandleEventUsingCoordinates(aFrameForPresShell, aGUIEvent,
6532 aEventStatus, aDontRetargetEvents);
6535 // Activation events need to be dispatched even if no frame was found, since
6536 // we don't want the focus to be out of sync.
6537 if (!aFrameForPresShell) {
6538 if (!NS_EVENT_NEEDS_FRAME(aGUIEvent)) {
6539 // Push nullptr for both current event target content and frame since
6540 // there is no frame but the event does not require a frame.
6541 AutoCurrentEventInfoSetter eventInfoSetter(*this);
6542 return HandleEventWithCurrentEventInfo(aGUIEvent, aEventStatus, true,
6543 nullptr);
6546 if (aGUIEvent->HasKeyEventMessage()) {
6547 // Keypress events in new blank tabs should not be completely thrown away.
6548 // Retarget them -- the parent chrome shell might make use of them.
6549 return RetargetEventToParent(aGUIEvent, aEventStatus);
6552 return NS_OK;
6555 if (aGUIEvent->IsTargetedAtFocusedContent()) {
6556 return HandleEventAtFocusedContent(aGUIEvent, aEventStatus);
6559 return HandleEventWithFrameForPresShell(aFrameForPresShell, aGUIEvent,
6560 aEventStatus);
6563 nsresult PresShell::EventHandler::HandleEventUsingCoordinates(
6564 nsIFrame* aFrameForPresShell, WidgetGUIEvent* aGUIEvent,
6565 nsEventStatus* aEventStatus, bool aDontRetargetEvents) {
6566 MOZ_ASSERT(aGUIEvent);
6567 MOZ_ASSERT(aGUIEvent->IsUsingCoordinates());
6568 MOZ_ASSERT(aEventStatus);
6570 // Flush pending notifications to handle the event with the latest layout.
6571 // But if it causes destroying the frame for mPresShell, stop handling the
6572 // event. (why?)
6573 AutoWeakFrame weakFrame(aFrameForPresShell);
6574 MaybeFlushPendingNotifications(aGUIEvent);
6575 if (!weakFrame.IsAlive()) {
6576 *aEventStatus = nsEventStatus_eIgnore;
6577 return NS_OK;
6580 // XXX Retrieving capturing content here. However, some of the following
6581 // methods allow to run script. So, isn't it possible the capturing
6582 // content outdated?
6583 nsCOMPtr<nsIContent> capturingContent =
6584 EventHandler::GetCapturingContentFor(aGUIEvent);
6586 if (GetDocument() && aGUIEvent->mClass == eTouchEventClass) {
6587 Document::UnlockPointer();
6590 nsIFrame* frameForPresShell = MaybeFlushThrottledStyles(aFrameForPresShell);
6591 if (NS_WARN_IF(!frameForPresShell)) {
6592 return NS_OK;
6595 bool isCapturingContentIgnored = false;
6596 bool isCaptureRetargeted = false;
6597 nsIFrame* rootFrameToHandleEvent = ComputeRootFrameToHandleEvent(
6598 frameForPresShell, aGUIEvent, capturingContent,
6599 &isCapturingContentIgnored, &isCaptureRetargeted);
6600 if (isCapturingContentIgnored) {
6601 capturingContent = nullptr;
6604 // The order to generate pointer event is
6605 // 1. check pending pointer capture.
6606 // 2. check if there is a capturing content.
6607 // 3. hit test
6608 // 4. dispatch pointer events
6609 // 5. check whether the targets of all Touch instances are in the same
6610 // document and suppress invalid instances.
6611 // 6. dispatch mouse or touch events.
6613 // Try to keep frame for following check, because frame can be damaged
6614 // during MaybeProcessPointerCapture.
6616 AutoWeakFrame frameKeeper(rootFrameToHandleEvent);
6617 PointerEventHandler::MaybeProcessPointerCapture(aGUIEvent);
6618 // Prevent application crashes, in case damaged frame.
6619 if (!frameKeeper.IsAlive()) {
6620 NS_WARNING("Nothing to handle this event!");
6621 return NS_OK;
6625 // Only capture mouse events and pointer events.
6626 nsCOMPtr<nsIContent> pointerCapturingContent =
6627 PointerEventHandler::GetPointerCapturingContent(aGUIEvent);
6629 if (pointerCapturingContent) {
6630 rootFrameToHandleEvent = pointerCapturingContent->GetPrimaryFrame();
6631 if (!rootFrameToHandleEvent) {
6632 return HandleEventWithPointerCapturingContentWithoutItsFrame(
6633 aFrameForPresShell, aGUIEvent, pointerCapturingContent, aEventStatus);
6637 WidgetMouseEvent* mouseEvent = aGUIEvent->AsMouseEvent();
6638 bool isWindowLevelMouseExit =
6639 (aGUIEvent->mMessage == eMouseExitFromWidget) &&
6640 (mouseEvent && mouseEvent->mExitFrom == WidgetMouseEvent::eTopLevel);
6642 // Get the frame at the event point. However, don't do this if we're
6643 // capturing and retargeting the event because the captured frame will
6644 // be used instead below. Also keep using the root frame if we're dealing
6645 // with a window-level mouse exit event since we want to start sending
6646 // mouse out events at the root EventStateManager.
6647 EventTargetData eventTargetData(mPresShell, rootFrameToHandleEvent);
6648 if (!isCaptureRetargeted && !isWindowLevelMouseExit &&
6649 !pointerCapturingContent) {
6650 if (!ComputeEventTargetFrameAndPresShellAtEventPoint(
6651 rootFrameToHandleEvent, aGUIEvent, &eventTargetData)) {
6652 *aEventStatus = nsEventStatus_eIgnore;
6653 return NS_OK;
6657 // if a node is capturing the mouse, check if the event needs to be
6658 // retargeted at the capturing content instead. This will be the case when
6659 // capture retargeting is being used, no frame was found or the frame's
6660 // content is not a descendant of the capturing content.
6661 if (capturingContent && !pointerCapturingContent &&
6662 (PresShell::sCapturingContentInfo.mRetargetToElement ||
6663 !eventTargetData.mFrame->GetContent() ||
6664 !nsContentUtils::ContentIsCrossDocDescendantOf(
6665 eventTargetData.mFrame->GetContent(), capturingContent))) {
6666 // A check was already done above to ensure that capturingContent is
6667 // in this presshell.
6668 NS_ASSERTION(capturingContent->GetComposedDoc() == GetDocument(),
6669 "Unexpected document");
6670 nsIFrame* capturingFrame = capturingContent->GetPrimaryFrame();
6671 if (capturingFrame) {
6672 eventTargetData.SetFrameAndComputePresShell(capturingFrame);
6676 if (NS_WARN_IF(!eventTargetData.mFrame)) {
6677 return NS_OK;
6680 // Suppress mouse event if it's being targeted at an element inside
6681 // a document which needs events suppressed
6682 if (MaybeDiscardOrDelayMouseEvent(eventTargetData.mFrame, aGUIEvent)) {
6683 return NS_OK;
6686 // Check if we have an active EventStateManager which isn't the
6687 // EventStateManager of the current PresContext. If that is the case, and
6688 // mouse is over some ancestor document, forward event handling to the
6689 // active document. This way content can get mouse events even when mouse
6690 // is over the chrome or outside the window.
6691 if (eventTargetData.MaybeRetargetToActiveDocument(aGUIEvent) &&
6692 NS_WARN_IF(!eventTargetData.mFrame)) {
6693 return NS_OK;
6696 if (!eventTargetData.ComputeElementFromFrame(aGUIEvent)) {
6697 return NS_OK;
6699 // Note that even if ComputeElementFromFrame() returns true,
6700 // eventTargetData.mContent can be nullptr here.
6702 // Dispatch a pointer event if Pointer Events is enabled. Note that if
6703 // pointer event listeners change the layout, eventTargetData is
6704 // automatically updated.
6705 if (!DispatchPrecedingPointerEvent(
6706 aFrameForPresShell, aGUIEvent, pointerCapturingContent,
6707 aDontRetargetEvents, &eventTargetData, aEventStatus)) {
6708 return NS_OK;
6711 // frame could be null after dispatching pointer events.
6712 // XXX Despite of this comment, we update the event target data outside
6713 // DispatchPrecedingPointerEvent(). Can we make it call
6714 // UpdateTouchEventTarget()?
6715 eventTargetData.UpdateTouchEventTarget(aGUIEvent);
6717 // Handle the event in the correct shell.
6718 // We pass the subshell's root frame as the frame to start from. This is
6719 // the only correct alternative; if the event was captured then it
6720 // must have been captured by us or some ancestor shell and we
6721 // now ask the subshell to dispatch it normally.
6722 EventHandler eventHandler(*eventTargetData.mPresShell);
6723 AutoCurrentEventInfoSetter eventInfoSetter(eventHandler, eventTargetData);
6724 // eventTargetData is on the stack and is guaranteed to keep its
6725 // mOverrideClickTarget alive, so we can just use MOZ_KnownLive here.
6726 nsresult rv = eventHandler.HandleEventWithCurrentEventInfo(
6727 aGUIEvent, aEventStatus, true,
6728 MOZ_KnownLive(eventTargetData.mOverrideClickTarget));
6729 #ifdef DEBUG
6730 eventTargetData.mPresShell->ShowEventTargetDebug();
6731 #endif
6732 return rv;
6735 bool PresShell::EventHandler::MaybeFlushPendingNotifications(
6736 WidgetGUIEvent* aGUIEvent) {
6737 MOZ_ASSERT(aGUIEvent);
6739 switch (aGUIEvent->mMessage) {
6740 case eMouseDown:
6741 case eMouseUp: {
6742 RefPtr<nsPresContext> presContext = mPresShell->GetPresContext();
6743 if (NS_WARN_IF(!presContext)) {
6744 return false;
6746 uint64_t framesConstructedCount = presContext->FramesConstructedCount();
6747 uint64_t framesReflowedCount = presContext->FramesReflowedCount();
6749 MOZ_KnownLive(mPresShell)->FlushPendingNotifications(FlushType::Layout);
6750 return framesConstructedCount != presContext->FramesConstructedCount() ||
6751 framesReflowedCount != presContext->FramesReflowedCount();
6753 default:
6754 return false;
6758 nsIFrame* PresShell::EventHandler::GetFrameToHandleNonTouchEvent(
6759 nsIFrame* aRootFrameToHandleEvent, WidgetGUIEvent* aGUIEvent) {
6760 MOZ_ASSERT(aGUIEvent);
6761 MOZ_ASSERT(aGUIEvent->mClass != eTouchEventClass);
6763 nsPoint eventPoint = nsLayoutUtils::GetEventCoordinatesRelativeTo(
6764 aGUIEvent, aRootFrameToHandleEvent);
6766 uint32_t flags = 0;
6767 if (aGUIEvent->mClass == eMouseEventClass) {
6768 WidgetMouseEvent* mouseEvent = aGUIEvent->AsMouseEvent();
6769 if (mouseEvent && mouseEvent->mIgnoreRootScrollFrame) {
6770 flags |= INPUT_IGNORE_ROOT_SCROLL_FRAME;
6774 nsIFrame* targetFrame = FindFrameTargetedByInputEvent(
6775 aGUIEvent, aRootFrameToHandleEvent, eventPoint, flags);
6776 if (!targetFrame) {
6777 return aRootFrameToHandleEvent;
6780 if (targetFrame->PresShell() == mPresShell) {
6781 // If found target is in mPresShell, we've already found it in the latest
6782 // layout so that we can use it.
6783 return targetFrame;
6786 // If target is in a child document, we've not flushed its layout yet.
6787 PresShell* childPresShell = targetFrame->PresShell();
6788 EventHandler childEventHandler(*childPresShell);
6789 AutoWeakFrame weakFrame(aRootFrameToHandleEvent);
6790 bool layoutChanged =
6791 childEventHandler.MaybeFlushPendingNotifications(aGUIEvent);
6792 if (!weakFrame.IsAlive()) {
6793 // Stop handling the event if the root frame to handle event is destroyed
6794 // by the reflow. (but why?)
6795 return nullptr;
6797 if (!layoutChanged) {
6798 // If the layout in the child PresShell hasn't been changed, we don't
6799 // need to recompute the target.
6800 return targetFrame;
6803 // Finally, we need to recompute the target with the latest layout.
6804 targetFrame = FindFrameTargetedByInputEvent(
6805 aGUIEvent, aRootFrameToHandleEvent, eventPoint, flags);
6807 return targetFrame ? targetFrame : aRootFrameToHandleEvent;
6810 bool PresShell::EventHandler::ComputeEventTargetFrameAndPresShellAtEventPoint(
6811 nsIFrame* aRootFrameToHandleEvent, WidgetGUIEvent* aGUIEvent,
6812 EventTargetData* aEventTargetData) {
6813 MOZ_ASSERT(aRootFrameToHandleEvent);
6814 MOZ_ASSERT(aGUIEvent);
6815 MOZ_ASSERT(aEventTargetData);
6817 if (aGUIEvent->mClass == eTouchEventClass) {
6818 nsIFrame* targetFrameAtTouchEvent = TouchManager::SetupTarget(
6819 aGUIEvent->AsTouchEvent(), aRootFrameToHandleEvent);
6820 aEventTargetData->SetFrameAndComputePresShell(targetFrameAtTouchEvent);
6821 return true;
6824 nsIFrame* targetFrame =
6825 GetFrameToHandleNonTouchEvent(aRootFrameToHandleEvent, aGUIEvent);
6826 aEventTargetData->SetFrameAndComputePresShell(targetFrame);
6827 return !!aEventTargetData->mFrame;
6830 bool PresShell::EventHandler::DispatchPrecedingPointerEvent(
6831 nsIFrame* aFrameForPresShell, WidgetGUIEvent* aGUIEvent,
6832 nsIContent* aPointerCapturingContent, bool aDontRetargetEvents,
6833 EventTargetData* aEventTargetData, nsEventStatus* aEventStatus) {
6834 MOZ_ASSERT(aFrameForPresShell);
6835 MOZ_ASSERT(aGUIEvent);
6836 MOZ_ASSERT(aEventTargetData);
6837 MOZ_ASSERT(aEventStatus);
6839 if (!StaticPrefs::dom_w3c_pointer_events_enabled()) {
6840 return true;
6843 // Dispatch pointer events from the mouse or touch events. Regarding
6844 // pointer events from mouse, we should dispatch those pointer events to
6845 // the same target as the source mouse events. We pass the frame found
6846 // in hit test to PointerEventHandler and dispatch pointer events to it.
6848 // Regarding pointer events from touch, the behavior is different. Touch
6849 // events are dispatched to the same target as the target of touchstart.
6850 // Multiple touch points must be dispatched to the same document. Pointer
6851 // events from touch can be dispatched to different documents. We Pass the
6852 // original frame to PointerEventHandler, reentry PresShell::HandleEvent,
6853 // and do hit test for each point.
6854 nsIFrame* targetFrame = aGUIEvent->mClass == eTouchEventClass
6855 ? aFrameForPresShell
6856 : aEventTargetData->mFrame;
6858 if (aPointerCapturingContent) {
6859 aEventTargetData->mOverrideClickTarget =
6860 GetOverrideClickTarget(aGUIEvent, aFrameForPresShell);
6861 aEventTargetData->mPresShell =
6862 PresShell::GetShellForEventTarget(nullptr, aPointerCapturingContent);
6863 if (!aEventTargetData->mPresShell) {
6864 // If we can't process event for the capturing content, release
6865 // the capture.
6866 PointerEventHandler::ReleaseIfCaptureByDescendant(
6867 aPointerCapturingContent);
6868 return false;
6871 targetFrame = aPointerCapturingContent->GetPrimaryFrame();
6872 aEventTargetData->mFrame = targetFrame;
6875 AutoWeakFrame weakTargetFrame(targetFrame);
6876 AutoWeakFrame weakFrame(aEventTargetData->mFrame);
6877 nsCOMPtr<nsIContent> content(aEventTargetData->mContent);
6878 RefPtr<PresShell> presShell(aEventTargetData->mPresShell);
6879 nsCOMPtr<nsIContent> targetContent;
6880 PointerEventHandler::DispatchPointerFromMouseOrTouch(
6881 presShell, aEventTargetData->mFrame, content, aGUIEvent,
6882 aDontRetargetEvents, aEventStatus, getter_AddRefs(targetContent));
6884 // If the target frame is alive, the caller should keep handling the event
6885 // unless event target frame is destroyed.
6886 if (weakTargetFrame.IsAlive()) {
6887 return weakFrame.IsAlive();
6890 // If the event is not a mouse event, the caller should keep handling the
6891 // event unless event target frame is destroyed. Note that this case is
6892 // not defined by the spec.
6893 if (aGUIEvent->mClass != eMouseEventClass) {
6894 return weakFrame.IsAlive();
6897 // Spec defines that mouse events must be dispatched to the same target as
6898 // the pointer event. If the target is no longer participating in its
6899 // ownerDocument's tree, fire the event at the original target's nearest
6900 // ancestor node
6901 if (!targetContent) {
6902 return false;
6905 // XXX Why don't we reset aEventTargetData->mContent here?
6906 aEventTargetData->mFrame = targetContent->GetPrimaryFrame();
6907 aEventTargetData->mPresShell = PresShell::GetShellForEventTarget(
6908 aEventTargetData->mFrame, targetContent);
6910 // If new target PresShel is not found, we cannot keep handling the event.
6911 return !!aEventTargetData->mPresShell;
6914 bool PresShell::EventHandler::MaybeHandleEventWithAccessibleCaret(
6915 WidgetGUIEvent* aGUIEvent, nsEventStatus* aEventStatus) {
6916 MOZ_ASSERT(aGUIEvent);
6917 MOZ_ASSERT(aEventStatus);
6919 // Don't dispatch event to AccessibleCaretEventHub when the event status
6920 // is nsEventStatus_eConsumeNoDefault. This might be happened when content
6921 // preventDefault on the pointer events. In such case, we also call
6922 // preventDefault on mouse events to stop default behaviors.
6923 if (*aEventStatus == nsEventStatus_eConsumeNoDefault) {
6924 return false;
6927 if (!AccessibleCaretEnabled(GetDocument()->GetDocShell())) {
6928 return false;
6931 // We have to target the focus window because regardless of where the
6932 // touch goes, we want to access the copy paste manager.
6933 nsCOMPtr<nsPIDOMWindowOuter> window = GetFocusedDOMWindowInOurWindow();
6934 if (!window) {
6935 return false;
6937 RefPtr<Document> retargetEventDoc = window->GetExtantDoc();
6938 if (!retargetEventDoc) {
6939 return false;
6941 RefPtr<PresShell> presShell = retargetEventDoc->GetPresShell();
6942 if (!presShell) {
6943 return false;
6946 RefPtr<AccessibleCaretEventHub> eventHub =
6947 presShell->GetAccessibleCaretEventHub();
6948 if (!eventHub) {
6949 return false;
6951 *aEventStatus = eventHub->HandleEvent(aGUIEvent);
6952 if (*aEventStatus != nsEventStatus_eConsumeNoDefault) {
6953 return false;
6955 // If the event is consumed, cancel APZC panning by setting
6956 // mMultipleActionsPrevented.
6957 aGUIEvent->mFlags.mMultipleActionsPrevented = true;
6958 return true;
6961 bool PresShell::EventHandler::MaybeDiscardEvent(WidgetGUIEvent* aGUIEvent) {
6962 MOZ_ASSERT(aGUIEvent);
6964 // If it is safe to dispatch events now, don't discard the event.
6965 if (nsContentUtils::IsSafeToRunScript()) {
6966 return false;
6969 // If the event does not cause dispatching DOM event (i.e., internal event),
6970 // we can keep handling it even when it's not safe to run script.
6971 if (!aGUIEvent->IsAllowedToDispatchDOMEvent()) {
6972 return false;
6975 // If the event is a composition event, we need to let IMEStateManager know
6976 // it's discarded because it needs to listen all composition events to manage
6977 // TextComposition instance.
6978 if (aGUIEvent->mClass == eCompositionEventClass) {
6979 IMEStateManager::OnCompositionEventDiscarded(
6980 aGUIEvent->AsCompositionEvent());
6983 #ifdef DEBUG
6984 if (aGUIEvent->IsIMERelatedEvent()) {
6985 nsPrintfCString warning("%s event is discarded",
6986 ToChar(aGUIEvent->mMessage));
6987 NS_WARNING(warning.get());
6989 #endif // #ifdef DEBUG
6991 nsContentUtils::WarnScriptWasIgnored(GetDocument());
6992 return true;
6995 // static
6996 nsIContent* PresShell::EventHandler::GetCapturingContentFor(
6997 WidgetGUIEvent* aGUIEvent) {
6998 return (aGUIEvent->mClass == ePointerEventClass ||
6999 aGUIEvent->mClass == eWheelEventClass ||
7000 aGUIEvent->HasMouseEventMessage())
7001 ? PresShell::GetCapturingContent()
7002 : nullptr;
7005 bool PresShell::EventHandler::GetRetargetEventDocument(
7006 WidgetGUIEvent* aGUIEvent, Document** aRetargetEventDocument) {
7007 MOZ_ASSERT(aGUIEvent);
7008 MOZ_ASSERT(aRetargetEventDocument);
7010 *aRetargetEventDocument = nullptr;
7012 // key and IME related events should not cross top level window boundary.
7013 // Basically, such input events should be fired only on focused widget.
7014 // However, some IMEs might need to clean up composition after focused
7015 // window is deactivated. And also some tests on MozMill want to test key
7016 // handling on deactivated window because MozMill window can be activated
7017 // during tests. So, there is no merit the events should be redirected to
7018 // active window. So, the events should be handled on the last focused
7019 // content in the last focused DOM window in same top level window.
7020 // Note, if no DOM window has been focused yet, we can discard the events.
7021 if (aGUIEvent->IsTargetedAtFocusedWindow()) {
7022 nsCOMPtr<nsPIDOMWindowOuter> window = GetFocusedDOMWindowInOurWindow();
7023 // No DOM window in same top level window has not been focused yet,
7024 // discard the events.
7025 if (!window) {
7026 return false;
7029 RefPtr<Document> retargetEventDoc = window->GetExtantDoc();
7030 if (!retargetEventDoc) {
7031 return false;
7033 retargetEventDoc.forget(aRetargetEventDocument);
7034 return true;
7037 nsIContent* capturingContent =
7038 EventHandler::GetCapturingContentFor(aGUIEvent);
7039 if (capturingContent) {
7040 // if the mouse is being captured then retarget the mouse event at the
7041 // document that is being captured.
7042 RefPtr<Document> retargetEventDoc = capturingContent->GetComposedDoc();
7043 retargetEventDoc.forget(aRetargetEventDocument);
7044 return true;
7047 #ifdef ANDROID
7048 if (aGUIEvent->mClass == eTouchEventClass ||
7049 aGUIEvent->mClass == eMouseEventClass ||
7050 aGUIEvent->mClass == eWheelEventClass) {
7051 RefPtr<Document> retargetEventDoc = mPresShell->GetPrimaryContentDocument();
7052 retargetEventDoc.forget(aRetargetEventDocument);
7053 return true;
7055 #endif // #ifdef ANDROID
7057 // When we don't find another document to handle the event, we need to keep
7058 // handling the event by ourselves.
7059 return true;
7062 nsIFrame* PresShell::EventHandler::GetFrameForHandlingEventWith(
7063 WidgetGUIEvent* aGUIEvent, Document* aRetargetDocument,
7064 nsIFrame* aFrameForPresShell) {
7065 MOZ_ASSERT(aGUIEvent);
7066 MOZ_ASSERT(aRetargetDocument);
7068 RefPtr<PresShell> retargetPresShell = aRetargetDocument->GetPresShell();
7069 // Even if the document doesn't have PresShell, i.e., it's invisible, we
7070 // need to dispatch only KeyboardEvent in its nearest visible document
7071 // because key focus shouldn't be caught by invisible document.
7072 if (!retargetPresShell) {
7073 if (!aGUIEvent->HasKeyEventMessage()) {
7074 return nullptr;
7076 Document* retargetEventDoc = aRetargetDocument;
7077 while (!retargetPresShell) {
7078 retargetEventDoc = retargetEventDoc->GetInProcessParentDocument();
7079 if (!retargetEventDoc) {
7080 return nullptr;
7082 retargetPresShell = retargetEventDoc->GetPresShell();
7086 // If the found PresShell is this instance, caller needs to keep handling
7087 // aGUIEvent by itself. Therefore, return the given frame which was set
7088 // to aFrame of HandleEvent().
7089 if (retargetPresShell == mPresShell) {
7090 return aFrameForPresShell;
7093 // Use root frame of the new PresShell if there is.
7094 nsIFrame* rootFrame = retargetPresShell->GetRootFrame();
7095 if (rootFrame) {
7096 return rootFrame;
7099 // Otherwise, and if aGUIEvent requires content of PresShell, caller should
7100 // stop handling the event.
7101 if (aGUIEvent->mMessage == eQueryTextContent ||
7102 aGUIEvent->IsContentCommandEvent()) {
7103 return nullptr;
7106 // Otherwise, use nearest ancestor frame which includes the PresShell.
7107 return GetNearestFrameContainingPresShell(retargetPresShell);
7110 bool PresShell::EventHandler::MaybeHandleEventWithAnotherPresShell(
7111 nsIFrame* aFrameForPresShell, WidgetGUIEvent* aGUIEvent,
7112 nsEventStatus* aEventStatus, nsresult* aRv) {
7113 MOZ_ASSERT(aGUIEvent);
7114 MOZ_ASSERT(aEventStatus);
7115 MOZ_ASSERT(aRv);
7117 *aRv = NS_OK;
7119 RefPtr<Document> retargetEventDoc;
7120 if (!GetRetargetEventDocument(aGUIEvent, getter_AddRefs(retargetEventDoc))) {
7121 // Nobody can handle this event. So, treat as handled by somebody to make
7122 // caller do nothing anymore.
7123 return true;
7126 // If there is no proper retarget document, the caller should handle the
7127 // event by itself.
7128 if (!retargetEventDoc) {
7129 return false;
7132 nsIFrame* frame = GetFrameForHandlingEventWith(aGUIEvent, retargetEventDoc,
7133 aFrameForPresShell);
7134 if (!frame) {
7135 // Nobody can handle this event. So, treat as handled by somebody to make
7136 // caller do nothing anymore.
7137 return true;
7140 // If we reached same frame as set to HandleEvent(), the caller should handle
7141 // the event by itself.
7142 if (frame == aFrameForPresShell) {
7143 return false;
7146 // We need to handle aGUIEvent with another PresShell.
7147 RefPtr<PresShell> presShell = frame->PresContext()->PresShell();
7148 *aRv = presShell->HandleEvent(frame, aGUIEvent, true, aEventStatus);
7149 return true;
7152 bool PresShell::EventHandler::MaybeDiscardOrDelayKeyboardEvent(
7153 WidgetGUIEvent* aGUIEvent) {
7154 MOZ_ASSERT(aGUIEvent);
7156 if (aGUIEvent->mClass != eKeyboardEventClass) {
7157 return false;
7160 Document* document = GetDocument();
7161 if (!document || !document->EventHandlingSuppressed()) {
7162 return false;
7165 if (aGUIEvent->mMessage == eKeyDown) {
7166 mPresShell->mNoDelayedKeyEvents = true;
7167 } else if (!mPresShell->mNoDelayedKeyEvents) {
7168 UniquePtr<DelayedKeyEvent> delayedKeyEvent =
7169 MakeUnique<DelayedKeyEvent>(aGUIEvent->AsKeyboardEvent());
7170 PushDelayedEventIntoQueue(std::move(delayedKeyEvent));
7172 aGUIEvent->mFlags.mIsSuppressedOrDelayed = true;
7173 return true;
7176 bool PresShell::EventHandler::MaybeDiscardOrDelayMouseEvent(
7177 nsIFrame* aFrameToHandleEvent, WidgetGUIEvent* aGUIEvent) {
7178 MOZ_ASSERT(aFrameToHandleEvent);
7179 MOZ_ASSERT(aGUIEvent);
7181 if (aGUIEvent->mClass != eMouseEventClass) {
7182 return false;
7185 if (!aFrameToHandleEvent->PresContext()
7186 ->Document()
7187 ->EventHandlingSuppressed()) {
7188 return false;
7191 if (aGUIEvent->mMessage == eMouseDown) {
7192 mPresShell->mNoDelayedMouseEvents = true;
7193 } else if (!mPresShell->mNoDelayedMouseEvents &&
7194 (aGUIEvent->mMessage == eMouseUp ||
7195 // contextmenu is triggered after right mouseup on Windows and
7196 // right mousedown on other platforms.
7197 aGUIEvent->mMessage == eContextMenu)) {
7198 UniquePtr<DelayedMouseEvent> delayedMouseEvent =
7199 MakeUnique<DelayedMouseEvent>(aGUIEvent->AsMouseEvent());
7200 PushDelayedEventIntoQueue(std::move(delayedMouseEvent));
7203 // If there is a suppressed event listener associated with the document,
7204 // notify it about the suppressed mouse event. This allows devtools
7205 // features to continue receiving mouse events even when the devtools
7206 // debugger has paused execution in a page.
7207 RefPtr<EventListener> suppressedListener = aFrameToHandleEvent->PresContext()
7208 ->Document()
7209 ->GetSuppressedEventListener();
7210 if (!suppressedListener ||
7211 aGUIEvent->AsMouseEvent()->mReason == WidgetMouseEvent::eSynthesized) {
7212 return true;
7215 nsCOMPtr<nsIContent> targetContent;
7216 aFrameToHandleEvent->GetContentForEvent(aGUIEvent,
7217 getter_AddRefs(targetContent));
7218 if (targetContent) {
7219 aGUIEvent->mTarget = targetContent;
7222 nsCOMPtr<EventTarget> eventTarget = aGUIEvent->mTarget;
7223 RefPtr<Event> event = EventDispatcher::CreateEvent(
7224 eventTarget, aFrameToHandleEvent->PresContext(), aGUIEvent,
7225 EmptyString());
7227 suppressedListener->HandleEvent(*event);
7228 return true;
7231 nsIFrame* PresShell::EventHandler::MaybeFlushThrottledStyles(
7232 nsIFrame* aFrameForPresShell) {
7233 if (!GetDocument()) {
7234 // XXX Only when mPresShell has document, we'll try to look for a frame
7235 // containing mPresShell even if given frame is nullptr. Does this
7236 // make sense?
7237 return aFrameForPresShell;
7240 PresShell* rootPresShell = mPresShell->GetRootPresShell();
7241 if (NS_WARN_IF(!rootPresShell)) {
7242 return nullptr;
7244 Document* rootDocument = rootPresShell->GetDocument();
7245 if (NS_WARN_IF(!rootDocument)) {
7246 return nullptr;
7249 AutoWeakFrame weakFrameForPresShell(aFrameForPresShell);
7250 { // scope for scriptBlocker.
7251 nsAutoScriptBlocker scriptBlocker;
7252 FlushThrottledStyles(rootDocument, nullptr);
7255 if (weakFrameForPresShell.IsAlive()) {
7256 return aFrameForPresShell;
7259 return GetNearestFrameContainingPresShell(mPresShell);
7262 nsIFrame* PresShell::EventHandler::ComputeRootFrameToHandleEvent(
7263 nsIFrame* aFrameForPresShell, WidgetGUIEvent* aGUIEvent,
7264 nsIContent* aCapturingContent, bool* aIsCapturingContentIgnored,
7265 bool* aIsCaptureRetargeted) {
7266 MOZ_ASSERT(aFrameForPresShell);
7267 MOZ_ASSERT(aGUIEvent);
7268 MOZ_ASSERT(aIsCapturingContentIgnored);
7269 MOZ_ASSERT(aIsCaptureRetargeted);
7271 nsIFrame* rootFrameToHandleEvent = ComputeRootFrameToHandleEventWithPopup(
7272 aFrameForPresShell, aGUIEvent, aCapturingContent,
7273 aIsCapturingContentIgnored);
7274 if (*aIsCapturingContentIgnored) {
7275 // If the capturing content is ignored, we don't need to respect it.
7276 return rootFrameToHandleEvent;
7279 if (!aCapturingContent) {
7280 return rootFrameToHandleEvent;
7283 // If we have capturing content, let's compute root frame with it again.
7284 return ComputeRootFrameToHandleEventWithCapturingContent(
7285 rootFrameToHandleEvent, aCapturingContent, aIsCapturingContentIgnored,
7286 aIsCaptureRetargeted);
7289 nsIFrame* PresShell::EventHandler::ComputeRootFrameToHandleEventWithPopup(
7290 nsIFrame* aRootFrameToHandleEvent, WidgetGUIEvent* aGUIEvent,
7291 nsIContent* aCapturingContent, bool* aIsCapturingContentIgnored) {
7292 MOZ_ASSERT(aRootFrameToHandleEvent);
7293 MOZ_ASSERT(aGUIEvent);
7294 MOZ_ASSERT(aIsCapturingContentIgnored);
7296 *aIsCapturingContentIgnored = false;
7298 nsPresContext* framePresContext = aRootFrameToHandleEvent->PresContext();
7299 nsPresContext* rootPresContext = framePresContext->GetRootPresContext();
7300 NS_ASSERTION(rootPresContext == GetPresContext()->GetRootPresContext(),
7301 "How did we end up outside the connected "
7302 "prescontext/viewmanager hierarchy?");
7303 nsIFrame* popupFrame = nsLayoutUtils::GetPopupFrameForEventCoordinates(
7304 rootPresContext, aGUIEvent);
7305 if (!popupFrame) {
7306 return aRootFrameToHandleEvent;
7309 // If a remote browser is currently capturing input break out if we
7310 // detect a chrome generated popup.
7311 if (aCapturingContent &&
7312 EventStateManager::IsRemoteTarget(aCapturingContent)) {
7313 *aIsCapturingContentIgnored = true;
7316 // If the popupFrame is an ancestor of the 'frame', the frame should
7317 // handle the event, otherwise, the popup should handle it.
7318 if (nsContentUtils::ContentIsCrossDocDescendantOf(
7319 framePresContext->GetPresShell()->GetDocument(),
7320 popupFrame->GetContent())) {
7321 return aRootFrameToHandleEvent;
7324 // If we aren't starting our event dispatch from the root frame of the
7325 // root prescontext, then someone must be capturing the mouse. In that
7326 // case we only want to use the popup list if the capture is
7327 // inside the popup.
7328 if (framePresContext == rootPresContext &&
7329 aRootFrameToHandleEvent == FrameConstructor()->GetRootFrame()) {
7330 return popupFrame;
7333 if (aCapturingContent && !*aIsCapturingContentIgnored &&
7334 aCapturingContent->IsInclusiveDescendantOf(popupFrame->GetContent())) {
7335 return popupFrame;
7338 return aRootFrameToHandleEvent;
7341 nsIFrame*
7342 PresShell::EventHandler::ComputeRootFrameToHandleEventWithCapturingContent(
7343 nsIFrame* aRootFrameToHandleEvent, nsIContent* aCapturingContent,
7344 bool* aIsCapturingContentIgnored, bool* aIsCaptureRetargeted) {
7345 MOZ_ASSERT(aRootFrameToHandleEvent);
7346 MOZ_ASSERT(aCapturingContent);
7347 MOZ_ASSERT(aIsCapturingContentIgnored);
7348 MOZ_ASSERT(aIsCaptureRetargeted);
7350 *aIsCapturingContentIgnored = false;
7351 *aIsCaptureRetargeted = false;
7353 // If a capture is active, determine if the docshell is visible. If not,
7354 // clear the capture and target the mouse event normally instead. This
7355 // would occur if the mouse button is held down while a tab change occurs.
7356 // If the docshell is visible, look for a scrolling container.
7357 nsCOMPtr<nsIBaseWindow> baseWindow =
7358 do_QueryInterface(GetPresContext()->GetContainerWeak());
7359 if (!baseWindow) {
7360 ClearMouseCapture(nullptr);
7361 *aIsCapturingContentIgnored = true;
7362 return aRootFrameToHandleEvent;
7365 bool isBaseWindowVisible = false;
7366 nsresult rv = baseWindow->GetVisibility(&isBaseWindowVisible);
7367 if (NS_FAILED(rv) || !isBaseWindowVisible) {
7368 ClearMouseCapture(nullptr);
7369 *aIsCapturingContentIgnored = true;
7370 return aRootFrameToHandleEvent;
7373 if (PresShell::sCapturingContentInfo.mRetargetToElement) {
7374 *aIsCaptureRetargeted = true;
7375 return aRootFrameToHandleEvent;
7378 // A check was already done above to ensure that aCapturingContent is
7379 // in this presshell.
7380 NS_ASSERTION(aCapturingContent->GetComposedDoc() == GetDocument(),
7381 "Unexpected document");
7382 nsIFrame* captureFrame = aCapturingContent->GetPrimaryFrame();
7383 if (!captureFrame) {
7384 return aRootFrameToHandleEvent;
7387 if (aCapturingContent->IsHTMLElement(nsGkAtoms::select)) {
7388 // a dropdown <select> has a child in its selectPopupList and we should
7389 // capture on that instead.
7390 nsIFrame* childFrame =
7391 captureFrame->GetChildList(nsIFrame::kSelectPopupList).FirstChild();
7392 if (childFrame) {
7393 captureFrame = childFrame;
7397 // scrollable frames should use the scrolling container as the root instead
7398 // of the document
7399 nsIScrollableFrame* scrollFrame = do_QueryFrame(captureFrame);
7400 return scrollFrame ? scrollFrame->GetScrolledFrame()
7401 : aRootFrameToHandleEvent;
7404 nsresult
7405 PresShell::EventHandler::HandleEventWithPointerCapturingContentWithoutItsFrame(
7406 nsIFrame* aFrameForPresShell, WidgetGUIEvent* aGUIEvent,
7407 nsIContent* aPointerCapturingContent, nsEventStatus* aEventStatus) {
7408 MOZ_ASSERT(aGUIEvent);
7409 MOZ_ASSERT(aPointerCapturingContent);
7410 MOZ_ASSERT(!aPointerCapturingContent->GetPrimaryFrame(),
7411 "Handle the event with frame rather than only with the content");
7412 MOZ_ASSERT(aEventStatus);
7414 RefPtr<PresShell> presShellForCapturingContent =
7415 PresShell::GetShellForEventTarget(nullptr, aPointerCapturingContent);
7416 if (!presShellForCapturingContent) {
7417 // If we can't process event for the capturing content, release
7418 // the capture.
7419 PointerEventHandler::ReleaseIfCaptureByDescendant(aPointerCapturingContent);
7420 return NS_OK;
7423 nsCOMPtr<nsIContent> overrideClickTarget =
7424 GetOverrideClickTarget(aGUIEvent, aFrameForPresShell);
7426 // Dispatch events to the capturing content even it's frame is
7427 // destroyed.
7428 PointerEventHandler::DispatchPointerFromMouseOrTouch(
7429 presShellForCapturingContent, nullptr, aPointerCapturingContent,
7430 aGUIEvent, false, aEventStatus, nullptr);
7432 if (presShellForCapturingContent == mPresShell) {
7433 return HandleEventWithTarget(aGUIEvent, nullptr, aPointerCapturingContent,
7434 aEventStatus, true, nullptr,
7435 overrideClickTarget);
7438 EventHandler eventHandlerForCapturingContent(
7439 std::move(presShellForCapturingContent));
7440 return eventHandlerForCapturingContent.HandleEventWithTarget(
7441 aGUIEvent, nullptr, aPointerCapturingContent, aEventStatus, true, nullptr,
7442 overrideClickTarget);
7445 nsresult PresShell::EventHandler::HandleEventAtFocusedContent(
7446 WidgetGUIEvent* aGUIEvent, nsEventStatus* aEventStatus) {
7447 MOZ_ASSERT(aGUIEvent);
7448 MOZ_ASSERT(aGUIEvent->IsTargetedAtFocusedContent());
7449 MOZ_ASSERT(aEventStatus);
7451 AutoCurrentEventInfoSetter eventInfoSetter(*this);
7453 RefPtr<Element> eventTargetElement =
7454 ComputeFocusedEventTargetElement(aGUIEvent);
7456 mPresShell->mCurrentEventFrame = nullptr;
7457 if (eventTargetElement) {
7458 nsresult rv = NS_OK;
7459 if (MaybeHandleEventWithAnotherPresShell(eventTargetElement, aGUIEvent,
7460 aEventStatus, &rv)) {
7461 return rv;
7465 // If we cannot handle the event with mPresShell, let's try to handle it
7466 // with parent PresShell.
7467 mPresShell->mCurrentEventContent = eventTargetElement;
7468 if (!mPresShell->GetCurrentEventContent() ||
7469 !mPresShell->GetCurrentEventFrame() ||
7470 InZombieDocument(mPresShell->mCurrentEventContent)) {
7471 return RetargetEventToParent(aGUIEvent, aEventStatus);
7474 nsresult rv =
7475 HandleEventWithCurrentEventInfo(aGUIEvent, aEventStatus, true, nullptr);
7477 #ifdef DEBUG
7478 mPresShell->ShowEventTargetDebug();
7479 #endif
7481 return rv;
7484 Element* PresShell::EventHandler::ComputeFocusedEventTargetElement(
7485 WidgetGUIEvent* aGUIEvent) {
7486 MOZ_ASSERT(aGUIEvent);
7487 MOZ_ASSERT(aGUIEvent->IsTargetedAtFocusedContent());
7489 // key and IME related events go to the focused frame in this DOM window.
7490 nsPIDOMWindowOuter* window = GetDocument()->GetWindow();
7491 nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
7492 Element* eventTargetElement = nsFocusManager::GetFocusedDescendant(
7493 window, nsFocusManager::eOnlyCurrentWindow,
7494 getter_AddRefs(focusedWindow));
7496 // otherwise, if there is no focused content or the focused content has
7497 // no frame, just use the root content. This ensures that key events
7498 // still get sent to the window properly if nothing is focused or if a
7499 // frame goes away while it is focused.
7500 if (!eventTargetElement || !eventTargetElement->GetPrimaryFrame()) {
7501 eventTargetElement = GetDocument()->GetUnfocusedKeyEventTarget();
7504 switch (aGUIEvent->mMessage) {
7505 case eKeyDown:
7506 sLastKeyDownEventTargetElement = eventTargetElement;
7507 return eventTargetElement;
7508 case eKeyPress:
7509 case eKeyUp:
7510 if (!sLastKeyDownEventTargetElement) {
7511 return eventTargetElement;
7513 // If a different element is now focused for the keypress/keyup event
7514 // than what was focused during the keydown event, check if the new
7515 // focused element is not in a chrome document any more, and if so,
7516 // retarget the event back at the keydown target. This prevents a
7517 // content area from grabbing the focus from chrome in-between key
7518 // events.
7519 if (eventTargetElement) {
7520 bool keyDownIsChrome = nsContentUtils::IsChromeDoc(
7521 sLastKeyDownEventTargetElement->GetComposedDoc());
7522 if (keyDownIsChrome != nsContentUtils::IsChromeDoc(
7523 eventTargetElement->GetComposedDoc()) ||
7524 (keyDownIsChrome && BrowserParent::GetFrom(eventTargetElement))) {
7525 eventTargetElement = sLastKeyDownEventTargetElement;
7529 if (aGUIEvent->mMessage == eKeyUp) {
7530 sLastKeyDownEventTargetElement = nullptr;
7532 MOZ_FALLTHROUGH;
7533 default:
7534 return eventTargetElement;
7538 bool PresShell::EventHandler::MaybeHandleEventWithAnotherPresShell(
7539 Element* aEventTargetElement, WidgetGUIEvent* aGUIEvent,
7540 nsEventStatus* aEventStatus, nsresult* aRv) {
7541 MOZ_ASSERT(aEventTargetElement);
7542 MOZ_ASSERT(aGUIEvent);
7543 MOZ_ASSERT(!aGUIEvent->IsUsingCoordinates());
7544 MOZ_ASSERT(aEventStatus);
7545 MOZ_ASSERT(aRv);
7547 Document* eventTargetDocument = aEventTargetElement->OwnerDoc();
7548 if (!eventTargetDocument || eventTargetDocument == GetDocument()) {
7549 *aRv = NS_OK;
7550 return false;
7553 RefPtr<PresShell> eventTargetPresShell = eventTargetDocument->GetPresShell();
7554 if (!eventTargetPresShell) {
7555 *aRv = NS_OK;
7556 return true; // No PresShell can handle the event.
7559 EventHandler eventHandler(std::move(eventTargetPresShell));
7560 *aRv = eventHandler.HandleRetargetedEvent(aGUIEvent, aEventStatus,
7561 aEventTargetElement);
7562 return true;
7565 nsresult PresShell::EventHandler::HandleEventWithFrameForPresShell(
7566 nsIFrame* aFrameForPresShell, WidgetGUIEvent* aGUIEvent,
7567 nsEventStatus* aEventStatus) {
7568 MOZ_ASSERT(aGUIEvent);
7569 MOZ_ASSERT(!aGUIEvent->IsUsingCoordinates());
7570 MOZ_ASSERT(!aGUIEvent->IsTargetedAtFocusedContent());
7571 MOZ_ASSERT(aEventStatus);
7573 AutoCurrentEventInfoSetter eventInfoSetter(*this, aFrameForPresShell,
7574 nullptr);
7576 nsresult rv = NS_OK;
7577 if (mPresShell->GetCurrentEventFrame()) {
7578 rv =
7579 HandleEventWithCurrentEventInfo(aGUIEvent, aEventStatus, true, nullptr);
7582 #ifdef DEBUG
7583 mPresShell->ShowEventTargetDebug();
7584 #endif
7586 return rv;
7589 Document* PresShell::GetPrimaryContentDocument() {
7590 nsPresContext* context = GetPresContext();
7591 if (!context || !context->IsRoot()) {
7592 return nullptr;
7595 nsCOMPtr<nsIDocShellTreeItem> shellAsTreeItem = context->GetDocShell();
7596 if (!shellAsTreeItem) {
7597 return nullptr;
7600 nsCOMPtr<nsIDocShellTreeOwner> owner;
7601 shellAsTreeItem->GetTreeOwner(getter_AddRefs(owner));
7602 if (!owner) {
7603 return nullptr;
7606 // now get the primary content shell (active tab)
7607 nsCOMPtr<nsIDocShellTreeItem> item;
7608 owner->GetPrimaryContentShell(getter_AddRefs(item));
7609 nsCOMPtr<nsIDocShell> childDocShell = do_QueryInterface(item);
7610 if (!childDocShell) {
7611 return nullptr;
7614 return childDocShell->GetDocument();
7617 #ifdef DEBUG
7618 void PresShell::ShowEventTargetDebug() {
7619 if (nsFrame::GetShowEventTargetFrameBorder() && GetCurrentEventFrame()) {
7620 if (mDrawEventTargetFrame) {
7621 mDrawEventTargetFrame->InvalidateFrame();
7624 mDrawEventTargetFrame = mCurrentEventFrame;
7625 mDrawEventTargetFrame->InvalidateFrame();
7628 #endif
7630 nsresult PresShell::EventHandler::HandleEventWithTarget(
7631 WidgetEvent* aEvent, nsIFrame* aNewEventFrame, nsIContent* aNewEventContent,
7632 nsEventStatus* aEventStatus, bool aIsHandlingNativeEvent,
7633 nsIContent** aTargetContent, nsIContent* aOverrideClickTarget) {
7634 MOZ_ASSERT(aEvent);
7635 MOZ_DIAGNOSTIC_ASSERT(aEvent->IsTrusted());
7637 #if DEBUG
7638 MOZ_ASSERT(!aNewEventFrame ||
7639 aNewEventFrame->PresContext()->GetPresShell() == mPresShell,
7640 "wrong shell");
7641 if (aNewEventContent) {
7642 Document* doc = aNewEventContent->GetComposedDoc();
7643 NS_ASSERTION(doc, "event for content that isn't in a document");
7644 // NOTE: We don't require that the document still have a PresShell.
7645 // See bug 1375940.
7647 #endif
7648 NS_ENSURE_STATE(!aNewEventContent ||
7649 aNewEventContent->GetComposedDoc() == GetDocument());
7650 AutoPointerEventTargetUpdater updater(mPresShell, aEvent, aNewEventFrame,
7651 aTargetContent);
7652 AutoCurrentEventInfoSetter eventInfoSetter(*this, aNewEventFrame,
7653 aNewEventContent);
7654 nsresult rv = HandleEventWithCurrentEventInfo(aEvent, aEventStatus, false,
7655 aOverrideClickTarget);
7656 return rv;
7659 namespace {
7661 class MOZ_RAII AutoEventHandler final {
7662 public:
7663 AutoEventHandler(WidgetEvent* aEvent, Document* aDocument) : mEvent(aEvent) {
7664 MOZ_ASSERT(mEvent);
7665 MOZ_ASSERT(mEvent->IsTrusted());
7667 if (mEvent->mMessage == eMouseDown) {
7668 PresShell::ReleaseCapturingContent();
7669 PresShell::AllowMouseCapture(true);
7671 if (aDocument && NeedsToResetFocusManagerMouseButtonHandlingState()) {
7672 nsFocusManager* fm = nsFocusManager::GetFocusManager();
7673 NS_ENSURE_TRUE_VOID(fm);
7674 // If it's in modal state, mouse button event handling may be nested.
7675 // E.g., a modal dialog is opened at mousedown or mouseup event handler
7676 // and the dialog is clicked. Therefore, we should store current
7677 // mouse button event handling document if nsFocusManager already has it.
7678 mMouseButtonEventHandlingDocument =
7679 fm->SetMouseButtonHandlingDocument(aDocument);
7681 if (NeedsToUpdateCurrentMouseBtnState()) {
7682 WidgetMouseEvent* mouseEvent = mEvent->AsMouseEvent();
7683 if (mouseEvent) {
7684 EventStateManager::sCurrentMouseBtn = mouseEvent->mButton;
7689 ~AutoEventHandler() {
7690 if (mEvent->mMessage == eMouseDown) {
7691 PresShell::AllowMouseCapture(false);
7693 if (NeedsToResetFocusManagerMouseButtonHandlingState()) {
7694 nsFocusManager* fm = nsFocusManager::GetFocusManager();
7695 NS_ENSURE_TRUE_VOID(fm);
7696 RefPtr<Document> document =
7697 fm->SetMouseButtonHandlingDocument(mMouseButtonEventHandlingDocument);
7699 if (NeedsToUpdateCurrentMouseBtnState()) {
7700 EventStateManager::sCurrentMouseBtn = MouseButton::eNotPressed;
7704 protected:
7705 bool NeedsToResetFocusManagerMouseButtonHandlingState() const {
7706 return mEvent->mMessage == eMouseDown || mEvent->mMessage == eMouseUp;
7709 bool NeedsToUpdateCurrentMouseBtnState() const {
7710 return mEvent->mMessage == eMouseDown || mEvent->mMessage == eMouseUp ||
7711 mEvent->mMessage == ePointerDown || mEvent->mMessage == ePointerUp;
7714 RefPtr<Document> mMouseButtonEventHandlingDocument;
7715 WidgetEvent* mEvent;
7718 } // anonymous namespace
7720 nsresult PresShell::EventHandler::HandleEventWithCurrentEventInfo(
7721 WidgetEvent* aEvent, nsEventStatus* aEventStatus,
7722 bool aIsHandlingNativeEvent, nsIContent* aOverrideClickTarget) {
7723 MOZ_ASSERT(aEvent);
7724 MOZ_ASSERT(aEventStatus);
7726 RefPtr<EventStateManager> manager = GetPresContext()->EventStateManager();
7728 // If we cannot handle the event with mPresShell because of no target,
7729 // just record the response time.
7730 // XXX Is this intentional? In such case, the score is really good because
7731 // of nothing to do. So, it may make average and median better.
7732 if (NS_EVENT_NEEDS_FRAME(aEvent) && !mPresShell->GetCurrentEventFrame() &&
7733 !mPresShell->GetCurrentEventContent()) {
7734 RecordEventHandlingResponsePerformance(aEvent);
7735 return NS_OK;
7738 if (mPresShell->mCurrentEventContent && aEvent->IsTargetedAtFocusedWindow()) {
7739 nsFocusManager* fm = nsFocusManager::GetFocusManager();
7740 if (fm) {
7741 // This may run script now. So, mPresShell might be destroyed after here.
7742 fm->FlushBeforeEventHandlingIfNeeded(mPresShell->mCurrentEventContent);
7746 bool touchIsNew = false;
7747 if (!PrepareToDispatchEvent(aEvent, aEventStatus, &touchIsNew)) {
7748 return NS_OK;
7751 // We finished preparing to dispatch the event. So, let's record the
7752 // performance.
7753 RecordEventPreparationPerformance(aEvent);
7755 AutoHandlingUserInputStatePusher userInpStatePusher(
7756 UserActivation::IsUserInteractionEvent(aEvent), aEvent);
7757 AutoEventHandler eventHandler(aEvent, GetDocument());
7758 AutoPopupStatePusher popupStatePusher(
7759 PopupBlocker::GetEventPopupControlState(aEvent));
7761 // FIXME. If the event was reused, we need to clear the old target,
7762 // bug 329430
7763 aEvent->mTarget = nullptr;
7765 HandlingTimeAccumulator handlingTimeAccumulator(*this, aEvent);
7767 nsresult rv = DispatchEvent(manager, aEvent, touchIsNew, aEventStatus,
7768 aOverrideClickTarget);
7770 if (!mPresShell->IsDestroying() && aIsHandlingNativeEvent) {
7771 // Ensure that notifications to IME should be sent before getting next
7772 // native event from the event queue.
7773 // XXX Should we check the event message or event class instead of
7774 // using aIsHandlingNativeEvent?
7775 manager->TryToFlushPendingNotificationsToIME();
7778 FinalizeHandlingEvent(aEvent);
7780 RecordEventHandlingResponsePerformance(aEvent);
7782 return rv; // Result of DispatchEvent()
7785 nsresult PresShell::EventHandler::DispatchEvent(
7786 EventStateManager* aEventStateManager, WidgetEvent* aEvent,
7787 bool aTouchIsNew, nsEventStatus* aEventStatus,
7788 nsIContent* aOverrideClickTarget) {
7789 MOZ_ASSERT(aEventStateManager);
7790 MOZ_ASSERT(aEvent);
7791 MOZ_ASSERT(aEventStatus);
7793 // 1. Give event to event manager for pre event state changes and
7794 // generation of synthetic events.
7795 { // Scope for presContext
7796 RefPtr<nsPresContext> presContext = GetPresContext();
7797 nsCOMPtr<nsIContent> eventContent = mPresShell->mCurrentEventContent;
7798 nsresult rv = aEventStateManager->PreHandleEvent(
7799 presContext, aEvent, mPresShell->mCurrentEventFrame, eventContent,
7800 aEventStatus, aOverrideClickTarget);
7801 if (NS_FAILED(rv)) {
7802 return rv;
7806 // 2. Give event to the DOM for third party and JS use.
7807 bool wasHandlingKeyBoardEvent = nsContentUtils::IsHandlingKeyBoardEvent();
7808 if (aEvent->mClass == eKeyboardEventClass) {
7809 nsContentUtils::SetIsHandlingKeyBoardEvent(true);
7811 // If EventStateManager or something wants reply from remote process and
7812 // needs to win any other event listeners in chrome, the event is both
7813 // stopped its propagation and marked as "waiting reply from remote
7814 // process". In this case, PresShell shouldn't dispatch the event into
7815 // the DOM tree because they don't have a chance to stop propagation in
7816 // the system event group. On the other hand, if its propagation is not
7817 // stopped, that means that the event may be reserved by chrome. If it's
7818 // reserved by chrome, the event shouldn't be sent to any remote
7819 // processes. In this case, PresShell needs to dispatch the event to
7820 // the DOM tree for checking if it's reserved.
7821 if (aEvent->IsAllowedToDispatchDOMEvent() &&
7822 !(aEvent->PropagationStopped() &&
7823 aEvent->IsWaitingReplyFromRemoteProcess())) {
7824 MOZ_ASSERT(nsContentUtils::IsSafeToRunScript(),
7825 "Somebody changed aEvent to cause a DOM event!");
7826 nsPresShellEventCB eventCB(mPresShell);
7827 if (nsIFrame* target = mPresShell->GetCurrentEventFrame()) {
7828 if (target->OnlySystemGroupDispatch(aEvent->mMessage)) {
7829 aEvent->StopPropagation();
7832 if (aEvent->mClass == eTouchEventClass) {
7833 DispatchTouchEventToDOM(aEvent, aEventStatus, &eventCB, aTouchIsNew);
7834 } else {
7835 DispatchEventToDOM(aEvent, aEventStatus, &eventCB);
7839 nsContentUtils::SetIsHandlingKeyBoardEvent(wasHandlingKeyBoardEvent);
7841 if (aEvent->mMessage == ePointerUp || aEvent->mMessage == ePointerCancel) {
7842 // Implicitly releasing capture for given pointer.
7843 // ePointerLostCapture should be send after ePointerUp or
7844 // ePointerCancel.
7845 WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent();
7846 MOZ_ASSERT(pointerEvent);
7847 PointerEventHandler::ReleasePointerCaptureById(pointerEvent->pointerId);
7848 PointerEventHandler::CheckPointerCaptureState(pointerEvent);
7851 if (mPresShell->IsDestroying()) {
7852 return NS_OK;
7855 // 3. Give event to event manager for post event state changes and
7856 // generation of synthetic events.
7857 // Refetch the prescontext, in case it changed.
7858 RefPtr<nsPresContext> presContext = GetPresContext();
7859 return aEventStateManager->PostHandleEvent(
7860 presContext, aEvent, mPresShell->GetCurrentEventFrame(), aEventStatus,
7861 aOverrideClickTarget);
7864 bool PresShell::EventHandler::PrepareToDispatchEvent(
7865 WidgetEvent* aEvent, nsEventStatus* aEventStatus, bool* aTouchIsNew) {
7866 MOZ_ASSERT(aEvent->IsTrusted());
7867 MOZ_ASSERT(aEventStatus);
7868 MOZ_ASSERT(aTouchIsNew);
7870 *aTouchIsNew = false;
7871 if (aEvent->IsUserAction()) {
7872 mPresShell->mHasHandledUserInput = true;
7875 switch (aEvent->mMessage) {
7876 case eKeyPress:
7877 case eKeyDown:
7878 case eKeyUp: {
7879 WidgetKeyboardEvent* keyboardEvent = aEvent->AsKeyboardEvent();
7880 MaybeHandleKeyboardEventBeforeDispatch(keyboardEvent);
7881 return true;
7883 case eMouseMove: {
7884 bool allowCapture = EventStateManager::GetActiveEventStateManager() &&
7885 GetPresContext() &&
7886 GetPresContext()->EventStateManager() ==
7887 EventStateManager::GetActiveEventStateManager();
7888 PresShell::AllowMouseCapture(allowCapture);
7889 return true;
7891 case eDrop: {
7892 nsCOMPtr<nsIDragSession> session = nsContentUtils::GetDragSession();
7893 if (session) {
7894 bool onlyChromeDrop = false;
7895 session->GetOnlyChromeDrop(&onlyChromeDrop);
7896 if (onlyChromeDrop) {
7897 aEvent->mFlags.mOnlyChromeDispatch = true;
7900 return true;
7902 case eContextMenu: {
7903 // If we cannot open context menu even though eContextMenu is fired, we
7904 // should stop dispatching it into the DOM.
7905 WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
7906 if (mouseEvent->IsContextMenuKeyEvent() &&
7907 !AdjustContextMenuKeyEvent(mouseEvent)) {
7908 return false;
7911 // If "Shift" state is active, context menu should be forcibly opened even
7912 // if web apps want to prevent it since we respect our users' intention.
7913 // In this case, we don't fire "contextmenu" event on web content because
7914 // of not cancelable.
7915 if (mouseEvent->IsShift()) {
7916 aEvent->mFlags.mOnlyChromeDispatch = true;
7917 aEvent->mFlags.mRetargetToNonNativeAnonymous = true;
7919 return true;
7921 case eTouchStart:
7922 case eTouchMove:
7923 case eTouchEnd:
7924 case eTouchCancel:
7925 case eTouchPointerCancel:
7926 return mPresShell->mTouchManager.PreHandleEvent(
7927 aEvent, aEventStatus, *aTouchIsNew, mPresShell->mCurrentEventContent);
7928 default:
7929 return true;
7933 void PresShell::EventHandler::FinalizeHandlingEvent(WidgetEvent* aEvent) {
7934 switch (aEvent->mMessage) {
7935 case eKeyPress:
7936 case eKeyDown:
7937 case eKeyUp: {
7938 if (aEvent->AsKeyboardEvent()->mKeyCode == NS_VK_ESCAPE) {
7939 if (aEvent->mMessage == eKeyUp) {
7940 // Reset this flag after key up is handled.
7941 mPresShell->mIsLastChromeOnlyEscapeKeyConsumed = false;
7942 } else {
7943 if (aEvent->mFlags.mOnlyChromeDispatch &&
7944 aEvent->mFlags.mDefaultPreventedByChrome) {
7945 mPresShell->mIsLastChromeOnlyEscapeKeyConsumed = true;
7949 if (aEvent->mMessage == eKeyDown) {
7950 mPresShell->mIsLastKeyDownCanceled = aEvent->mFlags.mDefaultPrevented;
7952 return;
7954 case eMouseUp:
7955 // reset the capturing content now that the mouse button is up
7956 PresShell::ReleaseCapturingContent();
7957 return;
7958 case eMouseMove:
7959 PresShell::AllowMouseCapture(false);
7960 return;
7961 case eDrag:
7962 case eDragEnd:
7963 case eDragEnter:
7964 case eDragExit:
7965 case eDragLeave:
7966 case eDragOver:
7967 case eDrop: {
7968 // After any drag event other than dragstart (which is handled
7969 // separately, as we need to collect the data first), the DataTransfer
7970 // needs to be made protected, and then disconnected.
7971 DataTransfer* dataTransfer = aEvent->AsDragEvent()->mDataTransfer;
7972 if (dataTransfer) {
7973 dataTransfer->Disconnect();
7975 return;
7977 default:
7978 return;
7982 void PresShell::EventHandler::MaybeHandleKeyboardEventBeforeDispatch(
7983 WidgetKeyboardEvent* aKeyboardEvent) {
7984 MOZ_ASSERT(aKeyboardEvent);
7986 if (aKeyboardEvent->mKeyCode != NS_VK_ESCAPE) {
7987 return;
7990 // If we're in fullscreen mode, exit from it forcibly when Escape key is
7991 // pressed.
7992 Document* doc = mPresShell->GetCurrentEventContent()
7993 ? mPresShell->mCurrentEventContent->OwnerDoc()
7994 : nullptr;
7995 Document* root = nsContentUtils::GetRootDocument(doc);
7996 if (root && root->GetFullscreenElement()) {
7997 // Prevent default action on ESC key press when exiting
7998 // DOM fullscreen mode. This prevents the browser ESC key
7999 // handler from stopping all loads in the document, which
8000 // would cause <video> loads to stop.
8001 // XXX We need to claim the Escape key event which will be
8002 // dispatched only into chrome is already consumed by
8003 // content because we need to prevent its default here
8004 // for some reasons (not sure) but we need to detect
8005 // if a chrome event handler will call PreventDefault()
8006 // again and check it later.
8007 aKeyboardEvent->PreventDefaultBeforeDispatch(CrossProcessForwarding::eStop);
8008 aKeyboardEvent->mFlags.mOnlyChromeDispatch = true;
8010 // The event listeners in chrome can prevent this ESC behavior by
8011 // calling prevent default on the preceding keydown/press events.
8012 if (!mPresShell->mIsLastChromeOnlyEscapeKeyConsumed &&
8013 aKeyboardEvent->mMessage == eKeyUp) {
8014 // ESC key released while in DOM fullscreen mode.
8015 // Fully exit all browser windows and documents from
8016 // fullscreen mode.
8017 Document::AsyncExitFullscreen(nullptr);
8021 nsCOMPtr<Document> pointerLockedDoc =
8022 do_QueryReferent(EventStateManager::sPointerLockedDoc);
8023 if (!mPresShell->mIsLastChromeOnlyEscapeKeyConsumed && pointerLockedDoc) {
8024 // XXX See above comment to understand the reason why this needs
8025 // to claim that the Escape key event is consumed by content
8026 // even though it will be dispatched only into chrome.
8027 aKeyboardEvent->PreventDefaultBeforeDispatch(CrossProcessForwarding::eStop);
8028 aKeyboardEvent->mFlags.mOnlyChromeDispatch = true;
8029 if (aKeyboardEvent->mMessage == eKeyUp) {
8030 Document::UnlockPointer();
8035 void PresShell::EventHandler::RecordEventPreparationPerformance(
8036 const WidgetEvent* aEvent) {
8037 MOZ_ASSERT(aEvent);
8039 switch (aEvent->mMessage) {
8040 case eKeyPress:
8041 case eKeyDown:
8042 case eKeyUp:
8043 if (aEvent->AsKeyboardEvent()->ShouldInteractionTimeRecorded()) {
8044 GetPresContext()->RecordInteractionTime(
8045 nsPresContext::InteractionType::KeyInteraction, aEvent->mTimeStamp);
8047 Telemetry::AccumulateTimeDelta(Telemetry::INPUT_EVENT_QUEUED_KEYBOARD_MS,
8048 aEvent->mTimeStamp);
8049 return;
8051 case eMouseDown:
8052 case eMouseUp:
8053 Telemetry::AccumulateTimeDelta(Telemetry::INPUT_EVENT_QUEUED_CLICK_MS,
8054 aEvent->mTimeStamp);
8055 MOZ_FALLTHROUGH;
8056 case ePointerDown:
8057 case ePointerUp:
8058 GetPresContext()->RecordInteractionTime(
8059 nsPresContext::InteractionType::ClickInteraction, aEvent->mTimeStamp);
8060 return;
8062 case eMouseMove:
8063 if (aEvent->mFlags.mHandledByAPZ) {
8064 Telemetry::AccumulateTimeDelta(
8065 Telemetry::INPUT_EVENT_QUEUED_APZ_MOUSE_MOVE_MS,
8066 aEvent->mTimeStamp);
8068 GetPresContext()->RecordInteractionTime(
8069 nsPresContext::InteractionType::MouseMoveInteraction,
8070 aEvent->mTimeStamp);
8071 return;
8073 case eWheel:
8074 if (aEvent->mFlags.mHandledByAPZ) {
8075 Telemetry::AccumulateTimeDelta(
8076 Telemetry::INPUT_EVENT_QUEUED_APZ_WHEEL_MS, aEvent->mTimeStamp);
8078 return;
8080 case eTouchMove:
8081 if (aEvent->mFlags.mHandledByAPZ) {
8082 Telemetry::AccumulateTimeDelta(
8083 Telemetry::INPUT_EVENT_QUEUED_APZ_TOUCH_MOVE_MS,
8084 aEvent->mTimeStamp);
8086 return;
8088 default:
8089 return;
8093 void PresShell::EventHandler::RecordEventHandlingResponsePerformance(
8094 const WidgetEvent* aEvent) {
8095 if (!Telemetry::CanRecordBase() || aEvent->mTimeStamp.IsNull() ||
8096 aEvent->mTimeStamp <= mPresShell->mLastOSWake ||
8097 !aEvent->AsInputEvent()) {
8098 return;
8101 TimeStamp now = TimeStamp::Now();
8102 double millis = (now - aEvent->mTimeStamp).ToMilliseconds();
8103 Telemetry::Accumulate(Telemetry::INPUT_EVENT_RESPONSE_MS, millis);
8104 if (GetDocument() &&
8105 GetDocument()->GetReadyStateEnum() != Document::READYSTATE_COMPLETE) {
8106 Telemetry::Accumulate(Telemetry::LOAD_INPUT_EVENT_RESPONSE_MS, millis);
8109 if (!sLastInputProcessed || sLastInputProcessed < aEvent->mTimeStamp) {
8110 if (sLastInputProcessed) {
8111 // This input event was created after we handled the last one.
8112 // Accumulate the previous events' coalesced duration.
8113 double lastMillis =
8114 (sLastInputProcessed - sLastInputCreated).ToMilliseconds();
8115 Telemetry::Accumulate(Telemetry::INPUT_EVENT_RESPONSE_COALESCED_MS,
8116 lastMillis);
8118 if (MOZ_UNLIKELY(!PresShell::sProcessInteractable)) {
8119 // For content process, we use the ready state of
8120 // top-level-content-document to know if the process has finished the
8121 // start-up.
8122 // For parent process, see the topic
8123 // 'sessionstore-one-or-no-tab-restored' in PresShell::Observe.
8124 if (XRE_IsContentProcess() && GetDocument() &&
8125 GetDocument()->IsTopLevelContentDocument()) {
8126 switch (GetDocument()->GetReadyStateEnum()) {
8127 case Document::READYSTATE_INTERACTIVE:
8128 case Document::READYSTATE_COMPLETE:
8129 PresShell::sProcessInteractable = true;
8130 break;
8131 default:
8132 break;
8136 if (MOZ_LIKELY(PresShell::sProcessInteractable)) {
8137 Telemetry::Accumulate(Telemetry::INPUT_EVENT_RESPONSE_POST_STARTUP_MS,
8138 lastMillis);
8139 } else {
8140 Telemetry::Accumulate(Telemetry::INPUT_EVENT_RESPONSE_STARTUP_MS,
8141 lastMillis);
8144 sLastInputCreated = aEvent->mTimeStamp;
8145 } else if (aEvent->mTimeStamp < sLastInputCreated) {
8146 // This event was created before the last input. May be processing out
8147 // of order, so coalesce backwards, too.
8148 sLastInputCreated = aEvent->mTimeStamp;
8150 sLastInputProcessed = now;
8153 // static
8154 already_AddRefed<nsIURI>
8155 PresShell::EventHandler::GetDocumentURIToCompareWithBlacklist(
8156 PresShell& aPresShell) {
8157 nsPresContext* presContext = aPresShell.GetPresContext();
8158 if (NS_WARN_IF(!presContext)) {
8159 return nullptr;
8161 // If the document is sandboxed document or data: document, we should
8162 // get URI of the parent document.
8163 for (Document* document = presContext->Document();
8164 document && document->IsContentDocument();
8165 document = document->GetInProcessParentDocument()) {
8166 // The document URI may be about:blank even if it comes from actual web
8167 // site. Therefore, we need to check the URI of its principal.
8168 nsIPrincipal* principal = document->NodePrincipal();
8169 if (principal->GetIsNullPrincipal()) {
8170 continue;
8172 nsCOMPtr<nsIURI> uri;
8173 principal->GetURI(getter_AddRefs(uri));
8174 return uri.forget();
8176 return nullptr;
8179 nsresult PresShell::EventHandler::DispatchEventToDOM(
8180 WidgetEvent* aEvent, nsEventStatus* aEventStatus,
8181 nsPresShellEventCB* aEventCB) {
8182 nsresult rv = NS_OK;
8183 nsCOMPtr<nsINode> eventTarget = mPresShell->mCurrentEventContent;
8184 nsPresShellEventCB* eventCBPtr = aEventCB;
8185 if (!eventTarget) {
8186 nsCOMPtr<nsIContent> targetContent;
8187 if (mPresShell->mCurrentEventFrame) {
8188 rv = mPresShell->mCurrentEventFrame->GetContentForEvent(
8189 aEvent, getter_AddRefs(targetContent));
8191 if (NS_SUCCEEDED(rv) && targetContent) {
8192 eventTarget = targetContent;
8193 } else if (GetDocument()) {
8194 eventTarget = GetDocument();
8195 // If we don't have any content, the callback wouldn't probably
8196 // do nothing.
8197 eventCBPtr = nullptr;
8200 if (eventTarget) {
8201 if (aEvent->IsBlockedForFingerprintingResistance()) {
8202 aEvent->mFlags.mOnlySystemGroupDispatchInContent = true;
8203 } else if (aEvent->mMessage == eKeyPress) {
8204 // If eKeyPress event is marked as not dispatched in the default event
8205 // group in web content, it's caused by non-printable key or key
8206 // combination. In this case, UI Events declares that browsers
8207 // shouldn't dispatch keypress event. However, some web apps may be
8208 // broken with this strict behavior due to historical issue.
8209 // Therefore, we need to keep dispatching keypress event for such keys
8210 // even with breaking the standard.
8211 // Similarly, the other browsers sets non-zero value of keyCode or
8212 // charCode of keypress event to the other. Therefore, we should
8213 // behave so, however, some web apps may be broken. On such web apps,
8214 // we should keep using legacy our behavior.
8215 if (!mPresShell->mInitializedWithKeyPressEventDispatchingBlacklist) {
8216 mPresShell->mInitializedWithKeyPressEventDispatchingBlacklist = true;
8217 nsCOMPtr<nsIURI> uri =
8218 GetDocumentURIToCompareWithBlacklist(*mPresShell);
8219 mPresShell->mForceDispatchKeyPressEventsForNonPrintableKeys =
8220 nsContentUtils::IsURIInPrefList(
8221 uri,
8222 "dom.keyboardevent.keypress.hack.dispatch_non_printable_keys");
8223 mPresShell->mForceDispatchKeyPressEventsForNonPrintableKeys |=
8224 nsContentUtils::IsURIInPrefList(uri,
8225 "dom.keyboardevent.keypress.hack."
8226 "dispatch_non_printable_keys.addl");
8227 mPresShell->mForceUseLegacyKeyCodeAndCharCodeValues |=
8228 nsContentUtils::IsURIInPrefList(uri,
8229 "dom.keyboardevent.keypress.hack."
8230 "use_legacy_keycode_and_charcode");
8231 mPresShell->mForceUseLegacyKeyCodeAndCharCodeValues |=
8232 nsContentUtils::IsURIInPrefList(
8233 uri,
8234 "dom.keyboardevent.keypress.hack."
8235 "use_legacy_keycode_and_charcode.addl");
8237 if (mPresShell->mForceDispatchKeyPressEventsForNonPrintableKeys) {
8238 aEvent->mFlags.mOnlySystemGroupDispatchInContent = false;
8240 if (mPresShell->mForceUseLegacyKeyCodeAndCharCodeValues) {
8241 aEvent->AsKeyboardEvent()->mUseLegacyKeyCodeAndCharCodeValues = true;
8243 } else if (aEvent->mMessage == eMouseUp) {
8244 // Historically Firefox has dispatched click events for non-primary
8245 // buttons, but only on window and document (and inside input/textarea),
8246 // not on elements in general. The UI events spec forbids click (and
8247 // dblclick) for non-primary mouse buttons, and specifies auxclick
8248 // instead. In case of some websites that rely on non-primary click to
8249 // prevent new tab etc. and don't have auxclick code to do the same, we
8250 // need to revert to the historial non-standard behaviour
8251 if (!mPresShell->mInitializedWithClickEventDispatchingBlacklist) {
8252 mPresShell->mInitializedWithClickEventDispatchingBlacklist = true;
8253 nsCOMPtr<nsIURI> uri =
8254 GetDocumentURIToCompareWithBlacklist(*mPresShell);
8255 mPresShell->mForceUseLegacyNonPrimaryDispatch =
8256 nsContentUtils::IsURIInPrefList(
8257 uri,
8258 "dom.mouseevent.click.hack.use_legacy_non-primary_dispatch");
8260 if (mPresShell->mForceUseLegacyNonPrimaryDispatch) {
8261 aEvent->AsMouseEvent()->mUseLegacyNonPrimaryDispatch = true;
8265 if (aEvent->mClass == eCompositionEventClass) {
8266 IMEStateManager::DispatchCompositionEvent(
8267 eventTarget, GetPresContext(), BrowserParent::GetFocused(),
8268 aEvent->AsCompositionEvent(), aEventStatus, eventCBPtr);
8269 } else {
8270 EventDispatcher::Dispatch(eventTarget, GetPresContext(), aEvent, nullptr,
8271 aEventStatus, eventCBPtr);
8274 return rv;
8277 void PresShell::EventHandler::DispatchTouchEventToDOM(
8278 WidgetEvent* aEvent, nsEventStatus* aEventStatus,
8279 nsPresShellEventCB* aEventCB, bool aTouchIsNew) {
8280 // calling preventDefault on touchstart or the first touchmove for a
8281 // point prevents mouse events. calling it on the touchend should
8282 // prevent click dispatching.
8283 bool canPrevent = (aEvent->mMessage == eTouchStart) ||
8284 (aEvent->mMessage == eTouchMove && aTouchIsNew) ||
8285 (aEvent->mMessage == eTouchEnd);
8286 bool preventDefault = false;
8287 nsEventStatus tmpStatus = nsEventStatus_eIgnore;
8288 WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
8290 // loop over all touches and dispatch events on any that have changed
8291 for (dom::Touch* touch : touchEvent->mTouches) {
8292 // We should remove all suppressed touch instances in
8293 // TouchManager::PreHandleEvent.
8294 MOZ_ASSERT(!touch->mIsTouchEventSuppressed);
8296 if (!touch || !touch->mChanged) {
8297 continue;
8300 nsCOMPtr<EventTarget> targetPtr = touch->mTarget;
8301 nsCOMPtr<nsIContent> content = do_QueryInterface(targetPtr);
8302 if (!content) {
8303 continue;
8306 Document* doc = content->OwnerDoc();
8307 nsIContent* capturingContent = PresShell::GetCapturingContent();
8308 if (capturingContent) {
8309 if (capturingContent->OwnerDoc() != doc) {
8310 // Wrong document, don't dispatch anything.
8311 continue;
8313 content = capturingContent;
8315 // copy the event
8316 MOZ_ASSERT(touchEvent->IsTrusted());
8317 WidgetTouchEvent newEvent(true, touchEvent->mMessage, touchEvent->mWidget);
8318 newEvent.AssignTouchEventData(*touchEvent, false);
8319 newEvent.mTarget = targetPtr;
8320 newEvent.mFlags.mHandledByAPZ = touchEvent->mFlags.mHandledByAPZ;
8322 RefPtr<PresShell> contentPresShell;
8323 if (doc == GetDocument()) {
8324 contentPresShell = doc->GetPresShell();
8325 if (contentPresShell) {
8326 // XXXsmaug huge hack. Pushing possibly capturing content,
8327 // even though event target is something else.
8328 contentPresShell->PushCurrentEventInfo(content->GetPrimaryFrame(),
8329 content);
8333 nsPresContext* context = doc->GetPresContext();
8334 if (!context) {
8335 if (contentPresShell) {
8336 contentPresShell->PopCurrentEventInfo();
8338 continue;
8341 tmpStatus = nsEventStatus_eIgnore;
8342 EventDispatcher::Dispatch(targetPtr, context, &newEvent, nullptr,
8343 &tmpStatus, aEventCB);
8344 if (nsEventStatus_eConsumeNoDefault == tmpStatus ||
8345 newEvent.mFlags.mMultipleActionsPrevented) {
8346 preventDefault = true;
8349 if (newEvent.mFlags.mMultipleActionsPrevented) {
8350 touchEvent->mFlags.mMultipleActionsPrevented = true;
8353 if (contentPresShell) {
8354 contentPresShell->PopCurrentEventInfo();
8358 if (preventDefault && canPrevent) {
8359 *aEventStatus = nsEventStatus_eConsumeNoDefault;
8360 } else {
8361 *aEventStatus = nsEventStatus_eIgnore;
8365 // Dispatch event to content only (NOT full processing)
8366 // See also HandleEventWithTarget which does full event processing.
8367 nsresult PresShell::HandleDOMEventWithTarget(nsIContent* aTargetContent,
8368 WidgetEvent* aEvent,
8369 nsEventStatus* aStatus) {
8370 nsresult rv = NS_OK;
8372 PushCurrentEventInfo(nullptr, aTargetContent);
8374 // Bug 41013: Check if the event should be dispatched to content.
8375 // It's possible that we are in the middle of destroying the window
8376 // and the js context is out of date. This check detects the case
8377 // that caused a crash in bug 41013, but there may be a better way
8378 // to handle this situation!
8379 nsCOMPtr<nsISupports> container = mPresContext->GetContainerWeak();
8380 if (container) {
8381 // Dispatch event to content
8382 rv = EventDispatcher::Dispatch(aTargetContent, mPresContext, aEvent,
8383 nullptr, aStatus);
8386 PopCurrentEventInfo();
8387 return rv;
8390 // See the method above.
8391 nsresult PresShell::HandleDOMEventWithTarget(nsIContent* aTargetContent,
8392 Event* aEvent,
8393 nsEventStatus* aStatus) {
8394 nsresult rv = NS_OK;
8396 PushCurrentEventInfo(nullptr, aTargetContent);
8397 nsCOMPtr<nsISupports> container = mPresContext->GetContainerWeak();
8398 if (container) {
8399 rv = EventDispatcher::DispatchDOMEvent(aTargetContent, nullptr, aEvent,
8400 mPresContext, aStatus);
8403 PopCurrentEventInfo();
8404 return rv;
8407 bool PresShell::EventHandler::AdjustContextMenuKeyEvent(
8408 WidgetMouseEvent* aMouseEvent) {
8409 #ifdef MOZ_XUL
8410 // if a menu is open, open the context menu relative to the active item on the
8411 // menu.
8412 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
8413 if (pm) {
8414 nsIFrame* popupFrame = pm->GetTopPopup(ePopupTypeMenu);
8415 if (popupFrame) {
8416 nsIFrame* itemFrame =
8417 (static_cast<nsMenuPopupFrame*>(popupFrame))->GetCurrentMenuItem();
8418 if (!itemFrame) itemFrame = popupFrame;
8420 nsCOMPtr<nsIWidget> widget = popupFrame->GetNearestWidget();
8421 aMouseEvent->mWidget = widget;
8422 LayoutDeviceIntPoint widgetPoint = widget->WidgetToScreenOffset();
8423 aMouseEvent->mRefPoint =
8424 LayoutDeviceIntPoint::FromAppUnitsToNearest(
8425 itemFrame->GetScreenRectInAppUnits().BottomLeft(),
8426 itemFrame->PresContext()->AppUnitsPerDevPixel()) -
8427 widgetPoint;
8429 mPresShell->mCurrentEventContent = itemFrame->GetContent();
8430 mPresShell->mCurrentEventFrame = itemFrame;
8432 return true;
8435 #endif
8437 // If we're here because of the key-equiv for showing context menus, we
8438 // have to twiddle with the NS event to make sure the context menu comes
8439 // up in the upper left of the relevant content area before we create
8440 // the DOM event. Since we never call InitMouseEvent() on the event,
8441 // the client X/Y will be 0,0. We can make use of that if the widget is null.
8442 // Use the root view manager's widget since it's most likely to have one,
8443 // and the coordinates returned by GetCurrentItemAndPositionForElement
8444 // are relative to the widget of the root of the root view manager.
8445 nsRootPresContext* rootPC = GetPresContext()->GetRootPresContext();
8446 aMouseEvent->mRefPoint = LayoutDeviceIntPoint(0, 0);
8447 if (rootPC) {
8448 rootPC->PresShell()->GetViewManager()->GetRootWidget(
8449 getter_AddRefs(aMouseEvent->mWidget));
8451 if (aMouseEvent->mWidget) {
8452 // default the refpoint to the topleft of our document
8453 nsPoint offset(0, 0);
8454 nsIFrame* rootFrame = FrameConstructor()->GetRootFrame();
8455 if (rootFrame) {
8456 nsView* view = rootFrame->GetClosestView(&offset);
8457 offset += view->GetOffsetToWidget(aMouseEvent->mWidget);
8458 aMouseEvent->mRefPoint = LayoutDeviceIntPoint::FromAppUnitsToNearest(
8459 offset, GetPresContext()->AppUnitsPerDevPixel());
8462 } else {
8463 aMouseEvent->mWidget = nullptr;
8466 // see if we should use the caret position for the popup
8467 LayoutDeviceIntPoint caretPoint;
8468 // Beware! This may flush notifications via synchronous
8469 // ScrollSelectionIntoView.
8470 if (PrepareToUseCaretPosition(MOZ_KnownLive(aMouseEvent->mWidget),
8471 caretPoint)) {
8472 // caret position is good
8473 aMouseEvent->mRefPoint = caretPoint;
8474 return true;
8477 // If we're here because of the key-equiv for showing context menus, we
8478 // have to reset the event target to the currently focused element. Get it
8479 // from the focus controller.
8480 RefPtr<Element> currentFocus;
8481 nsFocusManager* fm = nsFocusManager::GetFocusManager();
8482 if (fm) {
8483 currentFocus = fm->GetFocusedElement();
8486 // Reset event coordinates relative to focused frame in view
8487 if (currentFocus) {
8488 nsCOMPtr<nsIContent> currentPointElement;
8489 GetCurrentItemAndPositionForElement(
8490 currentFocus, getter_AddRefs(currentPointElement),
8491 aMouseEvent->mRefPoint, MOZ_KnownLive(aMouseEvent->mWidget));
8492 if (currentPointElement) {
8493 mPresShell->mCurrentEventContent = currentPointElement;
8494 mPresShell->mCurrentEventFrame = nullptr;
8495 mPresShell->GetCurrentEventFrame();
8499 return true;
8502 // PresShell::EventHandler::PrepareToUseCaretPosition
8504 // This checks to see if we should use the caret position for popup context
8505 // menus. Returns true if the caret position should be used, and the
8506 // coordinates of that position is returned in aTargetPt. This function
8507 // will also scroll the window as needed to make the caret visible.
8509 // The event widget should be the widget that generated the event, and
8510 // whose coordinate system the resulting event's mRefPoint should be
8511 // relative to. The returned point is in device pixels realtive to the
8512 // widget passed in.
8513 bool PresShell::EventHandler::PrepareToUseCaretPosition(
8514 nsIWidget* aEventWidget, LayoutDeviceIntPoint& aTargetPt) {
8515 nsresult rv;
8517 // check caret visibility
8518 RefPtr<nsCaret> caret = mPresShell->GetCaret();
8519 NS_ENSURE_TRUE(caret, false);
8521 bool caretVisible = caret->IsVisible();
8522 if (!caretVisible) return false;
8524 // caret selection, this is a temporary weak reference, so no refcounting is
8525 // needed
8526 Selection* domSelection = caret->GetSelection();
8527 NS_ENSURE_TRUE(domSelection, false);
8529 // since the match could be an anonymous textnode inside a
8530 // <textarea> or text <input>, we need to get the outer frame
8531 // note: frames are not refcounted
8532 nsIFrame* frame = nullptr; // may be nullptr
8533 nsINode* node = domSelection->GetFocusNode();
8534 NS_ENSURE_TRUE(node, false);
8535 nsCOMPtr<nsIContent> content = nsIContent::FromNode(node);
8536 if (content) {
8537 nsIContent* nonNative = content->FindFirstNonChromeOnlyAccessContent();
8538 content = nonNative;
8541 if (content) {
8542 // It seems like ScrollSelectionIntoView should be enough, but it's
8543 // not. The problem is that scrolling the selection into view when it is
8544 // below the current viewport will align the top line of the frame exactly
8545 // with the bottom of the window. This is fine, BUT, the popup event causes
8546 // the control to be re-focused which does this exact call to
8547 // ScrollContentIntoView, which has a one-pixel disagreement of whether the
8548 // frame is actually in view. The result is that the frame is aligned with
8549 // the top of the window, but the menu is still at the bottom.
8551 // Doing this call first forces the frame to be in view, eliminating the
8552 // problem. The only difference in the result is that if your cursor is in
8553 // an edit box below the current view, you'll get the edit box aligned with
8554 // the top of the window. This is arguably better behavior anyway.
8555 rv =
8556 MOZ_KnownLive(mPresShell)
8557 ->ScrollContentIntoView(
8558 content, ScrollAxis(kScrollMinimum, WhenToScroll::IfNotVisible),
8559 ScrollAxis(kScrollMinimum, WhenToScroll::IfNotVisible),
8560 ScrollFlags::ScrollOverflowHidden |
8561 ScrollFlags::IgnoreMarginAndPadding);
8562 NS_ENSURE_SUCCESS(rv, false);
8563 frame = content->GetPrimaryFrame();
8564 NS_WARNING_ASSERTION(frame, "No frame for focused content?");
8567 // Actually scroll the selection (ie caret) into view. Note that this must
8568 // be synchronous since we will be checking the caret position on the screen.
8570 // Be easy about errors, and just don't scroll in those cases. Better to have
8571 // the correct menu at a weird place than the wrong menu.
8572 // After ScrollSelectionIntoView(), the pending notifications might be
8573 // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
8574 nsCOMPtr<nsISelectionController> selCon;
8575 if (frame)
8576 frame->GetSelectionController(GetPresContext(), getter_AddRefs(selCon));
8577 else
8578 selCon = static_cast<nsISelectionController*>(mPresShell);
8579 if (selCon) {
8580 rv = selCon->ScrollSelectionIntoView(
8581 nsISelectionController::SELECTION_NORMAL,
8582 nsISelectionController::SELECTION_FOCUS_REGION,
8583 nsISelectionController::SCROLL_SYNCHRONOUS);
8584 NS_ENSURE_SUCCESS(rv, false);
8587 nsPresContext* presContext = GetPresContext();
8589 // get caret position relative to the closest view
8590 nsRect caretCoords;
8591 nsIFrame* caretFrame = caret->GetGeometry(&caretCoords);
8592 if (!caretFrame) return false;
8593 nsPoint viewOffset;
8594 nsView* view = caretFrame->GetClosestView(&viewOffset);
8595 if (!view) return false;
8596 // and then get the caret coords relative to the event widget
8597 if (aEventWidget) {
8598 viewOffset += view->GetOffsetToWidget(aEventWidget);
8600 caretCoords.MoveBy(viewOffset);
8602 // caret coordinates are in app units, convert to pixels
8603 aTargetPt.x =
8604 presContext->AppUnitsToDevPixels(caretCoords.x + caretCoords.width);
8605 aTargetPt.y =
8606 presContext->AppUnitsToDevPixels(caretCoords.y + caretCoords.height);
8608 // make sure rounding doesn't return a pixel which is outside the caret
8609 // (e.g. one line lower)
8610 aTargetPt.y -= 1;
8612 return true;
8615 void PresShell::EventHandler::GetCurrentItemAndPositionForElement(
8616 Element* aFocusedElement, nsIContent** aTargetToUse,
8617 LayoutDeviceIntPoint& aTargetPt, nsIWidget* aRootWidget) {
8618 nsCOMPtr<nsIContent> focusedContent = aFocusedElement;
8619 MOZ_KnownLive(mPresShell)
8620 ->ScrollContentIntoView(focusedContent, ScrollAxis(), ScrollAxis(),
8621 ScrollFlags::ScrollOverflowHidden |
8622 ScrollFlags::IgnoreMarginAndPadding);
8624 nsPresContext* presContext = GetPresContext();
8626 bool istree = false, checkLineHeight = true;
8627 nscoord extraTreeY = 0;
8629 #ifdef MOZ_XUL
8630 // Set the position to just underneath the current item for multi-select
8631 // lists or just underneath the selected item for single-select lists. If
8632 // the element is not a list, or there is no selection, leave the position
8633 // as is.
8634 nsCOMPtr<Element> item;
8635 nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelect =
8636 aFocusedElement->AsXULMultiSelectControl();
8637 if (multiSelect) {
8638 checkLineHeight = false;
8640 int32_t currentIndex;
8641 multiSelect->GetCurrentIndex(&currentIndex);
8642 if (currentIndex >= 0) {
8643 RefPtr<XULTreeElement> tree = XULTreeElement::FromNode(focusedContent);
8644 // Tree view special case (tree items have no frames)
8645 // Get the focused row and add its coordinates, which are already in
8646 // pixels
8647 // XXX Boris, should we create a new interface so that this doesn't
8648 // need to know about trees? Something like nsINodelessChildCreator
8649 // which could provide the current focus coordinates?
8650 if (tree) {
8651 tree->EnsureRowIsVisible(currentIndex);
8652 int32_t firstVisibleRow = tree->GetFirstVisibleRow();
8653 int32_t rowHeight = tree->RowHeight();
8655 extraTreeY += nsPresContext::CSSPixelsToAppUnits(
8656 (currentIndex - firstVisibleRow + 1) * rowHeight);
8657 istree = true;
8659 RefPtr<nsTreeColumns> cols = tree->GetColumns();
8660 if (cols) {
8661 nsTreeColumn* col = cols->GetFirstColumn();
8662 if (col) {
8663 RefPtr<Element> colElement = col->Element();
8664 nsIFrame* frame = colElement->GetPrimaryFrame();
8665 if (frame) {
8666 extraTreeY += frame->GetSize().height;
8670 } else {
8671 multiSelect->GetCurrentItem(getter_AddRefs(item));
8674 } else {
8675 // don't check menulists as the selected item will be inside a popup.
8676 nsCOMPtr<nsIDOMXULMenuListElement> menulist =
8677 aFocusedElement->AsXULMenuList();
8678 if (!menulist) {
8679 nsCOMPtr<nsIDOMXULSelectControlElement> select =
8680 aFocusedElement->AsXULSelectControl();
8681 if (select) {
8682 checkLineHeight = false;
8683 select->GetSelectedItem(getter_AddRefs(item));
8688 if (item) {
8689 focusedContent = item;
8691 #endif
8693 nsIFrame* frame = focusedContent->GetPrimaryFrame();
8694 if (frame) {
8695 NS_ASSERTION(
8696 frame->PresContext() == GetPresContext(),
8697 "handling event for focused content that is not in our document?");
8699 nsPoint frameOrigin(0, 0);
8701 // Get the frame's origin within its view
8702 nsView* view = frame->GetClosestView(&frameOrigin);
8703 NS_ASSERTION(view, "No view for frame");
8705 // View's origin relative the widget
8706 if (aRootWidget) {
8707 frameOrigin += view->GetOffsetToWidget(aRootWidget);
8710 // Start context menu down and to the right from top left of frame
8711 // use the lineheight. This is a good distance to move the context
8712 // menu away from the top left corner of the frame. If we always
8713 // used the frame height, the context menu could end up far away,
8714 // for example when we're focused on linked images.
8715 // On the other hand, we want to use the frame height if it's less
8716 // than the current line height, so that the context menu appears
8717 // associated with the correct frame.
8718 nscoord extra = 0;
8719 if (!istree) {
8720 extra = frame->GetSize().height;
8721 if (checkLineHeight) {
8722 nsIScrollableFrame* scrollFrame =
8723 nsLayoutUtils::GetNearestScrollableFrame(frame);
8724 if (scrollFrame) {
8725 nsSize scrollAmount = scrollFrame->GetLineScrollAmount();
8726 nsIFrame* f = do_QueryFrame(scrollFrame);
8727 int32_t APD = presContext->AppUnitsPerDevPixel();
8728 int32_t scrollAPD = f->PresContext()->AppUnitsPerDevPixel();
8729 scrollAmount = scrollAmount.ScaleToOtherAppUnits(scrollAPD, APD);
8730 if (extra > scrollAmount.height) {
8731 extra = scrollAmount.height;
8737 aTargetPt.x = presContext->AppUnitsToDevPixels(frameOrigin.x);
8738 aTargetPt.y =
8739 presContext->AppUnitsToDevPixels(frameOrigin.y + extra + extraTreeY);
8742 NS_IF_ADDREF(*aTargetToUse = focusedContent);
8745 bool PresShell::ShouldIgnoreInvalidation() {
8746 return mPaintingSuppressed || !mIsActive || mIsNeverPainting;
8749 void PresShell::WillPaint() {
8750 // Check the simplest things first. In particular, it's important to
8751 // check mIsActive before making any of the more expensive calls such
8752 // as GetRootPresContext, for the case of a browser with a large
8753 // number of tabs.
8754 // Don't bother doing anything if some viewmanager in our tree is painting
8755 // while we still have painting suppressed or we are not active.
8756 if (!mIsActive || mPaintingSuppressed || !IsVisible()) {
8757 return;
8760 nsRootPresContext* rootPresContext = mPresContext->GetRootPresContext();
8761 if (!rootPresContext) {
8762 // In some edge cases, such as when we don't have a root frame yet,
8763 // we can't find the root prescontext. There's nothing to do in that
8764 // case.
8765 return;
8768 rootPresContext->FlushWillPaintObservers();
8769 if (mIsDestroying) return;
8771 // Process reflows, if we have them, to reduce flicker due to invalidates and
8772 // reflow being interspersed. Note that we _do_ allow this to be
8773 // interruptible; if we can't do all the reflows it's better to flicker a bit
8774 // than to freeze up.
8775 FlushPendingNotifications(
8776 ChangesToFlush(FlushType::InterruptibleLayout, false));
8779 void PresShell::WillPaintWindow() {
8780 nsRootPresContext* rootPresContext = mPresContext->GetRootPresContext();
8781 if (rootPresContext != mPresContext) {
8782 // This could be a popup's presshell. We don't allow plugins in popups
8783 // so there's nothing to do here.
8784 return;
8787 #ifndef XP_MACOSX
8788 rootPresContext->ApplyPluginGeometryUpdates();
8789 #endif
8792 void PresShell::DidPaintWindow() {
8793 nsRootPresContext* rootPresContext = mPresContext->GetRootPresContext();
8794 if (rootPresContext != mPresContext) {
8795 // This could be a popup's presshell. No point in notifying XPConnect
8796 // about compositing of popups.
8797 return;
8800 if (!mHasReceivedPaintMessage) {
8801 mHasReceivedPaintMessage = true;
8803 nsCOMPtr<nsIObserverService> obsvc = services::GetObserverService();
8804 if (obsvc && mDocument) {
8805 nsPIDOMWindowOuter* window = mDocument->GetWindow();
8806 nsCOMPtr<nsIDOMChromeWindow> chromeWin(do_QueryInterface(window));
8807 if (chromeWin) {
8808 obsvc->NotifyObservers(chromeWin, "widget-first-paint", nullptr);
8814 bool PresShell::IsVisible() const {
8815 if (!mIsActive || !mViewManager) return false;
8817 nsView* view = mViewManager->GetRootView();
8818 if (!view) return true;
8820 // inner view of subdoc frame
8821 view = view->GetParent();
8822 if (!view) return true;
8824 // subdoc view
8825 view = view->GetParent();
8826 if (!view) return true;
8828 nsIFrame* frame = view->GetFrame();
8829 if (!frame) return true;
8831 return frame->IsVisibleConsideringAncestors(
8832 nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY);
8835 void PresShell::SuppressDisplayport(bool aEnabled) {
8836 if (aEnabled) {
8837 mActiveSuppressDisplayport++;
8838 } else if (mActiveSuppressDisplayport > 0) {
8839 bool isSuppressed = IsDisplayportSuppressed();
8840 mActiveSuppressDisplayport--;
8841 if (isSuppressed && !IsDisplayportSuppressed()) {
8842 // We unsuppressed the displayport, trigger a paint
8843 if (nsIFrame* rootFrame = mFrameConstructor->GetRootFrame()) {
8844 rootFrame->SchedulePaint();
8850 static bool sDisplayPortSuppressionRespected = true;
8852 void PresShell::RespectDisplayportSuppression(bool aEnabled) {
8853 bool isSuppressed = IsDisplayportSuppressed();
8854 sDisplayPortSuppressionRespected = aEnabled;
8855 if (isSuppressed && !IsDisplayportSuppressed()) {
8856 // We unsuppressed the displayport, trigger a paint
8857 if (nsIFrame* rootFrame = mFrameConstructor->GetRootFrame()) {
8858 rootFrame->SchedulePaint();
8863 bool PresShell::IsDisplayportSuppressed() {
8864 return sDisplayPortSuppressionRespected && mActiveSuppressDisplayport > 0;
8867 nsresult PresShell::AddOverrideStyleSheet(StyleSheet* aSheet) {
8868 StyleSet()->AppendStyleSheet(aSheet->GetOrigin(), aSheet);
8869 return NS_OK;
8872 nsresult PresShell::RemoveOverrideStyleSheet(StyleSheet* aSheet) {
8873 StyleSet()->RemoveStyleSheet(aSheet->GetOrigin(), aSheet);
8874 return NS_OK;
8877 static void FreezeElement(nsISupports* aSupports, void* /* unused */) {
8878 nsCOMPtr<nsIObjectLoadingContent> olc(do_QueryInterface(aSupports));
8879 if (olc) {
8880 olc->StopPluginInstance();
8884 static bool FreezeSubDocument(Document* aDocument, void* aData) {
8885 PresShell* presShell = aDocument->GetPresShell();
8886 if (presShell) {
8887 presShell->Freeze();
8889 return true;
8892 void PresShell::Freeze() {
8893 mUpdateApproximateFrameVisibilityEvent.Revoke();
8895 MaybeReleaseCapturingContent();
8897 mDocument->EnumerateActivityObservers(FreezeElement, nullptr);
8899 if (mCaret) {
8900 SetCaretEnabled(false);
8903 mPaintingSuppressed = true;
8905 if (mDocument) {
8906 mDocument->EnumerateSubDocuments(FreezeSubDocument, nullptr);
8909 nsPresContext* presContext = GetPresContext();
8910 if (presContext) {
8911 presContext->DisableInteractionTimeRecording();
8912 if (presContext->RefreshDriver()->GetPresContext() == presContext) {
8913 presContext->RefreshDriver()->Freeze();
8917 mFrozen = true;
8918 if (mDocument) {
8919 UpdateImageLockingState();
8923 void PresShell::FireOrClearDelayedEvents(bool aFireEvents) {
8924 mNoDelayedMouseEvents = false;
8925 mNoDelayedKeyEvents = false;
8926 if (!aFireEvents) {
8927 mDelayedEvents.Clear();
8928 return;
8931 if (mDocument) {
8932 RefPtr<Document> doc = mDocument;
8933 while (!mIsDestroying && mDelayedEvents.Length() &&
8934 !doc->EventHandlingSuppressed()) {
8935 UniquePtr<DelayedEvent> ev = std::move(mDelayedEvents[0]);
8936 mDelayedEvents.RemoveElementAt(0);
8937 if (ev->IsKeyPressEvent() && mIsLastKeyDownCanceled) {
8938 continue;
8940 ev->Dispatch();
8942 if (!doc->EventHandlingSuppressed()) {
8943 mDelayedEvents.Clear();
8948 static void ThawElement(nsISupports* aSupports, void* aShell) {
8949 nsCOMPtr<nsIObjectLoadingContent> olc(do_QueryInterface(aSupports));
8950 if (olc) {
8951 olc->AsyncStartPluginInstance();
8955 static bool ThawSubDocument(Document* aDocument, void* aData) {
8956 PresShell* presShell = aDocument->GetPresShell();
8957 if (presShell) {
8958 presShell->Thaw();
8960 return true;
8963 void PresShell::Thaw() {
8964 nsPresContext* presContext = GetPresContext();
8965 if (presContext &&
8966 presContext->RefreshDriver()->GetPresContext() == presContext) {
8967 presContext->RefreshDriver()->Thaw();
8970 mDocument->EnumerateActivityObservers(ThawElement, this);
8972 if (mDocument) mDocument->EnumerateSubDocuments(ThawSubDocument, nullptr);
8974 // Get the activeness of our presshell, as this might have changed
8975 // while we were in the bfcache
8976 QueryIsActive();
8978 // We're now unfrozen
8979 mFrozen = false;
8980 UpdateImageLockingState();
8982 UnsuppressPainting();
8985 //--------------------------------------------------------
8986 // Start of protected and private methods on the PresShell
8987 //--------------------------------------------------------
8989 void PresShell::MaybeScheduleReflow() {
8990 ASSERT_REFLOW_SCHEDULED_STATE();
8991 if (mObservingLayoutFlushes || mIsDestroying || mIsReflowing ||
8992 mDirtyRoots.IsEmpty())
8993 return;
8995 if (!mPresContext->HasPendingInterrupt() || !ScheduleReflowOffTimer()) {
8996 ScheduleReflow();
8999 ASSERT_REFLOW_SCHEDULED_STATE();
9002 void PresShell::ScheduleReflow() {
9003 ASSERT_REFLOW_SCHEDULED_STATE();
9004 DoObserveLayoutFlushes();
9005 ASSERT_REFLOW_SCHEDULED_STATE();
9008 void PresShell::WillCauseReflow() {
9009 nsContentUtils::AddScriptBlocker();
9010 ++mChangeNestCount;
9013 void PresShell::DidCauseReflow() {
9014 NS_ASSERTION(mChangeNestCount != 0, "Unexpected call to DidCauseReflow()");
9015 --mChangeNestCount;
9016 nsContentUtils::RemoveScriptBlocker();
9019 void PresShell::WillDoReflow() {
9020 mDocument->FlushUserFontSet();
9022 mPresContext->FlushCounterStyles();
9024 mPresContext->FlushFontFeatureValues();
9026 mLastReflowStart = GetPerformanceNowUnclamped();
9029 void PresShell::DidDoReflow(bool aInterruptible) {
9030 HandlePostedReflowCallbacks(aInterruptible);
9032 AutoAssertNoFlush noReentrantFlush(*this);
9033 if (nsCOMPtr<nsIDocShell> docShell = mPresContext->GetDocShell()) {
9034 DOMHighResTimeStamp now = GetPerformanceNowUnclamped();
9035 docShell->NotifyReflowObservers(aInterruptible, mLastReflowStart, now);
9038 if (!mPresContext->HasPendingInterrupt()) {
9039 mDocument->ScheduleResizeObserversNotification();
9042 if (StaticPrefs::layout_reflow_synthMouseMove()) {
9043 SynthesizeMouseMove(false);
9046 mPresContext->NotifyMissingFonts();
9049 DOMHighResTimeStamp PresShell::GetPerformanceNowUnclamped() {
9050 DOMHighResTimeStamp now = 0;
9052 if (nsPIDOMWindowInner* window = mDocument->GetInnerWindow()) {
9053 Performance* perf = window->GetPerformance();
9055 if (perf) {
9056 now = perf->NowUnclamped();
9060 return now;
9063 void PresShell::sReflowContinueCallback(nsITimer* aTimer, void* aPresShell) {
9064 RefPtr<PresShell> self = static_cast<PresShell*>(aPresShell);
9066 MOZ_ASSERT(aTimer == self->mReflowContinueTimer, "Unexpected timer");
9067 self->mReflowContinueTimer = nullptr;
9068 self->ScheduleReflow();
9071 bool PresShell::ScheduleReflowOffTimer() {
9072 MOZ_ASSERT(!mObservingLayoutFlushes, "Shouldn't get here");
9073 ASSERT_REFLOW_SCHEDULED_STATE();
9075 if (!mReflowContinueTimer) {
9076 nsresult rv = NS_NewTimerWithFuncCallback(
9077 getter_AddRefs(mReflowContinueTimer), sReflowContinueCallback, this, 30,
9078 nsITimer::TYPE_ONE_SHOT, "sReflowContinueCallback",
9079 mDocument->EventTargetFor(TaskCategory::Other));
9080 return NS_SUCCEEDED(rv);
9082 return true;
9085 bool PresShell::DoReflow(nsIFrame* target, bool aInterruptible,
9086 OverflowChangedTracker* aOverflowTracker) {
9087 #ifdef MOZ_GECKO_PROFILER
9088 nsIURI* uri = mDocument->GetDocumentURI();
9089 AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING(
9090 "Reflow", LAYOUT_Reflow,
9091 uri ? uri->GetSpecOrDefault() : NS_LITERAL_CSTRING("N/A"));
9092 #endif
9093 PerfStats::AutoMetricRecording<PerfStats::Metric::Reflowing> autoRecording;
9095 gfxTextPerfMetrics* tp = mPresContext->GetTextPerfMetrics();
9096 TimeStamp timeStart;
9097 if (tp) {
9098 tp->Accumulate();
9099 tp->reflowCount++;
9100 timeStart = TimeStamp::Now();
9103 // Schedule a paint, but don't actually mark this frame as changed for
9104 // retained DL building purposes. If any child frames get moved, then
9105 // they will schedule paint again. We could probaby skip this, and just
9106 // schedule a similar paint when a frame is deleted.
9107 target->SchedulePaint(nsIFrame::PAINT_DEFAULT, false);
9109 nsDocShell* docShell =
9110 static_cast<nsDocShell*>(GetPresContext()->GetDocShell());
9111 RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
9112 bool isTimelineRecording = timelines && timelines->HasConsumer(docShell);
9114 if (isTimelineRecording) {
9115 timelines->AddMarkerForDocShell(docShell, "Reflow",
9116 MarkerTracingType::START);
9119 #ifdef MOZ_GECKO_PROFILER
9120 DECLARE_DOCSHELL_AND_HISTORY_ID(docShell);
9121 AutoProfilerTracing tracingLayoutFlush(
9122 "Paint", "Reflow", JS::ProfilingCategoryPair::LAYOUT,
9123 std::move(mReflowCause), docShellId, docShellHistoryId);
9124 mReflowCause = nullptr;
9125 #endif
9127 FlushPendingScrollAnchorSelections();
9129 if (mReflowContinueTimer) {
9130 mReflowContinueTimer->Cancel();
9131 mReflowContinueTimer = nullptr;
9134 const bool isRoot = target == mFrameConstructor->GetRootFrame();
9136 MOZ_ASSERT(isRoot || aOverflowTracker,
9137 "caller must provide overflow tracker when reflowing "
9138 "non-root frames");
9140 // CreateReferenceRenderingContext can return nullptr
9141 RefPtr<gfxContext> rcx(CreateReferenceRenderingContext());
9143 #ifdef DEBUG
9144 mCurrentReflowRoot = target;
9145 #endif
9147 // If the target frame is the root of the frame hierarchy, then
9148 // use all the available space. If it's simply a `reflow root',
9149 // then use the target frame's size as the available space.
9150 WritingMode wm = target->GetWritingMode();
9151 LogicalSize size(wm);
9152 if (isRoot) {
9153 size = LogicalSize(wm, mPresContext->GetVisibleArea().Size());
9154 } else {
9155 size = target->GetLogicalSize();
9158 nsOverflowAreas oldOverflow; // initialized and used only when !isRoot
9159 if (!isRoot) {
9160 oldOverflow = target->GetOverflowAreas();
9163 NS_ASSERTION(!target->GetNextInFlow() && !target->GetPrevInFlow(),
9164 "reflow roots should never split");
9166 // Don't pass size directly to the reflow input, since a
9167 // constrained height implies page/column breaking.
9168 LogicalSize reflowSize(wm, size.ISize(wm), NS_UNCONSTRAINEDSIZE);
9169 ReflowInput reflowInput(mPresContext, target, rcx, reflowSize,
9170 ReflowInput::CALLER_WILL_INIT);
9171 reflowInput.mOrthogonalLimit = size.BSize(wm);
9173 if (isRoot) {
9174 reflowInput.Init(mPresContext);
9176 // When the root frame is being reflowed with unconstrained block-size
9177 // (which happens when we're called from
9178 // nsDocumentViewer::SizeToContent), we're effectively doing a
9179 // resize in the block direction, since it changes the meaning of
9180 // percentage block-sizes even if no block-sizes actually changed.
9181 // The same applies when we reflow again after that computation. This is
9182 // an unusual case, and isn't caught by ReflowInput::InitResizeFlags.
9183 bool hasUnconstrainedBSize = size.BSize(wm) == NS_UNCONSTRAINEDSIZE;
9185 if (hasUnconstrainedBSize || mLastRootReflowHadUnconstrainedBSize) {
9186 reflowInput.SetBResize(true);
9189 mLastRootReflowHadUnconstrainedBSize = hasUnconstrainedBSize;
9190 } else {
9191 // Initialize reflow input with current used border and padding,
9192 // in case this was set specially by the parent frame when the reflow root
9193 // was reflowed by its parent.
9194 nsMargin currentBorder = target->GetUsedBorder();
9195 nsMargin currentPadding = target->GetUsedPadding();
9196 reflowInput.Init(mPresContext, Nothing(), &currentBorder, &currentPadding);
9199 // fix the computed height
9200 NS_ASSERTION(reflowInput.ComputedPhysicalMargin() == nsMargin(0, 0, 0, 0),
9201 "reflow input should not set margin for reflow roots");
9202 if (size.BSize(wm) != NS_UNCONSTRAINEDSIZE) {
9203 nscoord computedBSize =
9204 size.BSize(wm) -
9205 reflowInput.ComputedLogicalBorderPadding().BStartEnd(wm);
9206 computedBSize = std::max(computedBSize, 0);
9207 reflowInput.SetComputedBSize(computedBSize);
9209 NS_ASSERTION(reflowInput.ComputedISize() ==
9210 size.ISize(wm) -
9211 reflowInput.ComputedLogicalBorderPadding().IStartEnd(wm),
9212 "reflow input computed incorrect inline size");
9214 mPresContext->ReflowStarted(aInterruptible);
9215 mIsReflowing = true;
9217 nsReflowStatus status;
9218 ReflowOutput desiredSize(reflowInput);
9219 target->Reflow(mPresContext, desiredSize, reflowInput, status);
9221 // If an incremental reflow is initiated at a frame other than the
9222 // root frame, then its desired size had better not change! If it's
9223 // initiated at the root, then the size better not change unless its
9224 // height was unconstrained to start with.
9225 nsRect boundsRelativeToTarget =
9226 nsRect(0, 0, desiredSize.Width(), desiredSize.Height());
9227 NS_ASSERTION((isRoot && size.BSize(wm) == NS_UNCONSTRAINEDSIZE) ||
9228 (desiredSize.ISize(wm) == size.ISize(wm) &&
9229 desiredSize.BSize(wm) == size.BSize(wm)),
9230 "non-root frame's desired size changed during an "
9231 "incremental reflow");
9232 NS_ASSERTION(status.IsEmpty(), "reflow roots should never split");
9234 target->SetSize(boundsRelativeToTarget.Size());
9236 // Always use boundsRelativeToTarget here, not
9237 // desiredSize.GetVisualOverflowArea(), because for root frames (where they
9238 // could be different, since root frames are allowed to have overflow) the
9239 // root view bounds need to match the viewport bounds; the view manager
9240 // "window dimensions" code depends on it.
9241 nsContainerFrame::SyncFrameViewAfterReflow(
9242 mPresContext, target, target->GetView(), boundsRelativeToTarget);
9243 nsContainerFrame::SyncWindowProperties(mPresContext, target,
9244 target->GetView(), rcx,
9245 nsContainerFrame::SET_ASYNC);
9247 target->DidReflow(mPresContext, nullptr);
9248 if (target->IsInScrollAnchorChain()) {
9249 ScrollAnchorContainer* container = ScrollAnchorContainer::FindFor(target);
9250 PostPendingScrollAnchorAdjustment(container);
9252 if (isRoot && size.BSize(wm) == NS_UNCONSTRAINEDSIZE) {
9253 mPresContext->SetVisibleArea(boundsRelativeToTarget);
9256 #ifdef DEBUG
9257 mCurrentReflowRoot = nullptr;
9258 #endif
9260 if (!isRoot && oldOverflow != target->GetOverflowAreas()) {
9261 // The overflow area changed. Propagate this change to ancestors.
9262 aOverflowTracker->AddFrame(target->GetParent(),
9263 OverflowChangedTracker::CHILDREN_CHANGED);
9266 NS_ASSERTION(
9267 mPresContext->HasPendingInterrupt() || mFramesToDirty.Count() == 0,
9268 "Why do we need to dirty anything if not interrupted?");
9270 mIsReflowing = false;
9271 bool interrupted = mPresContext->HasPendingInterrupt();
9272 if (interrupted) {
9273 // Make sure target gets reflowed again.
9274 for (auto iter = mFramesToDirty.Iter(); !iter.Done(); iter.Next()) {
9275 // Mark frames dirty until target frame.
9276 nsPtrHashKey<nsIFrame>* p = iter.Get();
9277 for (nsIFrame* f = p->GetKey(); f && !NS_SUBTREE_DIRTY(f);
9278 f = f->GetParent()) {
9279 f->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
9281 if (f == target) {
9282 break;
9287 NS_ASSERTION(NS_SUBTREE_DIRTY(target), "Why is the target not dirty?");
9288 mDirtyRoots.Add(target);
9289 SetNeedLayoutFlush();
9291 // Clear mFramesToDirty after we've done the NS_SUBTREE_DIRTY(target)
9292 // assertion so that if it fails it's easier to see what's going on.
9293 #ifdef NOISY_INTERRUPTIBLE_REFLOW
9294 printf("mFramesToDirty.Count() == %u\n", mFramesToDirty.Count());
9295 #endif /* NOISY_INTERRUPTIBLE_REFLOW */
9296 mFramesToDirty.Clear();
9298 // Any FlushPendingNotifications with interruptible reflows
9299 // should be suppressed now. We don't want to do extra reflow work
9300 // before our reflow event happens.
9301 mWasLastReflowInterrupted = true;
9302 MaybeScheduleReflow();
9305 // dump text perf metrics for reflows with significant text processing
9306 if (tp) {
9307 if (tp->current.numChars > 100) {
9308 TimeDuration reflowTime = TimeStamp::Now() - timeStart;
9309 LogTextPerfStats(tp, this, tp->current, reflowTime.ToMilliseconds(),
9310 eLog_reflow, nullptr);
9312 tp->Accumulate();
9315 if (isTimelineRecording) {
9316 timelines->AddMarkerForDocShell(docShell, "Reflow", MarkerTracingType::END);
9319 return !interrupted;
9322 #ifdef DEBUG
9323 void PresShell::DoVerifyReflow() {
9324 if (GetVerifyReflowEnable()) {
9325 // First synchronously render what we have so far so that we can
9326 // see it.
9327 nsView* rootView = mViewManager->GetRootView();
9328 mViewManager->InvalidateView(rootView);
9330 FlushPendingNotifications(FlushType::Layout);
9331 mInVerifyReflow = true;
9332 bool ok = VerifyIncrementalReflow();
9333 mInVerifyReflow = false;
9334 if (VerifyReflowFlags::All & gVerifyReflowFlags) {
9335 printf("ProcessReflowCommands: finished (%s)\n", ok ? "ok" : "failed");
9338 if (!mDirtyRoots.IsEmpty()) {
9339 printf("XXX yikes! reflow commands queued during verify-reflow\n");
9343 #endif
9345 // used with Telemetry metrics
9346 #define NS_LONG_REFLOW_TIME_MS 5000
9348 bool PresShell::ProcessReflowCommands(bool aInterruptible) {
9349 if (mDirtyRoots.IsEmpty() && !mShouldUnsuppressPainting) {
9350 // Nothing to do; bail out
9351 return true;
9354 mozilla::TimeStamp timerStart = mozilla::TimeStamp::Now();
9355 bool interrupted = false;
9356 if (!mDirtyRoots.IsEmpty()) {
9357 #ifdef DEBUG
9358 if (VerifyReflowFlags::DumpCommands & gVerifyReflowFlags) {
9359 printf("ProcessReflowCommands: begin incremental reflow\n");
9361 #endif
9363 // If reflow is interruptible, then make a note of our deadline.
9364 const PRIntervalTime deadline =
9365 aInterruptible
9366 ? PR_IntervalNow() + PR_MicrosecondsToInterval(gMaxRCProcessingTime)
9367 : (PRIntervalTime)0;
9369 // Scope for the reflow entry point
9371 nsAutoScriptBlocker scriptBlocker;
9372 WillDoReflow();
9373 AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Reflow);
9374 nsViewManager::AutoDisableRefresh refreshBlocker(mViewManager);
9376 OverflowChangedTracker overflowTracker;
9378 do {
9379 // Send an incremental reflow notification to the target frame.
9380 nsIFrame* target = mDirtyRoots.PopShallowestRoot();
9382 if (!NS_SUBTREE_DIRTY(target)) {
9383 // It's not dirty anymore, which probably means the notification
9384 // was posted in the middle of a reflow (perhaps with a reflow
9385 // root in the middle). Don't do anything.
9386 continue;
9389 interrupted = !DoReflow(target, aInterruptible, &overflowTracker);
9391 // Keep going until we're out of reflow commands, or we've run
9392 // past our deadline, or we're interrupted.
9393 } while (!interrupted && !mDirtyRoots.IsEmpty() &&
9394 (!aInterruptible || PR_IntervalNow() < deadline));
9396 interrupted = !mDirtyRoots.IsEmpty();
9398 overflowTracker.Flush();
9400 if (!interrupted) {
9401 // We didn't get interrupted. Go ahead and perform scroll anchor
9402 // adjustments.
9403 FlushPendingScrollAnchorAdjustments();
9407 // Exiting the scriptblocker might have killed us
9408 if (!mIsDestroying) {
9409 DidDoReflow(aInterruptible);
9412 // DidDoReflow might have killed us
9413 if (!mIsDestroying) {
9414 #ifdef DEBUG
9415 if (VerifyReflowFlags::DumpCommands & gVerifyReflowFlags) {
9416 printf("\nPresShell::ProcessReflowCommands() finished: this=%p\n",
9417 (void*)this);
9419 DoVerifyReflow();
9420 #endif
9422 // If any new reflow commands were enqueued during the reflow, schedule
9423 // another reflow event to process them. Note that we want to do this
9424 // after DidDoReflow(), since that method can change whether there are
9425 // dirty roots around by flushing, and there's no point in posting a
9426 // reflow event just to have the flush revoke it.
9427 if (!mDirtyRoots.IsEmpty()) {
9428 MaybeScheduleReflow();
9429 // And record that we might need flushing
9430 SetNeedLayoutFlush();
9435 if (!mIsDestroying && mShouldUnsuppressPainting && mDirtyRoots.IsEmpty()) {
9436 // We only unlock if we're out of reflows. It's pointless
9437 // to unlock if reflows are still pending, since reflows
9438 // are just going to thrash the frames around some more. By
9439 // waiting we avoid an overeager "jitter" effect.
9440 mShouldUnsuppressPainting = false;
9441 UnsuppressAndInvalidate();
9444 if (mDocument->GetRootElement()) {
9445 TimeDuration elapsed = TimeStamp::Now() - timerStart;
9446 int32_t intElapsed = int32_t(elapsed.ToMilliseconds());
9448 if (intElapsed > NS_LONG_REFLOW_TIME_MS) {
9449 Telemetry::Accumulate(Telemetry::LONG_REFLOW_INTERRUPTIBLE,
9450 aInterruptible ? 1 : 0);
9454 return !interrupted;
9457 void PresShell::WindowSizeMoveDone() {
9458 if (mPresContext) {
9459 EventStateManager::ClearGlobalActiveContent(nullptr);
9460 ClearMouseCapture(nullptr);
9464 NS_IMETHODIMP
9465 PresShell::Observe(nsISupports* aSubject, const char* aTopic,
9466 const char16_t* aData) {
9467 if (mIsDestroying) {
9468 NS_WARNING("our observers should have been unregistered by now");
9469 return NS_OK;
9472 if (!nsCRT::strcmp(aTopic, "memory-pressure")) {
9473 if (!AssumeAllFramesVisible() && mPresContext->IsRootContentDocument()) {
9474 DoUpdateApproximateFrameVisibility(/* aRemoveOnly = */ true);
9476 return NS_OK;
9479 if (!nsCRT::strcmp(aTopic, NS_WIDGET_WAKE_OBSERVER_TOPIC)) {
9480 mLastOSWake = TimeStamp::Now();
9481 return NS_OK;
9484 // For parent process, user may expect the UI is interactable after a
9485 // tab (previously opened page or home page) has restored.
9486 if (!nsCRT::strcmp(aTopic, "sessionstore-one-or-no-tab-restored")) {
9487 MOZ_ASSERT(XRE_IsParentProcess());
9488 sProcessInteractable = true;
9490 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
9491 if (os) {
9492 os->RemoveObserver(this, "sessionstore-one-or-no-tab-restored");
9494 return NS_OK;
9497 if (!nsCRT::strcmp(aTopic, "font-info-updated")) {
9498 mPresContext->ForceReflowForFontInfoUpdate();
9499 return NS_OK;
9502 if (!nsCRT::strcmp(aTopic, "look-and-feel-pref-changed")) {
9503 ThemeChanged();
9504 return NS_OK;
9507 NS_WARNING("unrecognized topic in PresShell::Observe");
9508 return NS_ERROR_FAILURE;
9511 bool PresShell::AddRefreshObserver(nsARefreshObserver* aObserver,
9512 FlushType aFlushType) {
9513 nsPresContext* presContext = GetPresContext();
9514 if (MOZ_UNLIKELY(!presContext)) {
9515 return false;
9517 presContext->RefreshDriver()->AddRefreshObserver(aObserver, aFlushType);
9518 return true;
9521 bool PresShell::RemoveRefreshObserver(nsARefreshObserver* aObserver,
9522 FlushType aFlushType) {
9523 nsPresContext* presContext = GetPresContext();
9524 return presContext && presContext->RefreshDriver()->RemoveRefreshObserver(
9525 aObserver, aFlushType);
9528 bool PresShell::AddPostRefreshObserver(nsAPostRefreshObserver* aObserver) {
9529 nsPresContext* presContext = GetPresContext();
9530 if (!presContext) {
9531 return false;
9533 presContext->RefreshDriver()->AddPostRefreshObserver(aObserver);
9534 return true;
9537 bool PresShell::RemovePostRefreshObserver(nsAPostRefreshObserver* aObserver) {
9538 nsPresContext* presContext = GetPresContext();
9539 if (!presContext) {
9540 return false;
9542 presContext->RefreshDriver()->RemovePostRefreshObserver(aObserver);
9543 return true;
9546 void PresShell::DoObserveStyleFlushes() {
9547 MOZ_ASSERT(!ObservingStyleFlushes());
9548 mObservingStyleFlushes = true;
9550 if (MOZ_LIKELY(!mDocument->GetBFCacheEntry())) {
9551 mPresContext->RefreshDriver()->AddStyleFlushObserver(this);
9555 void PresShell::DoObserveLayoutFlushes() {
9556 MOZ_ASSERT(!ObservingLayoutFlushes());
9557 mObservingLayoutFlushes = true;
9559 if (MOZ_LIKELY(!mDocument->GetBFCacheEntry())) {
9560 mPresContext->RefreshDriver()->AddLayoutFlushObserver(this);
9564 //------------------------------------------------------
9565 // End of protected and private methods on the PresShell
9566 //------------------------------------------------------
9568 //------------------------------------------------------------------
9569 //-- Delayed event Classes Impls
9570 //------------------------------------------------------------------
9572 PresShell::DelayedInputEvent::DelayedInputEvent()
9573 : DelayedEvent(), mEvent(nullptr) {}
9575 PresShell::DelayedInputEvent::~DelayedInputEvent() { delete mEvent; }
9577 void PresShell::DelayedInputEvent::Dispatch() {
9578 if (!mEvent || !mEvent->mWidget) {
9579 return;
9581 nsCOMPtr<nsIWidget> widget = mEvent->mWidget;
9582 nsEventStatus status;
9583 widget->DispatchEvent(mEvent, status);
9586 PresShell::DelayedMouseEvent::DelayedMouseEvent(WidgetMouseEvent* aEvent)
9587 : DelayedInputEvent() {
9588 MOZ_DIAGNOSTIC_ASSERT(aEvent->IsTrusted());
9589 WidgetMouseEvent* mouseEvent =
9590 new WidgetMouseEvent(true, aEvent->mMessage, aEvent->mWidget,
9591 aEvent->mReason, aEvent->mContextMenuTrigger);
9592 mouseEvent->AssignMouseEventData(*aEvent, false);
9593 mEvent = mouseEvent;
9596 PresShell::DelayedKeyEvent::DelayedKeyEvent(WidgetKeyboardEvent* aEvent)
9597 : DelayedInputEvent() {
9598 MOZ_DIAGNOSTIC_ASSERT(aEvent->IsTrusted());
9599 WidgetKeyboardEvent* keyEvent =
9600 new WidgetKeyboardEvent(true, aEvent->mMessage, aEvent->mWidget);
9601 keyEvent->AssignKeyEventData(*aEvent, false);
9602 keyEvent->mFlags.mIsSynthesizedForTests =
9603 aEvent->mFlags.mIsSynthesizedForTests;
9604 keyEvent->mFlags.mIsSuppressedOrDelayed = true;
9605 mEvent = keyEvent;
9608 bool PresShell::DelayedKeyEvent::IsKeyPressEvent() {
9609 return mEvent->mMessage == eKeyPress;
9612 // Start of DEBUG only code
9614 #ifdef DEBUG
9616 static void LogVerifyMessage(nsIFrame* k1, nsIFrame* k2, const char* aMsg) {
9617 nsAutoString n1, n2;
9618 if (k1) {
9619 k1->GetFrameName(n1);
9620 } else {
9621 n1.AssignLiteral(u"(null)");
9624 if (k2) {
9625 k2->GetFrameName(n2);
9626 } else {
9627 n2.AssignLiteral(u"(null)");
9630 printf("verifyreflow: %s %p != %s %p %s\n",
9631 NS_LossyConvertUTF16toASCII(n1).get(), (void*)k1,
9632 NS_LossyConvertUTF16toASCII(n2).get(), (void*)k2, aMsg);
9635 static void LogVerifyMessage(nsIFrame* k1, nsIFrame* k2, const char* aMsg,
9636 const nsRect& r1, const nsRect& r2) {
9637 printf("VerifyReflow Error:\n");
9638 nsAutoString name;
9640 if (k1) {
9641 k1->GetFrameName(name);
9642 printf(" %s %p ", NS_LossyConvertUTF16toASCII(name).get(), (void*)k1);
9644 printf("{%d, %d, %d, %d} != \n", r1.x, r1.y, r1.width, r1.height);
9646 if (k2) {
9647 k2->GetFrameName(name);
9648 printf(" %s %p ", NS_LossyConvertUTF16toASCII(name).get(), (void*)k2);
9650 printf("{%d, %d, %d, %d}\n %s\n", r2.x, r2.y, r2.width, r2.height, aMsg);
9653 static void LogVerifyMessage(nsIFrame* k1, nsIFrame* k2, const char* aMsg,
9654 const nsIntRect& r1, const nsIntRect& r2) {
9655 printf("VerifyReflow Error:\n");
9656 nsAutoString name;
9658 if (k1) {
9659 k1->GetFrameName(name);
9660 printf(" %s %p ", NS_LossyConvertUTF16toASCII(name).get(), (void*)k1);
9662 printf("{%d, %d, %d, %d} != \n", r1.x, r1.y, r1.width, r1.height);
9664 if (k2) {
9665 k2->GetFrameName(name);
9666 printf(" %s %p ", NS_LossyConvertUTF16toASCII(name).get(), (void*)k2);
9668 printf("{%d, %d, %d, %d}\n %s\n", r2.x, r2.y, r2.width, r2.height, aMsg);
9671 static bool CompareTrees(nsPresContext* aFirstPresContext,
9672 nsIFrame* aFirstFrame,
9673 nsPresContext* aSecondPresContext,
9674 nsIFrame* aSecondFrame) {
9675 if (!aFirstPresContext || !aFirstFrame || !aSecondPresContext ||
9676 !aSecondFrame)
9677 return true;
9678 // XXX Evil hack to reduce false positives; I can't seem to figure
9679 // out how to flush scrollbar changes correctly
9680 // if (aFirstFrame->IsScrollbarFrame())
9681 // return true;
9682 bool ok = true;
9683 nsIFrame::ChildListIterator lists1(aFirstFrame);
9684 nsIFrame::ChildListIterator lists2(aSecondFrame);
9685 do {
9686 const nsFrameList& kids1 =
9687 !lists1.IsDone() ? lists1.CurrentList() : nsFrameList();
9688 const nsFrameList& kids2 =
9689 !lists2.IsDone() ? lists2.CurrentList() : nsFrameList();
9690 int32_t l1 = kids1.GetLength();
9691 int32_t l2 = kids2.GetLength();
9692 if (l1 != l2) {
9693 ok = false;
9694 LogVerifyMessage(kids1.FirstChild(), kids2.FirstChild(),
9695 "child counts don't match: ");
9696 printf("%d != %d\n", l1, l2);
9697 if (!(VerifyReflowFlags::All & gVerifyReflowFlags)) {
9698 break;
9702 LayoutDeviceIntRect r1, r2;
9703 nsView* v1;
9704 nsView* v2;
9705 for (nsFrameList::Enumerator e1(kids1), e2(kids2);; e1.Next(), e2.Next()) {
9706 nsIFrame* k1 = e1.get();
9707 nsIFrame* k2 = e2.get();
9708 if (((nullptr == k1) && (nullptr != k2)) ||
9709 ((nullptr != k1) && (nullptr == k2))) {
9710 ok = false;
9711 LogVerifyMessage(k1, k2, "child lists are different\n");
9712 break;
9713 } else if (nullptr != k1) {
9714 // Verify that the frames are the same size
9715 if (!k1->GetRect().IsEqualInterior(k2->GetRect())) {
9716 ok = false;
9717 LogVerifyMessage(k1, k2, "(frame rects)", k1->GetRect(),
9718 k2->GetRect());
9721 // Make sure either both have views or neither have views; if they
9722 // do have views, make sure the views are the same size. If the
9723 // views have widgets, make sure they both do or neither does. If
9724 // they do, make sure the widgets are the same size.
9725 v1 = k1->GetView();
9726 v2 = k2->GetView();
9727 if (((nullptr == v1) && (nullptr != v2)) ||
9728 ((nullptr != v1) && (nullptr == v2))) {
9729 ok = false;
9730 LogVerifyMessage(k1, k2, "child views are not matched\n");
9731 } else if (nullptr != v1) {
9732 if (!v1->GetBounds().IsEqualInterior(v2->GetBounds())) {
9733 LogVerifyMessage(k1, k2, "(view rects)", v1->GetBounds(),
9734 v2->GetBounds());
9737 nsIWidget* w1 = v1->GetWidget();
9738 nsIWidget* w2 = v2->GetWidget();
9739 if (((nullptr == w1) && (nullptr != w2)) ||
9740 ((nullptr != w1) && (nullptr == w2))) {
9741 ok = false;
9742 LogVerifyMessage(k1, k2, "child widgets are not matched\n");
9743 } else if (nullptr != w1) {
9744 r1 = w1->GetBounds();
9745 r2 = w2->GetBounds();
9746 if (!r1.IsEqualEdges(r2)) {
9747 LogVerifyMessage(k1, k2, "(widget rects)", r1.ToUnknownRect(),
9748 r2.ToUnknownRect());
9752 if (!ok && !(VerifyReflowFlags::All & gVerifyReflowFlags)) {
9753 break;
9756 // XXX Should perhaps compare their float managers.
9758 // Compare the sub-trees too
9759 if (!CompareTrees(aFirstPresContext, k1, aSecondPresContext, k2)) {
9760 ok = false;
9761 if (!(VerifyReflowFlags::All & gVerifyReflowFlags)) {
9762 break;
9765 } else {
9766 break;
9769 if (!ok && (!(VerifyReflowFlags::All & gVerifyReflowFlags))) {
9770 break;
9773 lists1.Next();
9774 lists2.Next();
9775 if (lists1.IsDone() != lists2.IsDone() ||
9776 (!lists1.IsDone() && lists1.CurrentID() != lists2.CurrentID())) {
9777 if (!(VerifyReflowFlags::All & gVerifyReflowFlags)) {
9778 ok = false;
9780 LogVerifyMessage(kids1.FirstChild(), kids2.FirstChild(),
9781 "child list names are not matched: ");
9782 fprintf(
9783 stdout, "%s != %s\n",
9784 !lists1.IsDone() ? mozilla::layout::ChildListName(lists1.CurrentID())
9785 : "(null)",
9786 !lists2.IsDone() ? mozilla::layout::ChildListName(lists2.CurrentID())
9787 : "(null)");
9788 break;
9790 } while (ok && !lists1.IsDone());
9792 return ok;
9794 #endif
9796 #if 0
9797 static nsIFrame*
9798 FindTopFrame(nsIFrame* aRoot)
9800 if (aRoot) {
9801 nsIContent* content = aRoot->GetContent();
9802 if (content) {
9803 nsAtom* tag;
9804 content->GetTag(tag);
9805 if (nullptr != tag) {
9806 NS_RELEASE(tag);
9807 return aRoot;
9811 // Try one of the children
9812 for (nsIFrame* kid : aRoot->PrincipalChildList()) {
9813 nsIFrame* result = FindTopFrame(kid);
9814 if (nullptr != result) {
9815 return result;
9819 return nullptr;
9821 #endif
9823 #ifdef DEBUG
9825 // After an incremental reflow, we verify the correctness by doing a
9826 // full reflow into a fresh frame tree.
9827 bool PresShell::VerifyIncrementalReflow() {
9828 if (VerifyReflowFlags::Noisy & gVerifyReflowFlags) {
9829 printf("Building Verification Tree...\n");
9832 // Create a presentation context to view the new frame tree
9833 RefPtr<nsPresContext> cx = new nsRootPresContext(
9834 mDocument, mPresContext->IsPaginated()
9835 ? nsPresContext::eContext_PrintPreview
9836 : nsPresContext::eContext_Galley);
9837 NS_ENSURE_TRUE(cx, false);
9839 nsDeviceContext* dc = mPresContext->DeviceContext();
9840 nsresult rv = cx->Init(dc);
9841 NS_ENSURE_SUCCESS(rv, false);
9843 // Get our scrolling preference
9844 nsView* rootView = mViewManager->GetRootView();
9845 NS_ENSURE_TRUE(rootView->HasWidget(), false);
9846 nsIWidget* parentWidget = rootView->GetWidget();
9848 // Create a new view manager.
9849 RefPtr<nsViewManager> vm = new nsViewManager();
9850 NS_ENSURE_TRUE(vm, false);
9851 rv = vm->Init(dc);
9852 NS_ENSURE_SUCCESS(rv, false);
9854 // Create a child window of the parent that is our "root view/window"
9855 // Create a view
9856 nsRect tbounds = mPresContext->GetVisibleArea();
9857 nsView* view = vm->CreateView(tbounds, nullptr);
9858 NS_ENSURE_TRUE(view, false);
9860 // now create the widget for the view
9861 rv = view->CreateWidgetForParent(parentWidget, nullptr, true);
9862 NS_ENSURE_SUCCESS(rv, false);
9864 // Setup hierarchical relationship in view manager
9865 vm->SetRootView(view);
9867 // Make the new presentation context the same size as our
9868 // presentation context.
9869 cx->SetVisibleArea(mPresContext->GetVisibleArea());
9871 RefPtr<PresShell> presShell = mDocument->CreatePresShell(cx, vm);
9872 NS_ENSURE_TRUE(presShell, false);
9874 // Note that after we create the shell, we must make sure to destroy it
9875 presShell->SetVerifyReflowEnable(
9876 false); // turn off verify reflow while we're
9877 // reflowing the test frame tree
9878 vm->SetPresShell(presShell);
9880 nsAutoCauseReflowNotifier crNotifier(this);
9881 presShell->Initialize();
9883 mDocument->BindingManager()->ProcessAttachedQueue();
9884 presShell->FlushPendingNotifications(FlushType::Layout);
9885 presShell->SetVerifyReflowEnable(
9886 true); // turn on verify reflow again now that
9887 // we're done reflowing the test frame tree
9888 // Force the non-primary presshell to unsuppress; it doesn't want to normally
9889 // because it thinks it's hidden
9890 presShell->mPaintingSuppressed = false;
9891 if (VerifyReflowFlags::Noisy & gVerifyReflowFlags) {
9892 printf("Verification Tree built, comparing...\n");
9895 // Now that the document has been reflowed, use its frame tree to
9896 // compare against our frame tree.
9897 nsIFrame* root1 = mFrameConstructor->GetRootFrame();
9898 nsIFrame* root2 = presShell->GetRootFrame();
9899 bool ok = CompareTrees(mPresContext, root1, cx, root2);
9900 if (!ok && (VerifyReflowFlags::Noisy & gVerifyReflowFlags)) {
9901 printf("Verify reflow failed, primary tree:\n");
9902 root1->List(stdout);
9903 printf("Verification tree:\n");
9904 root2->List(stdout);
9907 # if 0
9908 // Sample code for dumping page to png
9909 // XXX Needs to be made more flexible
9910 if (!ok) {
9911 nsString stra;
9912 static int num = 0;
9913 stra.AppendLiteral("C:\\mozilla\\mozilla\\debug\\filea");
9914 stra.AppendInt(num);
9915 stra.AppendLiteral(".png");
9916 gfxUtils::WriteAsPNG(presShell, stra);
9917 nsString strb;
9918 strb.AppendLiteral("C:\\mozilla\\mozilla\\debug\\fileb");
9919 strb.AppendInt(num);
9920 strb.AppendLiteral(".png");
9921 gfxUtils::WriteAsPNG(presShell, strb);
9922 ++num;
9924 # endif
9926 presShell->EndObservingDocument();
9927 presShell->Destroy();
9928 if (VerifyReflowFlags::Noisy & gVerifyReflowFlags) {
9929 printf("Finished Verifying Reflow...\n");
9932 return ok;
9935 // Layout debugging hooks
9936 void PresShell::ListComputedStyles(FILE* out, int32_t aIndent) {
9937 nsIFrame* rootFrame = GetRootFrame();
9938 if (rootFrame) {
9939 rootFrame->Style()->List(out, aIndent);
9942 // The root element's frame's ComputedStyle is the root of a separate tree.
9943 Element* rootElement = mDocument->GetRootElement();
9944 if (rootElement) {
9945 nsIFrame* rootElementFrame = rootElement->GetPrimaryFrame();
9946 if (rootElementFrame) {
9947 rootElementFrame->Style()->List(out, aIndent);
9952 void PresShell::ListStyleSheets(FILE* out, int32_t aIndent) {
9953 int32_t sheetCount = StyleSet()->SheetCount(StyleOrigin::Author);
9954 for (int32_t i = 0; i < sheetCount; ++i) {
9955 StyleSet()->SheetAt(StyleOrigin::Author, i)->List(out, aIndent);
9956 fputs("\n", out);
9959 #endif
9961 //=============================================================
9962 //=============================================================
9963 //-- Debug Reflow Counts
9964 //=============================================================
9965 //=============================================================
9966 #ifdef MOZ_REFLOW_PERF
9967 //-------------------------------------------------------------
9968 void PresShell::DumpReflows() {
9969 if (mReflowCountMgr) {
9970 nsAutoCString uriStr;
9971 if (mDocument) {
9972 nsIURI* uri = mDocument->GetDocumentURI();
9973 if (uri) {
9974 uri->GetPathQueryRef(uriStr);
9977 mReflowCountMgr->DisplayTotals(uriStr.get());
9978 mReflowCountMgr->DisplayHTMLTotals(uriStr.get());
9979 mReflowCountMgr->DisplayDiffsInTotals();
9983 //-------------------------------------------------------------
9984 void PresShell::CountReflows(const char* aName, nsIFrame* aFrame) {
9985 if (mReflowCountMgr) {
9986 mReflowCountMgr->Add(aName, aFrame);
9990 //-------------------------------------------------------------
9991 void PresShell::PaintCount(const char* aName, gfxContext* aRenderingContext,
9992 nsPresContext* aPresContext, nsIFrame* aFrame,
9993 const nsPoint& aOffset, uint32_t aColor) {
9994 if (mReflowCountMgr) {
9995 mReflowCountMgr->PaintCount(aName, aRenderingContext, aPresContext, aFrame,
9996 aOffset, aColor);
10000 //-------------------------------------------------------------
10001 void PresShell::SetPaintFrameCount(bool aPaintFrameCounts) {
10002 if (mReflowCountMgr) {
10003 mReflowCountMgr->SetPaintFrameCounts(aPaintFrameCounts);
10007 bool PresShell::IsPaintingFrameCounts() {
10008 if (mReflowCountMgr) return mReflowCountMgr->IsPaintingFrameCounts();
10009 return false;
10012 //------------------------------------------------------------------
10013 //-- Reflow Counter Classes Impls
10014 //------------------------------------------------------------------
10016 //------------------------------------------------------------------
10017 ReflowCounter::ReflowCounter(ReflowCountMgr* aMgr) : mMgr(aMgr) {
10018 ClearTotals();
10019 SetTotalsCache();
10022 //------------------------------------------------------------------
10023 ReflowCounter::~ReflowCounter() {}
10025 //------------------------------------------------------------------
10026 void ReflowCounter::ClearTotals() { mTotal = 0; }
10028 //------------------------------------------------------------------
10029 void ReflowCounter::SetTotalsCache() { mCacheTotal = mTotal; }
10031 //------------------------------------------------------------------
10032 void ReflowCounter::CalcDiffInTotals() { mCacheTotal = mTotal - mCacheTotal; }
10034 //------------------------------------------------------------------
10035 void ReflowCounter::DisplayTotals(const char* aStr) {
10036 DisplayTotals(mTotal, aStr ? aStr : "Totals");
10039 //------------------------------------------------------------------
10040 void ReflowCounter::DisplayDiffTotals(const char* aStr) {
10041 DisplayTotals(mCacheTotal, aStr ? aStr : "Diff Totals");
10044 //------------------------------------------------------------------
10045 void ReflowCounter::DisplayHTMLTotals(const char* aStr) {
10046 DisplayHTMLTotals(mTotal, aStr ? aStr : "Totals");
10049 //------------------------------------------------------------------
10050 void ReflowCounter::DisplayTotals(uint32_t aTotal, const char* aTitle) {
10051 // figure total
10052 if (aTotal == 0) {
10053 return;
10055 ReflowCounter* gTots = (ReflowCounter*)mMgr->LookUp(kGrandTotalsStr);
10057 printf("%25s\t", aTitle);
10058 printf("%d\t", aTotal);
10059 if (gTots != this && aTotal > 0) {
10060 gTots->Add(aTotal);
10064 //------------------------------------------------------------------
10065 void ReflowCounter::DisplayHTMLTotals(uint32_t aTotal, const char* aTitle) {
10066 if (aTotal == 0) {
10067 return;
10070 ReflowCounter* gTots = (ReflowCounter*)mMgr->LookUp(kGrandTotalsStr);
10071 FILE* fd = mMgr->GetOutFile();
10072 if (!fd) {
10073 return;
10076 fprintf(fd, "<tr><td><center>%s</center></td>", aTitle);
10077 fprintf(fd, "<td><center>%d</center></td></tr>\n", aTotal);
10079 if (gTots != this && aTotal > 0) {
10080 gTots->Add(aTotal);
10084 //------------------------------------------------------------------
10085 //-- ReflowCountMgr
10086 //------------------------------------------------------------------
10088 # define KEY_BUF_SIZE_FOR_PTR \
10089 24 // adequate char[] buffer to sprintf a pointer
10091 ReflowCountMgr::ReflowCountMgr() : mCounts(10), mIndiFrameCounts(10) {
10092 mCycledOnce = false;
10093 mDumpFrameCounts = false;
10094 mDumpFrameByFrameCounts = false;
10095 mPaintFrameByFrameCounts = false;
10098 //------------------------------------------------------------------
10099 ReflowCountMgr::~ReflowCountMgr() {}
10101 //------------------------------------------------------------------
10102 ReflowCounter* ReflowCountMgr::LookUp(const char* aName) {
10103 return mCounts.Get(aName);
10106 //------------------------------------------------------------------
10107 void ReflowCountMgr::Add(const char* aName, nsIFrame* aFrame) {
10108 NS_ASSERTION(aName != nullptr, "Name shouldn't be null!");
10110 if (mDumpFrameCounts) {
10111 ReflowCounter* counter = mCounts.LookupForAdd(aName).OrInsert(
10112 [this]() { return new ReflowCounter(this); });
10113 counter->Add();
10116 if ((mDumpFrameByFrameCounts || mPaintFrameByFrameCounts) &&
10117 aFrame != nullptr) {
10118 char key[KEY_BUF_SIZE_FOR_PTR];
10119 SprintfLiteral(key, "%p", (void*)aFrame);
10120 IndiReflowCounter* counter =
10121 mIndiFrameCounts.LookupForAdd(key).OrInsert([&aName, &aFrame, this]() {
10122 auto counter = new IndiReflowCounter(this);
10123 counter->mFrame = aFrame;
10124 counter->mName.AssignASCII(aName);
10125 return counter;
10127 // this eliminates extra counts from super classes
10128 if (counter != nullptr && counter->mName.EqualsASCII(aName)) {
10129 counter->mCount++;
10130 counter->mCounter.Add(1);
10135 //------------------------------------------------------------------
10136 void ReflowCountMgr::PaintCount(const char* aName,
10137 gfxContext* aRenderingContext,
10138 nsPresContext* aPresContext, nsIFrame* aFrame,
10139 const nsPoint& aOffset, uint32_t aColor) {
10140 if (mPaintFrameByFrameCounts && aFrame != nullptr) {
10141 char key[KEY_BUF_SIZE_FOR_PTR];
10142 SprintfLiteral(key, "%p", (void*)aFrame);
10143 IndiReflowCounter* counter = mIndiFrameCounts.Get(key);
10144 if (counter != nullptr && counter->mName.EqualsASCII(aName)) {
10145 DrawTarget* drawTarget = aRenderingContext->GetDrawTarget();
10146 int32_t appUnitsPerDevPixel = aPresContext->AppUnitsPerDevPixel();
10148 aRenderingContext->Save();
10149 gfxPoint devPixelOffset =
10150 nsLayoutUtils::PointToGfxPoint(aOffset, appUnitsPerDevPixel);
10151 aRenderingContext->SetMatrixDouble(
10152 aRenderingContext->CurrentMatrixDouble().PreTranslate(
10153 devPixelOffset));
10155 // We don't care about the document language or user fonts here;
10156 // just get a default Latin font.
10157 nsFont font(StyleGenericFontFamily::Serif,
10158 nsPresContext::CSSPixelsToAppUnits(11));
10159 nsFontMetrics::Params params;
10160 params.language = nsGkAtoms::x_western;
10161 params.textPerf = aPresContext->GetTextPerfMetrics();
10162 params.featureValueLookup = aPresContext->GetFontFeatureValuesLookup();
10163 RefPtr<nsFontMetrics> fm =
10164 aPresContext->DeviceContext()->GetMetricsFor(font, params);
10166 char buf[16];
10167 int len = SprintfLiteral(buf, "%d", counter->mCount);
10168 nscoord x = 0, y = fm->MaxAscent();
10169 nscoord width, height = fm->MaxHeight();
10170 fm->SetTextRunRTL(false);
10171 width = fm->GetWidth(buf, len, drawTarget);
10173 Color color;
10174 Color color2;
10175 if (aColor != 0) {
10176 color = Color::FromABGR(aColor);
10177 color2 = Color(0.f, 0.f, 0.f);
10178 } else {
10179 gfx::Float rc = 0.f, gc = 0.f, bc = 0.f;
10180 if (counter->mCount < 5) {
10181 rc = 1.f;
10182 gc = 1.f;
10183 } else if (counter->mCount < 11) {
10184 gc = 1.f;
10185 } else {
10186 rc = 1.f;
10188 color = Color(rc, gc, bc);
10189 color2 = Color(rc / 2, gc / 2, bc / 2);
10192 nsRect rect(0, 0, width + 15, height + 15);
10193 Rect devPxRect =
10194 NSRectToSnappedRect(rect, appUnitsPerDevPixel, *drawTarget);
10195 ColorPattern black(ToDeviceColor(Color(0.f, 0.f, 0.f, 1.f)));
10196 drawTarget->FillRect(devPxRect, black);
10198 aRenderingContext->SetColor(color2);
10199 fm->DrawString(buf, len, x + 15, y + 15, aRenderingContext);
10200 aRenderingContext->SetColor(color);
10201 fm->DrawString(buf, len, x, y, aRenderingContext);
10203 aRenderingContext->Restore();
10208 //------------------------------------------------------------------
10209 void ReflowCountMgr::DoGrandTotals() {
10210 auto entry = mCounts.LookupForAdd(kGrandTotalsStr);
10211 if (!entry) {
10212 entry.OrInsert([this]() { return new ReflowCounter(this); });
10213 } else {
10214 entry.Data()->ClearTotals();
10217 printf("\t\t\t\tTotal\n");
10218 for (uint32_t i = 0; i < 78; i++) {
10219 printf("-");
10221 printf("\n");
10222 for (auto iter = mCounts.Iter(); !iter.Done(); iter.Next()) {
10223 iter.Data()->DisplayTotals(iter.Key());
10227 static void RecurseIndiTotals(
10228 nsPresContext* aPresContext,
10229 nsClassHashtable<nsCharPtrHashKey, IndiReflowCounter>& aHT,
10230 nsIFrame* aParentFrame, int32_t aLevel) {
10231 if (aParentFrame == nullptr) {
10232 return;
10235 char key[KEY_BUF_SIZE_FOR_PTR];
10236 SprintfLiteral(key, "%p", (void*)aParentFrame);
10237 IndiReflowCounter* counter = aHT.Get(key);
10238 if (counter) {
10239 counter->mHasBeenOutput = true;
10240 char* name = ToNewCString(counter->mName);
10241 for (int32_t i = 0; i < aLevel; i++) printf(" ");
10242 printf("%s - %p [%d][", name, (void*)aParentFrame, counter->mCount);
10243 printf("%d", counter->mCounter.GetTotal());
10244 printf("]\n");
10245 free(name);
10248 for (nsIFrame* child : aParentFrame->PrincipalChildList()) {
10249 RecurseIndiTotals(aPresContext, aHT, child, aLevel + 1);
10253 //------------------------------------------------------------------
10254 void ReflowCountMgr::DoIndiTotalsTree() {
10255 printf("\n------------------------------------------------\n");
10256 printf("-- Individual Frame Counts\n");
10257 printf("------------------------------------------------\n");
10259 if (mPresShell) {
10260 nsIFrame* rootFrame = mPresShell->GetRootFrame();
10261 RecurseIndiTotals(mPresContext, mIndiFrameCounts, rootFrame, 0);
10262 printf("------------------------------------------------\n");
10263 printf("-- Individual Counts of Frames not in Root Tree\n");
10264 printf("------------------------------------------------\n");
10265 for (auto iter = mIndiFrameCounts.Iter(); !iter.Done(); iter.Next()) {
10266 IndiReflowCounter* counter = iter.Data();
10267 if (!counter->mHasBeenOutput) {
10268 char* name = ToNewCString(counter->mName);
10269 printf("%s - %p [%d][", name, (void*)counter->mFrame,
10270 counter->mCount);
10271 printf("%d", counter->mCounter.GetTotal());
10272 printf("]\n");
10273 free(name);
10279 //------------------------------------------------------------------
10280 void ReflowCountMgr::DoGrandHTMLTotals() {
10281 auto entry = mCounts.LookupForAdd(kGrandTotalsStr);
10282 if (!entry) {
10283 entry.OrInsert([this]() { return new ReflowCounter(this); });
10284 } else {
10285 entry.Data()->ClearTotals();
10288 static const char* title[] = {"Class", "Reflows"};
10289 fprintf(mFD, "<tr>");
10290 for (uint32_t i = 0; i < ArrayLength(title); i++) {
10291 fprintf(mFD, "<td><center><b>%s<b></center></td>", title[i]);
10293 fprintf(mFD, "</tr>\n");
10295 for (auto iter = mCounts.Iter(); !iter.Done(); iter.Next()) {
10296 iter.Data()->DisplayHTMLTotals(iter.Key());
10300 //------------------------------------
10301 void ReflowCountMgr::DisplayTotals(const char* aStr) {
10302 # ifdef DEBUG_rods
10303 printf("%s\n", aStr ? aStr : "No name");
10304 # endif
10305 if (mDumpFrameCounts) {
10306 DoGrandTotals();
10308 if (mDumpFrameByFrameCounts) {
10309 DoIndiTotalsTree();
10312 //------------------------------------
10313 void ReflowCountMgr::DisplayHTMLTotals(const char* aStr) {
10314 # ifdef WIN32x // XXX NOT XP!
10315 char name[1024];
10317 char* sptr = strrchr(aStr, '/');
10318 if (sptr) {
10319 sptr++;
10320 strcpy(name, sptr);
10321 char* eptr = strrchr(name, '.');
10322 if (eptr) {
10323 *eptr = 0;
10325 strcat(name, "_stats.html");
10327 mFD = fopen(name, "w");
10328 if (mFD) {
10329 fprintf(mFD, "<html><head><title>Reflow Stats</title></head><body>\n");
10330 const char* title = aStr ? aStr : "No name";
10331 fprintf(mFD,
10332 "<center><b>%s</b><br><table border=1 "
10333 "style=\"background-color:#e0e0e0\">",
10334 title);
10335 DoGrandHTMLTotals();
10336 fprintf(mFD, "</center></table>\n");
10337 fprintf(mFD, "</body></html>\n");
10338 fclose(mFD);
10339 mFD = nullptr;
10341 # endif // not XP!
10344 //------------------------------------------------------------------
10345 void ReflowCountMgr::ClearTotals() {
10346 for (auto iter = mCounts.Iter(); !iter.Done(); iter.Next()) {
10347 iter.Data()->ClearTotals();
10351 //------------------------------------------------------------------
10352 void ReflowCountMgr::ClearGrandTotals() {
10353 auto entry = mCounts.LookupForAdd(kGrandTotalsStr);
10354 if (!entry) {
10355 entry.OrInsert([this]() { return new ReflowCounter(this); });
10356 } else {
10357 entry.Data()->ClearTotals();
10358 entry.Data()->SetTotalsCache();
10362 //------------------------------------------------------------------
10363 void ReflowCountMgr::DisplayDiffsInTotals() {
10364 if (mCycledOnce) {
10365 printf("Differences\n");
10366 for (int32_t i = 0; i < 78; i++) {
10367 printf("-");
10369 printf("\n");
10370 ClearGrandTotals();
10373 for (auto iter = mCounts.Iter(); !iter.Done(); iter.Next()) {
10374 if (mCycledOnce) {
10375 iter.Data()->CalcDiffInTotals();
10376 iter.Data()->DisplayDiffTotals(iter.Key());
10378 iter.Data()->SetTotalsCache();
10381 mCycledOnce = true;
10384 #endif // MOZ_REFLOW_PERF
10386 nsIFrame* PresShell::GetAbsoluteContainingBlock(nsIFrame* aFrame) {
10387 return FrameConstructor()->GetAbsoluteContainingBlock(
10388 aFrame, nsCSSFrameConstructor::ABS_POS);
10391 #ifdef ACCESSIBILITY
10393 // static
10394 bool PresShell::IsAccessibilityActive() { return GetAccService() != nullptr; }
10396 // static
10397 nsAccessibilityService* PresShell::GetAccessibilityService() {
10398 return GetAccService();
10401 #endif // #ifdef ACCESSIBILITY
10403 // Asks our docshell whether we're active.
10404 void PresShell::QueryIsActive() {
10405 nsCOMPtr<nsISupports> container = mPresContext->GetContainerWeak();
10406 if (mDocument) {
10407 Document* displayDoc = mDocument->GetDisplayDocument();
10408 if (displayDoc) {
10409 // Ok, we're an external resource document -- we need to use our display
10410 // document's docshell to determine "IsActive" status, since we lack
10411 // a container.
10412 MOZ_ASSERT(!container,
10413 "external resource doc shouldn't have its own container");
10415 nsPresContext* displayPresContext = displayDoc->GetPresContext();
10416 if (displayPresContext) {
10417 container = displayPresContext->GetContainerWeak();
10422 nsCOMPtr<nsIDocShell> docshell(do_QueryInterface(container));
10423 if (docshell) {
10424 bool isActive;
10425 nsresult rv = docshell->GetIsActive(&isActive);
10426 // Even though in theory the docshell here could be "Inactive and
10427 // Foreground", thus implying aIsHidden=false for SetIsActive(),
10428 // this is a newly created PresShell so we'd like to invalidate anyway
10429 // upon being made active to ensure that the contents get painted.
10430 if (NS_SUCCEEDED(rv)) SetIsActive(isActive);
10434 // Helper for propagating mIsActive changes to external resources
10435 static bool SetExternalResourceIsActive(Document* aDocument, void* aClosure) {
10436 PresShell* presShell = aDocument->GetPresShell();
10437 if (presShell) {
10438 presShell->SetIsActive(*static_cast<bool*>(aClosure));
10440 return true;
10443 static void SetPluginIsActive(nsISupports* aSupports, void* aClosure) {
10444 nsCOMPtr<nsIContent> content(do_QueryInterface(aSupports));
10445 if (!content) {
10446 return;
10449 nsIFrame* frame = content->GetPrimaryFrame();
10450 nsIObjectFrame* objectFrame = do_QueryFrame(frame);
10451 if (objectFrame) {
10452 objectFrame->SetIsDocumentActive(*static_cast<bool*>(aClosure));
10456 nsresult PresShell::SetIsActive(bool aIsActive) {
10457 MOZ_ASSERT(mDocument, "should only be called with a document");
10459 mIsActive = aIsActive;
10461 nsPresContext* presContext = GetPresContext();
10462 if (presContext &&
10463 presContext->RefreshDriver()->GetPresContext() == presContext) {
10464 presContext->RefreshDriver()->SetThrottled(!mIsActive);
10467 // Propagate state-change to my resource documents' PresShells
10468 mDocument->EnumerateExternalResources(SetExternalResourceIsActive,
10469 &aIsActive);
10470 mDocument->EnumerateActivityObservers(SetPluginIsActive, &aIsActive);
10471 nsresult rv = UpdateImageLockingState();
10472 #ifdef ACCESSIBILITY
10473 if (aIsActive) {
10474 if (nsAccessibilityService* accService =
10475 PresShell::GetAccessibilityService()) {
10476 accService->PresShellActivated(this);
10479 #endif // #ifdef ACCESSIBILITY
10480 return rv;
10483 RefPtr<MobileViewportManager> PresShell::GetMobileViewportManager() const {
10484 return mMobileViewportManager;
10487 void PresShell::UpdateViewportOverridden(bool aAfterInitialization) {
10488 // Determine if we require a MobileViewportManager. We need one any
10489 // time we allow resolution zooming for a document, and any time we
10490 // want to obey <meta name="viewport"> tags for it.
10491 bool needMVM = nsLayoutUtils::ShouldHandleMetaViewport(mDocument) ||
10492 nsLayoutUtils::AllowZoomingForDocument(mDocument);
10494 if (needMVM == !!mMobileViewportManager) {
10495 // Either we've need one and we've already got it, or we don't need one
10496 // and don't have it. Either way, we're done.
10497 return;
10500 if (needMVM) {
10501 if (mPresContext->IsRootContentDocumentCrossProcess()) {
10502 mMVMContext = new GeckoMVMContext(mDocument, this);
10503 mMobileViewportManager = new MobileViewportManager(mMVMContext);
10505 if (aAfterInitialization) {
10506 // Setting the initial viewport will trigger a reflow.
10507 mMobileViewportManager->SetInitialViewport();
10510 return;
10513 MOZ_ASSERT(mMobileViewportManager,
10514 "Shouldn't reach this without a MobileViewportManager.");
10515 // Before we get rid of our MVM, ask it to update the viewport while
10516 // forcing resolution, which will undo any scaling it might have imposed.
10517 // To do this correctly, we need to first null out mMobileViewportManager,
10518 // because during reflow we will check PresShell::GetIsViewportOverriden(),
10519 // which uses that value as a signifier.
10520 RefPtr<MobileViewportManager> oldMVM;
10521 mMobileViewportManager.swap(oldMVM);
10523 oldMVM->RequestReflow(true);
10524 ResetVisualViewportSize();
10526 oldMVM->Destroy();
10527 oldMVM = nullptr;
10528 mMVMContext = nullptr;
10530 if (aAfterInitialization) {
10531 // Force a reflow to our correct size by going back to the docShell
10532 // and asking it to reassert its size. This is necessary because
10533 // everything underneath the docShell, like the ViewManager, has been
10534 // altered by the MobileViewportManager in an irreversible way.
10535 nsDocShell* docShell =
10536 static_cast<nsDocShell*>(GetPresContext()->GetDocShell());
10537 int32_t width, height;
10538 docShell->GetSize(&width, &height);
10539 docShell->SetSize(width, height, false);
10543 bool PresShell::UsesMobileViewportSizing() const {
10544 return GetIsViewportOverridden() &&
10545 nsLayoutUtils::ShouldHandleMetaViewport(mDocument);
10549 * Determines the current image locking state. Called when one of the
10550 * dependent factors changes.
10552 nsresult PresShell::UpdateImageLockingState() {
10553 // We're locked if we're both thawed and active.
10554 bool locked = !mFrozen && mIsActive;
10556 nsresult rv = mDocument->ImageTracker()->SetLockingState(locked);
10558 if (locked) {
10559 // Request decodes for visible image frames; we want to start decoding as
10560 // quickly as possible when we get foregrounded to minimize flashing.
10561 for (auto iter = mApproximatelyVisibleFrames.Iter(); !iter.Done();
10562 iter.Next()) {
10563 nsImageFrame* imageFrame = do_QueryFrame(iter.Get()->GetKey());
10564 if (imageFrame) {
10565 imageFrame->MaybeDecodeForPredictedSize();
10570 return rv;
10573 PresShell* PresShell::GetRootPresShell() {
10574 if (mPresContext) {
10575 nsPresContext* rootPresContext = mPresContext->GetRootPresContext();
10576 if (rootPresContext) {
10577 return rootPresContext->PresShell();
10580 return nullptr;
10583 void PresShell::AddSizeOfIncludingThis(nsWindowSizes& aSizes) const {
10584 MallocSizeOf mallocSizeOf = aSizes.mState.mMallocSizeOf;
10585 mFrameArena.AddSizeOfExcludingThis(aSizes, Arena::ArenaKind::PresShell);
10586 aSizes.mLayoutPresShellSize += mallocSizeOf(this);
10587 if (mCaret) {
10588 aSizes.mLayoutPresShellSize += mCaret->SizeOfIncludingThis(mallocSizeOf);
10590 aSizes.mLayoutPresShellSize +=
10591 mApproximatelyVisibleFrames.ShallowSizeOfExcludingThis(mallocSizeOf) +
10592 mFramesToDirty.ShallowSizeOfExcludingThis(mallocSizeOf) +
10593 mPendingScrollAnchorSelection.ShallowSizeOfExcludingThis(mallocSizeOf) +
10594 mPendingScrollAnchorAdjustment.ShallowSizeOfExcludingThis(mallocSizeOf);
10596 aSizes.mLayoutTextRunsSize += SizeOfTextRuns(mallocSizeOf);
10598 aSizes.mLayoutPresContextSize +=
10599 mPresContext->SizeOfIncludingThis(mallocSizeOf);
10601 mFrameConstructor->AddSizeOfIncludingThis(aSizes);
10604 size_t PresShell::SizeOfTextRuns(MallocSizeOf aMallocSizeOf) const {
10605 nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
10606 if (!rootFrame) {
10607 return 0;
10610 // clear the TEXT_RUN_MEMORY_ACCOUNTED flags
10611 nsLayoutUtils::SizeOfTextRunsForFrames(rootFrame, nullptr,
10612 /* clear = */ true);
10614 // collect the total memory in use for textruns
10615 return nsLayoutUtils::SizeOfTextRunsForFrames(rootFrame, aMallocSizeOf,
10616 /* clear = */ false);
10619 void PresShell::MarkFixedFramesForReflow(IntrinsicDirty aIntrinsicDirty) {
10620 nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
10621 if (rootFrame) {
10622 const nsFrameList& childList =
10623 rootFrame->GetChildList(nsIFrame::kFixedList);
10624 for (nsIFrame* childFrame : childList) {
10625 FrameNeedsReflow(childFrame, aIntrinsicDirty, NS_FRAME_IS_DIRTY);
10630 void PresShell::CompleteChangeToVisualViewportSize() {
10631 if (nsIScrollableFrame* rootScrollFrame = GetRootScrollFrameAsScrollable()) {
10632 rootScrollFrame->MarkScrollbarsDirtyForReflow();
10634 MarkFixedFramesForReflow(IntrinsicDirty::Resize);
10636 if (auto* window = nsGlobalWindowInner::Cast(mDocument->GetInnerWindow())) {
10637 window->VisualViewport()->PostResizeEvent();
10640 if (nsIScrollableFrame* rootScrollFrame = GetRootScrollFrameAsScrollable()) {
10641 ScrollAnchorContainer* container = rootScrollFrame->Anchor();
10642 container->UserScrolled();
10646 void PresShell::SetVisualViewportSize(nscoord aWidth, nscoord aHeight) {
10647 if (!mVisualViewportSizeSet || mVisualViewportSize.width != aWidth ||
10648 mVisualViewportSize.height != aHeight) {
10649 mVisualViewportSizeSet = true;
10650 mVisualViewportSize.width = aWidth;
10651 mVisualViewportSize.height = aHeight;
10653 CompleteChangeToVisualViewportSize();
10657 void PresShell::ResetVisualViewportSize() {
10658 if (mVisualViewportSizeSet) {
10659 mVisualViewportSizeSet = false;
10660 mVisualViewportSize.width = 0;
10661 mVisualViewportSize.height = 0;
10663 CompleteChangeToVisualViewportSize();
10667 bool PresShell::SetVisualViewportOffset(const nsPoint& aScrollOffset,
10668 const nsPoint& aPrevLayoutScrollPos) {
10669 bool didChange = false;
10670 if (GetVisualViewportOffset() != aScrollOffset) {
10671 nsPoint prevOffset = GetVisualViewportOffset();
10672 mVisualViewportOffset = Some(aScrollOffset);
10673 didChange = true;
10675 if (auto* window = nsGlobalWindowInner::Cast(mDocument->GetInnerWindow())) {
10676 window->VisualViewport()->PostScrollEvent(prevOffset,
10677 aPrevLayoutScrollPos);
10680 if (nsIScrollableFrame* rootScrollFrame =
10681 GetRootScrollFrameAsScrollable()) {
10682 ScrollAnchorContainer* container = rootScrollFrame->Anchor();
10683 container->UserScrolled();
10686 return didChange;
10689 void PresShell::ScrollToVisual(const nsPoint& aVisualViewportOffset,
10690 FrameMetrics::ScrollOffsetUpdateType aUpdateType,
10691 ScrollMode aMode) {
10692 MOZ_ASSERT(aMode == ScrollMode::Instant || aMode == ScrollMode::SmoothMsd);
10694 if (aMode == ScrollMode::SmoothMsd) {
10695 if (nsIScrollableFrame* sf = GetRootScrollFrameAsScrollable()) {
10696 if (sf->SmoothScrollVisual(aVisualViewportOffset, aUpdateType)) {
10697 return;
10702 // If the caller asked for instant scroll, or if we failed
10703 // to do a smooth scroll, do an instant scroll.
10704 SetPendingVisualScrollUpdate(aVisualViewportOffset, aUpdateType);
10707 void PresShell::SetPendingVisualScrollUpdate(
10708 const nsPoint& aVisualViewportOffset,
10709 FrameMetrics::ScrollOffsetUpdateType aUpdateType) {
10710 mPendingVisualScrollUpdate =
10711 Some(VisualScrollUpdate{aVisualViewportOffset, aUpdateType});
10713 // The pending update is picked up during the next paint.
10714 // Schedule a paint to make sure one will happen.
10715 if (nsIFrame* rootFrame = GetRootFrame()) {
10716 rootFrame->SchedulePaint();
10720 void PresShell::ClearPendingVisualScrollUpdate() {
10721 if (mPendingVisualScrollUpdate && mPendingVisualScrollUpdate->mAcknowledged) {
10722 mPendingVisualScrollUpdate = mozilla::Nothing();
10726 void PresShell::AcknowledgePendingVisualScrollUpdate() {
10727 MOZ_ASSERT(mPendingVisualScrollUpdate);
10728 mPendingVisualScrollUpdate->mAcknowledged = true;
10731 nsPoint PresShell::GetVisualViewportOffsetRelativeToLayoutViewport() const {
10732 return GetVisualViewportOffset() - GetLayoutViewportOffset();
10735 nsPoint PresShell::GetLayoutViewportOffset() const {
10736 nsPoint result;
10737 if (nsIScrollableFrame* sf = GetRootScrollFrameAsScrollable()) {
10738 result = sf->GetScrollPosition();
10740 return result;
10743 nsSize PresShell::GetLayoutViewportSize() const {
10744 nsSize result;
10745 if (nsIScrollableFrame* sf = GetRootScrollFrameAsScrollable()) {
10746 result = sf->GetScrollPortRect().Size();
10748 return result;
10751 void PresShell::RecomputeFontSizeInflationEnabled() {
10752 mFontSizeInflationEnabled = DetermineFontSizeInflationState();
10754 // Divide by 100 to convert the pref from a percentage to a fraction.
10755 float fontScale = StaticPrefs::font_size_systemFontScale() / 100.0f;
10756 if (fontScale == 0.0f) {
10757 return;
10760 MOZ_ASSERT(mDocument);
10761 MOZ_ASSERT(mPresContext);
10762 if (mFontSizeInflationEnabled || mDocument->IsSyntheticDocument()) {
10763 mPresContext->SetSystemFontScale(1.0f);
10764 } else {
10765 mPresContext->SetSystemFontScale(fontScale);
10769 bool PresShell::DetermineFontSizeInflationState() {
10770 MOZ_ASSERT(mPresContext, "our pres context should not be null");
10771 if (mPresContext->IsChrome()) {
10772 return false;
10775 if (FontSizeInflationEmPerLine() == 0 && FontSizeInflationMinTwips() == 0) {
10776 return false;
10779 // Force-enabling font inflation always trumps the heuristics here.
10780 if (!FontSizeInflationForceEnabled()) {
10781 if (BrowserChild* tab = BrowserChild::GetFrom(this)) {
10782 // We're in a child process. Cancel inflation if we're not
10783 // async-pan zoomed.
10784 if (!tab->AsyncPanZoomEnabled()) {
10785 return false;
10787 } else if (XRE_IsParentProcess()) {
10788 // We're in the master process. Cancel inflation if it's been
10789 // explicitly disabled.
10790 if (FontSizeInflationDisabledInMasterProcess()) {
10791 return false;
10796 // XXXjwir3:
10797 // See bug 706918, comment 23 for more information on this particular section
10798 // of the code. We're using "screen size" in place of the size of the content
10799 // area, because on mobile, these are close or equal. This will work for our
10800 // purposes (bug 706198), but it will need to be changed in the future to be
10801 // more correct when we bring the rest of the viewport code into platform.
10802 // We actually want the size of the content area, in the event that we don't
10803 // have any metadata about the width and/or height. On mobile, the screen size
10804 // and the size of the content area are very close, or the same value.
10805 // In XUL fennec, the content area is the size of the <browser> widget, but
10806 // in native fennec, the content area is the size of the Gecko LayerView
10807 // object.
10809 // TODO:
10810 // Once bug 716575 has been resolved, this code should be changed so that it
10811 // does the right thing on all platforms.
10812 nsresult rv;
10813 nsCOMPtr<nsIScreenManager> screenMgr =
10814 do_GetService("@mozilla.org/gfx/screenmanager;1", &rv);
10815 if (!NS_SUCCEEDED(rv)) {
10816 return false;
10819 nsCOMPtr<nsIScreen> screen;
10820 screenMgr->GetPrimaryScreen(getter_AddRefs(screen));
10821 if (screen) {
10822 int32_t screenLeft, screenTop, screenWidth, screenHeight;
10823 screen->GetRect(&screenLeft, &screenTop, &screenWidth, &screenHeight);
10825 nsViewportInfo vInf = GetDocument()->GetViewportInfo(
10826 ScreenIntSize(screenWidth, screenHeight));
10828 if (vInf.GetDefaultZoom() >= CSSToScreenScale(1.0f) ||
10829 vInf.IsAutoSizeEnabled()) {
10830 return false;
10834 return true;
10837 void PresShell::PausePainting() {
10838 if (GetPresContext()->RefreshDriver()->GetPresContext() != GetPresContext())
10839 return;
10841 mPaintingIsFrozen = true;
10842 GetPresContext()->RefreshDriver()->Freeze();
10845 void PresShell::ResumePainting() {
10846 if (GetPresContext()->RefreshDriver()->GetPresContext() != GetPresContext())
10847 return;
10849 mPaintingIsFrozen = false;
10850 GetPresContext()->RefreshDriver()->Thaw();
10853 void PresShell::SyncWindowProperties(nsView* aView) {
10854 nsIFrame* frame = aView->GetFrame();
10855 if (frame && mPresContext) {
10856 // CreateReferenceRenderingContext can return nullptr
10857 RefPtr<gfxContext> rcx(CreateReferenceRenderingContext());
10858 nsContainerFrame::SyncWindowProperties(mPresContext, frame, aView, rcx, 0);
10862 static StyleOrigin ToOrigin(uint32_t aServiceSheetType) {
10863 switch (aServiceSheetType) {
10864 case nsIStyleSheetService::AGENT_SHEET:
10865 return StyleOrigin::UserAgent;
10866 break;
10867 case nsIStyleSheetService::USER_SHEET:
10868 return StyleOrigin::User;
10869 break;
10870 default:
10871 MOZ_FALLTHROUGH_ASSERT("unexpected aSheetType value");
10872 case nsIStyleSheetService::AUTHOR_SHEET:
10873 return StyleOrigin::Author;
10877 nsresult PresShell::HasRuleProcessorUsedByMultipleStyleSets(uint32_t aSheetType,
10878 bool* aRetVal) {
10879 *aRetVal = false;
10880 return NS_OK;
10883 void PresShell::NotifyStyleSheetServiceSheetAdded(StyleSheet* aSheet,
10884 uint32_t aSheetType) {
10885 switch (aSheetType) {
10886 case nsIStyleSheetService::AGENT_SHEET:
10887 AddAgentSheet(aSheet);
10888 break;
10889 case nsIStyleSheetService::USER_SHEET:
10890 AddUserSheet(aSheet);
10891 break;
10892 case nsIStyleSheetService::AUTHOR_SHEET:
10893 AddAuthorSheet(aSheet);
10894 break;
10895 default:
10896 MOZ_ASSERT_UNREACHABLE("unexpected aSheetType value");
10897 break;
10901 void PresShell::NotifyStyleSheetServiceSheetRemoved(StyleSheet* aSheet,
10902 uint32_t aSheetType) {
10903 RemoveSheet(ToOrigin(aSheetType), aSheet);
10906 void PresShell::SetIsUnderHiddenEmbedderElement(
10907 bool aUnderHiddenEmbedderElement) {
10908 if (mUnderHiddenEmbedderElement == aUnderHiddenEmbedderElement) {
10909 return;
10912 mUnderHiddenEmbedderElement = aUnderHiddenEmbedderElement;
10914 if (nsCOMPtr<nsIDocShell> docShell = mPresContext->GetDocShell()) {
10915 BrowsingContext* bc = docShell->GetBrowsingContext();
10917 // Propagate to children.
10918 for (BrowsingContext* child : bc->GetChildren()) {
10919 Element* embedderElement = child->GetEmbedderElement();
10920 if (!embedderElement) {
10921 // TODO: We shouldn't need to null check here since `child` and the
10922 // element returned by `child->GetEmbedderElement()` are in our
10923 // process (the actual browsing context represented by `child` may not
10924 // be, but that doesn't matter). However, there are currently a very
10925 // small number of crashes due to `embedderElement` being null, somehow
10926 // - see bug 1551241. For now we wallpaper the crash.
10927 continue;
10930 bool embedderFrameIsHidden = true;
10931 if (auto embedderFrame = embedderElement->GetPrimaryFrame()) {
10932 embedderFrameIsHidden = !embedderFrame->StyleVisibility()->IsVisible();
10935 if (nsIDocShell* childDocShell = child->GetDocShell()) {
10936 PresShell* presShell = childDocShell->GetPresShell();
10937 if (!presShell) {
10938 continue;
10940 presShell->SetIsUnderHiddenEmbedderElement(
10941 aUnderHiddenEmbedderElement || embedderFrameIsHidden);
10942 } else {
10943 BrowserBridgeChild* bridgeChild =
10944 BrowserBridgeChild::GetFrom(embedderElement);
10945 bridgeChild->SetIsUnderHiddenEmbedderElement(
10946 aUnderHiddenEmbedderElement || embedderFrameIsHidden);
10952 nsIContent* PresShell::EventHandler::GetOverrideClickTarget(
10953 WidgetGUIEvent* aGUIEvent, nsIFrame* aFrame) {
10954 if (aGUIEvent->mMessage != eMouseUp) {
10955 return nullptr;
10958 MOZ_ASSERT(aGUIEvent->mClass == eMouseEventClass);
10959 WidgetMouseEvent* mouseEvent = aGUIEvent->AsMouseEvent();
10961 uint32_t flags = 0;
10962 nsPoint eventPoint =
10963 nsLayoutUtils::GetEventCoordinatesRelativeTo(aGUIEvent, aFrame);
10964 if (mouseEvent->mIgnoreRootScrollFrame) {
10965 flags |= INPUT_IGNORE_ROOT_SCROLL_FRAME;
10968 nsIFrame* target =
10969 FindFrameTargetedByInputEvent(aGUIEvent, aFrame, eventPoint, flags);
10970 if (!target) {
10971 return nullptr;
10974 nsIContent* overrideClickTarget = target->GetContent();
10975 while (overrideClickTarget && !overrideClickTarget->IsElement()) {
10976 overrideClickTarget = overrideClickTarget->GetFlattenedTreeParent();
10978 return overrideClickTarget;
10981 /******************************************************************************
10982 * PresShell::EventHandler::EventTargetData
10983 ******************************************************************************/
10985 void PresShell::EventHandler::EventTargetData::SetFrameAndComputePresShell(
10986 nsIFrame* aFrameToHandleEvent) {
10987 if (aFrameToHandleEvent) {
10988 mFrame = aFrameToHandleEvent;
10989 mPresShell = aFrameToHandleEvent->PresShell();
10990 } else {
10991 mFrame = nullptr;
10992 mPresShell = nullptr;
10996 void PresShell::EventHandler::EventTargetData::
10997 SetFrameAndComputePresShellAndContent(nsIFrame* aFrameToHandleEvent,
10998 WidgetGUIEvent* aGUIEvent) {
10999 MOZ_ASSERT(aFrameToHandleEvent);
11000 MOZ_ASSERT(aGUIEvent);
11002 SetFrameAndComputePresShell(aFrameToHandleEvent);
11003 SetContentForEventFromFrame(aGUIEvent);
11006 void PresShell::EventHandler::EventTargetData::SetContentForEventFromFrame(
11007 WidgetGUIEvent* aGUIEvent) {
11008 MOZ_ASSERT(mFrame);
11009 mContent = nullptr;
11010 mFrame->GetContentForEvent(aGUIEvent, getter_AddRefs(mContent));
11013 nsIContent* PresShell::EventHandler::EventTargetData::GetFrameContent() const {
11014 return mFrame ? mFrame->GetContent() : nullptr;
11017 bool PresShell::EventHandler::EventTargetData::MaybeRetargetToActiveDocument(
11018 WidgetGUIEvent* aGUIEvent) {
11019 MOZ_ASSERT(aGUIEvent);
11020 MOZ_ASSERT(mFrame);
11021 MOZ_ASSERT(mPresShell);
11022 MOZ_ASSERT(!mContent, "Doesn't support to retarget the content");
11024 EventStateManager* activeESM =
11025 EventStateManager::GetActiveEventStateManager();
11026 if (!activeESM) {
11027 return false;
11030 if (aGUIEvent->mClass != ePointerEventClass &&
11031 !aGUIEvent->HasMouseEventMessage()) {
11032 return false;
11035 if (activeESM == GetEventStateManager()) {
11036 return false;
11039 nsPresContext* activePresContext = activeESM->GetPresContext();
11040 if (!activePresContext) {
11041 return false;
11044 PresShell* activePresShell = activePresContext->GetPresShell();
11045 if (!activePresShell) {
11046 return false;
11049 // Note, currently for backwards compatibility we don't forward mouse events
11050 // to the active document when mouse is over some subdocument.
11051 if (!nsContentUtils::ContentIsCrossDocDescendantOf(
11052 activePresShell->GetDocument(), GetDocument())) {
11053 return false;
11056 SetPresShellAndFrame(activePresShell, activePresShell->GetRootFrame());
11057 return true;
11060 bool PresShell::EventHandler::EventTargetData::ComputeElementFromFrame(
11061 WidgetGUIEvent* aGUIEvent) {
11062 MOZ_ASSERT(aGUIEvent);
11063 MOZ_ASSERT(aGUIEvent->IsUsingCoordinates());
11064 MOZ_ASSERT(mPresShell);
11065 MOZ_ASSERT(mFrame);
11067 SetContentForEventFromFrame(aGUIEvent);
11069 // If there is no content for this frame, target it anyway. Some frames can
11070 // be targeted but do not have content, particularly windows with scrolling
11071 // off.
11072 if (!mContent) {
11073 return true;
11076 // Bug 103055, bug 185889: mouse events apply to *elements*, not all nodes.
11077 // Thus we get the nearest element parent here.
11078 // XXX we leave the frame the same even if we find an element parent, so that
11079 // the text frame will receive the event (selection and friends are the ones
11080 // who care about that anyway)
11082 // We use weak pointers because during this tight loop, the node
11083 // will *not* go away. And this happens on every mousemove.
11084 nsIContent* content = mContent;
11085 while (content && !content->IsElement()) {
11086 content = content->GetFlattenedTreeParent();
11088 mContent = content;
11090 // If we found an element, target it. Otherwise, target *nothing*.
11091 return !!mContent;
11094 void PresShell::EventHandler::EventTargetData::UpdateTouchEventTarget(
11095 WidgetGUIEvent* aGUIEvent) {
11096 MOZ_ASSERT(aGUIEvent);
11098 if (aGUIEvent->mClass != eTouchEventClass) {
11099 return;
11102 if (aGUIEvent->mMessage == eTouchStart) {
11103 WidgetTouchEvent* touchEvent = aGUIEvent->AsTouchEvent();
11104 nsIFrame* newFrame =
11105 TouchManager::SuppressInvalidPointsAndGetTargetedFrame(touchEvent);
11106 if (!newFrame) {
11107 return; // XXX Why don't we stop handling the event in this case?
11109 SetFrameAndComputePresShellAndContent(newFrame, aGUIEvent);
11110 return;
11113 PresShell* newPresShell = PresShell::GetShellForTouchEvent(aGUIEvent);
11114 if (!newPresShell) {
11115 return; // XXX Why don't we stop handling the event in this case?
11118 // Touch events (except touchstart) are dispatching to the captured
11119 // element. Get correct shell from it.
11120 mPresShell = newPresShell;
11123 /******************************************************************************
11124 * PresShell::EventHandler::HandlingTimeAccumulator
11125 ******************************************************************************/
11127 PresShell::EventHandler::HandlingTimeAccumulator::HandlingTimeAccumulator(
11128 const PresShell::EventHandler& aEventHandler, const WidgetEvent* aEvent)
11129 : mEventHandler(aEventHandler),
11130 mEvent(aEvent),
11131 mHandlingStartTime(TimeStamp::Now()) {
11132 MOZ_ASSERT(mEvent);
11133 MOZ_ASSERT(mEvent->IsTrusted());
11136 PresShell::EventHandler::HandlingTimeAccumulator::~HandlingTimeAccumulator() {
11137 if (mEvent->mTimeStamp <= mEventHandler.mPresShell->mLastOSWake) {
11138 return;
11141 switch (mEvent->mMessage) {
11142 case eKeyPress:
11143 case eKeyDown:
11144 case eKeyUp:
11145 Telemetry::AccumulateTimeDelta(Telemetry::INPUT_EVENT_HANDLED_KEYBOARD_MS,
11146 mHandlingStartTime);
11147 return;
11148 case eMouseDown:
11149 Telemetry::AccumulateTimeDelta(
11150 Telemetry::INPUT_EVENT_HANDLED_MOUSE_DOWN_MS, mHandlingStartTime);
11151 return;
11152 case eMouseUp:
11153 Telemetry::AccumulateTimeDelta(Telemetry::INPUT_EVENT_HANDLED_MOUSE_UP_MS,
11154 mHandlingStartTime);
11155 return;
11156 case eMouseMove:
11157 if (mEvent->mFlags.mHandledByAPZ) {
11158 Telemetry::AccumulateTimeDelta(
11159 Telemetry::INPUT_EVENT_HANDLED_APZ_MOUSE_MOVE_MS,
11160 mHandlingStartTime);
11162 return;
11163 case eWheel:
11164 if (mEvent->mFlags.mHandledByAPZ) {
11165 Telemetry::AccumulateTimeDelta(
11166 Telemetry::INPUT_EVENT_HANDLED_APZ_WHEEL_MS, mHandlingStartTime);
11168 return;
11169 case eTouchMove:
11170 if (mEvent->mFlags.mHandledByAPZ) {
11171 Telemetry::AccumulateTimeDelta(
11172 Telemetry::INPUT_EVENT_HANDLED_APZ_TOUCH_MOVE_MS,
11173 mHandlingStartTime);
11175 return;
11176 default:
11177 return;
11181 static bool EndPaintHelper(Document* aDocument, void* aData) {
11182 if (PresShell* presShell = aDocument->GetPresShell()) {
11183 presShell->EndPaint();
11185 return true;
11188 void PresShell::EndPaint() {
11189 ClearPendingVisualScrollUpdate();
11191 if (mDocument) {
11192 mDocument->EnumerateSubDocuments(EndPaintHelper, nullptr);
11196 void PresShell::PingReqsPerFlushTelemetry(FlushKind aFlushKind) {
11197 if (aFlushKind == FlushKind::Layout) {
11198 auto styleFlushReqs = mReqsPerFlush[FlushKind::Style].value();
11199 auto layoutFlushReqs = mReqsPerFlush[FlushKind::Layout].value();
11200 Telemetry::Accumulate(Telemetry::PRESSHELL_REQS_PER_LAYOUT_FLUSH,
11201 NS_LITERAL_CSTRING("Style"), styleFlushReqs);
11202 Telemetry::Accumulate(Telemetry::PRESSHELL_REQS_PER_LAYOUT_FLUSH,
11203 NS_LITERAL_CSTRING("Layout"), layoutFlushReqs);
11204 mReqsPerFlush[FlushKind::Style] = SaturateUint8(0);
11205 mReqsPerFlush[FlushKind::Layout] = SaturateUint8(0);
11206 } else {
11207 auto styleFlushReqs = mReqsPerFlush[FlushKind::Style].value();
11208 Telemetry::Accumulate(Telemetry::PRESSHELL_REQS_PER_STYLE_FLUSH,
11209 styleFlushReqs);
11210 mReqsPerFlush[FlushKind::Style] = SaturateUint8(0);
11214 void PresShell::PingFlushPerTickTelemetry(FlushType aFlushType) {
11215 MOZ_ASSERT(aFlushType == FlushType::Style || aFlushType == FlushType::Layout);
11216 auto styleFlushes = mFlushesPerTick[FlushKind::Style].value();
11217 if (styleFlushes > 0) {
11218 Telemetry::Accumulate(Telemetry::PRESSHELL_FLUSHES_PER_TICK,
11219 NS_LITERAL_CSTRING("Style"), styleFlushes);
11220 mFlushesPerTick[FlushKind::Style] = SaturateUint8(0);
11223 auto layoutFlushes = mFlushesPerTick[FlushKind::Layout].value();
11224 if (aFlushType == FlushType::Layout && layoutFlushes > 0) {
11225 Telemetry::Accumulate(Telemetry::PRESSHELL_FLUSHES_PER_TICK,
11226 NS_LITERAL_CSTRING("Layout"), layoutFlushes);
11227 mFlushesPerTick[FlushKind::Layout] = SaturateUint8(0);