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