1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 /* a presentation of a document, part 2 */
8 
9 #include "mozilla/PresShell.h"
10 
11 #include "Units.h"
12 #include "mozilla/dom/FontFaceSet.h"
13 #include "mozilla/ArrayUtils.h"
14 #include "mozilla/Attributes.h"
15 #include "mozilla/AutoRestore.h"
16 #include "mozilla/ContentIterator.h"
17 #include "mozilla/DisplayPortUtils.h"
18 #include "mozilla/EventDispatcher.h"
19 #include "mozilla/EventStateManager.h"
20 #include "mozilla/EventStates.h"
21 #include "mozilla/GeckoMVMContext.h"
22 #include "mozilla/IMEStateManager.h"
23 #include "mozilla/MemoryReporting.h"
24 #include "mozilla/dom/BrowserChild.h"
25 #include "mozilla/Likely.h"
26 #include "mozilla/Logging.h"
27 #include "mozilla/MouseEvents.h"
28 #include "mozilla/PerfStats.h"
29 #include "mozilla/PointerLockManager.h"
30 #include "mozilla/PresShellInlines.h"
31 #include "mozilla/RangeUtils.h"
32 #include "mozilla/ScopeExit.h"
33 #include "mozilla/Sprintf.h"
34 #include "mozilla/StaticAnalysisFunctions.h"
35 #include "mozilla/StaticPrefs_apz.h"
36 #include "mozilla/StaticPrefs_browser.h"
37 #include "mozilla/StaticPrefs_dom.h"
38 #include "mozilla/StaticPrefs_font.h"
39 #include "mozilla/StaticPrefs_layout.h"
40 #include "mozilla/TextEvents.h"
41 #include "mozilla/TimeStamp.h"
42 #include "mozilla/TouchEvents.h"
43 #include "mozilla/UniquePtr.h"
44 #include "mozilla/Unused.h"
45 #include "mozilla/ViewportUtils.h"
46 #include <algorithm>
47 
48 #ifdef XP_WIN
49 #  include "winuser.h"
50 #endif
51 
52 #include "gfxContext.h"
53 #include "gfxUserFontSet.h"
54 #include "nsContentList.h"
55 #include "nsPresContext.h"
56 #include "nsIContent.h"
57 #include "mozilla/dom/BrowserBridgeChild.h"
58 #include "mozilla/dom/BrowsingContext.h"
59 #include "mozilla/dom/CanonicalBrowsingContext.h"
60 #include "mozilla/dom/ContentChild.h"
61 #include "mozilla/dom/ContentParent.h"
62 #include "mozilla/dom/Element.h"
63 #include "mozilla/dom/PointerEventHandler.h"
64 #include "mozilla/dom/PopupBlocker.h"
65 #include "mozilla/dom/Document.h"
66 #include "mozilla/dom/DocumentInlines.h"
67 #include "mozilla/dom/UserActivation.h"
68 #include "nsAnimationManager.h"
69 #include "nsNameSpaceManager.h"  // for Pref-related rule management (bugs 22963,20760,31816)
70 #include "nsFlexContainerFrame.h"
71 #include "nsIFrame.h"
72 #include "FrameLayerBuilder.h"
73 #include "nsViewManager.h"
74 #include "nsView.h"
75 #include "nsCRTGlue.h"
76 #include "prinrval.h"
77 #include "nsTArray.h"
78 #include "nsCOMArray.h"
79 #include "nsContainerFrame.h"
80 #include "mozilla/dom/Selection.h"
81 #include "nsGkAtoms.h"
82 #include "nsRange.h"
83 #include "nsWindowSizes.h"
84 #include "nsCOMPtr.h"
85 #include "nsReadableUtils.h"
86 #include "nsPageSequenceFrame.h"
87 #include "nsCaret.h"
88 #include "mozilla/AccessibleCaretEventHub.h"
89 #include "nsFrameManager.h"
90 #include "nsXPCOM.h"
91 #include "nsILayoutHistoryState.h"
92 #include "nsILineIterator.h"  // for ScrollContentIntoView
93 #include "PLDHashTable.h"
94 #include "mozilla/dom/Touch.h"
95 #include "mozilla/dom/TouchEvent.h"
96 #include "mozilla/dom/PointerEventBinding.h"
97 #include "mozilla/dom/ShadowIncludingTreeIterator.h"
98 #include "nsIObserverService.h"
99 #include "nsDocShell.h"  // for reflow observation
100 #include "nsIBaseWindow.h"
101 #include "nsError.h"
102 #include "nsLayoutUtils.h"
103 #include "nsViewportInfo.h"
104 #include "nsCSSRendering.h"
105 // for |#ifdef DEBUG| code
106 #include "prenv.h"
107 #include "nsDisplayList.h"
108 #include "nsRegion.h"
109 #include "nsAutoLayoutPhase.h"
110 #include "AutoProfilerStyleMarker.h"
111 #ifdef MOZ_REFLOW_PERF
112 #  include "nsFontMetrics.h"
113 #endif
114 #include "MobileViewportManager.h"
115 #include "OverflowChangedTracker.h"
116 #include "PositionedEventTargeting.h"
117 
118 #include "nsIReflowCallback.h"
119 
120 #include "nsPIDOMWindow.h"
121 #include "nsFocusManager.h"
122 #include "nsNetUtil.h"
123 #include "nsThreadUtils.h"
124 #include "nsStyleSheetService.h"
125 #include "gfxUtils.h"
126 #include "mozilla/SMILAnimationController.h"
127 #include "mozilla/dom/SVGAnimationElement.h"
128 #include "mozilla/SVGObserverUtils.h"
129 #include "mozilla/SVGFragmentIdentifier.h"
130 #include "nsFrameSelection.h"
131 
132 #include "mozilla/dom/Performance.h"
133 #include "nsRefreshDriver.h"
134 #include "nsDOMNavigationTiming.h"
135 
136 // Drag & Drop, Clipboard
137 #include "nsIDocShellTreeItem.h"
138 #include "nsIURI.h"
139 #include "nsIScrollableFrame.h"
140 #include "nsITimer.h"
141 #ifdef ACCESSIBILITY
142 #  include "nsAccessibilityService.h"
143 #  include "mozilla/a11y/DocAccessible.h"
144 #  ifdef DEBUG
145 #    include "mozilla/a11y/Logging.h"
146 #  endif
147 #endif
148 
149 // For style data reconstruction
150 #include "nsStyleChangeList.h"
151 #include "nsCSSFrameConstructor.h"
152 #ifdef MOZ_XUL
153 #  include "nsMenuFrame.h"
154 #  include "nsTreeBodyFrame.h"
155 #  include "XULTreeElement.h"
156 #  include "nsMenuPopupFrame.h"
157 #  include "nsTreeColumns.h"
158 #  include "nsIDOMXULMultSelectCntrlEl.h"
159 #  include "nsIDOMXULSelectCntrlItemEl.h"
160 #  include "nsIDOMXULMenuListElement.h"
161 #  include "nsXULElement.h"
162 #endif  // MOZ_XUL
163 
164 #include "mozilla/layers/CompositorBridgeChild.h"
165 #include "ClientLayerManager.h"
166 #include "gfxPlatform.h"
167 #include "Layers.h"
168 #include "LayerTreeInvalidation.h"
169 #include "mozilla/css/ImageLoader.h"
170 #include "mozilla/dom/DocumentTimeline.h"
171 #include "mozilla/dom/ScriptSettings.h"
172 #include "mozilla/ErrorResult.h"
173 #include "mozilla/Preferences.h"
174 #include "mozilla/Telemetry.h"
175 #include "nsCanvasFrame.h"
176 #include "nsImageFrame.h"
177 #include "nsIScreen.h"
178 #include "nsIScreenManager.h"
179 #include "nsPlaceholderFrame.h"
180 #include "nsTransitionManager.h"
181 #include "ChildIterator.h"
182 #include "mozilla/RestyleManager.h"
183 #include "nsIDragSession.h"
184 #include "nsIFrameInlines.h"
185 #include "mozilla/gfx/2D.h"
186 #include "nsNetUtil.h"
187 #include "nsSubDocumentFrame.h"
188 #include "nsQueryObject.h"
189 #include "mozilla/GlobalStyleSheetCache.h"
190 #include "mozilla/layers/InputAPZContext.h"
191 #include "mozilla/layers/FocusTarget.h"
192 #include "mozilla/layers/WebRenderLayerManager.h"
193 #include "mozilla/layers/WebRenderUserData.h"
194 #include "mozilla/layout/ScrollAnchorContainer.h"
195 #include "mozilla/ProfilerLabels.h"
196 #include "mozilla/ProfilerMarkers.h"
197 #include "mozilla/ScrollTypes.h"
198 #include "mozilla/ServoBindings.h"
199 #include "mozilla/ServoStyleSet.h"
200 #include "mozilla/StyleSheet.h"
201 #include "mozilla/StyleSheetInlines.h"
202 #include "mozilla/InputTaskManager.h"
203 #include "mozilla/dom/ImageTracker.h"
204 #include "nsIDocShellTreeOwner.h"
205 #include "nsClassHashtable.h"
206 #include "nsHashKeys.h"
207 #include "VisualViewport.h"
208 #include "ZoomConstraintsClient.h"
209 
210 // define the scalfactor of drag and drop images
211 // relative to the max screen height/width
212 #define RELATIVE_SCALEFACTOR 0.0925f
213 
214 using namespace mozilla;
215 using namespace mozilla::css;
216 using namespace mozilla::dom;
217 using namespace mozilla::gfx;
218 using namespace mozilla::layers;
219 using namespace mozilla::gfx;
220 using namespace mozilla::layout;
221 using PaintFrameFlags = nsLayoutUtils::PaintFrameFlags;
222 typedef ScrollableLayerGuid::ViewID ViewID;
223 
224 PresShell::CapturingContentInfo PresShell::sCapturingContentInfo;
225 
226 // RangePaintInfo is used to paint ranges to offscreen buffers
227 struct RangePaintInfo {
228   RefPtr<nsRange> mRange;
229   nsDisplayListBuilder mBuilder;
230   nsDisplayList mList;
231 
232   // offset of builder's reference frame to the root frame
233   nsPoint mRootOffset;
234 
235   // Resolution at which the items are normally painted. So if we're painting
236   // these items in a range separately from the "full display list", we may want
237   // to paint them at this resolution.
238   float mResolution = 1.0;
239 
RangePaintInfoRangePaintInfo240   RangePaintInfo(nsRange* aRange, nsIFrame* aFrame)
241       : mRange(aRange),
242         mBuilder(aFrame, nsDisplayListBuilderMode::Painting, false) {
243     MOZ_COUNT_CTOR(RangePaintInfo);
244     mBuilder.BeginFrame();
245   }
246 
~RangePaintInfoRangePaintInfo247   ~RangePaintInfo() {
248     mList.DeleteAll(&mBuilder);
249     mBuilder.EndFrame();
250     MOZ_COUNT_DTOR(RangePaintInfo);
251   }
252 };
253 
254 #undef NOISY
255 
256 // ----------------------------------------------------------------------
257 
258 #ifdef DEBUG
259 // Set the environment variable GECKO_VERIFY_REFLOW_FLAGS to one or
260 // more of the following flags (comma separated) for handy debug
261 // output.
262 static VerifyReflowFlags gVerifyReflowFlags;
263 
264 struct VerifyReflowFlagData {
265   const char* name;
266   VerifyReflowFlags bit;
267 };
268 
269 static const VerifyReflowFlagData gFlags[] = {
270     // clang-format off
271   { "verify",                VerifyReflowFlags::On },
272   { "reflow",                VerifyReflowFlags::Noisy },
273   { "all",                   VerifyReflowFlags::All },
274   { "list-commands",         VerifyReflowFlags::DumpCommands },
275   { "noisy-commands",        VerifyReflowFlags::NoisyCommands },
276   { "really-noisy-commands", VerifyReflowFlags::ReallyNoisyCommands },
277   { "resize",                VerifyReflowFlags::DuringResizeReflow },
278     // clang-format on
279 };
280 
281 #  define NUM_VERIFY_REFLOW_FLAGS (sizeof(gFlags) / sizeof(gFlags[0]))
282 
ShowVerifyReflowFlags()283 static void ShowVerifyReflowFlags() {
284   printf("Here are the available GECKO_VERIFY_REFLOW_FLAGS:\n");
285   const VerifyReflowFlagData* flag = gFlags;
286   const VerifyReflowFlagData* limit = gFlags + NUM_VERIFY_REFLOW_FLAGS;
287   while (flag < limit) {
288     printf("  %s\n", flag->name);
289     ++flag;
290   }
291   printf("Note: GECKO_VERIFY_REFLOW_FLAGS is a comma separated list of flag\n");
292   printf("names (no whitespace)\n");
293 }
294 #endif
295 
296 //========================================================================
297 //========================================================================
298 //========================================================================
299 #ifdef MOZ_REFLOW_PERF
300 class ReflowCountMgr;
301 
302 static const char kGrandTotalsStr[] = "Grand Totals";
303 
304 // Counting Class
305 class ReflowCounter {
306  public:
307   explicit ReflowCounter(ReflowCountMgr* aMgr = nullptr);
308   ~ReflowCounter();
309 
310   void ClearTotals();
311   void DisplayTotals(const char* aStr);
312   void DisplayDiffTotals(const char* aStr);
313   void DisplayHTMLTotals(const char* aStr);
314 
Add()315   void Add() { mTotal++; }
Add(uint32_t aTotal)316   void Add(uint32_t aTotal) { mTotal += aTotal; }
317 
318   void CalcDiffInTotals();
319   void SetTotalsCache();
320 
SetMgr(ReflowCountMgr * aMgr)321   void SetMgr(ReflowCountMgr* aMgr) { mMgr = aMgr; }
322 
GetTotal()323   uint32_t GetTotal() { return mTotal; }
324 
325  protected:
326   void DisplayTotals(uint32_t aTotal, const char* aTitle);
327   void DisplayHTMLTotals(uint32_t aTotal, const char* aTitle);
328 
329   uint32_t mTotal;
330   uint32_t mCacheTotal;
331 
332   ReflowCountMgr* mMgr;  // weak reference (don't delete)
333 };
334 
335 // Counting Class
336 class IndiReflowCounter {
337  public:
IndiReflowCounter(ReflowCountMgr * aMgr=nullptr)338   explicit IndiReflowCounter(ReflowCountMgr* aMgr = nullptr)
339       : mFrame(nullptr),
340         mCount(0),
341         mMgr(aMgr),
342         mCounter(aMgr),
343         mHasBeenOutput(false) {}
344   virtual ~IndiReflowCounter() = default;
345 
346   nsAutoString mName;
347   nsIFrame* mFrame;  // weak reference (don't delete)
348   int32_t mCount;
349 
350   ReflowCountMgr* mMgr;  // weak reference (don't delete)
351 
352   ReflowCounter mCounter;
353   bool mHasBeenOutput;
354 };
355 
356 //--------------------
357 // Manager Class
358 //--------------------
359 class ReflowCountMgr {
360  public:
361   ReflowCountMgr();
362   virtual ~ReflowCountMgr();
363 
364   void ClearTotals();
365   void ClearGrandTotals();
366   void DisplayTotals(const char* aStr);
367   void DisplayHTMLTotals(const char* aStr);
368   void DisplayDiffsInTotals();
369 
370   void Add(const char* aName, nsIFrame* aFrame);
371   ReflowCounter* LookUp(const char* aName);
372 
373   void PaintCount(const char* aName, gfxContext* aRenderingContext,
374                   nsPresContext* aPresContext, nsIFrame* aFrame,
375                   const nsPoint& aOffset, uint32_t aColor);
376 
GetOutFile()377   FILE* GetOutFile() { return mFD; }
378 
SetPresContext(nsPresContext * aPresContext)379   void SetPresContext(nsPresContext* aPresContext) {
380     mPresContext = aPresContext;  // weak reference
381   }
SetPresShell(PresShell * aPresShell)382   void SetPresShell(PresShell* aPresShell) {
383     mPresShell = aPresShell;  // weak reference
384   }
385 
SetDumpFrameCounts(bool aVal)386   void SetDumpFrameCounts(bool aVal) { mDumpFrameCounts = aVal; }
SetDumpFrameByFrameCounts(bool aVal)387   void SetDumpFrameByFrameCounts(bool aVal) { mDumpFrameByFrameCounts = aVal; }
SetPaintFrameCounts(bool aVal)388   void SetPaintFrameCounts(bool aVal) { mPaintFrameByFrameCounts = aVal; }
389 
IsPaintingFrameCounts()390   bool IsPaintingFrameCounts() { return mPaintFrameByFrameCounts; }
391 
392  protected:
393   void DisplayTotals(uint32_t aTotal, uint32_t* aDupArray, char* aTitle);
394   void DisplayHTMLTotals(uint32_t aTotal, uint32_t* aDupArray, char* aTitle);
395 
396   void DoGrandTotals();
397   void DoIndiTotalsTree();
398 
399   // HTML Output Methods
400   void DoGrandHTMLTotals();
401 
402   nsClassHashtable<nsCharPtrHashKey, ReflowCounter> mCounts;
403   nsClassHashtable<nsCharPtrHashKey, IndiReflowCounter> mIndiFrameCounts;
404   FILE* mFD;
405 
406   bool mDumpFrameCounts;
407   bool mDumpFrameByFrameCounts;
408   bool mPaintFrameByFrameCounts;
409 
410   bool mCycledOnce;
411 
412   // Root Frame for Individual Tracking
413   nsPresContext* mPresContext;
414   PresShell* mPresShell;
415 
416   // ReflowCountMgr gReflowCountMgr;
417 };
418 #endif
419 //========================================================================
420 
421 // comment out to hide caret
422 #define SHOW_CARET
423 
424 // The upper bound on the amount of time to spend reflowing, in
425 // microseconds.  When this bound is exceeded and reflow commands are
426 // still queued up, a reflow event is posted.  The idea is for reflow
427 // to not hog the processor beyond the time specifed in
428 // gMaxRCProcessingTime.  This data member is initialized from the
429 // layout.reflow.timeslice pref.
430 #define NS_MAX_REFLOW_TIME 1000000
431 static int32_t gMaxRCProcessingTime = -1;
432 
433 struct nsCallbackEventRequest {
434   nsIReflowCallback* callback;
435   nsCallbackEventRequest* next;
436 };
437 
438 // ----------------------------------------------------------------------------
439 //
440 // NOTE(emilio): It'd be nice for this to assert that our document isn't in the
441 // bfcache, but font pref changes don't care about that, and maybe / probably
442 // shouldn't.
443 #ifdef DEBUG
444 #  define ASSERT_REFLOW_SCHEDULED_STATE()                                   \
445     {                                                                       \
446       if (ObservingLayoutFlushes()) {                                       \
447         MOZ_ASSERT(                                                         \
448             mDocument->GetBFCacheEntry() ||                                 \
449                 mPresContext->RefreshDriver()->IsLayoutFlushObserver(this), \
450             "Unexpected state");                                            \
451       } else {                                                              \
452         MOZ_ASSERT(                                                         \
453             !mPresContext->RefreshDriver()->IsLayoutFlushObserver(this),    \
454             "Unexpected state");                                            \
455       }                                                                     \
456     }
457 #else
458 #  define ASSERT_REFLOW_SCHEDULED_STATE() /* nothing */
459 #endif
460 
461 class nsAutoCauseReflowNotifier {
462  public:
nsAutoCauseReflowNotifier(PresShell * aPresShell)463   MOZ_CAN_RUN_SCRIPT explicit nsAutoCauseReflowNotifier(PresShell* aPresShell)
464       : mPresShell(aPresShell) {
465     mPresShell->WillCauseReflow();
466   }
~nsAutoCauseReflowNotifier()467   MOZ_CAN_RUN_SCRIPT ~nsAutoCauseReflowNotifier() {
468     // This check should not be needed. Currently the only place that seem
469     // to need it is the code that deals with bug 337586.
470     if (!mPresShell->mHaveShutDown) {
471       RefPtr<PresShell> presShell(mPresShell);
472       presShell->DidCauseReflow();
473     } else {
474       nsContentUtils::RemoveScriptBlocker();
475     }
476   }
477 
478   PresShell* mPresShell;
479 };
480 
481 class MOZ_STACK_CLASS nsPresShellEventCB : public EventDispatchingCallback {
482  public:
nsPresShellEventCB(PresShell * aPresShell)483   explicit nsPresShellEventCB(PresShell* aPresShell) : mPresShell(aPresShell) {}
484 
485   MOZ_CAN_RUN_SCRIPT
HandleEvent(EventChainPostVisitor & aVisitor)486   virtual void HandleEvent(EventChainPostVisitor& aVisitor) override {
487     if (aVisitor.mPresContext && aVisitor.mEvent->mClass != eBasicEventClass) {
488       if (aVisitor.mEvent->mMessage == eMouseDown ||
489           aVisitor.mEvent->mMessage == eMouseUp) {
490         // Mouse-up and mouse-down events call nsIFrame::HandlePress/Release
491         // which call GetContentOffsetsFromPoint which requires up-to-date
492         // layout. Bring layout up-to-date now so that GetCurrentEventFrame()
493         // below will return a real frame and we don't have to worry about
494         // destroying it by flushing later.
495         MOZ_KnownLive(mPresShell)->FlushPendingNotifications(FlushType::Layout);
496       } else if (aVisitor.mEvent->mMessage == eWheel &&
497                  aVisitor.mEventStatus != nsEventStatus_eConsumeNoDefault) {
498         nsIFrame* frame = mPresShell->GetCurrentEventFrame();
499         if (frame) {
500           // chrome (including addons) should be able to know if content
501           // handles both D3E "wheel" event and legacy mouse scroll events.
502           // We should dispatch legacy mouse events before dispatching the
503           // "wheel" event into system group.
504           RefPtr<EventStateManager> esm =
505               aVisitor.mPresContext->EventStateManager();
506           esm->DispatchLegacyMouseScrollEvents(
507               frame, aVisitor.mEvent->AsWheelEvent(), &aVisitor.mEventStatus);
508         }
509       }
510       nsIFrame* frame = mPresShell->GetCurrentEventFrame();
511       if (!frame && (aVisitor.mEvent->mMessage == eMouseUp ||
512                      aVisitor.mEvent->mMessage == eTouchEnd)) {
513         // Redirect BUTTON_UP and TOUCH_END events to the root frame to ensure
514         // that capturing is released.
515         frame = mPresShell->GetRootFrame();
516       }
517       if (frame) {
518         frame->HandleEvent(aVisitor.mPresContext, aVisitor.mEvent->AsGUIEvent(),
519                            &aVisitor.mEventStatus);
520       }
521     }
522   }
523 
524   RefPtr<PresShell> mPresShell;
525 };
526 
527 class nsBeforeFirstPaintDispatcher : public Runnable {
528  public:
nsBeforeFirstPaintDispatcher(Document * aDocument)529   explicit nsBeforeFirstPaintDispatcher(Document* aDocument)
530       : mozilla::Runnable("nsBeforeFirstPaintDispatcher"),
531         mDocument(aDocument) {}
532 
533   // Fires the "before-first-paint" event so that interested parties (right now,
534   // the mobile browser) are aware of it.
Run()535   NS_IMETHOD Run() override {
536     nsCOMPtr<nsIObserverService> observerService =
537         mozilla::services::GetObserverService();
538     if (observerService) {
539       observerService->NotifyObservers(ToSupports(mDocument),
540                                        "before-first-paint", nullptr);
541     }
542     return NS_OK;
543   }
544 
545  private:
546   RefPtr<Document> mDocument;
547 };
548 
549 // This is a helper class to track whether the targeted frame is destroyed after
550 // dispatching pointer events. In that case, we need the original targeted
551 // content so that we can dispatch the mouse events to it.
552 class MOZ_STACK_CLASS AutoPointerEventTargetUpdater final {
553  public:
AutoPointerEventTargetUpdater(PresShell * aShell,WidgetEvent * aEvent,nsIFrame * aFrame,nsIContent ** aTargetContent)554   AutoPointerEventTargetUpdater(PresShell* aShell, WidgetEvent* aEvent,
555                                 nsIFrame* aFrame, nsIContent** aTargetContent) {
556     MOZ_ASSERT(aEvent);
557     if (!aTargetContent || aEvent->mClass != ePointerEventClass) {
558       // Make the destructor happy.
559       mTargetContent = nullptr;
560       return;
561     }
562     MOZ_ASSERT(aShell);
563     MOZ_ASSERT(aFrame);
564     MOZ_ASSERT(!aFrame->GetContent() ||
565                aShell->GetDocument() == aFrame->GetContent()->OwnerDoc());
566 
567     mShell = aShell;
568     mWeakFrame = aFrame;
569     mTargetContent = aTargetContent;
570     aShell->mPointerEventTarget = aFrame->GetContent();
571   }
572 
~AutoPointerEventTargetUpdater()573   ~AutoPointerEventTargetUpdater() {
574     if (!mTargetContent || !mShell || mWeakFrame.IsAlive()) {
575       return;
576     }
577     mShell->mPointerEventTarget.swap(*mTargetContent);
578   }
579 
580  private:
581   RefPtr<PresShell> mShell;
582   AutoWeakFrame mWeakFrame;
583   nsIContent** mTargetContent;
584 };
585 
Add(nsIFrame * aFrame)586 void PresShell::DirtyRootsList::Add(nsIFrame* aFrame) {
587   // Is this root already scheduled for reflow?
588   // FIXME: This could possibly be changed to a uniqueness assertion, with some
589   // work in ResizeReflowIgnoreOverride (and maybe others?)
590   if (mList.Contains(aFrame)) {
591     // We don't expect frame to change depths.
592     MOZ_ASSERT(aFrame->GetDepthInFrameTree() ==
593                mList[mList.IndexOf(aFrame)].mDepth);
594     return;
595   }
596 
597   mList.InsertElementSorted(
598       FrameAndDepth{aFrame, aFrame->GetDepthInFrameTree()},
599       FrameAndDepth::CompareByReverseDepth{});
600 }
601 
Remove(nsIFrame * aFrame)602 void PresShell::DirtyRootsList::Remove(nsIFrame* aFrame) {
603   mList.RemoveElement(aFrame);
604 }
605 
PopShallowestRoot()606 nsIFrame* PresShell::DirtyRootsList::PopShallowestRoot() {
607   // List is sorted in order of decreasing depth, so there are no deeper
608   // frames than the last one.
609   const FrameAndDepth& lastFAD = mList.PopLastElement();
610   nsIFrame* frame = lastFAD.mFrame;
611   // We don't expect frame to change depths.
612   MOZ_ASSERT(frame->GetDepthInFrameTree() == lastFAD.mDepth);
613   return frame;
614 }
615 
Clear()616 void PresShell::DirtyRootsList::Clear() { mList.Clear(); }
617 
Contains(nsIFrame * aFrame) const618 bool PresShell::DirtyRootsList::Contains(nsIFrame* aFrame) const {
619   return mList.Contains(aFrame);
620 }
621 
IsEmpty() const622 bool PresShell::DirtyRootsList::IsEmpty() const { return mList.IsEmpty(); }
623 
FrameIsAncestorOfDirtyRoot(nsIFrame * aFrame) const624 bool PresShell::DirtyRootsList::FrameIsAncestorOfDirtyRoot(
625     nsIFrame* aFrame) const {
626   MOZ_ASSERT(aFrame);
627 
628   // Look for a path from any dirty roots to aFrame, following GetParent().
629   // This check mirrors what FrameNeedsReflow() would have done if the reflow
630   // root didn't get in the way.
631   for (nsIFrame* dirtyFrame : mList) {
632     do {
633       if (dirtyFrame == aFrame) {
634         return true;
635       }
636       dirtyFrame = dirtyFrame->GetParent();
637     } while (dirtyFrame);
638   }
639 
640   return false;
641 }
642 
643 bool PresShell::sDisableNonTestMouseEvents = false;
644 
645 LazyLogModule PresShell::gLog("PresShell");
646 
647 TimeStamp PresShell::EventHandler::sLastInputCreated;
648 TimeStamp PresShell::EventHandler::sLastInputProcessed;
649 StaticRefPtr<Element> PresShell::EventHandler::sLastKeyDownEventTargetElement;
650 
651 bool PresShell::sProcessInteractable = false;
652 
653 static bool gVerifyReflowEnabled;
654 extern mozilla::LazyLogModule sApzMvmLog;
655 
GetVerifyReflowEnable()656 bool PresShell::GetVerifyReflowEnable() {
657 #ifdef DEBUG
658   static bool firstTime = true;
659   if (firstTime) {
660     firstTime = false;
661     char* flags = PR_GetEnv("GECKO_VERIFY_REFLOW_FLAGS");
662     if (flags) {
663       bool error = false;
664 
665       for (;;) {
666         char* comma = PL_strchr(flags, ',');
667         if (comma) *comma = '\0';
668 
669         bool found = false;
670         const VerifyReflowFlagData* flag = gFlags;
671         const VerifyReflowFlagData* limit = gFlags + NUM_VERIFY_REFLOW_FLAGS;
672         while (flag < limit) {
673           if (PL_strcasecmp(flag->name, flags) == 0) {
674             gVerifyReflowFlags |= flag->bit;
675             found = true;
676             break;
677           }
678           ++flag;
679         }
680 
681         if (!found) error = true;
682 
683         if (!comma) break;
684 
685         *comma = ',';
686         flags = comma + 1;
687       }
688 
689       if (error) ShowVerifyReflowFlags();
690     }
691 
692     if (VerifyReflowFlags::On & gVerifyReflowFlags) {
693       gVerifyReflowEnabled = true;
694 
695       printf("Note: verifyreflow is enabled");
696       if (VerifyReflowFlags::Noisy & gVerifyReflowFlags) {
697         printf(" (noisy)");
698       }
699       if (VerifyReflowFlags::All & gVerifyReflowFlags) {
700         printf(" (all)");
701       }
702       if (VerifyReflowFlags::DumpCommands & gVerifyReflowFlags) {
703         printf(" (show reflow commands)");
704       }
705       if (VerifyReflowFlags::NoisyCommands & gVerifyReflowFlags) {
706         printf(" (noisy reflow commands)");
707         if (VerifyReflowFlags::ReallyNoisyCommands & gVerifyReflowFlags) {
708           printf(" (REALLY noisy reflow commands)");
709         }
710       }
711       printf("\n");
712     }
713   }
714 #endif
715   return gVerifyReflowEnabled;
716 }
717 
SetVerifyReflowEnable(bool aEnabled)718 void PresShell::SetVerifyReflowEnable(bool aEnabled) {
719   gVerifyReflowEnabled = aEnabled;
720 }
721 
AddAutoWeakFrame(AutoWeakFrame * aWeakFrame)722 void PresShell::AddAutoWeakFrame(AutoWeakFrame* aWeakFrame) {
723   if (aWeakFrame->GetFrame()) {
724     aWeakFrame->GetFrame()->AddStateBits(NS_FRAME_EXTERNAL_REFERENCE);
725   }
726   aWeakFrame->SetPreviousWeakFrame(mAutoWeakFrames);
727   mAutoWeakFrames = aWeakFrame;
728 }
729 
AddWeakFrame(WeakFrame * aWeakFrame)730 void PresShell::AddWeakFrame(WeakFrame* aWeakFrame) {
731   if (aWeakFrame->GetFrame()) {
732     aWeakFrame->GetFrame()->AddStateBits(NS_FRAME_EXTERNAL_REFERENCE);
733   }
734   MOZ_ASSERT(!mWeakFrames.Contains(aWeakFrame));
735   mWeakFrames.Insert(aWeakFrame);
736 }
737 
RemoveAutoWeakFrame(AutoWeakFrame * aWeakFrame)738 void PresShell::RemoveAutoWeakFrame(AutoWeakFrame* aWeakFrame) {
739   if (mAutoWeakFrames == aWeakFrame) {
740     mAutoWeakFrames = aWeakFrame->GetPreviousWeakFrame();
741     return;
742   }
743   AutoWeakFrame* nextWeak = mAutoWeakFrames;
744   while (nextWeak && nextWeak->GetPreviousWeakFrame() != aWeakFrame) {
745     nextWeak = nextWeak->GetPreviousWeakFrame();
746   }
747   if (nextWeak) {
748     nextWeak->SetPreviousWeakFrame(aWeakFrame->GetPreviousWeakFrame());
749   }
750 }
751 
RemoveWeakFrame(WeakFrame * aWeakFrame)752 void PresShell::RemoveWeakFrame(WeakFrame* aWeakFrame) {
753   MOZ_ASSERT(mWeakFrames.Contains(aWeakFrame));
754   mWeakFrames.Remove(aWeakFrame);
755 }
756 
FrameSelection()757 already_AddRefed<nsFrameSelection> PresShell::FrameSelection() {
758   RefPtr<nsFrameSelection> ret = mSelection;
759   return ret.forget();
760 }
761 
762 //----------------------------------------------------------------------
763 
764 static uint32_t sNextPresShellId;
765 
766 /* static */
AccessibleCaretEnabled(nsIDocShell * aDocShell)767 bool PresShell::AccessibleCaretEnabled(nsIDocShell* aDocShell) {
768   // If the pref forces it on, then enable it.
769   if (StaticPrefs::layout_accessiblecaret_enabled()) {
770     return true;
771   }
772   // If the touch pref is on, and touch events are enabled (this depends
773   // on the specific device running), then enable it.
774   if (StaticPrefs::layout_accessiblecaret_enabled_on_touch() &&
775       dom::TouchEvent::PrefEnabled(aDocShell)) {
776     return true;
777   }
778   // Otherwise, disabled.
779   return false;
780 }
781 
PresShell(Document * aDocument)782 PresShell::PresShell(Document* aDocument)
783     : mDocument(aDocument),
784       mViewManager(nullptr),
785       mFrameManager(nullptr),
786       mAutoWeakFrames(nullptr),
787 #ifdef ACCESSIBILITY
788       mDocAccessible(nullptr),
789 #endif  // #ifdef ACCESSIBILITY
790       mCurrentEventFrame(nullptr),
791       mMouseLocation(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE),
792       mLastResolutionChangeOrigin(ResolutionChangeOrigin::Apz),
793       mPaintCount(0),
794       mAPZFocusSequenceNumber(0),
795       mCanvasBackgroundColor(NS_RGBA(0, 0, 0, 0)),
796       mActiveSuppressDisplayport(0),
797       mPresShellId(sNextPresShellId++),
798       mFontSizeInflationEmPerLine(0),
799       mFontSizeInflationMinTwips(0),
800       mFontSizeInflationLineThreshold(0),
801       mSelectionFlags(nsISelectionDisplay::DISPLAY_TEXT |
802                       nsISelectionDisplay::DISPLAY_IMAGES),
803       mChangeNestCount(0),
804       mRenderingStateFlags(RenderingStateFlags::None),
805       mInFlush(false),
806       mCaretEnabled(false),
807       mNeedLayoutFlush(true),
808       mNeedStyleFlush(true),
809       mNeedThrottledAnimationFlush(true),
810       mVisualViewportSizeSet(false),
811       mDidInitialize(false),
812       mIsDestroying(false),
813       mIsReflowing(false),
814       mIsObservingDocument(false),
815       mForbiddenToFlush(false),
816       mIsDocumentGone(false),
817       mHaveShutDown(false),
818       mPaintingSuppressed(false),
819       mLastRootReflowHadUnconstrainedBSize(false),
820       mShouldUnsuppressPainting(false),
821       mIgnoreFrameDestruction(false),
822       mIsActive(true),
823       mFrozen(false),
824       mIsFirstPaint(true),
825       mObservesMutationsForPrint(false),
826       mWasLastReflowInterrupted(false),
827       mObservingStyleFlushes(false),
828       mObservingLayoutFlushes(false),
829       mResizeEventPending(false),
830       mFontSizeInflationForceEnabled(false),
831       mFontSizeInflationDisabledInMasterProcess(false),
832       mFontSizeInflationEnabled(false),
833       mIsNeverPainting(false),
834       mResolutionUpdated(false),
835       mResolutionUpdatedByApz(false),
836       mUnderHiddenEmbedderElement(false),
837       mDocumentLoading(false),
838       mNoDelayedMouseEvents(false),
839       mNoDelayedKeyEvents(false),
840       mApproximateFrameVisibilityVisited(false),
841       mNextPaintCompressed(false),
842       mHasCSSBackgroundColor(true),
843       mIsLastChromeOnlyEscapeKeyConsumed(false),
844       mHasReceivedPaintMessage(false),
845       mIsLastKeyDownCanceled(false),
846       mHasHandledUserInput(false),
847       mForceDispatchKeyPressEventsForNonPrintableKeys(false),
848       mForceUseLegacyKeyCodeAndCharCodeValues(false),
849       mInitializedWithKeyPressEventDispatchingBlacklist(false),
850       mForceUseLegacyNonPrimaryDispatch(false),
851       mInitializedWithClickEventDispatchingBlacklist(false),
852       mMouseLocationWasSetBySynthesizedMouseEventForTests(false) {
853   MOZ_LOG(gLog, LogLevel::Debug, ("PresShell::PresShell this=%p", this));
854   MOZ_ASSERT(aDocument);
855 
856 #ifdef MOZ_REFLOW_PERF
857   mReflowCountMgr = MakeUnique<ReflowCountMgr>();
858   mReflowCountMgr->SetPresContext(mPresContext);
859   mReflowCountMgr->SetPresShell(this);
860 #endif
861   mLastOSWake = mLoadBegin = TimeStamp::Now();
862 }
863 
NS_INTERFACE_TABLE_HEAD(PresShell)864 NS_INTERFACE_TABLE_HEAD(PresShell)
865   NS_INTERFACE_TABLE_BEGIN
866     // In most cases, PresShell should be treated as concrete class, but need to
867     // QI for weak reference.  Therefore, the case needed by do_QueryReferent()
868     // should be tested first.
869     NS_INTERFACE_TABLE_ENTRY(PresShell, PresShell)
870     NS_INTERFACE_TABLE_ENTRY(PresShell, nsIDocumentObserver)
871     NS_INTERFACE_TABLE_ENTRY(PresShell, nsISelectionController)
872     NS_INTERFACE_TABLE_ENTRY(PresShell, nsISelectionDisplay)
873     NS_INTERFACE_TABLE_ENTRY(PresShell, nsIObserver)
874     NS_INTERFACE_TABLE_ENTRY(PresShell, nsISupportsWeakReference)
875     NS_INTERFACE_TABLE_ENTRY(PresShell, nsIMutationObserver)
876     NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(PresShell, nsISupports, nsIObserver)
877   NS_INTERFACE_TABLE_END
878   NS_INTERFACE_TABLE_TO_MAP_SEGUE
879 NS_INTERFACE_MAP_END
880 
881 NS_IMPL_ADDREF(PresShell)
882 NS_IMPL_RELEASE(PresShell)
883 
884 PresShell::~PresShell() {
885   MOZ_RELEASE_ASSERT(!mForbiddenToFlush,
886                      "Flag should only be set temporarily, while doing things "
887                      "that shouldn't cause destruction");
888   MOZ_LOG(gLog, LogLevel::Debug, ("PresShell::~PresShell this=%p", this));
889 
890   if (!mHaveShutDown) {
891     MOZ_ASSERT_UNREACHABLE("Someone did not call PresShell::Destroy()");
892     Destroy();
893   }
894 
895   NS_ASSERTION(mCurrentEventContentStack.Count() == 0,
896                "Huh, event content left on the stack in pres shell dtor!");
897   NS_ASSERTION(mFirstCallbackEventRequest == nullptr &&
898                    mLastCallbackEventRequest == nullptr,
899                "post-reflow queues not empty.  This means we're leaking");
900 
901   MOZ_ASSERT(mAllocatedPointers.IsEmpty(),
902              "Some pres arena objects were not freed");
903 
904   mFrameManager = nullptr;
905   mFrameConstructor = nullptr;
906 
907   mCurrentEventContent = nullptr;
908 }
909 
910 /**
911  * Initialize the presentation shell. Create view manager and style
912  * manager.
913  * Note this can't be merged into our constructor because caret initialization
914  * calls AddRef() on us.
915  */
Init(nsPresContext * aPresContext,nsViewManager * aViewManager)916 void PresShell::Init(nsPresContext* aPresContext, nsViewManager* aViewManager) {
917   MOZ_ASSERT(mDocument);
918   MOZ_ASSERT(aPresContext);
919   MOZ_ASSERT(aViewManager);
920   MOZ_ASSERT(!mViewManager, "already initialized");
921 
922   mViewManager = aViewManager;
923 
924   // mDocument is now set.  It might have a display document whose "need layout/
925   // style" flush flags are not set, but ours will be set.  To keep these
926   // consistent, call the flag setting functions to propagate those flags up
927   // to the display document.
928   SetNeedLayoutFlush();
929   SetNeedStyleFlush();
930 
931   // Create our frame constructor.
932   mFrameConstructor = MakeUnique<nsCSSFrameConstructor>(mDocument, this);
933 
934   mFrameManager = mFrameConstructor.get();
935 
936   // The document viewer owns both view manager and pres shell.
937   mViewManager->SetPresShell(this);
938 
939   // Bind the context to the presentation shell.
940   // FYI: We cannot initialize mPresContext in the constructor because we
941   //      cannot call AttachPresShell() in it and once we initialize
942   //      mPresContext, other objects may refer refresh driver or restyle
943   //      manager via mPresContext and that causes hitting MOZ_ASSERT in some
944   //      places.  Therefore, we should initialize mPresContext here with
945   //      const_cast hack since we want to guarantee that mPresContext lives
946   //      as long as the PresShell.
947   const_cast<RefPtr<nsPresContext>&>(mPresContext) = aPresContext;
948   mPresContext->AttachPresShell(this);
949 
950   mPresContext->DeviceContext()->InitFontCache();
951 
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();
956 
957   // Add the preference style sheet.
958   UpdatePreferenceStyles();
959 
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);
965   }
966 
967   mSelection = new nsFrameSelection(this, nullptr, accessibleCaretEnabled);
968 
969   // Important: this has to happen after the selection has been set up
970 #ifdef SHOW_CARET
971   // make the caret
972   mCaret = new nsCaret();
973   mCaret->Init(this);
974   mOriginalCaret = mCaret;
975 
976   // SetCaretEnabled(true);       // make it show in browser windows
977 #endif
978   // set up selection to be displayed in document
979   // Don't enable selection for print media
980   nsPresContext::nsPresContextType type = mPresContext->Type();
981   if (type != nsPresContext::eContext_PrintPreview &&
982       type != nsPresContext::eContext_Print) {
983     SetDisplaySelection(nsISelectionController::SELECTION_DISABLED);
984   }
985 
986   if (gMaxRCProcessingTime == -1) {
987     gMaxRCProcessingTime =
988         Preferences::GetInt("layout.reflow.timeslice", NS_MAX_REFLOW_TIME);
989   }
990 
991   if (nsStyleSheetService* ss = nsStyleSheetService::GetInstance()) {
992     ss->RegisterPresShell(this);
993   }
994 
995   {
996     nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
997     if (os) {
998       os->AddObserver(this, "memory-pressure", false);
999       os->AddObserver(this, NS_WIDGET_WAKE_OBSERVER_TOPIC, false);
1000       if (XRE_IsParentProcess() && !sProcessInteractable) {
1001         os->AddObserver(this, "sessionstore-one-or-no-tab-restored", false);
1002       }
1003       os->AddObserver(this, "font-info-updated", false);
1004       os->AddObserver(this, "look-and-feel-changed", false);
1005     }
1006   }
1007 
1008 #ifdef MOZ_REFLOW_PERF
1009   if (mReflowCountMgr) {
1010     bool paintFrameCounts =
1011         Preferences::GetBool("layout.reflow.showframecounts");
1012 
1013     bool dumpFrameCounts =
1014         Preferences::GetBool("layout.reflow.dumpframecounts");
1015 
1016     bool dumpFrameByFrameCounts =
1017         Preferences::GetBool("layout.reflow.dumpframebyframecounts");
1018 
1019     mReflowCountMgr->SetDumpFrameCounts(dumpFrameCounts);
1020     mReflowCountMgr->SetDumpFrameByFrameCounts(dumpFrameByFrameCounts);
1021     mReflowCountMgr->SetPaintFrameCounts(paintFrameCounts);
1022   }
1023 #endif
1024 
1025   if (mDocument->HasAnimationController()) {
1026     SMILAnimationController* animCtrl = mDocument->GetAnimationController();
1027     animCtrl->NotifyRefreshDriverCreated(GetPresContext()->RefreshDriver());
1028   }
1029 
1030   for (DocumentTimeline* timeline : mDocument->Timelines()) {
1031     timeline->NotifyRefreshDriverCreated(GetPresContext()->RefreshDriver());
1032   }
1033 
1034   // Get our activeness from the docShell.
1035   ActivenessMaybeChanged();
1036 
1037   // Setup our font inflation preferences.
1038   mFontSizeInflationEmPerLine = StaticPrefs::font_size_inflation_emPerLine();
1039   mFontSizeInflationMinTwips = StaticPrefs::font_size_inflation_minTwips();
1040   mFontSizeInflationLineThreshold =
1041       StaticPrefs::font_size_inflation_lineThreshold();
1042   mFontSizeInflationForceEnabled =
1043       StaticPrefs::font_size_inflation_forceEnabled();
1044   mFontSizeInflationDisabledInMasterProcess =
1045       StaticPrefs::font_size_inflation_disabledInMasterProcess();
1046   // We'll compute the font size inflation state in Initialize(), when we know
1047   // the document type.
1048 
1049   mTouchManager.Init(this, mDocument);
1050 
1051   if (mPresContext->IsRootContentDocumentCrossProcess()) {
1052     mZoomConstraintsClient = new ZoomConstraintsClient();
1053     mZoomConstraintsClient->Init(this, mDocument);
1054 
1055     // We call this to create mMobileViewportManager, if it is needed.
1056     MaybeRecreateMobileViewportManager(false);
1057   }
1058 
1059   if (nsCOMPtr<nsIDocShell> docShell = mPresContext->GetDocShell()) {
1060     BrowsingContext* bc = docShell->GetBrowsingContext();
1061     bool embedderFrameIsHidden = true;
1062     if (Element* embedderElement = bc->GetEmbedderElement()) {
1063       if (auto embedderFrame = embedderElement->GetPrimaryFrame()) {
1064         embedderFrameIsHidden = !embedderFrame->StyleVisibility()->IsVisible();
1065       }
1066     }
1067 
1068     if (BrowsingContext* parent = bc->GetParent()) {
1069       if (nsCOMPtr<nsIDocShell> parentDocShell = parent->GetDocShell()) {
1070         if (PresShell* parentPresShell = parentDocShell->GetPresShell()) {
1071           mUnderHiddenEmbedderElement =
1072               parentPresShell->IsUnderHiddenEmbedderElement() ||
1073               embedderFrameIsHidden;
1074         }
1075       }
1076     }
1077   }
1078 }
1079 
1080 enum TextPerfLogType { eLog_reflow, eLog_loaddone, eLog_totals };
1081 
LogTextPerfStats(gfxTextPerfMetrics * aTextPerf,PresShell * aPresShell,const gfxTextPerfMetrics::TextCounts & aCounts,float aTime,TextPerfLogType aLogType,const char * aURL)1082 static void LogTextPerfStats(gfxTextPerfMetrics* aTextPerf,
1083                              PresShell* aPresShell,
1084                              const gfxTextPerfMetrics::TextCounts& aCounts,
1085                              float aTime, TextPerfLogType aLogType,
1086                              const char* aURL) {
1087   LogModule* tpLog = gfxPlatform::GetLog(eGfxLog_textperf);
1088 
1089   // ignore XUL contexts unless at debug level
1090   mozilla::LogLevel logLevel = LogLevel::Warning;
1091   if (aCounts.numContentTextRuns == 0) {
1092     logLevel = LogLevel::Debug;
1093   }
1094 
1095   if (!MOZ_LOG_TEST(tpLog, logLevel)) {
1096     return;
1097   }
1098 
1099   char prefix[256];
1100 
1101   switch (aLogType) {
1102     case eLog_reflow:
1103       SprintfLiteral(prefix, "(textperf-reflow) %p time-ms: %7.0f", aPresShell,
1104                      aTime);
1105       break;
1106     case eLog_loaddone:
1107       SprintfLiteral(prefix, "(textperf-loaddone) %p time-ms: %7.0f",
1108                      aPresShell, aTime);
1109       break;
1110     default:
1111       MOZ_ASSERT(aLogType == eLog_totals, "unknown textperf log type");
1112       SprintfLiteral(prefix, "(textperf-totals) %p", aPresShell);
1113   }
1114 
1115   double hitRatio = 0.0;
1116   uint32_t lookups = aCounts.wordCacheHit + aCounts.wordCacheMiss;
1117   if (lookups) {
1118     hitRatio = double(aCounts.wordCacheHit) / double(lookups);
1119   }
1120 
1121   if (aLogType == eLog_loaddone) {
1122     MOZ_LOG(
1123         tpLog, logLevel,
1124         ("%s reflow: %d chars: %d "
1125          "[%s] "
1126          "content-textruns: %d chrome-textruns: %d "
1127          "max-textrun-len: %d "
1128          "word-cache-lookups: %d word-cache-hit-ratio: %4.3f "
1129          "word-cache-space: %d word-cache-long: %d "
1130          "pref-fallbacks: %d system-fallbacks: %d "
1131          "textruns-const: %d textruns-destr: %d "
1132          "generic-lookups: %d "
1133          "cumulative-textruns-destr: %d\n",
1134          prefix, aTextPerf->reflowCount, aCounts.numChars, (aURL ? aURL : ""),
1135          aCounts.numContentTextRuns, aCounts.numChromeTextRuns,
1136          aCounts.maxTextRunLen, lookups, hitRatio, aCounts.wordCacheSpaceRules,
1137          aCounts.wordCacheLong, aCounts.fallbackPrefs, aCounts.fallbackSystem,
1138          aCounts.textrunConst, aCounts.textrunDestr, aCounts.genericLookups,
1139          aTextPerf->cumulative.textrunDestr));
1140   } else {
1141     MOZ_LOG(
1142         tpLog, logLevel,
1143         ("%s reflow: %d chars: %d "
1144          "content-textruns: %d chrome-textruns: %d "
1145          "max-textrun-len: %d "
1146          "word-cache-lookups: %d word-cache-hit-ratio: %4.3f "
1147          "word-cache-space: %d word-cache-long: %d "
1148          "pref-fallbacks: %d system-fallbacks: %d "
1149          "textruns-const: %d textruns-destr: %d "
1150          "generic-lookups: %d "
1151          "cumulative-textruns-destr: %d\n",
1152          prefix, aTextPerf->reflowCount, aCounts.numChars,
1153          aCounts.numContentTextRuns, aCounts.numChromeTextRuns,
1154          aCounts.maxTextRunLen, lookups, hitRatio, aCounts.wordCacheSpaceRules,
1155          aCounts.wordCacheLong, aCounts.fallbackPrefs, aCounts.fallbackSystem,
1156          aCounts.textrunConst, aCounts.textrunDestr, aCounts.genericLookups,
1157          aTextPerf->cumulative.textrunDestr));
1158   }
1159 }
1160 
InRDMPane()1161 bool PresShell::InRDMPane() {
1162   if (Document* doc = GetDocument()) {
1163     if (BrowsingContext* bc = doc->GetBrowsingContext()) {
1164       return bc->InRDMPane();
1165     }
1166   }
1167   return false;
1168 }
1169 
Destroy()1170 void PresShell::Destroy() {
1171   // Do not add code before this line please!
1172   if (mHaveShutDown) {
1173     return;
1174   }
1175 
1176   NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
1177                "destroy called on presshell while scripts not blocked");
1178 
1179   AUTO_PROFILER_LABEL("PresShell::Destroy", LAYOUT);
1180 
1181   // Try to determine if the page is the user had a meaningful opportunity to
1182   // zoom this page. This is not 100% accurate but should be "good enough" for
1183   // telemetry purposes.
1184   auto isUserZoomablePage = [&]() -> bool {
1185     if (mIsFirstPaint) {
1186       // Page was never painted, so it wasn't zoomable by the user. We get a
1187       // handful of these "transient" presShells.
1188       return false;
1189     }
1190     if (!mPresContext->IsRootContentDocumentCrossProcess()) {
1191       // Not a root content document, so APZ doesn't support zooming it.
1192       return false;
1193     }
1194     if (InRDMPane()) {
1195       // Responsive design mode is a special case that we want to ignore here.
1196       return false;
1197     }
1198     if (mDocument && mDocument->IsInitialDocument()) {
1199       // Ignore initial about:blank page loads
1200       return false;
1201     }
1202     if (XRE_IsContentProcess() &&
1203         IsExtensionRemoteType(ContentChild::GetSingleton()->GetRemoteType())) {
1204       // Also omit presShells from the extension process because they sometimes
1205       // can't be zoomed by the user.
1206       return false;
1207     }
1208     // Otherwise assume the page is user-zoomable.
1209     return true;
1210   };
1211   if (isUserZoomablePage()) {
1212     Telemetry::Accumulate(Telemetry::APZ_ZOOM_ACTIVITY,
1213                           IsResolutionUpdatedByApz());
1214   }
1215 
1216   // dump out cumulative text perf metrics
1217   gfxTextPerfMetrics* tp;
1218   if (mPresContext && (tp = mPresContext->GetTextPerfMetrics())) {
1219     tp->Accumulate();
1220     if (tp->cumulative.numChars > 0) {
1221       LogTextPerfStats(tp, this, tp->cumulative, 0.0, eLog_totals, nullptr);
1222     }
1223   }
1224   if (mPresContext) {
1225     if (gfxUserFontSet* fs = mPresContext->GetUserFontSet()) {
1226       uint32_t fontCount;
1227       uint64_t fontSize;
1228       fs->GetLoadStatistics(fontCount, fontSize);
1229       Telemetry::Accumulate(Telemetry::WEBFONT_PER_PAGE, fontCount);
1230       Telemetry::Accumulate(Telemetry::WEBFONT_SIZE_PER_PAGE,
1231                             uint32_t(fontSize / 1024));
1232     } else {
1233       Telemetry::Accumulate(Telemetry::WEBFONT_PER_PAGE, 0);
1234       Telemetry::Accumulate(Telemetry::WEBFONT_SIZE_PER_PAGE, 0);
1235     }
1236     const auto* stats = mPresContext->GetFontMatchingStats();
1237     if (stats) {
1238       Document* doc = GetDocument();
1239       if (doc && doc->IsContentDocument()) {
1240         nsIURI* uri = doc->GetDocumentURI();
1241         nsAutoCString path;
1242         if (uri && !uri->SchemeIs("about") && !uri->SchemeIs("chrome") &&
1243             !uri->SchemeIs("resource") &&
1244             !(uri->SchemeIs("moz-extension") &&
1245               (NS_SUCCEEDED(uri->GetFilePath(path)) &&
1246                StringEndsWith(path, "/_generated_background_page.html"_ns)))) {
1247           Telemetry::Accumulate(Telemetry::BASE_FONT_FAMILIES_PER_PAGE,
1248                                 stats->mBaseFonts);
1249           Telemetry::Accumulate(Telemetry::LANGPACK_FONT_FAMILIES_PER_PAGE,
1250                                 stats->mLangPackFonts);
1251           Telemetry::Accumulate(Telemetry::USER_FONT_FAMILIES_PER_PAGE,
1252                                 stats->mUserFonts);
1253           Telemetry::Accumulate(Telemetry::WEB_FONT_FAMILIES_PER_PAGE,
1254                                 stats->mWebFonts);
1255           Telemetry::Accumulate(
1256               Telemetry::FALLBACK_TO_PREFS_FONT,
1257               bool(stats->mFallbacks & FallbackTypes::FallbackToPrefsFont));
1258           Telemetry::Accumulate(
1259               Telemetry::FALLBACK_TO_BASE_FONT,
1260               bool(stats->mFallbacks & FallbackTypes::FallbackToBaseFont));
1261           Telemetry::Accumulate(
1262               Telemetry::FALLBACK_TO_LANGPACK_FONT,
1263               bool(stats->mFallbacks & FallbackTypes::FallbackToLangPackFont));
1264           Telemetry::Accumulate(
1265               Telemetry::FALLBACK_TO_USER_FONT,
1266               bool(stats->mFallbacks & FallbackTypes::FallbackToUserFont));
1267           Telemetry::Accumulate(
1268               Telemetry::MISSING_FONT,
1269               bool(stats->mFallbacks & FallbackTypes::MissingFont));
1270           Telemetry::Accumulate(
1271               Telemetry::MISSING_FONT_LANGPACK,
1272               bool(stats->mFallbacks & FallbackTypes::MissingFontLangPack));
1273           Telemetry::Accumulate(
1274               Telemetry::MISSING_FONT_USER,
1275               bool(stats->mFallbacks & FallbackTypes::MissingFontUser));
1276         }
1277       }
1278     }
1279     mPresContext->CancelManagedPostRefreshObservers();
1280   }
1281 
1282 #ifdef MOZ_REFLOW_PERF
1283   DumpReflows();
1284   mReflowCountMgr = nullptr;
1285 #endif
1286 
1287   if (mZoomConstraintsClient) {
1288     mZoomConstraintsClient->Destroy();
1289     mZoomConstraintsClient = nullptr;
1290   }
1291   if (mMobileViewportManager) {
1292     mMobileViewportManager->Destroy();
1293     mMobileViewportManager = nullptr;
1294     mMVMContext = nullptr;
1295   }
1296 
1297 #ifdef ACCESSIBILITY
1298   if (mDocAccessible) {
1299 #  ifdef DEBUG
1300     if (a11y::logging::IsEnabled(a11y::logging::eDocDestroy))
1301       a11y::logging::DocDestroy("presshell destroyed", mDocument);
1302 #  endif
1303 
1304     mDocAccessible->Shutdown();
1305     mDocAccessible = nullptr;
1306   }
1307 #endif  // ACCESSIBILITY
1308 
1309   MaybeReleaseCapturingContent();
1310 
1311   EventHandler::OnPresShellDestroy(mDocument);
1312 
1313   if (mContentToScrollTo) {
1314     mContentToScrollTo->RemoveProperty(nsGkAtoms::scrolling);
1315     mContentToScrollTo = nullptr;
1316   }
1317 
1318   if (mPresContext) {
1319     // We need to notify the destroying the nsPresContext to ESM for
1320     // suppressing to use from ESM.
1321     mPresContext->EventStateManager()->NotifyDestroyPresContext(mPresContext);
1322   }
1323 
1324   if (nsStyleSheetService* ss = nsStyleSheetService::GetInstance()) {
1325     ss->UnregisterPresShell(this);
1326   }
1327 
1328   {
1329     nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
1330     if (os) {
1331       os->RemoveObserver(this, "memory-pressure");
1332       os->RemoveObserver(this, NS_WIDGET_WAKE_OBSERVER_TOPIC);
1333       if (XRE_IsParentProcess()) {
1334         os->RemoveObserver(this, "sessionstore-one-or-no-tab-restored");
1335       }
1336       os->RemoveObserver(this, "font-info-updated");
1337       os->RemoveObserver(this, "look-and-feel-changed");
1338     }
1339   }
1340 
1341   // If our paint suppression timer is still active, kill it.
1342   CancelPaintSuppressionTimer();
1343 
1344   // Same for our reflow continuation timer
1345   if (mReflowContinueTimer) {
1346     mReflowContinueTimer->Cancel();
1347     mReflowContinueTimer = nullptr;
1348   }
1349 
1350   if (mDelayedPaintTimer) {
1351     mDelayedPaintTimer->Cancel();
1352     mDelayedPaintTimer = nullptr;
1353   }
1354 
1355   mSynthMouseMoveEvent.Revoke();
1356 
1357   mUpdateApproximateFrameVisibilityEvent.Revoke();
1358 
1359   ClearApproximatelyVisibleFramesList(Some(OnNonvisible::DiscardImages));
1360 
1361   if (mCaret) {
1362     mCaret->Terminate();
1363     mCaret = nullptr;
1364   }
1365 
1366   mFocusedFrameSelection = nullptr;
1367 
1368   if (mSelection) {
1369     RefPtr<nsFrameSelection> frameSelection = mSelection;
1370     frameSelection->DisconnectFromPresShell();
1371   }
1372 
1373   // release our pref style sheet, if we have one still
1374   //
1375   // TODO(emilio): Should we move the preference sheet tracking to the Document?
1376   RemovePreferenceStyles();
1377 
1378   mIsDestroying = true;
1379 
1380   // We can't release all the event content in
1381   // mCurrentEventContentStack here since there might be code on the
1382   // stack that will release the event content too. Double release
1383   // bad!
1384 
1385   // The frames will be torn down, so remove them from the current
1386   // event frame stack (since they'd be dangling references if we'd
1387   // leave them in) and null out the mCurrentEventFrame pointer as
1388   // well.
1389 
1390   mCurrentEventFrame = nullptr;
1391 
1392   int32_t i, count = mCurrentEventFrameStack.Length();
1393   for (i = 0; i < count; i++) {
1394     mCurrentEventFrameStack[i] = nullptr;
1395   }
1396 
1397   mFramesToDirty.Clear();
1398   mPendingScrollAnchorSelection.Clear();
1399   mPendingScrollAnchorAdjustment.Clear();
1400 
1401   if (mViewManager) {
1402     // Clear the view manager's weak pointer back to |this| in case it
1403     // was leaked.
1404     mViewManager->SetPresShell(nullptr);
1405     mViewManager = nullptr;
1406   }
1407 
1408   nsRefreshDriver* rd = GetPresContext()->RefreshDriver();
1409 
1410   // This shell must be removed from the document before the frame
1411   // hierarchy is torn down to avoid finding deleted frames through
1412   // this presshell while the frames are being torn down
1413   if (mDocument) {
1414     NS_ASSERTION(mDocument->GetPresShell() == this, "Wrong shell?");
1415     mDocument->ClearServoRestyleRoot();
1416     mDocument->DeletePresShell();
1417 
1418     if (mDocument->HasAnimationController()) {
1419       mDocument->GetAnimationController()->NotifyRefreshDriverDestroying(rd);
1420     }
1421     for (DocumentTimeline* timeline : mDocument->Timelines()) {
1422       timeline->NotifyRefreshDriverDestroying(rd);
1423     }
1424   }
1425 
1426   if (mPresContext) {
1427     rd->CancelPendingAnimationEvents(mPresContext->AnimationEventDispatcher());
1428   }
1429 
1430   // Revoke any pending events.  We need to do this and cancel pending reflows
1431   // before we destroy the frame manager, since apparently frame destruction
1432   // sometimes spins the event queue when plug-ins are involved(!).
1433   // XXXmats is this still needed now that plugins are gone?
1434   StopObservingRefreshDriver();
1435 
1436   if (rd->GetPresContext() == GetPresContext()) {
1437     rd->RevokeViewManagerFlush();
1438     rd->ClearHasScheduleFlush();
1439   }
1440 
1441   CancelAllPendingReflows();
1442   CancelPostedReflowCallbacks();
1443 
1444   // Destroy the frame manager. This will destroy the frame hierarchy
1445   mFrameConstructor->WillDestroyFrameTree();
1446 
1447   NS_WARNING_ASSERTION(!mAutoWeakFrames && mWeakFrames.IsEmpty(),
1448                        "Weak frames alive after destroying FrameManager");
1449   while (mAutoWeakFrames) {
1450     mAutoWeakFrames->Clear(this);
1451   }
1452   const nsTArray<WeakFrame*> weakFrames = ToArray(mWeakFrames);
1453   for (WeakFrame* weakFrame : weakFrames) {
1454     weakFrame->Clear(this);
1455   }
1456 
1457   // Terminate AccessibleCaretEventHub after tearing down the frame tree so that
1458   // we don't need to remove caret element's frame in
1459   // AccessibleCaret::RemoveCaretElement().
1460   if (mAccessibleCaretEventHub) {
1461     mAccessibleCaretEventHub->Terminate();
1462     mAccessibleCaretEventHub = nullptr;
1463   }
1464 
1465   if (mPresContext) {
1466     // We hold a reference to the pres context, and it holds a weak link back
1467     // to us. To avoid the pres context having a dangling reference, set its
1468     // pres shell to nullptr
1469     mPresContext->DetachPresShell();
1470   }
1471 
1472   mHaveShutDown = true;
1473 
1474   mTouchManager.Destroy();
1475 }
1476 
StopObservingRefreshDriver()1477 void PresShell::StopObservingRefreshDriver() {
1478   nsRefreshDriver* rd = mPresContext->RefreshDriver();
1479   if (mResizeEventPending) {
1480     rd->RemoveResizeEventFlushObserver(this);
1481   }
1482   if (mObservingLayoutFlushes) {
1483     rd->RemoveLayoutFlushObserver(this);
1484   }
1485   if (mObservingStyleFlushes) {
1486     rd->RemoveStyleFlushObserver(this);
1487   }
1488 }
1489 
StartObservingRefreshDriver()1490 void PresShell::StartObservingRefreshDriver() {
1491   nsRefreshDriver* rd = mPresContext->RefreshDriver();
1492   if (mResizeEventPending) {
1493     rd->AddResizeEventFlushObserver(this);
1494   }
1495   if (mObservingLayoutFlushes) {
1496     rd->AddLayoutFlushObserver(this);
1497   }
1498   if (mObservingStyleFlushes) {
1499     rd->AddStyleFlushObserver(this);
1500   }
1501 }
1502 
GetRefreshDriver() const1503 nsRefreshDriver* PresShell::GetRefreshDriver() const {
1504   return mPresContext ? mPresContext->RefreshDriver() : nullptr;
1505 }
1506 
SetAuthorStyleDisabled(bool aStyleDisabled)1507 void PresShell::SetAuthorStyleDisabled(bool aStyleDisabled) {
1508   if (aStyleDisabled != StyleSet()->GetAuthorStyleDisabled()) {
1509     StyleSet()->SetAuthorStyleDisabled(aStyleDisabled);
1510     mDocument->ApplicableStylesChanged();
1511 
1512     nsCOMPtr<nsIObserverService> observerService =
1513         mozilla::services::GetObserverService();
1514     if (observerService) {
1515       observerService->NotifyObservers(
1516           ToSupports(mDocument), "author-style-disabled-changed", nullptr);
1517     }
1518   }
1519 }
1520 
GetAuthorStyleDisabled() const1521 bool PresShell::GetAuthorStyleDisabled() const {
1522   return StyleSet()->GetAuthorStyleDisabled();
1523 }
1524 
UpdatePreferenceStyles()1525 void PresShell::UpdatePreferenceStyles() {
1526   if (!mDocument) {
1527     return;
1528   }
1529 
1530   // If the document doesn't have a window there's no need to notify
1531   // its presshell about changes to preferences since the document is
1532   // in a state where it doesn't matter any more (see
1533   // nsDocumentViewer::Close()).
1534   if (!mDocument->GetWindow()) {
1535     return;
1536   }
1537 
1538   // Documents in chrome shells do not have any preference style rules applied.
1539   if (nsContentUtils::IsInChromeDocshell(mDocument)) {
1540     return;
1541   }
1542 
1543   PreferenceSheet::EnsureInitialized();
1544   auto* cache = GlobalStyleSheetCache::Singleton();
1545 
1546   RefPtr<StyleSheet> newPrefSheet =
1547       PreferenceSheet::ShouldUseChromePrefs(*mDocument)
1548           ? cache->ChromePreferenceSheet()
1549           : cache->ContentPreferenceSheet();
1550 
1551   if (mPrefStyleSheet == newPrefSheet) {
1552     return;
1553   }
1554 
1555   RemovePreferenceStyles();
1556 
1557   // NOTE(emilio): This sheet is added as an agent sheet, because we don't want
1558   // it to be modifiable from devtools and similar, see bugs 1239336 and
1559   // 1436782. I think it conceptually should be a user sheet, and could be
1560   // without too much trouble I'd think.
1561   StyleSet()->AppendStyleSheet(*newPrefSheet);
1562   mPrefStyleSheet = newPrefSheet;
1563 }
1564 
RemovePreferenceStyles()1565 void PresShell::RemovePreferenceStyles() {
1566   if (mPrefStyleSheet) {
1567     StyleSet()->RemoveStyleSheet(*mPrefStyleSheet);
1568     mPrefStyleSheet = nullptr;
1569   }
1570 }
1571 
AddUserSheet(StyleSheet * aSheet)1572 void PresShell::AddUserSheet(StyleSheet* aSheet) {
1573   // Make sure this does what nsDocumentViewer::CreateStyleSet does wrt
1574   // ordering. We want this new sheet to come after all the existing stylesheet
1575   // service sheets (which are at the start), but before other user sheets; see
1576   // nsIStyleSheetService.idl for the ordering.
1577 
1578   nsStyleSheetService* sheetService = nsStyleSheetService::GetInstance();
1579   nsTArray<RefPtr<StyleSheet>>& userSheets = *sheetService->UserStyleSheets();
1580 
1581   // Search for the place to insert the new user sheet. Since all of the
1582   // stylesheet service provided user sheets should be at the start of the style
1583   // set's list, and aSheet should be at the end of userSheets. Given that, we
1584   // can find the right place to insert the new sheet based on the length of
1585   // userSheets.
1586   MOZ_ASSERT(aSheet);
1587   MOZ_ASSERT(userSheets.LastElement() == aSheet);
1588 
1589   size_t index = userSheets.Length() - 1;
1590 
1591   // Assert that all of userSheets (except for the last, new element) matches up
1592   // with what's in the style set.
1593   for (size_t i = 0; i < index; ++i) {
1594     MOZ_ASSERT(StyleSet()->SheetAt(StyleOrigin::User, i) == userSheets[i]);
1595   }
1596 
1597   if (index == static_cast<size_t>(StyleSet()->SheetCount(StyleOrigin::User))) {
1598     StyleSet()->AppendStyleSheet(*aSheet);
1599   } else {
1600     StyleSheet* ref = StyleSet()->SheetAt(StyleOrigin::User, index);
1601     StyleSet()->InsertStyleSheetBefore(*aSheet, *ref);
1602   }
1603 
1604   mDocument->ApplicableStylesChanged();
1605 }
1606 
AddAgentSheet(StyleSheet * aSheet)1607 void PresShell::AddAgentSheet(StyleSheet* aSheet) {
1608   // Make sure this does what nsDocumentViewer::CreateStyleSet does
1609   // wrt ordering.
1610   StyleSet()->AppendStyleSheet(*aSheet);
1611   mDocument->ApplicableStylesChanged();
1612 }
1613 
AddAuthorSheet(StyleSheet * aSheet)1614 void PresShell::AddAuthorSheet(StyleSheet* aSheet) {
1615   // Document specific "additional" Author sheets should be stronger than the
1616   // ones added with the StyleSheetService.
1617   StyleSheet* firstAuthorSheet = mDocument->GetFirstAdditionalAuthorSheet();
1618   if (firstAuthorSheet) {
1619     StyleSet()->InsertStyleSheetBefore(*aSheet, *firstAuthorSheet);
1620   } else {
1621     StyleSet()->AppendStyleSheet(*aSheet);
1622   }
1623 
1624   mDocument->ApplicableStylesChanged();
1625 }
1626 
SelectionWillTakeFocus()1627 void PresShell::SelectionWillTakeFocus() {
1628   if (mSelection) {
1629     FrameSelectionWillTakeFocus(*mSelection);
1630   }
1631 }
1632 
SelectionWillLoseFocus()1633 void PresShell::SelectionWillLoseFocus() {
1634   // Do nothing, the main selection is the default focused selection.
1635 }
1636 
1637 // Selection repainting code relies on selection offsets being properly
1638 // adjusted (see bug 1626291), so we need to wait until the DOM is finished
1639 // notifying.
RepaintNormalSelectionWhenSafe(nsFrameSelection & aFrameSelection)1640 static void RepaintNormalSelectionWhenSafe(nsFrameSelection& aFrameSelection) {
1641   if (nsContentUtils::IsSafeToRunScript()) {
1642     aFrameSelection.RepaintSelection(SelectionType::eNormal);
1643     return;
1644   }
1645 
1646   // Note that importantly we don't defer changing the DisplaySelection. That'd
1647   // be potentially racy with other code that may change it.
1648   nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
1649       "RepaintNormalSelectionWhenSafe",
1650       [sel = RefPtr<nsFrameSelection>(&aFrameSelection)] {
1651         sel->RepaintSelection(SelectionType::eNormal);
1652       }));
1653 }
1654 
FrameSelectionWillLoseFocus(nsFrameSelection & aFrameSelection)1655 void PresShell::FrameSelectionWillLoseFocus(nsFrameSelection& aFrameSelection) {
1656   if (mFocusedFrameSelection != &aFrameSelection) {
1657     return;
1658   }
1659 
1660   // Do nothing, the main selection is the default focused selection.
1661   if (&aFrameSelection == mSelection) {
1662     return;
1663   }
1664 
1665   RefPtr<nsFrameSelection> old = std::move(mFocusedFrameSelection);
1666   MOZ_ASSERT(!mFocusedFrameSelection);
1667 
1668   if (old->GetDisplaySelection() != nsISelectionController::SELECTION_HIDDEN) {
1669     old->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN);
1670     RepaintNormalSelectionWhenSafe(*old);
1671   }
1672 
1673   if (mSelection) {
1674     FrameSelectionWillTakeFocus(*mSelection);
1675   }
1676 }
1677 
FrameSelectionWillTakeFocus(nsFrameSelection & aFrameSelection)1678 void PresShell::FrameSelectionWillTakeFocus(nsFrameSelection& aFrameSelection) {
1679   if (mFocusedFrameSelection == &aFrameSelection) {
1680 #ifdef XP_MACOSX
1681     // FIXME: Mac needs to update the global selection cache, even if the
1682     // document's focused selection doesn't change, and this is currently done
1683     // from RepaintSelection. Maybe we should move part of the global selection
1684     // handling here, or something of that sort, unclear.
1685     RepaintNormalSelectionWhenSafe(aFrameSelection);
1686 #endif
1687     return;
1688   }
1689 
1690   RefPtr<nsFrameSelection> old = std::move(mFocusedFrameSelection);
1691   mFocusedFrameSelection = &aFrameSelection;
1692 
1693   if (old &&
1694       old->GetDisplaySelection() != nsISelectionController::SELECTION_HIDDEN) {
1695     old->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN);
1696     RepaintNormalSelectionWhenSafe(*old);
1697   }
1698 
1699   if (aFrameSelection.GetDisplaySelection() !=
1700       nsISelectionController::SELECTION_ON) {
1701     aFrameSelection.SetDisplaySelection(nsISelectionController::SELECTION_ON);
1702     RepaintNormalSelectionWhenSafe(aFrameSelection);
1703   }
1704 }
1705 
1706 NS_IMETHODIMP
SetDisplaySelection(int16_t aToggle)1707 PresShell::SetDisplaySelection(int16_t aToggle) {
1708   RefPtr<nsFrameSelection> frameSelection = mSelection;
1709   frameSelection->SetDisplaySelection(aToggle);
1710   return NS_OK;
1711 }
1712 
1713 NS_IMETHODIMP
GetDisplaySelection(int16_t * aToggle)1714 PresShell::GetDisplaySelection(int16_t* aToggle) {
1715   RefPtr<nsFrameSelection> frameSelection = mSelection;
1716   *aToggle = frameSelection->GetDisplaySelection();
1717   return NS_OK;
1718 }
1719 
1720 NS_IMETHODIMP
GetSelectionFromScript(RawSelectionType aRawSelectionType,Selection ** aSelection)1721 PresShell::GetSelectionFromScript(RawSelectionType aRawSelectionType,
1722                                   Selection** aSelection) {
1723   if (!aSelection || !mSelection) return NS_ERROR_NULL_POINTER;
1724 
1725   RefPtr<nsFrameSelection> frameSelection = mSelection;
1726   RefPtr<Selection> selection =
1727       frameSelection->GetSelection(ToSelectionType(aRawSelectionType));
1728 
1729   if (!selection) {
1730     return NS_ERROR_INVALID_ARG;
1731   }
1732 
1733   selection.forget(aSelection);
1734   return NS_OK;
1735 }
1736 
GetSelection(RawSelectionType aRawSelectionType)1737 Selection* PresShell::GetSelection(RawSelectionType aRawSelectionType) {
1738   if (!mSelection) {
1739     return nullptr;
1740   }
1741 
1742   RefPtr<nsFrameSelection> frameSelection = mSelection;
1743   return frameSelection->GetSelection(ToSelectionType(aRawSelectionType));
1744 }
1745 
GetCurrentSelection(SelectionType aSelectionType)1746 Selection* PresShell::GetCurrentSelection(SelectionType aSelectionType) {
1747   if (!mSelection) {
1748     return nullptr;
1749   }
1750 
1751   RefPtr<nsFrameSelection> frameSelection = mSelection;
1752   return frameSelection->GetSelection(aSelectionType);
1753 }
1754 
GetLastFocusedFrameSelection()1755 nsFrameSelection* PresShell::GetLastFocusedFrameSelection() {
1756   return mFocusedFrameSelection ? mFocusedFrameSelection : mSelection;
1757 }
1758 
1759 NS_IMETHODIMP
ScrollSelectionIntoView(RawSelectionType aRawSelectionType,SelectionRegion aRegion,int16_t aFlags)1760 PresShell::ScrollSelectionIntoView(RawSelectionType aRawSelectionType,
1761                                    SelectionRegion aRegion, int16_t aFlags) {
1762   if (!mSelection) return NS_ERROR_NULL_POINTER;
1763 
1764   RefPtr<nsFrameSelection> frameSelection = mSelection;
1765   return frameSelection->ScrollSelectionIntoView(
1766       ToSelectionType(aRawSelectionType), aRegion, aFlags);
1767 }
1768 
1769 NS_IMETHODIMP
RepaintSelection(RawSelectionType aRawSelectionType)1770 PresShell::RepaintSelection(RawSelectionType aRawSelectionType) {
1771   if (!mSelection) {
1772     return NS_ERROR_NULL_POINTER;
1773   }
1774 
1775   if (MOZ_UNLIKELY(mIsDestroying)) {
1776     return NS_OK;
1777   }
1778 
1779   RefPtr<nsFrameSelection> frameSelection = mSelection;
1780   return frameSelection->RepaintSelection(ToSelectionType(aRawSelectionType));
1781 }
1782 
1783 // Make shell be a document observer
BeginObservingDocument()1784 void PresShell::BeginObservingDocument() {
1785   if (mDocument && !mIsDestroying) {
1786     mIsObservingDocument = true;
1787     if (mIsDocumentGone) {
1788       NS_WARNING(
1789           "Adding a presshell that was disconnected from the document "
1790           "as a document observer?  Sounds wrong...");
1791       mIsDocumentGone = false;
1792     }
1793   }
1794 }
1795 
1796 // Make shell stop being a document observer
EndObservingDocument()1797 void PresShell::EndObservingDocument() {
1798   // XXXbz do we need to tell the frame constructor that the document
1799   // is gone, perhaps?  Except for printing it's NOT gone, sometimes.
1800   mIsDocumentGone = true;
1801   mIsObservingDocument = false;
1802 }
1803 
1804 #ifdef DEBUG_kipp
1805 char* nsPresShell_ReflowStackPointerTop;
1806 #endif
1807 
InitPaintSuppressionTimer()1808 void PresShell::InitPaintSuppressionTimer() {
1809   // Default to PAINTLOCK_EVENT_DELAY if we can't get the pref value.
1810   Document* doc = mDocument->GetDisplayDocument()
1811                       ? mDocument->GetDisplayDocument()
1812                       : mDocument.get();
1813   const bool inProcess = !doc->GetBrowsingContext() ||
1814                          doc->GetBrowsingContext()->Top()->IsInProcess();
1815   int32_t delay = inProcess
1816                       ? StaticPrefs::nglayout_initialpaint_delay()
1817                       : StaticPrefs::nglayout_initialpaint_delay_in_oopif();
1818   mPaintSuppressionTimer->InitWithNamedFuncCallback(
1819       [](nsITimer* aTimer, void* aPresShell) {
1820         RefPtr<PresShell> self = static_cast<PresShell*>(aPresShell);
1821         self->UnsuppressPainting();
1822       },
1823       this, delay, nsITimer::TYPE_ONE_SHOT,
1824       "PresShell::sPaintSuppressionCallback");
1825 }
1826 
Initialize()1827 nsresult PresShell::Initialize() {
1828   if (mIsDestroying) {
1829     return NS_OK;
1830   }
1831 
1832   if (!mDocument) {
1833     // Nothing to do
1834     return NS_OK;
1835   }
1836 
1837   MOZ_LOG(gLog, LogLevel::Debug, ("PresShell::Initialize this=%p", this));
1838 
1839   NS_ASSERTION(!mDidInitialize, "Why are we being called?");
1840 
1841   RefPtr<PresShell> kungFuDeathGrip(this);
1842 
1843   RecomputeFontSizeInflationEnabled();
1844   MOZ_DIAGNOSTIC_ASSERT(!mIsDestroying);
1845 
1846   // Ensure the pres context doesn't think it has changed, since we haven't even
1847   // started layout. This avoids spurious restyles / reflows afterwards.
1848   //
1849   // Note that this is very intentionally before setting mDidInitialize so it
1850   // doesn't notify the document, or run media query change events.
1851   mPresContext->FlushPendingMediaFeatureValuesChanged();
1852   MOZ_DIAGNOSTIC_ASSERT(!mIsDestroying);
1853 
1854   mDidInitialize = true;
1855 
1856 #ifdef DEBUG
1857   if (VerifyReflowFlags::NoisyCommands & gVerifyReflowFlags) {
1858     if (mDocument) {
1859       nsIURI* uri = mDocument->GetDocumentURI();
1860       if (uri) {
1861         printf("*** PresShell::Initialize (this=%p, url='%s')\n", (void*)this,
1862                uri->GetSpecOrDefault().get());
1863       }
1864     }
1865   }
1866 #endif
1867 
1868   // Get the root frame from the frame manager
1869   // XXXbz it would be nice to move this somewhere else... like frame manager
1870   // Init(), say.  But we need to make sure our views are all set up by the
1871   // time we do this!
1872   nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
1873   NS_ASSERTION(!rootFrame, "How did that happen, exactly?");
1874 
1875   if (!rootFrame) {
1876     nsAutoScriptBlocker scriptBlocker;
1877     rootFrame = mFrameConstructor->ConstructRootFrame();
1878     mFrameConstructor->SetRootFrame(rootFrame);
1879   }
1880 
1881   NS_ENSURE_STATE(!mHaveShutDown);
1882 
1883   if (!rootFrame) {
1884     return NS_ERROR_OUT_OF_MEMORY;
1885   }
1886 
1887   if (Element* root = mDocument->GetRootElement()) {
1888     {
1889       nsAutoCauseReflowNotifier reflowNotifier(this);
1890       // Have the style sheet processor construct frame for the root
1891       // content object down
1892       mFrameConstructor->ContentInserted(
1893           root, nsCSSFrameConstructor::InsertionKind::Sync);
1894     }
1895     // Something in mFrameConstructor->ContentInserted may have caused
1896     // Destroy() to get called, bug 337586.  Or, nsAutoCauseReflowNotifier
1897     // (which sets up a script blocker) going out of scope may have killed us
1898     // too
1899     NS_ENSURE_STATE(!mHaveShutDown);
1900   }
1901 
1902   mDocument->TriggerAutoFocus();
1903 
1904   NS_ASSERTION(rootFrame, "How did that happen?");
1905 
1906   // Note: when the frame was created above it had the NS_FRAME_IS_DIRTY bit
1907   // set, but XBL processing could have caused a reflow which clears it.
1908   if (MOZ_LIKELY(rootFrame->HasAnyStateBits(NS_FRAME_IS_DIRTY))) {
1909     // Unset the DIRTY bits so that FrameNeedsReflow() will work right.
1910     rootFrame->RemoveStateBits(NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN);
1911     NS_ASSERTION(!mDirtyRoots.Contains(rootFrame),
1912                  "Why is the root in mDirtyRoots already?");
1913     FrameNeedsReflow(rootFrame, IntrinsicDirty::Resize, NS_FRAME_IS_DIRTY);
1914     NS_ASSERTION(mDirtyRoots.Contains(rootFrame),
1915                  "Should be in mDirtyRoots now");
1916     NS_ASSERTION(mObservingLayoutFlushes, "Why no reflow scheduled?");
1917   }
1918 
1919   // Restore our root scroll position now if we're getting here after EndLoad
1920   // got called, since this is our one chance to do it.  Note that we need not
1921   // have reflowed for this to work; when the scrollframe is finally reflowed
1922   // it'll pick up the position we store in it here.
1923   if (!mDocumentLoading) {
1924     RestoreRootScrollPosition();
1925   }
1926 
1927   // For printing, we just immediately unsuppress.
1928   if (!mPresContext->IsPaginated()) {
1929     // Kick off a one-shot timer based off our pref value.  When this timer
1930     // fires, if painting is still locked down, then we will go ahead and
1931     // trigger a full invalidate and allow painting to proceed normally.
1932     mPaintingSuppressed = true;
1933     // Don't suppress painting if the document isn't loading.
1934     Document::ReadyState readyState = mDocument->GetReadyStateEnum();
1935     if (readyState != Document::READYSTATE_COMPLETE) {
1936       mPaintSuppressionTimer = NS_NewTimer();
1937     }
1938     if (!mPaintSuppressionTimer) {
1939       mPaintingSuppressed = false;
1940     } else {
1941       // Initialize the timer.
1942       mPaintSuppressionTimer->SetTarget(
1943           mDocument->EventTargetFor(TaskCategory::Other));
1944       InitPaintSuppressionTimer();
1945     }
1946   }
1947 
1948   // If we get here and painting is not suppressed, we still want to run the
1949   // unsuppression logic, so set mShouldUnsuppressPainting to true.
1950   if (!mPaintingSuppressed) {
1951     mShouldUnsuppressPainting = true;
1952   }
1953 
1954   return NS_OK;  // XXX this needs to be real. MMP
1955 }
1956 
ResizeReflow(nscoord aWidth,nscoord aHeight,ResizeReflowOptions aOptions)1957 nsresult PresShell::ResizeReflow(nscoord aWidth, nscoord aHeight,
1958                                  ResizeReflowOptions aOptions) {
1959   if (mZoomConstraintsClient) {
1960     // If we have a ZoomConstraintsClient and the available screen area
1961     // changed, then we might need to disable double-tap-to-zoom, so notify
1962     // the ZCC to update itself.
1963     mZoomConstraintsClient->ScreenSizeChanged();
1964   }
1965   if (UsesMobileViewportSizing()) {
1966     // If we are using mobile viewport sizing, request a reflow from the MVM.
1967     // It can recompute the final CSS viewport and trigger a call to
1968     // ResizeReflowIgnoreOverride if it changed. We don't force adjusting
1969     // of resolution, because that is only necessary when we are destroying
1970     // the MVM.
1971     MOZ_ASSERT(mMobileViewportManager);
1972     mMobileViewportManager->RequestReflow(false);
1973     return NS_OK;
1974   }
1975 
1976   return ResizeReflowIgnoreOverride(aWidth, aHeight, aOptions);
1977 }
1978 
SimpleResizeReflow(nscoord aWidth,nscoord aHeight,ResizeReflowOptions aOptions)1979 void PresShell::SimpleResizeReflow(nscoord aWidth, nscoord aHeight,
1980                                    ResizeReflowOptions aOptions) {
1981   MOZ_ASSERT(aWidth != NS_UNCONSTRAINEDSIZE);
1982   MOZ_ASSERT(aHeight != NS_UNCONSTRAINEDSIZE);
1983   nsSize oldSize = mPresContext->GetVisibleArea().Size();
1984   mPresContext->SetVisibleArea(nsRect(0, 0, aWidth, aHeight));
1985   nsIFrame* rootFrame = GetRootFrame();
1986   if (!rootFrame) {
1987     return;
1988   }
1989   WritingMode wm = rootFrame->GetWritingMode();
1990   bool isBSizeChanging =
1991       wm.IsVertical() ? oldSize.width != aWidth : oldSize.height != aHeight;
1992   if (isBSizeChanging) {
1993     nsLayoutUtils::MarkIntrinsicISizesDirtyIfDependentOnBSize(rootFrame);
1994   }
1995   FrameNeedsReflow(rootFrame, IntrinsicDirty::Resize,
1996                    NS_FRAME_HAS_DIRTY_CHILDREN);
1997 
1998   // For compat with the old code path which always reflowed as long as there
1999   // was a root frame.
2000   bool suppressReflow = (aOptions & ResizeReflowOptions::SuppressReflow) ||
2001                         mPresContext->SuppressingResizeReflow();
2002   if (!suppressReflow) {
2003     mDocument->FlushPendingNotifications(FlushType::InterruptibleLayout);
2004   }
2005 }
2006 
AddResizeEventFlushObserverIfNeeded()2007 void PresShell::AddResizeEventFlushObserverIfNeeded() {
2008   if (!mIsDestroying && !mResizeEventPending &&
2009       MOZ_LIKELY(!mDocument->GetBFCacheEntry())) {
2010     mResizeEventPending = true;
2011     mPresContext->RefreshDriver()->AddResizeEventFlushObserver(this);
2012   }
2013 }
2014 
ResizeReflowIgnoreOverride(nscoord aWidth,nscoord aHeight,ResizeReflowOptions aOptions)2015 nsresult PresShell::ResizeReflowIgnoreOverride(nscoord aWidth, nscoord aHeight,
2016                                                ResizeReflowOptions aOptions) {
2017   MOZ_ASSERT(!mIsReflowing, "Shouldn't be in reflow here!");
2018 
2019   // Historically we never fired resize events if there was no root frame by the
2020   // time this function got called.
2021   const bool initialized = mDidInitialize;
2022   RefPtr<PresShell> kungFuDeathGrip(this);
2023 
2024   auto postResizeEventIfNeeded = [this, initialized]() {
2025     if (initialized) {
2026       AddResizeEventFlushObserverIfNeeded();
2027     }
2028   };
2029 
2030   if (!(aOptions & ResizeReflowOptions::BSizeLimit)) {
2031     nsSize oldSize = mPresContext->GetVisibleArea().Size();
2032     if (oldSize == nsSize(aWidth, aHeight)) {
2033       return NS_OK;
2034     }
2035 
2036     SimpleResizeReflow(aWidth, aHeight, aOptions);
2037     postResizeEventIfNeeded();
2038     return NS_OK;
2039   }
2040 
2041   MOZ_ASSERT(!mPresContext->SuppressingResizeReflow() &&
2042                  !(aOptions & ResizeReflowOptions::SuppressReflow),
2043              "Can't suppress resize reflow and shrink-wrap at the same time");
2044 
2045   // Make sure that style is flushed before setting the pres context
2046   // VisibleArea.
2047   //
2048   // Otherwise we may end up with bogus viewport units resolved against the
2049   // unconstrained bsize, or restyling the whole document resolving viewport
2050   // units against targetWidth, which may end up doing wasteful work.
2051   mDocument->FlushPendingNotifications(FlushType::Frames);
2052 
2053   nsIFrame* rootFrame = GetRootFrame();
2054   if (mIsDestroying || !rootFrame) {
2055     // If we don't have a root frame yet, that means we haven't had our initial
2056     // reflow... If that's the case, and aWidth or aHeight is unconstrained,
2057     // ignore them altogether.
2058     if (aHeight == NS_UNCONSTRAINEDSIZE || aWidth == NS_UNCONSTRAINEDSIZE) {
2059       // We can't do the work needed for SizeToContent without a root
2060       // frame, and we want to return before setting the visible area.
2061       return NS_ERROR_NOT_AVAILABLE;
2062     }
2063 
2064     mPresContext->SetVisibleArea(nsRect(0, 0, aWidth, aHeight));
2065     // There isn't anything useful we can do if the initial reflow hasn't
2066     // happened.
2067     return NS_OK;
2068   }
2069 
2070   WritingMode wm = rootFrame->GetWritingMode();
2071   MOZ_ASSERT((wm.IsVertical() ? aHeight : aWidth) != NS_UNCONSTRAINEDSIZE,
2072              "unconstrained isize not allowed");
2073 
2074   nscoord targetWidth = aWidth;
2075   nscoord targetHeight = aHeight;
2076   if (wm.IsVertical()) {
2077     targetWidth = NS_UNCONSTRAINEDSIZE;
2078   } else {
2079     targetHeight = NS_UNCONSTRAINEDSIZE;
2080   }
2081 
2082   mPresContext->SetVisibleArea(nsRect(0, 0, targetWidth, targetHeight));
2083   // XXX Do a full invalidate at the beginning so that invalidates along
2084   // the way don't have region accumulation issues?
2085 
2086   // For height:auto BSizes (i.e. layout-controlled), descendant
2087   // intrinsic sizes can't depend on them. So the only other case is
2088   // viewport-controlled BSizes which we handle here.
2089   nsLayoutUtils::MarkIntrinsicISizesDirtyIfDependentOnBSize(rootFrame);
2090 
2091   {
2092     nsAutoCauseReflowNotifier crNotifier(this);
2093     WillDoReflow();
2094 
2095     // Kick off a top-down reflow
2096     AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Reflow);
2097     nsViewManager::AutoDisableRefresh refreshBlocker(mViewManager);
2098 
2099     mDirtyRoots.Remove(rootFrame);
2100     DoReflow(rootFrame, true, nullptr);
2101 
2102     const bool reflowAgain =
2103         wm.IsVertical() ? mPresContext->GetVisibleArea().width > aWidth
2104                         : mPresContext->GetVisibleArea().height > aHeight;
2105 
2106     if (reflowAgain) {
2107       mPresContext->SetVisibleArea(nsRect(0, 0, aWidth, aHeight));
2108       DoReflow(rootFrame, true, nullptr);
2109     }
2110   }
2111 
2112   // Now, we may have been destroyed by the destructor of
2113   // `nsAutoCauseReflowNotifier`.
2114 
2115   DidDoReflow(true);
2116 
2117   // the reflow above should've set our bsize if it was NS_UNCONSTRAINEDSIZE,
2118   // and the isize shouldn't be NS_UNCONSTRAINEDSIZE anyway.
2119   MOZ_DIAGNOSTIC_ASSERT(
2120       mPresContext->GetVisibleArea().width != NS_UNCONSTRAINEDSIZE,
2121       "width should not be NS_UNCONSTRAINEDSIZE after reflow");
2122   MOZ_DIAGNOSTIC_ASSERT(
2123       mPresContext->GetVisibleArea().height != NS_UNCONSTRAINEDSIZE,
2124       "height should not be NS_UNCONSTRAINEDSIZE after reflow");
2125 
2126   postResizeEventIfNeeded();
2127   return NS_OK;  // XXX this needs to be real. MMP
2128 }
2129 
FireResizeEvent()2130 void PresShell::FireResizeEvent() {
2131   if (mIsDocumentGone) {
2132     return;
2133   }
2134 
2135   // If event handling is suppressed, repost the resize event to the refresh
2136   // driver. The event is marked as delayed so that the refresh driver does not
2137   // continue ticking.
2138   if (mDocument->EventHandlingSuppressed()) {
2139     if (MOZ_LIKELY(!mDocument->GetBFCacheEntry())) {
2140       mDocument->SetHasDelayedRefreshEvent();
2141       mPresContext->RefreshDriver()->AddResizeEventFlushObserver(
2142           this, /* aDelayed = */ true);
2143     }
2144     return;
2145   }
2146 
2147   mResizeEventPending = false;
2148 
2149   // Send resize event from here.
2150   WidgetEvent event(true, mozilla::eResize);
2151   nsEventStatus status = nsEventStatus_eIgnore;
2152 
2153   if (nsPIDOMWindowOuter* window = mDocument->GetWindow()) {
2154     EventDispatcher::Dispatch(window, mPresContext, &event, nullptr, &status);
2155   }
2156 }
2157 
GetNativeAnonymousSubtreeRoot(nsIContent * aContent)2158 static nsIContent* GetNativeAnonymousSubtreeRoot(nsIContent* aContent) {
2159   if (!aContent) {
2160     return nullptr;
2161   }
2162   return aContent->GetClosestNativeAnonymousSubtreeRoot();
2163 }
2164 
NativeAnonymousContentRemoved(nsIContent * aAnonContent)2165 void PresShell::NativeAnonymousContentRemoved(nsIContent* aAnonContent) {
2166   MOZ_ASSERT(aAnonContent->IsRootOfNativeAnonymousSubtree());
2167   if (nsIContent* root = GetNativeAnonymousSubtreeRoot(mCurrentEventContent)) {
2168     if (aAnonContent == root) {
2169       mCurrentEventContent = aAnonContent->GetFlattenedTreeParent();
2170       mCurrentEventFrame = nullptr;
2171     }
2172   }
2173 
2174   for (unsigned int i = 0; i < mCurrentEventContentStack.Length(); i++) {
2175     nsIContent* anon =
2176         GetNativeAnonymousSubtreeRoot(mCurrentEventContentStack.ElementAt(i));
2177     if (aAnonContent == anon) {
2178       mCurrentEventContentStack.ReplaceObjectAt(
2179           aAnonContent->GetFlattenedTreeParent(), i);
2180       mCurrentEventFrameStack[i] = nullptr;
2181     }
2182   }
2183 }
2184 
SetIgnoreFrameDestruction(bool aIgnore)2185 void PresShell::SetIgnoreFrameDestruction(bool aIgnore) {
2186   if (mDocument) {
2187     // We need to tell the ImageLoader to drop all its references to frames
2188     // because they're about to go away and it won't get notifications of that.
2189     mDocument->StyleImageLoader()->ClearFrames(mPresContext);
2190   }
2191   mIgnoreFrameDestruction = aIgnore;
2192 }
2193 
NotifyDestroyingFrame(nsIFrame * aFrame)2194 void PresShell::NotifyDestroyingFrame(nsIFrame* aFrame) {
2195   // We must remove these from FrameLayerBuilder::DisplayItemData::mFrameList
2196   // here, otherwise the DisplayItemData destructor will use the destroyed frame
2197   // when it tries to remove it from the (array) value of this property.
2198   aFrame->RemoveDisplayItemDataForDeletion();
2199 
2200   if (!mIgnoreFrameDestruction) {
2201     if (aFrame->HasImageRequest()) {
2202       mDocument->StyleImageLoader()->DropRequestsForFrame(aFrame);
2203     }
2204 
2205     mFrameConstructor->NotifyDestroyingFrame(aFrame);
2206 
2207     mDirtyRoots.Remove(aFrame);
2208 
2209     // Remove frame properties
2210     aFrame->RemoveAllProperties();
2211 
2212     if (aFrame == mCurrentEventFrame) {
2213       mCurrentEventContent = aFrame->GetContent();
2214       mCurrentEventFrame = nullptr;
2215     }
2216 
2217 #ifdef DEBUG
2218     if (aFrame == mDrawEventTargetFrame) {
2219       mDrawEventTargetFrame = nullptr;
2220     }
2221 #endif
2222 
2223     for (unsigned int i = 0; i < mCurrentEventFrameStack.Length(); i++) {
2224       if (aFrame == mCurrentEventFrameStack.ElementAt(i)) {
2225         // One of our stack frames was deleted.  Get its content so that when we
2226         // pop it we can still get its new frame from its content
2227         nsIContent* currentEventContent = aFrame->GetContent();
2228         mCurrentEventContentStack.ReplaceObjectAt(currentEventContent, i);
2229         mCurrentEventFrameStack[i] = nullptr;
2230       }
2231     }
2232 
2233     mFramesToDirty.Remove(aFrame);
2234 
2235     nsIScrollableFrame* scrollableFrame = do_QueryFrame(aFrame);
2236     if (scrollableFrame) {
2237       mPendingScrollAnchorSelection.Remove(scrollableFrame);
2238       mPendingScrollAnchorAdjustment.Remove(scrollableFrame);
2239     }
2240   }
2241 }
2242 
GetCaret() const2243 already_AddRefed<nsCaret> PresShell::GetCaret() const {
2244   RefPtr<nsCaret> caret = mCaret;
2245   return caret.forget();
2246 }
2247 
2248 already_AddRefed<AccessibleCaretEventHub>
GetAccessibleCaretEventHub() const2249 PresShell::GetAccessibleCaretEventHub() const {
2250   RefPtr<AccessibleCaretEventHub> eventHub = mAccessibleCaretEventHub;
2251   return eventHub.forget();
2252 }
2253 
SetCaret(nsCaret * aNewCaret)2254 void PresShell::SetCaret(nsCaret* aNewCaret) { mCaret = aNewCaret; }
2255 
RestoreCaret()2256 void PresShell::RestoreCaret() { mCaret = mOriginalCaret; }
2257 
SetCaretEnabled(bool aInEnable)2258 NS_IMETHODIMP PresShell::SetCaretEnabled(bool aInEnable) {
2259   bool oldEnabled = mCaretEnabled;
2260 
2261   mCaretEnabled = aInEnable;
2262 
2263   if (mCaretEnabled != oldEnabled) {
2264     MOZ_ASSERT(mCaret);
2265     if (mCaret) {
2266       mCaret->SetVisible(mCaretEnabled);
2267     }
2268   }
2269 
2270   return NS_OK;
2271 }
2272 
SetCaretReadOnly(bool aReadOnly)2273 NS_IMETHODIMP PresShell::SetCaretReadOnly(bool aReadOnly) {
2274   if (mCaret) mCaret->SetCaretReadOnly(aReadOnly);
2275   return NS_OK;
2276 }
2277 
GetCaretEnabled(bool * aOutEnabled)2278 NS_IMETHODIMP PresShell::GetCaretEnabled(bool* aOutEnabled) {
2279   NS_ENSURE_ARG_POINTER(aOutEnabled);
2280   *aOutEnabled = mCaretEnabled;
2281   return NS_OK;
2282 }
2283 
SetCaretVisibilityDuringSelection(bool aVisibility)2284 NS_IMETHODIMP PresShell::SetCaretVisibilityDuringSelection(bool aVisibility) {
2285   if (mCaret) mCaret->SetVisibilityDuringSelection(aVisibility);
2286   return NS_OK;
2287 }
2288 
GetCaretVisible(bool * aOutIsVisible)2289 NS_IMETHODIMP PresShell::GetCaretVisible(bool* aOutIsVisible) {
2290   *aOutIsVisible = false;
2291   if (mCaret) {
2292     *aOutIsVisible = mCaret->IsVisible();
2293   }
2294   return NS_OK;
2295 }
2296 
SetSelectionFlags(int16_t aFlags)2297 NS_IMETHODIMP PresShell::SetSelectionFlags(int16_t aFlags) {
2298   mSelectionFlags = aFlags;
2299   return NS_OK;
2300 }
2301 
GetSelectionFlags(int16_t * aFlags)2302 NS_IMETHODIMP PresShell::GetSelectionFlags(int16_t* aFlags) {
2303   if (!aFlags) {
2304     return NS_ERROR_INVALID_ARG;
2305   }
2306 
2307   *aFlags = mSelectionFlags;
2308   return NS_OK;
2309 }
2310 
2311 // implementation of nsISelectionController
2312 
2313 NS_IMETHODIMP
PhysicalMove(int16_t aDirection,int16_t aAmount,bool aExtend)2314 PresShell::PhysicalMove(int16_t aDirection, int16_t aAmount, bool aExtend) {
2315   RefPtr<nsFrameSelection> frameSelection = mSelection;
2316   return frameSelection->PhysicalMove(aDirection, aAmount, aExtend);
2317 }
2318 
2319 NS_IMETHODIMP
CharacterMove(bool aForward,bool aExtend)2320 PresShell::CharacterMove(bool aForward, bool aExtend) {
2321   RefPtr<nsFrameSelection> frameSelection = mSelection;
2322   return frameSelection->CharacterMove(aForward, aExtend);
2323 }
2324 
2325 NS_IMETHODIMP
WordMove(bool aForward,bool aExtend)2326 PresShell::WordMove(bool aForward, bool aExtend) {
2327   RefPtr<nsFrameSelection> frameSelection = mSelection;
2328   nsresult result = frameSelection->WordMove(aForward, aExtend);
2329   // if we can't go down/up any more we must then move caret completely to
2330   // end/beginning respectively.
2331   if (NS_FAILED(result)) result = CompleteMove(aForward, aExtend);
2332   return result;
2333 }
2334 
2335 NS_IMETHODIMP
LineMove(bool aForward,bool aExtend)2336 PresShell::LineMove(bool aForward, bool aExtend) {
2337   RefPtr<nsFrameSelection> frameSelection = mSelection;
2338   nsresult result = frameSelection->LineMove(aForward, aExtend);
2339   // if we can't go down/up any more we must then move caret completely to
2340   // end/beginning respectively.
2341   if (NS_FAILED(result)) result = CompleteMove(aForward, aExtend);
2342   return result;
2343 }
2344 
2345 NS_IMETHODIMP
IntraLineMove(bool aForward,bool aExtend)2346 PresShell::IntraLineMove(bool aForward, bool aExtend) {
2347   RefPtr<nsFrameSelection> frameSelection = mSelection;
2348   return frameSelection->IntraLineMove(aForward, aExtend);
2349 }
2350 
2351 NS_IMETHODIMP
PageMove(bool aForward,bool aExtend)2352 PresShell::PageMove(bool aForward, bool aExtend) {
2353   nsIFrame* frame = nullptr;
2354   if (!aExtend) {
2355     frame = do_QueryFrame(GetScrollableFrameToScroll(VerticalScrollDirection));
2356     // If there is no scrollable frame, get the frame to move caret instead.
2357   }
2358   if (!frame || frame->PresContext() != mPresContext) {
2359     frame = mSelection->GetFrameToPageSelect();
2360     if (!frame) {
2361       return NS_OK;
2362     }
2363   }
2364   // We may scroll parent scrollable element of current selection limiter.
2365   // In such case, we don't want to scroll selection into view unless
2366   // selection is changed.
2367   RefPtr<nsFrameSelection> frameSelection = mSelection;
2368   return frameSelection->PageMove(
2369       aForward, aExtend, frame, nsFrameSelection::SelectionIntoView::IfChanged);
2370 }
2371 
2372 NS_IMETHODIMP
ScrollPage(bool aForward)2373 PresShell::ScrollPage(bool aForward) {
2374   nsIScrollableFrame* scrollFrame =
2375       GetScrollableFrameToScroll(VerticalScrollDirection);
2376   if (scrollFrame) {
2377     scrollFrame->ScrollBy(
2378         nsIntPoint(0, aForward ? 1 : -1), ScrollUnit::PAGES, ScrollMode::Smooth,
2379         nullptr, mozilla::ScrollOrigin::NotSpecified,
2380         nsIScrollableFrame::NOT_MOMENTUM, nsIScrollableFrame::ENABLE_SNAP);
2381   }
2382   return NS_OK;
2383 }
2384 
2385 NS_IMETHODIMP
ScrollLine(bool aForward)2386 PresShell::ScrollLine(bool aForward) {
2387   nsIScrollableFrame* scrollFrame =
2388       GetScrollableFrameToScroll(VerticalScrollDirection);
2389   if (scrollFrame) {
2390     int32_t lineCount =
2391         Preferences::GetInt("toolkit.scrollbox.verticalScrollDistance",
2392                             NS_DEFAULT_VERTICAL_SCROLL_DISTANCE);
2393     scrollFrame->ScrollBy(
2394         nsIntPoint(0, aForward ? lineCount : -lineCount), ScrollUnit::LINES,
2395         ScrollMode::Smooth, nullptr, mozilla::ScrollOrigin::NotSpecified,
2396         nsIScrollableFrame::NOT_MOMENTUM, nsIScrollableFrame::ENABLE_SNAP);
2397   }
2398   return NS_OK;
2399 }
2400 
2401 NS_IMETHODIMP
ScrollCharacter(bool aRight)2402 PresShell::ScrollCharacter(bool aRight) {
2403   nsIScrollableFrame* scrollFrame =
2404       GetScrollableFrameToScroll(HorizontalScrollDirection);
2405   if (scrollFrame) {
2406     int32_t h =
2407         Preferences::GetInt("toolkit.scrollbox.horizontalScrollDistance",
2408                             NS_DEFAULT_HORIZONTAL_SCROLL_DISTANCE);
2409     scrollFrame->ScrollBy(
2410         nsIntPoint(aRight ? h : -h, 0), ScrollUnit::LINES, ScrollMode::Smooth,
2411         nullptr, mozilla::ScrollOrigin::NotSpecified,
2412         nsIScrollableFrame::NOT_MOMENTUM, nsIScrollableFrame::ENABLE_SNAP);
2413   }
2414   return NS_OK;
2415 }
2416 
2417 NS_IMETHODIMP
CompleteScroll(bool aForward)2418 PresShell::CompleteScroll(bool aForward) {
2419   nsIScrollableFrame* scrollFrame =
2420       GetScrollableFrameToScroll(VerticalScrollDirection);
2421   if (scrollFrame) {
2422     scrollFrame->ScrollBy(
2423         nsIntPoint(0, aForward ? 1 : -1), ScrollUnit::WHOLE, ScrollMode::Smooth,
2424         nullptr, mozilla::ScrollOrigin::NotSpecified,
2425         nsIScrollableFrame::NOT_MOMENTUM, nsIScrollableFrame::ENABLE_SNAP);
2426   }
2427   return NS_OK;
2428 }
2429 
2430 NS_IMETHODIMP
CompleteMove(bool aForward,bool aExtend)2431 PresShell::CompleteMove(bool aForward, bool aExtend) {
2432   // Beware! This may flush notifications via synchronous
2433   // ScrollSelectionIntoView.
2434   RefPtr<nsFrameSelection> frameSelection = mSelection;
2435   nsIContent* limiter = frameSelection->GetAncestorLimiter();
2436   nsIFrame* frame = limiter ? limiter->GetPrimaryFrame()
2437                             : FrameConstructor()->GetRootElementFrame();
2438   if (!frame) return NS_ERROR_FAILURE;
2439   nsIFrame::CaretPosition pos = frame->GetExtremeCaretPosition(!aForward);
2440 
2441   const nsFrameSelection::FocusMode focusMode =
2442       aExtend ? nsFrameSelection::FocusMode::kExtendSelection
2443               : nsFrameSelection::FocusMode::kCollapseToNewPoint;
2444   frameSelection->HandleClick(
2445       MOZ_KnownLive(pos.mResultContent) /* bug 1636889 */, pos.mContentOffset,
2446       pos.mContentOffset, focusMode,
2447       aForward ? CARET_ASSOCIATE_AFTER : CARET_ASSOCIATE_BEFORE);
2448   if (limiter) {
2449     // HandleClick resets ancestorLimiter, so set it again.
2450     frameSelection->SetAncestorLimiter(limiter);
2451   }
2452 
2453   // After ScrollSelectionIntoView(), the pending notifications might be
2454   // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
2455   return ScrollSelectionIntoView(
2456       nsISelectionController::SELECTION_NORMAL,
2457       nsISelectionController::SELECTION_FOCUS_REGION,
2458       nsISelectionController::SCROLL_SYNCHRONOUS |
2459           nsISelectionController::SCROLL_FOR_CARET_MOVE);
2460 }
2461 
DoCheckVisibility(nsPresContext * aPresContext,nsIContent * aNode,int16_t aStartOffset,int16_t aEndOffset,bool * aRetval)2462 static void DoCheckVisibility(nsPresContext* aPresContext, nsIContent* aNode,
2463                               int16_t aStartOffset, int16_t aEndOffset,
2464                               bool* aRetval) {
2465   nsIFrame* frame = aNode->GetPrimaryFrame();
2466   if (!frame) {
2467     // No frame to look at so it must not be visible.
2468     return;
2469   }
2470 
2471   // Start process now to go through all frames to find startOffset. Then check
2472   // chars after that to see if anything until EndOffset is visible.
2473   bool finished = false;
2474   frame->CheckVisibility(aPresContext, aStartOffset, aEndOffset, true,
2475                          &finished, aRetval);
2476   // Don't worry about other return value.
2477 }
2478 
2479 NS_IMETHODIMP
CheckVisibility(nsINode * node,int16_t startOffset,int16_t EndOffset,bool * _retval)2480 PresShell::CheckVisibility(nsINode* node, int16_t startOffset,
2481                            int16_t EndOffset, bool* _retval) {
2482   if (!node || startOffset > EndOffset || !_retval || startOffset < 0 ||
2483       EndOffset < 0)
2484     return NS_ERROR_INVALID_ARG;
2485   *_retval = false;  // initialize return parameter
2486   nsCOMPtr<nsIContent> content(do_QueryInterface(node));
2487   if (!content) return NS_ERROR_FAILURE;
2488 
2489   DoCheckVisibility(mPresContext, content, startOffset, EndOffset, _retval);
2490   return NS_OK;
2491 }
2492 
CheckVisibilityContent(nsIContent * aNode,int16_t aStartOffset,int16_t aEndOffset,bool * aRetval)2493 nsresult PresShell::CheckVisibilityContent(nsIContent* aNode,
2494                                            int16_t aStartOffset,
2495                                            int16_t aEndOffset, bool* aRetval) {
2496   if (!aNode || aStartOffset > aEndOffset || !aRetval || aStartOffset < 0 ||
2497       aEndOffset < 0) {
2498     return NS_ERROR_INVALID_ARG;
2499   }
2500 
2501   *aRetval = false;
2502   DoCheckVisibility(mPresContext, aNode, aStartOffset, aEndOffset, aRetval);
2503   return NS_OK;
2504 }
2505 
2506 // end implementations nsISelectionController
2507 
GetRootScrollFrame() const2508 nsIFrame* PresShell::GetRootScrollFrame() const {
2509   nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
2510   // Ensure root frame is a viewport frame
2511   if (!rootFrame || !rootFrame->IsViewportFrame()) return nullptr;
2512   nsIFrame* theFrame = rootFrame->PrincipalChildList().FirstChild();
2513   if (!theFrame || !theFrame->IsScrollFrame()) return nullptr;
2514   return theFrame;
2515 }
2516 
GetRootScrollFrameAsScrollable() const2517 nsIScrollableFrame* PresShell::GetRootScrollFrameAsScrollable() const {
2518   nsIFrame* frame = GetRootScrollFrame();
2519   if (!frame) return nullptr;
2520   nsIScrollableFrame* scrollableFrame = do_QueryFrame(frame);
2521   NS_ASSERTION(scrollableFrame,
2522                "All scroll frames must implement nsIScrollableFrame");
2523   return scrollableFrame;
2524 }
2525 
GetPageSequenceFrame() const2526 nsPageSequenceFrame* PresShell::GetPageSequenceFrame() const {
2527   return mFrameConstructor->GetPageSequenceFrame();
2528 }
2529 
GetCanvasFrame() const2530 nsCanvasFrame* PresShell::GetCanvasFrame() const {
2531   nsIFrame* frame = mFrameConstructor->GetDocElementContainingBlock();
2532   return do_QueryFrame(frame);
2533 }
2534 
RestoreRootScrollPosition()2535 void PresShell::RestoreRootScrollPosition() {
2536   nsIScrollableFrame* scrollableFrame = GetRootScrollFrameAsScrollable();
2537   if (scrollableFrame) {
2538     scrollableFrame->ScrollToRestoredPosition();
2539   }
2540 }
2541 
MaybeReleaseCapturingContent()2542 void PresShell::MaybeReleaseCapturingContent() {
2543   RefPtr<nsFrameSelection> frameSelection = FrameSelection();
2544   if (frameSelection) {
2545     frameSelection->SetDragState(false);
2546   }
2547   if (sCapturingContentInfo.mContent &&
2548       sCapturingContentInfo.mContent->OwnerDoc() == mDocument) {
2549     PresShell::ReleaseCapturingContent();
2550   }
2551 }
2552 
BeginLoad(Document * aDocument)2553 void PresShell::BeginLoad(Document* aDocument) {
2554   mDocumentLoading = true;
2555 
2556   gfxTextPerfMetrics* tp = nullptr;
2557   if (mPresContext) {
2558     tp = mPresContext->GetTextPerfMetrics();
2559   }
2560 
2561   bool shouldLog = MOZ_LOG_TEST(gLog, LogLevel::Debug);
2562   if (shouldLog || tp) {
2563     mLoadBegin = TimeStamp::Now();
2564   }
2565 
2566   if (shouldLog) {
2567     nsIURI* uri = mDocument->GetDocumentURI();
2568     MOZ_LOG(gLog, LogLevel::Debug,
2569             ("(presshell) %p load begin [%s]\n", this,
2570              uri ? uri->GetSpecOrDefault().get() : ""));
2571   }
2572 }
2573 
EndLoad(Document * aDocument)2574 void PresShell::EndLoad(Document* aDocument) {
2575   MOZ_ASSERT(aDocument == mDocument, "Wrong document");
2576 
2577   RestoreRootScrollPosition();
2578 
2579   mDocumentLoading = false;
2580 }
2581 
IsLayoutFlushObserver()2582 bool PresShell::IsLayoutFlushObserver() {
2583   return GetPresContext()->RefreshDriver()->IsLayoutFlushObserver(this);
2584 }
2585 
LoadComplete()2586 void PresShell::LoadComplete() {
2587   gfxTextPerfMetrics* tp = nullptr;
2588   if (mPresContext) {
2589     tp = mPresContext->GetTextPerfMetrics();
2590   }
2591 
2592   // log load
2593   bool shouldLog = MOZ_LOG_TEST(gLog, LogLevel::Debug);
2594   if (shouldLog || tp) {
2595     TimeDuration loadTime = TimeStamp::Now() - mLoadBegin;
2596     nsIURI* uri = mDocument->GetDocumentURI();
2597     nsAutoCString spec;
2598     if (uri) {
2599       spec = uri->GetSpecOrDefault();
2600     }
2601     if (shouldLog) {
2602       MOZ_LOG(gLog, LogLevel::Debug,
2603               ("(presshell) %p load done time-ms: %9.2f [%s]\n", this,
2604                loadTime.ToMilliseconds(), spec.get()));
2605     }
2606     if (tp) {
2607       tp->Accumulate();
2608       if (tp->cumulative.numChars > 0) {
2609         LogTextPerfStats(tp, this, tp->cumulative, loadTime.ToMilliseconds(),
2610                          eLog_loaddone, spec.get());
2611       }
2612     }
2613   }
2614 }
2615 
2616 #ifdef DEBUG
VerifyHasDirtyRootAncestor(nsIFrame * aFrame)2617 void PresShell::VerifyHasDirtyRootAncestor(nsIFrame* aFrame) {
2618   // XXXbz due to bug 372769, can't actually assert anything here...
2619   return;
2620 
2621   // XXXbz shouldn't need this part; remove it once FrameNeedsReflow
2622   // handles the root frame correctly.
2623   if (!aFrame->GetParent()) {
2624     return;
2625   }
2626 
2627   // Make sure that there is a reflow root ancestor of |aFrame| that's
2628   // in mDirtyRoots already.
2629   while (aFrame && aFrame->HasAnyStateBits(NS_FRAME_HAS_DIRTY_CHILDREN)) {
2630     if ((aFrame->HasAnyStateBits(NS_FRAME_REFLOW_ROOT |
2631                                  NS_FRAME_DYNAMIC_REFLOW_ROOT) ||
2632          !aFrame->GetParent()) &&
2633         mDirtyRoots.Contains(aFrame)) {
2634       return;
2635     }
2636 
2637     aFrame = aFrame->GetParent();
2638   }
2639 
2640   MOZ_ASSERT_UNREACHABLE(
2641       "Frame has dirty bits set but isn't scheduled to be "
2642       "reflowed?");
2643 }
2644 #endif
2645 
PostPendingScrollAnchorSelection(mozilla::layout::ScrollAnchorContainer * aContainer)2646 void PresShell::PostPendingScrollAnchorSelection(
2647     mozilla::layout::ScrollAnchorContainer* aContainer) {
2648   mPendingScrollAnchorSelection.Insert(aContainer->ScrollableFrame());
2649 }
2650 
FlushPendingScrollAnchorSelections()2651 void PresShell::FlushPendingScrollAnchorSelections() {
2652   for (nsIScrollableFrame* scroll : mPendingScrollAnchorSelection) {
2653     scroll->Anchor()->SelectAnchor();
2654   }
2655   mPendingScrollAnchorSelection.Clear();
2656 }
2657 
PostPendingScrollAnchorAdjustment(ScrollAnchorContainer * aContainer)2658 void PresShell::PostPendingScrollAnchorAdjustment(
2659     ScrollAnchorContainer* aContainer) {
2660   mPendingScrollAnchorAdjustment.Insert(aContainer->ScrollableFrame());
2661 }
2662 
FlushPendingScrollAnchorAdjustments()2663 void PresShell::FlushPendingScrollAnchorAdjustments() {
2664   for (nsIScrollableFrame* scroll : mPendingScrollAnchorAdjustment) {
2665     scroll->Anchor()->ApplyAdjustments();
2666   }
2667   mPendingScrollAnchorAdjustment.Clear();
2668 }
2669 
FrameNeedsReflow(nsIFrame * aFrame,IntrinsicDirty aIntrinsicDirty,nsFrameState aBitToAdd,ReflowRootHandling aRootHandling)2670 void PresShell::FrameNeedsReflow(nsIFrame* aFrame,
2671                                  IntrinsicDirty aIntrinsicDirty,
2672                                  nsFrameState aBitToAdd,
2673                                  ReflowRootHandling aRootHandling) {
2674   MOZ_ASSERT(aBitToAdd == NS_FRAME_IS_DIRTY ||
2675                  aBitToAdd == NS_FRAME_HAS_DIRTY_CHILDREN || !aBitToAdd,
2676              "Unexpected bits being added");
2677 
2678   // FIXME bug 478135
2679   NS_ASSERTION(!(aIntrinsicDirty == IntrinsicDirty::StyleChange &&
2680                  aBitToAdd == NS_FRAME_HAS_DIRTY_CHILDREN),
2681                "bits don't correspond to style change reason");
2682 
2683   // FIXME bug 457400
2684   NS_ASSERTION(!mIsReflowing, "can't mark frame dirty during reflow");
2685 
2686   // If we've not yet done the initial reflow, then don't bother
2687   // enqueuing a reflow command yet.
2688   if (!mDidInitialize) return;
2689 
2690   // If we're already destroying, don't bother with this either.
2691   if (mIsDestroying) return;
2692 
2693 #ifdef DEBUG
2694   // printf("gShellCounter: %d\n", gShellCounter++);
2695   if (mInVerifyReflow) return;
2696 
2697   if (VerifyReflowFlags::NoisyCommands & gVerifyReflowFlags) {
2698     printf("\nPresShell@%p: frame %p needs reflow\n", (void*)this,
2699            (void*)aFrame);
2700     if (VerifyReflowFlags::ReallyNoisyCommands & gVerifyReflowFlags) {
2701       printf("Current content model:\n");
2702       Element* rootElement = mDocument->GetRootElement();
2703       if (rootElement) {
2704         rootElement->List(stdout, 0);
2705       }
2706     }
2707   }
2708 #endif
2709 
2710   AutoTArray<nsIFrame*, 4> subtrees;
2711   subtrees.AppendElement(aFrame);
2712 
2713   do {
2714     nsIFrame* subtreeRoot = subtrees.PopLastElement();
2715 
2716     // Grab |wasDirty| now so we can go ahead and update the bits on
2717     // subtreeRoot.
2718     bool wasDirty = subtreeRoot->IsSubtreeDirty();
2719     subtreeRoot->AddStateBits(aBitToAdd);
2720 
2721     // Determine whether we need to keep looking for the next ancestor
2722     // reflow root if subtreeRoot itself is a reflow root.
2723     bool targetNeedsReflowFromParent;
2724     switch (aRootHandling) {
2725       case ReflowRootHandling::PositionOrSizeChange:
2726         targetNeedsReflowFromParent = true;
2727         break;
2728       case ReflowRootHandling::NoPositionOrSizeChange:
2729         targetNeedsReflowFromParent = false;
2730         break;
2731       case ReflowRootHandling::InferFromBitToAdd:
2732         targetNeedsReflowFromParent = (aBitToAdd == NS_FRAME_IS_DIRTY);
2733         break;
2734     }
2735 
2736 #define FRAME_IS_REFLOW_ROOT(_f)                          \
2737   ((_f)->HasAnyStateBits(NS_FRAME_REFLOW_ROOT |           \
2738                          NS_FRAME_DYNAMIC_REFLOW_ROOT) && \
2739    ((_f) != subtreeRoot || !targetNeedsReflowFromParent))
2740 
2741     // Mark the intrinsic widths as dirty on the frame, all of its ancestors,
2742     // and all of its descendants, if needed:
2743 
2744     if (aIntrinsicDirty != IntrinsicDirty::Resize) {
2745       // Mark argument and all ancestors dirty. (Unless we hit a reflow
2746       // root that should contain the reflow.  That root could be
2747       // subtreeRoot itself if it's not dirty, or it could be some
2748       // ancestor of subtreeRoot.)
2749       for (nsIFrame* a = subtreeRoot; a && !FRAME_IS_REFLOW_ROOT(a);
2750            a = a->GetParent()) {
2751         a->MarkIntrinsicISizesDirty();
2752         if (a->IsAbsolutelyPositioned()) {
2753           // If we get here, 'a' is abspos, so its subtree's intrinsic sizing
2754           // has no effect on its ancestors' intrinsic sizing. So, don't loop
2755           // upwards any further.
2756           break;
2757         }
2758       }
2759     }
2760 
2761     const bool styleChange = (aIntrinsicDirty == IntrinsicDirty::StyleChange);
2762     const bool dirty = (aBitToAdd == NS_FRAME_IS_DIRTY);
2763     if (styleChange || dirty) {
2764       // Mark all descendants dirty (using an nsTArray stack rather than
2765       // recursion).
2766       // Note that ReflowInput::InitResizeFlags has some similar
2767       // code; see comments there for how and why it differs.
2768       AutoTArray<nsIFrame*, 32> stack;
2769       stack.AppendElement(subtreeRoot);
2770 
2771       do {
2772         nsIFrame* f = stack.PopLastElement();
2773 
2774         if (styleChange && f->IsPlaceholderFrame()) {
2775           // Call `GetOutOfFlowFrame` directly because we can get here from
2776           // frame destruction and the placeholder might be already torn down.
2777           if (nsIFrame* oof =
2778                   static_cast<nsPlaceholderFrame*>(f)->GetOutOfFlowFrame()) {
2779             if (!nsLayoutUtils::IsProperAncestorFrame(subtreeRoot, oof)) {
2780               // We have another distinct subtree we need to mark.
2781               subtrees.AppendElement(oof);
2782             }
2783           }
2784         }
2785 
2786         for (const auto& childList : f->ChildLists()) {
2787           for (nsIFrame* kid : childList.mList) {
2788             if (styleChange) {
2789               kid->MarkIntrinsicISizesDirty();
2790             }
2791             if (dirty) {
2792               kid->AddStateBits(NS_FRAME_IS_DIRTY);
2793             }
2794             stack.AppendElement(kid);
2795           }
2796         }
2797       } while (stack.Length() != 0);
2798     }
2799 
2800     // Skip setting dirty bits up the tree if we weren't given a bit to add.
2801     if (!aBitToAdd) {
2802       continue;
2803     }
2804 
2805     // Set NS_FRAME_HAS_DIRTY_CHILDREN bits (via nsIFrame::ChildIsDirty)
2806     // up the tree until we reach either a frame that's already dirty or
2807     // a reflow root.
2808     nsIFrame* f = subtreeRoot;
2809     for (;;) {
2810       if (FRAME_IS_REFLOW_ROOT(f) || !f->GetParent()) {
2811         // we've hit a reflow root or the root frame
2812         if (!wasDirty) {
2813           mDirtyRoots.Add(f);
2814           SetNeedLayoutFlush();
2815         }
2816 #ifdef DEBUG
2817         else {
2818           VerifyHasDirtyRootAncestor(f);
2819         }
2820 #endif
2821 
2822         break;
2823       }
2824 
2825       nsIFrame* child = f;
2826       f = f->GetParent();
2827       wasDirty = f->IsSubtreeDirty();
2828       f->ChildIsDirty(child);
2829       NS_ASSERTION(f->HasAnyStateBits(NS_FRAME_HAS_DIRTY_CHILDREN),
2830                    "ChildIsDirty didn't do its job");
2831       if (wasDirty) {
2832         // This frame was already marked dirty.
2833 #ifdef DEBUG
2834         VerifyHasDirtyRootAncestor(f);
2835 #endif
2836         break;
2837       }
2838     }
2839   } while (subtrees.Length() != 0);
2840 
2841   MaybeScheduleReflow();
2842 }
2843 
FrameNeedsToContinueReflow(nsIFrame * aFrame)2844 void PresShell::FrameNeedsToContinueReflow(nsIFrame* aFrame) {
2845   NS_ASSERTION(mIsReflowing, "Must be in reflow when marking path dirty.");
2846   MOZ_ASSERT(mCurrentReflowRoot, "Must have a current reflow root here");
2847   NS_ASSERTION(
2848       aFrame == mCurrentReflowRoot ||
2849           nsLayoutUtils::IsProperAncestorFrame(mCurrentReflowRoot, aFrame),
2850       "Frame passed in is not the descendant of mCurrentReflowRoot");
2851   NS_ASSERTION(aFrame->HasAnyStateBits(NS_FRAME_IN_REFLOW),
2852                "Frame passed in not in reflow?");
2853 
2854   mFramesToDirty.Insert(aFrame);
2855 }
2856 
GetContentForScrolling() const2857 already_AddRefed<nsIContent> PresShell::GetContentForScrolling() const {
2858   if (nsCOMPtr<nsIContent> focused = GetFocusedContentInOurWindow()) {
2859     return focused.forget();
2860   }
2861   return GetSelectedContentForScrolling();
2862 }
2863 
GetSelectedContentForScrolling() const2864 already_AddRefed<nsIContent> PresShell::GetSelectedContentForScrolling() const {
2865   nsCOMPtr<nsIContent> selectedContent;
2866   if (mSelection) {
2867     Selection* domSelection = mSelection->GetSelection(SelectionType::eNormal);
2868     if (domSelection) {
2869       selectedContent =
2870           nsIContent::FromNodeOrNull(domSelection->GetFocusNode());
2871     }
2872   }
2873   return selectedContent.forget();
2874 }
2875 
GetScrollableFrameToScrollForContent(nsIContent * aContent,ScrollDirections aDirections)2876 nsIScrollableFrame* PresShell::GetScrollableFrameToScrollForContent(
2877     nsIContent* aContent, ScrollDirections aDirections) {
2878   nsIScrollableFrame* scrollFrame = nullptr;
2879   if (aContent) {
2880     nsIFrame* startFrame = aContent->GetPrimaryFrame();
2881     if (startFrame) {
2882       scrollFrame = startFrame->GetScrollTargetFrame();
2883       if (scrollFrame) {
2884         startFrame = scrollFrame->GetScrolledFrame();
2885       }
2886       scrollFrame = nsLayoutUtils::GetNearestScrollableFrameForDirection(
2887           startFrame, aDirections);
2888     }
2889   }
2890   if (!scrollFrame) {
2891     scrollFrame = GetRootScrollFrameAsScrollable();
2892     if (!scrollFrame || !scrollFrame->GetScrolledFrame()) {
2893       return nullptr;
2894     }
2895     scrollFrame = nsLayoutUtils::GetNearestScrollableFrameForDirection(
2896         scrollFrame->GetScrolledFrame(), aDirections);
2897   }
2898   return scrollFrame;
2899 }
2900 
GetScrollableFrameToScroll(ScrollDirections aDirections)2901 nsIScrollableFrame* PresShell::GetScrollableFrameToScroll(
2902     ScrollDirections aDirections) {
2903   nsCOMPtr<nsIContent> content = GetContentForScrolling();
2904   return GetScrollableFrameToScrollForContent(content.get(), aDirections);
2905 }
2906 
CancelAllPendingReflows()2907 void PresShell::CancelAllPendingReflows() {
2908   mDirtyRoots.Clear();
2909 
2910   if (mObservingLayoutFlushes) {
2911     GetPresContext()->RefreshDriver()->RemoveLayoutFlushObserver(this);
2912     mObservingLayoutFlushes = false;
2913   }
2914 
2915   ASSERT_REFLOW_SCHEDULED_STATE();
2916 }
2917 
DestroyFramesAndStyleDataFor(Element * aElement,nsPresContext & aPresContext,RestyleManager::IncludeRoot aIncludeRoot)2918 static bool DestroyFramesAndStyleDataFor(
2919     Element* aElement, nsPresContext& aPresContext,
2920     RestyleManager::IncludeRoot aIncludeRoot) {
2921   bool didReconstruct =
2922       aPresContext.FrameConstructor()->DestroyFramesFor(aElement);
2923   RestyleManager::ClearServoDataFromSubtree(aElement, aIncludeRoot);
2924   return didReconstruct;
2925 }
2926 
SlotAssignmentWillChange(Element & aElement,HTMLSlotElement * aOldSlot,HTMLSlotElement * aNewSlot)2927 void PresShell::SlotAssignmentWillChange(Element& aElement,
2928                                          HTMLSlotElement* aOldSlot,
2929                                          HTMLSlotElement* aNewSlot) {
2930   MOZ_ASSERT(aOldSlot != aNewSlot);
2931 
2932   if (MOZ_UNLIKELY(!mDidInitialize)) {
2933     return;
2934   }
2935 
2936   // If the old slot is about to become empty and show fallback, let layout know
2937   // that it needs to do work.
2938   if (aOldSlot && aOldSlot->AssignedNodes().Length() == 1 &&
2939       aOldSlot->HasChildren()) {
2940     DestroyFramesForAndRestyle(aOldSlot);
2941   }
2942 
2943   // Ensure the new element starts off clean.
2944   DestroyFramesAndStyleDataFor(&aElement, *mPresContext,
2945                                RestyleManager::IncludeRoot::Yes);
2946 
2947   if (aNewSlot) {
2948     // If the new slot will stop showing fallback content, we need to reframe it
2949     // altogether.
2950     if (aNewSlot->AssignedNodes().IsEmpty() && aNewSlot->HasChildren()) {
2951       DestroyFramesForAndRestyle(aNewSlot);
2952       // Otherwise we just care about the element, but we need to ensure that
2953       // something takes care of traversing to the relevant slot, if needed.
2954     } else if (aNewSlot->HasServoData() &&
2955                !Servo_Element_IsDisplayNone(aNewSlot)) {
2956       // Set the reframe bits...
2957       aNewSlot->NoteDescendantsNeedFramesForServo();
2958       aElement.SetFlags(NODE_NEEDS_FRAME);
2959       // Now the style dirty bits. Note that we can't just do
2960       // aElement.NoteDirtyForServo(), because the new slot is not setup yet.
2961       aNewSlot->SetHasDirtyDescendantsForServo();
2962       aNewSlot->NoteDirtySubtreeForServo();
2963     }
2964   }
2965 }
2966 
2967 #ifdef DEBUG
AssertNoFramesInSubtree(nsIContent * aContent)2968 static void AssertNoFramesInSubtree(nsIContent* aContent) {
2969   for (nsINode* node : ShadowIncludingTreeIterator(*aContent)) {
2970     nsIContent* c = nsIContent::FromNode(node);
2971     MOZ_ASSERT(!c->GetPrimaryFrame());
2972   }
2973 }
2974 #endif
2975 
DestroyFramesForAndRestyle(Element * aElement)2976 void PresShell::DestroyFramesForAndRestyle(Element* aElement) {
2977 #ifdef DEBUG
2978   auto postCondition =
2979       mozilla::MakeScopeExit([&]() { AssertNoFramesInSubtree(aElement); });
2980 #endif
2981 
2982   MOZ_ASSERT(aElement);
2983   if (MOZ_UNLIKELY(!mDidInitialize)) {
2984     return;
2985   }
2986 
2987   if (!aElement->GetFlattenedTreeParentNode()) {
2988     // Nothing to do here, the element already is out of the frame tree.
2989     return;
2990   }
2991 
2992   nsAutoScriptBlocker scriptBlocker;
2993 
2994   // Mark ourselves as not safe to flush while we're doing frame destruction.
2995   ++mChangeNestCount;
2996 
2997   const bool didReconstruct = FrameConstructor()->DestroyFramesFor(aElement);
2998 
2999   // Clear the style data from all the flattened tree descendants, but _not_
3000   // from us, since otherwise we wouldn't see the reframe.
3001   RestyleManager::ClearServoDataFromSubtree(aElement,
3002                                             RestyleManager::IncludeRoot::No);
3003 
3004   auto changeHint =
3005       didReconstruct ? nsChangeHint(0) : nsChangeHint_ReconstructFrame;
3006 
3007   mPresContext->RestyleManager()->PostRestyleEvent(
3008       aElement, RestyleHint::RestyleSubtree(), changeHint);
3009 
3010   --mChangeNestCount;
3011 }
3012 
PostRecreateFramesFor(Element * aElement)3013 void PresShell::PostRecreateFramesFor(Element* aElement) {
3014   if (MOZ_UNLIKELY(!mDidInitialize)) {
3015     // Nothing to do here. In fact, if we proceed and aElement is the root, we
3016     // will crash.
3017     return;
3018   }
3019 
3020   mPresContext->RestyleManager()->PostRestyleEvent(
3021       aElement, RestyleHint{0}, nsChangeHint_ReconstructFrame);
3022 }
3023 
RestyleForAnimation(Element * aElement,RestyleHint aHint)3024 void PresShell::RestyleForAnimation(Element* aElement, RestyleHint aHint) {
3025   // Now that we no longer have separate non-animation and animation
3026   // restyles, this method having a distinct identity is less important,
3027   // but it still seems useful to offer as a "more public" API and as a
3028   // chokepoint for these restyles to go through.
3029   mPresContext->RestyleManager()->PostRestyleEvent(aElement, aHint,
3030                                                    nsChangeHint(0));
3031 }
3032 
SetForwardingContainer(const WeakPtr<nsDocShell> & aContainer)3033 void PresShell::SetForwardingContainer(const WeakPtr<nsDocShell>& aContainer) {
3034   mForwardingContainer = aContainer;
3035 }
3036 
ClearFrameRefs(nsIFrame * aFrame)3037 void PresShell::ClearFrameRefs(nsIFrame* aFrame) {
3038   mPresContext->EventStateManager()->ClearFrameRefs(aFrame);
3039 
3040   AutoWeakFrame* weakFrame = mAutoWeakFrames;
3041   while (weakFrame) {
3042     AutoWeakFrame* prev = weakFrame->GetPreviousWeakFrame();
3043     if (weakFrame->GetFrame() == aFrame) {
3044       // This removes weakFrame from mAutoWeakFrames.
3045       weakFrame->Clear(this);
3046     }
3047     weakFrame = prev;
3048   }
3049 
3050   AutoTArray<WeakFrame*, 4> toRemove;
3051   for (WeakFrame* weakFrame : mWeakFrames) {
3052     if (weakFrame->GetFrame() == aFrame) {
3053       toRemove.AppendElement(weakFrame);
3054     }
3055   }
3056   for (WeakFrame* weakFrame : toRemove) {
3057     weakFrame->Clear(this);
3058   }
3059 }
3060 
CreateReferenceRenderingContext()3061 already_AddRefed<gfxContext> PresShell::CreateReferenceRenderingContext() {
3062   nsDeviceContext* devCtx = mPresContext->DeviceContext();
3063   RefPtr<gfxContext> rc;
3064   if (mPresContext->IsScreen()) {
3065     rc = gfxContext::CreateOrNull(
3066         gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget().get());
3067   } else {
3068     // We assume the devCtx has positive width and height for this call.
3069     // However, width and height, may be outside of the reasonable range
3070     // so rc may still be null.
3071     rc = devCtx->CreateReferenceRenderingContext();
3072   }
3073 
3074   return rc ? rc.forget() : nullptr;
3075 }
3076 
GoToAnchor(const nsAString & aAnchorName,bool aScroll,ScrollFlags aAdditionalScrollFlags)3077 nsresult PresShell::GoToAnchor(const nsAString& aAnchorName, bool aScroll,
3078                                ScrollFlags aAdditionalScrollFlags) {
3079   if (!mDocument) {
3080     return NS_ERROR_FAILURE;
3081   }
3082 
3083   const Element* root = mDocument->GetRootElement();
3084   if (root && root->IsSVGElement(nsGkAtoms::svg)) {
3085     // We need to execute this even if there is an empty anchor name
3086     // so that any existing SVG fragment identifier effect is removed
3087     if (SVGFragmentIdentifier::ProcessFragmentIdentifier(mDocument,
3088                                                          aAnchorName)) {
3089       return NS_OK;
3090     }
3091   }
3092 
3093   // Hold a reference to the ESM in case event dispatch tears us down.
3094   RefPtr<EventStateManager> esm = mPresContext->EventStateManager();
3095 
3096   if (aAnchorName.IsEmpty()) {
3097     NS_ASSERTION(!aScroll, "can't scroll to empty anchor name");
3098     esm->SetContentState(nullptr, NS_EVENT_STATE_URLTARGET);
3099     return NS_OK;
3100   }
3101 
3102   nsresult rv = NS_OK;
3103   nsCOMPtr<nsIContent> content;
3104 
3105   // Search for an element with a matching "id" attribute
3106   if (mDocument) {
3107     content = mDocument->GetElementById(aAnchorName);
3108   }
3109 
3110   // Search for an anchor element with a matching "name" attribute
3111   if (!content && mDocument->IsHTMLDocument()) {
3112     // Find a matching list of named nodes
3113     nsCOMPtr<nsINodeList> list = mDocument->GetElementsByName(aAnchorName);
3114     if (list) {
3115       // Loop through the named nodes looking for the first anchor
3116       uint32_t length = list->Length();
3117       for (uint32_t i = 0; i < length; i++) {
3118         nsIContent* node = list->Item(i);
3119         if (node->IsHTMLElement(nsGkAtoms::a)) {
3120           content = node;
3121           break;
3122         }
3123       }
3124     }
3125   }
3126 
3127   // Search for anchor in the HTML namespace with a matching name
3128   if (!content && !mDocument->IsHTMLDocument()) {
3129     constexpr auto nameSpace = u"http://www.w3.org/1999/xhtml"_ns;
3130     // Get the list of anchor elements
3131     nsCOMPtr<nsINodeList> list =
3132         mDocument->GetElementsByTagNameNS(nameSpace, u"a"_ns);
3133     // Loop through the anchors looking for the first one with the given name.
3134     for (uint32_t i = 0; true; i++) {
3135       nsIContent* node = list->Item(i);
3136       if (!node) {  // End of list
3137         break;
3138       }
3139 
3140       // Compare the name attribute
3141       if (node->IsElement() &&
3142           node->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name,
3143                                          aAnchorName, eCaseMatters)) {
3144         content = node;
3145         break;
3146       }
3147     }
3148   }
3149 
3150   esm->SetContentState(content, NS_EVENT_STATE_URLTARGET);
3151 
3152 #ifdef ACCESSIBILITY
3153   nsIContent* anchorTarget = content;
3154 #endif
3155 
3156   nsIScrollableFrame* rootScroll = GetRootScrollFrameAsScrollable();
3157   if (rootScroll && rootScroll->DidHistoryRestore()) {
3158     // Scroll position restored from history trumps scrolling to anchor.
3159     aScroll = false;
3160     rootScroll->ClearDidHistoryRestore();
3161   }
3162 
3163   if (content) {
3164     if (aScroll) {
3165       rv = ScrollContentIntoView(
3166           content, ScrollAxis(kScrollToTop, WhenToScroll::Always), ScrollAxis(),
3167           ScrollFlags::AnchorScrollFlags | aAdditionalScrollFlags);
3168       NS_ENSURE_SUCCESS(rv, rv);
3169 
3170       nsIScrollableFrame* rootScroll = GetRootScrollFrameAsScrollable();
3171       if (rootScroll) {
3172         mLastAnchorScrolledTo = content;
3173         mLastAnchorScrollPositionY = rootScroll->GetScrollPosition().y;
3174       }
3175     }
3176 
3177     // Should we select the target? This action is controlled by a
3178     // preference: the default is to not select.
3179     bool selectAnchor = Preferences::GetBool("layout.selectanchor");
3180 
3181     // Even if select anchor pref is false, we must still move the
3182     // caret there. That way tabbing will start from the new
3183     // location
3184     RefPtr<nsRange> jumpToRange = nsRange::Create(mDocument);
3185     while (content && content->GetFirstChild()) {
3186       content = content->GetFirstChild();
3187     }
3188     jumpToRange->SelectNodeContents(*content, IgnoreErrors());
3189     // Select the anchor
3190     RefPtr<Selection> sel = mSelection->GetSelection(SelectionType::eNormal);
3191     if (sel) {
3192       sel->RemoveAllRanges(IgnoreErrors());
3193       sel->AddRangeAndSelectFramesAndNotifyListeners(*jumpToRange,
3194                                                      IgnoreErrors());
3195       if (!selectAnchor) {
3196         // Use a caret (collapsed selection) at the start of the anchor
3197         sel->CollapseToStart(IgnoreErrors());
3198       }
3199     }
3200     // Selection is at anchor.
3201     // Now focus the document itself if focus is on an element within it.
3202     nsPIDOMWindowOuter* win = mDocument->GetWindow();
3203 
3204     nsFocusManager* fm = nsFocusManager::GetFocusManager();
3205     if (fm && win) {
3206       nsCOMPtr<mozIDOMWindowProxy> focusedWindow;
3207       fm->GetFocusedWindow(getter_AddRefs(focusedWindow));
3208       if (SameCOMIdentity(win, focusedWindow)) {
3209         fm->ClearFocus(focusedWindow);
3210       }
3211     }
3212 
3213     // If the target is an animation element, activate the animation
3214     nsCOMPtr<SVGAnimationElement> animationElement = do_QueryInterface(content);
3215     if (animationElement) {
3216       animationElement->ActivateByHyperlink();
3217     }
3218   } else {
3219     rv = NS_ERROR_FAILURE;
3220     if (nsContentUtils::EqualsIgnoreASCIICase(aAnchorName, u"top"_ns)) {
3221       // Scroll to the top/left if aAnchorName is "top" and there is no element
3222       // with such a name or id.
3223       rv = NS_OK;
3224       nsIScrollableFrame* sf = GetRootScrollFrameAsScrollable();
3225       // Check |aScroll| after setting |rv| so we set |rv| to the same
3226       // thing whether or not |aScroll| is true.
3227       if (aScroll && sf) {
3228         ScrollMode scrollMode =
3229             sf->IsSmoothScroll() ? ScrollMode::SmoothMsd : ScrollMode::Instant;
3230         // Scroll to the top of the page
3231         sf->ScrollTo(nsPoint(0, 0), scrollMode);
3232       }
3233     }
3234   }
3235 
3236 #ifdef ACCESSIBILITY
3237   if (anchorTarget) {
3238     if (nsAccessibilityService* accService = GetAccessibilityService()) {
3239       accService->NotifyOfAnchorJumpTo(anchorTarget);
3240     }
3241   }
3242 #endif  // #ifdef ACCESSIBILITY
3243 
3244   return rv;
3245 }
3246 
ScrollToAnchor()3247 nsresult PresShell::ScrollToAnchor() {
3248   nsCOMPtr<nsIContent> lastAnchor = std::move(mLastAnchorScrolledTo);
3249   if (!lastAnchor) {
3250     return NS_OK;
3251   }
3252 
3253   NS_ASSERTION(mDidInitialize, "should have done initial reflow by now");
3254   nsIScrollableFrame* rootScroll = GetRootScrollFrameAsScrollable();
3255   if (!rootScroll ||
3256       mLastAnchorScrollPositionY != rootScroll->GetScrollPosition().y) {
3257     return NS_OK;
3258   }
3259   return ScrollContentIntoView(lastAnchor,
3260                                ScrollAxis(kScrollToTop, WhenToScroll::Always),
3261                                ScrollAxis(), ScrollFlags::AnchorScrollFlags);
3262 }
3263 
3264 /*
3265  * Helper (per-continuation) for ScrollContentIntoView.
3266  *
3267  * @param aContainerFrame [in] the frame which aRect is relative to
3268  * @param aFrame [in] Frame whose bounds should be unioned
3269  * @param aUseWholeLineHeightForInlines [in] if true, then for inline frames
3270  * we should include the top of the line in the added rectangle
3271  * @param aRect [inout] rect into which its bounds should be unioned
3272  * @param aHaveRect [inout] whether aRect contains data yet
3273  * @param aPrevBlock [inout] the block aLines is a line iterator for
3274  * @param aLines [inout] the line iterator we're using
3275  * @param aCurLine [inout] the line to start looking from in this iterator
3276  */
AccumulateFrameBounds(nsIFrame * aContainerFrame,nsIFrame * aFrame,bool aUseWholeLineHeightForInlines,nsRect & aRect,bool & aHaveRect,nsIFrame * & aPrevBlock,nsAutoLineIterator & aLines,int32_t & aCurLine)3277 static void AccumulateFrameBounds(nsIFrame* aContainerFrame, nsIFrame* aFrame,
3278                                   bool aUseWholeLineHeightForInlines,
3279                                   nsRect& aRect, bool& aHaveRect,
3280                                   nsIFrame*& aPrevBlock,
3281                                   nsAutoLineIterator& aLines,
3282                                   int32_t& aCurLine) {
3283   nsIFrame* frame = aFrame;
3284   nsRect frameBounds = nsRect(nsPoint(0, 0), aFrame->GetSize());
3285 
3286   // If this is an inline frame and either the bounds height is 0 (quirks
3287   // layout model) or aUseWholeLineHeightForInlines is set, we need to
3288   // change the top of the bounds to include the whole line.
3289   if (frameBounds.height == 0 || aUseWholeLineHeightForInlines) {
3290     nsIFrame* prevFrame = aFrame;
3291     nsIFrame* f = aFrame;
3292 
3293     while (f && f->IsFrameOfType(nsIFrame::eLineParticipant) &&
3294            !f->IsTransformed() && !f->IsAbsPosContainingBlock()) {
3295       prevFrame = f;
3296       f = prevFrame->GetParent();
3297     }
3298 
3299     if (f != aFrame && f && f->IsBlockFrame()) {
3300       // find the line containing aFrame and increase the top of |offset|.
3301       if (f != aPrevBlock) {
3302         aLines = f->GetLineIterator();
3303         aPrevBlock = f;
3304         aCurLine = 0;
3305       }
3306       if (aLines) {
3307         int32_t index = aLines->FindLineContaining(prevFrame, aCurLine);
3308         if (index >= 0) {
3309           auto line = aLines->GetLine(index).unwrap();
3310           frameBounds += frame->GetOffsetTo(f);
3311           frame = f;
3312           if (line.mLineBounds.y < frameBounds.y) {
3313             frameBounds.height = frameBounds.YMost() - line.mLineBounds.y;
3314             frameBounds.y = line.mLineBounds.y;
3315           }
3316         }
3317       }
3318     }
3319   }
3320 
3321   nsRect transformedBounds = nsLayoutUtils::TransformFrameRectToAncestor(
3322       frame, frameBounds, aContainerFrame);
3323 
3324   if (aHaveRect) {
3325     // We can't use nsRect::UnionRect since it drops empty rects on
3326     // the floor, and we need to include them.  (Thus we need
3327     // aHaveRect to know when to drop the initial value on the floor.)
3328     aRect = aRect.UnionEdges(transformedBounds);
3329   } else {
3330     aHaveRect = true;
3331     aRect = transformedBounds;
3332   }
3333 }
3334 
ComputeNeedToScroll(WhenToScroll aWhenToScroll,nscoord aLineSize,nscoord aRectMin,nscoord aRectMax,nscoord aViewMin,nscoord aViewMax)3335 static bool ComputeNeedToScroll(WhenToScroll aWhenToScroll, nscoord aLineSize,
3336                                 nscoord aRectMin, nscoord aRectMax,
3337                                 nscoord aViewMin, nscoord aViewMax) {
3338   // See how the rect should be positioned in a given axis.
3339   switch (aWhenToScroll) {
3340     case WhenToScroll::Always:
3341       // The caller wants the frame as visible as possible
3342       return true;
3343     case WhenToScroll::IfNotVisible:
3344       // Scroll only if no part of the frame is visible in this view.
3345       return aRectMax - aLineSize <= aViewMin ||
3346              aRectMin + aLineSize >= aViewMax;
3347     case WhenToScroll::IfNotFullyVisible:
3348       // Scroll only if part of the frame is hidden and more can fit in view
3349       return !(aRectMin >= aViewMin && aRectMax <= aViewMax) &&
3350              std::min(aViewMax, aRectMax) - std::max(aRectMin, aViewMin) <
3351                  aViewMax - aViewMin;
3352   }
3353   return false;
3354 }
3355 
ComputeWhereToScroll(WhereToScroll aWhereToScroll,nscoord aOriginalCoord,nscoord aRectMin,nscoord aRectMax,nscoord aViewMin,nscoord aViewMax,nscoord * aRangeMin,nscoord * aRangeMax)3356 static nscoord ComputeWhereToScroll(WhereToScroll aWhereToScroll,
3357                                     nscoord aOriginalCoord, nscoord aRectMin,
3358                                     nscoord aRectMax, nscoord aViewMin,
3359                                     nscoord aViewMax, nscoord* aRangeMin,
3360                                     nscoord* aRangeMax) {
3361   nscoord resultCoord = aOriginalCoord;
3362   nscoord scrollPortLength = aViewMax - aViewMin;
3363   if (kScrollMinimum == aWhereToScroll) {
3364     // Scroll the minimum amount necessary to show as much as possible of the
3365     // frame. If the frame is too large, don't hide any initially visible part
3366     // of it.
3367     nscoord min = std::min(aRectMin, aRectMax - scrollPortLength);
3368     nscoord max = std::max(aRectMin, aRectMax - scrollPortLength);
3369     resultCoord = std::min(std::max(aOriginalCoord, min), max);
3370   } else {
3371     nscoord frameAlignCoord = NSToCoordRound(
3372         aRectMin + (aRectMax - aRectMin) * (aWhereToScroll / 100.0f));
3373     resultCoord = NSToCoordRound(frameAlignCoord -
3374                                  scrollPortLength * (aWhereToScroll / 100.0f));
3375   }
3376   // Force the scroll range to extend to include resultCoord.
3377   *aRangeMin = std::min(resultCoord, aRectMax - scrollPortLength);
3378   *aRangeMax = std::max(resultCoord, aRectMin);
3379   return resultCoord;
3380 }
3381 
3382 /**
3383  * This function takes a scrollable frame, a rect in the coordinate system
3384  * of the scrolled frame, and a desired percentage-based scroll
3385  * position and attempts to scroll the rect to that position in the
3386  * visual viewport.
3387  *
3388  * This needs to work even if aRect has a width or height of zero.
3389  */
ScrollToShowRect(nsIScrollableFrame * aFrameAsScrollable,const nsRect & aRect,const nsMargin & aMargin,ScrollAxis aVertical,ScrollAxis aHorizontal,ScrollFlags aScrollFlags)3390 static void ScrollToShowRect(nsIScrollableFrame* aFrameAsScrollable,
3391                              const nsRect& aRect, const nsMargin& aMargin,
3392                              ScrollAxis aVertical, ScrollAxis aHorizontal,
3393                              ScrollFlags aScrollFlags) {
3394   nsPoint scrollPt = aFrameAsScrollable->GetVisualViewportOffset();
3395   const nsPoint originalScrollPt = scrollPt;
3396   const nsRect visibleRect(scrollPt,
3397                            aFrameAsScrollable->GetVisualViewportSize());
3398 
3399   const nsMargin padding = aFrameAsScrollable->GetScrollPadding() + aMargin;
3400 
3401   const nsRect rectToScrollIntoView = [&] {
3402     nsRect r(aRect);
3403     r.Inflate(padding);
3404     return r.Intersect(aFrameAsScrollable->GetScrolledRect());
3405   }();
3406 
3407   nsSize lineSize;
3408   // Don't call GetLineScrollAmount unless we actually need it. Not only
3409   // does this save time, but it's not safe to call GetLineScrollAmount
3410   // during reflow (because it depends on font size inflation and doesn't
3411   // use the in-reflow-safe font-size inflation path). If we did call it,
3412   // it would assert and possible give the wrong result.
3413   if (aVertical.mWhenToScroll == WhenToScroll::IfNotVisible ||
3414       aHorizontal.mWhenToScroll == WhenToScroll::IfNotVisible) {
3415     lineSize = aFrameAsScrollable->GetLineScrollAmount();
3416   }
3417   ScrollStyles ss = aFrameAsScrollable->GetScrollStyles();
3418   nsRect allowedRange(scrollPt, nsSize(0, 0));
3419   ScrollDirections directions =
3420       aFrameAsScrollable->GetAvailableScrollingDirections();
3421 
3422   if (((aScrollFlags & ScrollFlags::ScrollOverflowHidden) ||
3423        ss.mVertical != StyleOverflow::Hidden) &&
3424       (!aVertical.mOnlyIfPerceivedScrollableDirection ||
3425        (directions.contains(ScrollDirection::eVertical)))) {
3426     if (ComputeNeedToScroll(aVertical.mWhenToScroll, lineSize.height, aRect.y,
3427                             aRect.YMost(), visibleRect.y + padding.top,
3428                             visibleRect.YMost() - padding.bottom)) {
3429       nscoord maxHeight;
3430       scrollPt.y = ComputeWhereToScroll(
3431           aVertical.mWhereToScroll, scrollPt.y, rectToScrollIntoView.y,
3432           rectToScrollIntoView.YMost(), visibleRect.y, visibleRect.YMost(),
3433           &allowedRange.y, &maxHeight);
3434       allowedRange.height = maxHeight - allowedRange.y;
3435     }
3436   }
3437 
3438   if (((aScrollFlags & ScrollFlags::ScrollOverflowHidden) ||
3439        ss.mHorizontal != StyleOverflow::Hidden) &&
3440       (!aHorizontal.mOnlyIfPerceivedScrollableDirection ||
3441        (directions.contains(ScrollDirection::eHorizontal)))) {
3442     if (ComputeNeedToScroll(aHorizontal.mWhenToScroll, lineSize.width, aRect.x,
3443                             aRect.XMost(), visibleRect.x + padding.left,
3444                             visibleRect.XMost() - padding.right)) {
3445       nscoord maxWidth;
3446       scrollPt.x = ComputeWhereToScroll(
3447           aHorizontal.mWhereToScroll, scrollPt.x, rectToScrollIntoView.x,
3448           rectToScrollIntoView.XMost(), visibleRect.x, visibleRect.XMost(),
3449           &allowedRange.x, &maxWidth);
3450       allowedRange.width = maxWidth - allowedRange.x;
3451     }
3452   }
3453 
3454   // If we don't need to scroll, then don't try since it might cancel
3455   // a current smooth scroll operation.
3456   if (scrollPt == originalScrollPt) {
3457     return;
3458   }
3459 
3460   ScrollMode scrollMode = ScrollMode::Instant;
3461   bool autoBehaviorIsSmooth = aFrameAsScrollable->IsSmoothScroll();
3462   bool smoothScroll =
3463       (aScrollFlags & ScrollFlags::ScrollSmooth) ||
3464       ((aScrollFlags & ScrollFlags::ScrollSmoothAuto) && autoBehaviorIsSmooth);
3465   if (StaticPrefs::layout_css_scroll_behavior_enabled() && smoothScroll) {
3466     scrollMode = ScrollMode::SmoothMsd;
3467   }
3468   nsIFrame* frame = do_QueryFrame(aFrameAsScrollable);
3469   AutoWeakFrame weakFrame(frame);
3470   aFrameAsScrollable->ScrollTo(scrollPt, scrollMode, &allowedRange,
3471                                aScrollFlags & ScrollFlags::ScrollSnap
3472                                    ? nsIScrollbarMediator::ENABLE_SNAP
3473                                    : nsIScrollbarMediator::DISABLE_SNAP);
3474   if (!weakFrame.IsAlive()) {
3475     return;
3476   }
3477 
3478   // If this is the RCD-RSF, also call ScrollToVisual() since we want to
3479   // scroll the rect into view visually, and that may require scrolling
3480   // the visual viewport in scenarios where there is not enough layout
3481   // scroll range.
3482   if (aFrameAsScrollable->IsRootScrollFrameOfDocument() &&
3483       frame->PresContext()->IsRootContentDocumentCrossProcess()) {
3484     frame->PresShell()->ScrollToVisual(scrollPt, FrameMetrics::eMainThread,
3485                                        scrollMode);
3486   }
3487 }
3488 
ScrollContentIntoView(nsIContent * aContent,ScrollAxis aVertical,ScrollAxis aHorizontal,ScrollFlags aScrollFlags)3489 nsresult PresShell::ScrollContentIntoView(nsIContent* aContent,
3490                                           ScrollAxis aVertical,
3491                                           ScrollAxis aHorizontal,
3492                                           ScrollFlags aScrollFlags) {
3493   NS_ENSURE_TRUE(aContent, NS_ERROR_NULL_POINTER);
3494   RefPtr<Document> composedDoc = aContent->GetComposedDoc();
3495   NS_ENSURE_STATE(composedDoc);
3496 
3497   NS_ASSERTION(mDidInitialize, "should have done initial reflow by now");
3498 
3499   if (mContentToScrollTo) {
3500     mContentToScrollTo->RemoveProperty(nsGkAtoms::scrolling);
3501   }
3502   mContentToScrollTo = aContent;
3503   ScrollIntoViewData* data = new ScrollIntoViewData();
3504   data->mContentScrollVAxis = aVertical;
3505   data->mContentScrollHAxis = aHorizontal;
3506   data->mContentToScrollToFlags = aScrollFlags;
3507   if (NS_FAILED(mContentToScrollTo->SetProperty(
3508           nsGkAtoms::scrolling, data,
3509           nsINode::DeleteProperty<PresShell::ScrollIntoViewData>))) {
3510     mContentToScrollTo = nullptr;
3511   }
3512 
3513   // Flush layout and attempt to scroll in the process.
3514   if (PresShell* presShell = composedDoc->GetPresShell()) {
3515     presShell->SetNeedLayoutFlush();
3516   }
3517   composedDoc->FlushPendingNotifications(FlushType::InterruptibleLayout);
3518 
3519   // If mContentToScrollTo is non-null, that means we interrupted the reflow
3520   // (or suppressed it altogether because we're suppressing interruptible
3521   // flushes right now) and won't necessarily get the position correct, but do
3522   // a best-effort scroll here.  The other option would be to do this inside
3523   // FlushPendingNotifications, but I'm not sure the repeated scrolling that
3524   // could trigger if reflows keep getting interrupted would be more desirable
3525   // than a single best-effort scroll followed by one final scroll on the first
3526   // completed reflow.
3527   if (mContentToScrollTo) {
3528     DoScrollContentIntoView();
3529   }
3530   return NS_OK;
3531 }
3532 
DoScrollContentIntoView()3533 void PresShell::DoScrollContentIntoView() {
3534   NS_ASSERTION(mDidInitialize, "should have done initial reflow by now");
3535 
3536   nsIFrame* frame = mContentToScrollTo->GetPrimaryFrame();
3537   if (!frame) {
3538     mContentToScrollTo->RemoveProperty(nsGkAtoms::scrolling);
3539     mContentToScrollTo = nullptr;
3540     return;
3541   }
3542 
3543   if (frame->HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
3544     // The reflow flush before this scroll got interrupted, and this frame's
3545     // coords and size are all zero, and it has no content showing anyway.
3546     // Don't bother scrolling to it.  We'll try again when we finish up layout.
3547     return;
3548   }
3549 
3550   // Make sure we skip 'frame' ... if it's scrollable, we should use its
3551   // scrollable ancestor as the container.
3552   nsIFrame* container = nsLayoutUtils::GetClosestFrameOfType(
3553       frame->GetParent(), LayoutFrameType::Scroll);
3554   if (!container) {
3555     // nothing can be scrolled
3556     return;
3557   }
3558 
3559   ScrollIntoViewData* data = static_cast<ScrollIntoViewData*>(
3560       mContentToScrollTo->GetProperty(nsGkAtoms::scrolling));
3561   if (MOZ_UNLIKELY(!data)) {
3562     mContentToScrollTo = nullptr;
3563     return;
3564   }
3565 
3566   // Get the scroll-margin here since |frame| is going to be changed to iterate
3567   // over all continuation frames below.
3568   const nsMargin scrollMargin = frame->StyleMargin()->GetScrollMargin();
3569 
3570   // This is a two-step process.
3571   // Step 1: Find the bounds of the rect we want to scroll into view.  For
3572   //         example, for an inline frame we may want to scroll in the whole
3573   //         line, or we may want to scroll multiple lines into view.
3574   // Step 2: Walk container frame and its ancestors and scroll them
3575   //         appropriately.
3576   // frameBounds is relative to container. We're assuming
3577   // that scrollframes don't split so every continuation of frame will
3578   // be a descendant of container. (Things would still mostly work
3579   // even if that assumption was false.)
3580   nsRect frameBounds;
3581   bool haveRect = false;
3582   bool useWholeLineHeightForInlines = data->mContentScrollVAxis.mWhenToScroll !=
3583                                       WhenToScroll::IfNotFullyVisible;
3584   // Reuse the same line iterator across calls to AccumulateFrameBounds.  We set
3585   // it every time we detect a new block (stored in prevBlock).
3586   nsIFrame* prevBlock = nullptr;
3587   nsAutoLineIterator lines;
3588   // The last line we found a continuation on in |lines|.  We assume that later
3589   // continuations cannot come on earlier lines.
3590   int32_t curLine = 0;
3591   do {
3592     AccumulateFrameBounds(container, frame, useWholeLineHeightForInlines,
3593                           frameBounds, haveRect, prevBlock, lines, curLine);
3594   } while ((frame = frame->GetNextContinuation()));
3595 
3596   ScrollFrameRectIntoView(container, frameBounds, scrollMargin,
3597                           data->mContentScrollVAxis, data->mContentScrollHAxis,
3598                           data->mContentToScrollToFlags);
3599 }
3600 
ScrollFrameRectIntoView(nsIFrame * aFrame,const nsRect & aRect,const nsMargin & aMargin,ScrollAxis aVertical,ScrollAxis aHorizontal,ScrollFlags aScrollFlags)3601 bool PresShell::ScrollFrameRectIntoView(nsIFrame* aFrame, const nsRect& aRect,
3602                                         const nsMargin& aMargin,
3603                                         ScrollAxis aVertical,
3604                                         ScrollAxis aHorizontal,
3605                                         ScrollFlags aScrollFlags) {
3606   bool didScroll = false;
3607   // This function needs to work even if rect has a width or height of 0.
3608   nsRect rect = aRect;
3609   nsIFrame* container = aFrame;
3610   // Walk up the frame hierarchy scrolling the rect into view and
3611   // keeping rect relative to container
3612   do {
3613     nsIScrollableFrame* sf = do_QueryFrame(container);
3614     if (sf) {
3615       nsPoint oldPosition = sf->GetScrollPosition();
3616       nsRect targetRect = rect;
3617       // Inflate the scrolled rect by the container's padding in each dimension,
3618       // unless we have 'overflow-clip-box-*: content-box' in that dimension.
3619       auto* disp = container->StyleDisplay();
3620       if (disp->mOverflowClipBoxBlock == StyleOverflowClipBox::ContentBox ||
3621           disp->mOverflowClipBoxInline == StyleOverflowClipBox::ContentBox) {
3622         WritingMode wm = container->GetWritingMode();
3623         bool cbH = (wm.IsVertical() ? disp->mOverflowClipBoxBlock
3624                                     : disp->mOverflowClipBoxInline) ==
3625                    StyleOverflowClipBox::ContentBox;
3626         bool cbV = (wm.IsVertical() ? disp->mOverflowClipBoxInline
3627                                     : disp->mOverflowClipBoxBlock) ==
3628                    StyleOverflowClipBox::ContentBox;
3629         nsMargin padding = container->GetUsedPadding();
3630         if (!cbH) {
3631           padding.left = padding.right = nscoord(0);
3632         }
3633         if (!cbV) {
3634           padding.top = padding.bottom = nscoord(0);
3635         }
3636         targetRect.Inflate(padding);
3637       }
3638 
3639       targetRect -= sf->GetScrolledFrame()->GetPosition();
3640 
3641       {
3642         AutoWeakFrame wf(container);
3643         ScrollToShowRect(sf, targetRect, aMargin, aVertical, aHorizontal,
3644                          aScrollFlags);
3645         if (!wf.IsAlive()) {
3646           return didScroll;
3647         }
3648       }
3649 
3650       nsPoint newPosition = sf->LastScrollDestination();
3651       // If the scroll position increased, that means our content moved up,
3652       // so our rect's offset should decrease
3653       rect += oldPosition - newPosition;
3654 
3655       if (oldPosition != newPosition) {
3656         didScroll = true;
3657       }
3658 
3659       // only scroll one container when this flag is set
3660       if (aScrollFlags & ScrollFlags::ScrollFirstAncestorOnly) {
3661         break;
3662       }
3663     }
3664     nsIFrame* parent;
3665     if (container->IsTransformed()) {
3666       container->GetTransformMatrix(ViewportType::Layout, RelativeTo{nullptr},
3667                                     &parent);
3668       rect =
3669           nsLayoutUtils::TransformFrameRectToAncestor(container, rect, parent);
3670     } else {
3671       rect += container->GetPosition();
3672       parent = container->GetParent();
3673     }
3674     if (!parent && !(aScrollFlags & ScrollFlags::ScrollNoParentFrames)) {
3675       nsPoint extraOffset(0, 0);
3676       int32_t APD = container->PresContext()->AppUnitsPerDevPixel();
3677       parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(container,
3678                                                               &extraOffset);
3679       if (parent) {
3680         int32_t parentAPD = parent->PresContext()->AppUnitsPerDevPixel();
3681         rect = rect.ScaleToOtherAppUnitsRoundOut(APD, parentAPD);
3682         rect += extraOffset;
3683       } else {
3684         nsCOMPtr<nsIDocShell> docShell =
3685             container->PresContext()->GetDocShell();
3686         if (BrowserChild* browserChild = BrowserChild::GetFrom(docShell)) {
3687           // Defer to the parent document if this is an out-of-process iframe.
3688           Unused << browserChild->SendScrollRectIntoView(
3689               rect, aVertical, aHorizontal, aScrollFlags, APD);
3690         }
3691       }
3692     }
3693     container = parent;
3694   } while (container);
3695 
3696   return didScroll;
3697 }
3698 
ScheduleViewManagerFlush(PaintType aType)3699 void PresShell::ScheduleViewManagerFlush(PaintType aType) {
3700   if (MOZ_UNLIKELY(mIsDestroying)) {
3701     return;
3702   }
3703 
3704   if (aType == PaintType::DelayedCompress) {
3705     // Delay paint for 1 second.
3706     static const uint32_t kPaintDelayPeriod = 1000;
3707     if (!mDelayedPaintTimer) {
3708       nsTimerCallbackFunc PaintTimerCallBack = [](nsITimer* aTimer,
3709                                                   void* aClosure) {
3710         // The passed-in PresShell is always alive here. Because if PresShell
3711         // died, mDelayedPaintTimer->Cancel() would be called during the
3712         // destruction and this callback would never be invoked.
3713         auto self = static_cast<PresShell*>(aClosure);
3714         self->SetNextPaintCompressed();
3715         self->ScheduleViewManagerFlush();
3716       };
3717 
3718       NS_NewTimerWithFuncCallback(
3719           getter_AddRefs(mDelayedPaintTimer), PaintTimerCallBack, this,
3720           kPaintDelayPeriod, nsITimer::TYPE_ONE_SHOT, "PaintTimerCallBack",
3721           mDocument->EventTargetFor(TaskCategory::Other));
3722     }
3723     return;
3724   }
3725 
3726   nsPresContext* presContext = GetPresContext();
3727   if (presContext) {
3728     presContext->RefreshDriver()->ScheduleViewManagerFlush();
3729   }
3730   SetNeedLayoutFlush();
3731 }
3732 
DispatchSynthMouseMove(WidgetGUIEvent * aEvent)3733 void PresShell::DispatchSynthMouseMove(WidgetGUIEvent* aEvent) {
3734   AUTO_PROFILER_TRACING_MARKER_DOCSHELL("Paint", "DispatchSynthMouseMove",
3735                                         GRAPHICS, mPresContext->GetDocShell());
3736   nsEventStatus status = nsEventStatus_eIgnore;
3737   nsView* targetView = nsView::GetViewFor(aEvent->mWidget);
3738   if (!targetView) return;
3739   RefPtr<nsViewManager> viewManager = targetView->GetViewManager();
3740   viewManager->DispatchEvent(aEvent, targetView, &status);
3741 }
3742 
ClearMouseCaptureOnView(nsView * aView)3743 void PresShell::ClearMouseCaptureOnView(nsView* aView) {
3744   if (nsIContent* capturingContent = GetCapturingContent()) {
3745     if (aView) {
3746       // if a view was specified, ensure that the captured content is within
3747       // this view.
3748       nsIFrame* frame = capturingContent->GetPrimaryFrame();
3749       if (frame) {
3750         nsView* view = frame->GetClosestView();
3751         // if there is no view, capturing won't be handled any more, so
3752         // just release the capture.
3753         if (view) {
3754           do {
3755             if (view == aView) {
3756               ReleaseCapturingContent();
3757               // the view containing the captured content likely disappeared so
3758               // disable capture for now.
3759               AllowMouseCapture(false);
3760               break;
3761             }
3762 
3763             view = view->GetParent();
3764           } while (view);
3765           // return if the view wasn't found
3766           return;
3767         }
3768       }
3769     }
3770 
3771     ReleaseCapturingContent();
3772   }
3773 
3774   // disable mouse capture until the next mousedown as a dialog has opened
3775   // or a drag has started. Otherwise, someone could start capture during
3776   // the modal dialog or drag.
3777   AllowMouseCapture(false);
3778 }
3779 
ClearMouseCapture()3780 void PresShell::ClearMouseCapture() {
3781   nsIContent* capturingContent = GetCapturingContent();
3782   if (!capturingContent) {
3783     AllowMouseCapture(false);
3784     return;
3785   }
3786 
3787   ReleaseCapturingContent();
3788   AllowMouseCapture(false);
3789 }
3790 
ClearMouseCapture(nsIFrame * aFrame)3791 void PresShell::ClearMouseCapture(nsIFrame* aFrame) {
3792   MOZ_ASSERT(
3793       aFrame && aFrame->GetParent() &&
3794           aFrame->GetParent()->Type() == LayoutFrameType::Deck,
3795       "This function should only be called with a child frame of <deck>");
3796 
3797   nsIContent* capturingContent = GetCapturingContent();
3798   if (!capturingContent) {
3799     AllowMouseCapture(false);
3800     return;
3801   }
3802 
3803   nsIFrame* capturingFrame = capturingContent->GetPrimaryFrame();
3804   if (!capturingFrame) {
3805     ReleaseCapturingContent();
3806     AllowMouseCapture(false);
3807     return;
3808   }
3809 
3810   if (nsLayoutUtils::IsAncestorFrameCrossDocInProcess(aFrame, capturingFrame)) {
3811     ReleaseCapturingContent();
3812     AllowMouseCapture(false);
3813   }
3814 }
3815 
CaptureHistoryState(nsILayoutHistoryState ** aState)3816 nsresult PresShell::CaptureHistoryState(nsILayoutHistoryState** aState) {
3817   MOZ_ASSERT(nullptr != aState, "null state pointer");
3818 
3819   // We actually have to mess with the docshell here, since we want to
3820   // store the state back in it.
3821   // XXXbz this isn't really right, since this is being called in the
3822   // content viewer's Hide() method...  by that point the docshell's
3823   // state could be wrong.  We should sort out a better ownership
3824   // model for the layout history state.
3825   nsCOMPtr<nsIDocShell> docShell(mPresContext->GetDocShell());
3826   if (!docShell) return NS_ERROR_FAILURE;
3827 
3828   nsCOMPtr<nsILayoutHistoryState> historyState;
3829   docShell->GetLayoutHistoryState(getter_AddRefs(historyState));
3830   if (!historyState) {
3831     // Create the document state object
3832     historyState = NS_NewLayoutHistoryState();
3833     docShell->SetLayoutHistoryState(historyState);
3834   }
3835 
3836   *aState = historyState;
3837   NS_IF_ADDREF(*aState);
3838 
3839   // Capture frame state for the entire frame hierarchy
3840   nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
3841   if (!rootFrame) return NS_OK;
3842 
3843   mFrameConstructor->CaptureFrameState(rootFrame, historyState);
3844 
3845   return NS_OK;
3846 }
3847 
ScheduleBeforeFirstPaint()3848 void PresShell::ScheduleBeforeFirstPaint() {
3849   if (!mDocument->IsResourceDoc()) {
3850     // Notify observers that a new page is about to be drawn. Execute this
3851     // as soon as it is safe to run JS, which is guaranteed to be before we
3852     // go back to the event loop and actually draw the page.
3853     MOZ_LOG(gLog, LogLevel::Debug,
3854             ("PresShell::ScheduleBeforeFirstPaint this=%p", this));
3855 
3856     nsContentUtils::AddScriptRunner(
3857         new nsBeforeFirstPaintDispatcher(mDocument));
3858   }
3859 }
3860 
UnsuppressAndInvalidate()3861 void PresShell::UnsuppressAndInvalidate() {
3862   // Note: We ignore the EnsureVisible check for resource documents, because
3863   // they won't have a docshell, so they'll always fail EnsureVisible.
3864   if ((!mDocument->IsResourceDoc() && !mPresContext->EnsureVisible()) ||
3865       mHaveShutDown) {
3866     // No point; we're about to be torn down anyway.
3867     return;
3868   }
3869 
3870   ScheduleBeforeFirstPaint();
3871 
3872   mPaintingSuppressed = false;
3873   nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
3874   if (rootFrame) {
3875     // let's assume that outline on a root frame is not supported
3876     rootFrame->InvalidateFrame();
3877   }
3878 
3879   // now that painting is unsuppressed, focus may be set on the document
3880   if (nsPIDOMWindowOuter* win = mDocument->GetWindow()) {
3881     win->SetReadyForFocus();
3882   }
3883 
3884   if (!mHaveShutDown) {
3885     SynthesizeMouseMove(false);
3886     ScheduleApproximateFrameVisibilityUpdateNow();
3887   }
3888 }
3889 
CancelPaintSuppressionTimer()3890 void PresShell::CancelPaintSuppressionTimer() {
3891   if (mPaintSuppressionTimer) {
3892     mPaintSuppressionTimer->Cancel();
3893     mPaintSuppressionTimer = nullptr;
3894   }
3895 }
3896 
UnsuppressPainting()3897 void PresShell::UnsuppressPainting() {
3898   CancelPaintSuppressionTimer();
3899 
3900   if (mIsDocumentGone || !mPaintingSuppressed) {
3901     return;
3902   }
3903 
3904   // If we have reflows pending, just wait until we process
3905   // the reflows and get all the frames where we want them
3906   // before actually unlocking the painting.  Otherwise
3907   // go ahead and unlock now.
3908   if (!mDirtyRoots.IsEmpty())
3909     mShouldUnsuppressPainting = true;
3910   else
3911     UnsuppressAndInvalidate();
3912 }
3913 
3914 // Post a request to handle an arbitrary callback after reflow has finished.
PostReflowCallback(nsIReflowCallback * aCallback)3915 nsresult PresShell::PostReflowCallback(nsIReflowCallback* aCallback) {
3916   void* result = AllocateByObjectID(eArenaObjectID_nsCallbackEventRequest,
3917                                     sizeof(nsCallbackEventRequest));
3918   nsCallbackEventRequest* request = (nsCallbackEventRequest*)result;
3919 
3920   request->callback = aCallback;
3921   request->next = nullptr;
3922 
3923   if (mLastCallbackEventRequest) {
3924     mLastCallbackEventRequest = mLastCallbackEventRequest->next = request;
3925   } else {
3926     mFirstCallbackEventRequest = request;
3927     mLastCallbackEventRequest = request;
3928   }
3929 
3930   return NS_OK;
3931 }
3932 
CancelReflowCallback(nsIReflowCallback * aCallback)3933 void PresShell::CancelReflowCallback(nsIReflowCallback* aCallback) {
3934   nsCallbackEventRequest* before = nullptr;
3935   nsCallbackEventRequest* node = mFirstCallbackEventRequest;
3936   while (node) {
3937     nsIReflowCallback* callback = node->callback;
3938 
3939     if (callback == aCallback) {
3940       nsCallbackEventRequest* toFree = node;
3941       if (node == mFirstCallbackEventRequest) {
3942         node = node->next;
3943         mFirstCallbackEventRequest = node;
3944         NS_ASSERTION(before == nullptr, "impossible");
3945       } else {
3946         node = node->next;
3947         before->next = node;
3948       }
3949 
3950       if (toFree == mLastCallbackEventRequest) {
3951         mLastCallbackEventRequest = before;
3952       }
3953 
3954       FreeByObjectID(eArenaObjectID_nsCallbackEventRequest, toFree);
3955     } else {
3956       before = node;
3957       node = node->next;
3958     }
3959   }
3960 }
3961 
CancelPostedReflowCallbacks()3962 void PresShell::CancelPostedReflowCallbacks() {
3963   while (mFirstCallbackEventRequest) {
3964     nsCallbackEventRequest* node = mFirstCallbackEventRequest;
3965     mFirstCallbackEventRequest = node->next;
3966     if (!mFirstCallbackEventRequest) {
3967       mLastCallbackEventRequest = nullptr;
3968     }
3969     nsIReflowCallback* callback = node->callback;
3970     FreeByObjectID(eArenaObjectID_nsCallbackEventRequest, node);
3971     if (callback) {
3972       callback->ReflowCallbackCanceled();
3973     }
3974   }
3975 }
3976 
HandlePostedReflowCallbacks(bool aInterruptible)3977 void PresShell::HandlePostedReflowCallbacks(bool aInterruptible) {
3978   bool shouldFlush = false;
3979 
3980   while (mFirstCallbackEventRequest) {
3981     nsCallbackEventRequest* node = mFirstCallbackEventRequest;
3982     mFirstCallbackEventRequest = node->next;
3983     if (!mFirstCallbackEventRequest) {
3984       mLastCallbackEventRequest = nullptr;
3985     }
3986     nsIReflowCallback* callback = node->callback;
3987     FreeByObjectID(eArenaObjectID_nsCallbackEventRequest, node);
3988     if (callback) {
3989       if (callback->ReflowFinished()) {
3990         shouldFlush = true;
3991       }
3992     }
3993   }
3994 
3995   FlushType flushType =
3996       aInterruptible ? FlushType::InterruptibleLayout : FlushType::Layout;
3997   if (shouldFlush && !mIsDestroying) {
3998     FlushPendingNotifications(flushType);
3999   }
4000 }
4001 
IsSafeToFlush() const4002 bool PresShell::IsSafeToFlush() const {
4003   // Not safe if we are getting torn down, reflowing, or in the middle of frame
4004   // construction.
4005   if (mIsReflowing || mChangeNestCount || mIsDestroying) {
4006     return false;
4007   }
4008 
4009   // Not safe if we are painting
4010   if (nsViewManager* viewManager = GetViewManager()) {
4011     bool isPainting = false;
4012     viewManager->IsPainting(isPainting);
4013     if (isPainting) {
4014       return false;
4015     }
4016   }
4017 
4018   return true;
4019 }
4020 
NotifyFontFaceSetOnRefresh()4021 void PresShell::NotifyFontFaceSetOnRefresh() {
4022   if (FontFaceSet* set = mDocument->GetFonts()) {
4023     set->DidRefresh();
4024   }
4025 }
4026 
DoFlushPendingNotifications(FlushType aType)4027 void PresShell::DoFlushPendingNotifications(FlushType aType) {
4028   // by default, flush animations if aType >= FlushType::Style
4029   mozilla::ChangesToFlush flush(aType, aType >= FlushType::Style);
4030   FlushPendingNotifications(flush);
4031 }
4032 
4033 #ifdef DEBUG
AssertFrameSubtreeIsSane(const nsIFrame & aRoot)4034 static void AssertFrameSubtreeIsSane(const nsIFrame& aRoot) {
4035   if (const nsIContent* content = aRoot.GetContent()) {
4036     MOZ_ASSERT(content->GetFlattenedTreeParentNodeForStyle(),
4037                "Node not in the flattened tree still has a frame?");
4038   }
4039 
4040   for (const auto& childList : aRoot.ChildLists()) {
4041     for (const nsIFrame* child : childList.mList) {
4042       AssertFrameSubtreeIsSane(*child);
4043     }
4044   }
4045 }
4046 #endif
4047 
AssertFrameTreeIsSane(const PresShell & aPresShell)4048 static inline void AssertFrameTreeIsSane(const PresShell& aPresShell) {
4049 #ifdef DEBUG
4050   if (const nsIFrame* root = aPresShell.GetRootFrame()) {
4051     AssertFrameSubtreeIsSane(*root);
4052   }
4053 #endif
4054 }
4055 
DoFlushPendingNotifications(mozilla::ChangesToFlush aFlush)4056 void PresShell::DoFlushPendingNotifications(mozilla::ChangesToFlush aFlush) {
4057   // FIXME(emilio, bug 1530177): Turn into a release assert when bug 1530188 and
4058   // bug 1530190 are fixed.
4059   MOZ_DIAGNOSTIC_ASSERT(!mForbiddenToFlush, "This is bad!");
4060 
4061   // Per our API contract, hold a strong ref to ourselves until we return.
4062   RefPtr<PresShell> kungFuDeathGrip = this;
4063 
4064   /**
4065    * VERY IMPORTANT: If you add some sort of new flushing to this
4066    * method, make sure to add the relevant SetNeedLayoutFlush or
4067    * SetNeedStyleFlush calls on the shell.
4068    */
4069   FlushType flushType = aFlush.mFlushType;
4070 
4071   MOZ_ASSERT(NeedFlush(flushType), "Why did we get called?");
4072 
4073   AUTO_PROFILER_MARKER_TEXT(
4074       "DoFlushPendingNotifications", LAYOUT,
4075       MarkerOptions(MarkerStack::Capture(), MarkerInnerWindowIdFromDocShell(
4076                                                 mPresContext->GetDocShell())),
4077       nsDependentCString(kFlushTypeNames[flushType]));
4078   AUTO_PROFILER_LABEL_DYNAMIC_CSTR_NONSENSITIVE(
4079       "PresShell::DoFlushPendingNotifications", LAYOUT,
4080       kFlushTypeNames[flushType]);
4081 
4082 #ifdef ACCESSIBILITY
4083 #  ifdef DEBUG
4084   if (nsAccessibilityService* accService = GetAccService()) {
4085     NS_ASSERTION(!accService->IsProcessingRefreshDriverNotification(),
4086                  "Flush during accessible tree update!");
4087   }
4088 #  endif
4089 #endif
4090 
4091   NS_ASSERTION(flushType >= FlushType::Style, "Why did we get called?");
4092 
4093   mNeedStyleFlush = false;
4094   mNeedThrottledAnimationFlush =
4095       mNeedThrottledAnimationFlush && !aFlush.mFlushAnimations;
4096   mNeedLayoutFlush =
4097       mNeedLayoutFlush && (flushType < FlushType::InterruptibleLayout);
4098 
4099   bool isSafeToFlush = IsSafeToFlush();
4100 
4101   // If layout could possibly trigger scripts, then it's only safe to flush if
4102   // it's safe to run script.
4103   bool hasHadScriptObject;
4104   if (mDocument->GetScriptHandlingObject(hasHadScriptObject) ||
4105       hasHadScriptObject) {
4106     isSafeToFlush = isSafeToFlush && nsContentUtils::IsSafeToRunScript();
4107   }
4108 
4109   // Don't flush if the doc is already in the bfcache.
4110   if (MOZ_UNLIKELY(mDocument->GetPresShell() != this)) {
4111     MOZ_DIAGNOSTIC_ASSERT(!mDocument->GetPresShell(),
4112                           "Where did this shell come from?");
4113     isSafeToFlush = false;
4114   }
4115 
4116   MOZ_DIAGNOSTIC_ASSERT(!mIsDestroying || !isSafeToFlush);
4117   MOZ_DIAGNOSTIC_ASSERT(mIsDestroying || mViewManager);
4118   MOZ_DIAGNOSTIC_ASSERT(mIsDestroying || mDocument->HasShellOrBFCacheEntry());
4119 
4120   // Make sure the view manager stays alive.
4121   RefPtr<nsViewManager> viewManager = mViewManager;
4122   bool didStyleFlush = false;
4123   bool didLayoutFlush = false;
4124   if (isSafeToFlush) {
4125     // Record that we are in a flush, so that our optimization in
4126     // Document::FlushPendingNotifications doesn't skip any re-entrant
4127     // calls to us.  Otherwise, we might miss some needed flushes, since
4128     // we clear mNeedStyleFlush / mNeedLayoutFlush here at the top of
4129     // the function but we might not have done the work yet.
4130     AutoRestore<bool> guard(mInFlush);
4131     mInFlush = true;
4132 
4133     // We need to make sure external resource documents are flushed too (for
4134     // example, svg filters that reference a filter in an external document
4135     // need the frames in the external document to be constructed for the
4136     // filter to work). We only need external resources to be flushed when the
4137     // main document is flushing >= FlushType::Frames, so we flush external
4138     // resources here instead of Document::FlushPendingNotifications.
4139     mDocument->FlushExternalResources(flushType);
4140 
4141     // Force flushing of any pending content notifications that might have
4142     // queued up while our event was pending.  That will ensure that we don't
4143     // construct frames for content right now that's still waiting to be
4144     // notified on,
4145     mDocument->FlushPendingNotifications(FlushType::ContentAndNotify);
4146 
4147     mDocument->UpdateSVGUseElementShadowTrees();
4148 
4149     // Process pending restyles, since any flush of the presshell wants
4150     // up-to-date style data.
4151     if (MOZ_LIKELY(!mIsDestroying)) {
4152       viewManager->FlushDelayedResize(false);
4153       mPresContext->FlushPendingMediaFeatureValuesChanged();
4154     }
4155 
4156     if (MOZ_LIKELY(!mIsDestroying)) {
4157       // Now that we have flushed media queries, update the rules before looking
4158       // up @font-face / @counter-style / @font-feature-values rules.
4159       StyleSet()->UpdateStylistIfNeeded();
4160 
4161       // Flush any pending update of the user font set, since that could
4162       // cause style changes (for updating ex/ch units, and to cause a
4163       // reflow).
4164       mDocument->FlushUserFontSet();
4165 
4166       mPresContext->FlushCounterStyles();
4167 
4168       mPresContext->FlushFontFeatureValues();
4169 
4170       // Flush any requested SMIL samples.
4171       if (mDocument->HasAnimationController()) {
4172         mDocument->GetAnimationController()->FlushResampleRequests();
4173       }
4174 
4175       if (aFlush.mFlushAnimations && mPresContext->EffectCompositor()) {
4176         mPresContext->EffectCompositor()->PostRestyleForThrottledAnimations();
4177       }
4178     }
4179 
4180     // The FlushResampleRequests() above flushed style changes.
4181     if (MOZ_LIKELY(!mIsDestroying)) {
4182       nsAutoScriptBlocker scriptBlocker;
4183       Maybe<uint64_t> innerWindowID;
4184       if (auto* window = mDocument->GetInnerWindow()) {
4185         innerWindowID = Some(window->WindowID());
4186       }
4187       AutoProfilerStyleMarker tracingStyleFlush(std::move(mStyleCause),
4188                                                 innerWindowID);
4189       PerfStats::AutoMetricRecording<PerfStats::Metric::Styling> autoRecording;
4190       LAYOUT_TELEMETRY_RECORD_BASE(Restyle);
4191 
4192       mPresContext->RestyleManager()->ProcessPendingRestyles();
4193     }
4194 
4195     // Now those constructors or events might have posted restyle
4196     // events.  At the same time, we still need up-to-date style data.
4197     // In particular, reflow depends on style being completely up to
4198     // date.  If it's not, then style reparenting, which can
4199     // happen during reflow, might suddenly pick up the new rules and
4200     // we'll end up with frames whose style doesn't match the frame
4201     // type.
4202     if (MOZ_LIKELY(!mIsDestroying)) {
4203       nsAutoScriptBlocker scriptBlocker;
4204       Maybe<uint64_t> innerWindowID;
4205       if (auto* window = mDocument->GetInnerWindow()) {
4206         innerWindowID = Some(window->WindowID());
4207       }
4208       AutoProfilerStyleMarker tracingStyleFlush(std::move(mStyleCause),
4209                                                 innerWindowID);
4210       PerfStats::AutoMetricRecording<PerfStats::Metric::Styling> autoRecording;
4211       LAYOUT_TELEMETRY_RECORD_BASE(Restyle);
4212 
4213       mPresContext->RestyleManager()->ProcessPendingRestyles();
4214       // Clear mNeedStyleFlush here agagin to make this flag work properly for
4215       // optimization since the flag might have set in ProcessPendingRestyles().
4216       mNeedStyleFlush = false;
4217     }
4218 
4219     AssertFrameTreeIsSane(*this);
4220 
4221     didStyleFlush = true;
4222 
4223     // There might be more pending constructors now, but we're not going to
4224     // worry about them.  They can't be triggered during reflow, so we should
4225     // be good.
4226 
4227     if (flushType >= (SuppressInterruptibleReflows()
4228                           ? FlushType::Layout
4229                           : FlushType::InterruptibleLayout) &&
4230         !mIsDestroying) {
4231       didLayoutFlush = true;
4232       mFrameConstructor->RecalcQuotesAndCounters();
4233       if (ProcessReflowCommands(flushType < FlushType::Layout)) {
4234         if (mContentToScrollTo) {
4235           DoScrollContentIntoView();
4236           if (mContentToScrollTo) {
4237             mContentToScrollTo->RemoveProperty(nsGkAtoms::scrolling);
4238             mContentToScrollTo = nullptr;
4239           }
4240         }
4241       }
4242     }
4243 
4244     if (flushType >= FlushType::Layout) {
4245       if (!mIsDestroying) {
4246         viewManager->UpdateWidgetGeometry();
4247       }
4248     }
4249   }
4250 
4251   if (!didStyleFlush && flushType >= FlushType::Style && !mIsDestroying) {
4252     SetNeedStyleFlush();
4253     if (aFlush.mFlushAnimations) {
4254       SetNeedThrottledAnimationFlush();
4255     }
4256   }
4257 
4258   if (!didLayoutFlush && flushType >= FlushType::InterruptibleLayout &&
4259       !mIsDestroying) {
4260     // We suppressed this flush either due to it not being safe to flush,
4261     // or due to SuppressInterruptibleReflows().  Either way, the
4262     // mNeedLayoutFlush flag needs to be re-set.
4263     SetNeedLayoutFlush();
4264   }
4265 
4266   // Update flush counters
4267   if (didStyleFlush) {
4268     mLayoutTelemetry.IncReqsPerFlush(FlushType::Style);
4269   }
4270 
4271   if (didLayoutFlush) {
4272     mLayoutTelemetry.IncReqsPerFlush(FlushType::Layout);
4273   }
4274 
4275   // Record telemetry for the number of requests per each flush type.
4276   //
4277   // Flushes happen as style or style+layout. This depends upon the `flushType`
4278   // where flushType >= InterruptibleLayout means flush layout and flushType >=
4279   // Style means flush style. We only report if didLayoutFlush or didStyleFlush
4280   // is true because we care if a flush really did take place. (Flush is guarded
4281   // by `isSafeToFlush == true`.)
4282   if (flushType >= FlushType::InterruptibleLayout && didLayoutFlush) {
4283     MOZ_ASSERT(didLayoutFlush == didStyleFlush);
4284     mLayoutTelemetry.PingReqsPerFlushTelemetry(FlushType::Layout);
4285   } else if (flushType >= FlushType::Style && didStyleFlush) {
4286     MOZ_ASSERT(!didLayoutFlush);
4287     mLayoutTelemetry.PingReqsPerFlushTelemetry(FlushType::Style);
4288   }
4289 }
4290 
CharacterDataChanged(nsIContent * aContent,const CharacterDataChangeInfo & aInfo)4291 MOZ_CAN_RUN_SCRIPT_BOUNDARY void PresShell::CharacterDataChanged(
4292     nsIContent* aContent, const CharacterDataChangeInfo& aInfo) {
4293   MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript());
4294   MOZ_ASSERT(!mIsDocumentGone, "Unexpected CharacterDataChanged");
4295   MOZ_ASSERT(aContent->OwnerDoc() == mDocument, "Unexpected document");
4296 
4297   nsAutoCauseReflowNotifier crNotifier(this);
4298 
4299   mPresContext->RestyleManager()->CharacterDataChanged(aContent, aInfo);
4300   mFrameConstructor->CharacterDataChanged(aContent, aInfo);
4301 }
4302 
ContentStateChanged(Document * aDocument,nsIContent * aContent,EventStates aStateMask)4303 MOZ_CAN_RUN_SCRIPT_BOUNDARY void PresShell::ContentStateChanged(
4304     Document* aDocument, nsIContent* aContent, EventStates aStateMask) {
4305   MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript());
4306   MOZ_ASSERT(!mIsDocumentGone, "Unexpected ContentStateChanged");
4307   MOZ_ASSERT(aDocument == mDocument, "Unexpected aDocument");
4308 
4309   if (mDidInitialize) {
4310     nsAutoCauseReflowNotifier crNotifier(this);
4311     mPresContext->RestyleManager()->ContentStateChanged(aContent, aStateMask);
4312   }
4313 }
4314 
DocumentStatesChanged(EventStates aStateMask)4315 void PresShell::DocumentStatesChanged(EventStates aStateMask) {
4316   MOZ_ASSERT(!mIsDocumentGone, "Unexpected DocumentStatesChanged");
4317   MOZ_ASSERT(mDocument);
4318   MOZ_ASSERT(!aStateMask.IsEmpty());
4319 
4320   if (mDidInitialize) {
4321     StyleSet()->InvalidateStyleForDocumentStateChanges(aStateMask);
4322   }
4323 
4324   if (aStateMask.HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE)) {
4325     if (nsIFrame* root = mFrameConstructor->GetRootFrame()) {
4326       root->SchedulePaint();
4327     }
4328   }
4329 }
4330 
AttributeWillChange(Element * aElement,int32_t aNameSpaceID,nsAtom * aAttribute,int32_t aModType)4331 MOZ_CAN_RUN_SCRIPT_BOUNDARY void PresShell::AttributeWillChange(
4332     Element* aElement, int32_t aNameSpaceID, nsAtom* aAttribute,
4333     int32_t aModType) {
4334   MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript());
4335   MOZ_ASSERT(!mIsDocumentGone, "Unexpected AttributeWillChange");
4336   MOZ_ASSERT(aElement->OwnerDoc() == mDocument, "Unexpected document");
4337 
4338   // XXXwaterson it might be more elegant to wait until after the
4339   // initial reflow to begin observing the document. That would
4340   // squelch any other inappropriate notifications as well.
4341   if (mDidInitialize) {
4342     nsAutoCauseReflowNotifier crNotifier(this);
4343     mPresContext->RestyleManager()->AttributeWillChange(aElement, aNameSpaceID,
4344                                                         aAttribute, aModType);
4345   }
4346 }
4347 
AttributeChanged(Element * aElement,int32_t aNameSpaceID,nsAtom * aAttribute,int32_t aModType,const nsAttrValue * aOldValue)4348 MOZ_CAN_RUN_SCRIPT_BOUNDARY void PresShell::AttributeChanged(
4349     Element* aElement, int32_t aNameSpaceID, nsAtom* aAttribute,
4350     int32_t aModType, const nsAttrValue* aOldValue) {
4351   MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript());
4352   MOZ_ASSERT(!mIsDocumentGone, "Unexpected AttributeChanged");
4353   MOZ_ASSERT(aElement->OwnerDoc() == mDocument, "Unexpected document");
4354 
4355   // XXXwaterson it might be more elegant to wait until after the
4356   // initial reflow to begin observing the document. That would
4357   // squelch any other inappropriate notifications as well.
4358   if (mDidInitialize) {
4359     nsAutoCauseReflowNotifier crNotifier(this);
4360     mPresContext->RestyleManager()->AttributeChanged(
4361         aElement, aNameSpaceID, aAttribute, aModType, aOldValue);
4362   }
4363 }
4364 
ContentAppended(nsIContent * aFirstNewContent)4365 MOZ_CAN_RUN_SCRIPT_BOUNDARY void PresShell::ContentAppended(
4366     nsIContent* aFirstNewContent) {
4367   MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript());
4368   MOZ_ASSERT(!mIsDocumentGone, "Unexpected ContentAppended");
4369   MOZ_ASSERT(aFirstNewContent->OwnerDoc() == mDocument, "Unexpected document");
4370 
4371   // We never call ContentAppended with a document as the container, so we can
4372   // assert that we have an nsIContent parent.
4373   MOZ_ASSERT(aFirstNewContent->GetParent());
4374   MOZ_ASSERT(aFirstNewContent->GetParent()->IsElement() ||
4375              aFirstNewContent->GetParent()->IsShadowRoot());
4376 
4377   if (!mDidInitialize) {
4378     return;
4379   }
4380 
4381   nsAutoCauseReflowNotifier crNotifier(this);
4382 
4383   // Call this here so it only happens for real content mutations and
4384   // not cases when the frame constructor calls its own methods to force
4385   // frame reconstruction.
4386   mPresContext->RestyleManager()->ContentAppended(aFirstNewContent);
4387 
4388   mFrameConstructor->ContentAppended(
4389       aFirstNewContent, nsCSSFrameConstructor::InsertionKind::Async);
4390 }
4391 
ContentInserted(nsIContent * aChild)4392 MOZ_CAN_RUN_SCRIPT_BOUNDARY void PresShell::ContentInserted(
4393     nsIContent* aChild) {
4394   MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript());
4395   MOZ_ASSERT(!mIsDocumentGone, "Unexpected ContentInserted");
4396   MOZ_ASSERT(aChild->OwnerDoc() == mDocument, "Unexpected document");
4397 
4398   if (!mDidInitialize) {
4399     return;
4400   }
4401 
4402   nsAutoCauseReflowNotifier crNotifier(this);
4403 
4404   // Call this here so it only happens for real content mutations and
4405   // not cases when the frame constructor calls its own methods to force
4406   // frame reconstruction.
4407   mPresContext->RestyleManager()->ContentInserted(aChild);
4408 
4409   mFrameConstructor->ContentInserted(
4410       aChild, nsCSSFrameConstructor::InsertionKind::Async);
4411 }
4412 
ContentRemoved(nsIContent * aChild,nsIContent * aPreviousSibling)4413 MOZ_CAN_RUN_SCRIPT_BOUNDARY void PresShell::ContentRemoved(
4414     nsIContent* aChild, nsIContent* aPreviousSibling) {
4415   MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript());
4416   MOZ_ASSERT(!mIsDocumentGone, "Unexpected ContentRemoved");
4417   MOZ_ASSERT(aChild->OwnerDoc() == mDocument, "Unexpected document");
4418   nsINode* container = aChild->GetParentNode();
4419 
4420   // Notify the ESM that the content has been removed, so that
4421   // it can clean up any state related to the content.
4422 
4423   mPresContext->EventStateManager()->ContentRemoved(mDocument, aChild);
4424 
4425   nsAutoCauseReflowNotifier crNotifier(this);
4426 
4427   // Call this here so it only happens for real content mutations and
4428   // not cases when the frame constructor calls its own methods to force
4429   // frame reconstruction.
4430   nsIContent* oldNextSibling = nullptr;
4431 
4432   // Editor calls into here with NAC via HTMLEditor::DeleteRefToAnonymousNode.
4433   // This could be asserted if that caller is fixed.
4434   if (MOZ_LIKELY(!aChild->IsRootOfNativeAnonymousSubtree())) {
4435     oldNextSibling = aPreviousSibling ? aPreviousSibling->GetNextSibling()
4436                                       : container->GetFirstChild();
4437   }
4438 
4439   // After removing aChild from tree we should save information about live
4440   // ancestor
4441   if (mPointerEventTarget &&
4442       mPointerEventTarget->IsInclusiveDescendantOf(aChild)) {
4443     mPointerEventTarget = aChild->GetParent();
4444   }
4445 
4446   mFrameConstructor->ContentRemoved(aChild, oldNextSibling,
4447                                     nsCSSFrameConstructor::REMOVE_CONTENT);
4448 
4449   // NOTE(emilio): It's important that this goes after the frame constructor
4450   // stuff, otherwise the frame constructor can't see elements which are
4451   // display: contents / display: none, because we'd have cleared all the style
4452   // data from there.
4453   mPresContext->RestyleManager()->ContentRemoved(aChild, oldNextSibling);
4454 }
4455 
NotifyCounterStylesAreDirty()4456 void PresShell::NotifyCounterStylesAreDirty() {
4457   // TODO: Looks like that nsFrameConstructor::NotifyCounterStylesAreDirty()
4458   //       does not run script.  If so, we don't need to block script with
4459   //       nsAutoCauseReflowNotifier here.  Instead, there should be methods
4460   //       and stack only class which manages only mChangeNestCount for
4461   //       avoiding unnecessary `MOZ_CAN_RUN_SCRIPT` marking.
4462   nsAutoCauseReflowNotifier reflowNotifier(this);
4463   mFrameConstructor->NotifyCounterStylesAreDirty();
4464 }
4465 
FrameIsAncestorOfDirtyRoot(nsIFrame * aFrame) const4466 bool PresShell::FrameIsAncestorOfDirtyRoot(nsIFrame* aFrame) const {
4467   return mDirtyRoots.FrameIsAncestorOfDirtyRoot(aFrame);
4468 }
4469 
ReconstructFrames()4470 void PresShell::ReconstructFrames() {
4471   MOZ_ASSERT(!mFrameConstructor->GetRootFrame() || mDidInitialize,
4472              "Must not have root frame before initial reflow");
4473   if (!mDidInitialize || mIsDestroying) {
4474     // Nothing to do here
4475     return;
4476   }
4477 
4478   // Have to make sure that the content notifications are flushed before we
4479   // start messing with the frame model; otherwise we can get content doubling.
4480   //
4481   // Also make sure that styles are flushed before calling into the frame
4482   // constructor, since that's what it expects.
4483   mDocument->FlushPendingNotifications(FlushType::Style);
4484 
4485   if (mIsDestroying) {
4486     return;
4487   }
4488 
4489   nsAutoCauseReflowNotifier crNotifier(this);
4490   mFrameConstructor->ReconstructDocElementHierarchy(
4491       nsCSSFrameConstructor::InsertionKind::Sync);
4492 }
4493 
RenderDocument(const nsRect & aRect,RenderDocumentFlags aFlags,nscolor aBackgroundColor,gfxContext * aThebesContext)4494 nsresult PresShell::RenderDocument(const nsRect& aRect,
4495                                    RenderDocumentFlags aFlags,
4496                                    nscolor aBackgroundColor,
4497                                    gfxContext* aThebesContext) {
4498   NS_ENSURE_TRUE(!(aFlags & RenderDocumentFlags::IsUntrusted),
4499                  NS_ERROR_NOT_IMPLEMENTED);
4500 
4501   nsRootPresContext* rootPresContext = mPresContext->GetRootPresContext();
4502   if (rootPresContext) {
4503     rootPresContext->FlushWillPaintObservers();
4504     if (mIsDestroying) return NS_OK;
4505   }
4506 
4507   nsAutoScriptBlocker blockScripts;
4508 
4509   // Set up the rectangle as the path in aThebesContext
4510   gfxRect r(0, 0, nsPresContext::AppUnitsToFloatCSSPixels(aRect.width),
4511             nsPresContext::AppUnitsToFloatCSSPixels(aRect.height));
4512   aThebesContext->NewPath();
4513 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
4514   aThebesContext->SnappedRectangle(r);
4515 #else
4516   aThebesContext->Rectangle(r);
4517 #endif
4518 
4519   nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
4520   if (!rootFrame) {
4521     // Nothing to paint, just fill the rect
4522     aThebesContext->SetColor(sRGBColor::FromABGR(aBackgroundColor));
4523     aThebesContext->Fill();
4524     return NS_OK;
4525   }
4526 
4527   gfxContextAutoSaveRestore save(aThebesContext);
4528 
4529   MOZ_ASSERT(aThebesContext->CurrentOp() == CompositionOp::OP_OVER);
4530 
4531   aThebesContext->Clip();
4532 
4533   nsDeviceContext* devCtx = mPresContext->DeviceContext();
4534 
4535   gfxPoint offset(-nsPresContext::AppUnitsToFloatCSSPixels(aRect.x),
4536                   -nsPresContext::AppUnitsToFloatCSSPixels(aRect.y));
4537   gfxFloat scale =
4538       gfxFloat(devCtx->AppUnitsPerDevPixel()) / AppUnitsPerCSSPixel();
4539 
4540   // Since canvas APIs use floats to set up their matrices, we may have some
4541   // slight rounding errors here.  We use NudgeToIntegers() here to adjust
4542   // matrix components that are integers up to the accuracy of floats to be
4543   // those integers.
4544   gfxMatrix newTM = aThebesContext->CurrentMatrixDouble()
4545                         .PreTranslate(offset)
4546                         .PreScale(scale, scale)
4547                         .NudgeToIntegers();
4548   aThebesContext->SetMatrixDouble(newTM);
4549 
4550   AutoSaveRestoreRenderingState _(this);
4551 
4552   bool wouldFlushRetainedLayers = false;
4553   PaintFrameFlags flags = PaintFrameFlags::IgnoreSuppression;
4554   if (aThebesContext->CurrentMatrix().HasNonIntegerTranslation()) {
4555     flags |= PaintFrameFlags::InTransform;
4556   }
4557   if (!(aFlags & RenderDocumentFlags::AsyncDecodeImages)) {
4558     flags |= PaintFrameFlags::SyncDecodeImages;
4559   }
4560   if (aFlags & RenderDocumentFlags::UseHighQualityScaling) {
4561     flags |= PaintFrameFlags::UseHighQualityScaling;
4562   }
4563   if (aFlags & RenderDocumentFlags::UseWidgetLayers) {
4564     // We only support using widget layers on display root's with widgets.
4565     nsView* view = rootFrame->GetView();
4566     if (view && view->GetWidget() &&
4567         nsLayoutUtils::GetDisplayRootFrame(rootFrame) == rootFrame) {
4568       LayerManager* layerManager = view->GetWidget()->GetLayerManager();
4569       // ClientLayerManagers or WebRenderLayerManagers in content processes
4570       // don't support taking snapshots.
4571       if (layerManager &&
4572           (!layerManager->AsKnowsCompositor() || XRE_IsParentProcess())) {
4573         flags |= PaintFrameFlags::WidgetLayers;
4574       }
4575     }
4576   }
4577   if (!(aFlags & RenderDocumentFlags::DrawCaret)) {
4578     wouldFlushRetainedLayers = true;
4579     flags |= PaintFrameFlags::HideCaret;
4580   }
4581   if (aFlags & RenderDocumentFlags::IgnoreViewportScrolling) {
4582     wouldFlushRetainedLayers = !IgnoringViewportScrolling();
4583     mRenderingStateFlags |= RenderingStateFlags::IgnoringViewportScrolling;
4584   }
4585   if (aFlags & RenderDocumentFlags::ResetViewportScrolling) {
4586     wouldFlushRetainedLayers = true;
4587     flags |= PaintFrameFlags::ResetViewportScrolling;
4588   }
4589   if (aFlags & RenderDocumentFlags::DrawWindowNotFlushing) {
4590     mRenderingStateFlags |= RenderingStateFlags::DrawWindowNotFlushing;
4591   }
4592   if (aFlags & RenderDocumentFlags::DocumentRelative) {
4593     // XXX be smarter about this ... drawWindow might want a rect
4594     // that's "pretty close" to what our retained layer tree covers.
4595     // In that case, it wouldn't disturb normal rendering too much,
4596     // and we should allow it.
4597     wouldFlushRetainedLayers = true;
4598     flags |= PaintFrameFlags::DocumentRelative;
4599   }
4600 
4601   // Don't let drawWindow blow away our retained layer tree
4602   if ((flags & PaintFrameFlags::WidgetLayers) && wouldFlushRetainedLayers) {
4603     flags &= ~PaintFrameFlags::WidgetLayers;
4604   }
4605 
4606   nsLayoutUtils::PaintFrame(aThebesContext, rootFrame, nsRegion(aRect),
4607                             aBackgroundColor,
4608                             nsDisplayListBuilderMode::Painting, flags);
4609 
4610   return NS_OK;
4611 }
4612 
4613 /*
4614  * Clip the display list aList to a range. Returns the clipped
4615  * rectangle surrounding the range.
4616  */
ClipListToRange(nsDisplayListBuilder * aBuilder,nsDisplayList * aList,nsRange * aRange)4617 nsRect PresShell::ClipListToRange(nsDisplayListBuilder* aBuilder,
4618                                   nsDisplayList* aList, nsRange* aRange) {
4619   // iterate though the display items and add up the bounding boxes of each.
4620   // This will allow the total area of the frames within the range to be
4621   // determined. To do this, remove an item from the bottom of the list, check
4622   // whether it should be part of the range, and if so, append it to the top
4623   // of the temporary list tmpList. If the item is a text frame at the end of
4624   // the selection range, clip it to the portion of the text frame that is
4625   // part of the selection. Then, append the wrapper to the top of the list.
4626   // Otherwise, just delete the item and don't append it.
4627   nsRect surfaceRect;
4628   nsDisplayList tmpList;
4629 
4630   nsDisplayItem* i;
4631   while ((i = aList->RemoveBottom())) {
4632     if (i->GetType() == DisplayItemType::TYPE_CONTAINER) {
4633       tmpList.AppendToTop(i);
4634       surfaceRect.UnionRect(
4635           surfaceRect, ClipListToRange(aBuilder, i->GetChildren(), aRange));
4636       continue;
4637     }
4638 
4639     // itemToInsert indiciates the item that should be inserted into the
4640     // temporary list. If null, no item should be inserted.
4641     nsDisplayItem* itemToInsert = nullptr;
4642     nsIFrame* frame = i->Frame();
4643     nsIContent* content = frame->GetContent();
4644     if (content) {
4645       bool atStart = (content == aRange->GetStartContainer());
4646       bool atEnd = (content == aRange->GetEndContainer());
4647       if ((atStart || atEnd) && frame->IsTextFrame()) {
4648         auto [frameStartOffset, frameEndOffset] = frame->GetOffsets();
4649 
4650         int32_t hilightStart =
4651             atStart ? std::max(static_cast<int32_t>(aRange->StartOffset()),
4652                                frameStartOffset)
4653                     : frameStartOffset;
4654         int32_t hilightEnd =
4655             atEnd ? std::min(static_cast<int32_t>(aRange->EndOffset()),
4656                              frameEndOffset)
4657                   : frameEndOffset;
4658         if (hilightStart < hilightEnd) {
4659           // determine the location of the start and end edges of the range.
4660           nsPoint startPoint, endPoint;
4661           frame->GetPointFromOffset(hilightStart, &startPoint);
4662           frame->GetPointFromOffset(hilightEnd, &endPoint);
4663 
4664           // The clip rectangle is determined by taking the the start and
4665           // end points of the range, offset from the reference frame.
4666           // Because of rtl, the end point may be to the left of (or above,
4667           // in vertical mode) the start point, so x (or y) is set to the
4668           // lower of the values.
4669           nsRect textRect(aBuilder->ToReferenceFrame(frame), frame->GetSize());
4670           if (frame->GetWritingMode().IsVertical()) {
4671             nscoord y = std::min(startPoint.y, endPoint.y);
4672             textRect.y += y;
4673             textRect.height = std::max(startPoint.y, endPoint.y) - y;
4674           } else {
4675             nscoord x = std::min(startPoint.x, endPoint.x);
4676             textRect.x += x;
4677             textRect.width = std::max(startPoint.x, endPoint.x) - x;
4678           }
4679           surfaceRect.UnionRect(surfaceRect, textRect);
4680 
4681           const ActiveScrolledRoot* asr = i->GetActiveScrolledRoot();
4682 
4683           DisplayItemClip newClip;
4684           newClip.SetTo(textRect);
4685 
4686           const DisplayItemClipChain* newClipChain =
4687               aBuilder->AllocateDisplayItemClipChain(newClip, asr, nullptr);
4688 
4689           i->IntersectClip(aBuilder, newClipChain, true);
4690           itemToInsert = i;
4691         }
4692       }
4693       // Don't try to descend into subdocuments.
4694       // If this ever changes we'd need to add handling for subdocuments with
4695       // different zoom levels.
4696       else if (content->GetUncomposedDoc() ==
4697                aRange->GetStartContainer()->GetUncomposedDoc()) {
4698         // if the node is within the range, append it to the temporary list
4699         bool before, after;
4700         nsresult rv =
4701             RangeUtils::CompareNodeToRange(content, aRange, &before, &after);
4702         if (NS_SUCCEEDED(rv) && !before && !after) {
4703           itemToInsert = i;
4704           bool snap;
4705           surfaceRect.UnionRect(surfaceRect, i->GetBounds(aBuilder, &snap));
4706         }
4707       }
4708     }
4709 
4710     // insert the item into the list if necessary. If the item has a child
4711     // list, insert that as well
4712     nsDisplayList* sublist = i->GetSameCoordinateSystemChildren();
4713     if (itemToInsert || sublist) {
4714       tmpList.AppendToTop(itemToInsert ? itemToInsert : i);
4715       // if the item is a list, iterate over it as well
4716       if (sublist)
4717         surfaceRect.UnionRect(surfaceRect,
4718                               ClipListToRange(aBuilder, sublist, aRange));
4719     } else {
4720       // otherwise, just delete the item and don't readd it to the list
4721       i->Destroy(aBuilder);
4722     }
4723   }
4724 
4725   // now add all the items back onto the original list again
4726   aList->AppendToTop(&tmpList);
4727 
4728   return surfaceRect;
4729 }
4730 
4731 #ifdef DEBUG
4732 #  include <stdio.h>
4733 
4734 static bool gDumpRangePaintList = false;
4735 #endif
4736 
CreateRangePaintInfo(nsRange * aRange,nsRect & aSurfaceRect,bool aForPrimarySelection)4737 UniquePtr<RangePaintInfo> PresShell::CreateRangePaintInfo(
4738     nsRange* aRange, nsRect& aSurfaceRect, bool aForPrimarySelection) {
4739   nsIFrame* ancestorFrame = nullptr;
4740   nsIFrame* rootFrame = GetRootFrame();
4741 
4742   // If the start or end of the range is the document, just use the root
4743   // frame, otherwise get the common ancestor of the two endpoints of the
4744   // range.
4745   nsINode* startContainer = aRange->GetStartContainer();
4746   nsINode* endContainer = aRange->GetEndContainer();
4747   Document* doc = startContainer->GetComposedDoc();
4748   if (startContainer == doc || endContainer == doc) {
4749     ancestorFrame = rootFrame;
4750   } else {
4751     nsINode* ancestor = nsContentUtils::GetClosestCommonInclusiveAncestor(
4752         startContainer, endContainer);
4753     NS_ASSERTION(!ancestor || ancestor->IsContent(),
4754                  "common ancestor is not content");
4755 
4756     while (ancestor && ancestor->IsContent()) {
4757       ancestorFrame = ancestor->AsContent()->GetPrimaryFrame();
4758       if (ancestorFrame) {
4759         break;
4760       }
4761 
4762       ancestor = ancestor->GetParentOrShadowHostNode();
4763     }
4764 
4765     // use the nearest ancestor frame that includes all continuations as the
4766     // root for building the display list
4767     while (ancestorFrame &&
4768            nsLayoutUtils::GetNextContinuationOrIBSplitSibling(ancestorFrame))
4769       ancestorFrame = ancestorFrame->GetParent();
4770   }
4771 
4772   if (!ancestorFrame) {
4773     return nullptr;
4774   }
4775 
4776   // get a display list containing the range
4777   auto info = MakeUnique<RangePaintInfo>(aRange, ancestorFrame);
4778   info->mBuilder.SetIncludeAllOutOfFlows();
4779   if (aForPrimarySelection) {
4780     info->mBuilder.SetSelectedFramesOnly();
4781   }
4782   info->mBuilder.EnterPresShell(ancestorFrame);
4783 
4784   ContentSubtreeIterator subtreeIter;
4785   nsresult rv = subtreeIter.Init(aRange);
4786   if (NS_FAILED(rv)) {
4787     return nullptr;
4788   }
4789 
4790   auto BuildDisplayListForNode = [&](nsINode* aNode) {
4791     if (MOZ_UNLIKELY(!aNode->IsContent())) {
4792       return;
4793     }
4794     nsIFrame* frame = aNode->AsContent()->GetPrimaryFrame();
4795     // XXX deal with frame being null due to display:contents
4796     for (; frame;
4797          frame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(frame)) {
4798       info->mBuilder.SetVisibleRect(frame->InkOverflowRect());
4799       info->mBuilder.SetDirtyRect(frame->InkOverflowRect());
4800       frame->BuildDisplayListForStackingContext(&info->mBuilder, &info->mList);
4801     }
4802   };
4803   if (startContainer->NodeType() == nsINode::TEXT_NODE) {
4804     BuildDisplayListForNode(startContainer);
4805   }
4806   for (; !subtreeIter.IsDone(); subtreeIter.Next()) {
4807     nsCOMPtr<nsINode> node = subtreeIter.GetCurrentNode();
4808     BuildDisplayListForNode(node);
4809   }
4810   if (endContainer != startContainer &&
4811       endContainer->NodeType() == nsINode::TEXT_NODE) {
4812     BuildDisplayListForNode(endContainer);
4813   }
4814 
4815   // If one of the ancestor presShells (including this one) has a resolution
4816   // set, we may have some APZ zoom applied. That means we may want to rasterize
4817   // the nodes at that zoom level. Populate `info` with the relevant information
4818   // so that the caller can decide what to do. Also wrap the display list in
4819   // appropriate nsDisplayAsyncZoom display items. This code handles the general
4820   // case with nested async zooms (even though that never actually happens),
4821   // because it fell out of the implementation for free.
4822   //
4823   // TODO: Do we need to do the same for ancestor transforms?
4824   for (nsPresContext* ctx = GetPresContext(); ctx;
4825        ctx = ctx->GetParentPresContext()) {
4826     PresShell* shell = ctx->PresShell();
4827     float resolution = shell->GetResolution();
4828 
4829     // If we are at the root document in the process, try to see if documents
4830     // in enclosing processes have a resolution and include that as well.
4831     if (!ctx->GetParentPresContext()) {
4832       // xScale is an arbitrary choice. Outside of edge cases involving CSS
4833       // transforms, xScale == yScale so it doesn't matter.
4834       resolution *= ViewportUtils::TryInferEnclosingResolution(shell).xScale;
4835     }
4836 
4837     if (resolution == 1.0) {
4838       continue;
4839     }
4840 
4841     info->mResolution *= resolution;
4842     nsIFrame* rootScrollFrame = shell->GetRootScrollFrame();
4843     ViewID zoomedId =
4844         nsLayoutUtils::FindOrCreateIDFor(rootScrollFrame->GetContent());
4845 
4846     nsDisplayList wrapped;
4847     wrapped.AppendNewToTop<nsDisplayAsyncZoom>(&info->mBuilder, rootScrollFrame,
4848                                                &info->mList, nullptr, zoomedId);
4849     info->mList.AppendToTop(&wrapped);
4850   }
4851 
4852 #ifdef DEBUG
4853   if (gDumpRangePaintList) {
4854     fprintf(stderr, "CreateRangePaintInfo --- before ClipListToRange:\n");
4855     nsIFrame::PrintDisplayList(&(info->mBuilder), info->mList);
4856   }
4857 #endif
4858 
4859   nsRect rangeRect = ClipListToRange(&info->mBuilder, &info->mList, aRange);
4860 
4861   info->mBuilder.LeavePresShell(ancestorFrame, &info->mList);
4862 
4863 #ifdef DEBUG
4864   if (gDumpRangePaintList) {
4865     fprintf(stderr, "CreateRangePaintInfo --- after ClipListToRange:\n");
4866     nsIFrame::PrintDisplayList(&(info->mBuilder), info->mList);
4867   }
4868 #endif
4869 
4870   // determine the offset of the reference frame for the display list
4871   // to the root frame. This will allow the coordinates used when painting
4872   // to all be offset from the same point
4873   info->mRootOffset = ancestorFrame->GetBoundingClientRect().TopLeft();
4874   rangeRect.MoveBy(info->mRootOffset);
4875   aSurfaceRect.UnionRect(aSurfaceRect, rangeRect);
4876 
4877   return info;
4878 }
4879 
PaintRangePaintInfo(const nsTArray<UniquePtr<RangePaintInfo>> & aItems,Selection * aSelection,const Maybe<CSSIntRegion> & aRegion,nsRect aArea,const LayoutDeviceIntPoint aPoint,LayoutDeviceIntRect * aScreenRect,RenderImageFlags aFlags)4880 already_AddRefed<SourceSurface> PresShell::PaintRangePaintInfo(
4881     const nsTArray<UniquePtr<RangePaintInfo>>& aItems, Selection* aSelection,
4882     const Maybe<CSSIntRegion>& aRegion, nsRect aArea,
4883     const LayoutDeviceIntPoint aPoint, LayoutDeviceIntRect* aScreenRect,
4884     RenderImageFlags aFlags) {
4885   nsPresContext* pc = GetPresContext();
4886   if (!pc || aArea.width == 0 || aArea.height == 0) return nullptr;
4887 
4888   // use the rectangle to create the surface
4889   LayoutDeviceIntRect pixelArea = LayoutDeviceIntRect::FromAppUnitsToOutside(
4890       aArea, pc->AppUnitsPerDevPixel());
4891 
4892   // if the image should not be resized, scale must be 1
4893   float scale = 1.0;
4894 
4895   nsRect maxSize;
4896   pc->DeviceContext()->GetClientRect(maxSize);
4897 
4898   // check if the image should be resized
4899   bool resize = !!(aFlags & RenderImageFlags::AutoScale);
4900 
4901   if (resize) {
4902     // check if image-resizing-algorithm should be used
4903     if (aFlags & RenderImageFlags::IsImage) {
4904       // get max screensize
4905       int32_t maxWidth = pc->AppUnitsToDevPixels(maxSize.width);
4906       int32_t maxHeight = pc->AppUnitsToDevPixels(maxSize.height);
4907       // resize image relative to the screensize
4908       // get best height/width relative to screensize
4909       float bestHeight = float(maxHeight) * RELATIVE_SCALEFACTOR;
4910       float bestWidth = float(maxWidth) * RELATIVE_SCALEFACTOR;
4911       // calculate scale for bestWidth
4912       float adjustedScale = bestWidth / float(pixelArea.width);
4913       // get the worst height (height when width is perfect)
4914       float worstHeight = float(pixelArea.height) * adjustedScale;
4915       // get the difference of best and worst height
4916       float difference = bestHeight - worstHeight;
4917       // halve the difference and add it to worstHeight to get
4918       // the best compromise between bestHeight and bestWidth,
4919       // then calculate the corresponding scale factor
4920       adjustedScale = (worstHeight + difference / 2) / float(pixelArea.height);
4921       // prevent upscaling
4922       scale = std::min(scale, adjustedScale);
4923     } else {
4924       // get half of max screensize
4925       int32_t maxWidth = pc->AppUnitsToDevPixels(maxSize.width >> 1);
4926       int32_t maxHeight = pc->AppUnitsToDevPixels(maxSize.height >> 1);
4927       if (pixelArea.width > maxWidth || pixelArea.height > maxHeight) {
4928         // divide the maximum size by the image size in both directions.
4929         // Whichever direction produces the smallest result determines how much
4930         // should be scaled.
4931         if (pixelArea.width > maxWidth)
4932           scale = std::min(scale, float(maxWidth) / pixelArea.width);
4933         if (pixelArea.height > maxHeight)
4934           scale = std::min(scale, float(maxHeight) / pixelArea.height);
4935       }
4936     }
4937 
4938     // Pick a resolution scale factor that is the highest we need for any of
4939     // the items. This means some items may get rendered at a higher-than-needed
4940     // resolution but at least nothing will be avoidably blurry.
4941     float resolutionScale = 1.0;
4942     for (const UniquePtr<RangePaintInfo>& rangeInfo : aItems) {
4943       resolutionScale = std::max(resolutionScale, rangeInfo->mResolution);
4944     }
4945     float unclampedResolution = resolutionScale;
4946     // Clamp the resolution scale so that `pixelArea` when scaled by `scale` and
4947     // `resolutionScale` isn't bigger than `maxSize`. This prevents creating
4948     // giant/unbounded images.
4949     resolutionScale =
4950         std::min(resolutionScale, maxSize.width / (scale * pixelArea.width));
4951     resolutionScale =
4952         std::min(resolutionScale, maxSize.height / (scale * pixelArea.height));
4953     // The following assert should only get hit if pixelArea scaled by `scale`
4954     // alone would already have been bigger than `maxSize`, which should never
4955     // be the case. For release builds we handle gracefully by reverting
4956     // resolutionScale to 1.0 to avoid unexpected consequences.
4957     MOZ_ASSERT(resolutionScale >= 1.0);
4958     resolutionScale = std::max(1.0f, resolutionScale);
4959 
4960     scale *= resolutionScale;
4961 
4962     // Now we need adjust the output screen position of the surface based on the
4963     // scaling factor and any APZ zoom that may be in effect. The goal is here
4964     // to set `aScreenRect`'s top-left corner (in screen-relative LD pixels)
4965     // such that the scaling effect on the surface appears anchored  at `aPoint`
4966     // ("anchor" here is like "transform-origin"). When this code is e.g. used
4967     // to generate a drag image for dragging operations, `aPoint` refers to the
4968     // position of the mouse cursor (also in screen-relative LD pixels), and the
4969     // user-visible effect of doing this is that the point at which the user
4970     // clicked to start the drag remains under the mouse during the drag.
4971 
4972     // In order to do this we first compute the top-left corner of the
4973     // pixelArea is screen-relative LD pixels.
4974     LayoutDevicePoint visualPoint = ViewportUtils::ToScreenRelativeVisual(
4975         LayoutDevicePoint(pixelArea.TopLeft()), pc);
4976     // And then adjust the output screen position based on that, which we can do
4977     // since everything here is screen-relative LD pixels. Note that the scale
4978     // factor we use here is the effective "transform" scale applied to the
4979     // content we're painting, relative to the scale at which it would normally
4980     // get painted at as part of page rendering (`unclampedResolution`).
4981     float scaleRelativeToNormalContent = scale / unclampedResolution;
4982     aScreenRect->x = NSToIntFloor(aPoint.x - float(aPoint.x - visualPoint.x) *
4983                                                  scaleRelativeToNormalContent);
4984     aScreenRect->y = NSToIntFloor(aPoint.y - float(aPoint.y - visualPoint.y) *
4985                                                  scaleRelativeToNormalContent);
4986 
4987     pixelArea.width = NSToIntFloor(float(pixelArea.width) * scale);
4988     pixelArea.height = NSToIntFloor(float(pixelArea.height) * scale);
4989     if (!pixelArea.width || !pixelArea.height) {
4990       return nullptr;
4991     }
4992   } else {
4993     // move aScreenRect to the position of the surface in screen coordinates
4994     LayoutDevicePoint visualPoint = ViewportUtils::ToScreenRelativeVisual(
4995         LayoutDevicePoint(pixelArea.TopLeft()), pc);
4996     aScreenRect->MoveTo(RoundedToInt(visualPoint));
4997   }
4998   aScreenRect->width = pixelArea.width;
4999   aScreenRect->height = pixelArea.height;
5000 
5001   RefPtr<DrawTarget> dt =
5002       gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
5003           IntSize(pixelArea.width, pixelArea.height), SurfaceFormat::B8G8R8A8);
5004   if (!dt || !dt->IsValid()) {
5005     return nullptr;
5006   }
5007 
5008   RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(dt);
5009   MOZ_ASSERT(ctx);  // already checked the draw target above
5010 
5011   if (aRegion) {
5012     RefPtr<PathBuilder> builder = dt->CreatePathBuilder(FillRule::FILL_WINDING);
5013 
5014     // Convert aRegion from CSS pixels to dev pixels
5015     nsIntRegion region = aRegion->ToAppUnits(AppUnitsPerCSSPixel())
5016                              .ToOutsidePixels(pc->AppUnitsPerDevPixel());
5017     for (auto iter = region.RectIter(); !iter.Done(); iter.Next()) {
5018       const IntRect& rect = iter.Get();
5019 
5020       builder->MoveTo(rect.TopLeft());
5021       builder->LineTo(rect.TopRight());
5022       builder->LineTo(rect.BottomRight());
5023       builder->LineTo(rect.BottomLeft());
5024       builder->LineTo(rect.TopLeft());
5025     }
5026 
5027     RefPtr<Path> path = builder->Finish();
5028     ctx->Clip(path);
5029   }
5030 
5031   gfxMatrix initialTM = ctx->CurrentMatrixDouble();
5032 
5033   if (resize) {
5034     initialTM.PreScale(scale, scale);
5035   }
5036 
5037   // translate so that points are relative to the surface area
5038   gfxPoint surfaceOffset = nsLayoutUtils::PointToGfxPoint(
5039       -aArea.TopLeft(), pc->AppUnitsPerDevPixel());
5040   initialTM.PreTranslate(surfaceOffset);
5041 
5042   // temporarily hide the selection so that text is drawn normally. If a
5043   // selection is being rendered, use that, otherwise use the presshell's
5044   // selection.
5045   RefPtr<nsFrameSelection> frameSelection;
5046   if (aSelection) {
5047     frameSelection = aSelection->GetFrameSelection();
5048   } else {
5049     frameSelection = FrameSelection();
5050   }
5051   int16_t oldDisplaySelection = frameSelection->GetDisplaySelection();
5052   frameSelection->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN);
5053 
5054   // next, paint each range in the selection
5055   for (const UniquePtr<RangePaintInfo>& rangeInfo : aItems) {
5056     // the display lists paint relative to the offset from the reference
5057     // frame, so account for that translation too:
5058     gfxPoint rootOffset = nsLayoutUtils::PointToGfxPoint(
5059         rangeInfo->mRootOffset, pc->AppUnitsPerDevPixel());
5060     ctx->SetMatrixDouble(initialTM.PreTranslate(rootOffset));
5061     aArea.MoveBy(-rangeInfo->mRootOffset.x, -rangeInfo->mRootOffset.y);
5062     nsRegion visible(aArea);
5063     RefPtr<LayerManager> layerManager = rangeInfo->mList.PaintRoot(
5064         &rangeInfo->mBuilder, ctx, nsDisplayList::PAINT_DEFAULT, Nothing());
5065     aArea.MoveBy(rangeInfo->mRootOffset.x, rangeInfo->mRootOffset.y);
5066   }
5067 
5068   // restore the old selection display state
5069   frameSelection->SetDisplaySelection(oldDisplaySelection);
5070 
5071   return dt->Snapshot();
5072 }
5073 
RenderNode(nsINode * aNode,const Maybe<CSSIntRegion> & aRegion,const LayoutDeviceIntPoint aPoint,LayoutDeviceIntRect * aScreenRect,RenderImageFlags aFlags)5074 already_AddRefed<SourceSurface> PresShell::RenderNode(
5075     nsINode* aNode, const Maybe<CSSIntRegion>& aRegion,
5076     const LayoutDeviceIntPoint aPoint, LayoutDeviceIntRect* aScreenRect,
5077     RenderImageFlags aFlags) {
5078   // area will hold the size of the surface needed to draw the node, measured
5079   // from the root frame.
5080   nsRect area;
5081   nsTArray<UniquePtr<RangePaintInfo>> rangeItems;
5082 
5083   // nothing to draw if the node isn't in a document
5084   if (!aNode->IsInComposedDoc()) {
5085     return nullptr;
5086   }
5087 
5088   RefPtr<nsRange> range = nsRange::Create(aNode);
5089   IgnoredErrorResult rv;
5090   range->SelectNode(*aNode, rv);
5091   if (rv.Failed()) {
5092     return nullptr;
5093   }
5094 
5095   UniquePtr<RangePaintInfo> info = CreateRangePaintInfo(range, area, false);
5096   if (info) {
5097     // XXX(Bug 1631371) Check if this should use a fallible operation as it
5098     // pretended earlier, or change the return type to void.
5099     rangeItems.AppendElement(std::move(info));
5100   }
5101 
5102   Maybe<CSSIntRegion> region = aRegion;
5103   if (region) {
5104     // combine the area with the supplied region
5105     CSSIntRect rrectPixels = region->GetBounds();
5106 
5107     nsRect rrect = ToAppUnits(rrectPixels, AppUnitsPerCSSPixel());
5108     area.IntersectRect(area, rrect);
5109 
5110     nsPresContext* pc = GetPresContext();
5111     if (!pc) return nullptr;
5112 
5113     // move the region so that it is offset from the topleft corner of the
5114     // surface
5115     region->MoveBy(-nsPresContext::AppUnitsToIntCSSPixels(area.x),
5116                    -nsPresContext::AppUnitsToIntCSSPixels(area.y));
5117   }
5118 
5119   return PaintRangePaintInfo(rangeItems, nullptr, region, area, aPoint,
5120                              aScreenRect, aFlags);
5121 }
5122 
RenderSelection(Selection * aSelection,const LayoutDeviceIntPoint aPoint,LayoutDeviceIntRect * aScreenRect,RenderImageFlags aFlags)5123 already_AddRefed<SourceSurface> PresShell::RenderSelection(
5124     Selection* aSelection, const LayoutDeviceIntPoint aPoint,
5125     LayoutDeviceIntRect* aScreenRect, RenderImageFlags aFlags) {
5126   // area will hold the size of the surface needed to draw the selection,
5127   // measured from the root frame.
5128   nsRect area;
5129   nsTArray<UniquePtr<RangePaintInfo>> rangeItems;
5130 
5131   // iterate over each range and collect them into the rangeItems array.
5132   // This is done so that the size of selection can be determined so as
5133   // to allocate a surface area
5134   uint32_t numRanges = aSelection->RangeCount();
5135   NS_ASSERTION(numRanges > 0, "RenderSelection called with no selection");
5136 
5137   for (uint32_t r = 0; r < numRanges; r++) {
5138     RefPtr<nsRange> range = aSelection->GetRangeAt(r);
5139 
5140     UniquePtr<RangePaintInfo> info = CreateRangePaintInfo(range, area, true);
5141     if (info) {
5142       // XXX(Bug 1631371) Check if this should use a fallible operation as it
5143       // pretended earlier.
5144       rangeItems.AppendElement(std::move(info));
5145     }
5146   }
5147 
5148   return PaintRangePaintInfo(rangeItems, aSelection, Nothing(), area, aPoint,
5149                              aScreenRect, aFlags);
5150 }
5151 
AddPrintPreviewBackgroundItem(nsDisplayListBuilder * aBuilder,nsDisplayList * aList,nsIFrame * aFrame,const nsRect & aBounds)5152 void PresShell::AddPrintPreviewBackgroundItem(nsDisplayListBuilder* aBuilder,
5153                                               nsDisplayList* aList,
5154                                               nsIFrame* aFrame,
5155                                               const nsRect& aBounds) {
5156   aList->AppendNewToBottom<nsDisplaySolidColor>(aBuilder, aFrame, aBounds,
5157                                                 NS_RGB(115, 115, 115));
5158 }
5159 
AddCanvasBackgroundColor(const nsDisplayList * aList,nsIFrame * aCanvasFrame,nscolor aColor,bool aCSSBackgroundColor)5160 static bool AddCanvasBackgroundColor(const nsDisplayList* aList,
5161                                      nsIFrame* aCanvasFrame, nscolor aColor,
5162                                      bool aCSSBackgroundColor) {
5163   for (nsDisplayItem* i = aList->GetBottom(); i; i = i->GetAbove()) {
5164     const DisplayItemType type = i->GetType();
5165 
5166     if (i->Frame() == aCanvasFrame &&
5167         type == DisplayItemType::TYPE_CANVAS_BACKGROUND_COLOR) {
5168       auto* bg = static_cast<nsDisplayCanvasBackgroundColor*>(i);
5169       bg->SetExtraBackgroundColor(aColor);
5170       return true;
5171     }
5172 
5173     const bool isBlendContainer =
5174         type == DisplayItemType::TYPE_BLEND_CONTAINER ||
5175         type == DisplayItemType::TYPE_TABLE_BLEND_CONTAINER;
5176 
5177     nsDisplayList* sublist = i->GetSameCoordinateSystemChildren();
5178     if (sublist && !(isBlendContainer && !aCSSBackgroundColor) &&
5179         AddCanvasBackgroundColor(sublist, aCanvasFrame, aColor,
5180                                  aCSSBackgroundColor))
5181       return true;
5182   }
5183   return false;
5184 }
5185 
AddCanvasBackgroundColorItem(nsDisplayListBuilder * aBuilder,nsDisplayList * aList,nsIFrame * aFrame,const nsRect & aBounds,nscolor aBackstopColor,AddCanvasBackgroundColorFlags aFlags)5186 void PresShell::AddCanvasBackgroundColorItem(
5187     nsDisplayListBuilder* aBuilder, nsDisplayList* aList, nsIFrame* aFrame,
5188     const nsRect& aBounds, nscolor aBackstopColor,
5189     AddCanvasBackgroundColorFlags aFlags) {
5190   if (aBounds.IsEmpty()) {
5191     return;
5192   }
5193   // We don't want to add an item for the canvas background color if the frame
5194   // (sub)tree we are painting doesn't include any canvas frames. There isn't
5195   // an easy way to check this directly, but if we check if the root of the
5196   // (sub)tree we are painting is a canvas frame that should cover us in all
5197   // cases (it will usually be a viewport frame when we have a canvas frame in
5198   // the (sub)tree).
5199   if (!(aFlags & AddCanvasBackgroundColorFlags::ForceDraw) &&
5200       !nsCSSRendering::IsCanvasFrame(aFrame)) {
5201     return;
5202   }
5203 
5204   nscolor bgcolor = NS_ComposeColors(aBackstopColor, mCanvasBackgroundColor);
5205   if (NS_GET_A(bgcolor) == 0) return;
5206 
5207   // To make layers work better, we want to avoid having a big non-scrolled
5208   // color background behind a scrolled transparent background. Instead,
5209   // we'll try to move the color background into the scrolled content
5210   // by making nsDisplayCanvasBackground paint it.
5211   // If we're only adding an unscrolled item, then pretend that we've
5212   // already done it.
5213   bool addedScrollingBackgroundColor =
5214       !!(aFlags & AddCanvasBackgroundColorFlags::AppendUnscrolledOnly);
5215   if (!aFrame->GetParent() && !addedScrollingBackgroundColor) {
5216     nsIScrollableFrame* sf =
5217         aFrame->PresShell()->GetRootScrollFrameAsScrollable();
5218     if (sf) {
5219       nsCanvasFrame* canvasFrame = do_QueryFrame(sf->GetScrolledFrame());
5220       if (canvasFrame && canvasFrame->IsVisibleForPainting()) {
5221         addedScrollingBackgroundColor = AddCanvasBackgroundColor(
5222             aList, canvasFrame, bgcolor, mHasCSSBackgroundColor);
5223       }
5224     }
5225   }
5226 
5227   // With async scrolling, we'd like to have two instances of the background
5228   // color: one that scrolls with the content (for the reasons stated above),
5229   // and one underneath which does not scroll with the content, but which can
5230   // be shown during checkerboarding and overscroll.
5231   // We can only do that if the color is opaque.
5232   bool forceUnscrolledItem =
5233       nsLayoutUtils::UsesAsyncScrolling(aFrame) && NS_GET_A(bgcolor) == 255;
5234 
5235   if (!addedScrollingBackgroundColor || forceUnscrolledItem) {
5236     aList->AppendNewToBottom<nsDisplaySolidColor>(aBuilder, aFrame, aBounds,
5237                                                   bgcolor);
5238   }
5239 }
5240 
IsTransparentContainerElement(nsPresContext * aPresContext)5241 static bool IsTransparentContainerElement(nsPresContext* aPresContext) {
5242   nsCOMPtr<nsIDocShell> docShell = aPresContext->GetDocShell();
5243   if (!docShell) {
5244     return false;
5245   }
5246 
5247   nsCOMPtr<nsPIDOMWindowOuter> pwin = docShell->GetWindow();
5248   if (!pwin) return false;
5249   nsCOMPtr<Element> containerElement = pwin->GetFrameElementInternal();
5250 
5251   BrowserChild* tab = BrowserChild::GetFrom(docShell);
5252   if (tab) {
5253     // Check if presShell is the top PresShell. Only the top can
5254     // influence the canvas background color.
5255     if (aPresContext->GetPresShell() != tab->GetTopLevelPresShell()) {
5256       tab = nullptr;
5257     }
5258   }
5259 
5260   return (containerElement && containerElement->HasAttr(
5261                                   kNameSpaceID_None, nsGkAtoms::transparent)) ||
5262          (tab && tab->IsTransparent());
5263 }
5264 
GetDefaultBackgroundColorToDraw()5265 nscolor PresShell::GetDefaultBackgroundColorToDraw() {
5266   if (!mPresContext || !mPresContext->GetBackgroundColorDraw()) {
5267     return NS_RGB(255, 255, 255);
5268   }
5269 
5270   nscolor backgroundColor = mPresContext->DefaultBackgroundColor();
5271   if (backgroundColor != NS_RGB(255, 255, 255)) {
5272     // Return non-default color.
5273     return backgroundColor;
5274   }
5275 
5276   // Use a dark background for top-level about:blank that is inaccessible to
5277   // content JS.
5278   Document* doc = GetDocument();
5279   BrowsingContext* bc = doc->GetBrowsingContext();
5280   if (bc && bc->IsTop() && !bc->HasOpener() && doc->GetDocumentURI() &&
5281       NS_IsAboutBlank(doc->GetDocumentURI()) &&
5282       doc->PrefersColorScheme(Document::IgnoreRFP::Yes) ==
5283           StylePrefersColorScheme::Dark) {
5284     // Use --in-content-page-background for prefers-color-scheme: dark.
5285     return StaticPrefs::browser_proton_enabled() ? NS_RGB(0x1C, 0x1B, 0x22)
5286                                                  : NS_RGB(0x2A, 0x2A, 0x2E);
5287   }
5288 
5289   return backgroundColor;
5290 }
5291 
UpdateCanvasBackground()5292 void PresShell::UpdateCanvasBackground() {
5293   // If we have a frame tree and it has style information that
5294   // specifies the background color of the canvas, update our local
5295   // cache of that color.
5296   nsIFrame* rootStyleFrame = FrameConstructor()->GetRootElementStyleFrame();
5297   if (rootStyleFrame) {
5298     ComputedStyle* bgStyle =
5299         nsCSSRendering::FindRootFrameBackground(rootStyleFrame);
5300     // XXX We should really be passing the canvasframe, not the root element
5301     // style frame but we don't have access to the canvasframe here. It isn't
5302     // a problem because only a few frames can return something other than true
5303     // and none of them would be a canvas frame or root element style frame.
5304     bool drawBackgroundImage = false;
5305     bool drawBackgroundColor = false;
5306     const nsStyleDisplay* disp = rootStyleFrame->StyleDisplay();
5307     StyleAppearance appearance = disp->EffectiveAppearance();
5308     if (rootStyleFrame->IsThemed(disp) &&
5309         appearance != StyleAppearance::MozWinGlass &&
5310         appearance != StyleAppearance::MozWinBorderlessGlass) {
5311       // Ignore the CSS background-color if -moz-appearance is used and it is
5312       // not one of the glass values. (Windows 7 Glass has traditionally not
5313       // overridden background colors, so we preserve that behavior for now.)
5314       mCanvasBackgroundColor = NS_RGBA(0, 0, 0, 0);
5315     } else {
5316       mCanvasBackgroundColor = nsCSSRendering::DetermineBackgroundColor(
5317           mPresContext, bgStyle, rootStyleFrame, drawBackgroundImage,
5318           drawBackgroundColor);
5319     }
5320     mHasCSSBackgroundColor = drawBackgroundColor;
5321     if (mPresContext->IsRootContentDocumentCrossProcess() &&
5322         !IsTransparentContainerElement(mPresContext)) {
5323       mCanvasBackgroundColor = NS_ComposeColors(
5324           GetDefaultBackgroundColorToDraw(), mCanvasBackgroundColor);
5325     }
5326   }
5327 
5328   // If the root element of the document (ie html) has style 'display: none'
5329   // then the document's background color does not get drawn; cache the
5330   // color we actually draw.
5331   if (!FrameConstructor()->GetRootElementFrame()) {
5332     mCanvasBackgroundColor = GetDefaultBackgroundColorToDraw();
5333   }
5334 }
5335 
ComputeBackstopColor(nsView * aDisplayRoot)5336 nscolor PresShell::ComputeBackstopColor(nsView* aDisplayRoot) {
5337   nsIWidget* widget = aDisplayRoot->GetWidget();
5338   if (widget && (widget->GetTransparencyMode() != eTransparencyOpaque ||
5339                  widget->WidgetPaintsBackground())) {
5340     // Within a transparent widget, so the backstop color must be
5341     // totally transparent.
5342     return NS_RGBA(0, 0, 0, 0);
5343   }
5344   // Within an opaque widget (or no widget at all), so the backstop
5345   // color must be totally opaque. The user's default background
5346   // as reported by the prescontext is guaranteed to be opaque.
5347   return GetDefaultBackgroundColorToDraw();
5348 }
5349 
5350 struct PaintParams {
5351   nscolor mBackgroundColor;
5352 };
5353 
GetLayerManager()5354 LayerManager* PresShell::GetLayerManager() {
5355   NS_ASSERTION(mViewManager, "Should have view manager");
5356 
5357   nsView* rootView = mViewManager->GetRootView();
5358   if (rootView) {
5359     if (nsIWidget* widget = rootView->GetWidget()) {
5360       return widget->GetLayerManager();
5361     }
5362   }
5363   return nullptr;
5364 }
5365 
AsyncPanZoomEnabled()5366 bool PresShell::AsyncPanZoomEnabled() {
5367   NS_ASSERTION(mViewManager, "Should have view manager");
5368   nsView* rootView = mViewManager->GetRootView();
5369   if (rootView) {
5370     if (nsIWidget* widget = rootView->GetWidget()) {
5371       return widget->AsyncPanZoomEnabled();
5372     }
5373   }
5374   return gfxPlatform::AsyncPanZoomEnabled();
5375 }
5376 
SetResolutionAndScaleTo(float aResolution,ResolutionChangeOrigin aOrigin)5377 nsresult PresShell::SetResolutionAndScaleTo(float aResolution,
5378                                             ResolutionChangeOrigin aOrigin) {
5379   if (!(aResolution > 0.0)) {
5380     return NS_ERROR_ILLEGAL_VALUE;
5381   }
5382   if (aResolution == mResolution.valueOr(0.0)) {
5383     MOZ_ASSERT(mResolution.isSome());
5384     return NS_OK;
5385   }
5386 
5387   // GetResolution handles mResolution being nothing by returning 1 so this
5388   // is checking that the resolution is actually changing.
5389   bool resolutionUpdated = (aResolution != GetResolution());
5390 
5391   mLastResolutionChangeOrigin = aOrigin;
5392 
5393   RenderingState state(this);
5394   state.mResolution = Some(aResolution);
5395   SetRenderingState(state);
5396   if (mMobileViewportManager) {
5397     mMobileViewportManager->ResolutionUpdated(aOrigin);
5398   }
5399   if (aOrigin == ResolutionChangeOrigin::Apz) {
5400     mResolutionUpdatedByApz = true;
5401   } else if (resolutionUpdated) {
5402     mResolutionUpdated = true;
5403   }
5404 
5405   if (auto* window = nsGlobalWindowInner::Cast(mDocument->GetInnerWindow())) {
5406     window->VisualViewport()->PostResizeEvent();
5407   }
5408 
5409   return NS_OK;
5410 }
5411 
GetCumulativeResolution() const5412 float PresShell::GetCumulativeResolution() const {
5413   float resolution = GetResolution();
5414   nsPresContext* parentCtx = GetPresContext()->GetParentPresContext();
5415   if (parentCtx) {
5416     resolution *= parentCtx->PresShell()->GetCumulativeResolution();
5417   }
5418   return resolution;
5419 }
5420 
SetRestoreResolution(float aResolution,LayoutDeviceIntSize aDisplaySize)5421 void PresShell::SetRestoreResolution(float aResolution,
5422                                      LayoutDeviceIntSize aDisplaySize) {
5423   if (mMobileViewportManager) {
5424     mMobileViewportManager->SetRestoreResolution(aResolution, aDisplaySize);
5425   }
5426 }
5427 
SetRenderingState(const RenderingState & aState)5428 void PresShell::SetRenderingState(const RenderingState& aState) {
5429   if (mRenderingStateFlags != aState.mRenderingStateFlags) {
5430     // Rendering state changed in a way that forces us to flush any
5431     // retained layers we might already have.
5432     LayerManager* manager = GetLayerManager();
5433     if (manager) {
5434       FrameLayerBuilder::InvalidateAllLayers(manager);
5435     }
5436   }
5437 
5438   if (GetResolution() != aState.mResolution.valueOr(1.f)) {
5439     if (nsIFrame* frame = GetRootFrame()) {
5440       frame->SchedulePaint();
5441     }
5442   }
5443 
5444   mRenderingStateFlags = aState.mRenderingStateFlags;
5445   mResolution = aState.mResolution;
5446 }
5447 
SynthesizeMouseMove(bool aFromScroll)5448 void PresShell::SynthesizeMouseMove(bool aFromScroll) {
5449   if (!StaticPrefs::layout_reflow_synthMouseMove()) return;
5450 
5451   if (mPaintingSuppressed || !mIsActive || !mPresContext) {
5452     return;
5453   }
5454 
5455   if (!mPresContext->IsRoot()) {
5456     if (PresShell* rootPresShell = GetRootPresShell()) {
5457       rootPresShell->SynthesizeMouseMove(aFromScroll);
5458     }
5459     return;
5460   }
5461 
5462   if (mMouseLocation == nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE))
5463     return;
5464 
5465   if (!mSynthMouseMoveEvent.IsPending()) {
5466     RefPtr<nsSynthMouseMoveEvent> ev =
5467         new nsSynthMouseMoveEvent(this, aFromScroll);
5468 
5469     GetPresContext()->RefreshDriver()->AddRefreshObserver(
5470         ev, FlushType::Display, "Synthetic mouse move event");
5471     mSynthMouseMoveEvent = std::move(ev);
5472   }
5473 }
5474 
5475 /**
5476  * Find the first floating view with a frame and a widget in a postorder
5477  * traversal of the view tree that contains the point. Thus more deeply nested
5478  * floating views are preferred over their ancestors, and floating views earlier
5479  * in the view hierarchy (i.e., added later) are preferred over their siblings.
5480  * This is adequate for finding the "topmost" floating view under a point, given
5481  * that floating views don't supporting having a specific z-index.
5482  *
5483  * We cannot exit early when aPt is outside the view bounds, because floating
5484  * views aren't necessarily included in their parent's bounds, so this could
5485  * traverse the entire view hierarchy --- use carefully.
5486  *
5487  * aPt is relative aRelativeToView with the viewport type
5488  * aRelativeToViewportType. aRelativeToView will always have a frame. If aView
5489  * has a frame then aRelativeToView will be aView. (The reason aRelativeToView
5490  * and aView are separate is because we need to traverse into views without
5491  * frames (ie the inner view of a subdocument frame) but we can only easily
5492  * transform between views using TransformPoint which takes frames.)
5493  */
FindFloatingViewContaining(nsView * aRelativeToView,ViewportType aRelativeToViewportType,nsView * aView,nsPoint aPt)5494 static nsView* FindFloatingViewContaining(nsView* aRelativeToView,
5495                                           ViewportType aRelativeToViewportType,
5496                                           nsView* aView, nsPoint aPt) {
5497   MOZ_ASSERT(aRelativeToView->GetFrame());
5498 
5499   if (aView->GetVisibility() == nsViewVisibility_kHide) {
5500     // No need to look into descendants.
5501     return nullptr;
5502   }
5503 
5504   bool crossingZoomBoundary = false;
5505 
5506   nsIFrame* frame = aView->GetFrame();
5507   if (frame) {
5508     if (!frame->IsVisibleConsideringAncestors(
5509             nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY) ||
5510         !frame->PresShell()->IsActive()) {
5511       return nullptr;
5512     }
5513 
5514     // We start out in visual coords and then if we cross the zoom boundary we
5515     // become in layout coords. The zoom boundary always occurs in a document
5516     // with IsRootContentDocumentCrossProcess. The root view of such a document
5517     // is outside the zoom boundary and any child view must be inside the zoom
5518     // boundary because we only create views for certain kinds of frames and
5519     // none of them can be between the root frame and the zoom boundary.
5520     if (aRelativeToViewportType == ViewportType::Visual) {
5521       if (!aRelativeToView->GetParent() ||
5522           aRelativeToView->GetViewManager() !=
5523               aRelativeToView->GetParent()->GetViewManager()) {
5524         if (aRelativeToView->GetFrame()
5525                 ->PresContext()
5526                 ->IsRootContentDocumentCrossProcess()) {
5527           crossingZoomBoundary = true;
5528         }
5529       }
5530     }
5531 
5532     ViewportType nextRelativeToViewportType = aRelativeToViewportType;
5533     if (crossingZoomBoundary) {
5534       nextRelativeToViewportType = ViewportType::Layout;
5535     }
5536 
5537     nsLayoutUtils::TransformResult result = nsLayoutUtils::TransformPoint(
5538         RelativeTo{aRelativeToView->GetFrame(), aRelativeToViewportType},
5539         RelativeTo{frame, nextRelativeToViewportType}, aPt);
5540     if (result != nsLayoutUtils::TRANSFORM_SUCCEEDED) {
5541       return nullptr;
5542     }
5543 
5544     aRelativeToView = aView;
5545     aRelativeToViewportType = nextRelativeToViewportType;
5546   }
5547 
5548   for (nsView* v = aView->GetFirstChild(); v; v = v->GetNextSibling()) {
5549     nsView* r = FindFloatingViewContaining(aRelativeToView,
5550                                            aRelativeToViewportType, v, aPt);
5551     if (r) return r;
5552   }
5553 
5554   if (!frame || !aView->GetFloating() || !aView->HasWidget()) {
5555     return nullptr;
5556   }
5557 
5558   // Even though aPt is in visual coordinates until we cross the zoom boundary
5559   // it is valid to compare it to view coords (which are in layout coords)
5560   // because visual coords are the same as layout coords for every view outside
5561   // of the zoom boundary except for the root view of the root content document.
5562   // For the root view of the root content document, its bounds don't actually
5563   // correspond to what is visible when we have a MobileViewportManager. So we
5564   // skip the hit test. This is okay because the point has already been hit
5565   // test: 1) if we are the root view in the process then the point comes from a
5566   // real mouse event so it must have been over our widget, or 2) if we are the
5567   // root of a subdocument then hittesting against the view of the subdocument
5568   // frame that contains us already happened and succeeded before getting here.
5569   if (!crossingZoomBoundary) {
5570     if (aView->GetDimensions().Contains(aPt)) {
5571       return aView;
5572     }
5573   }
5574 
5575   return nullptr;
5576 }
5577 
5578 /*
5579  * This finds the first view with a frame that contains the given point in a
5580  * postorder traversal of the view tree, assuming that the point is not in a
5581  * floating view.  It assumes that only floating views extend outside the bounds
5582  * of their parents.
5583  *
5584  * This methods should only be called if FindFloatingViewContaining returns
5585  * null.
5586  *
5587  * aPt is relative aRelativeToView with the viewport type
5588  * aRelativeToViewportType. aRelativeToView will always have a frame. If aView
5589  * has a frame then aRelativeToView will be aView. (The reason aRelativeToView
5590  * and aView are separate is because we need to traverse into views without
5591  * frames (ie the inner view of a subdocument frame) but we can only easily
5592  * transform between views using TransformPoint which takes frames.)
5593  */
FindViewContaining(nsView * aRelativeToView,ViewportType aRelativeToViewportType,nsView * aView,nsPoint aPt)5594 static nsView* FindViewContaining(nsView* aRelativeToView,
5595                                   ViewportType aRelativeToViewportType,
5596                                   nsView* aView, nsPoint aPt) {
5597   MOZ_ASSERT(aRelativeToView->GetFrame());
5598 
5599   if (aView->GetVisibility() == nsViewVisibility_kHide) {
5600     return nullptr;
5601   }
5602 
5603   nsIFrame* frame = aView->GetFrame();
5604   if (frame) {
5605     if (!frame->IsVisibleConsideringAncestors(
5606             nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY) ||
5607         !frame->PresShell()->IsActive()) {
5608       return nullptr;
5609     }
5610 
5611     // We start out in visual coords and then if we cross the zoom boundary we
5612     // become in layout coords. The zoom boundary always occurs in a document
5613     // with IsRootContentDocumentCrossProcess. The root view of such a document
5614     // is outside the zoom boundary and any child view must be inside the zoom
5615     // boundary because we only create views for certain kinds of frames and
5616     // none of them can be between the root frame and the zoom boundary.
5617     bool crossingZoomBoundary = false;
5618     if (aRelativeToViewportType == ViewportType::Visual) {
5619       if (!aRelativeToView->GetParent() ||
5620           aRelativeToView->GetViewManager() !=
5621               aRelativeToView->GetParent()->GetViewManager()) {
5622         if (aRelativeToView->GetFrame()
5623                 ->PresContext()
5624                 ->IsRootContentDocumentCrossProcess()) {
5625           crossingZoomBoundary = true;
5626         }
5627       }
5628     }
5629 
5630     ViewportType nextRelativeToViewportType = aRelativeToViewportType;
5631     if (crossingZoomBoundary) {
5632       nextRelativeToViewportType = ViewportType::Layout;
5633     }
5634 
5635     nsLayoutUtils::TransformResult result = nsLayoutUtils::TransformPoint(
5636         RelativeTo{aRelativeToView->GetFrame(), aRelativeToViewportType},
5637         RelativeTo{frame, nextRelativeToViewportType}, aPt);
5638     if (result != nsLayoutUtils::TRANSFORM_SUCCEEDED) {
5639       return nullptr;
5640     }
5641 
5642     // Even though aPt is in visual coordinates until we cross the zoom boundary
5643     // it is valid to compare it to view coords (which are in layout coords)
5644     // because visual coords are the same as layout coords for every view
5645     // outside of the zoom boundary except for the root view of the root content
5646     // document.
5647     // For the root view of the root content document, its bounds don't
5648     // actually correspond to what is visible when we have a
5649     // MobileViewportManager. So we skip the hit test. This is okay because the
5650     // point has already been hit test: 1) if we are the root view in the
5651     // process then the point comes from a real mouse event so it must have been
5652     // over our widget, or 2) if we are the root of a subdocument then
5653     // hittesting against the view of the subdocument frame that contains us
5654     // already happened and succeeded before getting here.
5655     if (!crossingZoomBoundary) {
5656       if (!aView->GetDimensions().Contains(aPt)) {
5657         return nullptr;
5658       }
5659     }
5660 
5661     aRelativeToView = aView;
5662     aRelativeToViewportType = nextRelativeToViewportType;
5663   }
5664 
5665   for (nsView* v = aView->GetFirstChild(); v; v = v->GetNextSibling()) {
5666     nsView* r =
5667         FindViewContaining(aRelativeToView, aRelativeToViewportType, v, aPt);
5668     if (r) return r;
5669   }
5670 
5671   return frame ? aView : nullptr;
5672 }
5673 
GetChildBrowser(nsView * aView)5674 static BrowserBridgeChild* GetChildBrowser(nsView* aView) {
5675   if (!aView) {
5676     return nullptr;
5677   }
5678   nsIFrame* frame = aView->GetFrame();
5679   if (!frame && aView->GetParent()) {
5680     // If frame is null then view is an anonymous inner view, and we want
5681     // the frame from the corresponding outer view.
5682     frame = aView->GetParent()->GetFrame();
5683   }
5684   if (!frame || !frame->GetContent()) {
5685     return nullptr;
5686   }
5687   return BrowserBridgeChild::GetFrom(frame->GetContent());
5688 }
5689 
ProcessSynthMouseMoveEvent(bool aFromScroll)5690 void PresShell::ProcessSynthMouseMoveEvent(bool aFromScroll) {
5691   // If drag session has started, we shouldn't synthesize mousemove event.
5692   nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
5693   if (dragSession) {
5694     mSynthMouseMoveEvent.Forget();
5695     return;
5696   }
5697 
5698   // allow new event to be posted while handling this one only if the
5699   // source of the event is a scroll (to prevent infinite reflow loops)
5700   if (aFromScroll) {
5701     mSynthMouseMoveEvent.Forget();
5702   }
5703 
5704   nsView* rootView = mViewManager ? mViewManager->GetRootView() : nullptr;
5705   if (mMouseLocation == nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE) ||
5706       !rootView || !rootView->HasWidget() || !mPresContext) {
5707     mSynthMouseMoveEvent.Forget();
5708     return;
5709   }
5710 
5711   NS_ASSERTION(mPresContext->IsRoot(), "Only a root pres shell should be here");
5712 
5713   // Hold a ref to ourselves so DispatchEvent won't destroy us (since
5714   // we need to access members after we call DispatchEvent).
5715   RefPtr<PresShell> kungFuDeathGrip(this);
5716 
5717 #ifdef DEBUG_MOUSE_LOCATION
5718   printf("[ps=%p]synthesizing mouse move to (%d,%d)\n", this, mMouseLocation.x,
5719          mMouseLocation.y);
5720 #endif
5721 
5722   int32_t APD = mPresContext->AppUnitsPerDevPixel();
5723 
5724   // We need a widget to put in the event we are going to dispatch so we look
5725   // for a view that has a widget and the mouse location is over. We first look
5726   // for floating views, if there isn't one we use the root view. |view| holds
5727   // that view.
5728   nsView* view = nullptr;
5729 
5730   // The appunits per devpixel ratio of |view|.
5731   int32_t viewAPD;
5732 
5733   // mRefPoint will be mMouseLocation relative to the widget of |view|, the
5734   // widget we will put in the event we dispatch, in viewAPD appunits
5735   nsPoint refpoint(0, 0);
5736 
5737   // We always dispatch the event to the pres shell that contains the view that
5738   // the mouse is over. pointVM is the VM of that pres shell.
5739   nsViewManager* pointVM = nullptr;
5740 
5741   // This could be a bit slow (traverses entire view hierarchy)
5742   // but it's OK to do it once per synthetic mouse event
5743   if (rootView->GetFrame()) {
5744     view = FindFloatingViewContaining(rootView, ViewportType::Visual, rootView,
5745                                       mMouseLocation);
5746   }
5747   nsView* pointView = view;
5748   if (!view) {
5749     view = rootView;
5750     if (rootView->GetFrame()) {
5751       pointView = FindViewContaining(rootView, ViewportType::Visual, rootView,
5752                                      mMouseLocation);
5753     } else {
5754       pointView = rootView;
5755     }
5756     // pointView can be null in situations related to mouse capture
5757     pointVM = (pointView ? pointView : view)->GetViewManager();
5758     refpoint = mMouseLocation + rootView->ViewToWidgetOffset();
5759     viewAPD = APD;
5760   } else {
5761     pointVM = view->GetViewManager();
5762     nsIFrame* frame = view->GetFrame();
5763     NS_ASSERTION(frame, "floating views can't be anonymous");
5764     viewAPD = frame->PresContext()->AppUnitsPerDevPixel();
5765     refpoint = mMouseLocation;
5766     DebugOnly<nsLayoutUtils::TransformResult> result =
5767         nsLayoutUtils::TransformPoint(
5768             RelativeTo{rootView->GetFrame(), ViewportType::Visual},
5769             RelativeTo{frame, ViewportType::Layout}, refpoint);
5770     MOZ_ASSERT(result == nsLayoutUtils::TRANSFORM_SUCCEEDED);
5771     refpoint += view->ViewToWidgetOffset();
5772   }
5773   NS_ASSERTION(view->GetWidget(), "view should have a widget here");
5774   WidgetMouseEvent event(true, eMouseMove, view->GetWidget(),
5775                          WidgetMouseEvent::eSynthesized);
5776   event.mRefPoint =
5777       LayoutDeviceIntPoint::FromAppUnitsToNearest(refpoint, viewAPD);
5778   event.mTime = PR_IntervalNow();
5779   // XXX set event.mModifiers ?
5780   // XXX mnakano I think that we should get the latest information from widget.
5781 
5782   if (BrowserBridgeChild* bbc = GetChildBrowser(pointView)) {
5783     // If we have a BrowserBridgeChild, we're going to be dispatching this
5784     // mouse event into an OOP iframe of the current document.
5785     event.mLayersId = bbc->GetLayersId();
5786     bbc->SendDispatchSynthesizedMouseEvent(event);
5787   } else if (RefPtr<PresShell> presShell = pointVM->GetPresShell()) {
5788     // Since this gets run in a refresh tick there isn't an InputAPZContext on
5789     // the stack from the nsBaseWidget. We need to simulate one with at least
5790     // the correct target guid, so that the correct callback transform gets
5791     // applied if this event goes to a child process. The input block id is set
5792     // to 0 because this is a synthetic event which doesn't really belong to any
5793     // input block. Same for the APZ response field.
5794     InputAPZContext apzContext(mMouseEventTargetGuid, 0, nsEventStatus_eIgnore);
5795     presShell->DispatchSynthMouseMove(&event);
5796   }
5797 
5798   if (!aFromScroll) {
5799     mSynthMouseMoveEvent.Forget();
5800   }
5801 }
5802 
5803 /* static */
MarkFramesInListApproximatelyVisible(const nsDisplayList & aList)5804 void PresShell::MarkFramesInListApproximatelyVisible(
5805     const nsDisplayList& aList) {
5806   for (nsDisplayItem* item = aList.GetBottom(); item; item = item->GetAbove()) {
5807     nsDisplayList* sublist = item->GetChildren();
5808     if (sublist) {
5809       MarkFramesInListApproximatelyVisible(*sublist);
5810       continue;
5811     }
5812 
5813     nsIFrame* frame = item->Frame();
5814     MOZ_ASSERT(frame);
5815 
5816     if (!frame->TrackingVisibility()) {
5817       continue;
5818     }
5819 
5820     // Use the presshell containing the frame.
5821     PresShell* presShell = frame->PresShell();
5822     MOZ_ASSERT(!presShell->AssumeAllFramesVisible());
5823     if (presShell->mApproximatelyVisibleFrames.EnsureInserted(frame)) {
5824       // The frame was added to mApproximatelyVisibleFrames, so increment its
5825       // visible count.
5826       frame->IncApproximateVisibleCount();
5827     }
5828   }
5829 }
5830 
5831 /* static */
DecApproximateVisibleCount(VisibleFrames & aFrames,const Maybe<OnNonvisible> & aNonvisibleAction)5832 void PresShell::DecApproximateVisibleCount(
5833     VisibleFrames& aFrames, const Maybe<OnNonvisible>& aNonvisibleAction
5834     /* = Nothing() */) {
5835   for (nsIFrame* frame : aFrames) {
5836     // Decrement the frame's visible count if we're still tracking its
5837     // visibility. (We may not be, if the frame disabled visibility tracking
5838     // after we added it to the visible frames list.)
5839     if (frame->TrackingVisibility()) {
5840       frame->DecApproximateVisibleCount(aNonvisibleAction);
5841     }
5842   }
5843 }
5844 
RebuildApproximateFrameVisibilityDisplayList(const nsDisplayList & aList)5845 void PresShell::RebuildApproximateFrameVisibilityDisplayList(
5846     const nsDisplayList& aList) {
5847   MOZ_ASSERT(!mApproximateFrameVisibilityVisited, "already visited?");
5848   mApproximateFrameVisibilityVisited = true;
5849 
5850   // Remove the entries of the mApproximatelyVisibleFrames hashtable and put
5851   // them in oldApproxVisibleFrames.
5852   VisibleFrames oldApproximatelyVisibleFrames =
5853       std::move(mApproximatelyVisibleFrames);
5854 
5855   MarkFramesInListApproximatelyVisible(aList);
5856 
5857   DecApproximateVisibleCount(oldApproximatelyVisibleFrames);
5858 }
5859 
5860 /* static */
ClearApproximateFrameVisibilityVisited(nsView * aView,bool aClear)5861 void PresShell::ClearApproximateFrameVisibilityVisited(nsView* aView,
5862                                                        bool aClear) {
5863   nsViewManager* vm = aView->GetViewManager();
5864   if (aClear) {
5865     PresShell* presShell = vm->GetPresShell();
5866     if (!presShell->mApproximateFrameVisibilityVisited) {
5867       presShell->ClearApproximatelyVisibleFramesList();
5868     }
5869     presShell->mApproximateFrameVisibilityVisited = false;
5870   }
5871   for (nsView* v = aView->GetFirstChild(); v; v = v->GetNextSibling()) {
5872     ClearApproximateFrameVisibilityVisited(v, v->GetViewManager() != vm);
5873   }
5874 }
5875 
ClearApproximatelyVisibleFramesList(const Maybe<OnNonvisible> & aNonvisibleAction)5876 void PresShell::ClearApproximatelyVisibleFramesList(
5877     const Maybe<OnNonvisible>& aNonvisibleAction
5878     /* = Nothing() */) {
5879   DecApproximateVisibleCount(mApproximatelyVisibleFrames, aNonvisibleAction);
5880   mApproximatelyVisibleFrames.Clear();
5881 }
5882 
MarkFramesInSubtreeApproximatelyVisible(nsIFrame * aFrame,const nsRect & aRect,bool aRemoveOnly)5883 void PresShell::MarkFramesInSubtreeApproximatelyVisible(
5884     nsIFrame* aFrame, const nsRect& aRect, bool aRemoveOnly /* = false */) {
5885   MOZ_ASSERT(aFrame->PresShell() == this, "wrong presshell");
5886 
5887   if (aFrame->TrackingVisibility() && aFrame->StyleVisibility()->IsVisible() &&
5888       (!aRemoveOnly ||
5889        aFrame->GetVisibility() == Visibility::ApproximatelyVisible)) {
5890     MOZ_ASSERT(!AssumeAllFramesVisible());
5891     if (mApproximatelyVisibleFrames.EnsureInserted(aFrame)) {
5892       // The frame was added to mApproximatelyVisibleFrames, so increment its
5893       // visible count.
5894       aFrame->IncApproximateVisibleCount();
5895     }
5896   }
5897 
5898   nsSubDocumentFrame* subdocFrame = do_QueryFrame(aFrame);
5899   if (subdocFrame) {
5900     PresShell* presShell = subdocFrame->GetSubdocumentPresShellForPainting(
5901         nsSubDocumentFrame::IGNORE_PAINT_SUPPRESSION);
5902     if (presShell && !presShell->AssumeAllFramesVisible()) {
5903       nsRect rect = aRect;
5904       nsIFrame* root = presShell->GetRootFrame();
5905       if (root) {
5906         rect.MoveBy(aFrame->GetOffsetToCrossDoc(root));
5907       } else {
5908         rect.MoveBy(-aFrame->GetContentRectRelativeToSelf().TopLeft());
5909       }
5910       rect = rect.ScaleToOtherAppUnitsRoundOut(
5911           aFrame->PresContext()->AppUnitsPerDevPixel(),
5912           presShell->GetPresContext()->AppUnitsPerDevPixel());
5913 
5914       presShell->RebuildApproximateFrameVisibility(&rect);
5915     }
5916     return;
5917   }
5918 
5919   nsRect rect = aRect;
5920 
5921   nsIScrollableFrame* scrollFrame = do_QueryFrame(aFrame);
5922   if (scrollFrame) {
5923     bool ignoreDisplayPort = false;
5924     if (DisplayPortUtils::IsMissingDisplayPortBaseRect(aFrame->GetContent())) {
5925       // We can properly set the base rect for root scroll frames on top level
5926       // and root content documents. Otherwise the base rect we compute might
5927       // be way too big without the limiting that
5928       // ScrollFrameHelper::DecideScrollableLayer does, so we just ignore the
5929       // displayport in that case.
5930       nsPresContext* pc = aFrame->PresContext();
5931       if (scrollFrame->IsRootScrollFrameOfDocument() &&
5932           (pc->IsRootContentDocumentCrossProcess() ||
5933            (pc->IsChrome() && !pc->GetParentPresContext()))) {
5934         nsRect baseRect(
5935             nsPoint(), nsLayoutUtils::CalculateCompositionSizeForFrame(aFrame));
5936         DisplayPortUtils::SetDisplayPortBase(aFrame->GetContent(), baseRect);
5937       } else {
5938         ignoreDisplayPort = true;
5939       }
5940     }
5941 
5942     nsRect displayPort;
5943     bool usingDisplayport =
5944         !ignoreDisplayPort &&
5945         DisplayPortUtils::GetDisplayPortForVisibilityTesting(
5946             aFrame->GetContent(), &displayPort);
5947 
5948     scrollFrame->NotifyApproximateFrameVisibilityUpdate(!usingDisplayport);
5949 
5950     if (usingDisplayport) {
5951       rect = displayPort;
5952     } else {
5953       rect = rect.Intersect(scrollFrame->GetScrollPortRect());
5954     }
5955     rect = scrollFrame->ExpandRectToNearlyVisible(rect);
5956   }
5957 
5958   bool preserves3DChildren = aFrame->Extend3DContext();
5959 
5960   // We assume all frames in popups are visible, so we skip them here.
5961   const nsIFrame::ChildListIDs skip = {nsIFrame::kPopupList,
5962                                        nsIFrame::kSelectPopupList};
5963   for (const auto& [list, listID] : aFrame->ChildLists()) {
5964     if (skip.contains(listID)) {
5965       continue;
5966     }
5967 
5968     for (nsIFrame* child : list) {
5969       nsRect r = rect - child->GetPosition();
5970       if (!r.IntersectRect(r, child->InkOverflowRect())) {
5971         continue;
5972       }
5973       if (child->IsTransformed()) {
5974         // for children of a preserve3d element we just pass down the same dirty
5975         // rect
5976         if (!preserves3DChildren ||
5977             !child->Combines3DTransformWithAncestors()) {
5978           const nsRect overflow = child->InkOverflowRectRelativeToSelf();
5979           nsRect out;
5980           if (nsDisplayTransform::UntransformRect(r, overflow, child, &out)) {
5981             r = out;
5982           } else {
5983             r.SetEmpty();
5984           }
5985         }
5986       }
5987       MarkFramesInSubtreeApproximatelyVisible(child, r);
5988     }
5989   }
5990 }
5991 
RebuildApproximateFrameVisibility(nsRect * aRect,bool aRemoveOnly)5992 void PresShell::RebuildApproximateFrameVisibility(
5993     nsRect* aRect, bool aRemoveOnly /* = false */) {
5994   MOZ_ASSERT(!mApproximateFrameVisibilityVisited, "already visited?");
5995   mApproximateFrameVisibilityVisited = true;
5996 
5997   nsIFrame* rootFrame = GetRootFrame();
5998   if (!rootFrame) {
5999     return;
6000   }
6001 
6002   // Remove the entries of the mApproximatelyVisibleFrames hashtable and put
6003   // them in oldApproximatelyVisibleFrames.
6004   VisibleFrames oldApproximatelyVisibleFrames =
6005       std::move(mApproximatelyVisibleFrames);
6006 
6007   nsRect vis(nsPoint(0, 0), rootFrame->GetSize());
6008   if (aRect) {
6009     vis = *aRect;
6010   }
6011 
6012   MarkFramesInSubtreeApproximatelyVisible(rootFrame, vis, aRemoveOnly);
6013 
6014   DecApproximateVisibleCount(oldApproximatelyVisibleFrames);
6015 }
6016 
UpdateApproximateFrameVisibility()6017 void PresShell::UpdateApproximateFrameVisibility() {
6018   DoUpdateApproximateFrameVisibility(/* aRemoveOnly = */ false);
6019 }
6020 
DoUpdateApproximateFrameVisibility(bool aRemoveOnly)6021 void PresShell::DoUpdateApproximateFrameVisibility(bool aRemoveOnly) {
6022   MOZ_ASSERT(
6023       !mPresContext || mPresContext->IsRootContentDocumentInProcess(),
6024       "Updating approximate frame visibility on a non-root content document?");
6025 
6026   mUpdateApproximateFrameVisibilityEvent.Revoke();
6027 
6028   if (mHaveShutDown || mIsDestroying) {
6029     return;
6030   }
6031 
6032   // call update on that frame
6033   nsIFrame* rootFrame = GetRootFrame();
6034   if (!rootFrame) {
6035     ClearApproximatelyVisibleFramesList(Some(OnNonvisible::DiscardImages));
6036     return;
6037   }
6038 
6039   RebuildApproximateFrameVisibility(/* aRect = */ nullptr, aRemoveOnly);
6040   ClearApproximateFrameVisibilityVisited(rootFrame->GetView(), true);
6041 
6042 #ifdef DEBUG_FRAME_VISIBILITY_DISPLAY_LIST
6043   // This can be used to debug the frame walker by comparing beforeFrameList
6044   // and mApproximatelyVisibleFrames in RebuildFrameVisibilityDisplayList to see
6045   // if they produce the same results (mApproximatelyVisibleFrames holds the
6046   // frames the display list thinks are visible, beforeFrameList holds the
6047   // frames the frame walker thinks are visible).
6048   nsDisplayListBuilder builder(
6049       rootFrame, nsDisplayListBuilderMode::FRAME_VISIBILITY, false);
6050   nsRect updateRect(nsPoint(0, 0), rootFrame->GetSize());
6051   nsIFrame* rootScroll = GetRootScrollFrame();
6052   if (rootScroll) {
6053     nsIContent* content = rootScroll->GetContent();
6054     if (content) {
6055       Unused << nsLayoutUtils::GetDisplayPortForVisibilityTesting(
6056           content, &updateRect, RelativeTo::ScrollFrame);
6057     }
6058 
6059     if (IgnoringViewportScrolling()) {
6060       builder.SetIgnoreScrollFrame(rootScroll);
6061     }
6062   }
6063   builder.IgnorePaintSuppression();
6064   builder.EnterPresShell(rootFrame);
6065   nsDisplayList list;
6066   rootFrame->BuildDisplayListForStackingContext(&builder, updateRect, &list);
6067   builder.LeavePresShell(rootFrame, &list);
6068 
6069   RebuildApproximateFrameVisibilityDisplayList(list);
6070 
6071   ClearApproximateFrameVisibilityVisited(rootFrame->GetView(), true);
6072 
6073   list.DeleteAll(&builder);
6074 #endif
6075 }
6076 
AssumeAllFramesVisible()6077 bool PresShell::AssumeAllFramesVisible() {
6078   if (!StaticPrefs::layout_framevisibility_enabled() || !mPresContext ||
6079       !mDocument) {
6080     return true;
6081   }
6082 
6083   // We assume all frames are visible in print, print preview, chrome, and
6084   // resource docs and don't keep track of them.
6085   if (mPresContext->Type() == nsPresContext::eContext_PrintPreview ||
6086       mPresContext->Type() == nsPresContext::eContext_Print ||
6087       mPresContext->IsChrome() || mDocument->IsResourceDoc()) {
6088     return true;
6089   }
6090 
6091   // If we're assuming all frames are visible in the top level content
6092   // document, we need to in subdocuments as well. Otherwise we can get in a
6093   // situation where things like animations won't work in subdocuments because
6094   // their frames appear not to be visible, since we won't schedule an image
6095   // visibility update if the top level content document is assuming all
6096   // frames are visible.
6097   //
6098   // Note that it's not safe to call IsRootContentDocument() if we're
6099   // currently being destroyed, so we have to check that first.
6100   if (!mHaveShutDown && !mIsDestroying &&
6101       !mPresContext->IsRootContentDocumentInProcess()) {
6102     nsPresContext* presContext =
6103         mPresContext->GetInProcessRootContentDocumentPresContext();
6104     if (presContext && presContext->PresShell()->AssumeAllFramesVisible()) {
6105       return true;
6106     }
6107   }
6108 
6109   return false;
6110 }
6111 
ScheduleApproximateFrameVisibilityUpdateSoon()6112 void PresShell::ScheduleApproximateFrameVisibilityUpdateSoon() {
6113   if (AssumeAllFramesVisible()) {
6114     return;
6115   }
6116 
6117   if (!mPresContext) {
6118     return;
6119   }
6120 
6121   nsRefreshDriver* refreshDriver = mPresContext->RefreshDriver();
6122   if (!refreshDriver) {
6123     return;
6124   }
6125 
6126   // Ask the refresh driver to update frame visibility soon.
6127   refreshDriver->ScheduleFrameVisibilityUpdate();
6128 }
6129 
ScheduleApproximateFrameVisibilityUpdateNow()6130 void PresShell::ScheduleApproximateFrameVisibilityUpdateNow() {
6131   if (AssumeAllFramesVisible()) {
6132     return;
6133   }
6134 
6135   if (!mPresContext->IsRootContentDocumentInProcess()) {
6136     nsPresContext* presContext =
6137         mPresContext->GetInProcessRootContentDocumentPresContext();
6138     if (!presContext) return;
6139     MOZ_ASSERT(presContext->IsRootContentDocumentInProcess(),
6140                "Didn't get a root prescontext from "
6141                "GetInProcessRootContentDocumentPresContext?");
6142     presContext->PresShell()->ScheduleApproximateFrameVisibilityUpdateNow();
6143     return;
6144   }
6145 
6146   if (mHaveShutDown || mIsDestroying) {
6147     return;
6148   }
6149 
6150   if (mUpdateApproximateFrameVisibilityEvent.IsPending()) {
6151     return;
6152   }
6153 
6154   RefPtr<nsRunnableMethod<PresShell>> event =
6155       NewRunnableMethod("PresShell::UpdateApproximateFrameVisibility", this,
6156                         &PresShell::UpdateApproximateFrameVisibility);
6157   nsresult rv = mDocument->Dispatch(TaskCategory::Other, do_AddRef(event));
6158 
6159   if (NS_SUCCEEDED(rv)) {
6160     mUpdateApproximateFrameVisibilityEvent = std::move(event);
6161   }
6162 }
6163 
EnsureFrameInApproximatelyVisibleList(nsIFrame * aFrame)6164 void PresShell::EnsureFrameInApproximatelyVisibleList(nsIFrame* aFrame) {
6165   if (!aFrame->TrackingVisibility()) {
6166     return;
6167   }
6168 
6169   if (AssumeAllFramesVisible()) {
6170     aFrame->IncApproximateVisibleCount();
6171     return;
6172   }
6173 
6174 #ifdef DEBUG
6175   // Make sure it's in this pres shell.
6176   nsCOMPtr<nsIContent> content = aFrame->GetContent();
6177   if (content) {
6178     PresShell* presShell = content->OwnerDoc()->GetPresShell();
6179     MOZ_ASSERT(!presShell || presShell == this, "wrong shell");
6180   }
6181 #endif
6182 
6183   if (mApproximatelyVisibleFrames.EnsureInserted(aFrame)) {
6184     // We inserted a new entry.
6185     aFrame->IncApproximateVisibleCount();
6186   }
6187 }
6188 
RemoveFrameFromApproximatelyVisibleList(nsIFrame * aFrame)6189 void PresShell::RemoveFrameFromApproximatelyVisibleList(nsIFrame* aFrame) {
6190 #ifdef DEBUG
6191   // Make sure it's in this pres shell.
6192   nsCOMPtr<nsIContent> content = aFrame->GetContent();
6193   if (content) {
6194     PresShell* presShell = content->OwnerDoc()->GetPresShell();
6195     MOZ_ASSERT(!presShell || presShell == this, "wrong shell");
6196   }
6197 #endif
6198 
6199   if (AssumeAllFramesVisible()) {
6200     MOZ_ASSERT(mApproximatelyVisibleFrames.Count() == 0,
6201                "Shouldn't have any frames in the table");
6202     return;
6203   }
6204 
6205   if (mApproximatelyVisibleFrames.EnsureRemoved(aFrame) &&
6206       aFrame->TrackingVisibility()) {
6207     // aFrame was in the hashtable, and we're still tracking its visibility,
6208     // so we need to decrement its visible count.
6209     aFrame->DecApproximateVisibleCount();
6210   }
6211 }
6212 
6213 class nsAutoNotifyDidPaint {
6214  public:
nsAutoNotifyDidPaint(PresShell * aShell,PaintFlags aFlags)6215   nsAutoNotifyDidPaint(PresShell* aShell, PaintFlags aFlags)
6216       : mShell(aShell), mFlags(aFlags) {}
~nsAutoNotifyDidPaint()6217   ~nsAutoNotifyDidPaint() {
6218     if (!!(mFlags & PaintFlags::PaintComposite)) {
6219       mShell->GetPresContext()->NotifyDidPaintForSubtree();
6220     }
6221   }
6222 
6223  private:
6224   PresShell* mShell;
6225   PaintFlags mFlags;
6226 };
6227 
Paint(nsView * aViewToPaint,const nsRegion & aDirtyRegion,PaintFlags aFlags)6228 void PresShell::Paint(nsView* aViewToPaint, const nsRegion& aDirtyRegion,
6229                       PaintFlags aFlags) {
6230   nsCString url;
6231   nsIURI* uri = mDocument->GetDocumentURI();
6232   Document* contentRoot = GetPrimaryContentDocument();
6233   if (contentRoot) {
6234     uri = contentRoot->GetDocumentURI();
6235   }
6236   url = uri ? uri->GetSpecOrDefault() : "N/A"_ns;
6237   AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING("PresShell::Paint", GRAPHICS, url);
6238 
6239   Maybe<js::AutoAssertNoContentJS> nojs;
6240 
6241   // On Android, Flash can call into content JS during painting, so we can't
6242   // assert there. However, we don't rely on this assertion on Android because
6243   // we don't paint while JS is running.
6244 #if !defined(MOZ_WIDGET_ANDROID)
6245   if (!(aFlags & PaintFlags::PaintComposite)) {
6246     // We need to allow content JS when the flag is set since we may trigger
6247     // MozAfterPaint events in content in those cases.
6248     nojs.emplace(dom::danger::GetJSContext());
6249   }
6250 #endif
6251 
6252   NS_ASSERTION(!mIsDestroying, "painting a destroyed PresShell");
6253   NS_ASSERTION(aViewToPaint, "null view");
6254 
6255   MOZ_ASSERT(!mApproximateFrameVisibilityVisited, "Should have been cleared");
6256 
6257   if (!mIsActive) {
6258     return;
6259   }
6260 
6261   if (StaticPrefs::apz_keyboard_enabled_AtStartup()) {
6262     // Update the focus target for async keyboard scrolling. This will be
6263     // forwarded to APZ by nsDisplayList::PaintRoot. We need to to do this
6264     // before we enter the paint phase because dispatching eVoid events can
6265     // cause layout to happen.
6266     mAPZFocusTarget = FocusTarget(this, mAPZFocusSequenceNumber);
6267   }
6268 
6269   nsPresContext* presContext = GetPresContext();
6270   AUTO_LAYOUT_PHASE_ENTRY_POINT(presContext, Paint);
6271 
6272   nsIFrame* frame = aViewToPaint->GetFrame();
6273 
6274   LayerManager* layerManager = aViewToPaint->GetWidget()->GetLayerManager();
6275   NS_ASSERTION(layerManager, "Must be in paint event");
6276   bool shouldInvalidate = layerManager->NeedsWidgetInvalidation();
6277 
6278   nsAutoNotifyDidPaint notifyDidPaint(this, aFlags);
6279 
6280   // Whether or not we should set first paint when painting is suppressed
6281   // is debatable. For now we'll do it because B2G relied on first paint
6282   // to configure the viewport and we only want to do that when we have
6283   // real content to paint. See Bug 798245
6284   if (mIsFirstPaint && !mPaintingSuppressed) {
6285     MOZ_LOG(gLog, LogLevel::Debug,
6286             ("PresShell::Paint, first paint, this=%p", this));
6287 
6288     layerManager->SetIsFirstPaint();
6289     mIsFirstPaint = false;
6290   }
6291 
6292   if (!layerManager->BeginTransaction(url)) {
6293     return;
6294   }
6295 
6296   // Send an updated focus target with this transaction. Be sure to do this
6297   // before we paint in the case this is an empty transaction.
6298   layerManager->SetFocusTarget(mAPZFocusTarget);
6299 
6300   if (frame) {
6301     // Try to do an empty transaction, if the frame tree does not
6302     // need to be updated. Do not try to do an empty transaction on
6303     // a non-retained layer manager (like the BasicLayerManager that
6304     // draws the window title bar on Mac), because a) it won't work
6305     // and b) below we don't want to clear NS_FRAME_UPDATE_LAYER_TREE,
6306     // that will cause us to forget to update the real layer manager!
6307 
6308     if (!(aFlags & PaintFlags::PaintLayers)) {
6309       if (layerManager->EndEmptyTransaction()) {
6310         return;
6311       }
6312       NS_WARNING("Must complete empty transaction when compositing!");
6313     }
6314 
6315     if (!(aFlags & PaintFlags::PaintSyncDecodeImages) &&
6316         !frame->HasAnyStateBits(NS_FRAME_UPDATE_LAYER_TREE) &&
6317         !mNextPaintCompressed) {
6318       NotifySubDocInvalidationFunc computeInvalidFunc =
6319           presContext->MayHavePaintEventListenerInSubDocument()
6320               ? nsPresContext::NotifySubDocInvalidation
6321               : 0;
6322       bool computeInvalidRect =
6323           computeInvalidFunc ||
6324           (layerManager->GetBackendType() == LayersBackend::LAYERS_BASIC);
6325 
6326       UniquePtr<LayerProperties> props;
6327       // For WR, the layermanager has no root layer. We want to avoid
6328       // calling ComputeDifferences in that case because it assumes non-null
6329       // and crashes.
6330       if (computeInvalidRect && layerManager->GetRoot()) {
6331         props = LayerProperties::CloneFrom(layerManager->GetRoot());
6332       }
6333 
6334       MaybeSetupTransactionIdAllocator(layerManager, presContext);
6335 
6336       if (layerManager->EndEmptyTransaction(
6337               (aFlags & PaintFlags::PaintComposite)
6338                   ? LayerManager::END_DEFAULT
6339                   : LayerManager::END_NO_COMPOSITE)) {
6340         nsIntRegion invalid;
6341         bool areaOverflowed = false;
6342         if (props) {
6343           if (!props->ComputeDifferences(layerManager->GetRoot(), invalid,
6344                                          computeInvalidFunc)) {
6345             areaOverflowed = true;
6346           }
6347         } else {
6348           LayerProperties::ClearInvalidations(layerManager->GetRoot());
6349         }
6350         if (props && !areaOverflowed) {
6351           if (!invalid.IsEmpty()) {
6352             nsIntRect bounds = invalid.GetBounds();
6353             nsRect rect(presContext->DevPixelsToAppUnits(bounds.x),
6354                         presContext->DevPixelsToAppUnits(bounds.y),
6355                         presContext->DevPixelsToAppUnits(bounds.width),
6356                         presContext->DevPixelsToAppUnits(bounds.height));
6357             if (shouldInvalidate) {
6358               aViewToPaint->GetViewManager()->InvalidateViewNoSuppression(
6359                   aViewToPaint, rect);
6360             }
6361             presContext->NotifyInvalidation(
6362                 layerManager->GetLastTransactionId(), bounds);
6363           }
6364         } else if (shouldInvalidate) {
6365           aViewToPaint->GetViewManager()->InvalidateView(aViewToPaint);
6366         }
6367 
6368         frame->UpdatePaintCountForPaintedPresShells();
6369         return;
6370       }
6371     }
6372     frame->RemoveStateBits(NS_FRAME_UPDATE_LAYER_TREE);
6373   }
6374   if (frame) {
6375     frame->ClearPresShellsFromLastPaint();
6376   }
6377 
6378   nscolor bgcolor = ComputeBackstopColor(aViewToPaint);
6379   PaintFrameFlags flags =
6380       PaintFrameFlags::WidgetLayers | PaintFrameFlags::ExistingTransaction;
6381   if (!(aFlags & PaintFlags::PaintComposite)) {
6382     flags |= PaintFrameFlags::NoComposite;
6383   }
6384   // We force sync-decode for printing / print-preview (printing already does
6385   // this from nsPageSequenceFrame::PrintNextSheet).
6386   if (aFlags & PaintFlags::PaintSyncDecodeImages ||
6387       mDocument->IsStaticDocument()) {
6388     flags |= PaintFrameFlags::SyncDecodeImages;
6389   }
6390   if (mNextPaintCompressed) {
6391     flags |= PaintFrameFlags::Compressed;
6392     mNextPaintCompressed = false;
6393   }
6394   if (layerManager->GetBackendType() == layers::LayersBackend::LAYERS_WR) {
6395     flags |= PaintFrameFlags::ForWebRender;
6396   }
6397 
6398   if (frame) {
6399     // We can paint directly into the widget using its layer manager.
6400     nsLayoutUtils::PaintFrame(nullptr, frame, aDirtyRegion, bgcolor,
6401                               nsDisplayListBuilderMode::Painting, flags);
6402     return;
6403   }
6404 
6405   if (layerManager->GetBackendType() == layers::LayersBackend::LAYERS_WR) {
6406     nsPresContext* pc = GetPresContext();
6407     LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
6408         pc->GetVisibleArea(), pc->AppUnitsPerDevPixel());
6409     bgcolor = NS_ComposeColors(bgcolor, mCanvasBackgroundColor);
6410     WebRenderBackgroundData data(wr::ToLayoutRect(bounds),
6411                                  wr::ToColorF(ToDeviceColor(bgcolor)));
6412     WrFiltersHolder wrFilters;
6413 
6414     MaybeSetupTransactionIdAllocator(layerManager, presContext);
6415     layerManager->AsWebRenderLayerManager()->EndTransactionWithoutLayer(
6416         nullptr, nullptr, std::move(wrFilters), &data, 0);
6417     return;
6418   }
6419 
6420   RefPtr<ColorLayer> root = layerManager->CreateColorLayer();
6421   if (root) {
6422     nsPresContext* pc = GetPresContext();
6423     nsIntRect bounds =
6424         pc->GetVisibleArea().ToOutsidePixels(pc->AppUnitsPerDevPixel());
6425     bgcolor = NS_ComposeColors(bgcolor, mCanvasBackgroundColor);
6426     root->SetColor(ToDeviceColor(bgcolor));
6427     root->SetVisibleRegion(LayerIntRegion::FromUnknownRegion(bounds));
6428     layerManager->SetRoot(root);
6429   }
6430   MaybeSetupTransactionIdAllocator(layerManager, presContext);
6431   layerManager->EndTransaction(nullptr, nullptr,
6432                                (aFlags & PaintFlags::PaintComposite)
6433                                    ? LayerManager::END_DEFAULT
6434                                    : LayerManager::END_NO_COMPOSITE);
6435 }
6436 
6437 // static
SetCapturingContent(nsIContent * aContent,CaptureFlags aFlags,WidgetEvent * aEvent)6438 void PresShell::SetCapturingContent(nsIContent* aContent, CaptureFlags aFlags,
6439                                     WidgetEvent* aEvent) {
6440   // If capture was set for pointer lock, don't unlock unless we are coming
6441   // out of pointer lock explicitly.
6442   if (!aContent && sCapturingContentInfo.mPointerLock &&
6443       !(aFlags & CaptureFlags::PointerLock)) {
6444     return;
6445   }
6446 
6447   sCapturingContentInfo.mContent = nullptr;
6448   sCapturingContentInfo.mRemoteTarget = nullptr;
6449 
6450   // only set capturing content if allowed or the
6451   // CaptureFlags::IgnoreAllowedState or CaptureFlags::PointerLock are used.
6452   if ((aFlags & CaptureFlags::IgnoreAllowedState) ||
6453       sCapturingContentInfo.mAllowed || (aFlags & CaptureFlags::PointerLock)) {
6454     if (aContent) {
6455       sCapturingContentInfo.mContent = aContent;
6456     }
6457     if (aEvent) {
6458       MOZ_ASSERT(XRE_IsParentProcess());
6459       MOZ_ASSERT(aEvent->mMessage == eMouseDown);
6460       MOZ_ASSERT(aEvent->HasBeenPostedToRemoteProcess());
6461       sCapturingContentInfo.mRemoteTarget =
6462           BrowserParent::GetLastMouseRemoteTarget();
6463       MOZ_ASSERT(sCapturingContentInfo.mRemoteTarget);
6464     }
6465     // CaptureFlags::PointerLock is the same as
6466     // CaptureFlags::RetargetToElement & CaptureFlags::IgnoreAllowedState.
6467     sCapturingContentInfo.mRetargetToElement =
6468         !!(aFlags & CaptureFlags::RetargetToElement) ||
6469         !!(aFlags & CaptureFlags::PointerLock);
6470     sCapturingContentInfo.mPreventDrag =
6471         !!(aFlags & CaptureFlags::PreventDragStart);
6472     sCapturingContentInfo.mPointerLock = !!(aFlags & CaptureFlags::PointerLock);
6473   }
6474 }
6475 
GetCurrentEventContent()6476 nsIContent* PresShell::GetCurrentEventContent() {
6477   if (mCurrentEventContent &&
6478       mCurrentEventContent->GetComposedDoc() != mDocument) {
6479     mCurrentEventContent = nullptr;
6480     mCurrentEventFrame = nullptr;
6481   }
6482   return mCurrentEventContent;
6483 }
6484 
GetCurrentEventFrame()6485 nsIFrame* PresShell::GetCurrentEventFrame() {
6486   if (MOZ_UNLIKELY(mIsDestroying)) {
6487     return nullptr;
6488   }
6489 
6490   // GetCurrentEventContent() makes sure the content is still in the
6491   // same document that this pres shell belongs to. If not, then the
6492   // frame shouldn't get an event, nor should we even assume its safe
6493   // to try and find the frame.
6494   nsIContent* content = GetCurrentEventContent();
6495   if (!mCurrentEventFrame && content) {
6496     mCurrentEventFrame = content->GetPrimaryFrame();
6497     MOZ_ASSERT(!mCurrentEventFrame ||
6498                mCurrentEventFrame->PresContext()->GetPresShell() == this);
6499   }
6500   return mCurrentEventFrame;
6501 }
6502 
GetEventTargetContent(WidgetEvent * aEvent)6503 already_AddRefed<nsIContent> PresShell::GetEventTargetContent(
6504     WidgetEvent* aEvent) {
6505   nsCOMPtr<nsIContent> content = GetCurrentEventContent();
6506   if (!content) {
6507     nsIFrame* currentEventFrame = GetCurrentEventFrame();
6508     if (currentEventFrame) {
6509       currentEventFrame->GetContentForEvent(aEvent, getter_AddRefs(content));
6510       NS_ASSERTION(!content || content->GetComposedDoc() == mDocument,
6511                    "handing out content from a different doc");
6512     }
6513   }
6514   return content.forget();
6515 }
6516 
PushCurrentEventInfo(nsIFrame * aFrame,nsIContent * aContent)6517 void PresShell::PushCurrentEventInfo(nsIFrame* aFrame, nsIContent* aContent) {
6518   if (mCurrentEventFrame || mCurrentEventContent) {
6519     mCurrentEventFrameStack.InsertElementAt(0, mCurrentEventFrame);
6520     mCurrentEventContentStack.InsertObjectAt(mCurrentEventContent, 0);
6521   }
6522   mCurrentEventFrame = aFrame;
6523   mCurrentEventContent = aContent;
6524 }
6525 
PopCurrentEventInfo()6526 void PresShell::PopCurrentEventInfo() {
6527   mCurrentEventFrame = nullptr;
6528   mCurrentEventContent = nullptr;
6529 
6530   if (0 != mCurrentEventFrameStack.Length()) {
6531     mCurrentEventFrame = mCurrentEventFrameStack.ElementAt(0);
6532     mCurrentEventFrameStack.RemoveElementAt(0);
6533     mCurrentEventContent = mCurrentEventContentStack.ObjectAt(0);
6534     mCurrentEventContentStack.RemoveObjectAt(0);
6535 
6536     // Don't use it if it has moved to a different document.
6537     if (mCurrentEventContent &&
6538         mCurrentEventContent->GetComposedDoc() != mDocument) {
6539       mCurrentEventContent = nullptr;
6540       mCurrentEventFrame = nullptr;
6541     }
6542   }
6543 }
6544 
6545 // static
InZombieDocument(nsIContent * aContent)6546 bool PresShell::EventHandler::InZombieDocument(nsIContent* aContent) {
6547   // If a content node points to a null document, or the document is not
6548   // attached to a window, then it is possibly in a zombie document,
6549   // about to be replaced by a newly loading document.
6550   // Such documents cannot handle DOM events.
6551   // It might actually be in a node not attached to any document,
6552   // in which case there is not parent presshell to retarget it to.
6553   Document* doc = aContent->GetComposedDoc();
6554   return !doc || !doc->GetWindow();
6555 }
6556 
GetRootWindow()6557 already_AddRefed<nsPIDOMWindowOuter> PresShell::GetRootWindow() {
6558   nsCOMPtr<nsPIDOMWindowOuter> window = mDocument->GetWindow();
6559   if (window) {
6560     nsCOMPtr<nsPIDOMWindowOuter> rootWindow = window->GetPrivateRoot();
6561     NS_ASSERTION(rootWindow, "nsPIDOMWindow::GetPrivateRoot() returns NULL");
6562     return rootWindow.forget();
6563   }
6564 
6565   // If we don't have DOM window, we're zombie, we should find the root window
6566   // with our parent shell.
6567   RefPtr<PresShell> parentPresShell = GetParentPresShellForEventHandling();
6568   NS_ENSURE_TRUE(parentPresShell, nullptr);
6569   return parentPresShell->GetRootWindow();
6570 }
6571 
6572 already_AddRefed<nsPIDOMWindowOuter>
GetFocusedDOMWindowInOurWindow()6573 PresShell::GetFocusedDOMWindowInOurWindow() {
6574   nsCOMPtr<nsPIDOMWindowOuter> rootWindow = GetRootWindow();
6575   NS_ENSURE_TRUE(rootWindow, nullptr);
6576   nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
6577   nsFocusManager::GetFocusedDescendant(rootWindow,
6578                                        nsFocusManager::eIncludeAllDescendants,
6579                                        getter_AddRefs(focusedWindow));
6580   return focusedWindow.forget();
6581 }
6582 
GetFocusedContentInOurWindow() const6583 already_AddRefed<nsIContent> PresShell::GetFocusedContentInOurWindow() const {
6584   nsFocusManager* fm = nsFocusManager::GetFocusManager();
6585   if (fm && mDocument) {
6586     RefPtr<Element> focusedElement;
6587     fm->GetFocusedElementForWindow(mDocument->GetWindow(), false, nullptr,
6588                                    getter_AddRefs(focusedElement));
6589     return focusedElement.forget();
6590   }
6591   return nullptr;
6592 }
6593 
GetParentPresShellForEventHandling()6594 already_AddRefed<PresShell> PresShell::GetParentPresShellForEventHandling() {
6595   if (!mPresContext) {
6596     return nullptr;
6597   }
6598 
6599   // Now, find the parent pres shell and send the event there
6600   RefPtr<nsDocShell> docShell = mPresContext->GetDocShell();
6601   if (!docShell) {
6602     docShell = mForwardingContainer.get();
6603   }
6604 
6605   // Might have gone away, or never been around to start with
6606   if (!docShell) {
6607     return nullptr;
6608   }
6609 
6610   BrowsingContext* bc = docShell->GetBrowsingContext();
6611   if (!bc) {
6612     return nullptr;
6613   }
6614 
6615   RefPtr<BrowsingContext> parentBC;
6616   if (XRE_IsParentProcess()) {
6617     parentBC = bc->Canonical()->GetParentCrossChromeBoundary();
6618   } else {
6619     parentBC = bc->GetParent();
6620   }
6621 
6622   RefPtr<nsIDocShell> parentDocShell =
6623       parentBC ? parentBC->GetDocShell() : nullptr;
6624   if (!parentDocShell) {
6625     return nullptr;
6626   }
6627 
6628   RefPtr<PresShell> parentPresShell = parentDocShell->GetPresShell();
6629   return parentPresShell.forget();
6630 }
6631 
RetargetEventToParent(WidgetGUIEvent * aGUIEvent,nsEventStatus * aEventStatus)6632 nsresult PresShell::EventHandler::RetargetEventToParent(
6633     WidgetGUIEvent* aGUIEvent, nsEventStatus* aEventStatus) {
6634   // Send this events straight up to the parent pres shell.
6635   // We do this for keystroke events in zombie documents or if either a frame
6636   // or a root content is not present.
6637   // That way at least the UI key bindings can work.
6638 
6639   RefPtr<PresShell> parentPresShell = GetParentPresShellForEventHandling();
6640   NS_ENSURE_TRUE(parentPresShell, NS_ERROR_FAILURE);
6641 
6642   // Fake the event as though it's from the parent pres shell's root frame.
6643   return parentPresShell->HandleEvent(parentPresShell->GetRootFrame(),
6644                                       aGUIEvent, true, aEventStatus);
6645 }
6646 
DisableNonTestMouseEvents(bool aDisable)6647 void PresShell::DisableNonTestMouseEvents(bool aDisable) {
6648   sDisableNonTestMouseEvents = aDisable;
6649 }
6650 
MouseLocationWasSetBySynthesizedMouseEventForTests() const6651 bool PresShell::MouseLocationWasSetBySynthesizedMouseEventForTests() const {
6652   if (!mPresContext) {
6653     return false;
6654   }
6655   if (mPresContext->IsRoot()) {
6656     return mMouseLocationWasSetBySynthesizedMouseEventForTests;
6657   }
6658   PresShell* rootPresShell = GetRootPresShell();
6659   return rootPresShell &&
6660          rootPresShell->mMouseLocationWasSetBySynthesizedMouseEventForTests;
6661 }
6662 
RecordMouseLocation(WidgetGUIEvent * aEvent)6663 void PresShell::RecordMouseLocation(WidgetGUIEvent* aEvent) {
6664   if (!mPresContext) return;
6665 
6666   if (!mPresContext->IsRoot()) {
6667     PresShell* rootPresShell = GetRootPresShell();
6668     if (rootPresShell) {
6669       rootPresShell->RecordMouseLocation(aEvent);
6670     }
6671     return;
6672   }
6673 
6674   if ((aEvent->mMessage == eMouseMove &&
6675        aEvent->AsMouseEvent()->mReason == WidgetMouseEvent::eReal) ||
6676       aEvent->mMessage == eMouseEnterIntoWidget ||
6677       aEvent->mMessage == eMouseDown || aEvent->mMessage == eMouseUp) {
6678     nsIFrame* rootFrame = GetRootFrame();
6679     if (!rootFrame) {
6680       nsView* rootView = mViewManager->GetRootView();
6681       mMouseLocation = nsLayoutUtils::TranslateWidgetToView(
6682           mPresContext, aEvent->mWidget, aEvent->mRefPoint, rootView);
6683       mMouseEventTargetGuid = InputAPZContext::GetTargetLayerGuid();
6684     } else {
6685       RelativeTo relativeTo{rootFrame};
6686       if (rootFrame->PresContext()->IsRootContentDocumentCrossProcess()) {
6687         relativeTo.mViewportType = ViewportType::Visual;
6688       }
6689       mMouseLocation =
6690           nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, relativeTo);
6691       mMouseEventTargetGuid = InputAPZContext::GetTargetLayerGuid();
6692     }
6693     mMouseLocationWasSetBySynthesizedMouseEventForTests =
6694         aEvent->mFlags.mIsSynthesizedForTests;
6695 #ifdef DEBUG_MOUSE_LOCATION
6696     if (aEvent->mMessage == eMouseEnterIntoWidget) {
6697       printf("[ps=%p]got mouse enter for %p\n", this, aEvent->mWidget);
6698     }
6699     printf("[ps=%p]setting mouse location to (%d,%d)\n", this, mMouseLocation.x,
6700            mMouseLocation.y);
6701 #endif
6702     if (aEvent->mMessage == eMouseEnterIntoWidget) {
6703       SynthesizeMouseMove(false);
6704     }
6705   } else if (aEvent->mMessage == eMouseExitFromWidget) {
6706     // Although we only care about the mouse moving into an area for which this
6707     // pres shell doesn't receive mouse move events, we don't check which widget
6708     // the mouse exit was for since this seems to vary by platform.  Hopefully
6709     // this won't matter at all since we'll get the mouse move or enter after
6710     // the mouse exit when the mouse moves from one of our widgets into another.
6711     mMouseLocation = nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
6712     mMouseEventTargetGuid = InputAPZContext::GetTargetLayerGuid();
6713     mMouseLocationWasSetBySynthesizedMouseEventForTests =
6714         aEvent->mFlags.mIsSynthesizedForTests;
6715 #ifdef DEBUG_MOUSE_LOCATION
6716     printf("[ps=%p]got mouse exit for %p\n", this, aEvent->mWidget);
6717     printf("[ps=%p]clearing mouse location\n", this);
6718 #endif
6719   }
6720 }
6721 
Revoke()6722 void PresShell::nsSynthMouseMoveEvent::Revoke() {
6723   if (mPresShell) {
6724     mPresShell->GetPresContext()->RefreshDriver()->RemoveRefreshObserver(
6725         this, FlushType::Display);
6726     mPresShell = nullptr;
6727   }
6728 }
6729 
6730 // static
GetNearestFrameContainingPresShell(PresShell * aPresShell)6731 nsIFrame* PresShell::EventHandler::GetNearestFrameContainingPresShell(
6732     PresShell* aPresShell) {
6733   nsView* view = aPresShell->GetViewManager()->GetRootView();
6734   while (view && !view->GetFrame()) {
6735     view = view->GetParent();
6736   }
6737 
6738   nsIFrame* frame = nullptr;
6739   if (view) {
6740     frame = view->GetFrame();
6741   }
6742 
6743   return frame;
6744 }
6745 
FlushThrottledStyles(Document & aDocument)6746 static CallState FlushThrottledStyles(Document& aDocument) {
6747   PresShell* presShell = aDocument.GetPresShell();
6748   if (presShell && presShell->IsVisible()) {
6749     if (nsPresContext* presContext = presShell->GetPresContext()) {
6750       presContext->RestyleManager()->UpdateOnlyAnimationStyles();
6751     }
6752   }
6753 
6754   aDocument.EnumerateSubDocuments(FlushThrottledStyles);
6755   return CallState::Continue;
6756 }
6757 
CanDispatchEvent(const WidgetGUIEvent * aEvent) const6758 bool PresShell::CanDispatchEvent(const WidgetGUIEvent* aEvent) const {
6759   bool rv =
6760       mPresContext && !mHaveShutDown && nsContentUtils::IsSafeToRunScript();
6761   if (aEvent) {
6762     rv &= (aEvent && aEvent->mWidget && !aEvent->mWidget->Destroyed());
6763   }
6764   return rv;
6765 }
6766 
6767 /* static */
GetShellForEventTarget(nsIFrame * aFrame,nsIContent * aContent)6768 PresShell* PresShell::GetShellForEventTarget(nsIFrame* aFrame,
6769                                              nsIContent* aContent) {
6770   if (aFrame) {
6771     return aFrame->PresShell();
6772   }
6773   if (aContent) {
6774     Document* doc = aContent->GetComposedDoc();
6775     if (!doc) {
6776       return nullptr;
6777     }
6778     return doc->GetPresShell();
6779   }
6780   return nullptr;
6781 }
6782 
6783 /* static */
GetShellForTouchEvent(WidgetGUIEvent * aEvent)6784 PresShell* PresShell::GetShellForTouchEvent(WidgetGUIEvent* aEvent) {
6785   switch (aEvent->mMessage) {
6786     case eTouchMove:
6787     case eTouchCancel:
6788     case eTouchEnd: {
6789       // get the correct shell to dispatch to
6790       WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
6791       for (dom::Touch* touch : touchEvent->mTouches) {
6792         if (!touch) {
6793           return nullptr;
6794         }
6795 
6796         RefPtr<dom::Touch> oldTouch =
6797             TouchManager::GetCapturedTouch(touch->Identifier());
6798         if (!oldTouch) {
6799           return nullptr;
6800         }
6801 
6802         nsCOMPtr<nsIContent> content = do_QueryInterface(oldTouch->GetTarget());
6803         if (!content) {
6804           return nullptr;
6805         }
6806 
6807         nsIFrame* contentFrame = content->GetPrimaryFrame();
6808         if (!contentFrame) {
6809           return nullptr;
6810         }
6811 
6812         PresShell* presShell = contentFrame->PresContext()->GetPresShell();
6813         if (presShell) {
6814           return presShell;
6815         }
6816       }
6817       return nullptr;
6818     }
6819     default:
6820       return nullptr;
6821   }
6822 }
6823 
HandleEvent(nsIFrame * aFrameForPresShell,WidgetGUIEvent * aGUIEvent,bool aDontRetargetEvents,nsEventStatus * aEventStatus)6824 nsresult PresShell::HandleEvent(nsIFrame* aFrameForPresShell,
6825                                 WidgetGUIEvent* aGUIEvent,
6826                                 bool aDontRetargetEvents,
6827                                 nsEventStatus* aEventStatus) {
6828   MOZ_ASSERT(aGUIEvent);
6829   // If it's synthesized in the parent process and our mouse location was set
6830   // by a mouse event which was synthesized for tests because the test does not
6831   // want to change `:hover` state with the synthesized mouse event for native
6832   // mouse cursor position.
6833   if (aGUIEvent->mMessage == eMouseMove &&
6834       aGUIEvent->CameFromAnotherProcess() && XRE_IsContentProcess() &&
6835       !aGUIEvent->mFlags.mIsSynthesizedForTests &&
6836       MouseLocationWasSetBySynthesizedMouseEventForTests() &&
6837       aGUIEvent->AsMouseEvent()->mReason == WidgetMouseEvent::eSynthesized) {
6838     return NS_OK;
6839   }
6840   EventHandler eventHandler(*this);
6841   return eventHandler.HandleEvent(aFrameForPresShell, aGUIEvent,
6842                                   aDontRetargetEvents, aEventStatus);
6843 }
6844 
HandleEvent(nsIFrame * aFrameForPresShell,WidgetGUIEvent * aGUIEvent,bool aDontRetargetEvents,nsEventStatus * aEventStatus)6845 nsresult PresShell::EventHandler::HandleEvent(nsIFrame* aFrameForPresShell,
6846                                               WidgetGUIEvent* aGUIEvent,
6847                                               bool aDontRetargetEvents,
6848                                               nsEventStatus* aEventStatus) {
6849   MOZ_ASSERT(aGUIEvent);
6850   MOZ_DIAGNOSTIC_ASSERT(aGUIEvent->IsTrusted());
6851   MOZ_ASSERT(aEventStatus);
6852 
6853   NS_ASSERTION(aFrameForPresShell, "aFrameForPresShell should be not null");
6854 
6855   // Update the latest focus sequence number with this new sequence number;
6856   // the next transasction that gets sent to the compositor will carry this over
6857   if (mPresShell->mAPZFocusSequenceNumber < aGUIEvent->mFocusSequenceNumber) {
6858     mPresShell->mAPZFocusSequenceNumber = aGUIEvent->mFocusSequenceNumber;
6859   }
6860 
6861   if (mPresShell->IsDestroying() ||
6862       (PresShell::sDisableNonTestMouseEvents &&
6863        !aGUIEvent->mFlags.mIsSynthesizedForTests &&
6864        aGUIEvent->HasMouseEventMessage())) {
6865     return NS_OK;
6866   }
6867 
6868   mPresShell->RecordMouseLocation(aGUIEvent);
6869 
6870   if (MaybeHandleEventWithAccessibleCaret(aFrameForPresShell, aGUIEvent,
6871                                           aEventStatus)) {
6872     // Handled by AccessibleCaretEventHub.
6873     return NS_OK;
6874   }
6875 
6876   if (MaybeDiscardEvent(aGUIEvent)) {
6877     // Cannot handle the event for now.
6878     return NS_OK;
6879   }
6880 
6881   if (!aDontRetargetEvents) {
6882     // If aGUIEvent should be handled in another PresShell, we should call its
6883     // HandleEvent() and do nothing here.
6884     nsresult rv = NS_OK;
6885     if (MaybeHandleEventWithAnotherPresShell(aFrameForPresShell, aGUIEvent,
6886                                              aEventStatus, &rv)) {
6887       // Handled by another PresShell or nobody can handle the event.
6888       return rv;
6889     }
6890   }
6891 
6892   if (MaybeDiscardOrDelayKeyboardEvent(aGUIEvent)) {
6893     // The event is discarded or put into the delayed event queue.
6894     return NS_OK;
6895   }
6896 
6897   if (aGUIEvent->IsUsingCoordinates()) {
6898     return HandleEventUsingCoordinates(aFrameForPresShell, aGUIEvent,
6899                                        aEventStatus, aDontRetargetEvents);
6900   }
6901 
6902   // Activation events need to be dispatched even if no frame was found, since
6903   // we don't want the focus to be out of sync.
6904   if (!aFrameForPresShell) {
6905     if (!NS_EVENT_NEEDS_FRAME(aGUIEvent)) {
6906       // Push nullptr for both current event target content and frame since
6907       // there is no frame but the event does not require a frame.
6908       AutoCurrentEventInfoSetter eventInfoSetter(*this);
6909       return HandleEventWithCurrentEventInfo(aGUIEvent, aEventStatus, true,
6910                                              nullptr);
6911     }
6912 
6913     if (aGUIEvent->HasKeyEventMessage()) {
6914       // Keypress events in new blank tabs should not be completely thrown away.
6915       // Retarget them -- the parent chrome shell might make use of them.
6916       return RetargetEventToParent(aGUIEvent, aEventStatus);
6917     }
6918 
6919     return NS_OK;
6920   }
6921 
6922   if (aGUIEvent->IsTargetedAtFocusedContent()) {
6923     return HandleEventAtFocusedContent(aGUIEvent, aEventStatus);
6924   }
6925 
6926   return HandleEventWithFrameForPresShell(aFrameForPresShell, aGUIEvent,
6927                                           aEventStatus);
6928 }
6929 
HandleEventUsingCoordinates(nsIFrame * aFrameForPresShell,WidgetGUIEvent * aGUIEvent,nsEventStatus * aEventStatus,bool aDontRetargetEvents)6930 nsresult PresShell::EventHandler::HandleEventUsingCoordinates(
6931     nsIFrame* aFrameForPresShell, WidgetGUIEvent* aGUIEvent,
6932     nsEventStatus* aEventStatus, bool aDontRetargetEvents) {
6933   MOZ_ASSERT(aGUIEvent);
6934   MOZ_ASSERT(aGUIEvent->IsUsingCoordinates());
6935   MOZ_ASSERT(aEventStatus);
6936 
6937   // Flush pending notifications to handle the event with the latest layout.
6938   // But if it causes destroying the frame for mPresShell, stop handling the
6939   // event. (why?)
6940   AutoWeakFrame weakFrame(aFrameForPresShell);
6941   MaybeFlushPendingNotifications(aGUIEvent);
6942   if (!weakFrame.IsAlive()) {
6943     *aEventStatus = nsEventStatus_eIgnore;
6944     return NS_OK;
6945   }
6946 
6947   // XXX Retrieving capturing content here.  However, some of the following
6948   //     methods allow to run script.  So, isn't it possible the capturing
6949   //     content outdated?
6950   nsCOMPtr<nsIContent> capturingContent =
6951       EventHandler::GetCapturingContentFor(aGUIEvent);
6952 
6953   if (GetDocument() && aGUIEvent->mClass == eTouchEventClass) {
6954     PointerLockManager::Unlock();
6955   }
6956 
6957   nsIFrame* frameForPresShell = MaybeFlushThrottledStyles(aFrameForPresShell);
6958   if (NS_WARN_IF(!frameForPresShell)) {
6959     return NS_OK;
6960   }
6961 
6962   bool isCapturingContentIgnored = false;
6963   bool isCaptureRetargeted = false;
6964   nsIFrame* rootFrameToHandleEvent = ComputeRootFrameToHandleEvent(
6965       frameForPresShell, aGUIEvent, capturingContent,
6966       &isCapturingContentIgnored, &isCaptureRetargeted);
6967   if (isCapturingContentIgnored) {
6968     capturingContent = nullptr;
6969   }
6970 
6971   // The order to generate pointer event is
6972   // 1. check pending pointer capture.
6973   // 2. check if there is a capturing content.
6974   // 3. hit test
6975   // 4. dispatch pointer events
6976   // 5. check whether the targets of all Touch instances are in the same
6977   //    document and suppress invalid instances.
6978   // 6. dispatch mouse or touch events.
6979 
6980   // Try to keep frame for following check, because frame can be damaged
6981   // during MaybeProcessPointerCapture.
6982   {
6983     AutoWeakFrame frameKeeper(rootFrameToHandleEvent);
6984     PointerEventHandler::MaybeProcessPointerCapture(aGUIEvent);
6985     // Prevent application crashes, in case damaged frame.
6986     if (!frameKeeper.IsAlive()) {
6987       NS_WARNING("Nothing to handle this event!");
6988       return NS_OK;
6989     }
6990   }
6991 
6992   // Only capture mouse events and pointer events.
6993   RefPtr<Element> pointerCapturingElement =
6994       PointerEventHandler::GetPointerCapturingElement(aGUIEvent);
6995 
6996   if (pointerCapturingElement) {
6997     rootFrameToHandleEvent = pointerCapturingElement->GetPrimaryFrame();
6998     if (!rootFrameToHandleEvent) {
6999       return HandleEventWithPointerCapturingContentWithoutItsFrame(
7000           aFrameForPresShell, aGUIEvent, pointerCapturingElement, aEventStatus);
7001     }
7002   }
7003 
7004   WidgetMouseEvent* mouseEvent = aGUIEvent->AsMouseEvent();
7005   bool isWindowLevelMouseExit =
7006       (aGUIEvent->mMessage == eMouseExitFromWidget) &&
7007       (mouseEvent &&
7008        (mouseEvent->mExitFrom.value() == WidgetMouseEvent::ePlatformTopLevel ||
7009         mouseEvent->mExitFrom.value() == WidgetMouseEvent::ePuppet));
7010 
7011   // Get the frame at the event point. However, don't do this if we're
7012   // capturing and retargeting the event because the captured frame will
7013   // be used instead below. Also keep using the root frame if we're dealing
7014   // with a window-level mouse exit event since we want to start sending
7015   // mouse out events at the root EventStateManager.
7016   EventTargetData eventTargetData(rootFrameToHandleEvent);
7017   if (!isCaptureRetargeted && !isWindowLevelMouseExit &&
7018       !pointerCapturingElement) {
7019     if (!ComputeEventTargetFrameAndPresShellAtEventPoint(
7020             rootFrameToHandleEvent, aGUIEvent, &eventTargetData)) {
7021       *aEventStatus = nsEventStatus_eIgnore;
7022       return NS_OK;
7023     }
7024   }
7025 
7026   // if a node is capturing the mouse, check if the event needs to be
7027   // retargeted at the capturing content instead. This will be the case when
7028   // capture retargeting is being used, no frame was found or the frame's
7029   // content is not a descendant of the capturing content.
7030   if (capturingContent && !pointerCapturingElement &&
7031       (PresShell::sCapturingContentInfo.mRetargetToElement ||
7032        !eventTargetData.mFrame->GetContent() ||
7033        !nsContentUtils::ContentIsCrossDocDescendantOf(
7034            eventTargetData.mFrame->GetContent(), capturingContent))) {
7035     // A check was already done above to ensure that capturingContent is
7036     // in this presshell.
7037     NS_ASSERTION(capturingContent->OwnerDoc() == GetDocument(),
7038                  "Unexpected document");
7039     nsIFrame* capturingFrame = capturingContent->GetPrimaryFrame();
7040     if (capturingFrame) {
7041       eventTargetData.SetFrameAndComputePresShell(capturingFrame);
7042     }
7043   }
7044 
7045   if (NS_WARN_IF(!eventTargetData.mFrame)) {
7046     return NS_OK;
7047   }
7048 
7049   // Suppress mouse event if it's being targeted at an element inside
7050   // a document which needs events suppressed
7051   if (MaybeDiscardOrDelayMouseEvent(eventTargetData.mFrame, aGUIEvent)) {
7052     return NS_OK;
7053   }
7054 
7055   // Check if we have an active EventStateManager which isn't the
7056   // EventStateManager of the current PresContext.  If that is the case, and
7057   // mouse is over some ancestor document, forward event handling to the
7058   // active document.  This way content can get mouse events even when mouse
7059   // is over the chrome or outside the window.
7060   if (eventTargetData.MaybeRetargetToActiveDocument(aGUIEvent) &&
7061       NS_WARN_IF(!eventTargetData.mFrame)) {
7062     return NS_OK;
7063   }
7064 
7065   if (!eventTargetData.ComputeElementFromFrame(aGUIEvent)) {
7066     return NS_OK;
7067   }
7068   // Note that even if ComputeElementFromFrame() returns true,
7069   // eventTargetData.mContent can be nullptr here.
7070 
7071   // Dispatch a pointer event if Pointer Events is enabled.  Note that if
7072   // pointer event listeners change the layout, eventTargetData is
7073   // automatically updated.
7074   if (!DispatchPrecedingPointerEvent(
7075           aFrameForPresShell, aGUIEvent, pointerCapturingElement,
7076           aDontRetargetEvents, &eventTargetData, aEventStatus)) {
7077     return NS_OK;
7078   }
7079 
7080   // frame could be null after dispatching pointer events.
7081   // XXX Despite of this comment, we update the event target data outside
7082   //     DispatchPrecedingPointerEvent().  Can we make it call
7083   //     UpdateTouchEventTarget()?
7084   eventTargetData.UpdateTouchEventTarget(aGUIEvent);
7085 
7086   // Handle the event in the correct shell.
7087   // We pass the subshell's root frame as the frame to start from. This is
7088   // the only correct alternative; if the event was captured then it
7089   // must have been captured by us or some ancestor shell and we
7090   // now ask the subshell to dispatch it normally.
7091   EventHandler eventHandler(*eventTargetData.mPresShell);
7092   AutoCurrentEventInfoSetter eventInfoSetter(eventHandler, eventTargetData);
7093   // eventTargetData is on the stack and is guaranteed to keep its
7094   // mOverrideClickTarget alive, so we can just use MOZ_KnownLive here.
7095   nsresult rv = eventHandler.HandleEventWithCurrentEventInfo(
7096       aGUIEvent, aEventStatus, true,
7097       MOZ_KnownLive(eventTargetData.mOverrideClickTarget));
7098 #ifdef DEBUG
7099   eventTargetData.mPresShell->ShowEventTargetDebug();
7100 #endif
7101   return rv;
7102 }
7103 
MaybeFlushPendingNotifications(WidgetGUIEvent * aGUIEvent)7104 bool PresShell::EventHandler::MaybeFlushPendingNotifications(
7105     WidgetGUIEvent* aGUIEvent) {
7106   MOZ_ASSERT(aGUIEvent);
7107 
7108   switch (aGUIEvent->mMessage) {
7109     case eMouseDown:
7110     case eMouseUp: {
7111       RefPtr<nsPresContext> presContext = mPresShell->GetPresContext();
7112       if (NS_WARN_IF(!presContext)) {
7113         return false;
7114       }
7115       uint64_t framesConstructedCount = presContext->FramesConstructedCount();
7116       uint64_t framesReflowedCount = presContext->FramesReflowedCount();
7117 
7118       MOZ_KnownLive(mPresShell)->FlushPendingNotifications(FlushType::Layout);
7119       return framesConstructedCount != presContext->FramesConstructedCount() ||
7120              framesReflowedCount != presContext->FramesReflowedCount();
7121     }
7122     default:
7123       return false;
7124   }
7125 }
7126 
7127 // The type of coordinates to use for hit-testing input events
7128 // that are relative to the RCD's viewport frame.
7129 // On most platforms, use visual coordinates so that scrollbars
7130 // can be targeted.
7131 // On mobile, use layout coordinates because hit-testing in
7132 // visual coordinates clashes with mobile viewport sizing, where
7133 // the ViewportFrame is sized to the initial containing block
7134 // (ICB) size, which is in layout coordinates. This is fine
7135 // because we don't need to be able to target scrollbars on mobile
7136 // (scrollbar dragging isn't supported).
ViewportTypeForInputEventsRelativeToRoot()7137 static ViewportType ViewportTypeForInputEventsRelativeToRoot() {
7138 #ifdef MOZ_WIDGET_ANDROID
7139   return ViewportType::Layout;
7140 #else
7141   return ViewportType::Visual;
7142 #endif
7143 }
7144 
GetFrameToHandleNonTouchEvent(nsIFrame * aRootFrameToHandleEvent,WidgetGUIEvent * aGUIEvent)7145 nsIFrame* PresShell::EventHandler::GetFrameToHandleNonTouchEvent(
7146     nsIFrame* aRootFrameToHandleEvent, WidgetGUIEvent* aGUIEvent) {
7147   MOZ_ASSERT(aGUIEvent);
7148   MOZ_ASSERT(aGUIEvent->mClass != eTouchEventClass);
7149 
7150   ViewportType viewportType = ViewportType::Layout;
7151   if (aRootFrameToHandleEvent->Type() == LayoutFrameType::Viewport) {
7152     nsPresContext* pc = aRootFrameToHandleEvent->PresContext();
7153     if (pc->IsChrome()) {
7154       viewportType = ViewportType::Visual;
7155     } else if (pc->IsRootContentDocument()) {
7156       viewportType = ViewportTypeForInputEventsRelativeToRoot();
7157     }
7158   }
7159   RelativeTo relativeTo{aRootFrameToHandleEvent, viewportType};
7160   nsPoint eventPoint =
7161       nsLayoutUtils::GetEventCoordinatesRelativeTo(aGUIEvent, relativeTo);
7162 
7163   uint32_t flags = 0;
7164   if (aGUIEvent->mClass == eMouseEventClass) {
7165     WidgetMouseEvent* mouseEvent = aGUIEvent->AsMouseEvent();
7166     if (mouseEvent && mouseEvent->mIgnoreRootScrollFrame) {
7167       flags |= INPUT_IGNORE_ROOT_SCROLL_FRAME;
7168     }
7169   }
7170 
7171   nsIFrame* targetFrame =
7172       FindFrameTargetedByInputEvent(aGUIEvent, relativeTo, eventPoint, flags);
7173   if (!targetFrame) {
7174     return aRootFrameToHandleEvent;
7175   }
7176 
7177   if (targetFrame->PresShell() == mPresShell) {
7178     // If found target is in mPresShell, we've already found it in the latest
7179     // layout so that we can use it.
7180     return targetFrame;
7181   }
7182 
7183   // If target is in a child document, we've not flushed its layout yet.
7184   PresShell* childPresShell = targetFrame->PresShell();
7185   EventHandler childEventHandler(*childPresShell);
7186   AutoWeakFrame weakFrame(aRootFrameToHandleEvent);
7187   bool layoutChanged =
7188       childEventHandler.MaybeFlushPendingNotifications(aGUIEvent);
7189   if (!weakFrame.IsAlive()) {
7190     // Stop handling the event if the root frame to handle event is destroyed
7191     // by the reflow. (but why?)
7192     return nullptr;
7193   }
7194   if (!layoutChanged) {
7195     // If the layout in the child PresShell hasn't been changed, we don't
7196     // need to recompute the target.
7197     return targetFrame;
7198   }
7199 
7200   // Finally, we need to recompute the target with the latest layout.
7201   targetFrame =
7202       FindFrameTargetedByInputEvent(aGUIEvent, relativeTo, eventPoint, flags);
7203 
7204   return targetFrame ? targetFrame : aRootFrameToHandleEvent;
7205 }
7206 
ComputeEventTargetFrameAndPresShellAtEventPoint(nsIFrame * aRootFrameToHandleEvent,WidgetGUIEvent * aGUIEvent,EventTargetData * aEventTargetData)7207 bool PresShell::EventHandler::ComputeEventTargetFrameAndPresShellAtEventPoint(
7208     nsIFrame* aRootFrameToHandleEvent, WidgetGUIEvent* aGUIEvent,
7209     EventTargetData* aEventTargetData) {
7210   MOZ_ASSERT(aRootFrameToHandleEvent);
7211   MOZ_ASSERT(aGUIEvent);
7212   MOZ_ASSERT(aEventTargetData);
7213 
7214   if (aGUIEvent->mClass == eTouchEventClass) {
7215     nsIFrame* targetFrameAtTouchEvent = TouchManager::SetupTarget(
7216         aGUIEvent->AsTouchEvent(), aRootFrameToHandleEvent);
7217     aEventTargetData->SetFrameAndComputePresShell(targetFrameAtTouchEvent);
7218     return true;
7219   }
7220 
7221   nsIFrame* targetFrame =
7222       GetFrameToHandleNonTouchEvent(aRootFrameToHandleEvent, aGUIEvent);
7223   aEventTargetData->SetFrameAndComputePresShell(targetFrame);
7224   return !!aEventTargetData->mFrame;
7225 }
7226 
DispatchPrecedingPointerEvent(nsIFrame * aFrameForPresShell,WidgetGUIEvent * aGUIEvent,nsIContent * aPointerCapturingContent,bool aDontRetargetEvents,EventTargetData * aEventTargetData,nsEventStatus * aEventStatus)7227 bool PresShell::EventHandler::DispatchPrecedingPointerEvent(
7228     nsIFrame* aFrameForPresShell, WidgetGUIEvent* aGUIEvent,
7229     nsIContent* aPointerCapturingContent, bool aDontRetargetEvents,
7230     EventTargetData* aEventTargetData, nsEventStatus* aEventStatus) {
7231   MOZ_ASSERT(aFrameForPresShell);
7232   MOZ_ASSERT(aGUIEvent);
7233   MOZ_ASSERT(aEventTargetData);
7234   MOZ_ASSERT(aEventStatus);
7235 
7236   // Dispatch pointer events from the mouse or touch events. Regarding
7237   // pointer events from mouse, we should dispatch those pointer events to
7238   // the same target as the source mouse events. We pass the frame found
7239   // in hit test to PointerEventHandler and dispatch pointer events to it.
7240   //
7241   // Regarding pointer events from touch, the behavior is different. Touch
7242   // events are dispatched to the same target as the target of touchstart.
7243   // Multiple touch points must be dispatched to the same document. Pointer
7244   // events from touch can be dispatched to different documents. We Pass the
7245   // original frame to PointerEventHandler, reentry PresShell::HandleEvent,
7246   // and do hit test for each point.
7247   nsIFrame* targetFrame = aGUIEvent->mClass == eTouchEventClass
7248                               ? aFrameForPresShell
7249                               : aEventTargetData->mFrame;
7250 
7251   if (aPointerCapturingContent) {
7252     aEventTargetData->mOverrideClickTarget =
7253         GetOverrideClickTarget(aGUIEvent, aFrameForPresShell);
7254     aEventTargetData->mPresShell =
7255         PresShell::GetShellForEventTarget(nullptr, aPointerCapturingContent);
7256     if (!aEventTargetData->mPresShell) {
7257       // If we can't process event for the capturing content, release
7258       // the capture.
7259       PointerEventHandler::ReleaseIfCaptureByDescendant(
7260           aPointerCapturingContent);
7261       return false;
7262     }
7263 
7264     targetFrame = aPointerCapturingContent->GetPrimaryFrame();
7265     aEventTargetData->mFrame = targetFrame;
7266   }
7267 
7268   AutoWeakFrame weakTargetFrame(targetFrame);
7269   AutoWeakFrame weakFrame(aEventTargetData->mFrame);
7270   nsCOMPtr<nsIContent> content(aEventTargetData->mContent);
7271   RefPtr<PresShell> presShell(aEventTargetData->mPresShell);
7272   nsCOMPtr<nsIContent> targetContent;
7273   PointerEventHandler::DispatchPointerFromMouseOrTouch(
7274       presShell, aEventTargetData->mFrame, content, aGUIEvent,
7275       aDontRetargetEvents, aEventStatus, getter_AddRefs(targetContent));
7276 
7277   // If the target frame is alive, the caller should keep handling the event
7278   // unless event target frame is destroyed.
7279   if (weakTargetFrame.IsAlive()) {
7280     return weakFrame.IsAlive();
7281   }
7282 
7283   // If the event is not a mouse event, the caller should keep handling the
7284   // event unless event target frame is destroyed.  Note that this case is
7285   // not defined by the spec.
7286   if (aGUIEvent->mClass != eMouseEventClass) {
7287     return weakFrame.IsAlive();
7288   }
7289 
7290   // Spec defines that mouse events must be dispatched to the same target as
7291   // the pointer event. If the target is no longer participating in its
7292   // ownerDocument's tree, fire the event at the original target's nearest
7293   // ancestor node
7294   if (!targetContent) {
7295     return false;
7296   }
7297 
7298   // XXX Why don't we reset aEventTargetData->mContent here?
7299   aEventTargetData->mFrame = targetContent->GetPrimaryFrame();
7300   aEventTargetData->mPresShell = PresShell::GetShellForEventTarget(
7301       aEventTargetData->mFrame, targetContent);
7302 
7303   // If new target PresShel is not found, we cannot keep handling the event.
7304   return !!aEventTargetData->mPresShell;
7305 }
7306 
MaybeHandleEventWithAccessibleCaret(nsIFrame * aFrameForPresShell,WidgetGUIEvent * aGUIEvent,nsEventStatus * aEventStatus)7307 bool PresShell::EventHandler::MaybeHandleEventWithAccessibleCaret(
7308     nsIFrame* aFrameForPresShell, WidgetGUIEvent* aGUIEvent,
7309     nsEventStatus* aEventStatus) {
7310   MOZ_ASSERT(aGUIEvent);
7311   MOZ_ASSERT(aEventStatus);
7312 
7313   // Don't dispatch event to AccessibleCaretEventHub when the event status
7314   // is nsEventStatus_eConsumeNoDefault. This might be happened when content
7315   // preventDefault on the pointer events. In such case, we also call
7316   // preventDefault on mouse events to stop default behaviors.
7317   if (*aEventStatus == nsEventStatus_eConsumeNoDefault) {
7318     return false;
7319   }
7320 
7321   if (!AccessibleCaretEnabled(GetDocument()->GetDocShell())) {
7322     return false;
7323   }
7324 
7325   // AccessibleCaretEventHub handles only mouse, touch, and keyboard events.
7326   if (aGUIEvent->mClass != eMouseEventClass &&
7327       aGUIEvent->mClass != eTouchEventClass &&
7328       aGUIEvent->mClass != eKeyboardEventClass) {
7329     return false;
7330   }
7331 
7332   // First, try the event hub at the event point to handle a long press to
7333   // select a word in an unfocused window.
7334   do {
7335     EventTargetData eventTargetData(nullptr);
7336     if (!ComputeEventTargetFrameAndPresShellAtEventPoint(
7337             aFrameForPresShell, aGUIEvent, &eventTargetData)) {
7338       break;
7339     }
7340 
7341     if (!eventTargetData.mPresShell) {
7342       break;
7343     }
7344 
7345     RefPtr<AccessibleCaretEventHub> eventHub =
7346         eventTargetData.mPresShell->GetAccessibleCaretEventHub();
7347     if (!eventHub) {
7348       break;
7349     }
7350 
7351     *aEventStatus = eventHub->HandleEvent(aGUIEvent);
7352     if (*aEventStatus != nsEventStatus_eConsumeNoDefault) {
7353       break;
7354     }
7355 
7356     // If the event is consumed, cancel APZC panning by setting
7357     // mMultipleActionsPrevented.
7358     aGUIEvent->mFlags.mMultipleActionsPrevented = true;
7359     return true;
7360   } while (false);
7361 
7362   // Then, we target the event to the event hub at the focused window.
7363   nsCOMPtr<nsPIDOMWindowOuter> window = GetFocusedDOMWindowInOurWindow();
7364   if (!window) {
7365     return false;
7366   }
7367   RefPtr<Document> retargetEventDoc = window->GetExtantDoc();
7368   if (!retargetEventDoc) {
7369     return false;
7370   }
7371   RefPtr<PresShell> presShell = retargetEventDoc->GetPresShell();
7372   if (!presShell) {
7373     return false;
7374   }
7375 
7376   RefPtr<AccessibleCaretEventHub> eventHub =
7377       presShell->GetAccessibleCaretEventHub();
7378   if (!eventHub) {
7379     return false;
7380   }
7381   *aEventStatus = eventHub->HandleEvent(aGUIEvent);
7382   if (*aEventStatus != nsEventStatus_eConsumeNoDefault) {
7383     return false;
7384   }
7385   // If the event is consumed, cancel APZC panning by setting
7386   // mMultipleActionsPrevented.
7387   aGUIEvent->mFlags.mMultipleActionsPrevented = true;
7388   return true;
7389 }
7390 
MaybeDiscardEvent(WidgetGUIEvent * aGUIEvent)7391 bool PresShell::EventHandler::MaybeDiscardEvent(WidgetGUIEvent* aGUIEvent) {
7392   MOZ_ASSERT(aGUIEvent);
7393 
7394   // If it is safe to dispatch events now, don't discard the event.
7395   if (nsContentUtils::IsSafeToRunScript()) {
7396     return false;
7397   }
7398 
7399   // If the event does not cause dispatching DOM event (i.e., internal event),
7400   // we can keep handling it even when it's not safe to run script.
7401   if (!aGUIEvent->IsAllowedToDispatchDOMEvent()) {
7402     return false;
7403   }
7404 
7405   // If the event is a composition event, we need to let IMEStateManager know
7406   // it's discarded because it needs to listen all composition events to manage
7407   // TextComposition instance.
7408   if (aGUIEvent->mClass == eCompositionEventClass) {
7409     IMEStateManager::OnCompositionEventDiscarded(
7410         aGUIEvent->AsCompositionEvent());
7411   }
7412 
7413 #ifdef DEBUG
7414   if (aGUIEvent->IsIMERelatedEvent()) {
7415     nsPrintfCString warning("%s event is discarded",
7416                             ToChar(aGUIEvent->mMessage));
7417     NS_WARNING(warning.get());
7418   }
7419 #endif  // #ifdef DEBUG
7420 
7421   nsContentUtils::WarnScriptWasIgnored(GetDocument());
7422   return true;
7423 }
7424 
7425 // static
GetCapturingContentFor(WidgetGUIEvent * aGUIEvent)7426 nsIContent* PresShell::EventHandler::GetCapturingContentFor(
7427     WidgetGUIEvent* aGUIEvent) {
7428   return (aGUIEvent->mClass == ePointerEventClass ||
7429           aGUIEvent->mClass == eWheelEventClass ||
7430           aGUIEvent->HasMouseEventMessage())
7431              ? PresShell::GetCapturingContent()
7432              : nullptr;
7433 }
7434 
GetRetargetEventDocument(WidgetGUIEvent * aGUIEvent,Document ** aRetargetEventDocument)7435 bool PresShell::EventHandler::GetRetargetEventDocument(
7436     WidgetGUIEvent* aGUIEvent, Document** aRetargetEventDocument) {
7437   MOZ_ASSERT(aGUIEvent);
7438   MOZ_ASSERT(aRetargetEventDocument);
7439 
7440   *aRetargetEventDocument = nullptr;
7441 
7442   // key and IME related events should not cross top level window boundary.
7443   // Basically, such input events should be fired only on focused widget.
7444   // However, some IMEs might need to clean up composition after focused
7445   // window is deactivated.  And also some tests on MozMill want to test key
7446   // handling on deactivated window because MozMill window can be activated
7447   // during tests.  So, there is no merit the events should be redirected to
7448   // active window.  So, the events should be handled on the last focused
7449   // content in the last focused DOM window in same top level window.
7450   // Note, if no DOM window has been focused yet, we can discard the events.
7451   if (aGUIEvent->IsTargetedAtFocusedWindow()) {
7452     nsCOMPtr<nsPIDOMWindowOuter> window = GetFocusedDOMWindowInOurWindow();
7453     // No DOM window in same top level window has not been focused yet,
7454     // discard the events.
7455     if (!window) {
7456       return false;
7457     }
7458 
7459     RefPtr<Document> retargetEventDoc = window->GetExtantDoc();
7460     if (!retargetEventDoc) {
7461       return false;
7462     }
7463     retargetEventDoc.forget(aRetargetEventDocument);
7464     return true;
7465   }
7466 
7467   nsIContent* capturingContent =
7468       EventHandler::GetCapturingContentFor(aGUIEvent);
7469   if (capturingContent) {
7470     // if the mouse is being captured then retarget the mouse event at the
7471     // document that is being captured.
7472     RefPtr<Document> retargetEventDoc = capturingContent->GetComposedDoc();
7473     retargetEventDoc.forget(aRetargetEventDocument);
7474     return true;
7475   }
7476 
7477 #ifdef ANDROID
7478   if (aGUIEvent->mClass == eTouchEventClass ||
7479       aGUIEvent->mClass == eMouseEventClass ||
7480       aGUIEvent->mClass == eWheelEventClass) {
7481     RefPtr<Document> retargetEventDoc = mPresShell->GetPrimaryContentDocument();
7482     retargetEventDoc.forget(aRetargetEventDocument);
7483     return true;
7484   }
7485 #endif  // #ifdef ANDROID
7486 
7487   // When we don't find another document to handle the event, we need to keep
7488   // handling the event by ourselves.
7489   return true;
7490 }
7491 
GetFrameForHandlingEventWith(WidgetGUIEvent * aGUIEvent,Document * aRetargetDocument,nsIFrame * aFrameForPresShell)7492 nsIFrame* PresShell::EventHandler::GetFrameForHandlingEventWith(
7493     WidgetGUIEvent* aGUIEvent, Document* aRetargetDocument,
7494     nsIFrame* aFrameForPresShell) {
7495   MOZ_ASSERT(aGUIEvent);
7496   MOZ_ASSERT(aRetargetDocument);
7497 
7498   RefPtr<PresShell> retargetPresShell = aRetargetDocument->GetPresShell();
7499   // Even if the document doesn't have PresShell, i.e., it's invisible, we
7500   // need to dispatch only KeyboardEvent in its nearest visible document
7501   // because key focus shouldn't be caught by invisible document.
7502   if (!retargetPresShell) {
7503     if (!aGUIEvent->HasKeyEventMessage()) {
7504       return nullptr;
7505     }
7506     Document* retargetEventDoc = aRetargetDocument;
7507     while (!retargetPresShell) {
7508       retargetEventDoc = retargetEventDoc->GetInProcessParentDocument();
7509       if (!retargetEventDoc) {
7510         return nullptr;
7511       }
7512       retargetPresShell = retargetEventDoc->GetPresShell();
7513     }
7514   }
7515 
7516   // If the found PresShell is this instance, caller needs to keep handling
7517   // aGUIEvent by itself.  Therefore, return the given frame which was set
7518   // to aFrame of HandleEvent().
7519   if (retargetPresShell == mPresShell) {
7520     return aFrameForPresShell;
7521   }
7522 
7523   // Use root frame of the new PresShell if there is.
7524   nsIFrame* rootFrame = retargetPresShell->GetRootFrame();
7525   if (rootFrame) {
7526     return rootFrame;
7527   }
7528 
7529   // Otherwise, and if aGUIEvent requires content of PresShell, caller should
7530   // stop handling the event.
7531   if (aGUIEvent->mMessage == eQueryTextContent ||
7532       aGUIEvent->IsContentCommandEvent()) {
7533     return nullptr;
7534   }
7535 
7536   // Otherwise, use nearest ancestor frame which includes the PresShell.
7537   return GetNearestFrameContainingPresShell(retargetPresShell);
7538 }
7539 
MaybeHandleEventWithAnotherPresShell(nsIFrame * aFrameForPresShell,WidgetGUIEvent * aGUIEvent,nsEventStatus * aEventStatus,nsresult * aRv)7540 bool PresShell::EventHandler::MaybeHandleEventWithAnotherPresShell(
7541     nsIFrame* aFrameForPresShell, WidgetGUIEvent* aGUIEvent,
7542     nsEventStatus* aEventStatus, nsresult* aRv) {
7543   MOZ_ASSERT(aGUIEvent);
7544   MOZ_ASSERT(aEventStatus);
7545   MOZ_ASSERT(aRv);
7546 
7547   *aRv = NS_OK;
7548 
7549   RefPtr<Document> retargetEventDoc;
7550   if (!GetRetargetEventDocument(aGUIEvent, getter_AddRefs(retargetEventDoc))) {
7551     // Nobody can handle this event.  So, treat as handled by somebody to make
7552     // caller do nothing anymore.
7553     return true;
7554   }
7555 
7556   // If there is no proper retarget document, the caller should handle the
7557   // event by itself.
7558   if (!retargetEventDoc) {
7559     return false;
7560   }
7561 
7562   nsIFrame* frame = GetFrameForHandlingEventWith(aGUIEvent, retargetEventDoc,
7563                                                  aFrameForPresShell);
7564   if (!frame) {
7565     // Nobody can handle this event.  So, treat as handled by somebody to make
7566     // caller do nothing anymore.
7567     return true;
7568   }
7569 
7570   // If we reached same frame as set to HandleEvent(), the caller should handle
7571   // the event by itself.
7572   if (frame == aFrameForPresShell) {
7573     return false;
7574   }
7575 
7576   // We need to handle aGUIEvent with another PresShell.
7577   RefPtr<PresShell> presShell = frame->PresContext()->PresShell();
7578   *aRv = presShell->HandleEvent(frame, aGUIEvent, true, aEventStatus);
7579   return true;
7580 }
7581 
MaybeDiscardOrDelayKeyboardEvent(WidgetGUIEvent * aGUIEvent)7582 bool PresShell::EventHandler::MaybeDiscardOrDelayKeyboardEvent(
7583     WidgetGUIEvent* aGUIEvent) {
7584   MOZ_ASSERT(aGUIEvent);
7585 
7586   if (aGUIEvent->mClass != eKeyboardEventClass) {
7587     return false;
7588   }
7589 
7590   Document* document = GetDocument();
7591   if (!document || !document->EventHandlingSuppressed()) {
7592     return false;
7593   }
7594 
7595   MOZ_ASSERT_IF(InputTaskManager::CanSuspendInputEvent(),
7596                 !InputTaskManager::Get()->IsSuspended());
7597 
7598   if (aGUIEvent->mMessage == eKeyDown) {
7599     mPresShell->mNoDelayedKeyEvents = true;
7600   } else if (!mPresShell->mNoDelayedKeyEvents) {
7601     UniquePtr<DelayedKeyEvent> delayedKeyEvent =
7602         MakeUnique<DelayedKeyEvent>(aGUIEvent->AsKeyboardEvent());
7603     mPresShell->mDelayedEvents.AppendElement(std::move(delayedKeyEvent));
7604   }
7605   aGUIEvent->mFlags.mIsSuppressedOrDelayed = true;
7606   return true;
7607 }
7608 
MaybeDiscardOrDelayMouseEvent(nsIFrame * aFrameToHandleEvent,WidgetGUIEvent * aGUIEvent)7609 bool PresShell::EventHandler::MaybeDiscardOrDelayMouseEvent(
7610     nsIFrame* aFrameToHandleEvent, WidgetGUIEvent* aGUIEvent) {
7611   MOZ_ASSERT(aFrameToHandleEvent);
7612   MOZ_ASSERT(aGUIEvent);
7613 
7614   if (aGUIEvent->mClass != eMouseEventClass) {
7615     return false;
7616   }
7617 
7618   if (!aFrameToHandleEvent->PresContext()
7619            ->Document()
7620            ->EventHandlingSuppressed()) {
7621     return false;
7622   }
7623 
7624   MOZ_ASSERT_IF(InputTaskManager::CanSuspendInputEvent() &&
7625                     aGUIEvent->mMessage != eMouseMove,
7626                 !InputTaskManager::Get()->IsSuspended());
7627 
7628   RefPtr<PresShell> ps = aFrameToHandleEvent->PresShell();
7629 
7630   if (aGUIEvent->mMessage == eMouseDown) {
7631     ps->mNoDelayedMouseEvents = true;
7632   } else if (!ps->mNoDelayedMouseEvents &&
7633              (aGUIEvent->mMessage == eMouseUp ||
7634               // contextmenu is triggered after right mouseup on Windows and
7635               // right mousedown on other platforms.
7636               aGUIEvent->mMessage == eContextMenu ||
7637               aGUIEvent->mMessage == eMouseExitFromWidget)) {
7638     UniquePtr<DelayedMouseEvent> delayedMouseEvent =
7639         MakeUnique<DelayedMouseEvent>(aGUIEvent->AsMouseEvent());
7640     ps->mDelayedEvents.AppendElement(std::move(delayedMouseEvent));
7641   }
7642 
7643   // If there is a suppressed event listener associated with the document,
7644   // notify it about the suppressed mouse event. This allows devtools
7645   // features to continue receiving mouse events even when the devtools
7646   // debugger has paused execution in a page.
7647   RefPtr<EventListener> suppressedListener = aFrameToHandleEvent->PresContext()
7648                                                  ->Document()
7649                                                  ->GetSuppressedEventListener();
7650   if (!suppressedListener ||
7651       aGUIEvent->AsMouseEvent()->mReason == WidgetMouseEvent::eSynthesized) {
7652     return true;
7653   }
7654 
7655   nsCOMPtr<nsIContent> targetContent;
7656   aFrameToHandleEvent->GetContentForEvent(aGUIEvent,
7657                                           getter_AddRefs(targetContent));
7658   if (targetContent) {
7659     aGUIEvent->mTarget = targetContent;
7660   }
7661 
7662   nsCOMPtr<EventTarget> eventTarget = aGUIEvent->mTarget;
7663   RefPtr<Event> event = EventDispatcher::CreateEvent(
7664       eventTarget, aFrameToHandleEvent->PresContext(), aGUIEvent, u""_ns);
7665 
7666   suppressedListener->HandleEvent(*event);
7667   return true;
7668 }
7669 
MaybeFlushThrottledStyles(nsIFrame * aFrameForPresShell)7670 nsIFrame* PresShell::EventHandler::MaybeFlushThrottledStyles(
7671     nsIFrame* aFrameForPresShell) {
7672   if (!GetDocument()) {
7673     // XXX Only when mPresShell has document, we'll try to look for a frame
7674     //     containing mPresShell even if given frame is nullptr.  Does this
7675     //     make sense?
7676     return aFrameForPresShell;
7677   }
7678 
7679   PresShell* rootPresShell = mPresShell->GetRootPresShell();
7680   if (NS_WARN_IF(!rootPresShell)) {
7681     return nullptr;
7682   }
7683   Document* rootDocument = rootPresShell->GetDocument();
7684   if (NS_WARN_IF(!rootDocument)) {
7685     return nullptr;
7686   }
7687 
7688   AutoWeakFrame weakFrameForPresShell(aFrameForPresShell);
7689   {  // scope for scriptBlocker.
7690     nsAutoScriptBlocker scriptBlocker;
7691     FlushThrottledStyles(*rootDocument);
7692   }
7693 
7694   if (weakFrameForPresShell.IsAlive()) {
7695     return aFrameForPresShell;
7696   }
7697 
7698   return GetNearestFrameContainingPresShell(mPresShell);
7699 }
7700 
ComputeRootFrameToHandleEvent(nsIFrame * aFrameForPresShell,WidgetGUIEvent * aGUIEvent,nsIContent * aCapturingContent,bool * aIsCapturingContentIgnored,bool * aIsCaptureRetargeted)7701 nsIFrame* PresShell::EventHandler::ComputeRootFrameToHandleEvent(
7702     nsIFrame* aFrameForPresShell, WidgetGUIEvent* aGUIEvent,
7703     nsIContent* aCapturingContent, bool* aIsCapturingContentIgnored,
7704     bool* aIsCaptureRetargeted) {
7705   MOZ_ASSERT(aFrameForPresShell);
7706   MOZ_ASSERT(aGUIEvent);
7707   MOZ_ASSERT(aIsCapturingContentIgnored);
7708   MOZ_ASSERT(aIsCaptureRetargeted);
7709 
7710   nsIFrame* rootFrameToHandleEvent = ComputeRootFrameToHandleEventWithPopup(
7711       aFrameForPresShell, aGUIEvent, aCapturingContent,
7712       aIsCapturingContentIgnored);
7713   if (*aIsCapturingContentIgnored) {
7714     // If the capturing content is ignored, we don't need to respect it.
7715     return rootFrameToHandleEvent;
7716   }
7717 
7718   if (!aCapturingContent) {
7719     return rootFrameToHandleEvent;
7720   }
7721 
7722   // If we have capturing content, let's compute root frame with it again.
7723   return ComputeRootFrameToHandleEventWithCapturingContent(
7724       rootFrameToHandleEvent, aCapturingContent, aIsCapturingContentIgnored,
7725       aIsCaptureRetargeted);
7726 }
7727 
ComputeRootFrameToHandleEventWithPopup(nsIFrame * aRootFrameToHandleEvent,WidgetGUIEvent * aGUIEvent,nsIContent * aCapturingContent,bool * aIsCapturingContentIgnored)7728 nsIFrame* PresShell::EventHandler::ComputeRootFrameToHandleEventWithPopup(
7729     nsIFrame* aRootFrameToHandleEvent, WidgetGUIEvent* aGUIEvent,
7730     nsIContent* aCapturingContent, bool* aIsCapturingContentIgnored) {
7731   MOZ_ASSERT(aRootFrameToHandleEvent);
7732   MOZ_ASSERT(aGUIEvent);
7733   MOZ_ASSERT(aIsCapturingContentIgnored);
7734 
7735   *aIsCapturingContentIgnored = false;
7736 
7737   nsPresContext* framePresContext = aRootFrameToHandleEvent->PresContext();
7738   nsPresContext* rootPresContext = framePresContext->GetRootPresContext();
7739   NS_ASSERTION(rootPresContext == GetPresContext()->GetRootPresContext(),
7740                "How did we end up outside the connected "
7741                "prescontext/viewmanager hierarchy?");
7742   nsIFrame* popupFrame = nsLayoutUtils::GetPopupFrameForEventCoordinates(
7743       rootPresContext, aGUIEvent);
7744   if (!popupFrame) {
7745     return aRootFrameToHandleEvent;
7746   }
7747 
7748   // If a remote browser is currently capturing input break out if we
7749   // detect a chrome generated popup.
7750   // XXXedgar, do we need to check fission OOP iframe?
7751   if (aCapturingContent &&
7752       EventStateManager::IsTopLevelRemoteTarget(aCapturingContent)) {
7753     *aIsCapturingContentIgnored = true;
7754   }
7755 
7756   // If the popupFrame is an ancestor of the 'frame', the frame should
7757   // handle the event, otherwise, the popup should handle it.
7758   if (nsContentUtils::ContentIsCrossDocDescendantOf(
7759           framePresContext->GetPresShell()->GetDocument(),
7760           popupFrame->GetContent())) {
7761     return aRootFrameToHandleEvent;
7762   }
7763 
7764   // If we aren't starting our event dispatch from the root frame of the
7765   // root prescontext, then someone must be capturing the mouse. In that
7766   // case we only want to use the popup list if the capture is
7767   // inside the popup.
7768   if (framePresContext == rootPresContext &&
7769       aRootFrameToHandleEvent == FrameConstructor()->GetRootFrame()) {
7770     return popupFrame;
7771   }
7772 
7773   if (aCapturingContent && !*aIsCapturingContentIgnored &&
7774       aCapturingContent->IsInclusiveDescendantOf(popupFrame->GetContent())) {
7775     return popupFrame;
7776   }
7777 
7778   return aRootFrameToHandleEvent;
7779 }
7780 
7781 nsIFrame*
ComputeRootFrameToHandleEventWithCapturingContent(nsIFrame * aRootFrameToHandleEvent,nsIContent * aCapturingContent,bool * aIsCapturingContentIgnored,bool * aIsCaptureRetargeted)7782 PresShell::EventHandler::ComputeRootFrameToHandleEventWithCapturingContent(
7783     nsIFrame* aRootFrameToHandleEvent, nsIContent* aCapturingContent,
7784     bool* aIsCapturingContentIgnored, bool* aIsCaptureRetargeted) {
7785   MOZ_ASSERT(aRootFrameToHandleEvent);
7786   MOZ_ASSERT(aCapturingContent);
7787   MOZ_ASSERT(aIsCapturingContentIgnored);
7788   MOZ_ASSERT(aIsCaptureRetargeted);
7789 
7790   *aIsCapturingContentIgnored = false;
7791   *aIsCaptureRetargeted = false;
7792 
7793   // If a capture is active, determine if the BrowsingContext is active. If
7794   // not, clear the capture and target the mouse event normally instead. This
7795   // would occur if the mouse button is held down while a tab change occurs.
7796   // If the BrowsingContext is active, look for a scrolling container.
7797   BrowsingContext* bc = GetPresContext()->Document()->GetBrowsingContext();
7798   if (!bc || !bc->IsActive()) {
7799     ClearMouseCapture();
7800     *aIsCapturingContentIgnored = true;
7801     return aRootFrameToHandleEvent;
7802   }
7803 
7804   if (PresShell::sCapturingContentInfo.mRetargetToElement) {
7805     *aIsCaptureRetargeted = true;
7806     return aRootFrameToHandleEvent;
7807   }
7808 
7809   // A check was already done above to ensure that aCapturingContent is
7810   // in this presshell.
7811   NS_ASSERTION(aCapturingContent->OwnerDoc() == GetDocument(),
7812                "Unexpected document");
7813   nsIFrame* captureFrame = aCapturingContent->GetPrimaryFrame();
7814   if (!captureFrame) {
7815     return aRootFrameToHandleEvent;
7816   }
7817 
7818   if (aCapturingContent->IsHTMLElement(nsGkAtoms::select)) {
7819     // a dropdown <select> has a child in its selectPopupList and we should
7820     // capture on that instead.
7821     nsIFrame* childFrame =
7822         captureFrame->GetChildList(nsIFrame::kSelectPopupList).FirstChild();
7823     if (childFrame) {
7824       captureFrame = childFrame;
7825     }
7826   }
7827 
7828   // scrollable frames should use the scrolling container as the root instead
7829   // of the document
7830   nsIScrollableFrame* scrollFrame = do_QueryFrame(captureFrame);
7831   return scrollFrame ? scrollFrame->GetScrolledFrame()
7832                      : aRootFrameToHandleEvent;
7833 }
7834 
7835 nsresult
HandleEventWithPointerCapturingContentWithoutItsFrame(nsIFrame * aFrameForPresShell,WidgetGUIEvent * aGUIEvent,nsIContent * aPointerCapturingContent,nsEventStatus * aEventStatus)7836 PresShell::EventHandler::HandleEventWithPointerCapturingContentWithoutItsFrame(
7837     nsIFrame* aFrameForPresShell, WidgetGUIEvent* aGUIEvent,
7838     nsIContent* aPointerCapturingContent, nsEventStatus* aEventStatus) {
7839   MOZ_ASSERT(aGUIEvent);
7840   MOZ_ASSERT(aPointerCapturingContent);
7841   MOZ_ASSERT(!aPointerCapturingContent->GetPrimaryFrame(),
7842              "Handle the event with frame rather than only with the content");
7843   MOZ_ASSERT(aEventStatus);
7844 
7845   RefPtr<PresShell> presShellForCapturingContent =
7846       PresShell::GetShellForEventTarget(nullptr, aPointerCapturingContent);
7847   if (!presShellForCapturingContent) {
7848     // If we can't process event for the capturing content, release
7849     // the capture.
7850     PointerEventHandler::ReleaseIfCaptureByDescendant(aPointerCapturingContent);
7851     return NS_OK;
7852   }
7853 
7854   nsCOMPtr<nsIContent> overrideClickTarget =
7855       GetOverrideClickTarget(aGUIEvent, aFrameForPresShell);
7856 
7857   // Dispatch events to the capturing content even it's frame is
7858   // destroyed.
7859   PointerEventHandler::DispatchPointerFromMouseOrTouch(
7860       presShellForCapturingContent, nullptr, aPointerCapturingContent,
7861       aGUIEvent, false, aEventStatus, nullptr);
7862 
7863   if (presShellForCapturingContent == mPresShell) {
7864     return HandleEventWithTarget(aGUIEvent, nullptr, aPointerCapturingContent,
7865                                  aEventStatus, true, nullptr,
7866                                  overrideClickTarget);
7867   }
7868 
7869   EventHandler eventHandlerForCapturingContent(
7870       std::move(presShellForCapturingContent));
7871   return eventHandlerForCapturingContent.HandleEventWithTarget(
7872       aGUIEvent, nullptr, aPointerCapturingContent, aEventStatus, true, nullptr,
7873       overrideClickTarget);
7874 }
7875 
HandleEventAtFocusedContent(WidgetGUIEvent * aGUIEvent,nsEventStatus * aEventStatus)7876 nsresult PresShell::EventHandler::HandleEventAtFocusedContent(
7877     WidgetGUIEvent* aGUIEvent, nsEventStatus* aEventStatus) {
7878   MOZ_ASSERT(aGUIEvent);
7879   MOZ_ASSERT(aGUIEvent->IsTargetedAtFocusedContent());
7880   MOZ_ASSERT(aEventStatus);
7881 
7882   AutoCurrentEventInfoSetter eventInfoSetter(*this);
7883 
7884   RefPtr<Element> eventTargetElement =
7885       ComputeFocusedEventTargetElement(aGUIEvent);
7886 
7887   mPresShell->mCurrentEventFrame = nullptr;
7888   if (eventTargetElement) {
7889     nsresult rv = NS_OK;
7890     if (MaybeHandleEventWithAnotherPresShell(eventTargetElement, aGUIEvent,
7891                                              aEventStatus, &rv)) {
7892       return rv;
7893     }
7894   }
7895 
7896   // If we cannot handle the event with mPresShell, let's try to handle it
7897   // with parent PresShell.
7898   mPresShell->mCurrentEventContent = eventTargetElement;
7899   if (!mPresShell->GetCurrentEventContent() ||
7900       !mPresShell->GetCurrentEventFrame() ||
7901       InZombieDocument(mPresShell->mCurrentEventContent)) {
7902     return RetargetEventToParent(aGUIEvent, aEventStatus);
7903   }
7904 
7905   nsresult rv =
7906       HandleEventWithCurrentEventInfo(aGUIEvent, aEventStatus, true, nullptr);
7907 
7908 #ifdef DEBUG
7909   mPresShell->ShowEventTargetDebug();
7910 #endif
7911 
7912   return rv;
7913 }
7914 
ComputeFocusedEventTargetElement(WidgetGUIEvent * aGUIEvent)7915 Element* PresShell::EventHandler::ComputeFocusedEventTargetElement(
7916     WidgetGUIEvent* aGUIEvent) {
7917   MOZ_ASSERT(aGUIEvent);
7918   MOZ_ASSERT(aGUIEvent->IsTargetedAtFocusedContent());
7919 
7920   // key and IME related events go to the focused frame in this DOM window.
7921   nsPIDOMWindowOuter* window = GetDocument()->GetWindow();
7922   nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
7923   Element* eventTargetElement = nsFocusManager::GetFocusedDescendant(
7924       window, nsFocusManager::eOnlyCurrentWindow,
7925       getter_AddRefs(focusedWindow));
7926 
7927   // otherwise, if there is no focused content or the focused content has
7928   // no frame, just use the root content. This ensures that key events
7929   // still get sent to the window properly if nothing is focused or if a
7930   // frame goes away while it is focused.
7931   if (!eventTargetElement || !eventTargetElement->GetPrimaryFrame()) {
7932     eventTargetElement = GetDocument()->GetUnfocusedKeyEventTarget();
7933   }
7934 
7935   switch (aGUIEvent->mMessage) {
7936     case eKeyDown:
7937       sLastKeyDownEventTargetElement = eventTargetElement;
7938       return eventTargetElement;
7939     case eKeyPress:
7940     case eKeyUp:
7941       if (!sLastKeyDownEventTargetElement) {
7942         return eventTargetElement;
7943       }
7944       // If a different element is now focused for the keypress/keyup event
7945       // than what was focused during the keydown event, check if the new
7946       // focused element is not in a chrome document any more, and if so,
7947       // retarget the event back at the keydown target. This prevents a
7948       // content area from grabbing the focus from chrome in-between key
7949       // events.
7950       if (eventTargetElement) {
7951         bool keyDownIsChrome = nsContentUtils::IsChromeDoc(
7952             sLastKeyDownEventTargetElement->GetComposedDoc());
7953         if (keyDownIsChrome != nsContentUtils::IsChromeDoc(
7954                                    eventTargetElement->GetComposedDoc()) ||
7955             (keyDownIsChrome && BrowserParent::GetFrom(eventTargetElement))) {
7956           eventTargetElement = sLastKeyDownEventTargetElement;
7957         }
7958       }
7959 
7960       if (aGUIEvent->mMessage == eKeyUp) {
7961         sLastKeyDownEventTargetElement = nullptr;
7962       }
7963       [[fallthrough]];
7964     default:
7965       return eventTargetElement;
7966   }
7967 }
7968 
MaybeHandleEventWithAnotherPresShell(Element * aEventTargetElement,WidgetGUIEvent * aGUIEvent,nsEventStatus * aEventStatus,nsresult * aRv)7969 bool PresShell::EventHandler::MaybeHandleEventWithAnotherPresShell(
7970     Element* aEventTargetElement, WidgetGUIEvent* aGUIEvent,
7971     nsEventStatus* aEventStatus, nsresult* aRv) {
7972   MOZ_ASSERT(aEventTargetElement);
7973   MOZ_ASSERT(aGUIEvent);
7974   MOZ_ASSERT(!aGUIEvent->IsUsingCoordinates());
7975   MOZ_ASSERT(aEventStatus);
7976   MOZ_ASSERT(aRv);
7977 
7978   Document* eventTargetDocument = aEventTargetElement->OwnerDoc();
7979   if (!eventTargetDocument || eventTargetDocument == GetDocument()) {
7980     *aRv = NS_OK;
7981     return false;
7982   }
7983 
7984   RefPtr<PresShell> eventTargetPresShell = eventTargetDocument->GetPresShell();
7985   if (!eventTargetPresShell) {
7986     *aRv = NS_OK;
7987     return true;  // No PresShell can handle the event.
7988   }
7989 
7990   EventHandler eventHandler(std::move(eventTargetPresShell));
7991   *aRv = eventHandler.HandleRetargetedEvent(aGUIEvent, aEventStatus,
7992                                             aEventTargetElement);
7993   return true;
7994 }
7995 
HandleEventWithFrameForPresShell(nsIFrame * aFrameForPresShell,WidgetGUIEvent * aGUIEvent,nsEventStatus * aEventStatus)7996 nsresult PresShell::EventHandler::HandleEventWithFrameForPresShell(
7997     nsIFrame* aFrameForPresShell, WidgetGUIEvent* aGUIEvent,
7998     nsEventStatus* aEventStatus) {
7999   MOZ_ASSERT(aGUIEvent);
8000   MOZ_ASSERT(!aGUIEvent->IsUsingCoordinates());
8001   MOZ_ASSERT(!aGUIEvent->IsTargetedAtFocusedContent());
8002   MOZ_ASSERT(aEventStatus);
8003 
8004   AutoCurrentEventInfoSetter eventInfoSetter(*this, aFrameForPresShell,
8005                                              nullptr);
8006 
8007   nsresult rv = NS_OK;
8008   if (mPresShell->GetCurrentEventFrame()) {
8009     rv =
8010         HandleEventWithCurrentEventInfo(aGUIEvent, aEventStatus, true, nullptr);
8011   }
8012 
8013 #ifdef DEBUG
8014   mPresShell->ShowEventTargetDebug();
8015 #endif
8016 
8017   return rv;
8018 }
8019 
GetPrimaryContentDocument()8020 Document* PresShell::GetPrimaryContentDocument() {
8021   nsPresContext* context = GetPresContext();
8022   if (!context || !context->IsRoot()) {
8023     return nullptr;
8024   }
8025 
8026   nsCOMPtr<nsIDocShellTreeItem> shellAsTreeItem = context->GetDocShell();
8027   if (!shellAsTreeItem) {
8028     return nullptr;
8029   }
8030 
8031   nsCOMPtr<nsIDocShellTreeOwner> owner;
8032   shellAsTreeItem->GetTreeOwner(getter_AddRefs(owner));
8033   if (!owner) {
8034     return nullptr;
8035   }
8036 
8037   // now get the primary content shell (active tab)
8038   nsCOMPtr<nsIDocShellTreeItem> item;
8039   owner->GetPrimaryContentShell(getter_AddRefs(item));
8040   nsCOMPtr<nsIDocShell> childDocShell = do_QueryInterface(item);
8041   if (!childDocShell) {
8042     return nullptr;
8043   }
8044 
8045   return childDocShell->GetExtantDocument();
8046 }
8047 
8048 #ifdef DEBUG
ShowEventTargetDebug()8049 void PresShell::ShowEventTargetDebug() {
8050   if (nsIFrame::GetShowEventTargetFrameBorder() && GetCurrentEventFrame()) {
8051     if (mDrawEventTargetFrame) {
8052       mDrawEventTargetFrame->InvalidateFrame();
8053     }
8054 
8055     mDrawEventTargetFrame = mCurrentEventFrame;
8056     mDrawEventTargetFrame->InvalidateFrame();
8057   }
8058 }
8059 #endif
8060 
HandleEventWithTarget(WidgetEvent * aEvent,nsIFrame * aNewEventFrame,nsIContent * aNewEventContent,nsEventStatus * aEventStatus,bool aIsHandlingNativeEvent,nsIContent ** aTargetContent,nsIContent * aOverrideClickTarget)8061 nsresult PresShell::EventHandler::HandleEventWithTarget(
8062     WidgetEvent* aEvent, nsIFrame* aNewEventFrame, nsIContent* aNewEventContent,
8063     nsEventStatus* aEventStatus, bool aIsHandlingNativeEvent,
8064     nsIContent** aTargetContent, nsIContent* aOverrideClickTarget) {
8065   MOZ_ASSERT(aEvent);
8066   MOZ_DIAGNOSTIC_ASSERT(aEvent->IsTrusted());
8067 
8068 #if DEBUG
8069   MOZ_ASSERT(!aNewEventFrame ||
8070                  aNewEventFrame->PresContext()->GetPresShell() == mPresShell,
8071              "wrong shell");
8072   if (aNewEventContent) {
8073     Document* doc = aNewEventContent->GetComposedDoc();
8074     NS_ASSERTION(doc, "event for content that isn't in a document");
8075     // NOTE: We don't require that the document still have a PresShell.
8076     // See bug 1375940.
8077   }
8078 #endif
8079   NS_ENSURE_STATE(!aNewEventContent ||
8080                   aNewEventContent->GetComposedDoc() == GetDocument());
8081   AutoPointerEventTargetUpdater updater(mPresShell, aEvent, aNewEventFrame,
8082                                         aTargetContent);
8083   AutoCurrentEventInfoSetter eventInfoSetter(*this, aNewEventFrame,
8084                                              aNewEventContent);
8085   nsresult rv = HandleEventWithCurrentEventInfo(aEvent, aEventStatus, false,
8086                                                 aOverrideClickTarget);
8087   return rv;
8088 }
8089 
8090 namespace {
8091 
8092 class MOZ_RAII AutoEventHandler final {
8093  public:
AutoEventHandler(WidgetEvent * aEvent,Document * aDocument)8094   AutoEventHandler(WidgetEvent* aEvent, Document* aDocument) : mEvent(aEvent) {
8095     MOZ_ASSERT(mEvent);
8096     MOZ_ASSERT(mEvent->IsTrusted());
8097 
8098     if (mEvent->mMessage == eMouseDown) {
8099       PresShell::ReleaseCapturingContent();
8100       PresShell::AllowMouseCapture(true);
8101     }
8102     if (NeedsToUpdateCurrentMouseBtnState()) {
8103       WidgetMouseEvent* mouseEvent = mEvent->AsMouseEvent();
8104       if (mouseEvent) {
8105         EventStateManager::sCurrentMouseBtn = mouseEvent->mButton;
8106       }
8107     }
8108   }
8109 
~AutoEventHandler()8110   ~AutoEventHandler() {
8111     if (mEvent->mMessage == eMouseDown) {
8112       PresShell::AllowMouseCapture(false);
8113     }
8114     if (NeedsToUpdateCurrentMouseBtnState()) {
8115       EventStateManager::sCurrentMouseBtn = MouseButton::eNotPressed;
8116     }
8117   }
8118 
8119  protected:
NeedsToUpdateCurrentMouseBtnState() const8120   bool NeedsToUpdateCurrentMouseBtnState() const {
8121     return mEvent->mMessage == eMouseDown || mEvent->mMessage == eMouseUp ||
8122            mEvent->mMessage == ePointerDown || mEvent->mMessage == ePointerUp;
8123   }
8124 
8125   WidgetEvent* mEvent;
8126 };
8127 
8128 }  // anonymous namespace
8129 
HandleEventWithCurrentEventInfo(WidgetEvent * aEvent,nsEventStatus * aEventStatus,bool aIsHandlingNativeEvent,nsIContent * aOverrideClickTarget)8130 nsresult PresShell::EventHandler::HandleEventWithCurrentEventInfo(
8131     WidgetEvent* aEvent, nsEventStatus* aEventStatus,
8132     bool aIsHandlingNativeEvent, nsIContent* aOverrideClickTarget) {
8133   MOZ_ASSERT(aEvent);
8134   MOZ_ASSERT(aEventStatus);
8135 
8136   RefPtr<EventStateManager> manager = GetPresContext()->EventStateManager();
8137 
8138   // If we cannot handle the event with mPresShell because of no target,
8139   // just record the response time.
8140   // XXX Is this intentional?  In such case, the score is really good because
8141   //     of nothing to do.  So, it may make average and median better.
8142   if (NS_EVENT_NEEDS_FRAME(aEvent) && !mPresShell->GetCurrentEventFrame() &&
8143       !mPresShell->GetCurrentEventContent()) {
8144     RecordEventHandlingResponsePerformance(aEvent);
8145     return NS_OK;
8146   }
8147 
8148   if (mPresShell->mCurrentEventContent && aEvent->IsTargetedAtFocusedWindow()) {
8149     nsFocusManager* fm = nsFocusManager::GetFocusManager();
8150     if (fm) {
8151       // This may run script now.  So, mPresShell might be destroyed after here.
8152       fm->FlushBeforeEventHandlingIfNeeded(mPresShell->mCurrentEventContent);
8153     }
8154   }
8155 
8156   bool touchIsNew = false;
8157   if (!PrepareToDispatchEvent(aEvent, aEventStatus, &touchIsNew)) {
8158     return NS_OK;
8159   }
8160 
8161   // We finished preparing to dispatch the event.  So, let's record the
8162   // performance.
8163   RecordEventPreparationPerformance(aEvent);
8164 
8165   AutoHandlingUserInputStatePusher userInpStatePusher(
8166       UserActivation::IsUserInteractionEvent(aEvent), aEvent);
8167   AutoEventHandler eventHandler(aEvent, GetDocument());
8168   AutoPopupStatePusher popupStatePusher(
8169       PopupBlocker::GetEventPopupControlState(aEvent));
8170 
8171   // FIXME. If the event was reused, we need to clear the old target,
8172   // bug 329430
8173   aEvent->mTarget = nullptr;
8174 
8175   HandlingTimeAccumulator handlingTimeAccumulator(*this, aEvent);
8176 
8177   nsresult rv = DispatchEvent(manager, aEvent, touchIsNew, aEventStatus,
8178                               aOverrideClickTarget);
8179 
8180   if (!mPresShell->IsDestroying() && aIsHandlingNativeEvent) {
8181     // Ensure that notifications to IME should be sent before getting next
8182     // native event from the event queue.
8183     // XXX Should we check the event message or event class instead of
8184     //     using aIsHandlingNativeEvent?
8185     manager->TryToFlushPendingNotificationsToIME();
8186   }
8187 
8188   FinalizeHandlingEvent(aEvent);
8189 
8190   RecordEventHandlingResponsePerformance(aEvent);
8191 
8192   return rv;  // Result of DispatchEvent()
8193 }
8194 
DispatchEvent(EventStateManager * aEventStateManager,WidgetEvent * aEvent,bool aTouchIsNew,nsEventStatus * aEventStatus,nsIContent * aOverrideClickTarget)8195 nsresult PresShell::EventHandler::DispatchEvent(
8196     EventStateManager* aEventStateManager, WidgetEvent* aEvent,
8197     bool aTouchIsNew, nsEventStatus* aEventStatus,
8198     nsIContent* aOverrideClickTarget) {
8199   MOZ_ASSERT(aEventStateManager);
8200   MOZ_ASSERT(aEvent);
8201   MOZ_ASSERT(aEventStatus);
8202 
8203   // 1. Give event to event manager for pre event state changes and
8204   //    generation of synthetic events.
8205   {  // Scope for presContext
8206     RefPtr<nsPresContext> presContext = GetPresContext();
8207     nsCOMPtr<nsIContent> eventContent = mPresShell->mCurrentEventContent;
8208     nsresult rv = aEventStateManager->PreHandleEvent(
8209         presContext, aEvent, mPresShell->mCurrentEventFrame, eventContent,
8210         aEventStatus, aOverrideClickTarget);
8211     if (NS_FAILED(rv)) {
8212       return rv;
8213     }
8214   }
8215 
8216   // 2. Give event to the DOM for third party and JS use.
8217   bool wasHandlingKeyBoardEvent = nsContentUtils::IsHandlingKeyBoardEvent();
8218   if (aEvent->mClass == eKeyboardEventClass) {
8219     nsContentUtils::SetIsHandlingKeyBoardEvent(true);
8220   }
8221   // If EventStateManager or something wants reply from remote process and
8222   // needs to win any other event listeners in chrome, the event is both
8223   // stopped its propagation and marked as "waiting reply from remote
8224   // process".  In this case, PresShell shouldn't dispatch the event into
8225   // the DOM tree because they don't have a chance to stop propagation in
8226   // the system event group.  On the other hand, if its propagation is not
8227   // stopped, that means that the event may be reserved by chrome.  If it's
8228   // reserved by chrome, the event shouldn't be sent to any remote
8229   // processes.  In this case, PresShell needs to dispatch the event to
8230   // the DOM tree for checking if it's reserved.
8231   if (aEvent->IsAllowedToDispatchDOMEvent() &&
8232       !(aEvent->PropagationStopped() &&
8233         aEvent->IsWaitingReplyFromRemoteProcess())) {
8234     MOZ_ASSERT(nsContentUtils::IsSafeToRunScript(),
8235                "Somebody changed aEvent to cause a DOM event!");
8236     nsPresShellEventCB eventCB(mPresShell);
8237     if (nsIFrame* target = mPresShell->GetCurrentEventFrame()) {
8238       if (target->OnlySystemGroupDispatch(aEvent->mMessage)) {
8239         aEvent->StopPropagation();
8240       }
8241     }
8242     if (aEvent->mClass == eTouchEventClass) {
8243       DispatchTouchEventToDOM(aEvent, aEventStatus, &eventCB, aTouchIsNew);
8244     } else {
8245       DispatchEventToDOM(aEvent, aEventStatus, &eventCB);
8246     }
8247   }
8248 
8249   nsContentUtils::SetIsHandlingKeyBoardEvent(wasHandlingKeyBoardEvent);
8250 
8251   if (mPresShell->IsDestroying()) {
8252     return NS_OK;
8253   }
8254 
8255   // 3. Give event to event manager for post event state changes and
8256   //    generation of synthetic events.
8257   // Refetch the prescontext, in case it changed.
8258   RefPtr<nsPresContext> presContext = GetPresContext();
8259   return aEventStateManager->PostHandleEvent(
8260       presContext, aEvent, mPresShell->GetCurrentEventFrame(), aEventStatus,
8261       aOverrideClickTarget);
8262 }
8263 
PrepareToDispatchEvent(WidgetEvent * aEvent,nsEventStatus * aEventStatus,bool * aTouchIsNew)8264 bool PresShell::EventHandler::PrepareToDispatchEvent(
8265     WidgetEvent* aEvent, nsEventStatus* aEventStatus, bool* aTouchIsNew) {
8266   MOZ_ASSERT(aEvent->IsTrusted());
8267   MOZ_ASSERT(aEventStatus);
8268   MOZ_ASSERT(aTouchIsNew);
8269 
8270   *aTouchIsNew = false;
8271   if (aEvent->IsUserAction()) {
8272     mPresShell->mHasHandledUserInput = true;
8273   }
8274 
8275   switch (aEvent->mMessage) {
8276     case eKeyPress:
8277     case eKeyDown:
8278     case eKeyUp: {
8279       WidgetKeyboardEvent* keyboardEvent = aEvent->AsKeyboardEvent();
8280       MaybeHandleKeyboardEventBeforeDispatch(keyboardEvent);
8281       return true;
8282     }
8283     case eMouseMove: {
8284       bool allowCapture = EventStateManager::GetActiveEventStateManager() &&
8285                           GetPresContext() &&
8286                           GetPresContext()->EventStateManager() ==
8287                               EventStateManager::GetActiveEventStateManager();
8288       PresShell::AllowMouseCapture(allowCapture);
8289       return true;
8290     }
8291     case eDrop: {
8292       nsCOMPtr<nsIDragSession> session = nsContentUtils::GetDragSession();
8293       if (session) {
8294         bool onlyChromeDrop = false;
8295         session->GetOnlyChromeDrop(&onlyChromeDrop);
8296         if (onlyChromeDrop) {
8297           aEvent->mFlags.mOnlyChromeDispatch = true;
8298         }
8299       }
8300       return true;
8301     }
8302     case eContextMenu: {
8303       // If we cannot open context menu even though eContextMenu is fired, we
8304       // should stop dispatching it into the DOM.
8305       WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
8306       if (mouseEvent->IsContextMenuKeyEvent() &&
8307           !AdjustContextMenuKeyEvent(mouseEvent)) {
8308         return false;
8309       }
8310 
8311       // If "Shift" state is active, context menu should be forcibly opened even
8312       // if web apps want to prevent it since we respect our users' intention.
8313       // In this case, we don't fire "contextmenu" event on web content because
8314       // of not cancelable.
8315       if (mouseEvent->IsShift()) {
8316         aEvent->mFlags.mOnlyChromeDispatch = true;
8317         aEvent->mFlags.mRetargetToNonNativeAnonymous = true;
8318       }
8319       return true;
8320     }
8321     case eTouchStart:
8322     case eTouchMove:
8323     case eTouchEnd:
8324     case eTouchCancel:
8325     case eTouchPointerCancel:
8326       return mPresShell->mTouchManager.PreHandleEvent(
8327           aEvent, aEventStatus, *aTouchIsNew, mPresShell->mCurrentEventContent);
8328     default:
8329       return true;
8330   }
8331 }
8332 
FinalizeHandlingEvent(WidgetEvent * aEvent)8333 void PresShell::EventHandler::FinalizeHandlingEvent(WidgetEvent* aEvent) {
8334   switch (aEvent->mMessage) {
8335     case eKeyPress:
8336     case eKeyDown:
8337     case eKeyUp: {
8338       if (aEvent->AsKeyboardEvent()->mKeyCode == NS_VK_ESCAPE) {
8339         if (aEvent->mMessage == eKeyUp) {
8340           // Reset this flag after key up is handled.
8341           mPresShell->mIsLastChromeOnlyEscapeKeyConsumed = false;
8342         } else {
8343           if (aEvent->mFlags.mOnlyChromeDispatch &&
8344               aEvent->mFlags.mDefaultPreventedByChrome) {
8345             mPresShell->mIsLastChromeOnlyEscapeKeyConsumed = true;
8346           }
8347           if (aEvent->mMessage == eKeyDown &&
8348               !aEvent->mFlags.mDefaultPrevented) {
8349             if (Document* doc = GetDocument()) {
8350               doc->TryCancelDialog();
8351             }
8352           }
8353         }
8354       }
8355       if (aEvent->mMessage == eKeyDown) {
8356         mPresShell->mIsLastKeyDownCanceled = aEvent->mFlags.mDefaultPrevented;
8357       }
8358       return;
8359     }
8360     case eMouseUp:
8361       // reset the capturing content now that the mouse button is up
8362       PresShell::ReleaseCapturingContent();
8363       return;
8364     case eMouseMove:
8365       PresShell::AllowMouseCapture(false);
8366       return;
8367     case eDrag:
8368     case eDragEnd:
8369     case eDragEnter:
8370     case eDragExit:
8371     case eDragLeave:
8372     case eDragOver:
8373     case eDrop: {
8374       // After any drag event other than dragstart (which is handled
8375       // separately, as we need to collect the data first), the DataTransfer
8376       // needs to be made protected, and then disconnected.
8377       DataTransfer* dataTransfer = aEvent->AsDragEvent()->mDataTransfer;
8378       if (dataTransfer) {
8379         dataTransfer->Disconnect();
8380       }
8381       return;
8382     }
8383     default:
8384       return;
8385   }
8386 }
8387 
MaybeHandleKeyboardEventBeforeDispatch(WidgetKeyboardEvent * aKeyboardEvent)8388 void PresShell::EventHandler::MaybeHandleKeyboardEventBeforeDispatch(
8389     WidgetKeyboardEvent* aKeyboardEvent) {
8390   MOZ_ASSERT(aKeyboardEvent);
8391 
8392   if (aKeyboardEvent->mKeyCode != NS_VK_ESCAPE) {
8393     return;
8394   }
8395 
8396   // If we're in fullscreen mode, exit from it forcibly when Escape key is
8397   // pressed.
8398   Document* doc = mPresShell->GetCurrentEventContent()
8399                       ? mPresShell->mCurrentEventContent->OwnerDoc()
8400                       : nullptr;
8401   Document* root = nsContentUtils::GetInProcessSubtreeRootDocument(doc);
8402   if (root && root->GetFullscreenElement()) {
8403     // Prevent default action on ESC key press when exiting
8404     // DOM fullscreen mode. This prevents the browser ESC key
8405     // handler from stopping all loads in the document, which
8406     // would cause <video> loads to stop.
8407     // XXX We need to claim the Escape key event which will be
8408     //     dispatched only into chrome is already consumed by
8409     //     content because we need to prevent its default here
8410     //     for some reasons (not sure) but we need to detect
8411     //     if a chrome event handler will call PreventDefault()
8412     //     again and check it later.
8413     aKeyboardEvent->PreventDefaultBeforeDispatch(CrossProcessForwarding::eStop);
8414     aKeyboardEvent->mFlags.mOnlyChromeDispatch = true;
8415 
8416     // The event listeners in chrome can prevent this ESC behavior by
8417     // calling prevent default on the preceding keydown/press events.
8418     if (!mPresShell->mIsLastChromeOnlyEscapeKeyConsumed &&
8419         aKeyboardEvent->mMessage == eKeyUp) {
8420       // ESC key released while in DOM fullscreen mode.
8421       // Fully exit all browser windows and documents from
8422       // fullscreen mode.
8423       Document::AsyncExitFullscreen(nullptr);
8424     }
8425   }
8426 
8427   nsCOMPtr<Document> pointerLockedDoc = PointerLockManager::GetLockedDocument();
8428   if (!mPresShell->mIsLastChromeOnlyEscapeKeyConsumed && pointerLockedDoc) {
8429     // XXX See above comment to understand the reason why this needs
8430     //     to claim that the Escape key event is consumed by content
8431     //     even though it will be dispatched only into chrome.
8432     aKeyboardEvent->PreventDefaultBeforeDispatch(CrossProcessForwarding::eStop);
8433     aKeyboardEvent->mFlags.mOnlyChromeDispatch = true;
8434     if (aKeyboardEvent->mMessage == eKeyUp) {
8435       PointerLockManager::Unlock();
8436     }
8437   }
8438 }
8439 
RecordEventPreparationPerformance(const WidgetEvent * aEvent)8440 void PresShell::EventHandler::RecordEventPreparationPerformance(
8441     const WidgetEvent* aEvent) {
8442   MOZ_ASSERT(aEvent);
8443 
8444   switch (aEvent->mMessage) {
8445     case eKeyPress:
8446     case eKeyDown:
8447     case eKeyUp:
8448       if (aEvent->AsKeyboardEvent()->ShouldInteractionTimeRecorded()) {
8449         GetPresContext()->RecordInteractionTime(
8450             nsPresContext::InteractionType::KeyInteraction, aEvent->mTimeStamp);
8451       }
8452       Telemetry::AccumulateTimeDelta(Telemetry::INPUT_EVENT_QUEUED_KEYBOARD_MS,
8453                                      aEvent->mTimeStamp);
8454       return;
8455 
8456     case eMouseDown:
8457     case eMouseUp:
8458       Telemetry::AccumulateTimeDelta(Telemetry::INPUT_EVENT_QUEUED_CLICK_MS,
8459                                      aEvent->mTimeStamp);
8460       [[fallthrough]];
8461     case ePointerDown:
8462     case ePointerUp:
8463       GetPresContext()->RecordInteractionTime(
8464           nsPresContext::InteractionType::ClickInteraction, aEvent->mTimeStamp);
8465       return;
8466 
8467     case eMouseMove:
8468       if (aEvent->mFlags.mHandledByAPZ) {
8469         Telemetry::AccumulateTimeDelta(
8470             Telemetry::INPUT_EVENT_QUEUED_APZ_MOUSE_MOVE_MS,
8471             aEvent->mTimeStamp);
8472       }
8473       GetPresContext()->RecordInteractionTime(
8474           nsPresContext::InteractionType::MouseMoveInteraction,
8475           aEvent->mTimeStamp);
8476       return;
8477 
8478     case eWheel:
8479       if (aEvent->mFlags.mHandledByAPZ) {
8480         Telemetry::AccumulateTimeDelta(
8481             Telemetry::INPUT_EVENT_QUEUED_APZ_WHEEL_MS, aEvent->mTimeStamp);
8482       }
8483       return;
8484 
8485     case eTouchMove:
8486       if (aEvent->mFlags.mHandledByAPZ) {
8487         Telemetry::AccumulateTimeDelta(
8488             Telemetry::INPUT_EVENT_QUEUED_APZ_TOUCH_MOVE_MS,
8489             aEvent->mTimeStamp);
8490       }
8491       return;
8492 
8493     default:
8494       return;
8495   }
8496 }
8497 
RecordEventHandlingResponsePerformance(const WidgetEvent * aEvent)8498 void PresShell::EventHandler::RecordEventHandlingResponsePerformance(
8499     const WidgetEvent* aEvent) {
8500   if (!Telemetry::CanRecordBase() || aEvent->mTimeStamp.IsNull() ||
8501       aEvent->mTimeStamp <= mPresShell->mLastOSWake ||
8502       !aEvent->AsInputEvent()) {
8503     return;
8504   }
8505 
8506   TimeStamp now = TimeStamp::Now();
8507   double millis = (now - aEvent->mTimeStamp).ToMilliseconds();
8508   Telemetry::Accumulate(Telemetry::INPUT_EVENT_RESPONSE_MS, millis);
8509   if (GetDocument() &&
8510       GetDocument()->GetReadyStateEnum() != Document::READYSTATE_COMPLETE) {
8511     Telemetry::Accumulate(Telemetry::LOAD_INPUT_EVENT_RESPONSE_MS, millis);
8512   }
8513 
8514   if (!sLastInputProcessed || sLastInputProcessed < aEvent->mTimeStamp) {
8515     if (sLastInputProcessed) {
8516       // This input event was created after we handled the last one.
8517       // Accumulate the previous events' coalesced duration.
8518       double lastMillis =
8519           (sLastInputProcessed - sLastInputCreated).ToMilliseconds();
8520       Telemetry::Accumulate(Telemetry::INPUT_EVENT_RESPONSE_COALESCED_MS,
8521                             lastMillis);
8522 
8523       if (MOZ_UNLIKELY(!PresShell::sProcessInteractable)) {
8524         // For content process, we use the ready state of
8525         // top-level-content-document to know if the process has finished the
8526         // start-up.
8527         // For parent process, see the topic
8528         // 'sessionstore-one-or-no-tab-restored' in PresShell::Observe.
8529         if (XRE_IsContentProcess() && GetDocument() &&
8530             GetDocument()->IsTopLevelContentDocument()) {
8531           switch (GetDocument()->GetReadyStateEnum()) {
8532             case Document::READYSTATE_INTERACTIVE:
8533             case Document::READYSTATE_COMPLETE:
8534               PresShell::sProcessInteractable = true;
8535               break;
8536             default:
8537               break;
8538           }
8539         }
8540       }
8541       if (MOZ_LIKELY(PresShell::sProcessInteractable)) {
8542         Telemetry::Accumulate(Telemetry::INPUT_EVENT_RESPONSE_POST_STARTUP_MS,
8543                               lastMillis);
8544       } else {
8545         Telemetry::Accumulate(Telemetry::INPUT_EVENT_RESPONSE_STARTUP_MS,
8546                               lastMillis);
8547       }
8548     }
8549     sLastInputCreated = aEvent->mTimeStamp;
8550   } else if (aEvent->mTimeStamp < sLastInputCreated) {
8551     // This event was created before the last input. May be processing out
8552     // of order, so coalesce backwards, too.
8553     sLastInputCreated = aEvent->mTimeStamp;
8554   }
8555   sLastInputProcessed = now;
8556 }
8557 
8558 // static
8559 nsIPrincipal*
GetDocumentPrincipalToCompareWithBlacklist(PresShell & aPresShell)8560 PresShell::EventHandler::GetDocumentPrincipalToCompareWithBlacklist(
8561     PresShell& aPresShell) {
8562   nsPresContext* presContext = aPresShell.GetPresContext();
8563   if (NS_WARN_IF(!presContext)) {
8564     return nullptr;
8565   }
8566   return presContext->Document()->GetPrincipalForPrefBasedHacks();
8567 }
8568 
DispatchEventToDOM(WidgetEvent * aEvent,nsEventStatus * aEventStatus,nsPresShellEventCB * aEventCB)8569 nsresult PresShell::EventHandler::DispatchEventToDOM(
8570     WidgetEvent* aEvent, nsEventStatus* aEventStatus,
8571     nsPresShellEventCB* aEventCB) {
8572   nsresult rv = NS_OK;
8573   nsCOMPtr<nsINode> eventTarget = mPresShell->mCurrentEventContent;
8574   nsPresShellEventCB* eventCBPtr = aEventCB;
8575   if (!eventTarget) {
8576     nsCOMPtr<nsIContent> targetContent;
8577     if (mPresShell->mCurrentEventFrame) {
8578       rv = mPresShell->mCurrentEventFrame->GetContentForEvent(
8579           aEvent, getter_AddRefs(targetContent));
8580     }
8581     if (NS_SUCCEEDED(rv) && targetContent) {
8582       eventTarget = targetContent;
8583     } else if (GetDocument()) {
8584       eventTarget = GetDocument();
8585       // If we don't have any content, the callback wouldn't probably
8586       // do nothing.
8587       eventCBPtr = nullptr;
8588     }
8589   }
8590   if (eventTarget) {
8591     if (aEvent->IsBlockedForFingerprintingResistance()) {
8592       aEvent->mFlags.mOnlySystemGroupDispatchInContent = true;
8593     } else if (aEvent->mMessage == eKeyPress) {
8594       // If eKeyPress event is marked as not dispatched in the default event
8595       // group in web content, it's caused by non-printable key or key
8596       // combination.  In this case, UI Events declares that browsers
8597       // shouldn't dispatch keypress event.  However, some web apps may be
8598       // broken with this strict behavior due to historical issue.
8599       // Therefore, we need to keep dispatching keypress event for such keys
8600       // even with breaking the standard.
8601       // Similarly, the other browsers sets non-zero value of keyCode or
8602       // charCode of keypress event to the other.  Therefore, we should
8603       // behave so, however, some web apps may be broken.  On such web apps,
8604       // we should keep using legacy our behavior.
8605       if (!mPresShell->mInitializedWithKeyPressEventDispatchingBlacklist) {
8606         mPresShell->mInitializedWithKeyPressEventDispatchingBlacklist = true;
8607         nsCOMPtr<nsIPrincipal> principal =
8608             GetDocumentPrincipalToCompareWithBlacklist(*mPresShell);
8609         if (principal) {
8610           mPresShell->mForceDispatchKeyPressEventsForNonPrintableKeys =
8611               principal->IsURIInPrefList(
8612                   "dom.keyboardevent.keypress.hack.dispatch_non_printable_"
8613                   "keys") ||
8614               principal->IsURIInPrefList(
8615                   "dom.keyboardevent.keypress.hack."
8616                   "dispatch_non_printable_keys.addl");
8617 
8618           mPresShell->mForceUseLegacyKeyCodeAndCharCodeValues |=
8619               principal->IsURIInPrefList(
8620                   "dom.keyboardevent.keypress.hack."
8621                   "use_legacy_keycode_and_charcode") ||
8622               principal->IsURIInPrefList(
8623                   "dom.keyboardevent.keypress.hack."
8624                   "use_legacy_keycode_and_charcode.addl");
8625         }
8626       }
8627       if (mPresShell->mForceDispatchKeyPressEventsForNonPrintableKeys) {
8628         aEvent->mFlags.mOnlySystemGroupDispatchInContent = false;
8629       }
8630       if (mPresShell->mForceUseLegacyKeyCodeAndCharCodeValues) {
8631         aEvent->AsKeyboardEvent()->mUseLegacyKeyCodeAndCharCodeValues = true;
8632       }
8633     } else if (aEvent->mMessage == eMouseUp) {
8634       // Historically Firefox has dispatched click events for non-primary
8635       // buttons, but only on window and document (and inside input/textarea),
8636       // not on elements in general. The UI events spec forbids click (and
8637       // dblclick) for non-primary mouse buttons, and specifies auxclick
8638       // instead. In case of some websites that rely on non-primary click to
8639       // prevent new tab etc. and don't have auxclick code to do the same, we
8640       // need to revert to the historial non-standard behaviour
8641       if (!mPresShell->mInitializedWithClickEventDispatchingBlacklist) {
8642         mPresShell->mInitializedWithClickEventDispatchingBlacklist = true;
8643 
8644         nsCOMPtr<nsIPrincipal> principal =
8645             GetDocumentPrincipalToCompareWithBlacklist(*mPresShell);
8646 
8647         if (principal) {
8648           mPresShell->mForceUseLegacyNonPrimaryDispatch =
8649               principal->IsURIInPrefList(
8650                   "dom.mouseevent.click.hack.use_legacy_non-primary_dispatch");
8651         }
8652       }
8653       if (mPresShell->mForceUseLegacyNonPrimaryDispatch) {
8654         aEvent->AsMouseEvent()->mUseLegacyNonPrimaryDispatch = true;
8655       }
8656     }
8657 
8658     if (aEvent->mClass == eCompositionEventClass) {
8659       RefPtr<nsPresContext> presContext = GetPresContext();
8660       RefPtr<BrowserParent> browserParent =
8661           IMEStateManager::GetActiveBrowserParent();
8662       IMEStateManager::DispatchCompositionEvent(
8663           eventTarget, presContext, browserParent, aEvent->AsCompositionEvent(),
8664           aEventStatus, eventCBPtr);
8665     } else {
8666       RefPtr<nsPresContext> presContext = GetPresContext();
8667       EventDispatcher::Dispatch(eventTarget, presContext, aEvent, nullptr,
8668                                 aEventStatus, eventCBPtr);
8669     }
8670   }
8671   return rv;
8672 }
8673 
DispatchTouchEventToDOM(WidgetEvent * aEvent,nsEventStatus * aEventStatus,nsPresShellEventCB * aEventCB,bool aTouchIsNew)8674 void PresShell::EventHandler::DispatchTouchEventToDOM(
8675     WidgetEvent* aEvent, nsEventStatus* aEventStatus,
8676     nsPresShellEventCB* aEventCB, bool aTouchIsNew) {
8677   // calling preventDefault on touchstart or the first touchmove for a
8678   // point prevents mouse events. calling it on the touchend should
8679   // prevent click dispatching.
8680   bool canPrevent = (aEvent->mMessage == eTouchStart) ||
8681                     (aEvent->mMessage == eTouchMove && aTouchIsNew) ||
8682                     (aEvent->mMessage == eTouchEnd);
8683   bool preventDefault = false;
8684   nsEventStatus tmpStatus = nsEventStatus_eIgnore;
8685   WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
8686 
8687   // loop over all touches and dispatch events on any that have changed
8688   for (dom::Touch* touch : touchEvent->mTouches) {
8689     // We should remove all suppressed touch instances in
8690     // TouchManager::PreHandleEvent.
8691     MOZ_ASSERT(!touch->mIsTouchEventSuppressed);
8692 
8693     if (!touch || !touch->mChanged) {
8694       continue;
8695     }
8696 
8697     nsCOMPtr<EventTarget> targetPtr = touch->mTarget;
8698     nsCOMPtr<nsIContent> content = do_QueryInterface(targetPtr);
8699     if (!content) {
8700       continue;
8701     }
8702 
8703     Document* doc = content->OwnerDoc();
8704     nsIContent* capturingContent = PresShell::GetCapturingContent();
8705     if (capturingContent) {
8706       if (capturingContent->OwnerDoc() != doc) {
8707         // Wrong document, don't dispatch anything.
8708         continue;
8709       }
8710       content = capturingContent;
8711     }
8712     // copy the event
8713     MOZ_ASSERT(touchEvent->IsTrusted());
8714     WidgetTouchEvent newEvent(true, touchEvent->mMessage, touchEvent->mWidget);
8715     newEvent.AssignTouchEventData(*touchEvent, false);
8716     newEvent.mTarget = targetPtr;
8717     newEvent.mFlags.mHandledByAPZ = touchEvent->mFlags.mHandledByAPZ;
8718 
8719     RefPtr<PresShell> contentPresShell;
8720     if (doc == GetDocument()) {
8721       contentPresShell = doc->GetPresShell();
8722       if (contentPresShell) {
8723         // XXXsmaug huge hack. Pushing possibly capturing content,
8724         //         even though event target is something else.
8725         contentPresShell->PushCurrentEventInfo(content->GetPrimaryFrame(),
8726                                                content);
8727       }
8728     }
8729 
8730     nsPresContext* context = doc->GetPresContext();
8731     if (!context) {
8732       if (contentPresShell) {
8733         contentPresShell->PopCurrentEventInfo();
8734       }
8735       continue;
8736     }
8737 
8738     tmpStatus = nsEventStatus_eIgnore;
8739     EventDispatcher::Dispatch(targetPtr, context, &newEvent, nullptr,
8740                               &tmpStatus, aEventCB);
8741     if (nsEventStatus_eConsumeNoDefault == tmpStatus ||
8742         newEvent.mFlags.mMultipleActionsPrevented) {
8743       preventDefault = true;
8744     }
8745 
8746     if (newEvent.mFlags.mMultipleActionsPrevented) {
8747       touchEvent->mFlags.mMultipleActionsPrevented = true;
8748     }
8749 
8750     if (contentPresShell) {
8751       contentPresShell->PopCurrentEventInfo();
8752     }
8753   }
8754 
8755   if (preventDefault && canPrevent) {
8756     *aEventStatus = nsEventStatus_eConsumeNoDefault;
8757   } else {
8758     *aEventStatus = nsEventStatus_eIgnore;
8759   }
8760 }
8761 
8762 // Dispatch event to content only (NOT full processing)
8763 // See also HandleEventWithTarget which does full event processing.
HandleDOMEventWithTarget(nsIContent * aTargetContent,WidgetEvent * aEvent,nsEventStatus * aStatus)8764 nsresult PresShell::HandleDOMEventWithTarget(nsIContent* aTargetContent,
8765                                              WidgetEvent* aEvent,
8766                                              nsEventStatus* aStatus) {
8767   nsresult rv = NS_OK;
8768 
8769   PushCurrentEventInfo(nullptr, aTargetContent);
8770 
8771   // Bug 41013: Check if the event should be dispatched to content.
8772   // It's possible that we are in the middle of destroying the window
8773   // and the js context is out of date. This check detects the case
8774   // that caused a crash in bug 41013, but there may be a better way
8775   // to handle this situation!
8776   nsCOMPtr<nsISupports> container = mPresContext->GetContainerWeak();
8777   if (container) {
8778     // Dispatch event to content
8779     rv = EventDispatcher::Dispatch(aTargetContent, mPresContext, aEvent,
8780                                    nullptr, aStatus);
8781   }
8782 
8783   PopCurrentEventInfo();
8784   return rv;
8785 }
8786 
8787 // See the method above.
HandleDOMEventWithTarget(nsIContent * aTargetContent,Event * aEvent,nsEventStatus * aStatus)8788 nsresult PresShell::HandleDOMEventWithTarget(nsIContent* aTargetContent,
8789                                              Event* aEvent,
8790                                              nsEventStatus* aStatus) {
8791   nsresult rv = NS_OK;
8792 
8793   PushCurrentEventInfo(nullptr, aTargetContent);
8794   nsCOMPtr<nsISupports> container = mPresContext->GetContainerWeak();
8795   if (container) {
8796     rv = EventDispatcher::DispatchDOMEvent(aTargetContent, nullptr, aEvent,
8797                                            mPresContext, aStatus);
8798   }
8799 
8800   PopCurrentEventInfo();
8801   return rv;
8802 }
8803 
AdjustContextMenuKeyEvent(WidgetMouseEvent * aMouseEvent)8804 bool PresShell::EventHandler::AdjustContextMenuKeyEvent(
8805     WidgetMouseEvent* aMouseEvent) {
8806 #ifdef MOZ_XUL
8807   // if a menu is open, open the context menu relative to the active item on the
8808   // menu.
8809   nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
8810   if (pm) {
8811     nsIFrame* popupFrame = pm->GetTopPopup(ePopupTypeMenu);
8812     if (popupFrame) {
8813       nsIFrame* itemFrame =
8814           (static_cast<nsMenuPopupFrame*>(popupFrame))->GetCurrentMenuItem();
8815       if (!itemFrame) itemFrame = popupFrame;
8816 
8817       nsCOMPtr<nsIWidget> widget = popupFrame->GetNearestWidget();
8818       aMouseEvent->mWidget = widget;
8819       LayoutDeviceIntPoint widgetPoint = widget->WidgetToScreenOffset();
8820       aMouseEvent->mRefPoint =
8821           LayoutDeviceIntPoint::FromAppUnitsToNearest(
8822               itemFrame->GetScreenRectInAppUnits().BottomLeft(),
8823               itemFrame->PresContext()->AppUnitsPerDevPixel()) -
8824           widgetPoint;
8825 
8826       mPresShell->mCurrentEventContent = itemFrame->GetContent();
8827       mPresShell->mCurrentEventFrame = itemFrame;
8828 
8829       return true;
8830     }
8831   }
8832 #endif
8833 
8834   // If we're here because of the key-equiv for showing context menus, we
8835   // have to twiddle with the NS event to make sure the context menu comes
8836   // up in the upper left of the relevant content area before we create
8837   // the DOM event. Since we never call InitMouseEvent() on the event,
8838   // the client X/Y will be 0,0. We can make use of that if the widget is null.
8839   // Use the root view manager's widget since it's most likely to have one,
8840   // and the coordinates returned by GetCurrentItemAndPositionForElement
8841   // are relative to the widget of the root of the root view manager.
8842   nsRootPresContext* rootPC = GetPresContext()->GetRootPresContext();
8843   aMouseEvent->mRefPoint = LayoutDeviceIntPoint(0, 0);
8844   if (rootPC) {
8845     rootPC->PresShell()->GetViewManager()->GetRootWidget(
8846         getter_AddRefs(aMouseEvent->mWidget));
8847 
8848     if (aMouseEvent->mWidget) {
8849       // default the refpoint to the topleft of our document
8850       nsPoint offset(0, 0);
8851       nsIFrame* rootFrame = FrameConstructor()->GetRootFrame();
8852       if (rootFrame) {
8853         nsView* view = rootFrame->GetClosestView(&offset);
8854         offset += view->GetOffsetToWidget(aMouseEvent->mWidget);
8855         aMouseEvent->mRefPoint = LayoutDeviceIntPoint::FromAppUnitsToNearest(
8856             offset, GetPresContext()->AppUnitsPerDevPixel());
8857       }
8858     }
8859   } else {
8860     aMouseEvent->mWidget = nullptr;
8861   }
8862 
8863   // see if we should use the caret position for the popup
8864   LayoutDeviceIntPoint caretPoint;
8865   // Beware! This may flush notifications via synchronous
8866   // ScrollSelectionIntoView.
8867   if (PrepareToUseCaretPosition(MOZ_KnownLive(aMouseEvent->mWidget),
8868                                 caretPoint)) {
8869     // caret position is good
8870     int32_t devPixelRatio = GetPresContext()->AppUnitsPerDevPixel();
8871     caretPoint = LayoutDeviceIntPoint::FromAppUnitsToNearest(
8872         ViewportUtils::LayoutToVisual(
8873             LayoutDeviceIntPoint::ToAppUnits(caretPoint, devPixelRatio),
8874             GetPresContext()->PresShell()),
8875         devPixelRatio);
8876     aMouseEvent->mRefPoint = caretPoint;
8877     return true;
8878   }
8879 
8880   // If we're here because of the key-equiv for showing context menus, we
8881   // have to reset the event target to the currently focused element. Get it
8882   // from the focus controller.
8883   RefPtr<Element> currentFocus;
8884   nsFocusManager* fm = nsFocusManager::GetFocusManager();
8885   if (fm) {
8886     currentFocus = fm->GetFocusedElement();
8887   }
8888 
8889   // Reset event coordinates relative to focused frame in view
8890   if (currentFocus) {
8891     nsCOMPtr<nsIContent> currentPointElement;
8892     GetCurrentItemAndPositionForElement(
8893         currentFocus, getter_AddRefs(currentPointElement),
8894         aMouseEvent->mRefPoint, MOZ_KnownLive(aMouseEvent->mWidget));
8895     if (currentPointElement) {
8896       mPresShell->mCurrentEventContent = currentPointElement;
8897       mPresShell->mCurrentEventFrame = nullptr;
8898       mPresShell->GetCurrentEventFrame();
8899     }
8900   }
8901 
8902   return true;
8903 }
8904 
8905 // PresShell::EventHandler::PrepareToUseCaretPosition
8906 //
8907 //    This checks to see if we should use the caret position for popup context
8908 //    menus. Returns true if the caret position should be used, and the
8909 //    coordinates of that position is returned in aTargetPt. This function
8910 //    will also scroll the window as needed to make the caret visible.
8911 //
8912 //    The event widget should be the widget that generated the event, and
8913 //    whose coordinate system the resulting event's mRefPoint should be
8914 //    relative to.  The returned point is in device pixels realtive to the
8915 //    widget passed in.
PrepareToUseCaretPosition(nsIWidget * aEventWidget,LayoutDeviceIntPoint & aTargetPt)8916 bool PresShell::EventHandler::PrepareToUseCaretPosition(
8917     nsIWidget* aEventWidget, LayoutDeviceIntPoint& aTargetPt) {
8918   nsresult rv;
8919 
8920   // check caret visibility
8921   RefPtr<nsCaret> caret = mPresShell->GetCaret();
8922   NS_ENSURE_TRUE(caret, false);
8923 
8924   bool caretVisible = caret->IsVisible();
8925   if (!caretVisible) return false;
8926 
8927   // caret selection, this is a temporary weak reference, so no refcounting is
8928   // needed
8929   Selection* domSelection = caret->GetSelection();
8930   NS_ENSURE_TRUE(domSelection, false);
8931 
8932   // since the match could be an anonymous textnode inside a
8933   // <textarea> or text <input>, we need to get the outer frame
8934   // note: frames are not refcounted
8935   nsIFrame* frame = nullptr;  // may be nullptr
8936   nsINode* node = domSelection->GetFocusNode();
8937   NS_ENSURE_TRUE(node, false);
8938   nsCOMPtr<nsIContent> content = nsIContent::FromNode(node);
8939   if (content) {
8940     nsIContent* nonNative = content->FindFirstNonChromeOnlyAccessContent();
8941     content = nonNative;
8942   }
8943 
8944   if (content) {
8945     // It seems like ScrollSelectionIntoView should be enough, but it's
8946     // not. The problem is that scrolling the selection into view when it is
8947     // below the current viewport will align the top line of the frame exactly
8948     // with the bottom of the window. This is fine, BUT, the popup event causes
8949     // the control to be re-focused which does this exact call to
8950     // ScrollContentIntoView, which has a one-pixel disagreement of whether the
8951     // frame is actually in view. The result is that the frame is aligned with
8952     // the top of the window, but the menu is still at the bottom.
8953     //
8954     // Doing this call first forces the frame to be in view, eliminating the
8955     // problem. The only difference in the result is that if your cursor is in
8956     // an edit box below the current view, you'll get the edit box aligned with
8957     // the top of the window. This is arguably better behavior anyway.
8958     rv =
8959         MOZ_KnownLive(mPresShell)
8960             ->ScrollContentIntoView(
8961                 content, ScrollAxis(kScrollMinimum, WhenToScroll::IfNotVisible),
8962                 ScrollAxis(kScrollMinimum, WhenToScroll::IfNotVisible),
8963                 ScrollFlags::ScrollOverflowHidden);
8964     NS_ENSURE_SUCCESS(rv, false);
8965     frame = content->GetPrimaryFrame();
8966     NS_WARNING_ASSERTION(frame, "No frame for focused content?");
8967   }
8968 
8969   // Actually scroll the selection (ie caret) into view. Note that this must
8970   // be synchronous since we will be checking the caret position on the screen.
8971   //
8972   // Be easy about errors, and just don't scroll in those cases. Better to have
8973   // the correct menu at a weird place than the wrong menu.
8974   // After ScrollSelectionIntoView(), the pending notifications might be
8975   // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
8976   nsCOMPtr<nsISelectionController> selCon;
8977   if (frame)
8978     frame->GetSelectionController(GetPresContext(), getter_AddRefs(selCon));
8979   else
8980     selCon = static_cast<nsISelectionController*>(mPresShell);
8981   if (selCon) {
8982     rv = selCon->ScrollSelectionIntoView(
8983         nsISelectionController::SELECTION_NORMAL,
8984         nsISelectionController::SELECTION_FOCUS_REGION,
8985         nsISelectionController::SCROLL_SYNCHRONOUS);
8986     NS_ENSURE_SUCCESS(rv, false);
8987   }
8988 
8989   nsPresContext* presContext = GetPresContext();
8990 
8991   // get caret position relative to the closest view
8992   nsRect caretCoords;
8993   nsIFrame* caretFrame = caret->GetGeometry(&caretCoords);
8994   if (!caretFrame) return false;
8995   nsPoint viewOffset;
8996   nsView* view = caretFrame->GetClosestView(&viewOffset);
8997   if (!view) return false;
8998   // and then get the caret coords relative to the event widget
8999   if (aEventWidget) {
9000     viewOffset += view->GetOffsetToWidget(aEventWidget);
9001   }
9002   caretCoords.MoveBy(viewOffset);
9003 
9004   // caret coordinates are in app units, convert to pixels
9005   aTargetPt.x =
9006       presContext->AppUnitsToDevPixels(caretCoords.x + caretCoords.width);
9007   aTargetPt.y =
9008       presContext->AppUnitsToDevPixels(caretCoords.y + caretCoords.height);
9009 
9010   // make sure rounding doesn't return a pixel which is outside the caret
9011   // (e.g. one line lower)
9012   aTargetPt.y -= 1;
9013 
9014   return true;
9015 }
9016 
GetCurrentItemAndPositionForElement(Element * aFocusedElement,nsIContent ** aTargetToUse,LayoutDeviceIntPoint & aTargetPt,nsIWidget * aRootWidget)9017 void PresShell::EventHandler::GetCurrentItemAndPositionForElement(
9018     Element* aFocusedElement, nsIContent** aTargetToUse,
9019     LayoutDeviceIntPoint& aTargetPt, nsIWidget* aRootWidget) {
9020   nsCOMPtr<nsIContent> focusedContent = aFocusedElement;
9021   MOZ_KnownLive(mPresShell)
9022       ->ScrollContentIntoView(focusedContent, ScrollAxis(), ScrollAxis(),
9023                               ScrollFlags::ScrollOverflowHidden);
9024 
9025   nsPresContext* presContext = GetPresContext();
9026 
9027   bool istree = false, checkLineHeight = true;
9028   nscoord extraTreeY = 0;
9029 
9030 #ifdef MOZ_XUL
9031   // Set the position to just underneath the current item for multi-select
9032   // lists or just underneath the selected item for single-select lists. If
9033   // the element is not a list, or there is no selection, leave the position
9034   // as is.
9035   nsCOMPtr<Element> item;
9036   nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelect =
9037       aFocusedElement->AsXULMultiSelectControl();
9038   if (multiSelect) {
9039     checkLineHeight = false;
9040 
9041     int32_t currentIndex;
9042     multiSelect->GetCurrentIndex(&currentIndex);
9043     if (currentIndex >= 0) {
9044       RefPtr<XULTreeElement> tree = XULTreeElement::FromNode(focusedContent);
9045       // Tree view special case (tree items have no frames)
9046       // Get the focused row and add its coordinates, which are already in
9047       // pixels
9048       // XXX Boris, should we create a new interface so that this doesn't
9049       // need to know about trees? Something like nsINodelessChildCreator
9050       // which could provide the current focus coordinates?
9051       if (tree) {
9052         tree->EnsureRowIsVisible(currentIndex);
9053         int32_t firstVisibleRow = tree->GetFirstVisibleRow();
9054         int32_t rowHeight = tree->RowHeight();
9055 
9056         extraTreeY += nsPresContext::CSSPixelsToAppUnits(
9057             (currentIndex - firstVisibleRow + 1) * rowHeight);
9058         istree = true;
9059 
9060         RefPtr<nsTreeColumns> cols = tree->GetColumns();
9061         if (cols) {
9062           nsTreeColumn* col = cols->GetFirstColumn();
9063           if (col) {
9064             RefPtr<Element> colElement = col->Element();
9065             nsIFrame* frame = colElement->GetPrimaryFrame();
9066             if (frame) {
9067               extraTreeY += frame->GetSize().height;
9068             }
9069           }
9070         }
9071       } else {
9072         multiSelect->GetCurrentItem(getter_AddRefs(item));
9073       }
9074     }
9075   } else {
9076     // don't check menulists as the selected item will be inside a popup.
9077     nsCOMPtr<nsIDOMXULMenuListElement> menulist =
9078         aFocusedElement->AsXULMenuList();
9079     if (!menulist) {
9080       nsCOMPtr<nsIDOMXULSelectControlElement> select =
9081           aFocusedElement->AsXULSelectControl();
9082       if (select) {
9083         checkLineHeight = false;
9084         select->GetSelectedItem(getter_AddRefs(item));
9085       }
9086     }
9087   }
9088 
9089   if (item) {
9090     focusedContent = item;
9091   }
9092 #endif
9093 
9094   nsIFrame* frame = focusedContent->GetPrimaryFrame();
9095   if (frame) {
9096     NS_ASSERTION(
9097         frame->PresContext() == GetPresContext(),
9098         "handling event for focused content that is not in our document?");
9099 
9100     nsPoint frameOrigin(0, 0);
9101 
9102     // Get the frame's origin within its view
9103     nsView* view = frame->GetClosestView(&frameOrigin);
9104     NS_ASSERTION(view, "No view for frame");
9105 
9106     // View's origin relative the widget
9107     if (aRootWidget) {
9108       frameOrigin += view->GetOffsetToWidget(aRootWidget);
9109     }
9110 
9111     // Start context menu down and to the right from top left of frame
9112     // use the lineheight. This is a good distance to move the context
9113     // menu away from the top left corner of the frame. If we always
9114     // used the frame height, the context menu could end up far away,
9115     // for example when we're focused on linked images.
9116     // On the other hand, we want to use the frame height if it's less
9117     // than the current line height, so that the context menu appears
9118     // associated with the correct frame.
9119     nscoord extra = 0;
9120     if (!istree) {
9121       extra = frame->GetSize().height;
9122       if (checkLineHeight) {
9123         nsIScrollableFrame* scrollFrame =
9124             nsLayoutUtils::GetNearestScrollableFrame(
9125                 frame, nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN |
9126                            nsLayoutUtils::SCROLLABLE_FIXEDPOS_FINDS_ROOT);
9127         if (scrollFrame) {
9128           nsSize scrollAmount = scrollFrame->GetLineScrollAmount();
9129           nsIFrame* f = do_QueryFrame(scrollFrame);
9130           int32_t APD = presContext->AppUnitsPerDevPixel();
9131           int32_t scrollAPD = f->PresContext()->AppUnitsPerDevPixel();
9132           scrollAmount = scrollAmount.ScaleToOtherAppUnits(scrollAPD, APD);
9133           if (extra > scrollAmount.height) {
9134             extra = scrollAmount.height;
9135           }
9136         }
9137       }
9138     }
9139 
9140     aTargetPt.x = presContext->AppUnitsToDevPixels(frameOrigin.x);
9141     aTargetPt.y =
9142         presContext->AppUnitsToDevPixels(frameOrigin.y + extra + extraTreeY);
9143   }
9144 
9145   NS_IF_ADDREF(*aTargetToUse = focusedContent);
9146 }
9147 
ShouldIgnoreInvalidation()9148 bool PresShell::ShouldIgnoreInvalidation() {
9149   return mPaintingSuppressed || !mIsActive || mIsNeverPainting;
9150 }
9151 
WillPaint()9152 void PresShell::WillPaint() {
9153   // Check the simplest things first.  In particular, it's important to
9154   // check mIsActive before making any of the more expensive calls such
9155   // as GetRootPresContext, for the case of a browser with a large
9156   // number of tabs.
9157   // Don't bother doing anything if some viewmanager in our tree is painting
9158   // while we still have painting suppressed or we are not active.
9159   if (!mIsActive || mPaintingSuppressed || !IsVisible()) {
9160     return;
9161   }
9162 
9163   nsRootPresContext* rootPresContext = mPresContext->GetRootPresContext();
9164   if (!rootPresContext) {
9165     // In some edge cases, such as when we don't have a root frame yet,
9166     // we can't find the root prescontext. There's nothing to do in that
9167     // case.
9168     return;
9169   }
9170 
9171   rootPresContext->FlushWillPaintObservers();
9172   if (mIsDestroying) return;
9173 
9174   // Process reflows, if we have them, to reduce flicker due to invalidates and
9175   // reflow being interspersed.  Note that we _do_ allow this to be
9176   // interruptible; if we can't do all the reflows it's better to flicker a bit
9177   // than to freeze up.
9178   FlushPendingNotifications(
9179       ChangesToFlush(FlushType::InterruptibleLayout, false));
9180 }
9181 
DidPaintWindow()9182 void PresShell::DidPaintWindow() {
9183   nsRootPresContext* rootPresContext = mPresContext->GetRootPresContext();
9184   if (rootPresContext != mPresContext) {
9185     // This could be a popup's presshell. No point in notifying XPConnect
9186     // about compositing of popups.
9187     return;
9188   }
9189 
9190   if (!mHasReceivedPaintMessage) {
9191     mHasReceivedPaintMessage = true;
9192 
9193     nsCOMPtr<nsIObserverService> obsvc = services::GetObserverService();
9194     if (obsvc && mDocument) {
9195       nsPIDOMWindowOuter* window = mDocument->GetWindow();
9196       nsCOMPtr<nsIDOMChromeWindow> chromeWin(do_QueryInterface(window));
9197       if (chromeWin) {
9198         obsvc->NotifyObservers(chromeWin, "widget-first-paint", nullptr);
9199       }
9200     }
9201   }
9202 }
9203 
IsVisible() const9204 bool PresShell::IsVisible() const {
9205   if (!mIsActive || !mViewManager) return false;
9206 
9207   nsView* view = mViewManager->GetRootView();
9208   if (!view) return true;
9209 
9210   // inner view of subdoc frame
9211   view = view->GetParent();
9212   if (!view) return true;
9213 
9214   // subdoc view
9215   view = view->GetParent();
9216   if (!view) return true;
9217 
9218   nsIFrame* frame = view->GetFrame();
9219   if (!frame) return true;
9220 
9221   return frame->IsVisibleConsideringAncestors(
9222       nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY);
9223 }
9224 
SuppressDisplayport(bool aEnabled)9225 void PresShell::SuppressDisplayport(bool aEnabled) {
9226   if (aEnabled) {
9227     mActiveSuppressDisplayport++;
9228   } else if (mActiveSuppressDisplayport > 0) {
9229     bool isSuppressed = IsDisplayportSuppressed();
9230     mActiveSuppressDisplayport--;
9231     if (isSuppressed && !IsDisplayportSuppressed()) {
9232       // We unsuppressed the displayport, trigger a paint
9233       if (nsIFrame* rootFrame = mFrameConstructor->GetRootFrame()) {
9234         rootFrame->SchedulePaint();
9235       }
9236     }
9237   }
9238 }
9239 
9240 static bool sDisplayPortSuppressionRespected = true;
9241 
RespectDisplayportSuppression(bool aEnabled)9242 void PresShell::RespectDisplayportSuppression(bool aEnabled) {
9243   bool isSuppressed = IsDisplayportSuppressed();
9244   sDisplayPortSuppressionRespected = aEnabled;
9245   if (isSuppressed && !IsDisplayportSuppressed()) {
9246     // We unsuppressed the displayport, trigger a paint
9247     if (nsIFrame* rootFrame = mFrameConstructor->GetRootFrame()) {
9248       rootFrame->SchedulePaint();
9249     }
9250   }
9251 }
9252 
IsDisplayportSuppressed()9253 bool PresShell::IsDisplayportSuppressed() {
9254   return sDisplayPortSuppressionRespected && mActiveSuppressDisplayport > 0;
9255 }
9256 
FreezeSubDocument(Document & aDocument)9257 static CallState FreezeSubDocument(Document& aDocument) {
9258   if (PresShell* presShell = aDocument.GetPresShell()) {
9259     presShell->Freeze();
9260   }
9261   return CallState::Continue;
9262 }
9263 
Freeze(bool aIncludeSubDocuments)9264 void PresShell::Freeze(bool aIncludeSubDocuments) {
9265   mUpdateApproximateFrameVisibilityEvent.Revoke();
9266 
9267   MaybeReleaseCapturingContent();
9268 
9269   if (mCaret) {
9270     SetCaretEnabled(false);
9271   }
9272 
9273   mPaintingSuppressed = true;
9274 
9275   if (aIncludeSubDocuments && mDocument) {
9276     mDocument->EnumerateSubDocuments(FreezeSubDocument);
9277   }
9278 
9279   nsPresContext* presContext = GetPresContext();
9280   if (presContext) {
9281     presContext->DisableInteractionTimeRecording();
9282     if (presContext->RefreshDriver()->GetPresContext() == presContext) {
9283       presContext->RefreshDriver()->Freeze();
9284     }
9285   }
9286 
9287   mFrozen = true;
9288   if (mDocument) {
9289     UpdateImageLockingState();
9290   }
9291 }
9292 
FireOrClearDelayedEvents(bool aFireEvents)9293 void PresShell::FireOrClearDelayedEvents(bool aFireEvents) {
9294   mNoDelayedMouseEvents = false;
9295   mNoDelayedKeyEvents = false;
9296   if (!aFireEvents) {
9297     mDelayedEvents.Clear();
9298     return;
9299   }
9300 
9301   if (mDocument) {
9302     RefPtr<Document> doc = mDocument;
9303     while (!mIsDestroying && mDelayedEvents.Length() &&
9304            !doc->EventHandlingSuppressed()) {
9305       UniquePtr<DelayedEvent> ev = std::move(mDelayedEvents[0]);
9306       mDelayedEvents.RemoveElementAt(0);
9307       if (ev->IsKeyPressEvent() && mIsLastKeyDownCanceled) {
9308         continue;
9309       }
9310       ev->Dispatch();
9311     }
9312     if (!doc->EventHandlingSuppressed()) {
9313       mDelayedEvents.Clear();
9314     }
9315   }
9316 }
9317 
Thaw(bool aIncludeSubDocuments)9318 void PresShell::Thaw(bool aIncludeSubDocuments) {
9319   nsPresContext* presContext = GetPresContext();
9320   if (presContext &&
9321       presContext->RefreshDriver()->GetPresContext() == presContext) {
9322     presContext->RefreshDriver()->Thaw();
9323   }
9324 
9325   if (aIncludeSubDocuments && mDocument) {
9326     mDocument->EnumerateSubDocuments([](Document& aSubDoc) {
9327       if (PresShell* presShell = aSubDoc.GetPresShell()) {
9328         presShell->Thaw();
9329       }
9330       return CallState::Continue;
9331     });
9332   }
9333 
9334   // Get the activeness of our presshell, as this might have changed
9335   // while we were in the bfcache
9336   ActivenessMaybeChanged();
9337 
9338   // We're now unfrozen
9339   mFrozen = false;
9340   UpdateImageLockingState();
9341 
9342   UnsuppressPainting();
9343 }
9344 
9345 //--------------------------------------------------------
9346 // Start of protected and private methods on the PresShell
9347 //--------------------------------------------------------
9348 
MaybeScheduleReflow()9349 void PresShell::MaybeScheduleReflow() {
9350   ASSERT_REFLOW_SCHEDULED_STATE();
9351   if (mObservingLayoutFlushes || mIsDestroying || mIsReflowing ||
9352       mDirtyRoots.IsEmpty())
9353     return;
9354 
9355   if (!mPresContext->HasPendingInterrupt() || !ScheduleReflowOffTimer()) {
9356     ScheduleReflow();
9357   }
9358 
9359   ASSERT_REFLOW_SCHEDULED_STATE();
9360 }
9361 
ScheduleReflow()9362 void PresShell::ScheduleReflow() {
9363   ASSERT_REFLOW_SCHEDULED_STATE();
9364   DoObserveLayoutFlushes();
9365   ASSERT_REFLOW_SCHEDULED_STATE();
9366 }
9367 
WillCauseReflow()9368 void PresShell::WillCauseReflow() {
9369   nsContentUtils::AddScriptBlocker();
9370   ++mChangeNestCount;
9371 }
9372 
DidCauseReflow()9373 void PresShell::DidCauseReflow() {
9374   NS_ASSERTION(mChangeNestCount != 0, "Unexpected call to DidCauseReflow()");
9375   --mChangeNestCount;
9376   nsContentUtils::RemoveScriptBlocker();
9377 }
9378 
WillDoReflow()9379 void PresShell::WillDoReflow() {
9380   mDocument->FlushUserFontSet();
9381 
9382   mPresContext->FlushCounterStyles();
9383 
9384   mPresContext->FlushFontFeatureValues();
9385 
9386   mLastReflowStart = GetPerformanceNowUnclamped();
9387 }
9388 
DidDoReflow(bool aInterruptible)9389 void PresShell::DidDoReflow(bool aInterruptible) {
9390   HandlePostedReflowCallbacks(aInterruptible);
9391   if (mIsDestroying) {
9392     return;
9393   }
9394 
9395   nsAutoScriptBlocker scriptBlocker;
9396   AutoAssertNoFlush noReentrantFlush(*this);
9397   if (nsCOMPtr<nsIDocShell> docShell = mPresContext->GetDocShell()) {
9398     DOMHighResTimeStamp now = GetPerformanceNowUnclamped();
9399     docShell->NotifyReflowObservers(aInterruptible, mLastReflowStart, now);
9400   }
9401 
9402   if (!mPresContext->HasPendingInterrupt()) {
9403     mDocument->ScheduleResizeObserversNotification();
9404   }
9405 
9406   if (StaticPrefs::layout_reflow_synthMouseMove()) {
9407     SynthesizeMouseMove(false);
9408   }
9409 
9410   mPresContext->NotifyMissingFonts();
9411 }
9412 
GetPerformanceNowUnclamped()9413 DOMHighResTimeStamp PresShell::GetPerformanceNowUnclamped() {
9414   DOMHighResTimeStamp now = 0;
9415 
9416   if (nsPIDOMWindowInner* window = mDocument->GetInnerWindow()) {
9417     Performance* perf = window->GetPerformance();
9418 
9419     if (perf) {
9420       now = perf->NowUnclamped();
9421     }
9422   }
9423 
9424   return now;
9425 }
9426 
sReflowContinueCallback(nsITimer * aTimer,void * aPresShell)9427 void PresShell::sReflowContinueCallback(nsITimer* aTimer, void* aPresShell) {
9428   RefPtr<PresShell> self = static_cast<PresShell*>(aPresShell);
9429 
9430   MOZ_ASSERT(aTimer == self->mReflowContinueTimer, "Unexpected timer");
9431   self->mReflowContinueTimer = nullptr;
9432   self->ScheduleReflow();
9433 }
9434 
ScheduleReflowOffTimer()9435 bool PresShell::ScheduleReflowOffTimer() {
9436   MOZ_ASSERT(!mObservingLayoutFlushes, "Shouldn't get here");
9437   ASSERT_REFLOW_SCHEDULED_STATE();
9438 
9439   if (!mReflowContinueTimer) {
9440     nsresult rv = NS_NewTimerWithFuncCallback(
9441         getter_AddRefs(mReflowContinueTimer), sReflowContinueCallback, this, 30,
9442         nsITimer::TYPE_ONE_SHOT, "sReflowContinueCallback",
9443         mDocument->EventTargetFor(TaskCategory::Other));
9444     return NS_SUCCEEDED(rv);
9445   }
9446   return true;
9447 }
9448 
DoReflow(nsIFrame * target,bool aInterruptible,OverflowChangedTracker * aOverflowTracker)9449 bool PresShell::DoReflow(nsIFrame* target, bool aInterruptible,
9450                          OverflowChangedTracker* aOverflowTracker) {
9451   [[maybe_unused]] nsIURI* uri = mDocument->GetDocumentURI();
9452   AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING(
9453       "Reflow", LAYOUT_Reflow, uri ? uri->GetSpecOrDefault() : "N/A"_ns);
9454 
9455   LAYOUT_TELEMETRY_RECORD_BASE(Reflow);
9456 
9457   PerfStats::AutoMetricRecording<PerfStats::Metric::Reflowing> autoRecording;
9458 
9459   gfxTextPerfMetrics* tp = mPresContext->GetTextPerfMetrics();
9460   TimeStamp timeStart;
9461   if (tp) {
9462     tp->Accumulate();
9463     tp->reflowCount++;
9464     timeStart = TimeStamp::Now();
9465   }
9466 
9467   if (mMobileViewportManager) {
9468     mMobileViewportManager->UpdateSizesBeforeReflow();
9469   }
9470 
9471   // Schedule a paint, but don't actually mark this frame as changed for
9472   // retained DL building purposes. If any child frames get moved, then
9473   // they will schedule paint again. We could probaby skip this, and just
9474   // schedule a similar paint when a frame is deleted.
9475   target->SchedulePaint(nsIFrame::PAINT_DEFAULT, false);
9476 
9477   nsDocShell* docShell =
9478       static_cast<nsDocShell*>(GetPresContext()->GetDocShell());
9479   RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
9480   bool isTimelineRecording = timelines && timelines->HasConsumer(docShell);
9481 
9482   if (isTimelineRecording) {
9483     timelines->AddMarkerForDocShell(docShell, "Reflow",
9484                                     MarkerTracingType::START);
9485   }
9486 
9487   Maybe<uint64_t> innerWindowID;
9488   if (auto* window = mDocument->GetInnerWindow()) {
9489     innerWindowID = Some(window->WindowID());
9490   }
9491   AutoProfilerTracing tracingLayoutFlush(
9492       "Paint", "Reflow", geckoprofiler::category::LAYOUT,
9493       std::move(mReflowCause), innerWindowID);
9494   mReflowCause = nullptr;
9495 
9496   FlushPendingScrollAnchorSelections();
9497 
9498   if (mReflowContinueTimer) {
9499     mReflowContinueTimer->Cancel();
9500     mReflowContinueTimer = nullptr;
9501   }
9502 
9503   const bool isRoot = target == mFrameConstructor->GetRootFrame();
9504 
9505   MOZ_ASSERT(isRoot || aOverflowTracker,
9506              "caller must provide overflow tracker when reflowing "
9507              "non-root frames");
9508 
9509   // CreateReferenceRenderingContext can return nullptr
9510   RefPtr<gfxContext> rcx(CreateReferenceRenderingContext());
9511 
9512 #ifdef DEBUG
9513   mCurrentReflowRoot = target;
9514 #endif
9515 
9516   // If the target frame is the root of the frame hierarchy, then
9517   // use all the available space. If it's simply a `reflow root',
9518   // then use the target frame's size as the available space.
9519   WritingMode wm = target->GetWritingMode();
9520   LogicalSize size(wm);
9521   if (isRoot) {
9522     size = LogicalSize(wm, mPresContext->GetVisibleArea().Size());
9523   } else {
9524     size = target->GetLogicalSize();
9525   }
9526 
9527   OverflowAreas oldOverflow;  // initialized and used only when !isRoot
9528   if (!isRoot) {
9529     oldOverflow = target->GetOverflowAreas();
9530   }
9531 
9532   NS_ASSERTION(!target->GetNextInFlow() && !target->GetPrevInFlow(),
9533                "reflow roots should never split");
9534 
9535   // Don't pass size directly to the reflow input, since a
9536   // constrained height implies page/column breaking.
9537   LogicalSize reflowSize(wm, size.ISize(wm), NS_UNCONSTRAINEDSIZE);
9538   ReflowInput reflowInput(mPresContext, target, rcx, reflowSize,
9539                           ReflowInput::InitFlag::CallerWillInit);
9540   reflowInput.mOrthogonalLimit = size.BSize(wm);
9541 
9542   if (isRoot) {
9543     reflowInput.Init(mPresContext);
9544 
9545     // When the root frame is being reflowed with unconstrained block-size
9546     // (which happens when we're called from
9547     // nsDocumentViewer::SizeToContent), we're effectively doing a
9548     // resize in the block direction, since it changes the meaning of
9549     // percentage block-sizes even if no block-sizes actually changed.
9550     // The same applies when we reflow again after that computation. This is
9551     // an unusual case, and isn't caught by ReflowInput::InitResizeFlags.
9552     bool hasUnconstrainedBSize = size.BSize(wm) == NS_UNCONSTRAINEDSIZE;
9553 
9554     if (hasUnconstrainedBSize || mLastRootReflowHadUnconstrainedBSize) {
9555       reflowInput.SetBResize(true);
9556     }
9557 
9558     mLastRootReflowHadUnconstrainedBSize = hasUnconstrainedBSize;
9559   } else {
9560     // Initialize reflow input with current used border and padding,
9561     // in case this was set specially by the parent frame when the reflow root
9562     // was reflowed by its parent.
9563     reflowInput.Init(mPresContext, Nothing(),
9564                      Some(target->GetLogicalUsedBorder(wm)),
9565                      Some(target->GetLogicalUsedPadding(wm)));
9566   }
9567 
9568   // fix the computed height
9569   NS_ASSERTION(reflowInput.ComputedPhysicalMargin() == nsMargin(0, 0, 0, 0),
9570                "reflow input should not set margin for reflow roots");
9571   if (size.BSize(wm) != NS_UNCONSTRAINEDSIZE) {
9572     nscoord computedBSize =
9573         size.BSize(wm) -
9574         reflowInput.ComputedLogicalBorderPadding(wm).BStartEnd(wm);
9575     computedBSize = std::max(computedBSize, 0);
9576     reflowInput.SetComputedBSize(computedBSize);
9577   }
9578   NS_ASSERTION(
9579       reflowInput.ComputedISize() ==
9580           size.ISize(wm) -
9581               reflowInput.ComputedLogicalBorderPadding(wm).IStartEnd(wm),
9582       "reflow input computed incorrect inline size");
9583 
9584   mPresContext->ReflowStarted(aInterruptible);
9585   mIsReflowing = true;
9586 
9587   nsReflowStatus status;
9588   ReflowOutput desiredSize(reflowInput);
9589   target->Reflow(mPresContext, desiredSize, reflowInput, status);
9590 
9591   // If an incremental reflow is initiated at a frame other than the
9592   // root frame, then its desired size had better not change!  If it's
9593   // initiated at the root, then the size better not change unless its
9594   // height was unconstrained to start with.
9595   nsRect boundsRelativeToTarget =
9596       nsRect(0, 0, desiredSize.Width(), desiredSize.Height());
9597   NS_ASSERTION((isRoot && size.BSize(wm) == NS_UNCONSTRAINEDSIZE) ||
9598                    (desiredSize.ISize(wm) == size.ISize(wm) &&
9599                     desiredSize.BSize(wm) == size.BSize(wm)),
9600                "non-root frame's desired size changed during an "
9601                "incremental reflow");
9602   NS_ASSERTION(status.IsEmpty(), "reflow roots should never split");
9603 
9604   target->SetSize(boundsRelativeToTarget.Size());
9605 
9606   // Always use boundsRelativeToTarget here, not
9607   // desiredSize.InkOverflowRect(), because for root frames (where they
9608   // could be different, since root frames are allowed to have overflow) the
9609   // root view bounds need to match the viewport bounds; the view manager
9610   // "window dimensions" code depends on it.
9611   nsContainerFrame::SyncFrameViewAfterReflow(
9612       mPresContext, target, target->GetView(), boundsRelativeToTarget);
9613   nsContainerFrame::SyncWindowProperties(mPresContext, target,
9614                                          target->GetView(), rcx,
9615                                          nsContainerFrame::SET_ASYNC);
9616 
9617   target->DidReflow(mPresContext, nullptr);
9618   if (target->IsInScrollAnchorChain()) {
9619     ScrollAnchorContainer* container = ScrollAnchorContainer::FindFor(target);
9620     PostPendingScrollAnchorAdjustment(container);
9621   }
9622   if (isRoot && size.BSize(wm) == NS_UNCONSTRAINEDSIZE) {
9623     mPresContext->SetVisibleArea(boundsRelativeToTarget);
9624   }
9625 
9626 #ifdef DEBUG
9627   mCurrentReflowRoot = nullptr;
9628 #endif
9629 
9630   if (!isRoot && oldOverflow != target->GetOverflowAreas()) {
9631     // The overflow area changed.  Propagate this change to ancestors.
9632     aOverflowTracker->AddFrame(target->GetParent(),
9633                                OverflowChangedTracker::CHILDREN_CHANGED);
9634   }
9635 
9636   NS_ASSERTION(
9637       mPresContext->HasPendingInterrupt() || mFramesToDirty.Count() == 0,
9638       "Why do we need to dirty anything if not interrupted?");
9639 
9640   mIsReflowing = false;
9641   bool interrupted = mPresContext->HasPendingInterrupt();
9642   if (interrupted) {
9643     // Make sure target gets reflowed again.
9644     for (const auto& key : mFramesToDirty) {
9645       // Mark frames dirty until target frame.
9646       for (nsIFrame* f = key; f && !f->IsSubtreeDirty(); f = f->GetParent()) {
9647         f->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
9648         if (f->IsFlexItem()) {
9649           nsFlexContainerFrame::MarkCachedFlexMeasurementsDirty(f);
9650         }
9651 
9652         if (f == target) {
9653           break;
9654         }
9655       }
9656     }
9657 
9658     NS_ASSERTION(target->IsSubtreeDirty(), "Why is the target not dirty?");
9659     mDirtyRoots.Add(target);
9660     SetNeedLayoutFlush();
9661 
9662     // Clear mFramesToDirty after we've done the target->IsSubtreeDirty()
9663     // assertion so that if it fails it's easier to see what's going on.
9664 #ifdef NOISY_INTERRUPTIBLE_REFLOW
9665     printf("mFramesToDirty.Count() == %u\n", mFramesToDirty.Count());
9666 #endif /* NOISY_INTERRUPTIBLE_REFLOW */
9667     mFramesToDirty.Clear();
9668 
9669     // Any FlushPendingNotifications with interruptible reflows
9670     // should be suppressed now. We don't want to do extra reflow work
9671     // before our reflow event happens.
9672     mWasLastReflowInterrupted = true;
9673     MaybeScheduleReflow();
9674   }
9675 
9676   // dump text perf metrics for reflows with significant text processing
9677   if (tp) {
9678     if (tp->current.numChars > 100) {
9679       TimeDuration reflowTime = TimeStamp::Now() - timeStart;
9680       LogTextPerfStats(tp, this, tp->current, reflowTime.ToMilliseconds(),
9681                        eLog_reflow, nullptr);
9682     }
9683     tp->Accumulate();
9684   }
9685 
9686   if (isTimelineRecording) {
9687     timelines->AddMarkerForDocShell(docShell, "Reflow", MarkerTracingType::END);
9688   }
9689 
9690   return !interrupted;
9691 }
9692 
9693 #ifdef DEBUG
DoVerifyReflow()9694 void PresShell::DoVerifyReflow() {
9695   if (GetVerifyReflowEnable()) {
9696     // First synchronously render what we have so far so that we can
9697     // see it.
9698     nsView* rootView = mViewManager->GetRootView();
9699     mViewManager->InvalidateView(rootView);
9700 
9701     FlushPendingNotifications(FlushType::Layout);
9702     mInVerifyReflow = true;
9703     bool ok = VerifyIncrementalReflow();
9704     mInVerifyReflow = false;
9705     if (VerifyReflowFlags::All & gVerifyReflowFlags) {
9706       printf("ProcessReflowCommands: finished (%s)\n", ok ? "ok" : "failed");
9707     }
9708 
9709     if (!mDirtyRoots.IsEmpty()) {
9710       printf("XXX yikes! reflow commands queued during verify-reflow\n");
9711     }
9712   }
9713 }
9714 #endif
9715 
9716 // used with Telemetry metrics
9717 #define NS_LONG_REFLOW_TIME_MS 5000
9718 
ProcessReflowCommands(bool aInterruptible)9719 bool PresShell::ProcessReflowCommands(bool aInterruptible) {
9720   if (mDirtyRoots.IsEmpty() && !mShouldUnsuppressPainting) {
9721     // Nothing to do; bail out
9722     return true;
9723   }
9724 
9725   mozilla::TimeStamp timerStart = mozilla::TimeStamp::Now();
9726   bool interrupted = false;
9727   if (!mDirtyRoots.IsEmpty()) {
9728 #ifdef DEBUG
9729     if (VerifyReflowFlags::DumpCommands & gVerifyReflowFlags) {
9730       printf("ProcessReflowCommands: begin incremental reflow\n");
9731     }
9732 #endif
9733 
9734     // If reflow is interruptible, then make a note of our deadline.
9735     const PRIntervalTime deadline =
9736         aInterruptible
9737             ? PR_IntervalNow() + PR_MicrosecondsToInterval(gMaxRCProcessingTime)
9738             : (PRIntervalTime)0;
9739 
9740     // Scope for the reflow entry point
9741     {
9742       nsAutoScriptBlocker scriptBlocker;
9743       WillDoReflow();
9744       AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Reflow);
9745       nsViewManager::AutoDisableRefresh refreshBlocker(mViewManager);
9746 
9747       OverflowChangedTracker overflowTracker;
9748 
9749       do {
9750         // Send an incremental reflow notification to the target frame.
9751         nsIFrame* target = mDirtyRoots.PopShallowestRoot();
9752 
9753         if (!target->IsSubtreeDirty()) {
9754           // It's not dirty anymore, which probably means the notification
9755           // was posted in the middle of a reflow (perhaps with a reflow
9756           // root in the middle).  Don't do anything.
9757           continue;
9758         }
9759 
9760         interrupted = !DoReflow(target, aInterruptible, &overflowTracker);
9761 
9762         // Keep going until we're out of reflow commands, or we've run
9763         // past our deadline, or we're interrupted.
9764       } while (!interrupted && !mDirtyRoots.IsEmpty() &&
9765                (!aInterruptible || PR_IntervalNow() < deadline));
9766 
9767       interrupted = !mDirtyRoots.IsEmpty();
9768 
9769       overflowTracker.Flush();
9770 
9771       if (!interrupted) {
9772         // We didn't get interrupted. Go ahead and perform scroll anchor
9773         // adjustments.
9774         FlushPendingScrollAnchorAdjustments();
9775       }
9776     }
9777 
9778     // Exiting the scriptblocker might have killed us
9779     if (!mIsDestroying) {
9780       DidDoReflow(aInterruptible);
9781     }
9782 
9783     // DidDoReflow might have killed us
9784     if (!mIsDestroying) {
9785 #ifdef DEBUG
9786       if (VerifyReflowFlags::DumpCommands & gVerifyReflowFlags) {
9787         printf("\nPresShell::ProcessReflowCommands() finished: this=%p\n",
9788                (void*)this);
9789       }
9790       DoVerifyReflow();
9791 #endif
9792 
9793       // If any new reflow commands were enqueued during the reflow, schedule
9794       // another reflow event to process them.  Note that we want to do this
9795       // after DidDoReflow(), since that method can change whether there are
9796       // dirty roots around by flushing, and there's no point in posting a
9797       // reflow event just to have the flush revoke it.
9798       if (!mDirtyRoots.IsEmpty()) {
9799         MaybeScheduleReflow();
9800         // And record that we might need flushing
9801         SetNeedLayoutFlush();
9802       }
9803     }
9804   }
9805 
9806   if (!mIsDestroying && mShouldUnsuppressPainting && mDirtyRoots.IsEmpty()) {
9807     // We only unlock if we're out of reflows.  It's pointless
9808     // to unlock if reflows are still pending, since reflows
9809     // are just going to thrash the frames around some more.  By
9810     // waiting we avoid an overeager "jitter" effect.
9811     mShouldUnsuppressPainting = false;
9812     UnsuppressAndInvalidate();
9813   }
9814 
9815   if (mDocument->GetRootElement()) {
9816     TimeDuration elapsed = TimeStamp::Now() - timerStart;
9817     int32_t intElapsed = int32_t(elapsed.ToMilliseconds());
9818 
9819     if (intElapsed > NS_LONG_REFLOW_TIME_MS) {
9820       Telemetry::Accumulate(Telemetry::LONG_REFLOW_INTERRUPTIBLE,
9821                             aInterruptible ? 1 : 0);
9822     }
9823   }
9824 
9825   return !interrupted;
9826 }
9827 
WindowSizeMoveDone()9828 void PresShell::WindowSizeMoveDone() {
9829   if (mPresContext) {
9830     EventStateManager::ClearGlobalActiveContent(nullptr);
9831     ClearMouseCapture();
9832   }
9833 }
9834 
9835 NS_IMETHODIMP
Observe(nsISupports * aSubject,const char * aTopic,const char16_t * aData)9836 PresShell::Observe(nsISupports* aSubject, const char* aTopic,
9837                    const char16_t* aData) {
9838   if (mIsDestroying) {
9839     NS_WARNING("our observers should have been unregistered by now");
9840     return NS_OK;
9841   }
9842 
9843   if (!nsCRT::strcmp(aTopic, "memory-pressure")) {
9844     if (!AssumeAllFramesVisible() && mPresContext->IsRootContentDocument()) {
9845       DoUpdateApproximateFrameVisibility(/* aRemoveOnly = */ true);
9846     }
9847     return NS_OK;
9848   }
9849 
9850   if (!nsCRT::strcmp(aTopic, NS_WIDGET_WAKE_OBSERVER_TOPIC)) {
9851     mLastOSWake = TimeStamp::Now();
9852     return NS_OK;
9853   }
9854 
9855   // For parent process, user may expect the UI is interactable after a
9856   // tab (previously opened page or home page) has restored.
9857   if (!nsCRT::strcmp(aTopic, "sessionstore-one-or-no-tab-restored")) {
9858     MOZ_ASSERT(XRE_IsParentProcess());
9859     sProcessInteractable = true;
9860 
9861     nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
9862     if (os) {
9863       os->RemoveObserver(this, "sessionstore-one-or-no-tab-restored");
9864     }
9865     return NS_OK;
9866   }
9867 
9868   if (!nsCRT::strcmp(aTopic, "font-info-updated")) {
9869     mPresContext->ForceReflowForFontInfoUpdate();
9870     return NS_OK;
9871   }
9872 
9873   if (!nsCRT::strcmp(aTopic, "look-and-feel-changed")) {
9874     // See how LookAndFeel::NotifyChangedAllWindows encodes this.
9875     auto kind = widget::ThemeChangeKind(aData[0]);
9876     ThemeChanged(kind);
9877     return NS_OK;
9878   }
9879 
9880   NS_WARNING("unrecognized topic in PresShell::Observe");
9881   return NS_ERROR_FAILURE;
9882 }
9883 
AddRefreshObserver(nsARefreshObserver * aObserver,FlushType aFlushType,const char * aObserverDescription)9884 bool PresShell::AddRefreshObserver(nsARefreshObserver* aObserver,
9885                                    FlushType aFlushType,
9886                                    const char* aObserverDescription) {
9887   nsPresContext* presContext = GetPresContext();
9888   if (MOZ_UNLIKELY(!presContext)) {
9889     return false;
9890   }
9891   presContext->RefreshDriver()->AddRefreshObserver(aObserver, aFlushType,
9892                                                    aObserverDescription);
9893   return true;
9894 }
9895 
RemoveRefreshObserver(nsARefreshObserver * aObserver,FlushType aFlushType)9896 bool PresShell::RemoveRefreshObserver(nsARefreshObserver* aObserver,
9897                                       FlushType aFlushType) {
9898   nsPresContext* presContext = GetPresContext();
9899   return presContext && presContext->RefreshDriver()->RemoveRefreshObserver(
9900                             aObserver, aFlushType);
9901 }
9902 
AddPostRefreshObserver(nsAPostRefreshObserver * aObserver)9903 bool PresShell::AddPostRefreshObserver(nsAPostRefreshObserver* aObserver) {
9904   nsPresContext* presContext = GetPresContext();
9905   if (!presContext) {
9906     return false;
9907   }
9908   presContext->RefreshDriver()->AddPostRefreshObserver(aObserver);
9909   return true;
9910 }
9911 
RemovePostRefreshObserver(nsAPostRefreshObserver * aObserver)9912 bool PresShell::RemovePostRefreshObserver(nsAPostRefreshObserver* aObserver) {
9913   nsPresContext* presContext = GetPresContext();
9914   if (!presContext) {
9915     return false;
9916   }
9917   presContext->RefreshDriver()->RemovePostRefreshObserver(aObserver);
9918   return true;
9919 }
9920 
DoObserveStyleFlushes()9921 void PresShell::DoObserveStyleFlushes() {
9922   MOZ_ASSERT(!ObservingStyleFlushes());
9923   mObservingStyleFlushes = true;
9924 
9925   if (MOZ_LIKELY(!mDocument->GetBFCacheEntry())) {
9926     mPresContext->RefreshDriver()->AddStyleFlushObserver(this);
9927   }
9928 }
9929 
DoObserveLayoutFlushes()9930 void PresShell::DoObserveLayoutFlushes() {
9931   MOZ_ASSERT(!ObservingLayoutFlushes());
9932   mObservingLayoutFlushes = true;
9933 
9934   if (MOZ_LIKELY(!mDocument->GetBFCacheEntry())) {
9935     mPresContext->RefreshDriver()->AddLayoutFlushObserver(this);
9936   }
9937 }
9938 
9939 //------------------------------------------------------
9940 // End of protected and private methods on the PresShell
9941 //------------------------------------------------------
9942 
9943 //------------------------------------------------------------------
9944 //-- Delayed event Classes Impls
9945 //------------------------------------------------------------------
9946 
DelayedInputEvent()9947 PresShell::DelayedInputEvent::DelayedInputEvent()
9948     : DelayedEvent(), mEvent(nullptr) {}
9949 
~DelayedInputEvent()9950 PresShell::DelayedInputEvent::~DelayedInputEvent() { delete mEvent; }
9951 
Dispatch()9952 void PresShell::DelayedInputEvent::Dispatch() {
9953   if (!mEvent || !mEvent->mWidget) {
9954     return;
9955   }
9956   nsCOMPtr<nsIWidget> widget = mEvent->mWidget;
9957   nsEventStatus status;
9958   widget->DispatchEvent(mEvent, status);
9959 }
9960 
DelayedMouseEvent(WidgetMouseEvent * aEvent)9961 PresShell::DelayedMouseEvent::DelayedMouseEvent(WidgetMouseEvent* aEvent)
9962     : DelayedInputEvent() {
9963   MOZ_DIAGNOSTIC_ASSERT(aEvent->IsTrusted());
9964   WidgetMouseEvent* mouseEvent =
9965       new WidgetMouseEvent(true, aEvent->mMessage, aEvent->mWidget,
9966                            aEvent->mReason, aEvent->mContextMenuTrigger);
9967   mouseEvent->AssignMouseEventData(*aEvent, false);
9968   mEvent = mouseEvent;
9969 }
9970 
DelayedKeyEvent(WidgetKeyboardEvent * aEvent)9971 PresShell::DelayedKeyEvent::DelayedKeyEvent(WidgetKeyboardEvent* aEvent)
9972     : DelayedInputEvent() {
9973   MOZ_DIAGNOSTIC_ASSERT(aEvent->IsTrusted());
9974   WidgetKeyboardEvent* keyEvent =
9975       new WidgetKeyboardEvent(true, aEvent->mMessage, aEvent->mWidget);
9976   keyEvent->AssignKeyEventData(*aEvent, false);
9977   keyEvent->mFlags.mIsSynthesizedForTests =
9978       aEvent->mFlags.mIsSynthesizedForTests;
9979   keyEvent->mFlags.mIsSuppressedOrDelayed = true;
9980   mEvent = keyEvent;
9981 }
9982 
IsKeyPressEvent()9983 bool PresShell::DelayedKeyEvent::IsKeyPressEvent() {
9984   return mEvent->mMessage == eKeyPress;
9985 }
9986 
9987 // Start of DEBUG only code
9988 
9989 #ifdef DEBUG
9990 
LogVerifyMessage(nsIFrame * k1,nsIFrame * k2,const char * aMsg)9991 static void LogVerifyMessage(nsIFrame* k1, nsIFrame* k2, const char* aMsg) {
9992   nsAutoString n1, n2;
9993   if (k1) {
9994     k1->GetFrameName(n1);
9995   } else {
9996     n1.AssignLiteral(u"(null)");
9997   }
9998 
9999   if (k2) {
10000     k2->GetFrameName(n2);
10001   } else {
10002     n2.AssignLiteral(u"(null)");
10003   }
10004 
10005   printf("verifyreflow: %s %p != %s %p  %s\n",
10006          NS_LossyConvertUTF16toASCII(n1).get(), (void*)k1,
10007          NS_LossyConvertUTF16toASCII(n2).get(), (void*)k2, aMsg);
10008 }
10009 
LogVerifyMessage(nsIFrame * k1,nsIFrame * k2,const char * aMsg,const nsRect & r1,const nsRect & r2)10010 static void LogVerifyMessage(nsIFrame* k1, nsIFrame* k2, const char* aMsg,
10011                              const nsRect& r1, const nsRect& r2) {
10012   printf("VerifyReflow Error:\n");
10013   nsAutoString name;
10014 
10015   if (k1) {
10016     k1->GetFrameName(name);
10017     printf("  %s %p ", NS_LossyConvertUTF16toASCII(name).get(), (void*)k1);
10018   }
10019   printf("{%d, %d, %d, %d} != \n", r1.x, r1.y, r1.width, r1.height);
10020 
10021   if (k2) {
10022     k2->GetFrameName(name);
10023     printf("  %s %p ", NS_LossyConvertUTF16toASCII(name).get(), (void*)k2);
10024   }
10025   printf("{%d, %d, %d, %d}\n  %s\n", r2.x, r2.y, r2.width, r2.height, aMsg);
10026 }
10027 
LogVerifyMessage(nsIFrame * k1,nsIFrame * k2,const char * aMsg,const nsIntRect & r1,const nsIntRect & r2)10028 static void LogVerifyMessage(nsIFrame* k1, nsIFrame* k2, const char* aMsg,
10029                              const nsIntRect& r1, const nsIntRect& r2) {
10030   printf("VerifyReflow Error:\n");
10031   nsAutoString name;
10032 
10033   if (k1) {
10034     k1->GetFrameName(name);
10035     printf("  %s %p ", NS_LossyConvertUTF16toASCII(name).get(), (void*)k1);
10036   }
10037   printf("{%d, %d, %d, %d} != \n", r1.x, r1.y, r1.width, r1.height);
10038 
10039   if (k2) {
10040     k2->GetFrameName(name);
10041     printf("  %s %p ", NS_LossyConvertUTF16toASCII(name).get(), (void*)k2);
10042   }
10043   printf("{%d, %d, %d, %d}\n  %s\n", r2.x, r2.y, r2.width, r2.height, aMsg);
10044 }
10045 
CompareTrees(nsPresContext * aFirstPresContext,nsIFrame * aFirstFrame,nsPresContext * aSecondPresContext,nsIFrame * aSecondFrame)10046 static bool CompareTrees(nsPresContext* aFirstPresContext,
10047                          nsIFrame* aFirstFrame,
10048                          nsPresContext* aSecondPresContext,
10049                          nsIFrame* aSecondFrame) {
10050   if (!aFirstPresContext || !aFirstFrame || !aSecondPresContext ||
10051       !aSecondFrame)
10052     return true;
10053   // XXX Evil hack to reduce false positives; I can't seem to figure
10054   // out how to flush scrollbar changes correctly
10055   // if (aFirstFrame->IsScrollbarFrame())
10056   //  return true;
10057   bool ok = true;
10058   const auto& childLists1 = aFirstFrame->ChildLists();
10059   const auto& childLists2 = aSecondFrame->ChildLists();
10060   auto iterLists1 = childLists1.begin();
10061   auto iterLists2 = childLists2.begin();
10062   do {
10063     const nsFrameList& kids1 =
10064         iterLists1 != childLists1.end() ? iterLists1->mList : nsFrameList();
10065     const nsFrameList& kids2 =
10066         iterLists2 != childLists2.end() ? iterLists2->mList : nsFrameList();
10067     int32_t l1 = kids1.GetLength();
10068     int32_t l2 = kids2.GetLength();
10069     if (l1 != l2) {
10070       ok = false;
10071       LogVerifyMessage(kids1.FirstChild(), kids2.FirstChild(),
10072                        "child counts don't match: ");
10073       printf("%d != %d\n", l1, l2);
10074       if (!(VerifyReflowFlags::All & gVerifyReflowFlags)) {
10075         break;
10076       }
10077     }
10078 
10079     LayoutDeviceIntRect r1, r2;
10080     nsView* v1;
10081     nsView* v2;
10082     for (nsFrameList::Enumerator e1(kids1), e2(kids2);; e1.Next(), e2.Next()) {
10083       nsIFrame* k1 = e1.get();
10084       nsIFrame* k2 = e2.get();
10085       if (((nullptr == k1) && (nullptr != k2)) ||
10086           ((nullptr != k1) && (nullptr == k2))) {
10087         ok = false;
10088         LogVerifyMessage(k1, k2, "child lists are different\n");
10089         break;
10090       } else if (nullptr != k1) {
10091         // Verify that the frames are the same size
10092         if (!k1->GetRect().IsEqualInterior(k2->GetRect())) {
10093           ok = false;
10094           LogVerifyMessage(k1, k2, "(frame rects)", k1->GetRect(),
10095                            k2->GetRect());
10096         }
10097 
10098         // Make sure either both have views or neither have views; if they
10099         // do have views, make sure the views are the same size. If the
10100         // views have widgets, make sure they both do or neither does. If
10101         // they do, make sure the widgets are the same size.
10102         v1 = k1->GetView();
10103         v2 = k2->GetView();
10104         if (((nullptr == v1) && (nullptr != v2)) ||
10105             ((nullptr != v1) && (nullptr == v2))) {
10106           ok = false;
10107           LogVerifyMessage(k1, k2, "child views are not matched\n");
10108         } else if (nullptr != v1) {
10109           if (!v1->GetBounds().IsEqualInterior(v2->GetBounds())) {
10110             LogVerifyMessage(k1, k2, "(view rects)", v1->GetBounds(),
10111                              v2->GetBounds());
10112           }
10113 
10114           nsIWidget* w1 = v1->GetWidget();
10115           nsIWidget* w2 = v2->GetWidget();
10116           if (((nullptr == w1) && (nullptr != w2)) ||
10117               ((nullptr != w1) && (nullptr == w2))) {
10118             ok = false;
10119             LogVerifyMessage(k1, k2, "child widgets are not matched\n");
10120           } else if (nullptr != w1) {
10121             r1 = w1->GetBounds();
10122             r2 = w2->GetBounds();
10123             if (!r1.IsEqualEdges(r2)) {
10124               LogVerifyMessage(k1, k2, "(widget rects)", r1.ToUnknownRect(),
10125                                r2.ToUnknownRect());
10126             }
10127           }
10128         }
10129         if (!ok && !(VerifyReflowFlags::All & gVerifyReflowFlags)) {
10130           break;
10131         }
10132 
10133         // XXX Should perhaps compare their float managers.
10134 
10135         // Compare the sub-trees too
10136         if (!CompareTrees(aFirstPresContext, k1, aSecondPresContext, k2)) {
10137           ok = false;
10138           if (!(VerifyReflowFlags::All & gVerifyReflowFlags)) {
10139             break;
10140           }
10141         }
10142       } else {
10143         break;
10144       }
10145     }
10146     if (!ok && (!(VerifyReflowFlags::All & gVerifyReflowFlags))) {
10147       break;
10148     }
10149 
10150     ++iterLists1;
10151     ++iterLists2;
10152     const bool lists1Done = iterLists1 == childLists1.end();
10153     const bool lists2Done = iterLists2 == childLists2.end();
10154     if (lists1Done != lists2Done ||
10155         (!lists1Done && iterLists1->mID != iterLists2->mID)) {
10156       if (!(VerifyReflowFlags::All & gVerifyReflowFlags)) {
10157         ok = false;
10158       }
10159       LogVerifyMessage(kids1.FirstChild(), kids2.FirstChild(),
10160                        "child list names are not matched: ");
10161       fprintf(stdout, "%s != %s\n",
10162               !lists1Done ? ChildListName(iterLists1->mID) : "(null)",
10163               !lists2Done ? ChildListName(iterLists2->mID) : "(null)");
10164       break;
10165     }
10166   } while (ok && iterLists1 != childLists1.end());
10167 
10168   return ok;
10169 }
10170 #endif
10171 
10172 #if 0
10173 static nsIFrame*
10174 FindTopFrame(nsIFrame* aRoot)
10175 {
10176   if (aRoot) {
10177     nsIContent* content = aRoot->GetContent();
10178     if (content) {
10179       nsAtom* tag;
10180       content->GetTag(tag);
10181       if (nullptr != tag) {
10182         NS_RELEASE(tag);
10183         return aRoot;
10184       }
10185     }
10186 
10187     // Try one of the children
10188     for (nsIFrame* kid : aRoot->PrincipalChildList()) {
10189       nsIFrame* result = FindTopFrame(kid);
10190       if (nullptr != result) {
10191         return result;
10192       }
10193     }
10194   }
10195   return nullptr;
10196 }
10197 #endif
10198 
10199 #ifdef DEBUG
10200 
10201 // After an incremental reflow, we verify the correctness by doing a
10202 // full reflow into a fresh frame tree.
VerifyIncrementalReflow()10203 bool PresShell::VerifyIncrementalReflow() {
10204   if (VerifyReflowFlags::Noisy & gVerifyReflowFlags) {
10205     printf("Building Verification Tree...\n");
10206   }
10207 
10208   // Create a presentation context to view the new frame tree
10209   RefPtr<nsPresContext> cx = new nsRootPresContext(
10210       mDocument, mPresContext->IsPaginated()
10211                      ? nsPresContext::eContext_PrintPreview
10212                      : nsPresContext::eContext_Galley);
10213   NS_ENSURE_TRUE(cx, false);
10214 
10215   nsDeviceContext* dc = mPresContext->DeviceContext();
10216   nsresult rv = cx->Init(dc);
10217   NS_ENSURE_SUCCESS(rv, false);
10218 
10219   // Get our scrolling preference
10220   nsView* rootView = mViewManager->GetRootView();
10221   NS_ENSURE_TRUE(rootView->HasWidget(), false);
10222   nsIWidget* parentWidget = rootView->GetWidget();
10223 
10224   // Create a new view manager.
10225   RefPtr<nsViewManager> vm = new nsViewManager();
10226   NS_ENSURE_TRUE(vm, false);
10227   rv = vm->Init(dc);
10228   NS_ENSURE_SUCCESS(rv, false);
10229 
10230   // Create a child window of the parent that is our "root view/window"
10231   // Create a view
10232   nsRect tbounds = mPresContext->GetVisibleArea();
10233   nsView* view = vm->CreateView(tbounds, nullptr);
10234   NS_ENSURE_TRUE(view, false);
10235 
10236   // now create the widget for the view
10237   rv = view->CreateWidgetForParent(parentWidget, nullptr, true);
10238   NS_ENSURE_SUCCESS(rv, false);
10239 
10240   // Setup hierarchical relationship in view manager
10241   vm->SetRootView(view);
10242 
10243   // Make the new presentation context the same size as our
10244   // presentation context.
10245   cx->SetVisibleArea(mPresContext->GetVisibleArea());
10246 
10247   RefPtr<PresShell> presShell = mDocument->CreatePresShell(cx, vm);
10248   NS_ENSURE_TRUE(presShell, false);
10249 
10250   // Note that after we create the shell, we must make sure to destroy it
10251   presShell->SetVerifyReflowEnable(
10252       false);  // turn off verify reflow while we're
10253                // reflowing the test frame tree
10254   vm->SetPresShell(presShell);
10255   {
10256     nsAutoCauseReflowNotifier crNotifier(this);
10257     presShell->Initialize();
10258   }
10259   presShell->FlushPendingNotifications(FlushType::Layout);
10260   presShell->SetVerifyReflowEnable(
10261       true);  // turn on verify reflow again now that
10262               // we're done reflowing the test frame tree
10263   // Force the non-primary presshell to unsuppress; it doesn't want to normally
10264   // because it thinks it's hidden
10265   presShell->mPaintingSuppressed = false;
10266   if (VerifyReflowFlags::Noisy & gVerifyReflowFlags) {
10267     printf("Verification Tree built, comparing...\n");
10268   }
10269 
10270   // Now that the document has been reflowed, use its frame tree to
10271   // compare against our frame tree.
10272   nsIFrame* root1 = mFrameConstructor->GetRootFrame();
10273   nsIFrame* root2 = presShell->GetRootFrame();
10274   bool ok = CompareTrees(mPresContext, root1, cx, root2);
10275   if (!ok && (VerifyReflowFlags::Noisy & gVerifyReflowFlags)) {
10276     printf("Verify reflow failed, primary tree:\n");
10277     root1->List(stdout);
10278     printf("Verification tree:\n");
10279     root2->List(stdout);
10280   }
10281 
10282 #  if 0
10283   // Sample code for dumping page to png
10284   // XXX Needs to be made more flexible
10285   if (!ok) {
10286     nsString stra;
10287     static int num = 0;
10288     stra.AppendLiteral("C:\\mozilla\\mozilla\\debug\\filea");
10289     stra.AppendInt(num);
10290     stra.AppendLiteral(".png");
10291     gfxUtils::WriteAsPNG(presShell, stra);
10292     nsString strb;
10293     strb.AppendLiteral("C:\\mozilla\\mozilla\\debug\\fileb");
10294     strb.AppendInt(num);
10295     strb.AppendLiteral(".png");
10296     gfxUtils::WriteAsPNG(presShell, strb);
10297     ++num;
10298   }
10299 #  endif
10300 
10301   presShell->EndObservingDocument();
10302   presShell->Destroy();
10303   if (VerifyReflowFlags::Noisy & gVerifyReflowFlags) {
10304     printf("Finished Verifying Reflow...\n");
10305   }
10306 
10307   return ok;
10308 }
10309 
10310 // Layout debugging hooks
ListComputedStyles(FILE * out,int32_t aIndent)10311 void PresShell::ListComputedStyles(FILE* out, int32_t aIndent) {
10312   nsIFrame* rootFrame = GetRootFrame();
10313   if (rootFrame) {
10314     rootFrame->Style()->List(out, aIndent);
10315   }
10316 
10317   // The root element's frame's ComputedStyle is the root of a separate tree.
10318   Element* rootElement = mDocument->GetRootElement();
10319   if (rootElement) {
10320     nsIFrame* rootElementFrame = rootElement->GetPrimaryFrame();
10321     if (rootElementFrame) {
10322       rootElementFrame->Style()->List(out, aIndent);
10323     }
10324   }
10325 }
10326 #endif
10327 
10328 #if defined(DEBUG) || defined(MOZ_LAYOUT_DEBUGGER)
ListStyleSheets(FILE * out,int32_t aIndent)10329 void PresShell::ListStyleSheets(FILE* out, int32_t aIndent) {
10330   auto ListStyleSheetsAtOrigin = [this, out, aIndent](StyleOrigin origin) {
10331     int32_t sheetCount = StyleSet()->SheetCount(origin);
10332     for (int32_t i = 0; i < sheetCount; ++i) {
10333       StyleSet()->SheetAt(origin, i)->List(out, aIndent);
10334     }
10335   };
10336 
10337   ListStyleSheetsAtOrigin(StyleOrigin::UserAgent);
10338   ListStyleSheetsAtOrigin(StyleOrigin::User);
10339   ListStyleSheetsAtOrigin(StyleOrigin::Author);
10340 }
10341 #endif
10342 
10343 //=============================================================
10344 //=============================================================
10345 //-- Debug Reflow Counts
10346 //=============================================================
10347 //=============================================================
10348 #ifdef MOZ_REFLOW_PERF
10349 //-------------------------------------------------------------
DumpReflows()10350 void PresShell::DumpReflows() {
10351   if (mReflowCountMgr) {
10352     nsAutoCString uriStr;
10353     if (mDocument) {
10354       nsIURI* uri = mDocument->GetDocumentURI();
10355       if (uri) {
10356         uri->GetPathQueryRef(uriStr);
10357       }
10358     }
10359     mReflowCountMgr->DisplayTotals(uriStr.get());
10360     mReflowCountMgr->DisplayHTMLTotals(uriStr.get());
10361     mReflowCountMgr->DisplayDiffsInTotals();
10362   }
10363 }
10364 
10365 //-------------------------------------------------------------
CountReflows(const char * aName,nsIFrame * aFrame)10366 void PresShell::CountReflows(const char* aName, nsIFrame* aFrame) {
10367   if (mReflowCountMgr) {
10368     mReflowCountMgr->Add(aName, aFrame);
10369   }
10370 }
10371 
10372 //-------------------------------------------------------------
PaintCount(const char * aName,gfxContext * aRenderingContext,nsPresContext * aPresContext,nsIFrame * aFrame,const nsPoint & aOffset,uint32_t aColor)10373 void PresShell::PaintCount(const char* aName, gfxContext* aRenderingContext,
10374                            nsPresContext* aPresContext, nsIFrame* aFrame,
10375                            const nsPoint& aOffset, uint32_t aColor) {
10376   if (mReflowCountMgr) {
10377     mReflowCountMgr->PaintCount(aName, aRenderingContext, aPresContext, aFrame,
10378                                 aOffset, aColor);
10379   }
10380 }
10381 
10382 //-------------------------------------------------------------
SetPaintFrameCount(bool aPaintFrameCounts)10383 void PresShell::SetPaintFrameCount(bool aPaintFrameCounts) {
10384   if (mReflowCountMgr) {
10385     mReflowCountMgr->SetPaintFrameCounts(aPaintFrameCounts);
10386   }
10387 }
10388 
IsPaintingFrameCounts()10389 bool PresShell::IsPaintingFrameCounts() {
10390   if (mReflowCountMgr) return mReflowCountMgr->IsPaintingFrameCounts();
10391   return false;
10392 }
10393 
10394 //------------------------------------------------------------------
10395 //-- Reflow Counter Classes Impls
10396 //------------------------------------------------------------------
10397 
10398 //------------------------------------------------------------------
ReflowCounter(ReflowCountMgr * aMgr)10399 ReflowCounter::ReflowCounter(ReflowCountMgr* aMgr) : mMgr(aMgr) {
10400   ClearTotals();
10401   SetTotalsCache();
10402 }
10403 
10404 //------------------------------------------------------------------
10405 ReflowCounter::~ReflowCounter() = default;
10406 
10407 //------------------------------------------------------------------
ClearTotals()10408 void ReflowCounter::ClearTotals() { mTotal = 0; }
10409 
10410 //------------------------------------------------------------------
SetTotalsCache()10411 void ReflowCounter::SetTotalsCache() { mCacheTotal = mTotal; }
10412 
10413 //------------------------------------------------------------------
CalcDiffInTotals()10414 void ReflowCounter::CalcDiffInTotals() { mCacheTotal = mTotal - mCacheTotal; }
10415 
10416 //------------------------------------------------------------------
DisplayTotals(const char * aStr)10417 void ReflowCounter::DisplayTotals(const char* aStr) {
10418   DisplayTotals(mTotal, aStr ? aStr : "Totals");
10419 }
10420 
10421 //------------------------------------------------------------------
DisplayDiffTotals(const char * aStr)10422 void ReflowCounter::DisplayDiffTotals(const char* aStr) {
10423   DisplayTotals(mCacheTotal, aStr ? aStr : "Diff Totals");
10424 }
10425 
10426 //------------------------------------------------------------------
DisplayHTMLTotals(const char * aStr)10427 void ReflowCounter::DisplayHTMLTotals(const char* aStr) {
10428   DisplayHTMLTotals(mTotal, aStr ? aStr : "Totals");
10429 }
10430 
10431 //------------------------------------------------------------------
DisplayTotals(uint32_t aTotal,const char * aTitle)10432 void ReflowCounter::DisplayTotals(uint32_t aTotal, const char* aTitle) {
10433   // figure total
10434   if (aTotal == 0) {
10435     return;
10436   }
10437   ReflowCounter* gTots = (ReflowCounter*)mMgr->LookUp(kGrandTotalsStr);
10438 
10439   printf("%25s\t", aTitle);
10440   printf("%d\t", aTotal);
10441   if (gTots != this && aTotal > 0) {
10442     gTots->Add(aTotal);
10443   }
10444 }
10445 
10446 //------------------------------------------------------------------
DisplayHTMLTotals(uint32_t aTotal,const char * aTitle)10447 void ReflowCounter::DisplayHTMLTotals(uint32_t aTotal, const char* aTitle) {
10448   if (aTotal == 0) {
10449     return;
10450   }
10451 
10452   ReflowCounter* gTots = (ReflowCounter*)mMgr->LookUp(kGrandTotalsStr);
10453   FILE* fd = mMgr->GetOutFile();
10454   if (!fd) {
10455     return;
10456   }
10457 
10458   fprintf(fd, "<tr><td><center>%s</center></td>", aTitle);
10459   fprintf(fd, "<td><center>%d</center></td></tr>\n", aTotal);
10460 
10461   if (gTots != this && aTotal > 0) {
10462     gTots->Add(aTotal);
10463   }
10464 }
10465 
10466 //------------------------------------------------------------------
10467 //-- ReflowCountMgr
10468 //------------------------------------------------------------------
10469 
10470 #  define KEY_BUF_SIZE_FOR_PTR \
10471     24  // adequate char[] buffer to sprintf a pointer
10472 
ReflowCountMgr()10473 ReflowCountMgr::ReflowCountMgr() : mCounts(10), mIndiFrameCounts(10) {
10474   mCycledOnce = false;
10475   mDumpFrameCounts = false;
10476   mDumpFrameByFrameCounts = false;
10477   mPaintFrameByFrameCounts = false;
10478 }
10479 
10480 //------------------------------------------------------------------
10481 ReflowCountMgr::~ReflowCountMgr() = default;
10482 
10483 //------------------------------------------------------------------
LookUp(const char * aName)10484 ReflowCounter* ReflowCountMgr::LookUp(const char* aName) {
10485   return mCounts.Get(aName);
10486 }
10487 
10488 //------------------------------------------------------------------
Add(const char * aName,nsIFrame * aFrame)10489 void ReflowCountMgr::Add(const char* aName, nsIFrame* aFrame) {
10490   NS_ASSERTION(aName != nullptr, "Name shouldn't be null!");
10491 
10492   if (mDumpFrameCounts) {
10493     auto* const counter = mCounts.GetOrInsertNew(aName, this);
10494     counter->Add();
10495   }
10496 
10497   if ((mDumpFrameByFrameCounts || mPaintFrameByFrameCounts) &&
10498       aFrame != nullptr) {
10499     char key[KEY_BUF_SIZE_FOR_PTR];
10500     SprintfLiteral(key, "%p", (void*)aFrame);
10501     auto* const counter =
10502         mIndiFrameCounts
10503             .LookupOrInsertWith(key,
10504                                 [&aName, &aFrame, this]() {
10505                                   auto counter =
10506                                       MakeUnique<IndiReflowCounter>(this);
10507                                   counter->mFrame = aFrame;
10508                                   counter->mName.AssignASCII(aName);
10509                                   return counter;
10510                                 })
10511             .get();
10512     // this eliminates extra counts from super classes
10513     if (counter && counter->mName.EqualsASCII(aName)) {
10514       counter->mCount++;
10515       counter->mCounter.Add(1);
10516     }
10517   }
10518 }
10519 
10520 //------------------------------------------------------------------
PaintCount(const char * aName,gfxContext * aRenderingContext,nsPresContext * aPresContext,nsIFrame * aFrame,const nsPoint & aOffset,uint32_t aColor)10521 void ReflowCountMgr::PaintCount(const char* aName,
10522                                 gfxContext* aRenderingContext,
10523                                 nsPresContext* aPresContext, nsIFrame* aFrame,
10524                                 const nsPoint& aOffset, uint32_t aColor) {
10525   if (mPaintFrameByFrameCounts && aFrame != nullptr) {
10526     char key[KEY_BUF_SIZE_FOR_PTR];
10527     SprintfLiteral(key, "%p", (void*)aFrame);
10528     IndiReflowCounter* counter = mIndiFrameCounts.Get(key);
10529     if (counter != nullptr && counter->mName.EqualsASCII(aName)) {
10530       DrawTarget* drawTarget = aRenderingContext->GetDrawTarget();
10531       int32_t appUnitsPerDevPixel = aPresContext->AppUnitsPerDevPixel();
10532 
10533       aRenderingContext->Save();
10534       gfxPoint devPixelOffset =
10535           nsLayoutUtils::PointToGfxPoint(aOffset, appUnitsPerDevPixel);
10536       aRenderingContext->SetMatrixDouble(
10537           aRenderingContext->CurrentMatrixDouble().PreTranslate(
10538               devPixelOffset));
10539 
10540       // We don't care about the document language or user fonts here;
10541       // just get a default Latin font.
10542       nsFont font(StyleGenericFontFamily::Serif, Length::FromPixels(11));
10543       nsFontMetrics::Params params;
10544       params.language = nsGkAtoms::x_western;
10545       params.textPerf = aPresContext->GetTextPerfMetrics();
10546       params.fontStats = nullptr;  // not interested here
10547       params.featureValueLookup = aPresContext->GetFontFeatureValuesLookup();
10548       RefPtr<nsFontMetrics> fm =
10549           aPresContext->DeviceContext()->GetMetricsFor(font, params);
10550 
10551       char buf[16];
10552       int len = SprintfLiteral(buf, "%d", counter->mCount);
10553       nscoord x = 0, y = fm->MaxAscent();
10554       nscoord width, height = fm->MaxHeight();
10555       fm->SetTextRunRTL(false);
10556       width = fm->GetWidth(buf, len, drawTarget);
10557 
10558       sRGBColor color;
10559       sRGBColor color2;
10560       if (aColor != 0) {
10561         color = sRGBColor::FromABGR(aColor);
10562         color2 = sRGBColor(0.f, 0.f, 0.f);
10563       } else {
10564         gfx::Float rc = 0.f, gc = 0.f, bc = 0.f;
10565         if (counter->mCount < 5) {
10566           rc = 1.f;
10567           gc = 1.f;
10568         } else if (counter->mCount < 11) {
10569           gc = 1.f;
10570         } else {
10571           rc = 1.f;
10572         }
10573         color = sRGBColor(rc, gc, bc);
10574         color2 = sRGBColor(rc / 2, gc / 2, bc / 2);
10575       }
10576 
10577       nsRect rect(0, 0, width + 15, height + 15);
10578       Rect devPxRect =
10579           NSRectToSnappedRect(rect, appUnitsPerDevPixel, *drawTarget);
10580       ColorPattern black(ToDeviceColor(sRGBColor::OpaqueBlack()));
10581       drawTarget->FillRect(devPxRect, black);
10582 
10583       aRenderingContext->SetColor(color2);
10584       fm->DrawString(buf, len, x + 15, y + 15, aRenderingContext);
10585       aRenderingContext->SetColor(color);
10586       fm->DrawString(buf, len, x, y, aRenderingContext);
10587 
10588       aRenderingContext->Restore();
10589     }
10590   }
10591 }
10592 
10593 //------------------------------------------------------------------
DoGrandTotals()10594 void ReflowCountMgr::DoGrandTotals() {
10595   mCounts.WithEntryHandle(kGrandTotalsStr, [this](auto&& entry) {
10596     if (!entry) {
10597       entry.Insert(MakeUnique<ReflowCounter>(this));
10598     } else {
10599       entry.Data()->ClearTotals();
10600     }
10601   });
10602 
10603   printf("\t\t\t\tTotal\n");
10604   for (uint32_t i = 0; i < 78; i++) {
10605     printf("-");
10606   }
10607   printf("\n");
10608   for (const auto& entry : mCounts) {
10609     entry.GetData()->DisplayTotals(entry.GetKey());
10610   }
10611 }
10612 
RecurseIndiTotals(nsPresContext * aPresContext,nsClassHashtable<nsCharPtrHashKey,IndiReflowCounter> & aHT,nsIFrame * aParentFrame,int32_t aLevel)10613 static void RecurseIndiTotals(
10614     nsPresContext* aPresContext,
10615     nsClassHashtable<nsCharPtrHashKey, IndiReflowCounter>& aHT,
10616     nsIFrame* aParentFrame, int32_t aLevel) {
10617   if (aParentFrame == nullptr) {
10618     return;
10619   }
10620 
10621   char key[KEY_BUF_SIZE_FOR_PTR];
10622   SprintfLiteral(key, "%p", (void*)aParentFrame);
10623   IndiReflowCounter* counter = aHT.Get(key);
10624   if (counter) {
10625     counter->mHasBeenOutput = true;
10626     char* name = ToNewCString(counter->mName);
10627     for (int32_t i = 0; i < aLevel; i++) printf(" ");
10628     printf("%s - %p   [%d][", name, (void*)aParentFrame, counter->mCount);
10629     printf("%d", counter->mCounter.GetTotal());
10630     printf("]\n");
10631     free(name);
10632   }
10633 
10634   for (nsIFrame* child : aParentFrame->PrincipalChildList()) {
10635     RecurseIndiTotals(aPresContext, aHT, child, aLevel + 1);
10636   }
10637 }
10638 
10639 //------------------------------------------------------------------
DoIndiTotalsTree()10640 void ReflowCountMgr::DoIndiTotalsTree() {
10641   printf("\n------------------------------------------------\n");
10642   printf("-- Individual Frame Counts\n");
10643   printf("------------------------------------------------\n");
10644 
10645   if (mPresShell) {
10646     nsIFrame* rootFrame = mPresShell->GetRootFrame();
10647     RecurseIndiTotals(mPresContext, mIndiFrameCounts, rootFrame, 0);
10648     printf("------------------------------------------------\n");
10649     printf("-- Individual Counts of Frames not in Root Tree\n");
10650     printf("------------------------------------------------\n");
10651     for (const auto& counter : mIndiFrameCounts.Values()) {
10652       if (!counter->mHasBeenOutput) {
10653         char* name = ToNewCString(counter->mName);
10654         printf("%s - %p   [%d][", name, (void*)counter->mFrame,
10655                counter->mCount);
10656         printf("%d", counter->mCounter.GetTotal());
10657         printf("]\n");
10658         free(name);
10659       }
10660     }
10661   }
10662 }
10663 
10664 //------------------------------------------------------------------
DoGrandHTMLTotals()10665 void ReflowCountMgr::DoGrandHTMLTotals() {
10666   mCounts.WithEntryHandle(kGrandTotalsStr, [this](auto&& entry) {
10667     if (!entry) {
10668       entry.Insert(MakeUnique<ReflowCounter>(this));
10669     } else {
10670       entry.Data()->ClearTotals();
10671     }
10672   });
10673 
10674   static const char* title[] = {"Class", "Reflows"};
10675   fprintf(mFD, "<tr>");
10676   for (uint32_t i = 0; i < ArrayLength(title); i++) {
10677     fprintf(mFD, "<td><center><b>%s<b></center></td>", title[i]);
10678   }
10679   fprintf(mFD, "</tr>\n");
10680 
10681   for (const auto& entry : mCounts) {
10682     entry.GetData()->DisplayHTMLTotals(entry.GetKey());
10683   }
10684 }
10685 
10686 //------------------------------------
DisplayTotals(const char * aStr)10687 void ReflowCountMgr::DisplayTotals(const char* aStr) {
10688 #  ifdef DEBUG_rods
10689   printf("%s\n", aStr ? aStr : "No name");
10690 #  endif
10691   if (mDumpFrameCounts) {
10692     DoGrandTotals();
10693   }
10694   if (mDumpFrameByFrameCounts) {
10695     DoIndiTotalsTree();
10696   }
10697 }
10698 //------------------------------------
DisplayHTMLTotals(const char * aStr)10699 void ReflowCountMgr::DisplayHTMLTotals(const char* aStr) {
10700 #  ifdef WIN32x  // XXX NOT XP!
10701   char name[1024];
10702 
10703   char* sptr = strrchr(aStr, '/');
10704   if (sptr) {
10705     sptr++;
10706     strcpy(name, sptr);
10707     char* eptr = strrchr(name, '.');
10708     if (eptr) {
10709       *eptr = 0;
10710     }
10711     strcat(name, "_stats.html");
10712   }
10713   mFD = fopen(name, "w");
10714   if (mFD) {
10715     fprintf(mFD, "<html><head><title>Reflow Stats</title></head><body>\n");
10716     const char* title = aStr ? aStr : "No name";
10717     fprintf(mFD,
10718             "<center><b>%s</b><br><table border=1 "
10719             "style=\"background-color:#e0e0e0\">",
10720             title);
10721     DoGrandHTMLTotals();
10722     fprintf(mFD, "</center></table>\n");
10723     fprintf(mFD, "</body></html>\n");
10724     fclose(mFD);
10725     mFD = nullptr;
10726   }
10727 #  endif  // not XP!
10728 }
10729 
10730 //------------------------------------------------------------------
ClearTotals()10731 void ReflowCountMgr::ClearTotals() {
10732   for (const auto& data : mCounts.Values()) {
10733     data->ClearTotals();
10734   }
10735 }
10736 
10737 //------------------------------------------------------------------
ClearGrandTotals()10738 void ReflowCountMgr::ClearGrandTotals() {
10739   mCounts.WithEntryHandle(kGrandTotalsStr, [&](auto&& entry) {
10740     if (!entry) {
10741       entry.Insert(MakeUnique<ReflowCounter>(this));
10742     } else {
10743       entry.Data()->ClearTotals();
10744       entry.Data()->SetTotalsCache();
10745     }
10746   });
10747 }
10748 
10749 //------------------------------------------------------------------
DisplayDiffsInTotals()10750 void ReflowCountMgr::DisplayDiffsInTotals() {
10751   if (mCycledOnce) {
10752     printf("Differences\n");
10753     for (int32_t i = 0; i < 78; i++) {
10754       printf("-");
10755     }
10756     printf("\n");
10757     ClearGrandTotals();
10758   }
10759 
10760   for (const auto& entry : mCounts) {
10761     if (mCycledOnce) {
10762       entry.GetData()->CalcDiffInTotals();
10763       entry.GetData()->DisplayDiffTotals(entry.GetKey());
10764     }
10765     entry.GetData()->SetTotalsCache();
10766   }
10767 
10768   mCycledOnce = true;
10769 }
10770 
10771 #endif  // MOZ_REFLOW_PERF
10772 
GetAbsoluteContainingBlock(nsIFrame * aFrame)10773 nsIFrame* PresShell::GetAbsoluteContainingBlock(nsIFrame* aFrame) {
10774   return FrameConstructor()->GetAbsoluteContainingBlock(
10775       aFrame, nsCSSFrameConstructor::ABS_POS);
10776 }
10777 
10778 #ifdef ACCESSIBILITY
10779 
10780 // static
IsAccessibilityActive()10781 bool PresShell::IsAccessibilityActive() { return GetAccService() != nullptr; }
10782 
10783 // static
GetAccessibilityService()10784 nsAccessibilityService* PresShell::GetAccessibilityService() {
10785   return GetAccService();
10786 }
10787 
10788 #endif  // #ifdef ACCESSIBILITY
10789 
ActivenessMaybeChanged()10790 void PresShell::ActivenessMaybeChanged() {
10791   if (!mDocument) {
10792     return;
10793   }
10794   SetIsActive(ShouldBeActive());
10795 }
10796 
ShouldBeActive() const10797 bool PresShell::ShouldBeActive() const {
10798   MOZ_LOG(gLog, LogLevel::Debug,
10799           ("PresShell::ShouldBeActive(%s, %d)\n",
10800            mDocument->GetDocumentURI()
10801                ? mDocument->GetDocumentURI()->GetSpecOrDefault().get()
10802                : "(no uri)",
10803            mIsActive));
10804 
10805   Document* doc = mDocument;
10806 
10807   if (doc->IsBeingUsedAsImage()) {
10808     // Documents used as an image can remain active. They do not tick their
10809     // refresh driver if not painted, and they can't run script or such so they
10810     // can't really observe much else.
10811     return true;
10812   }
10813 
10814   if (Document* displayDoc = doc->GetDisplayDocument()) {
10815     // Ok, we're an external resource document -- we need to use our display
10816     // document's docshell to determine "IsActive" status, since we lack
10817     // a browsing context of our own.
10818     MOZ_ASSERT(!doc->GetBrowsingContext(),
10819                "external resource doc shouldn't have its own BC");
10820     doc = displayDoc;
10821   }
10822 
10823   Document* root = nsContentUtils::GetInProcessSubtreeRootDocument(doc);
10824   if (auto* browserChild = BrowserChild::GetFrom(root->GetDocShell())) {
10825     // We might want to activate a tab even though the browsing-context is not
10826     // active if the BrowserChild is considered visible. This serves two
10827     // purposes:
10828     //
10829     //  * For top-level tabs, we use this for tab warming. The browsing-context
10830     //    might still be inactive, but we want to activate the pres shell and
10831     //    the refresh driver.
10832     //
10833     //  * For oop iframes, we do want to throttle them if they're not visible.
10834     //
10835     // TODO(emilio): Consider unifying the in-process vs. fission iframe
10836     // throttling code (in-process throttling for non-visible iframes lives
10837     // right now in Document::ShouldThrottleFrameRequests(), but that only
10838     // throttles rAF).
10839     if (!browserChild->IsVisible()) {
10840       MOZ_LOG(gLog, LogLevel::Debug,
10841               (" > BrowserChild %p is not visible", browserChild));
10842       return false;
10843     }
10844 
10845     // If the browser is visible but just due to be preserving layers
10846     // artificially, we do want to fall back to the browsing context activeness
10847     // instead. Otherwise we do want to be active for the use cases above.
10848     if (!browserChild->IsPreservingLayers()) {
10849       MOZ_LOG(gLog, LogLevel::Debug,
10850               (" > BrowserChild %p is visible and not preserving layers",
10851                browserChild));
10852       return true;
10853     }
10854     MOZ_LOG(
10855         gLog, LogLevel::Debug,
10856         (" > BrowserChild %p is visible and preserving layers", browserChild));
10857   }
10858 
10859   BrowsingContext* bc = doc->GetBrowsingContext();
10860   MOZ_LOG(gLog, LogLevel::Debug,
10861           (" > BrowsingContext %p  active: %d", bc, bc && bc->IsActive()));
10862   return bc && bc->IsActive();
10863 }
10864 
SetIsActive(bool aIsActive)10865 void PresShell::SetIsActive(bool aIsActive) {
10866   MOZ_ASSERT(mDocument, "should only be called with a document");
10867 
10868   const bool changed = mIsActive != aIsActive;
10869 
10870   mIsActive = aIsActive;
10871 
10872   nsPresContext* presContext = GetPresContext();
10873   if (presContext &&
10874       presContext->RefreshDriver()->GetPresContext() == presContext) {
10875     presContext->RefreshDriver()->SetThrottled(!mIsActive);
10876   }
10877 
10878   if (changed) {
10879     // Propagate state-change to my resource documents' PresShells and other
10880     // subdocuments.
10881     //
10882     // Note that it is fine to not propagate to fission iframes. Those will
10883     // become active / inactive as needed as a result of they getting painted /
10884     // not painted eventually.
10885     auto recurse = [aIsActive](Document& aSubDoc) {
10886       if (PresShell* presShell = aSubDoc.GetPresShell()) {
10887         presShell->SetIsActive(aIsActive);
10888       }
10889       return CallState::Continue;
10890     };
10891     mDocument->EnumerateExternalResources(recurse);
10892     mDocument->EnumerateSubDocuments(recurse);
10893   }
10894 
10895   UpdateImageLockingState();
10896 #ifdef ACCESSIBILITY
10897   if (aIsActive) {
10898     if (nsAccessibilityService* accService =
10899             PresShell::GetAccessibilityService()) {
10900       accService->PresShellActivated(this);
10901     }
10902   }
10903 #endif  // #ifdef ACCESSIBILITY
10904 
10905 #if defined(MOZ_WIDGET_ANDROID)
10906   if (changed && !aIsActive && presContext &&
10907       presContext->IsRootContentDocumentCrossProcess()) {
10908     if (BrowserChild* browserChild = BrowserChild::GetFrom(this)) {
10909       // Reset the dynamic toolbar offset state.
10910       presContext->UpdateDynamicToolbarOffset(0);
10911     }
10912   }
10913 #endif
10914 
10915   if (aIsActive) {
10916     if (nsIFrame* rootFrame = GetRootFrame()) {
10917       rootFrame->SchedulePaint();
10918     }
10919   }
10920 }
10921 
GetMobileViewportManager() const10922 RefPtr<MobileViewportManager> PresShell::GetMobileViewportManager() const {
10923   return mMobileViewportManager;
10924 }
10925 
UseMobileViewportManager(PresShell * aPresShell,Document * aDocument)10926 Maybe<MobileViewportManager::ManagerType> UseMobileViewportManager(
10927     PresShell* aPresShell, Document* aDocument) {
10928   // If we're not using APZ, we won't be able to zoom, so there is no
10929   // point in having an MVM.
10930   if (nsPresContext* presContext = aPresShell->GetPresContext()) {
10931     if (nsIWidget* widget = presContext->GetNearestWidget()) {
10932       if (!widget->AsyncPanZoomEnabled()) {
10933         return Nothing();
10934       }
10935     }
10936   }
10937   if (nsLayoutUtils::ShouldHandleMetaViewport(aDocument)) {
10938     return Some(MobileViewportManager::ManagerType::VisualAndMetaViewport);
10939   }
10940   if (StaticPrefs::apz_mvm_force_enabled() ||
10941       nsLayoutUtils::AllowZoomingForDocument(aDocument)) {
10942     return Some(MobileViewportManager::ManagerType::VisualViewportOnly);
10943   }
10944   return Nothing();
10945 }
10946 
MaybeRecreateMobileViewportManager(bool aAfterInitialization)10947 void PresShell::MaybeRecreateMobileViewportManager(bool aAfterInitialization) {
10948   // Determine if we require a MobileViewportManager, and what kind if so. We
10949   // need one any time we allow resolution zooming for a document, and any time
10950   // we want to obey <meta name="viewport"> tags for it.
10951   Maybe<MobileViewportManager::ManagerType> mvmType =
10952       UseMobileViewportManager(this, mDocument);
10953 
10954   if (mvmType.isNothing() && !mMobileViewportManager) {
10955     // We don't need one and don't have it. So we're done.
10956     return;
10957   }
10958   if (mvmType && mMobileViewportManager &&
10959       *mvmType == mMobileViewportManager->GetManagerType()) {
10960     // We need one and we have one of the correct type, so we're done.
10961     return;
10962   }
10963 
10964   if (mMobileViewportManager) {
10965     // We have one, but we need to either destroy it completely to replace it
10966     // with another one of the correct type. So either way, let's destroy the
10967     // one we have.
10968     mMobileViewportManager->Destroy();
10969     mMobileViewportManager = nullptr;
10970     mMVMContext = nullptr;
10971 
10972     ResetVisualViewportSize();
10973 
10974     // After we clear out the MVM and the MVMContext, also reset the
10975     // resolution to its pre-MVM value.
10976     SetResolutionAndScaleTo(mDocument->GetSavedResolutionBeforeMVM(),
10977                             ResolutionChangeOrigin::MainThreadRestore);
10978 
10979     if (aAfterInitialization) {
10980       // Force a reflow to our correct size by going back to the docShell
10981       // and asking it to reassert its size. This is necessary because
10982       // everything underneath the docShell, like the ViewManager, has been
10983       // altered by the MobileViewportManager in an irreversible way.
10984       nsDocShell* docShell =
10985           static_cast<nsDocShell*>(GetPresContext()->GetDocShell());
10986       int32_t width, height;
10987       docShell->GetSize(&width, &height);
10988       docShell->SetSize(width, height, false);
10989     }
10990   }
10991 
10992   if (mvmType) {
10993     // Let's create the MVM of the type that we need. At this point we shouldn't
10994     // have one.
10995     MOZ_ASSERT(!mMobileViewportManager);
10996 
10997     if (mPresContext->IsRootContentDocumentCrossProcess()) {
10998       // Store the resolution so we can restore to this resolution when
10999       // the MVM is destroyed.
11000       mDocument->SetSavedResolutionBeforeMVM(mResolution.valueOr(1.0f));
11001 
11002       mMVMContext = new GeckoMVMContext(mDocument, this);
11003       mMobileViewportManager = new MobileViewportManager(mMVMContext, *mvmType);
11004       if (MOZ_UNLIKELY(MOZ_LOG_TEST(sApzMvmLog, LogLevel::Debug))) {
11005         nsIURI* uri = mDocument->GetDocumentURI();
11006         MOZ_LOG(sApzMvmLog, LogLevel::Debug,
11007                 ("Created MVM %p (type %d) for URI %s",
11008                  mMobileViewportManager.get(), (int)*mvmType,
11009                  uri ? uri->GetSpecOrDefault().get() : "(null)"));
11010       }
11011 
11012       if (aAfterInitialization) {
11013         // Setting the initial viewport will trigger a reflow.
11014         mMobileViewportManager->SetInitialViewport();
11015       }
11016     }
11017   }
11018 }
11019 
UsesMobileViewportSizing() const11020 bool PresShell::UsesMobileViewportSizing() const {
11021   return mMobileViewportManager != nullptr &&
11022          nsLayoutUtils::ShouldHandleMetaViewport(mDocument);
11023 }
11024 
11025 /*
11026  * Determines the current image locking state. Called when one of the
11027  * dependent factors changes.
11028  */
UpdateImageLockingState()11029 void PresShell::UpdateImageLockingState() {
11030   // We're locked if we're both thawed and active.
11031   bool locked = !mFrozen && mIsActive;
11032 
11033   mDocument->ImageTracker()->SetLockingState(locked);
11034 
11035   if (locked) {
11036     // Request decodes for visible image frames; we want to start decoding as
11037     // quickly as possible when we get foregrounded to minimize flashing.
11038     for (const auto& key : mApproximatelyVisibleFrames) {
11039       if (nsImageFrame* imageFrame = do_QueryFrame(key)) {
11040         imageFrame->MaybeDecodeForPredictedSize();
11041       }
11042     }
11043   }
11044 }
11045 
GetRootPresShell() const11046 PresShell* PresShell::GetRootPresShell() const {
11047   if (mPresContext) {
11048     nsPresContext* rootPresContext = mPresContext->GetRootPresContext();
11049     if (rootPresContext) {
11050       return rootPresContext->PresShell();
11051     }
11052   }
11053   return nullptr;
11054 }
11055 
AddSizeOfIncludingThis(nsWindowSizes & aSizes) const11056 void PresShell::AddSizeOfIncludingThis(nsWindowSizes& aSizes) const {
11057   MallocSizeOf mallocSizeOf = aSizes.mState.mMallocSizeOf;
11058   mFrameArena.AddSizeOfExcludingThis(aSizes, Arena::ArenaKind::PresShell);
11059   aSizes.mLayoutPresShellSize += mallocSizeOf(this);
11060   if (mCaret) {
11061     aSizes.mLayoutPresShellSize += mCaret->SizeOfIncludingThis(mallocSizeOf);
11062   }
11063   aSizes.mLayoutPresShellSize +=
11064       mApproximatelyVisibleFrames.ShallowSizeOfExcludingThis(mallocSizeOf) +
11065       mFramesToDirty.ShallowSizeOfExcludingThis(mallocSizeOf) +
11066       mPendingScrollAnchorSelection.ShallowSizeOfExcludingThis(mallocSizeOf) +
11067       mPendingScrollAnchorAdjustment.ShallowSizeOfExcludingThis(mallocSizeOf);
11068 
11069   aSizes.mLayoutTextRunsSize += SizeOfTextRuns(mallocSizeOf);
11070 
11071   aSizes.mLayoutPresContextSize +=
11072       mPresContext->SizeOfIncludingThis(mallocSizeOf);
11073 
11074   mFrameConstructor->AddSizeOfIncludingThis(aSizes);
11075 }
11076 
SizeOfTextRuns(MallocSizeOf aMallocSizeOf) const11077 size_t PresShell::SizeOfTextRuns(MallocSizeOf aMallocSizeOf) const {
11078   nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
11079   if (!rootFrame) {
11080     return 0;
11081   }
11082 
11083   // clear the TEXT_RUN_MEMORY_ACCOUNTED flags
11084   nsLayoutUtils::SizeOfTextRunsForFrames(rootFrame, nullptr,
11085                                          /* clear = */ true);
11086 
11087   // collect the total memory in use for textruns
11088   return nsLayoutUtils::SizeOfTextRunsForFrames(rootFrame, aMallocSizeOf,
11089                                                 /* clear = */ false);
11090 }
11091 
MarkFixedFramesForReflow(IntrinsicDirty aIntrinsicDirty)11092 void PresShell::MarkFixedFramesForReflow(IntrinsicDirty aIntrinsicDirty) {
11093   nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
11094   if (rootFrame) {
11095     const nsFrameList& childList =
11096         rootFrame->GetChildList(nsIFrame::kFixedList);
11097     for (nsIFrame* childFrame : childList) {
11098       FrameNeedsReflow(childFrame, aIntrinsicDirty, NS_FRAME_IS_DIRTY);
11099     }
11100   }
11101 }
11102 
AppendSubtree(nsIDocShell * aDocShell,nsTArray<nsCOMPtr<nsIContentViewer>> & aArray)11103 static void AppendSubtree(nsIDocShell* aDocShell,
11104                           nsTArray<nsCOMPtr<nsIContentViewer>>& aArray) {
11105   if (nsCOMPtr<nsIContentViewer> cv = aDocShell->GetContentViewer()) {
11106     aArray.AppendElement(cv);
11107   }
11108 
11109   int32_t n = aDocShell->GetInProcessChildCount();
11110   for (int32_t i = 0; i < n; i++) {
11111     nsCOMPtr<nsIDocShellTreeItem> childItem;
11112     aDocShell->GetInProcessChildAt(i, getter_AddRefs(childItem));
11113     if (childItem) {
11114       nsCOMPtr<nsIDocShell> child(do_QueryInterface(childItem));
11115       AppendSubtree(child, aArray);
11116     }
11117   }
11118 }
11119 
MaybeReflowForInflationScreenSizeChange()11120 void PresShell::MaybeReflowForInflationScreenSizeChange() {
11121   nsPresContext* pc = GetPresContext();
11122   const bool fontInflationWasEnabled = FontSizeInflationEnabled();
11123   RecomputeFontSizeInflationEnabled();
11124   bool changed = false;
11125   if (FontSizeInflationEnabled() && FontSizeInflationMinTwips() != 0) {
11126     pc->ScreenSizeInchesForFontInflation(&changed);
11127   }
11128 
11129   changed = changed || fontInflationWasEnabled != FontSizeInflationEnabled();
11130   if (!changed) {
11131     return;
11132   }
11133   if (nsCOMPtr<nsIDocShell> docShell = pc->GetDocShell()) {
11134     nsTArray<nsCOMPtr<nsIContentViewer>> array;
11135     AppendSubtree(docShell, array);
11136     for (uint32_t i = 0, iEnd = array.Length(); i < iEnd; ++i) {
11137       nsCOMPtr<nsIContentViewer> cv = array[i];
11138       if (RefPtr<PresShell> descendantPresShell = cv->GetPresShell()) {
11139         nsIFrame* rootFrame = descendantPresShell->GetRootFrame();
11140         if (rootFrame) {
11141           descendantPresShell->FrameNeedsReflow(
11142               rootFrame, IntrinsicDirty::StyleChange, NS_FRAME_IS_DIRTY);
11143         }
11144       }
11145     }
11146   }
11147 }
11148 
CompleteChangeToVisualViewportSize()11149 void PresShell::CompleteChangeToVisualViewportSize() {
11150   // This can get called during reflow, if the caller wants to get the latest
11151   // visual viewport size after scrollbars have been added/removed. In such a
11152   // case, we don't need to mark things as dirty because the things that we
11153   // would mark dirty either just got updated (the root scrollframe's
11154   // scrollbars), or will be laid out later during this reflow cycle (fixed-pos
11155   // items). Callers that update the visual viewport during a reflow are
11156   // responsible for maintaining these invariants.
11157   if (!mIsReflowing) {
11158     if (nsIScrollableFrame* rootScrollFrame =
11159             GetRootScrollFrameAsScrollable()) {
11160       rootScrollFrame->MarkScrollbarsDirtyForReflow();
11161     }
11162     MarkFixedFramesForReflow(IntrinsicDirty::Resize);
11163   }
11164 
11165   MaybeReflowForInflationScreenSizeChange();
11166 
11167   if (auto* window = nsGlobalWindowInner::Cast(mDocument->GetInnerWindow())) {
11168     window->VisualViewport()->PostResizeEvent();
11169   }
11170 }
11171 
SetVisualViewportSize(nscoord aWidth,nscoord aHeight)11172 void PresShell::SetVisualViewportSize(nscoord aWidth, nscoord aHeight) {
11173   MOZ_ASSERT(aWidth >= 0.0 && aHeight >= 0.0);
11174 
11175   if (!mVisualViewportSizeSet || mVisualViewportSize.width != aWidth ||
11176       mVisualViewportSize.height != aHeight) {
11177     mVisualViewportSizeSet = true;
11178     mVisualViewportSize.width = aWidth;
11179     mVisualViewportSize.height = aHeight;
11180 
11181     CompleteChangeToVisualViewportSize();
11182   }
11183 }
11184 
ResetVisualViewportSize()11185 void PresShell::ResetVisualViewportSize() {
11186   if (mVisualViewportSizeSet) {
11187     mVisualViewportSizeSet = false;
11188     mVisualViewportSize.width = 0;
11189     mVisualViewportSize.height = 0;
11190 
11191     CompleteChangeToVisualViewportSize();
11192   }
11193 }
11194 
SetVisualViewportOffset(const nsPoint & aScrollOffset,const nsPoint & aPrevLayoutScrollPos)11195 bool PresShell::SetVisualViewportOffset(const nsPoint& aScrollOffset,
11196                                         const nsPoint& aPrevLayoutScrollPos) {
11197   nsPoint newOffset = aScrollOffset;
11198   nsIScrollableFrame* rootScrollFrame = GetRootScrollFrameAsScrollable();
11199   if (rootScrollFrame) {
11200     // See the comment in nsHTMLScrollFrame::Reflow above the call to
11201     // SetVisualViewportOffset for why we need to do this.
11202     nsRect scrollRange = rootScrollFrame->GetScrollRangeForUserInputEvents();
11203     if (!scrollRange.Contains(newOffset)) {
11204       newOffset.x = std::min(newOffset.x, scrollRange.XMost());
11205       newOffset.x = std::max(newOffset.x, scrollRange.x);
11206       newOffset.y = std::min(newOffset.y, scrollRange.YMost());
11207       newOffset.y = std::max(newOffset.y, scrollRange.y);
11208     }
11209   }
11210 
11211   nsPoint prevOffset = GetVisualViewportOffset();
11212   if (prevOffset == newOffset) {
11213     return false;
11214   }
11215 
11216   mVisualViewportOffset = Some(newOffset);
11217 
11218   if (auto* window = nsGlobalWindowInner::Cast(mDocument->GetInnerWindow())) {
11219     window->VisualViewport()->PostScrollEvent(prevOffset, aPrevLayoutScrollPos);
11220   }
11221 
11222   if (IsVisualViewportSizeSet() && rootScrollFrame) {
11223     rootScrollFrame->Anchor()->UserScrolled();
11224   }
11225 
11226   if (gfxPlatform::UseDesktopZoomingScrollbars()) {
11227     if (nsIScrollableFrame* rootScrollFrame =
11228             GetRootScrollFrameAsScrollable()) {
11229       rootScrollFrame->UpdateScrollbarPosition();
11230     }
11231   }
11232 
11233   return true;
11234 }
11235 
ScrollToVisual(const nsPoint & aVisualViewportOffset,FrameMetrics::ScrollOffsetUpdateType aUpdateType,ScrollMode aMode)11236 void PresShell::ScrollToVisual(const nsPoint& aVisualViewportOffset,
11237                                FrameMetrics::ScrollOffsetUpdateType aUpdateType,
11238                                ScrollMode aMode) {
11239   MOZ_ASSERT(aMode == ScrollMode::Instant || aMode == ScrollMode::SmoothMsd);
11240 
11241   if (aMode == ScrollMode::SmoothMsd) {
11242     if (nsIScrollableFrame* sf = GetRootScrollFrameAsScrollable()) {
11243       if (sf->SmoothScrollVisual(aVisualViewportOffset, aUpdateType)) {
11244         return;
11245       }
11246     }
11247   }
11248 
11249   // If the caller asked for instant scroll, or if we failed
11250   // to do a smooth scroll, do an instant scroll.
11251   SetPendingVisualScrollUpdate(aVisualViewportOffset, aUpdateType);
11252 }
11253 
SetPendingVisualScrollUpdate(const nsPoint & aVisualViewportOffset,FrameMetrics::ScrollOffsetUpdateType aUpdateType)11254 void PresShell::SetPendingVisualScrollUpdate(
11255     const nsPoint& aVisualViewportOffset,
11256     FrameMetrics::ScrollOffsetUpdateType aUpdateType) {
11257   mPendingVisualScrollUpdate =
11258       Some(VisualScrollUpdate{aVisualViewportOffset, aUpdateType});
11259 
11260   // The pending update is picked up during the next paint.
11261   // Schedule a paint to make sure one will happen.
11262   if (nsIFrame* rootFrame = GetRootFrame()) {
11263     rootFrame->SchedulePaint();
11264   }
11265 }
11266 
ClearPendingVisualScrollUpdate()11267 void PresShell::ClearPendingVisualScrollUpdate() {
11268   if (mPendingVisualScrollUpdate && mPendingVisualScrollUpdate->mAcknowledged) {
11269     mPendingVisualScrollUpdate = mozilla::Nothing();
11270   }
11271 }
11272 
AcknowledgePendingVisualScrollUpdate()11273 void PresShell::AcknowledgePendingVisualScrollUpdate() {
11274   MOZ_ASSERT(mPendingVisualScrollUpdate);
11275   mPendingVisualScrollUpdate->mAcknowledged = true;
11276 }
11277 
GetVisualViewportOffsetRelativeToLayoutViewport() const11278 nsPoint PresShell::GetVisualViewportOffsetRelativeToLayoutViewport() const {
11279   return GetVisualViewportOffset() - GetLayoutViewportOffset();
11280 }
11281 
GetLayoutViewportOffset() const11282 nsPoint PresShell::GetLayoutViewportOffset() const {
11283   nsPoint result;
11284   if (nsIScrollableFrame* sf = GetRootScrollFrameAsScrollable()) {
11285     result = sf->GetScrollPosition();
11286   }
11287   return result;
11288 }
11289 
GetLayoutViewportSize() const11290 nsSize PresShell::GetLayoutViewportSize() const {
11291   nsSize result;
11292   if (nsIScrollableFrame* sf = GetRootScrollFrameAsScrollable()) {
11293     result = sf->GetScrollPortRect().Size();
11294   }
11295   return result;
11296 }
11297 
GetVisualViewportSizeUpdatedByDynamicToolbar() const11298 nsSize PresShell::GetVisualViewportSizeUpdatedByDynamicToolbar() const {
11299   NS_ASSERTION(mVisualViewportSizeSet,
11300                "asking for visual viewport size when its not set?");
11301   if (!mMobileViewportManager) {
11302     return mVisualViewportSize;
11303   }
11304 
11305   MOZ_ASSERT(GetDynamicToolbarState() == DynamicToolbarState::InTransition ||
11306              GetDynamicToolbarState() == DynamicToolbarState::Collapsed);
11307 
11308   nsSize sizeUpdatedByDynamicToolbar =
11309       mMobileViewportManager->GetVisualViewportSizeUpdatedByDynamicToolbar();
11310   return sizeUpdatedByDynamicToolbar == nsSize() ? mVisualViewportSize
11311                                                  : sizeUpdatedByDynamicToolbar;
11312 }
11313 
RecomputeFontSizeInflationEnabled()11314 void PresShell::RecomputeFontSizeInflationEnabled() {
11315   mFontSizeInflationEnabled = DetermineFontSizeInflationState();
11316 
11317   // Divide by 100 to convert the pref from a percentage to a fraction.
11318   float fontScale = StaticPrefs::font_size_systemFontScale() / 100.0f;
11319   if (fontScale == 0.0f) {
11320     return;
11321   }
11322 
11323   MOZ_ASSERT(mDocument);
11324   MOZ_ASSERT(mPresContext);
11325   if (mFontSizeInflationEnabled || mDocument->IsSyntheticDocument()) {
11326     mPresContext->SetSystemFontScale(1.0f);
11327   } else {
11328     mPresContext->SetSystemFontScale(fontScale);
11329   }
11330 }
11331 
DetermineFontSizeInflationState()11332 bool PresShell::DetermineFontSizeInflationState() {
11333   MOZ_ASSERT(mPresContext, "our pres context should not be null");
11334   if (mPresContext->IsChrome()) {
11335     return false;
11336   }
11337 
11338   if (FontSizeInflationEmPerLine() == 0 && FontSizeInflationMinTwips() == 0) {
11339     return false;
11340   }
11341 
11342   // Force-enabling font inflation always trumps the heuristics here.
11343   if (!FontSizeInflationForceEnabled()) {
11344     if (BrowserChild* tab = BrowserChild::GetFrom(this)) {
11345       // We're in a child process.  Cancel inflation if we're not
11346       // async-pan zoomed.
11347       if (!tab->AsyncPanZoomEnabled()) {
11348         return false;
11349       }
11350     } else if (XRE_IsParentProcess()) {
11351       // We're in the master process.  Cancel inflation if it's been
11352       // explicitly disabled.
11353       if (FontSizeInflationDisabledInMasterProcess()) {
11354         return false;
11355       }
11356     }
11357   }
11358 
11359   // XXXjwir3:
11360   // See bug 706918, comment 23 for more information on this particular section
11361   // of the code. We're using "screen size" in place of the size of the content
11362   // area, because on mobile, these are close or equal. This will work for our
11363   // purposes (bug 706198), but it will need to be changed in the future to be
11364   // more correct when we bring the rest of the viewport code into platform.
11365   // We actually want the size of the content area, in the event that we don't
11366   // have any metadata about the width and/or height. On mobile, the screen size
11367   // and the size of the content area are very close, or the same value.
11368   // In XUL fennec, the content area is the size of the <browser> widget, but
11369   // in native fennec, the content area is the size of the Gecko LayerView
11370   // object.
11371 
11372   // TODO:
11373   // Once bug 716575 has been resolved, this code should be changed so that it
11374   // does the right thing on all platforms.
11375   nsresult rv;
11376   nsCOMPtr<nsIScreenManager> screenMgr =
11377       do_GetService("@mozilla.org/gfx/screenmanager;1", &rv);
11378   if (!NS_SUCCEEDED(rv)) {
11379     return false;
11380   }
11381 
11382   nsCOMPtr<nsIScreen> screen;
11383   screenMgr->GetPrimaryScreen(getter_AddRefs(screen));
11384   if (screen) {
11385     int32_t screenLeft, screenTop, screenWidth, screenHeight;
11386     screen->GetRect(&screenLeft, &screenTop, &screenWidth, &screenHeight);
11387 
11388     nsViewportInfo vInf = GetDocument()->GetViewportInfo(
11389         ScreenIntSize(screenWidth, screenHeight));
11390 
11391     if (vInf.GetDefaultZoom() >= CSSToScreenScale(1.0f) ||
11392         vInf.IsAutoSizeEnabled()) {
11393       return false;
11394     }
11395   }
11396 
11397   return true;
11398 }
11399 
SyncWindowProperties(nsView * aView)11400 void PresShell::SyncWindowProperties(nsView* aView) {
11401   nsIFrame* frame = aView->GetFrame();
11402   if (frame && mPresContext) {
11403     // CreateReferenceRenderingContext can return nullptr
11404     RefPtr<gfxContext> rcx(CreateReferenceRenderingContext());
11405     nsContainerFrame::SyncWindowProperties(mPresContext, frame, aView, rcx, 0);
11406   }
11407 }
11408 
HasRuleProcessorUsedByMultipleStyleSets(uint32_t aSheetType,bool * aRetVal)11409 nsresult PresShell::HasRuleProcessorUsedByMultipleStyleSets(uint32_t aSheetType,
11410                                                             bool* aRetVal) {
11411   *aRetVal = false;
11412   return NS_OK;
11413 }
11414 
NotifyStyleSheetServiceSheetAdded(StyleSheet * aSheet,uint32_t aSheetType)11415 void PresShell::NotifyStyleSheetServiceSheetAdded(StyleSheet* aSheet,
11416                                                   uint32_t aSheetType) {
11417   switch (aSheetType) {
11418     case nsIStyleSheetService::AGENT_SHEET:
11419       AddAgentSheet(aSheet);
11420       break;
11421     case nsIStyleSheetService::USER_SHEET:
11422       AddUserSheet(aSheet);
11423       break;
11424     case nsIStyleSheetService::AUTHOR_SHEET:
11425       AddAuthorSheet(aSheet);
11426       break;
11427     default:
11428       MOZ_ASSERT_UNREACHABLE("unexpected aSheetType value");
11429       break;
11430   }
11431 }
11432 
NotifyStyleSheetServiceSheetRemoved(StyleSheet * aSheet,uint32_t aSheetType)11433 void PresShell::NotifyStyleSheetServiceSheetRemoved(StyleSheet* aSheet,
11434                                                     uint32_t aSheetType) {
11435   StyleSet()->RemoveStyleSheet(*aSheet);
11436   mDocument->ApplicableStylesChanged();
11437 }
11438 
SetIsUnderHiddenEmbedderElement(bool aUnderHiddenEmbedderElement)11439 void PresShell::SetIsUnderHiddenEmbedderElement(
11440     bool aUnderHiddenEmbedderElement) {
11441   if (mUnderHiddenEmbedderElement == aUnderHiddenEmbedderElement) {
11442     return;
11443   }
11444 
11445   mUnderHiddenEmbedderElement = aUnderHiddenEmbedderElement;
11446 
11447   if (nsCOMPtr<nsIDocShell> docShell = mPresContext->GetDocShell()) {
11448     BrowsingContext* bc = docShell->GetBrowsingContext();
11449 
11450     // Propagate to children.
11451     for (BrowsingContext* child : bc->Children()) {
11452       Element* embedderElement = child->GetEmbedderElement();
11453       if (!embedderElement) {
11454         // TODO: We shouldn't need to null check here since `child` and the
11455         // element returned by `child->GetEmbedderElement()` are in our
11456         // process (the actual browsing context represented by `child` may not
11457         // be, but that doesn't matter).  However, there are currently a very
11458         // small number of crashes due to `embedderElement` being null, somehow
11459         // - see bug 1551241.  For now we wallpaper the crash.
11460         continue;
11461       }
11462 
11463       bool embedderFrameIsHidden = true;
11464       if (auto embedderFrame = embedderElement->GetPrimaryFrame()) {
11465         embedderFrameIsHidden = !embedderFrame->StyleVisibility()->IsVisible();
11466       }
11467 
11468       if (nsIDocShell* childDocShell = child->GetDocShell()) {
11469         PresShell* presShell = childDocShell->GetPresShell();
11470         if (!presShell) {
11471           continue;
11472         }
11473         presShell->SetIsUnderHiddenEmbedderElement(
11474             aUnderHiddenEmbedderElement || embedderFrameIsHidden);
11475       } else {
11476         BrowserBridgeChild* bridgeChild =
11477             BrowserBridgeChild::GetFrom(embedderElement);
11478         bridgeChild->SetIsUnderHiddenEmbedderElement(
11479             aUnderHiddenEmbedderElement || embedderFrameIsHidden);
11480       }
11481     }
11482   }
11483 }
11484 
GetOverrideClickTarget(WidgetGUIEvent * aGUIEvent,nsIFrame * aFrame)11485 nsIContent* PresShell::EventHandler::GetOverrideClickTarget(
11486     WidgetGUIEvent* aGUIEvent, nsIFrame* aFrame) {
11487   if (aGUIEvent->mMessage != eMouseUp) {
11488     return nullptr;
11489   }
11490 
11491   MOZ_ASSERT(aGUIEvent->mClass == eMouseEventClass);
11492   WidgetMouseEvent* mouseEvent = aGUIEvent->AsMouseEvent();
11493 
11494   uint32_t flags = 0;
11495   RelativeTo relativeTo{aFrame};
11496   nsPoint eventPoint =
11497       nsLayoutUtils::GetEventCoordinatesRelativeTo(aGUIEvent, relativeTo);
11498   if (mouseEvent->mIgnoreRootScrollFrame) {
11499     flags |= INPUT_IGNORE_ROOT_SCROLL_FRAME;
11500   }
11501 
11502   nsIFrame* target =
11503       FindFrameTargetedByInputEvent(aGUIEvent, relativeTo, eventPoint, flags);
11504   if (!target) {
11505     return nullptr;
11506   }
11507 
11508   nsIContent* overrideClickTarget = target->GetContent();
11509   while (overrideClickTarget && !overrideClickTarget->IsElement()) {
11510     overrideClickTarget = overrideClickTarget->GetFlattenedTreeParent();
11511   }
11512   return overrideClickTarget;
11513 }
11514 
11515 /******************************************************************************
11516  * PresShell::EventHandler::EventTargetData
11517  ******************************************************************************/
11518 
SetFrameAndComputePresShell(nsIFrame * aFrameToHandleEvent)11519 void PresShell::EventHandler::EventTargetData::SetFrameAndComputePresShell(
11520     nsIFrame* aFrameToHandleEvent) {
11521   if (aFrameToHandleEvent) {
11522     mFrame = aFrameToHandleEvent;
11523     mPresShell = aFrameToHandleEvent->PresShell();
11524   } else {
11525     mFrame = nullptr;
11526     mPresShell = nullptr;
11527   }
11528 }
11529 
11530 void PresShell::EventHandler::EventTargetData::
SetFrameAndComputePresShellAndContent(nsIFrame * aFrameToHandleEvent,WidgetGUIEvent * aGUIEvent)11531     SetFrameAndComputePresShellAndContent(nsIFrame* aFrameToHandleEvent,
11532                                           WidgetGUIEvent* aGUIEvent) {
11533   MOZ_ASSERT(aFrameToHandleEvent);
11534   MOZ_ASSERT(aGUIEvent);
11535 
11536   SetFrameAndComputePresShell(aFrameToHandleEvent);
11537   SetContentForEventFromFrame(aGUIEvent);
11538 }
11539 
SetContentForEventFromFrame(WidgetGUIEvent * aGUIEvent)11540 void PresShell::EventHandler::EventTargetData::SetContentForEventFromFrame(
11541     WidgetGUIEvent* aGUIEvent) {
11542   MOZ_ASSERT(mFrame);
11543   mContent = nullptr;
11544   mFrame->GetContentForEvent(aGUIEvent, getter_AddRefs(mContent));
11545 }
11546 
GetFrameContent() const11547 nsIContent* PresShell::EventHandler::EventTargetData::GetFrameContent() const {
11548   return mFrame ? mFrame->GetContent() : nullptr;
11549 }
11550 
MaybeRetargetToActiveDocument(WidgetGUIEvent * aGUIEvent)11551 bool PresShell::EventHandler::EventTargetData::MaybeRetargetToActiveDocument(
11552     WidgetGUIEvent* aGUIEvent) {
11553   MOZ_ASSERT(aGUIEvent);
11554   MOZ_ASSERT(mFrame);
11555   MOZ_ASSERT(mPresShell);
11556   MOZ_ASSERT(!mContent, "Doesn't support to retarget the content");
11557 
11558   EventStateManager* activeESM =
11559       EventStateManager::GetActiveEventStateManager();
11560   if (!activeESM) {
11561     return false;
11562   }
11563 
11564   if (aGUIEvent->mClass != ePointerEventClass &&
11565       !aGUIEvent->HasMouseEventMessage()) {
11566     return false;
11567   }
11568 
11569   if (activeESM == GetEventStateManager()) {
11570     return false;
11571   }
11572 
11573   nsPresContext* activePresContext = activeESM->GetPresContext();
11574   if (!activePresContext) {
11575     return false;
11576   }
11577 
11578   PresShell* activePresShell = activePresContext->GetPresShell();
11579   if (!activePresShell) {
11580     return false;
11581   }
11582 
11583   // Note, currently for backwards compatibility we don't forward mouse events
11584   // to the active document when mouse is over some subdocument.
11585   if (!nsContentUtils::ContentIsCrossDocDescendantOf(
11586           activePresShell->GetDocument(), GetDocument())) {
11587     return false;
11588   }
11589 
11590   SetFrameAndComputePresShell(activePresShell->GetRootFrame());
11591   return true;
11592 }
11593 
ComputeElementFromFrame(WidgetGUIEvent * aGUIEvent)11594 bool PresShell::EventHandler::EventTargetData::ComputeElementFromFrame(
11595     WidgetGUIEvent* aGUIEvent) {
11596   MOZ_ASSERT(aGUIEvent);
11597   MOZ_ASSERT(aGUIEvent->IsUsingCoordinates());
11598   MOZ_ASSERT(mPresShell);
11599   MOZ_ASSERT(mFrame);
11600 
11601   SetContentForEventFromFrame(aGUIEvent);
11602 
11603   // If there is no content for this frame, target it anyway.  Some frames can
11604   // be targeted but do not have content, particularly windows with scrolling
11605   // off.
11606   if (!mContent) {
11607     return true;
11608   }
11609 
11610   // Bug 103055, bug 185889: mouse events apply to *elements*, not all nodes.
11611   // Thus we get the nearest element parent here.
11612   // XXX we leave the frame the same even if we find an element parent, so that
11613   // the text frame will receive the event (selection and friends are the ones
11614   // who care about that anyway)
11615   //
11616   // We use weak pointers because during this tight loop, the node
11617   // will *not* go away.  And this happens on every mousemove.
11618   nsIContent* content = mContent;
11619   while (content && !content->IsElement()) {
11620     content = content->GetFlattenedTreeParent();
11621   }
11622   mContent = content;
11623 
11624   // If we found an element, target it.  Otherwise, target *nothing*.
11625   return !!mContent;
11626 }
11627 
UpdateTouchEventTarget(WidgetGUIEvent * aGUIEvent)11628 void PresShell::EventHandler::EventTargetData::UpdateTouchEventTarget(
11629     WidgetGUIEvent* aGUIEvent) {
11630   MOZ_ASSERT(aGUIEvent);
11631 
11632   if (aGUIEvent->mClass != eTouchEventClass) {
11633     return;
11634   }
11635 
11636   if (aGUIEvent->mMessage == eTouchStart) {
11637     WidgetTouchEvent* touchEvent = aGUIEvent->AsTouchEvent();
11638     nsIFrame* newFrame =
11639         TouchManager::SuppressInvalidPointsAndGetTargetedFrame(touchEvent);
11640     if (!newFrame) {
11641       return;  // XXX Why don't we stop handling the event in this case?
11642     }
11643     SetFrameAndComputePresShellAndContent(newFrame, aGUIEvent);
11644     return;
11645   }
11646 
11647   PresShell* newPresShell = PresShell::GetShellForTouchEvent(aGUIEvent);
11648   if (!newPresShell) {
11649     return;  // XXX Why don't we stop handling the event in this case?
11650   }
11651 
11652   // Touch events (except touchstart) are dispatching to the captured
11653   // element. Get correct shell from it.
11654   mPresShell = newPresShell;
11655 }
11656 
11657 /******************************************************************************
11658  * PresShell::EventHandler::HandlingTimeAccumulator
11659  ******************************************************************************/
11660 
HandlingTimeAccumulator(const PresShell::EventHandler & aEventHandler,const WidgetEvent * aEvent)11661 PresShell::EventHandler::HandlingTimeAccumulator::HandlingTimeAccumulator(
11662     const PresShell::EventHandler& aEventHandler, const WidgetEvent* aEvent)
11663     : mEventHandler(aEventHandler),
11664       mEvent(aEvent),
11665       mHandlingStartTime(TimeStamp::Now()) {
11666   MOZ_ASSERT(mEvent);
11667   MOZ_ASSERT(mEvent->IsTrusted());
11668 }
11669 
~HandlingTimeAccumulator()11670 PresShell::EventHandler::HandlingTimeAccumulator::~HandlingTimeAccumulator() {
11671   if (mEvent->mTimeStamp <= mEventHandler.mPresShell->mLastOSWake) {
11672     return;
11673   }
11674 
11675   switch (mEvent->mMessage) {
11676     case eKeyPress:
11677     case eKeyDown:
11678     case eKeyUp:
11679       Telemetry::AccumulateTimeDelta(Telemetry::INPUT_EVENT_HANDLED_KEYBOARD_MS,
11680                                      mHandlingStartTime);
11681       return;
11682     case eMouseDown:
11683       Telemetry::AccumulateTimeDelta(
11684           Telemetry::INPUT_EVENT_HANDLED_MOUSE_DOWN_MS, mHandlingStartTime);
11685       return;
11686     case eMouseUp:
11687       Telemetry::AccumulateTimeDelta(Telemetry::INPUT_EVENT_HANDLED_MOUSE_UP_MS,
11688                                      mHandlingStartTime);
11689       return;
11690     case eMouseMove:
11691       if (mEvent->mFlags.mHandledByAPZ) {
11692         Telemetry::AccumulateTimeDelta(
11693             Telemetry::INPUT_EVENT_HANDLED_APZ_MOUSE_MOVE_MS,
11694             mHandlingStartTime);
11695       }
11696       return;
11697     case eWheel:
11698       if (mEvent->mFlags.mHandledByAPZ) {
11699         Telemetry::AccumulateTimeDelta(
11700             Telemetry::INPUT_EVENT_HANDLED_APZ_WHEEL_MS, mHandlingStartTime);
11701       }
11702       return;
11703     case eTouchMove:
11704       if (mEvent->mFlags.mHandledByAPZ) {
11705         Telemetry::AccumulateTimeDelta(
11706             Telemetry::INPUT_EVENT_HANDLED_APZ_TOUCH_MOVE_MS,
11707             mHandlingStartTime);
11708       }
11709       return;
11710     default:
11711       return;
11712   }
11713 }
11714 
EndPaint()11715 void PresShell::EndPaint() {
11716   ClearPendingVisualScrollUpdate();
11717 
11718   if (mDocument) {
11719     mDocument->EnumerateSubDocuments([](Document& aSubDoc) {
11720       if (PresShell* presShell = aSubDoc.GetPresShell()) {
11721         presShell->EndPaint();
11722       }
11723       return CallState::Continue;
11724     });
11725   }
11726 }
11727 
PingPerTickTelemetry(FlushType aFlushType)11728 void PresShell::PingPerTickTelemetry(FlushType aFlushType) {
11729   mLayoutTelemetry.PingPerTickTelemetry(aFlushType);
11730 }
11731 
GetZoomableByAPZ() const11732 bool PresShell::GetZoomableByAPZ() const {
11733   return mZoomConstraintsClient && mZoomConstraintsClient->GetAllowZoom();
11734 }
11735