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(¤tIndex);
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