Bumping manifests a=b2g-bump
[gecko.git] / layout / base / nsPresShell.cpp
blobc523eb3fb05f470194568d5cc60cce1521dcf946
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=2 sw=2 et tw=78:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 * This Original Code has been modified by IBM Corporation.
8 * Modifications made by IBM described herein are
9 * Copyright (c) International Business Machines
10 * Corporation, 2000
12 * Modifications to Mozilla code or documentation
13 * identified per MPL Section 3.3
15 * Date Modified by Description of modification
16 * 05/03/2000 IBM Corp. Observer events for reflow states
19 /* a presentation of a document, part 2 */
21 #ifdef MOZ_LOGGING
22 #define FORCE_PR_LOG /* Allow logging in the release build */
23 #endif
24 #include "prlog.h"
26 #include "mozilla/ArrayUtils.h"
27 #include "mozilla/CSSStyleSheet.h"
28 #include "mozilla/EventDispatcher.h"
29 #include "mozilla/EventStateManager.h"
30 #include "mozilla/EventStates.h"
31 #include "mozilla/IMEStateManager.h"
32 #include "mozilla/MemoryReporting.h"
33 #include "mozilla/dom/TabChild.h"
34 #include "mozilla/Likely.h"
35 #include "mozilla/MouseEvents.h"
36 #include "mozilla/TextEvents.h"
37 #include "mozilla/TouchEvents.h"
38 #include "mozilla/UniquePtr.h"
39 #include <algorithm>
41 #ifdef XP_WIN
42 #include "winuser.h"
43 #endif
45 #include "nsPresShell.h"
46 #include "nsPresContext.h"
47 #include "nsIContent.h"
48 #include "mozilla/dom/Element.h"
49 #include "mozilla/dom/Event.h" // for Event::GetEventPopupControlState()
50 #include "mozilla/dom/ShadowRoot.h"
51 #include "mozilla/dom/PointerEvent.h"
52 #include "nsIDocument.h"
53 #include "nsAnimationManager.h"
54 #include "nsNameSpaceManager.h" // for Pref-related rule management (bugs 22963,20760,31816)
55 #include "nsFrame.h"
56 #include "FrameLayerBuilder.h"
57 #include "nsViewManager.h"
58 #include "nsView.h"
59 #include "nsCRTGlue.h"
60 #include "prprf.h"
61 #include "prinrval.h"
62 #include "nsTArray.h"
63 #include "nsCOMArray.h"
64 #include "nsContainerFrame.h"
65 #include "nsISelection.h"
66 #include "mozilla/dom/Selection.h"
67 #include "nsGkAtoms.h"
68 #include "nsIDOMRange.h"
69 #include "nsIDOMDocument.h"
70 #include "nsIDOMNode.h"
71 #include "nsIDOMNodeList.h"
72 #include "nsIDOMElement.h"
73 #include "nsRange.h"
74 #include "nsCOMPtr.h"
75 #include "nsAutoPtr.h"
76 #include "nsReadableUtils.h"
77 #include "nsIPageSequenceFrame.h"
78 #include "nsCaret.h"
79 #include "TouchCaret.h"
80 #include "SelectionCarets.h"
81 #include "nsIDOMHTMLDocument.h"
82 #include "nsFrameManager.h"
83 #include "nsXPCOM.h"
84 #include "nsILayoutHistoryState.h"
85 #include "nsILineIterator.h" // for ScrollContentIntoView
86 #include "pldhash.h"
87 #include "mozilla/dom/Touch.h"
88 #include "mozilla/dom/PointerEventBinding.h"
89 #include "nsIObserverService.h"
90 #include "nsDocShell.h" // for reflow observation
91 #include "nsIBaseWindow.h"
92 #include "nsError.h"
93 #include "nsLayoutUtils.h"
94 #include "nsViewportInfo.h"
95 #include "nsCSSRendering.h"
96 // for |#ifdef DEBUG| code
97 #include "prenv.h"
98 #include "nsDisplayList.h"
99 #include "nsRegion.h"
100 #include "nsRenderingContext.h"
101 #include "nsAutoLayoutPhase.h"
102 #ifdef MOZ_REFLOW_PERF
103 #include "nsFontMetrics.h"
104 #endif
105 #include "PositionedEventTargeting.h"
107 #include "nsIReflowCallback.h"
109 #include "nsPIDOMWindow.h"
110 #include "nsFocusManager.h"
111 #include "nsIObjectFrame.h"
112 #include "nsIObjectLoadingContent.h"
113 #include "nsNetUtil.h"
114 #include "nsThreadUtils.h"
115 #include "nsStyleSheetService.h"
116 #include "gfxContext.h"
117 #include "gfxUtils.h"
118 #include "nsSMILAnimationController.h"
119 #include "SVGContentUtils.h"
120 #include "nsSVGEffects.h"
121 #include "SVGFragmentIdentifier.h"
122 #include "nsArenaMemoryStats.h"
123 #include "nsFrameSelection.h"
125 #include "nsPerformance.h"
126 #include "nsRefreshDriver.h"
127 #include "nsDOMNavigationTiming.h"
129 // Drag & Drop, Clipboard
130 #include "nsIDocShellTreeItem.h"
131 #include "nsIURI.h"
132 #include "nsIScrollableFrame.h"
133 #include "nsITimer.h"
134 #ifdef ACCESSIBILITY
135 #include "nsAccessibilityService.h"
136 #include "mozilla/a11y/DocAccessible.h"
137 #ifdef DEBUG
138 #include "mozilla/a11y/Logging.h"
139 #endif
140 #endif
142 // For style data reconstruction
143 #include "nsStyleChangeList.h"
144 #include "nsCSSFrameConstructor.h"
145 #ifdef MOZ_XUL
146 #include "nsMenuFrame.h"
147 #include "nsTreeBodyFrame.h"
148 #include "nsIBoxObject.h"
149 #include "nsITreeBoxObject.h"
150 #include "nsMenuPopupFrame.h"
151 #include "nsITreeColumns.h"
152 #include "nsIDOMXULMultSelectCntrlEl.h"
153 #include "nsIDOMXULSelectCntrlItemEl.h"
154 #include "nsIDOMXULMenuListElement.h"
156 #endif
158 #include "GeckoProfiler.h"
159 #include "gfxPlatform.h"
160 #include "Layers.h"
161 #include "LayerTreeInvalidation.h"
162 #include "mozilla/css/ImageLoader.h"
163 #include "mozilla/Preferences.h"
164 #include "mozilla/Telemetry.h"
165 #include "nsCanvasFrame.h"
166 #include "nsIImageLoadingContent.h"
167 #include "nsIScreen.h"
168 #include "nsIScreenManager.h"
169 #include "nsPlaceholderFrame.h"
170 #include "nsTransitionManager.h"
171 #include "ChildIterator.h"
172 #include "RestyleManager.h"
173 #include "nsIDOMHTMLElement.h"
174 #include "nsIDragSession.h"
175 #include "nsIFrameInlines.h"
176 #include "mozilla/gfx/2D.h"
177 #include "nsSubDocumentFrame.h"
179 #ifdef ANDROID
180 #include "nsIDocShellTreeOwner.h"
181 #endif
183 #ifdef MOZ_TASK_TRACER
184 #include "GeckoTaskTracer.h"
185 using namespace mozilla::tasktracer;
186 #endif
188 #define ANCHOR_SCROLL_FLAGS \
189 (nsIPresShell::SCROLL_OVERFLOW_HIDDEN | nsIPresShell::SCROLL_NO_PARENT_FRAMES)
191 using namespace mozilla;
192 using namespace mozilla::css;
193 using namespace mozilla::dom;
194 using namespace mozilla::gfx;
195 using namespace mozilla::layers;
196 using namespace mozilla::gfx;
197 using namespace mozilla::layout;
199 CapturingContentInfo nsIPresShell::gCaptureInfo =
200 { false /* mAllowed */, false /* mPointerLock */, false /* mRetargetToElement */,
201 false /* mPreventDrag */, nullptr /* mContent */ };
202 nsIContent* nsIPresShell::gKeyDownTarget;
203 nsRefPtrHashtable<nsUint32HashKey, dom::Touch>* nsIPresShell::gCaptureTouchList;
204 nsRefPtrHashtable<nsUint32HashKey, nsIContent>* nsIPresShell::gPointerCaptureList;
205 nsClassHashtable<nsUint32HashKey, nsIPresShell::PointerInfo>* nsIPresShell::gActivePointersIds;
206 bool nsIPresShell::gPreventMouseEvents = false;
208 // convert a color value to a string, in the CSS format #RRGGBB
209 // * - initially created for bugs 31816, 20760, 22963
210 static void ColorToString(nscolor aColor, nsAutoString &aString);
212 // RangePaintInfo is used to paint ranges to offscreen buffers
213 struct RangePaintInfo {
214 nsRefPtr<nsRange> mRange;
215 nsDisplayListBuilder mBuilder;
216 nsDisplayList mList;
218 // offset of builder's reference frame to the root frame
219 nsPoint mRootOffset;
221 RangePaintInfo(nsRange* aRange, nsIFrame* aFrame)
222 : mRange(aRange), mBuilder(aFrame, nsDisplayListBuilder::PAINTING, false)
224 MOZ_COUNT_CTOR(RangePaintInfo);
227 ~RangePaintInfo()
229 mList.DeleteAll();
230 MOZ_COUNT_DTOR(RangePaintInfo);
234 #undef NOISY
236 // ----------------------------------------------------------------------
238 #ifdef DEBUG
239 // Set the environment variable GECKO_VERIFY_REFLOW_FLAGS to one or
240 // more of the following flags (comma separated) for handy debug
241 // output.
242 static uint32_t gVerifyReflowFlags;
244 struct VerifyReflowFlags {
245 const char* name;
246 uint32_t bit;
249 static const VerifyReflowFlags gFlags[] = {
250 { "verify", VERIFY_REFLOW_ON },
251 { "reflow", VERIFY_REFLOW_NOISY },
252 { "all", VERIFY_REFLOW_ALL },
253 { "list-commands", VERIFY_REFLOW_DUMP_COMMANDS },
254 { "noisy-commands", VERIFY_REFLOW_NOISY_RC },
255 { "really-noisy-commands", VERIFY_REFLOW_REALLY_NOISY_RC },
256 { "resize", VERIFY_REFLOW_DURING_RESIZE_REFLOW },
259 #define NUM_VERIFY_REFLOW_FLAGS (sizeof(gFlags) / sizeof(gFlags[0]))
261 static void
262 ShowVerifyReflowFlags()
264 printf("Here are the available GECKO_VERIFY_REFLOW_FLAGS:\n");
265 const VerifyReflowFlags* flag = gFlags;
266 const VerifyReflowFlags* limit = gFlags + NUM_VERIFY_REFLOW_FLAGS;
267 while (flag < limit) {
268 printf(" %s\n", flag->name);
269 ++flag;
271 printf("Note: GECKO_VERIFY_REFLOW_FLAGS is a comma separated list of flag\n");
272 printf("names (no whitespace)\n");
274 #endif
276 //========================================================================
277 //========================================================================
278 //========================================================================
279 #ifdef MOZ_REFLOW_PERF
280 class ReflowCountMgr;
282 static const char kGrandTotalsStr[] = "Grand Totals";
284 // Counting Class
285 class ReflowCounter {
286 public:
287 explicit ReflowCounter(ReflowCountMgr * aMgr = nullptr);
288 ~ReflowCounter();
290 void ClearTotals();
291 void DisplayTotals(const char * aStr);
292 void DisplayDiffTotals(const char * aStr);
293 void DisplayHTMLTotals(const char * aStr);
295 void Add() { mTotal++; }
296 void Add(uint32_t aTotal) { mTotal += aTotal; }
298 void CalcDiffInTotals();
299 void SetTotalsCache();
301 void SetMgr(ReflowCountMgr * aMgr) { mMgr = aMgr; }
303 uint32_t GetTotal() { return mTotal; }
305 protected:
306 void DisplayTotals(uint32_t aTotal, const char * aTitle);
307 void DisplayHTMLTotals(uint32_t aTotal, const char * aTitle);
309 uint32_t mTotal;
310 uint32_t mCacheTotal;
312 ReflowCountMgr * mMgr; // weak reference (don't delete)
315 // Counting Class
316 class IndiReflowCounter {
317 public:
318 explicit IndiReflowCounter(ReflowCountMgr * aMgr = nullptr)
319 : mFrame(nullptr),
320 mCount(0),
321 mMgr(aMgr),
322 mCounter(aMgr),
323 mHasBeenOutput(false)
325 virtual ~IndiReflowCounter() {}
327 nsAutoString mName;
328 nsIFrame * mFrame; // weak reference (don't delete)
329 int32_t mCount;
331 ReflowCountMgr * mMgr; // weak reference (don't delete)
333 ReflowCounter mCounter;
334 bool mHasBeenOutput;
338 //--------------------
339 // Manager Class
340 //--------------------
341 class ReflowCountMgr {
342 public:
343 ReflowCountMgr();
344 virtual ~ReflowCountMgr();
346 void ClearTotals();
347 void ClearGrandTotals();
348 void DisplayTotals(const char * aStr);
349 void DisplayHTMLTotals(const char * aStr);
350 void DisplayDiffsInTotals(const char * aStr);
352 void Add(const char * aName, nsIFrame * aFrame);
353 ReflowCounter * LookUp(const char * aName);
355 void PaintCount(const char *aName, nsRenderingContext* aRenderingContext,
356 nsPresContext *aPresContext, nsIFrame *aFrame,
357 const nsPoint &aOffset, uint32_t aColor);
359 FILE * GetOutFile() { return mFD; }
361 PLHashTable * GetIndiFrameHT() { return mIndiFrameCounts; }
363 void SetPresContext(nsPresContext * aPresContext) { mPresContext = aPresContext; } // weak reference
364 void SetPresShell(nsIPresShell* aPresShell) { mPresShell= aPresShell; } // weak reference
366 void SetDumpFrameCounts(bool aVal) { mDumpFrameCounts = aVal; }
367 void SetDumpFrameByFrameCounts(bool aVal) { mDumpFrameByFrameCounts = aVal; }
368 void SetPaintFrameCounts(bool aVal) { mPaintFrameByFrameCounts = aVal; }
370 bool IsPaintingFrameCounts() { return mPaintFrameByFrameCounts; }
372 protected:
373 void DisplayTotals(uint32_t aTotal, uint32_t * aDupArray, char * aTitle);
374 void DisplayHTMLTotals(uint32_t aTotal, uint32_t * aDupArray, char * aTitle);
376 static int RemoveItems(PLHashEntry *he, int i, void *arg);
377 static int RemoveIndiItems(PLHashEntry *he, int i, void *arg);
378 void CleanUp();
380 // stdout Output Methods
381 static int DoSingleTotal(PLHashEntry *he, int i, void *arg);
382 static int DoSingleIndi(PLHashEntry *he, int i, void *arg);
384 void DoGrandTotals();
385 void DoIndiTotalsTree();
387 // HTML Output Methods
388 static int DoSingleHTMLTotal(PLHashEntry *he, int i, void *arg);
389 void DoGrandHTMLTotals();
391 // Zero Out the Totals
392 static int DoClearTotals(PLHashEntry *he, int i, void *arg);
394 // Displays the Diff Totals
395 static int DoDisplayDiffTotals(PLHashEntry *he, int i, void *arg);
397 PLHashTable * mCounts;
398 PLHashTable * mIndiFrameCounts;
399 FILE * mFD;
401 bool mDumpFrameCounts;
402 bool mDumpFrameByFrameCounts;
403 bool mPaintFrameByFrameCounts;
405 bool mCycledOnce;
407 // Root Frame for Individual Tracking
408 nsPresContext * mPresContext;
409 nsIPresShell* mPresShell;
411 // ReflowCountMgr gReflowCountMgr;
413 #endif
414 //========================================================================
416 // comment out to hide caret
417 #define SHOW_CARET
419 // The upper bound on the amount of time to spend reflowing, in
420 // microseconds. When this bound is exceeded and reflow commands are
421 // still queued up, a reflow event is posted. The idea is for reflow
422 // to not hog the processor beyond the time specifed in
423 // gMaxRCProcessingTime. This data member is initialized from the
424 // layout.reflow.timeslice pref.
425 #define NS_MAX_REFLOW_TIME 1000000
426 static int32_t gMaxRCProcessingTime = -1;
428 struct nsCallbackEventRequest
430 nsIReflowCallback* callback;
431 nsCallbackEventRequest* next;
434 // ----------------------------------------------------------------------------
435 #define ASSERT_REFLOW_SCHEDULED_STATE() \
436 NS_ASSERTION(mReflowScheduled == \
437 GetPresContext()->RefreshDriver()-> \
438 IsLayoutFlushObserver(this), "Unexpected state")
440 class nsAutoCauseReflowNotifier
442 public:
443 explicit nsAutoCauseReflowNotifier(PresShell* aShell)
444 : mShell(aShell)
446 mShell->WillCauseReflow();
448 ~nsAutoCauseReflowNotifier()
450 // This check should not be needed. Currently the only place that seem
451 // to need it is the code that deals with bug 337586.
452 if (!mShell->mHaveShutDown) {
453 mShell->DidCauseReflow();
455 else {
456 nsContentUtils::RemoveScriptBlocker();
460 PresShell* mShell;
463 class MOZ_STACK_CLASS nsPresShellEventCB : public EventDispatchingCallback
465 public:
466 explicit nsPresShellEventCB(PresShell* aPresShell) : mPresShell(aPresShell) {}
468 virtual void HandleEvent(EventChainPostVisitor& aVisitor) MOZ_OVERRIDE
470 if (aVisitor.mPresContext && aVisitor.mEvent->mClass != eBasicEventClass) {
471 if (aVisitor.mEvent->message == NS_MOUSE_BUTTON_DOWN ||
472 aVisitor.mEvent->message == NS_MOUSE_BUTTON_UP) {
473 // Mouse-up and mouse-down events call nsFrame::HandlePress/Release
474 // which call GetContentOffsetsFromPoint which requires up-to-date layout.
475 // Bring layout up-to-date now so that GetCurrentEventFrame() below
476 // will return a real frame and we don't have to worry about
477 // destroying it by flushing later.
478 mPresShell->FlushPendingNotifications(Flush_Layout);
479 } else if (aVisitor.mEvent->message == NS_WHEEL_WHEEL &&
480 aVisitor.mEventStatus != nsEventStatus_eConsumeNoDefault) {
481 nsIFrame* frame = mPresShell->GetCurrentEventFrame();
482 if (frame) {
483 // chrome (including addons) should be able to know if content
484 // handles both D3E "wheel" event and legacy mouse scroll events.
485 // We should dispatch legacy mouse events before dispatching the
486 // "wheel" event into system group.
487 nsRefPtr<EventStateManager> esm =
488 aVisitor.mPresContext->EventStateManager();
489 esm->DispatchLegacyMouseScrollEvents(frame,
490 aVisitor.mEvent->AsWheelEvent(),
491 &aVisitor.mEventStatus);
494 nsIFrame* frame = mPresShell->GetCurrentEventFrame();
495 if (!frame &&
496 (aVisitor.mEvent->message == NS_MOUSE_BUTTON_UP ||
497 aVisitor.mEvent->message == NS_TOUCH_END)) {
498 // Redirect BUTTON_UP and TOUCH_END events to the root frame to ensure
499 // that capturing is released.
500 frame = mPresShell->GetRootFrame();
502 if (frame) {
503 frame->HandleEvent(aVisitor.mPresContext,
504 aVisitor.mEvent->AsGUIEvent(),
505 &aVisitor.mEventStatus);
510 nsRefPtr<PresShell> mPresShell;
513 class nsBeforeFirstPaintDispatcher : public nsRunnable
515 public:
516 explicit nsBeforeFirstPaintDispatcher(nsIDocument* aDocument)
517 : mDocument(aDocument) {}
519 // Fires the "before-first-paint" event so that interested parties (right now, the
520 // mobile browser) are aware of it.
521 NS_IMETHOD Run() MOZ_OVERRIDE
523 nsCOMPtr<nsIObserverService> observerService =
524 mozilla::services::GetObserverService();
525 if (observerService) {
526 observerService->NotifyObservers(mDocument, "before-first-paint",
527 nullptr);
529 return NS_OK;
532 private:
533 nsCOMPtr<nsIDocument> mDocument;
536 bool PresShell::sDisableNonTestMouseEvents = false;
538 #ifdef PR_LOGGING
539 PRLogModuleInfo* PresShell::gLog;
540 #endif
542 #ifdef DEBUG
543 static void
544 VerifyStyleTree(nsPresContext* aPresContext, nsFrameManager* aFrameManager)
546 if (nsFrame::GetVerifyStyleTreeEnable()) {
547 nsIFrame* rootFrame = aFrameManager->GetRootFrame();
548 aPresContext->RestyleManager()->DebugVerifyStyleTree(rootFrame);
551 #define VERIFY_STYLE_TREE ::VerifyStyleTree(mPresContext, mFrameConstructor)
552 #else
553 #define VERIFY_STYLE_TREE
554 #endif
556 static bool gVerifyReflowEnabled;
558 bool
559 nsIPresShell::GetVerifyReflowEnable()
561 #ifdef DEBUG
562 static bool firstTime = true;
563 if (firstTime) {
564 firstTime = false;
565 char* flags = PR_GetEnv("GECKO_VERIFY_REFLOW_FLAGS");
566 if (flags) {
567 bool error = false;
569 for (;;) {
570 char* comma = PL_strchr(flags, ',');
571 if (comma)
572 *comma = '\0';
574 bool found = false;
575 const VerifyReflowFlags* flag = gFlags;
576 const VerifyReflowFlags* limit = gFlags + NUM_VERIFY_REFLOW_FLAGS;
577 while (flag < limit) {
578 if (PL_strcasecmp(flag->name, flags) == 0) {
579 gVerifyReflowFlags |= flag->bit;
580 found = true;
581 break;
583 ++flag;
586 if (! found)
587 error = true;
589 if (! comma)
590 break;
592 *comma = ',';
593 flags = comma + 1;
596 if (error)
597 ShowVerifyReflowFlags();
600 if (VERIFY_REFLOW_ON & gVerifyReflowFlags) {
601 gVerifyReflowEnabled = true;
603 printf("Note: verifyreflow is enabled");
604 if (VERIFY_REFLOW_NOISY & gVerifyReflowFlags) {
605 printf(" (noisy)");
607 if (VERIFY_REFLOW_ALL & gVerifyReflowFlags) {
608 printf(" (all)");
610 if (VERIFY_REFLOW_DUMP_COMMANDS & gVerifyReflowFlags) {
611 printf(" (show reflow commands)");
613 if (VERIFY_REFLOW_NOISY_RC & gVerifyReflowFlags) {
614 printf(" (noisy reflow commands)");
615 if (VERIFY_REFLOW_REALLY_NOISY_RC & gVerifyReflowFlags) {
616 printf(" (REALLY noisy reflow commands)");
619 printf("\n");
622 #endif
623 return gVerifyReflowEnabled;
626 void
627 PresShell::AddInvalidateHiddenPresShellObserver(nsRefreshDriver *aDriver)
629 if (!mHiddenInvalidationObserverRefreshDriver && !mIsDestroying && !mHaveShutDown) {
630 aDriver->AddPresShellToInvalidateIfHidden(this);
631 mHiddenInvalidationObserverRefreshDriver = aDriver;
635 void
636 nsIPresShell::InvalidatePresShellIfHidden()
638 if (!IsVisible() && mPresContext) {
639 mPresContext->NotifyInvalidation(0);
641 mHiddenInvalidationObserverRefreshDriver = nullptr;
644 void
645 nsIPresShell::CancelInvalidatePresShellIfHidden()
647 if (mHiddenInvalidationObserverRefreshDriver) {
648 mHiddenInvalidationObserverRefreshDriver->RemovePresShellToInvalidateIfHidden(this);
649 mHiddenInvalidationObserverRefreshDriver = nullptr;
653 void
654 nsIPresShell::SetVerifyReflowEnable(bool aEnabled)
656 gVerifyReflowEnabled = aEnabled;
659 /* virtual */ void
660 nsIPresShell::AddWeakFrameExternal(nsWeakFrame* aWeakFrame)
662 AddWeakFrameInternal(aWeakFrame);
665 void
666 nsIPresShell::AddWeakFrameInternal(nsWeakFrame* aWeakFrame)
668 if (aWeakFrame->GetFrame()) {
669 aWeakFrame->GetFrame()->AddStateBits(NS_FRAME_EXTERNAL_REFERENCE);
671 aWeakFrame->SetPreviousWeakFrame(mWeakFrames);
672 mWeakFrames = aWeakFrame;
675 /* virtual */ void
676 nsIPresShell::RemoveWeakFrameExternal(nsWeakFrame* aWeakFrame)
678 RemoveWeakFrameInternal(aWeakFrame);
681 void
682 nsIPresShell::RemoveWeakFrameInternal(nsWeakFrame* aWeakFrame)
684 if (mWeakFrames == aWeakFrame) {
685 mWeakFrames = aWeakFrame->GetPreviousWeakFrame();
686 return;
688 nsWeakFrame* nextWeak = mWeakFrames;
689 while (nextWeak && nextWeak->GetPreviousWeakFrame() != aWeakFrame) {
690 nextWeak = nextWeak->GetPreviousWeakFrame();
692 if (nextWeak) {
693 nextWeak->SetPreviousWeakFrame(aWeakFrame->GetPreviousWeakFrame());
697 already_AddRefed<nsFrameSelection>
698 nsIPresShell::FrameSelection()
700 nsRefPtr<nsFrameSelection> ret = mSelection;
701 return ret.forget();
704 //----------------------------------------------------------------------
706 static bool sSynthMouseMove = true;
707 static uint32_t sNextPresShellId;
708 static bool sPointerEventEnabled = true;
709 static bool sTouchCaretEnabled = false;
710 static bool sSelectionCaretEnabled = false;
712 /* static */ bool
713 PresShell::TouchCaretPrefEnabled()
715 static bool initialized = false;
716 if (!initialized) {
717 Preferences::AddBoolVarCache(&sTouchCaretEnabled, "touchcaret.enabled");
718 initialized = true;
720 return sTouchCaretEnabled;
723 /* static */ bool
724 PresShell::SelectionCaretPrefEnabled()
726 static bool initialized = false;
727 if (!initialized) {
728 Preferences::AddBoolVarCache(&sSelectionCaretEnabled, "selectioncaret.enabled");
729 initialized = true;
731 return sSelectionCaretEnabled;
734 PresShell::PresShell()
735 : mMouseLocation(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE)
737 mSelection = nullptr;
738 #ifdef MOZ_REFLOW_PERF
739 mReflowCountMgr = new ReflowCountMgr();
740 mReflowCountMgr->SetPresContext(mPresContext);
741 mReflowCountMgr->SetPresShell(this);
742 #endif
743 #ifdef PR_LOGGING
744 mLoadBegin = TimeStamp::Now();
745 if (!gLog) {
746 gLog = PR_NewLogModule("PresShell");
748 #endif
749 mSelectionFlags = nsISelectionDisplay::DISPLAY_TEXT | nsISelectionDisplay::DISPLAY_IMAGES;
750 mIsThemeSupportDisabled = false;
751 mIsActive = true;
752 // FIXME/bug 735029: find a better solution to this problem
753 #ifdef MOZ_WIDGET_ANDROID
754 // The java pan/zoom code uses this to mean approximately "request a
755 // reset of pan/zoom state" which doesn't necessarily correspond
756 // with the first paint of content.
757 mIsFirstPaint = false;
758 #else
759 mIsFirstPaint = true;
760 #endif
761 mPresShellId = sNextPresShellId++;
762 mFrozen = false;
763 #ifdef DEBUG
764 mPresArenaAllocCount = 0;
765 #endif
766 mRenderFlags = 0;
767 mXResolution = 1.0;
768 mYResolution = 1.0;
769 mViewportOverridden = false;
771 mScrollPositionClampingScrollPortSizeSet = false;
773 mMaxLineBoxWidth = 0;
775 static bool addedSynthMouseMove = false;
776 if (!addedSynthMouseMove) {
777 Preferences::AddBoolVarCache(&sSynthMouseMove,
778 "layout.reflow.synthMouseMove", true);
779 addedSynthMouseMove = true;
781 static bool addedPointerEventEnabled = false;
782 if (!addedPointerEventEnabled) {
783 Preferences::AddBoolVarCache(&sPointerEventEnabled,
784 "dom.w3c_pointer_events.enabled", true);
785 addedPointerEventEnabled = true;
788 mPaintingIsFrozen = false;
791 NS_IMPL_ISUPPORTS(PresShell, nsIPresShell, nsIDocumentObserver,
792 nsISelectionController,
793 nsISelectionDisplay, nsIObserver, nsISupportsWeakReference,
794 nsIMutationObserver)
796 PresShell::~PresShell()
798 if (!mHaveShutDown) {
799 NS_NOTREACHED("Someone did not call nsIPresShell::destroy");
800 Destroy();
803 NS_ASSERTION(mCurrentEventContentStack.Count() == 0,
804 "Huh, event content left on the stack in pres shell dtor!");
805 NS_ASSERTION(mFirstCallbackEventRequest == nullptr &&
806 mLastCallbackEventRequest == nullptr,
807 "post-reflow queues not empty. This means we're leaking");
809 // Verify that if painting was frozen, but we're being removed from the tree,
810 // that we now re-enable painting on our refresh driver, since it may need to
811 // be re-used by another presentation.
812 if (mPaintingIsFrozen) {
813 mPresContext->RefreshDriver()->Thaw();
816 #ifdef DEBUG
817 MOZ_ASSERT(mPresArenaAllocCount == 0,
818 "Some pres arena objects were not freed");
819 #endif
821 delete mStyleSet;
822 delete mFrameConstructor;
824 mCurrentEventContent = nullptr;
826 NS_IF_RELEASE(mPresContext);
827 NS_IF_RELEASE(mDocument);
828 NS_IF_RELEASE(mSelection);
832 * Initialize the presentation shell. Create view manager and style
833 * manager.
834 * Note this can't be merged into our constructor because caret initialization
835 * calls AddRef() on us.
837 void
838 PresShell::Init(nsIDocument* aDocument,
839 nsPresContext* aPresContext,
840 nsViewManager* aViewManager,
841 nsStyleSet* aStyleSet,
842 nsCompatibility aCompatMode)
844 NS_PRECONDITION(aDocument, "null ptr");
845 NS_PRECONDITION(aPresContext, "null ptr");
846 NS_PRECONDITION(aViewManager, "null ptr");
847 NS_PRECONDITION(!mDocument, "already initialized");
849 if (!aDocument || !aPresContext || !aViewManager || mDocument) {
850 return;
853 mDocument = aDocument;
854 NS_ADDREF(mDocument);
855 mViewManager = aViewManager;
857 // Create our frame constructor.
858 mFrameConstructor = new nsCSSFrameConstructor(mDocument, this, aStyleSet);
860 mFrameManager = mFrameConstructor;
862 // The document viewer owns both view manager and pres shell.
863 mViewManager->SetPresShell(this);
865 // Bind the context to the presentation shell.
866 mPresContext = aPresContext;
867 NS_ADDREF(mPresContext);
868 aPresContext->SetShell(this);
870 // Now we can initialize the style set.
871 aStyleSet->Init(aPresContext);
872 mStyleSet = aStyleSet;
874 // Notify our prescontext that it now has a compatibility mode. Note that
875 // this MUST happen after we set up our style set but before we create any
876 // frames.
877 mPresContext->CompatibilityModeChanged();
879 // setup the preference style rules (no forced reflow), and do it
880 // before creating any frames.
881 SetPreferenceStyleRules(false);
883 if (TouchCaretPrefEnabled()) {
884 // Create touch caret handle
885 mTouchCaret = new TouchCaret(this);
888 if (SelectionCaretPrefEnabled()) {
889 // Create selection caret handle
890 mSelectionCarets = new SelectionCarets(this);
894 NS_ADDREF(mSelection = new nsFrameSelection());
896 mSelection->Init(this, nullptr);
898 // Important: this has to happen after the selection has been set up
899 #ifdef SHOW_CARET
900 // make the caret
901 mCaret = new nsCaret();
902 mCaret->Init(this);
903 mOriginalCaret = mCaret;
905 //SetCaretEnabled(true); // make it show in browser windows
906 #endif
907 //set up selection to be displayed in document
908 // Don't enable selection for print media
909 nsPresContext::nsPresContextType type = aPresContext->Type();
910 if (type != nsPresContext::eContext_PrintPreview &&
911 type != nsPresContext::eContext_Print)
912 SetDisplaySelection(nsISelectionController::SELECTION_DISABLED);
914 if (gMaxRCProcessingTime == -1) {
915 gMaxRCProcessingTime =
916 Preferences::GetInt("layout.reflow.timeslice", NS_MAX_REFLOW_TIME);
920 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
921 if (os) {
922 os->AddObserver(this, "agent-sheet-added", false);
923 os->AddObserver(this, "user-sheet-added", false);
924 os->AddObserver(this, "author-sheet-added", false);
925 os->AddObserver(this, "agent-sheet-removed", false);
926 os->AddObserver(this, "user-sheet-removed", false);
927 os->AddObserver(this, "author-sheet-removed", false);
928 #ifdef MOZ_XUL
929 os->AddObserver(this, "chrome-flush-skin-caches", false);
930 #endif
934 #ifdef MOZ_REFLOW_PERF
935 if (mReflowCountMgr) {
936 bool paintFrameCounts =
937 Preferences::GetBool("layout.reflow.showframecounts");
939 bool dumpFrameCounts =
940 Preferences::GetBool("layout.reflow.dumpframecounts");
942 bool dumpFrameByFrameCounts =
943 Preferences::GetBool("layout.reflow.dumpframebyframecounts");
945 mReflowCountMgr->SetDumpFrameCounts(dumpFrameCounts);
946 mReflowCountMgr->SetDumpFrameByFrameCounts(dumpFrameByFrameCounts);
947 mReflowCountMgr->SetPaintFrameCounts(paintFrameCounts);
949 #endif
951 if (mDocument->HasAnimationController()) {
952 nsSMILAnimationController* animCtrl = mDocument->GetAnimationController();
953 animCtrl->NotifyRefreshDriverCreated(GetPresContext()->RefreshDriver());
956 // Get our activeness from the docShell.
957 QueryIsActive();
959 // Setup our font inflation preferences.
960 SetupFontInflation();
963 #ifdef PR_LOGGING
964 enum TextPerfLogType {
965 eLog_reflow,
966 eLog_loaddone,
967 eLog_totals
970 static void
971 LogTextPerfStats(gfxTextPerfMetrics* aTextPerf,
972 PresShell* aPresShell,
973 const gfxTextPerfMetrics::TextCounts& aCounts,
974 float aTime, TextPerfLogType aLogType, const char* aURL)
976 char prefix[256];
978 switch (aLogType) {
979 case eLog_reflow:
980 sprintf(prefix, "(textperf-reflow) %p time-ms: %7.0f", aPresShell, aTime);
981 break;
982 case eLog_loaddone:
983 sprintf(prefix, "(textperf-loaddone) %p time-ms: %7.0f", aPresShell, aTime);
984 break;
985 default:
986 MOZ_ASSERT(aLogType == eLog_totals, "unknown textperf log type");
987 sprintf(prefix, "(textperf-totals) %p", aPresShell);
990 PRLogModuleInfo* tpLog = gfxPlatform::GetLog(eGfxLog_textperf);
992 // ignore XUL contexts unless at debug level
993 PRLogModuleLevel logLevel = PR_LOG_WARNING;
994 if (aCounts.numContentTextRuns == 0) {
995 logLevel = PR_LOG_DEBUG;
998 double hitRatio = 0.0;
999 uint32_t lookups = aCounts.wordCacheHit + aCounts.wordCacheMiss;
1000 if (lookups) {
1001 hitRatio = double(aCounts.wordCacheHit) / double(lookups);
1004 if (aLogType == eLog_loaddone) {
1005 PR_LOG(tpLog, logLevel,
1006 ("%s reflow: %d chars: %d "
1007 "[%s] "
1008 "content-textruns: %d chrome-textruns: %d "
1009 "max-textrun-len: %d "
1010 "word-cache-lookups: %d word-cache-hit-ratio: %4.3f "
1011 "word-cache-space: %d word-cache-long: %d "
1012 "pref-fallbacks: %d system-fallbacks: %d "
1013 "textruns-const: %d textruns-destr: %d "
1014 "cumulative-textruns-destr: %d\n",
1015 prefix, aTextPerf->reflowCount, aCounts.numChars,
1016 (aURL ? aURL : ""),
1017 aCounts.numContentTextRuns, aCounts.numChromeTextRuns,
1018 aCounts.maxTextRunLen,
1019 lookups, hitRatio,
1020 aCounts.wordCacheSpaceRules, aCounts.wordCacheLong,
1021 aCounts.fallbackPrefs, aCounts.fallbackSystem,
1022 aCounts.textrunConst, aCounts.textrunDestr,
1023 aTextPerf->cumulative.textrunDestr));
1024 } else {
1025 PR_LOG(tpLog, logLevel,
1026 ("%s reflow: %d chars: %d "
1027 "content-textruns: %d chrome-textruns: %d "
1028 "max-textrun-len: %d "
1029 "word-cache-lookups: %d word-cache-hit-ratio: %4.3f "
1030 "word-cache-space: %d word-cache-long: %d "
1031 "pref-fallbacks: %d system-fallbacks: %d "
1032 "textruns-const: %d textruns-destr: %d "
1033 "cumulative-textruns-destr: %d\n",
1034 prefix, aTextPerf->reflowCount, aCounts.numChars,
1035 aCounts.numContentTextRuns, aCounts.numChromeTextRuns,
1036 aCounts.maxTextRunLen,
1037 lookups, hitRatio,
1038 aCounts.wordCacheSpaceRules, aCounts.wordCacheLong,
1039 aCounts.fallbackPrefs, aCounts.fallbackSystem,
1040 aCounts.textrunConst, aCounts.textrunDestr,
1041 aTextPerf->cumulative.textrunDestr));
1044 #endif
1046 void
1047 PresShell::Destroy()
1049 NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
1050 "destroy called on presshell while scripts not blocked");
1052 // dump out cumulative text perf metrics
1053 #ifdef PR_LOGGING
1054 gfxTextPerfMetrics* tp;
1055 if (mPresContext && (tp = mPresContext->GetTextPerfMetrics())) {
1056 tp->Accumulate();
1057 if (tp->cumulative.numChars > 0) {
1058 LogTextPerfStats(tp, this, tp->cumulative, 0.0, eLog_totals, nullptr);
1061 #endif
1063 #ifdef MOZ_REFLOW_PERF
1064 DumpReflows();
1065 if (mReflowCountMgr) {
1066 delete mReflowCountMgr;
1067 mReflowCountMgr = nullptr;
1069 #endif
1071 if (mHaveShutDown)
1072 return;
1074 #ifdef ACCESSIBILITY
1075 if (mDocAccessible) {
1076 #ifdef DEBUG
1077 if (a11y::logging::IsEnabled(a11y::logging::eDocDestroy))
1078 a11y::logging::DocDestroy("presshell destroyed", mDocument);
1079 #endif
1081 mDocAccessible->Shutdown();
1082 mDocAccessible = nullptr;
1084 #endif // ACCESSIBILITY
1086 MaybeReleaseCapturingContent();
1088 if (gKeyDownTarget && gKeyDownTarget->OwnerDoc() == mDocument) {
1089 NS_RELEASE(gKeyDownTarget);
1092 if (mContentToScrollTo) {
1093 mContentToScrollTo->DeleteProperty(nsGkAtoms::scrolling);
1094 mContentToScrollTo = nullptr;
1097 if (mPresContext) {
1098 // We need to notify the destroying the nsPresContext to ESM for
1099 // suppressing to use from ESM.
1100 mPresContext->EventStateManager()->NotifyDestroyPresContext(mPresContext);
1104 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
1105 if (os) {
1106 os->RemoveObserver(this, "agent-sheet-added");
1107 os->RemoveObserver(this, "user-sheet-added");
1108 os->RemoveObserver(this, "author-sheet-added");
1109 os->RemoveObserver(this, "agent-sheet-removed");
1110 os->RemoveObserver(this, "user-sheet-removed");
1111 os->RemoveObserver(this, "author-sheet-removed");
1112 #ifdef MOZ_XUL
1113 os->RemoveObserver(this, "chrome-flush-skin-caches");
1114 #endif
1118 // If our paint suppression timer is still active, kill it.
1119 if (mPaintSuppressionTimer) {
1120 mPaintSuppressionTimer->Cancel();
1121 mPaintSuppressionTimer = nullptr;
1124 // Same for our reflow continuation timer
1125 if (mReflowContinueTimer) {
1126 mReflowContinueTimer->Cancel();
1127 mReflowContinueTimer = nullptr;
1130 if (mDelayedPaintTimer) {
1131 mDelayedPaintTimer->Cancel();
1132 mDelayedPaintTimer = nullptr;
1135 mSynthMouseMoveEvent.Revoke();
1137 mUpdateImageVisibilityEvent.Revoke();
1139 ClearVisibleImagesList();
1141 if (mCaret) {
1142 mCaret->Terminate();
1143 mCaret = nullptr;
1146 if (mSelection) {
1147 mSelection->DisconnectFromPresShell();
1150 if (mTouchCaret) {
1151 mTouchCaret->Terminate();
1152 mTouchCaret = nullptr;
1155 if (mSelectionCarets) {
1156 mSelectionCarets->Terminate();
1157 mSelectionCarets = nullptr;
1160 // release our pref style sheet, if we have one still
1161 ClearPreferenceStyleRules();
1163 mIsDestroying = true;
1165 // We can't release all the event content in
1166 // mCurrentEventContentStack here since there might be code on the
1167 // stack that will release the event content too. Double release
1168 // bad!
1170 // The frames will be torn down, so remove them from the current
1171 // event frame stack (since they'd be dangling references if we'd
1172 // leave them in) and null out the mCurrentEventFrame pointer as
1173 // well.
1175 mCurrentEventFrame = nullptr;
1177 int32_t i, count = mCurrentEventFrameStack.Length();
1178 for (i = 0; i < count; i++) {
1179 mCurrentEventFrameStack[i] = nullptr;
1182 mFramesToDirty.Clear();
1184 if (mViewManager) {
1185 // Clear the view manager's weak pointer back to |this| in case it
1186 // was leaked.
1187 mViewManager->SetPresShell(nullptr);
1188 mViewManager = nullptr;
1191 mStyleSet->BeginShutdown(mPresContext);
1192 nsRefreshDriver* rd = GetPresContext()->RefreshDriver();
1194 // This shell must be removed from the document before the frame
1195 // hierarchy is torn down to avoid finding deleted frames through
1196 // this presshell while the frames are being torn down
1197 if (mDocument) {
1198 NS_ASSERTION(mDocument->GetShell() == this, "Wrong shell?");
1199 mDocument->DeleteShell();
1201 if (mDocument->HasAnimationController()) {
1202 mDocument->GetAnimationController()->NotifyRefreshDriverDestroying(rd);
1206 // Revoke any pending events. We need to do this and cancel pending reflows
1207 // before we destroy the frame manager, since apparently frame destruction
1208 // sometimes spins the event queue when plug-ins are involved(!).
1209 rd->RemoveLayoutFlushObserver(this);
1210 if (mHiddenInvalidationObserverRefreshDriver) {
1211 mHiddenInvalidationObserverRefreshDriver->RemovePresShellToInvalidateIfHidden(this);
1214 if (rd->PresContext() == GetPresContext()) {
1215 rd->RevokeViewManagerFlush();
1218 mResizeEvent.Revoke();
1219 if (mAsyncResizeTimerIsActive) {
1220 mAsyncResizeEventTimer->Cancel();
1221 mAsyncResizeTimerIsActive = false;
1224 CancelAllPendingReflows();
1225 CancelPostedReflowCallbacks();
1227 // Destroy the frame manager. This will destroy the frame hierarchy
1228 mFrameConstructor->WillDestroyFrameTree();
1230 // Destroy all frame properties (whose destruction was suppressed
1231 // while destroying the frame tree, but which might contain more
1232 // frames within the properties.
1233 if (mPresContext) {
1234 // Clear out the prescontext's property table -- since our frame tree is
1235 // now dead, we shouldn't be looking up any more properties in that table.
1236 // We want to do this before we call SetShell() on the prescontext, so
1237 // property destructors can usefully call GetPresShell() on the
1238 // prescontext.
1239 mPresContext->PropertyTable()->DeleteAll();
1243 NS_WARN_IF_FALSE(!mWeakFrames, "Weak frames alive after destroying FrameManager");
1244 while (mWeakFrames) {
1245 mWeakFrames->Clear(this);
1248 // Let the style set do its cleanup.
1249 mStyleSet->Shutdown(mPresContext);
1251 if (mPresContext) {
1252 // We hold a reference to the pres context, and it holds a weak link back
1253 // to us. To avoid the pres context having a dangling reference, set its
1254 // pres shell to nullptr
1255 mPresContext->SetShell(nullptr);
1257 // Clear the link handler (weak reference) as well
1258 mPresContext->SetLinkHandler(nullptr);
1261 mHaveShutDown = true;
1263 EvictTouches();
1266 void
1267 PresShell::MakeZombie()
1269 mIsZombie = true;
1270 CancelAllPendingReflows();
1273 void
1274 nsIPresShell::SetAuthorStyleDisabled(bool aStyleDisabled)
1276 if (aStyleDisabled != mStyleSet->GetAuthorStyleDisabled()) {
1277 mStyleSet->SetAuthorStyleDisabled(aStyleDisabled);
1278 ReconstructStyleData();
1280 nsCOMPtr<nsIObserverService> observerService =
1281 mozilla::services::GetObserverService();
1282 if (observerService) {
1283 observerService->NotifyObservers(mDocument,
1284 "author-style-disabled-changed",
1285 nullptr);
1290 bool
1291 nsIPresShell::GetAuthorStyleDisabled() const
1293 return mStyleSet->GetAuthorStyleDisabled();
1296 nsresult
1297 PresShell::SetPreferenceStyleRules(bool aForceReflow)
1299 if (!mDocument) {
1300 return NS_ERROR_NULL_POINTER;
1303 nsPIDOMWindow *window = mDocument->GetWindow();
1305 // If the document doesn't have a window there's no need to notify
1306 // its presshell about changes to preferences since the document is
1307 // in a state where it doesn't matter any more (see
1308 // nsDocumentViewer::Close()).
1310 if (!window) {
1311 return NS_ERROR_NULL_POINTER;
1314 NS_PRECONDITION(mPresContext, "presContext cannot be null");
1315 if (mPresContext) {
1316 // first, make sure this is not a chrome shell
1317 if (nsContentUtils::IsInChromeDocshell(mDocument)) {
1318 return NS_OK;
1321 #ifdef DEBUG_attinasi
1322 printf("Setting Preference Style Rules:\n");
1323 #endif
1324 // if here, we need to create rules for the prefs
1325 // - this includes the background-color, the text-color,
1326 // the link color, the visited link color and the link-underlining
1328 // first clear any exising rules
1329 nsresult result = ClearPreferenceStyleRules();
1331 // now the link rules (must come after the color rules, or links will not be correct color!)
1332 // XXX - when there is both an override and agent pref stylesheet this won't matter,
1333 // as the color rules will be overrides and the links rules will be agent
1334 if (NS_SUCCEEDED(result)) {
1335 result = SetPrefLinkRules();
1337 if (NS_SUCCEEDED(result)) {
1338 result = SetPrefFocusRules();
1340 if (NS_SUCCEEDED(result)) {
1341 result = SetPrefNoScriptRule();
1343 if (NS_SUCCEEDED(result)) {
1344 result = SetPrefNoFramesRule();
1346 #ifdef DEBUG_attinasi
1347 printf( "Preference Style Rules set: error=%ld\n", (long)result);
1348 #endif
1350 // Note that this method never needs to force any calculation; the caller
1351 // will recalculate style if needed
1353 return result;
1356 return NS_ERROR_NULL_POINTER;
1359 nsresult PresShell::ClearPreferenceStyleRules(void)
1361 nsresult result = NS_OK;
1362 if (mPrefStyleSheet) {
1363 NS_ASSERTION(mStyleSet, "null styleset entirely unexpected!");
1364 if (mStyleSet) {
1365 // remove the sheet from the styleset:
1366 // - note that we have to check for success by comparing the count before and after...
1367 #ifdef DEBUG
1368 int32_t numBefore = mStyleSet->SheetCount(nsStyleSet::eUserSheet);
1369 NS_ASSERTION(numBefore > 0, "no user stylesheets in styleset, but we have one!");
1370 #endif
1371 mStyleSet->RemoveStyleSheet(nsStyleSet::eUserSheet, mPrefStyleSheet);
1373 #ifdef DEBUG_attinasi
1374 NS_ASSERTION((numBefore - 1) == mStyleSet->GetNumberOfUserStyleSheets(),
1375 "Pref stylesheet was not removed");
1376 printf("PrefStyleSheet removed\n");
1377 #endif
1378 // clear the sheet pointer: it is strictly historical now
1379 mPrefStyleSheet = nullptr;
1382 return result;
1385 nsresult
1386 PresShell::CreatePreferenceStyleSheet()
1388 NS_ASSERTION(!mPrefStyleSheet, "prefStyleSheet already exists");
1389 mPrefStyleSheet = new CSSStyleSheet(CORS_NONE);
1390 nsCOMPtr<nsIURI> uri;
1391 nsresult rv = NS_NewURI(getter_AddRefs(uri), "about:PreferenceStyleSheet", nullptr);
1392 if (NS_FAILED(rv)) {
1393 mPrefStyleSheet = nullptr;
1394 return rv;
1396 NS_ASSERTION(uri, "null but no error");
1397 mPrefStyleSheet->SetURIs(uri, uri, uri);
1398 mPrefStyleSheet->SetComplete();
1399 uint32_t index;
1400 rv =
1401 mPrefStyleSheet->InsertRuleInternal(NS_LITERAL_STRING("@namespace svg url(http://www.w3.org/2000/svg);"),
1402 0, &index);
1403 if (NS_FAILED(rv)) {
1404 mPrefStyleSheet = nullptr;
1405 return rv;
1407 rv =
1408 mPrefStyleSheet->InsertRuleInternal(NS_LITERAL_STRING("@namespace url(http://www.w3.org/1999/xhtml);"),
1409 0, &index);
1410 if (NS_FAILED(rv)) {
1411 mPrefStyleSheet = nullptr;
1412 return rv;
1415 mStyleSet->AppendStyleSheet(nsStyleSet::eUserSheet, mPrefStyleSheet);
1416 return NS_OK;
1419 // XXX We want these after the @namespace rules. Does order matter
1420 // for these rules, or can we call StyleRule::StyleRuleCount()
1421 // and just "append"?
1422 static uint32_t sInsertPrefSheetRulesAt = 2;
1424 nsresult
1425 PresShell::SetPrefNoScriptRule()
1427 nsresult rv = NS_OK;
1429 // also handle the case where print is done from print preview
1430 // see bug #342439 for more details
1431 nsIDocument* doc = mDocument;
1432 if (doc->IsStaticDocument()) {
1433 doc = doc->GetOriginalDocument();
1436 bool scriptEnabled = doc->IsScriptEnabled();
1437 if (scriptEnabled) {
1438 if (!mPrefStyleSheet) {
1439 rv = CreatePreferenceStyleSheet();
1440 NS_ENSURE_SUCCESS(rv, rv);
1443 uint32_t index = 0;
1444 mPrefStyleSheet->
1445 InsertRuleInternal(NS_LITERAL_STRING("noscript{display:none!important}"),
1446 sInsertPrefSheetRulesAt, &index);
1449 return rv;
1452 nsresult PresShell::SetPrefNoFramesRule(void)
1454 NS_ASSERTION(mPresContext,"null prescontext not allowed");
1455 if (!mPresContext) {
1456 return NS_ERROR_FAILURE;
1459 nsresult rv = NS_OK;
1461 if (!mPrefStyleSheet) {
1462 rv = CreatePreferenceStyleSheet();
1463 NS_ENSURE_SUCCESS(rv, rv);
1466 NS_ASSERTION(mPrefStyleSheet, "prefstylesheet should not be null");
1468 bool allowSubframes = true;
1469 nsCOMPtr<nsIDocShell> docShell(mPresContext->GetDocShell());
1470 if (docShell) {
1471 docShell->GetAllowSubframes(&allowSubframes);
1473 if (!allowSubframes) {
1474 uint32_t index = 0;
1475 rv = mPrefStyleSheet->
1476 InsertRuleInternal(NS_LITERAL_STRING("noframes{display:block}"),
1477 sInsertPrefSheetRulesAt, &index);
1478 NS_ENSURE_SUCCESS(rv, rv);
1479 rv = mPrefStyleSheet->
1480 InsertRuleInternal(NS_LITERAL_STRING("frame, frameset, iframe {display:none!important}"),
1481 sInsertPrefSheetRulesAt, &index);
1483 return rv;
1486 nsresult PresShell::SetPrefLinkRules(void)
1488 NS_ASSERTION(mPresContext,"null prescontext not allowed");
1489 if (!mPresContext) {
1490 return NS_ERROR_FAILURE;
1493 nsresult rv = NS_OK;
1495 if (!mPrefStyleSheet) {
1496 rv = CreatePreferenceStyleSheet();
1497 NS_ENSURE_SUCCESS(rv, rv);
1500 NS_ASSERTION(mPrefStyleSheet, "prefstylesheet should not be null");
1502 // support default link colors:
1503 // this means the link colors need to be overridable,
1504 // which they are if we put them in the agent stylesheet,
1505 // though if using an override sheet this will cause authors grief still
1506 // In the agent stylesheet, they are !important when we are ignoring document colors
1508 nscolor linkColor(mPresContext->DefaultLinkColor());
1509 nscolor activeColor(mPresContext->DefaultActiveLinkColor());
1510 nscolor visitedColor(mPresContext->DefaultVisitedLinkColor());
1512 NS_NAMED_LITERAL_STRING(ruleClose, "}");
1513 uint32_t index = 0;
1514 nsAutoString strColor;
1516 // insert a rule to color links: '*|*:link {color: #RRGGBB [!important];}'
1517 ColorToString(linkColor, strColor);
1518 rv = mPrefStyleSheet->
1519 InsertRuleInternal(NS_LITERAL_STRING("*|*:link{color:") +
1520 strColor + ruleClose,
1521 sInsertPrefSheetRulesAt, &index);
1522 NS_ENSURE_SUCCESS(rv, rv);
1524 // - visited links: '*|*:visited {color: #RRGGBB [!important];}'
1525 ColorToString(visitedColor, strColor);
1526 rv = mPrefStyleSheet->
1527 InsertRuleInternal(NS_LITERAL_STRING("*|*:visited{color:") +
1528 strColor + ruleClose,
1529 sInsertPrefSheetRulesAt, &index);
1530 NS_ENSURE_SUCCESS(rv, rv);
1532 // - active links: '*|*:-moz-any-link:active {color: #RRGGBB [!important];}'
1533 ColorToString(activeColor, strColor);
1534 rv = mPrefStyleSheet->
1535 InsertRuleInternal(NS_LITERAL_STRING("*|*:-moz-any-link:active{color:") +
1536 strColor + ruleClose,
1537 sInsertPrefSheetRulesAt, &index);
1538 NS_ENSURE_SUCCESS(rv, rv);
1540 bool underlineLinks =
1541 mPresContext->GetCachedBoolPref(kPresContext_UnderlineLinks);
1543 if (underlineLinks) {
1544 // create a rule to make underlining happen
1545 // '*|*:-moz-any-link {text-decoration:[underline|none];}'
1546 // no need for important, we want these to be overridable
1547 // NOTE: these must go in the agent stylesheet or they cannot be
1548 // overridden by authors
1549 rv = mPrefStyleSheet->
1550 InsertRuleInternal(NS_LITERAL_STRING("*|*:-moz-any-link:not(svg|a){text-decoration:underline}"),
1551 sInsertPrefSheetRulesAt, &index);
1552 } else {
1553 rv = mPrefStyleSheet->
1554 InsertRuleInternal(NS_LITERAL_STRING("*|*:-moz-any-link{text-decoration:none}"),
1555 sInsertPrefSheetRulesAt, &index);
1558 return rv;
1561 nsresult PresShell::SetPrefFocusRules(void)
1563 NS_ASSERTION(mPresContext,"null prescontext not allowed");
1564 nsresult result = NS_OK;
1566 if (!mPresContext)
1567 result = NS_ERROR_FAILURE;
1569 if (NS_SUCCEEDED(result) && !mPrefStyleSheet)
1570 result = CreatePreferenceStyleSheet();
1572 if (NS_SUCCEEDED(result)) {
1573 NS_ASSERTION(mPrefStyleSheet, "prefstylesheet should not be null");
1575 if (mPresContext->GetUseFocusColors()) {
1576 nscolor focusBackground(mPresContext->FocusBackgroundColor());
1577 nscolor focusText(mPresContext->FocusTextColor());
1579 // insert a rule to make focus the preferred color
1580 uint32_t index = 0;
1581 nsAutoString strRule, strColor;
1583 ///////////////////////////////////////////////////////////////
1584 // - focus: '*:focus
1585 ColorToString(focusText,strColor);
1586 strRule.AppendLiteral("*:focus,*:focus>font {color: ");
1587 strRule.Append(strColor);
1588 strRule.AppendLiteral(" !important; background-color: ");
1589 ColorToString(focusBackground,strColor);
1590 strRule.Append(strColor);
1591 strRule.AppendLiteral(" !important; } ");
1592 // insert the rules
1593 result = mPrefStyleSheet->
1594 InsertRuleInternal(strRule, sInsertPrefSheetRulesAt, &index);
1596 uint8_t focusRingWidth = mPresContext->FocusRingWidth();
1597 bool focusRingOnAnything = mPresContext->GetFocusRingOnAnything();
1598 uint8_t focusRingStyle = mPresContext->GetFocusRingStyle();
1600 if ((NS_SUCCEEDED(result) && focusRingWidth != 1 && focusRingWidth <= 4 ) || focusRingOnAnything) {
1601 uint32_t index = 0;
1602 nsAutoString strRule;
1603 if (!focusRingOnAnything)
1604 strRule.AppendLiteral("*|*:link:focus, *|*:visited"); // If we only want focus rings on the normal things like links
1605 strRule.AppendLiteral(":focus {outline: "); // For example 3px dotted WindowText (maximum 4)
1606 strRule.AppendInt(focusRingWidth);
1607 if (focusRingStyle == 0) // solid
1608 strRule.AppendLiteral("px solid -moz-mac-focusring !important; -moz-outline-radius: 3px; outline-offset: 1px; } ");
1609 else // dotted
1610 strRule.AppendLiteral("px dotted WindowText !important; } ");
1611 // insert the rules
1612 result = mPrefStyleSheet->
1613 InsertRuleInternal(strRule, sInsertPrefSheetRulesAt, &index);
1614 NS_ENSURE_SUCCESS(result, result);
1615 if (focusRingWidth != 1) {
1616 // If the focus ring width is different from the default, fix buttons with rings
1617 strRule.AssignLiteral("button::-moz-focus-inner, input[type=\"reset\"]::-moz-focus-inner,");
1618 strRule.AppendLiteral("input[type=\"button\"]::-moz-focus-inner, ");
1619 strRule.AppendLiteral("input[type=\"submit\"]::-moz-focus-inner { padding: 1px 2px 1px 2px; border: ");
1620 strRule.AppendInt(focusRingWidth);
1621 if (focusRingStyle == 0) // solid
1622 strRule.AppendLiteral("px solid transparent !important; } ");
1623 else
1624 strRule.AppendLiteral("px dotted transparent !important; } ");
1625 result = mPrefStyleSheet->
1626 InsertRuleInternal(strRule, sInsertPrefSheetRulesAt, &index);
1627 NS_ENSURE_SUCCESS(result, result);
1629 strRule.AssignLiteral("button:focus::-moz-focus-inner, input[type=\"reset\"]:focus::-moz-focus-inner,");
1630 strRule.AppendLiteral("input[type=\"button\"]:focus::-moz-focus-inner, input[type=\"submit\"]:focus::-moz-focus-inner {");
1631 strRule.AppendLiteral("border-color: ButtonText !important; }");
1632 result = mPrefStyleSheet->
1633 InsertRuleInternal(strRule, sInsertPrefSheetRulesAt, &index);
1637 return result;
1640 void
1641 PresShell::AddUserSheet(nsISupports* aSheet)
1643 // Make sure this does what nsDocumentViewer::CreateStyleSet does wrt
1644 // ordering. We want this new sheet to come after all the existing stylesheet
1645 // service sheets, but before other user sheets; see nsIStyleSheetService.idl
1646 // for the ordering. Just remove and readd all the nsStyleSheetService
1647 // sheets.
1648 nsCOMPtr<nsIStyleSheetService> dummy =
1649 do_GetService(NS_STYLESHEETSERVICE_CONTRACTID);
1651 mStyleSet->BeginUpdate();
1653 nsStyleSheetService *sheetService = nsStyleSheetService::gInstance;
1654 nsCOMArray<nsIStyleSheet> & userSheets = *sheetService->UserStyleSheets();
1655 int32_t i;
1656 // Iterate forwards when removing so the searches for RemoveStyleSheet are as
1657 // short as possible.
1658 for (i = 0; i < userSheets.Count(); ++i) {
1659 mStyleSet->RemoveStyleSheet(nsStyleSet::eUserSheet, userSheets[i]);
1662 // Now iterate backwards, so that the order of userSheets will be the same as
1663 // the order of sheets from it in the style set.
1664 for (i = userSheets.Count() - 1; i >= 0; --i) {
1665 mStyleSet->PrependStyleSheet(nsStyleSet::eUserSheet, userSheets[i]);
1668 mStyleSet->EndUpdate();
1670 ReconstructStyleData();
1673 void
1674 PresShell::AddAgentSheet(nsISupports* aSheet)
1676 // Make sure this does what nsDocumentViewer::CreateStyleSet does
1677 // wrt ordering.
1678 nsCOMPtr<nsIStyleSheet> sheet = do_QueryInterface(aSheet);
1679 if (!sheet) {
1680 return;
1683 mStyleSet->AppendStyleSheet(nsStyleSet::eAgentSheet, sheet);
1684 ReconstructStyleData();
1687 void
1688 PresShell::AddAuthorSheet(nsISupports* aSheet)
1690 nsCOMPtr<nsIStyleSheet> sheet = do_QueryInterface(aSheet);
1691 if (!sheet) {
1692 return;
1695 // Document specific "additional" Author sheets should be stronger than the ones
1696 // added with the StyleSheetService.
1697 nsIStyleSheet* firstAuthorSheet = mDocument->FirstAdditionalAuthorSheet();
1698 if (firstAuthorSheet) {
1699 mStyleSet->InsertStyleSheetBefore(nsStyleSet::eDocSheet, sheet, firstAuthorSheet);
1700 } else {
1701 mStyleSet->AppendStyleSheet(nsStyleSet::eDocSheet, sheet);
1704 ReconstructStyleData();
1707 void
1708 PresShell::RemoveSheet(nsStyleSet::sheetType aType, nsISupports* aSheet)
1710 nsCOMPtr<nsIStyleSheet> sheet = do_QueryInterface(aSheet);
1711 if (!sheet) {
1712 return;
1715 mStyleSet->RemoveStyleSheet(aType, sheet);
1716 ReconstructStyleData();
1719 NS_IMETHODIMP
1720 PresShell::SetDisplaySelection(int16_t aToggle)
1722 mSelection->SetDisplaySelection(aToggle);
1723 return NS_OK;
1726 NS_IMETHODIMP
1727 PresShell::GetDisplaySelection(int16_t *aToggle)
1729 *aToggle = mSelection->GetDisplaySelection();
1730 return NS_OK;
1733 NS_IMETHODIMP
1734 PresShell::GetSelection(SelectionType aType, nsISelection **aSelection)
1736 if (!aSelection || !mSelection)
1737 return NS_ERROR_NULL_POINTER;
1739 *aSelection = mSelection->GetSelection(aType);
1741 if (!(*aSelection))
1742 return NS_ERROR_INVALID_ARG;
1744 NS_ADDREF(*aSelection);
1746 return NS_OK;
1749 Selection*
1750 PresShell::GetCurrentSelection(SelectionType aType)
1752 if (!mSelection)
1753 return nullptr;
1755 return mSelection->GetSelection(aType);
1758 NS_IMETHODIMP
1759 PresShell::ScrollSelectionIntoView(SelectionType aType, SelectionRegion aRegion,
1760 int16_t aFlags)
1762 if (!mSelection)
1763 return NS_ERROR_NULL_POINTER;
1765 return mSelection->ScrollSelectionIntoView(aType, aRegion, aFlags);
1768 NS_IMETHODIMP
1769 PresShell::RepaintSelection(SelectionType aType)
1771 if (!mSelection)
1772 return NS_ERROR_NULL_POINTER;
1774 return mSelection->RepaintSelection(aType);
1777 // Make shell be a document observer
1778 void
1779 PresShell::BeginObservingDocument()
1781 if (mDocument && !mIsDestroying) {
1782 mDocument->AddObserver(this);
1783 if (mIsDocumentGone) {
1784 NS_WARNING("Adding a presshell that was disconnected from the document "
1785 "as a document observer? Sounds wrong...");
1786 mIsDocumentGone = false;
1791 // Make shell stop being a document observer
1792 void
1793 PresShell::EndObservingDocument()
1795 // XXXbz do we need to tell the frame constructor that the document
1796 // is gone, perhaps? Except for printing it's NOT gone, sometimes.
1797 mIsDocumentGone = true;
1798 if (mDocument) {
1799 mDocument->RemoveObserver(this);
1803 #ifdef DEBUG_kipp
1804 char* nsPresShell_ReflowStackPointerTop;
1805 #endif
1807 nsresult
1808 PresShell::Initialize(nscoord aWidth, nscoord aHeight)
1810 if (mIsDestroying) {
1811 return NS_OK;
1814 if (!mDocument) {
1815 // Nothing to do
1816 return NS_OK;
1819 mozilla::TimeStamp timerStart = mozilla::TimeStamp::Now();
1821 NS_ASSERTION(!mDidInitialize, "Why are we being called?");
1823 nsCOMPtr<nsIPresShell> kungFuDeathGrip(this);
1824 mDidInitialize = true;
1826 #ifdef DEBUG
1827 if (VERIFY_REFLOW_NOISY_RC & gVerifyReflowFlags) {
1828 if (mDocument) {
1829 nsIURI *uri = mDocument->GetDocumentURI();
1830 if (uri) {
1831 nsAutoCString url;
1832 uri->GetSpec(url);
1833 printf("*** PresShell::Initialize (this=%p, url='%s')\n", (void*)this, url.get());
1837 #endif
1839 // XXX Do a full invalidate at the beginning so that invalidates along
1840 // the way don't have region accumulation issues?
1842 mPresContext->SetVisibleArea(nsRect(0, 0, aWidth, aHeight));
1844 // Get the root frame from the frame manager
1845 // XXXbz it would be nice to move this somewhere else... like frame manager
1846 // Init(), say. But we need to make sure our views are all set up by the
1847 // time we do this!
1848 nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
1849 NS_ASSERTION(!rootFrame, "How did that happen, exactly?");
1850 if (!rootFrame) {
1851 nsAutoScriptBlocker scriptBlocker;
1852 mFrameConstructor->BeginUpdate();
1853 rootFrame = mFrameConstructor->ConstructRootFrame();
1854 mFrameConstructor->SetRootFrame(rootFrame);
1855 mFrameConstructor->EndUpdate();
1858 NS_ENSURE_STATE(!mHaveShutDown);
1860 if (!rootFrame) {
1861 return NS_ERROR_OUT_OF_MEMORY;
1864 nsIFrame* invalidateFrame = nullptr;
1865 for (nsIFrame* f = rootFrame; f; f = nsLayoutUtils::GetCrossDocParentFrame(f)) {
1866 if (f->GetStateBits() & NS_FRAME_NO_COMPONENT_ALPHA) {
1867 invalidateFrame = f;
1868 f->RemoveStateBits(NS_FRAME_NO_COMPONENT_ALPHA);
1870 nsCOMPtr<nsIPresShell> shell;
1871 if (f->GetType() == nsGkAtoms::subDocumentFrame &&
1872 (shell = static_cast<nsSubDocumentFrame*>(f)->GetSubdocumentPresShellForPainting(0)) &&
1873 shell->GetPresContext()->IsRootContentDocument()) {
1874 // Root content documents build a 'force active' layer, and component alpha flattening
1875 // can't be propagated across that so no need to invalidate above this frame.
1876 break;
1881 if (invalidateFrame) {
1882 invalidateFrame->InvalidateFrameSubtree();
1885 Element *root = mDocument->GetRootElement();
1887 if (root) {
1889 nsAutoCauseReflowNotifier reflowNotifier(this);
1890 mFrameConstructor->BeginUpdate();
1892 // Have the style sheet processor construct frame for the root
1893 // content object down
1894 mFrameConstructor->ContentInserted(nullptr, root, nullptr, false);
1895 VERIFY_STYLE_TREE;
1897 // Something in mFrameConstructor->ContentInserted may have caused
1898 // Destroy() to get called, bug 337586.
1899 NS_ENSURE_STATE(!mHaveShutDown);
1901 mFrameConstructor->EndUpdate();
1904 // nsAutoScriptBlocker going out of scope may have killed us too
1905 NS_ENSURE_STATE(!mHaveShutDown);
1907 // Run the XBL binding constructors for any new frames we've constructed
1908 mDocument->BindingManager()->ProcessAttachedQueue();
1910 // Constructors may have killed us too
1911 NS_ENSURE_STATE(!mHaveShutDown);
1913 // Now flush out pending restyles before we actually reflow, in
1914 // case XBL constructors changed styles somewhere.
1916 nsAutoScriptBlocker scriptBlocker;
1917 mPresContext->RestyleManager()->ProcessPendingRestyles();
1920 // And that might have run _more_ XBL constructors
1921 NS_ENSURE_STATE(!mHaveShutDown);
1924 NS_ASSERTION(rootFrame, "How did that happen?");
1926 // Note: when the frame was created above it had the NS_FRAME_IS_DIRTY bit
1927 // set, but XBL processing could have caused a reflow which clears it.
1928 if (MOZ_LIKELY(rootFrame->GetStateBits() & NS_FRAME_IS_DIRTY)) {
1929 // Unset the DIRTY bits so that FrameNeedsReflow() will work right.
1930 rootFrame->RemoveStateBits(NS_FRAME_IS_DIRTY |
1931 NS_FRAME_HAS_DIRTY_CHILDREN);
1932 NS_ASSERTION(!mDirtyRoots.Contains(rootFrame),
1933 "Why is the root in mDirtyRoots already?");
1934 FrameNeedsReflow(rootFrame, eResize, NS_FRAME_IS_DIRTY);
1935 NS_ASSERTION(mDirtyRoots.Contains(rootFrame),
1936 "Should be in mDirtyRoots now");
1937 NS_ASSERTION(mReflowScheduled, "Why no reflow scheduled?");
1940 // Restore our root scroll position now if we're getting here after EndLoad
1941 // got called, since this is our one chance to do it. Note that we need not
1942 // have reflowed for this to work; when the scrollframe is finally reflowed
1943 // it'll pick up the position we store in it here.
1944 if (!mDocumentLoading) {
1945 RestoreRootScrollPosition();
1948 // For printing, we just immediately unsuppress.
1949 if (!mPresContext->IsPaginated()) {
1950 // Kick off a one-shot timer based off our pref value. When this timer
1951 // fires, if painting is still locked down, then we will go ahead and
1952 // trigger a full invalidate and allow painting to proceed normally.
1953 mPaintingSuppressed = true;
1954 // Don't suppress painting if the document isn't loading.
1955 nsIDocument::ReadyState readyState = mDocument->GetReadyStateEnum();
1956 if (readyState != nsIDocument::READYSTATE_COMPLETE) {
1957 mPaintSuppressionTimer = do_CreateInstance("@mozilla.org/timer;1");
1959 if (!mPaintSuppressionTimer) {
1960 mPaintingSuppressed = false;
1961 } else {
1962 // Initialize the timer.
1964 // Default to PAINTLOCK_EVENT_DELAY if we can't get the pref value.
1965 int32_t delay =
1966 Preferences::GetInt("nglayout.initialpaint.delay",
1967 PAINTLOCK_EVENT_DELAY);
1969 mPaintSuppressionTimer->InitWithFuncCallback(sPaintSuppressionCallback,
1970 this, delay,
1971 nsITimer::TYPE_ONE_SHOT);
1975 if (root && root->IsXUL()) {
1976 mozilla::Telemetry::AccumulateTimeDelta(Telemetry::XUL_INITIAL_FRAME_CONSTRUCTION,
1977 timerStart);
1980 return NS_OK; //XXX this needs to be real. MMP
1983 void
1984 PresShell::sPaintSuppressionCallback(nsITimer *aTimer, void* aPresShell)
1986 nsRefPtr<PresShell> self = static_cast<PresShell*>(aPresShell);
1987 if (self)
1988 self->UnsuppressPainting();
1991 void
1992 PresShell::AsyncResizeEventCallback(nsITimer* aTimer, void* aPresShell)
1994 static_cast<PresShell*>(aPresShell)->FireResizeEvent();
1997 nsresult
1998 PresShell::ResizeReflowOverride(nscoord aWidth, nscoord aHeight)
2000 mViewportOverridden = true;
2001 return ResizeReflowIgnoreOverride(aWidth, aHeight);
2004 nsresult
2005 PresShell::ResizeReflow(nscoord aWidth, nscoord aHeight)
2007 if (mViewportOverridden) {
2008 // The viewport has been overridden, and this reflow request
2009 // didn't ask to ignore the override. Pretend it didn't happen.
2010 return NS_OK;
2012 return ResizeReflowIgnoreOverride(aWidth, aHeight);
2015 nsresult
2016 PresShell::ResizeReflowIgnoreOverride(nscoord aWidth, nscoord aHeight)
2018 NS_PRECONDITION(!mIsReflowing, "Shouldn't be in reflow here!");
2019 NS_PRECONDITION(aWidth != NS_UNCONSTRAINEDSIZE,
2020 "shouldn't use unconstrained widths anymore");
2022 // If we don't have a root frame yet, that means we haven't had our initial
2023 // reflow... If that's the case, and aWidth or aHeight is unconstrained,
2024 // ignore them altogether.
2025 nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
2026 if (!rootFrame && aHeight == NS_UNCONSTRAINEDSIZE) {
2027 // We can't do the work needed for SizeToContent without a root
2028 // frame, and we want to return before setting the visible area.
2029 return NS_ERROR_NOT_AVAILABLE;
2032 mPresContext->SetVisibleArea(nsRect(0, 0, aWidth, aHeight));
2034 // There isn't anything useful we can do if the initial reflow hasn't happened.
2035 if (!rootFrame) {
2036 return NS_OK;
2039 nsRefPtr<nsViewManager> viewManagerDeathGrip = mViewManager;
2040 // Take this ref after viewManager so it'll make sure to go away first.
2041 nsCOMPtr<nsIPresShell> kungFuDeathGrip(this);
2043 if (!GetPresContext()->SupressingResizeReflow()) {
2044 // Have to make sure that the content notifications are flushed before we
2045 // start messing with the frame model; otherwise we can get content doubling.
2046 mDocument->FlushPendingNotifications(Flush_ContentAndNotify);
2048 // Make sure style is up to date
2050 nsAutoScriptBlocker scriptBlocker;
2051 mPresContext->RestyleManager()->ProcessPendingRestyles();
2054 rootFrame = mFrameConstructor->GetRootFrame();
2055 if (!mIsDestroying && rootFrame) {
2056 // XXX Do a full invalidate at the beginning so that invalidates along
2057 // the way don't have region accumulation issues?
2060 nsAutoCauseReflowNotifier crNotifier(this);
2061 WillDoReflow();
2063 // Kick off a top-down reflow
2064 AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Reflow);
2065 nsViewManager::AutoDisableRefresh refreshBlocker(mViewManager);
2067 mDirtyRoots.RemoveElement(rootFrame);
2068 DoReflow(rootFrame, true);
2071 DidDoReflow(true, false);
2075 rootFrame = mFrameConstructor->GetRootFrame();
2076 if (aHeight == NS_UNCONSTRAINEDSIZE && rootFrame) {
2077 mPresContext->SetVisibleArea(
2078 nsRect(0, 0, aWidth, rootFrame->GetRect().height));
2081 if (!mIsDestroying && !mResizeEvent.IsPending() &&
2082 !mAsyncResizeTimerIsActive) {
2083 if (mInResize) {
2084 if (!mAsyncResizeEventTimer) {
2085 mAsyncResizeEventTimer = do_CreateInstance("@mozilla.org/timer;1");
2087 if (mAsyncResizeEventTimer) {
2088 mAsyncResizeTimerIsActive = true;
2089 mAsyncResizeEventTimer->InitWithFuncCallback(AsyncResizeEventCallback,
2090 this, 15,
2091 nsITimer::TYPE_ONE_SHOT);
2093 } else {
2094 nsRefPtr<nsRunnableMethod<PresShell> > resizeEvent =
2095 NS_NewRunnableMethod(this, &PresShell::FireResizeEvent);
2096 if (NS_SUCCEEDED(NS_DispatchToCurrentThread(resizeEvent))) {
2097 mResizeEvent = resizeEvent;
2098 mDocument->SetNeedStyleFlush();
2103 return NS_OK; //XXX this needs to be real. MMP
2106 void
2107 PresShell::FireResizeEvent()
2109 if (mAsyncResizeTimerIsActive) {
2110 mAsyncResizeTimerIsActive = false;
2111 mAsyncResizeEventTimer->Cancel();
2113 mResizeEvent.Revoke();
2115 if (mIsDocumentGone)
2116 return;
2118 //Send resize event from here.
2119 WidgetEvent event(true, NS_RESIZE_EVENT);
2120 nsEventStatus status = nsEventStatus_eIgnore;
2122 nsPIDOMWindow *window = mDocument->GetWindow();
2123 if (window) {
2124 nsCOMPtr<nsIPresShell> kungFuDeathGrip(this);
2125 mInResize = true;
2126 EventDispatcher::Dispatch(window, mPresContext, &event, nullptr, &status);
2127 mInResize = false;
2131 void
2132 PresShell::SetIgnoreFrameDestruction(bool aIgnore)
2134 if (mDocument) {
2135 // We need to tell the ImageLoader to drop all its references to frames
2136 // because they're about to go away and it won't get notifications of that.
2137 mDocument->StyleImageLoader()->ClearFrames();
2139 mIgnoreFrameDestruction = aIgnore;
2142 void
2143 PresShell::NotifyDestroyingFrame(nsIFrame* aFrame)
2145 if (!mIgnoreFrameDestruction) {
2146 mDocument->StyleImageLoader()->DropRequestsForFrame(aFrame);
2148 mFrameConstructor->NotifyDestroyingFrame(aFrame);
2150 for (int32_t idx = mDirtyRoots.Length(); idx; ) {
2151 --idx;
2152 if (mDirtyRoots[idx] == aFrame) {
2153 mDirtyRoots.RemoveElementAt(idx);
2157 // Remove frame properties
2158 mPresContext->NotifyDestroyingFrame(aFrame);
2160 if (aFrame == mCurrentEventFrame) {
2161 mCurrentEventContent = aFrame->GetContent();
2162 mCurrentEventFrame = nullptr;
2165 #ifdef DEBUG
2166 if (aFrame == mDrawEventTargetFrame) {
2167 mDrawEventTargetFrame = nullptr;
2169 #endif
2171 for (unsigned int i=0; i < mCurrentEventFrameStack.Length(); i++) {
2172 if (aFrame == mCurrentEventFrameStack.ElementAt(i)) {
2173 //One of our stack frames was deleted. Get its content so that when we
2174 //pop it we can still get its new frame from its content
2175 nsIContent *currentEventContent = aFrame->GetContent();
2176 mCurrentEventContentStack.ReplaceObjectAt(currentEventContent, i);
2177 mCurrentEventFrameStack[i] = nullptr;
2181 mFramesToDirty.RemoveEntry(aFrame);
2182 } else {
2183 // We must delete this property in situ so that its destructor removes the
2184 // frame from FrameLayerBuilder::DisplayItemData::mFrameList -- otherwise
2185 // the DisplayItemData destructor will use the destroyed frame when it
2186 // tries to remove it from the (array) value of this property.
2187 mPresContext->PropertyTable()->
2188 Delete(aFrame, FrameLayerBuilder::LayerManagerDataProperty());
2192 already_AddRefed<nsCaret> PresShell::GetCaret() const
2194 nsRefPtr<nsCaret> caret = mCaret;
2195 return caret.forget();
2198 // TouchCaret
2199 already_AddRefed<TouchCaret> PresShell::GetTouchCaret() const
2201 nsRefPtr<TouchCaret> touchCaret = mTouchCaret;
2202 return touchCaret.forget();
2205 already_AddRefed<SelectionCarets> PresShell::GetSelectionCarets() const
2207 nsRefPtr<SelectionCarets> selectionCaret = mSelectionCarets;
2208 return selectionCaret.forget();
2211 void PresShell::SetCaret(nsCaret *aNewCaret)
2213 mCaret = aNewCaret;
2216 void PresShell::RestoreCaret()
2218 mCaret = mOriginalCaret;
2221 NS_IMETHODIMP PresShell::SetCaretEnabled(bool aInEnable)
2223 bool oldEnabled = mCaretEnabled;
2225 mCaretEnabled = aInEnable;
2227 if (mCaretEnabled != oldEnabled)
2229 /* Don't change the caret's selection here! This was an evil side-effect of SetCaretEnabled()
2230 nsCOMPtr<nsIDOMSelection> domSel;
2231 if (NS_SUCCEEDED(GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(domSel))) && domSel)
2232 mCaret->SetCaretDOMSelection(domSel);
2235 MOZ_ASSERT(mCaret || mTouchCaret);
2236 if (mCaret) {
2237 mCaret->SetVisible(mCaretEnabled);
2239 if (mTouchCaret) {
2240 mTouchCaret->SyncVisibilityWithCaret();
2244 return NS_OK;
2247 NS_IMETHODIMP PresShell::SetCaretReadOnly(bool aReadOnly)
2249 if (mCaret)
2250 mCaret->SetCaretReadOnly(aReadOnly);
2251 return NS_OK;
2254 NS_IMETHODIMP PresShell::GetCaretEnabled(bool *aOutEnabled)
2256 NS_ENSURE_ARG_POINTER(aOutEnabled);
2257 *aOutEnabled = mCaretEnabled;
2258 return NS_OK;
2261 NS_IMETHODIMP PresShell::SetCaretVisibilityDuringSelection(bool aVisibility)
2263 if (mCaret)
2264 mCaret->SetVisibilityDuringSelection(aVisibility);
2265 return NS_OK;
2268 NS_IMETHODIMP PresShell::GetCaretVisible(bool *aOutIsVisible)
2270 *aOutIsVisible = false;
2271 if (mCaret) {
2272 *aOutIsVisible = mCaret->IsVisible();
2274 return NS_OK;
2277 NS_IMETHODIMP PresShell::SetSelectionFlags(int16_t aInEnable)
2279 mSelectionFlags = aInEnable;
2280 return NS_OK;
2283 NS_IMETHODIMP PresShell::GetSelectionFlags(int16_t *aOutEnable)
2285 if (!aOutEnable)
2286 return NS_ERROR_INVALID_ARG;
2287 *aOutEnable = mSelectionFlags;
2288 return NS_OK;
2291 //implementation of nsISelectionController
2293 NS_IMETHODIMP
2294 PresShell::CharacterMove(bool aForward, bool aExtend)
2296 return mSelection->CharacterMove(aForward, aExtend);
2299 NS_IMETHODIMP
2300 PresShell::CharacterExtendForDelete()
2302 return mSelection->CharacterExtendForDelete();
2305 NS_IMETHODIMP
2306 PresShell::CharacterExtendForBackspace()
2308 return mSelection->CharacterExtendForBackspace();
2311 NS_IMETHODIMP
2312 PresShell::WordMove(bool aForward, bool aExtend)
2314 nsresult result = mSelection->WordMove(aForward, aExtend);
2315 // if we can't go down/up any more we must then move caret completely to
2316 // end/beginning respectively.
2317 if (NS_FAILED(result))
2318 result = CompleteMove(aForward, aExtend);
2319 return result;
2322 NS_IMETHODIMP
2323 PresShell::WordExtendForDelete(bool aForward)
2325 return mSelection->WordExtendForDelete(aForward);
2328 NS_IMETHODIMP
2329 PresShell::LineMove(bool aForward, bool aExtend)
2331 nsresult result = mSelection->LineMove(aForward, aExtend);
2332 // if we can't go down/up any more we must then move caret completely to
2333 // end/beginning respectively.
2334 if (NS_FAILED(result))
2335 result = CompleteMove(aForward,aExtend);
2336 return result;
2339 NS_IMETHODIMP
2340 PresShell::IntraLineMove(bool aForward, bool aExtend)
2342 return mSelection->IntraLineMove(aForward, aExtend);
2347 NS_IMETHODIMP
2348 PresShell::PageMove(bool aForward, bool aExtend)
2350 nsIScrollableFrame *scrollableFrame =
2351 GetFrameToScrollAsScrollable(nsIPresShell::eVertical);
2352 if (!scrollableFrame)
2353 return NS_OK;
2355 mSelection->CommonPageMove(aForward, aExtend, scrollableFrame);
2356 // After ScrollSelectionIntoView(), the pending notifications might be
2357 // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
2358 return ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
2359 nsISelectionController::SELECTION_FOCUS_REGION,
2360 nsISelectionController::SCROLL_SYNCHRONOUS);
2365 NS_IMETHODIMP
2366 PresShell::ScrollPage(bool aForward)
2368 nsIScrollableFrame* scrollFrame =
2369 GetFrameToScrollAsScrollable(nsIPresShell::eVertical);
2370 if (scrollFrame) {
2371 scrollFrame->ScrollBy(nsIntPoint(0, aForward ? 1 : -1),
2372 nsIScrollableFrame::PAGES,
2373 nsIScrollableFrame::SMOOTH);
2375 return NS_OK;
2378 NS_IMETHODIMP
2379 PresShell::ScrollLine(bool aForward)
2381 nsIScrollableFrame* scrollFrame =
2382 GetFrameToScrollAsScrollable(nsIPresShell::eVertical);
2383 if (scrollFrame) {
2384 int32_t lineCount = Preferences::GetInt("toolkit.scrollbox.verticalScrollDistance",
2385 NS_DEFAULT_VERTICAL_SCROLL_DISTANCE);
2386 scrollFrame->ScrollBy(nsIntPoint(0, aForward ? lineCount : -lineCount),
2387 nsIScrollableFrame::LINES,
2388 nsIScrollableFrame::SMOOTH);
2390 return NS_OK;
2393 NS_IMETHODIMP
2394 PresShell::ScrollCharacter(bool aRight)
2396 nsIScrollableFrame* scrollFrame =
2397 GetFrameToScrollAsScrollable(nsIPresShell::eHorizontal);
2398 if (scrollFrame) {
2399 int32_t h = Preferences::GetInt("toolkit.scrollbox.horizontalScrollDistance",
2400 NS_DEFAULT_HORIZONTAL_SCROLL_DISTANCE);
2401 scrollFrame->ScrollBy(nsIntPoint(aRight ? h : -h, 0),
2402 nsIScrollableFrame::LINES,
2403 nsIScrollableFrame::SMOOTH);
2405 return NS_OK;
2408 NS_IMETHODIMP
2409 PresShell::CompleteScroll(bool aForward)
2411 nsIScrollableFrame* scrollFrame =
2412 GetFrameToScrollAsScrollable(nsIPresShell::eVertical);
2413 if (scrollFrame) {
2414 scrollFrame->ScrollBy(nsIntPoint(0, aForward ? 1 : -1),
2415 nsIScrollableFrame::WHOLE,
2416 nsIScrollableFrame::SMOOTH);
2418 return NS_OK;
2421 NS_IMETHODIMP
2422 PresShell::CompleteMove(bool aForward, bool aExtend)
2424 // Beware! This may flush notifications via synchronous
2425 // ScrollSelectionIntoView.
2426 nsIContent* limiter = mSelection->GetAncestorLimiter();
2427 nsIFrame* frame = limiter ? limiter->GetPrimaryFrame()
2428 : FrameConstructor()->GetRootElementFrame();
2429 if (!frame)
2430 return NS_ERROR_FAILURE;
2431 nsIFrame::CaretPosition pos =
2432 frame->GetExtremeCaretPosition(!aForward);
2433 mSelection->HandleClick(pos.mResultContent, pos.mContentOffset,
2434 pos.mContentOffset, aExtend, false,
2435 aForward ? CARET_ASSOCIATE_AFTER : CARET_ASSOCIATE_BEFORE);
2436 if (limiter) {
2437 // HandleClick resets ancestorLimiter, so set it again.
2438 mSelection->SetAncestorLimiter(limiter);
2441 // After ScrollSelectionIntoView(), the pending notifications might be
2442 // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
2443 return ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
2444 nsISelectionController::SELECTION_FOCUS_REGION,
2445 nsISelectionController::SCROLL_SYNCHRONOUS);
2448 NS_IMETHODIMP
2449 PresShell::SelectAll()
2451 return mSelection->SelectAll();
2454 static void
2455 DoCheckVisibility(nsPresContext* aPresContext,
2456 nsIContent* aNode,
2457 int16_t aStartOffset,
2458 int16_t aEndOffset,
2459 bool* aRetval)
2461 nsIFrame* frame = aNode->GetPrimaryFrame();
2462 if (!frame) {
2463 // No frame to look at so it must not be visible.
2464 return;
2467 // Start process now to go through all frames to find startOffset. Then check
2468 // chars after that to see if anything until EndOffset is visible.
2469 bool finished = false;
2470 frame->CheckVisibility(aPresContext, aStartOffset, aEndOffset, true,
2471 &finished, aRetval);
2472 // Don't worry about other return value.
2475 NS_IMETHODIMP
2476 PresShell::CheckVisibility(nsIDOMNode *node, int16_t startOffset, int16_t EndOffset, bool *_retval)
2478 if (!node || startOffset>EndOffset || !_retval || startOffset<0 || EndOffset<0)
2479 return NS_ERROR_INVALID_ARG;
2480 *_retval = false; //initialize return parameter
2481 nsCOMPtr<nsIContent> content(do_QueryInterface(node));
2482 if (!content)
2483 return NS_ERROR_FAILURE;
2485 DoCheckVisibility(mPresContext, content, startOffset, EndOffset, _retval);
2486 return NS_OK;
2489 nsresult
2490 PresShell::CheckVisibilityContent(nsIContent* aNode, int16_t aStartOffset,
2491 int16_t aEndOffset, bool* aRetval)
2493 if (!aNode || aStartOffset > aEndOffset || !aRetval ||
2494 aStartOffset < 0 || aEndOffset < 0) {
2495 return NS_ERROR_INVALID_ARG;
2498 *aRetval = false;
2499 DoCheckVisibility(mPresContext, aNode, aStartOffset, aEndOffset, aRetval);
2500 return NS_OK;
2503 //end implementations nsISelectionController
2505 nsIFrame*
2506 nsIPresShell::GetRootFrameExternal() const
2508 return mFrameConstructor->GetRootFrame();
2511 nsIFrame*
2512 nsIPresShell::GetRootScrollFrame() const
2514 nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
2515 // Ensure root frame is a viewport frame
2516 if (!rootFrame || nsGkAtoms::viewportFrame != rootFrame->GetType())
2517 return nullptr;
2518 nsIFrame* theFrame = rootFrame->GetFirstPrincipalChild();
2519 if (!theFrame || nsGkAtoms::scrollFrame != theFrame->GetType())
2520 return nullptr;
2521 return theFrame;
2524 nsIScrollableFrame*
2525 nsIPresShell::GetRootScrollFrameAsScrollable() const
2527 nsIFrame* frame = GetRootScrollFrame();
2528 if (!frame)
2529 return nullptr;
2530 nsIScrollableFrame* scrollableFrame = do_QueryFrame(frame);
2531 NS_ASSERTION(scrollableFrame,
2532 "All scroll frames must implement nsIScrollableFrame");
2533 return scrollableFrame;
2536 nsIScrollableFrame*
2537 nsIPresShell::GetRootScrollFrameAsScrollableExternal() const
2539 return GetRootScrollFrameAsScrollable();
2542 nsIPageSequenceFrame*
2543 PresShell::GetPageSequenceFrame() const
2545 nsIFrame* frame = mFrameConstructor->GetPageSequenceFrame();
2546 return do_QueryFrame(frame);
2549 nsCanvasFrame*
2550 PresShell::GetCanvasFrame() const
2552 nsIFrame* frame = mFrameConstructor->GetDocElementContainingBlock();
2553 return do_QueryFrame(frame);
2556 Element*
2557 PresShell::GetTouchCaretElement() const
2559 return GetCanvasFrame() ? GetCanvasFrame()->GetTouchCaretElement() : nullptr;
2562 void
2563 PresShell::SetMayHaveTouchCaret(bool aSet)
2565 if (!mPresContext) {
2566 return;
2569 if (!mPresContext->IsRoot()) {
2570 nsIPresShell* rootPresShell = GetRootPresShell();
2571 if (rootPresShell) {
2572 rootPresShell->SetMayHaveTouchCaret(aSet);
2574 return;
2577 nsIDocument* document = GetDocument();
2578 if (document) {
2579 nsPIDOMWindow* innerWin = document->GetInnerWindow();
2580 if (innerWin) {
2581 innerWin->SetMayHaveTouchCaret(aSet);
2586 bool
2587 PresShell::MayHaveTouchCaret()
2589 if (!mPresContext) {
2590 return false;
2593 if (!mPresContext->IsRoot()) {
2594 nsIPresShell* rootPresShell = GetRootPresShell();
2595 return rootPresShell ? rootPresShell->MayHaveTouchCaret() : false;
2598 nsIDocument* document = GetDocument();
2599 if (document) {
2600 nsPIDOMWindow* innerWin = document->GetInnerWindow();
2601 if (innerWin) {
2602 return innerWin->MayHaveTouchCaret();
2605 return false;
2608 Element*
2609 PresShell::GetSelectionCaretsStartElement() const
2611 return GetCanvasFrame() ? GetCanvasFrame()->GetSelectionCaretsStartElement() : nullptr;
2614 Element*
2615 PresShell::GetSelectionCaretsEndElement() const
2617 return GetCanvasFrame() ? GetCanvasFrame()->GetSelectionCaretsEndElement() : nullptr;
2620 void
2621 PresShell::BeginUpdate(nsIDocument *aDocument, nsUpdateType aUpdateType)
2623 #ifdef DEBUG
2624 mUpdateCount++;
2625 #endif
2626 mFrameConstructor->BeginUpdate();
2628 if (aUpdateType & UPDATE_STYLE)
2629 mStyleSet->BeginUpdate();
2632 void
2633 PresShell::EndUpdate(nsIDocument *aDocument, nsUpdateType aUpdateType)
2635 #ifdef DEBUG
2636 NS_PRECONDITION(0 != mUpdateCount, "too many EndUpdate's");
2637 --mUpdateCount;
2638 #endif
2640 if (aUpdateType & UPDATE_STYLE) {
2641 mStyleSet->EndUpdate();
2642 if (mStylesHaveChanged || !mChangedScopeStyleRoots.IsEmpty())
2643 ReconstructStyleData();
2646 mFrameConstructor->EndUpdate();
2649 void
2650 PresShell::RestoreRootScrollPosition()
2652 nsIScrollableFrame* scrollableFrame = GetRootScrollFrameAsScrollable();
2653 if (scrollableFrame) {
2654 scrollableFrame->ScrollToRestoredPosition();
2658 void
2659 PresShell::MaybeReleaseCapturingContent()
2661 nsRefPtr<nsFrameSelection> frameSelection = FrameSelection();
2662 if (frameSelection) {
2663 frameSelection->SetDragState(false);
2665 if (gCaptureInfo.mContent &&
2666 gCaptureInfo.mContent->OwnerDoc() == mDocument) {
2667 SetCapturingContent(nullptr, 0);
2671 void
2672 PresShell::BeginLoad(nsIDocument *aDocument)
2674 mDocumentLoading = true;
2676 #ifdef PR_LOGGING
2677 gfxTextPerfMetrics *tp = nullptr;
2678 if (mPresContext) {
2679 tp = mPresContext->GetTextPerfMetrics();
2682 bool shouldLog = gLog && PR_LOG_TEST(gLog, PR_LOG_DEBUG);
2683 if (shouldLog || tp) {
2684 mLoadBegin = TimeStamp::Now();
2687 if (shouldLog) {
2688 nsIURI* uri = mDocument->GetDocumentURI();
2689 nsAutoCString spec;
2690 if (uri) {
2691 uri->GetSpec(spec);
2693 PR_LOG(gLog, PR_LOG_DEBUG,
2694 ("(presshell) %p load begin [%s]\n",
2695 this, spec.get()));
2697 #endif
2700 void
2701 PresShell::EndLoad(nsIDocument *aDocument)
2703 NS_PRECONDITION(aDocument == mDocument, "Wrong document");
2705 RestoreRootScrollPosition();
2707 mDocumentLoading = false;
2710 void
2711 PresShell::LoadComplete()
2713 #ifdef PR_LOGGING
2714 gfxTextPerfMetrics *tp = nullptr;
2715 if (mPresContext) {
2716 tp = mPresContext->GetTextPerfMetrics();
2719 // log load
2720 bool shouldLog = gLog && PR_LOG_TEST(gLog, PR_LOG_DEBUG);
2721 if (shouldLog || tp) {
2722 TimeDuration loadTime = TimeStamp::Now() - mLoadBegin;
2723 nsIURI* uri = mDocument->GetDocumentURI();
2724 nsAutoCString spec;
2725 if (uri) {
2726 uri->GetSpec(spec);
2728 if (shouldLog) {
2729 PR_LOG(gLog, PR_LOG_DEBUG,
2730 ("(presshell) %p load done time-ms: %9.2f [%s]\n",
2731 this, loadTime.ToMilliseconds(), spec.get()));
2733 if (tp) {
2734 tp->Accumulate();
2735 if (tp->cumulative.numChars > 0) {
2736 LogTextPerfStats(tp, this, tp->cumulative, loadTime.ToMilliseconds(),
2737 eLog_loaddone, spec.get());
2741 #endif
2744 #ifdef DEBUG
2745 void
2746 PresShell::VerifyHasDirtyRootAncestor(nsIFrame* aFrame)
2748 // XXXbz due to bug 372769, can't actually assert anything here...
2749 return;
2751 // XXXbz shouldn't need this part; remove it once FrameNeedsReflow
2752 // handles the root frame correctly.
2753 if (!aFrame->GetParent()) {
2754 return;
2757 // Make sure that there is a reflow root ancestor of |aFrame| that's
2758 // in mDirtyRoots already.
2759 while (aFrame && (aFrame->GetStateBits() & NS_FRAME_HAS_DIRTY_CHILDREN)) {
2760 if (((aFrame->GetStateBits() & NS_FRAME_REFLOW_ROOT) ||
2761 !aFrame->GetParent()) &&
2762 mDirtyRoots.Contains(aFrame)) {
2763 return;
2766 aFrame = aFrame->GetParent();
2768 NS_NOTREACHED("Frame has dirty bits set but isn't scheduled to be "
2769 "reflowed?");
2771 #endif
2773 void
2774 PresShell::FrameNeedsReflow(nsIFrame *aFrame, IntrinsicDirty aIntrinsicDirty,
2775 nsFrameState aBitToAdd)
2777 NS_PRECONDITION(aBitToAdd == NS_FRAME_IS_DIRTY ||
2778 aBitToAdd == NS_FRAME_HAS_DIRTY_CHILDREN ||
2779 !aBitToAdd,
2780 "Unexpected bits being added");
2781 NS_PRECONDITION(!(aIntrinsicDirty == eStyleChange &&
2782 aBitToAdd == NS_FRAME_HAS_DIRTY_CHILDREN),
2783 "bits don't correspond to style change reason");
2785 NS_ASSERTION(!mIsReflowing, "can't mark frame dirty during reflow");
2787 // If we've not yet done the initial reflow, then don't bother
2788 // enqueuing a reflow command yet.
2789 if (! mDidInitialize)
2790 return;
2792 // If we're already destroying, don't bother with this either.
2793 if (mIsDestroying)
2794 return;
2796 #ifdef DEBUG
2797 //printf("gShellCounter: %d\n", gShellCounter++);
2798 if (mInVerifyReflow)
2799 return;
2801 if (VERIFY_REFLOW_NOISY_RC & gVerifyReflowFlags) {
2802 printf("\nPresShell@%p: frame %p needs reflow\n", (void*)this, (void*)aFrame);
2803 if (VERIFY_REFLOW_REALLY_NOISY_RC & gVerifyReflowFlags) {
2804 printf("Current content model:\n");
2805 Element *rootElement = mDocument->GetRootElement();
2806 if (rootElement) {
2807 rootElement->List(stdout, 0);
2811 #endif
2813 nsAutoTArray<nsIFrame*, 4> subtrees;
2814 subtrees.AppendElement(aFrame);
2816 do {
2817 nsIFrame *subtreeRoot = subtrees.ElementAt(subtrees.Length() - 1);
2818 subtrees.RemoveElementAt(subtrees.Length() - 1);
2820 // Grab |wasDirty| now so we can go ahead and update the bits on
2821 // subtreeRoot.
2822 bool wasDirty = NS_SUBTREE_DIRTY(subtreeRoot);
2823 subtreeRoot->AddStateBits(aBitToAdd);
2825 // Now if subtreeRoot is a reflow root we can cut off this reflow at it if
2826 // the bit being added is NS_FRAME_HAS_DIRTY_CHILDREN.
2827 bool targetFrameDirty = (aBitToAdd == NS_FRAME_IS_DIRTY);
2829 #define FRAME_IS_REFLOW_ROOT(_f) \
2830 ((_f->GetStateBits() & NS_FRAME_REFLOW_ROOT) && \
2831 (_f != subtreeRoot || !targetFrameDirty))
2834 // Mark the intrinsic widths as dirty on the frame, all of its ancestors,
2835 // and all of its descendants, if needed:
2837 if (aIntrinsicDirty != eResize) {
2838 // Mark argument and all ancestors dirty. (Unless we hit a reflow
2839 // root that should contain the reflow. That root could be
2840 // subtreeRoot itself if it's not dirty, or it could be some
2841 // ancestor of subtreeRoot.)
2842 for (nsIFrame *a = subtreeRoot;
2843 a && !FRAME_IS_REFLOW_ROOT(a);
2844 a = a->GetParent())
2845 a->MarkIntrinsicISizesDirty();
2848 if (aIntrinsicDirty == eStyleChange) {
2849 // Mark all descendants dirty (using an nsTArray stack rather than
2850 // recursion).
2851 // Note that nsHTMLReflowState::InitResizeFlags has some similar
2852 // code; see comments there for how and why it differs.
2853 nsAutoTArray<nsIFrame*, 32> stack;
2854 stack.AppendElement(subtreeRoot);
2856 do {
2857 nsIFrame *f = stack.ElementAt(stack.Length() - 1);
2858 stack.RemoveElementAt(stack.Length() - 1);
2860 if (f->GetType() == nsGkAtoms::placeholderFrame) {
2861 nsIFrame *oof = nsPlaceholderFrame::GetRealFrameForPlaceholder(f);
2862 if (!nsLayoutUtils::IsProperAncestorFrame(subtreeRoot, oof)) {
2863 // We have another distinct subtree we need to mark.
2864 subtrees.AppendElement(oof);
2868 nsIFrame::ChildListIterator lists(f);
2869 for (; !lists.IsDone(); lists.Next()) {
2870 nsFrameList::Enumerator childFrames(lists.CurrentList());
2871 for (; !childFrames.AtEnd(); childFrames.Next()) {
2872 nsIFrame* kid = childFrames.get();
2873 kid->MarkIntrinsicISizesDirty();
2874 stack.AppendElement(kid);
2877 } while (stack.Length() != 0);
2880 // Skip setting dirty bits up the tree if we weren't given a bit to add.
2881 if (!aBitToAdd) {
2882 continue;
2885 // Set NS_FRAME_HAS_DIRTY_CHILDREN bits (via nsIFrame::ChildIsDirty)
2886 // up the tree until we reach either a frame that's already dirty or
2887 // a reflow root.
2888 nsIFrame *f = subtreeRoot;
2889 for (;;) {
2890 if (FRAME_IS_REFLOW_ROOT(f) || !f->GetParent()) {
2891 // we've hit a reflow root or the root frame
2892 if (!wasDirty) {
2893 mDirtyRoots.AppendElement(f);
2894 mDocument->SetNeedLayoutFlush();
2896 #ifdef DEBUG
2897 else {
2898 VerifyHasDirtyRootAncestor(f);
2900 #endif
2902 break;
2905 nsIFrame *child = f;
2906 f = f->GetParent();
2907 wasDirty = NS_SUBTREE_DIRTY(f);
2908 f->ChildIsDirty(child);
2909 NS_ASSERTION(f->GetStateBits() & NS_FRAME_HAS_DIRTY_CHILDREN,
2910 "ChildIsDirty didn't do its job");
2911 if (wasDirty) {
2912 // This frame was already marked dirty.
2913 #ifdef DEBUG
2914 VerifyHasDirtyRootAncestor(f);
2915 #endif
2916 break;
2919 } while (subtrees.Length() != 0);
2921 MaybeScheduleReflow();
2924 void
2925 PresShell::FrameNeedsToContinueReflow(nsIFrame *aFrame)
2927 NS_ASSERTION(mIsReflowing, "Must be in reflow when marking path dirty.");
2928 NS_PRECONDITION(mCurrentReflowRoot, "Must have a current reflow root here");
2929 NS_ASSERTION(aFrame == mCurrentReflowRoot ||
2930 nsLayoutUtils::IsProperAncestorFrame(mCurrentReflowRoot, aFrame),
2931 "Frame passed in is not the descendant of mCurrentReflowRoot");
2932 NS_ASSERTION(aFrame->GetStateBits() & NS_FRAME_IN_REFLOW,
2933 "Frame passed in not in reflow?");
2935 mFramesToDirty.PutEntry(aFrame);
2938 nsIScrollableFrame*
2939 nsIPresShell::GetFrameToScrollAsScrollable(
2940 nsIPresShell::ScrollDirection aDirection)
2942 nsIScrollableFrame* scrollFrame = nullptr;
2944 nsCOMPtr<nsIContent> focusedContent;
2945 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
2946 if (fm && mDocument) {
2947 nsCOMPtr<nsIDOMWindow> window = do_QueryInterface(mDocument->GetWindow());
2949 nsCOMPtr<nsIDOMElement> focusedElement;
2950 fm->GetFocusedElementForWindow(window, false, nullptr, getter_AddRefs(focusedElement));
2951 focusedContent = do_QueryInterface(focusedElement);
2953 if (!focusedContent && mSelection) {
2954 nsISelection* domSelection = mSelection->
2955 GetSelection(nsISelectionController::SELECTION_NORMAL);
2956 if (domSelection) {
2957 nsCOMPtr<nsIDOMNode> focusedNode;
2958 domSelection->GetFocusNode(getter_AddRefs(focusedNode));
2959 focusedContent = do_QueryInterface(focusedNode);
2962 if (focusedContent) {
2963 nsIFrame* startFrame = focusedContent->GetPrimaryFrame();
2964 if (startFrame) {
2965 scrollFrame = startFrame->GetScrollTargetFrame();
2966 if (scrollFrame) {
2967 startFrame = scrollFrame->GetScrolledFrame();
2969 if (aDirection == nsIPresShell::eEither) {
2970 scrollFrame =
2971 nsLayoutUtils::GetNearestScrollableFrame(startFrame);
2972 } else {
2973 scrollFrame =
2974 nsLayoutUtils::GetNearestScrollableFrameForDirection(startFrame,
2975 aDirection == eVertical ? nsLayoutUtils::eVertical :
2976 nsLayoutUtils::eHorizontal);
2980 if (!scrollFrame) {
2981 scrollFrame = GetRootScrollFrameAsScrollable();
2983 return scrollFrame;
2986 void
2987 PresShell::CancelAllPendingReflows()
2989 mDirtyRoots.Clear();
2991 if (mReflowScheduled) {
2992 GetPresContext()->RefreshDriver()->RemoveLayoutFlushObserver(this);
2993 mReflowScheduled = false;
2996 ASSERT_REFLOW_SCHEDULED_STATE();
2999 nsresult
3000 PresShell::RecreateFramesFor(nsIContent* aContent)
3002 NS_ENSURE_TRUE(mPresContext, NS_ERROR_FAILURE);
3003 if (!mDidInitialize) {
3004 // Nothing to do here. In fact, if we proceed and aContent is the
3005 // root we will crash.
3006 return NS_OK;
3009 // Don't call RecreateFramesForContent since that is not exported and we want
3010 // to keep the number of entrypoints down.
3012 NS_ASSERTION(mViewManager, "Should have view manager");
3014 // Have to make sure that the content notifications are flushed before we
3015 // start messing with the frame model; otherwise we can get content doubling.
3016 mDocument->FlushPendingNotifications(Flush_ContentAndNotify);
3018 nsAutoScriptBlocker scriptBlocker;
3020 nsStyleChangeList changeList;
3021 changeList.AppendChange(nullptr, aContent, nsChangeHint_ReconstructFrame);
3023 // Mark ourselves as not safe to flush while we're doing frame construction.
3024 ++mChangeNestCount;
3025 RestyleManager* restyleManager = mPresContext->RestyleManager();
3026 nsresult rv = restyleManager->ProcessRestyledFrames(changeList);
3027 restyleManager->FlushOverflowChangedTracker();
3028 --mChangeNestCount;
3030 return rv;
3033 void
3034 nsIPresShell::PostRecreateFramesFor(Element* aElement)
3036 mPresContext->RestyleManager()->PostRestyleEvent(aElement, nsRestyleHint(0),
3037 nsChangeHint_ReconstructFrame);
3040 void
3041 nsIPresShell::RestyleForAnimation(Element* aElement, nsRestyleHint aHint)
3043 mPresContext->RestyleManager()->PostAnimationRestyleEvent(aElement, aHint,
3044 NS_STYLE_HINT_NONE);
3047 void
3048 nsIPresShell::SetForwardingContainer(const WeakPtr<nsDocShell> &aContainer)
3050 mForwardingContainer = aContainer;
3053 void
3054 PresShell::ClearFrameRefs(nsIFrame* aFrame)
3056 mPresContext->EventStateManager()->ClearFrameRefs(aFrame);
3058 nsWeakFrame* weakFrame = mWeakFrames;
3059 while (weakFrame) {
3060 nsWeakFrame* prev = weakFrame->GetPreviousWeakFrame();
3061 if (weakFrame->GetFrame() == aFrame) {
3062 // This removes weakFrame from mWeakFrames.
3063 weakFrame->Clear(this);
3065 weakFrame = prev;
3069 already_AddRefed<nsRenderingContext>
3070 PresShell::CreateReferenceRenderingContext()
3072 nsDeviceContext* devCtx = mPresContext->DeviceContext();
3073 nsRefPtr<nsRenderingContext> rc;
3074 if (mPresContext->IsScreen()) {
3075 rc = new nsRenderingContext();
3076 rc->Init(devCtx, gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget());
3077 } else {
3078 rc = devCtx->CreateRenderingContext();
3081 MOZ_ASSERT(rc, "shouldn't break promise to return non-null");
3082 return rc.forget();
3085 nsresult
3086 PresShell::GoToAnchor(const nsAString& aAnchorName, bool aScroll)
3088 if (!mDocument) {
3089 return NS_ERROR_FAILURE;
3092 const Element *root = mDocument->GetRootElement();
3093 if (root && root->IsSVG(nsGkAtoms::svg)) {
3094 // We need to execute this even if there is an empty anchor name
3095 // so that any existing SVG fragment identifier effect is removed
3096 if (SVGFragmentIdentifier::ProcessFragmentIdentifier(mDocument, aAnchorName)) {
3097 return NS_OK;
3101 // Hold a reference to the ESM in case event dispatch tears us down.
3102 nsRefPtr<EventStateManager> esm = mPresContext->EventStateManager();
3104 if (aAnchorName.IsEmpty()) {
3105 NS_ASSERTION(!aScroll, "can't scroll to empty anchor name");
3106 esm->SetContentState(nullptr, NS_EVENT_STATE_URLTARGET);
3107 return NS_OK;
3110 nsCOMPtr<nsIDOMHTMLDocument> htmlDoc = do_QueryInterface(mDocument);
3111 nsresult rv = NS_OK;
3112 nsCOMPtr<nsIContent> content;
3114 // Search for an element with a matching "id" attribute
3115 if (mDocument) {
3116 content = mDocument->GetElementById(aAnchorName);
3119 // Search for an anchor element with a matching "name" attribute
3120 if (!content && htmlDoc) {
3121 nsCOMPtr<nsIDOMNodeList> list;
3122 // Find a matching list of named nodes
3123 rv = htmlDoc->GetElementsByName(aAnchorName, getter_AddRefs(list));
3124 if (NS_SUCCEEDED(rv) && list) {
3125 uint32_t i;
3126 // Loop through the named nodes looking for the first anchor
3127 for (i = 0; true; i++) {
3128 nsCOMPtr<nsIDOMNode> node;
3129 rv = list->Item(i, getter_AddRefs(node));
3130 if (!node) { // End of list
3131 break;
3133 // Ensure it's an anchor element
3134 content = do_QueryInterface(node);
3135 if (content) {
3136 if (content->Tag() == nsGkAtoms::a && content->IsHTML()) {
3137 break;
3139 content = nullptr;
3145 // Search for anchor in the HTML namespace with a matching name
3146 if (!content && !htmlDoc)
3148 nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(mDocument);
3149 nsCOMPtr<nsIDOMNodeList> list;
3150 NS_NAMED_LITERAL_STRING(nameSpace, "http://www.w3.org/1999/xhtml");
3151 // Get the list of anchor elements
3152 rv = doc->GetElementsByTagNameNS(nameSpace, NS_LITERAL_STRING("a"), getter_AddRefs(list));
3153 if (NS_SUCCEEDED(rv) && list) {
3154 uint32_t i;
3155 // Loop through the named nodes looking for the first anchor
3156 for (i = 0; true; i++) {
3157 nsCOMPtr<nsIDOMNode> node;
3158 rv = list->Item(i, getter_AddRefs(node));
3159 if (!node) { // End of list
3160 break;
3162 // Compare the name attribute
3163 nsCOMPtr<nsIDOMElement> element = do_QueryInterface(node);
3164 nsAutoString value;
3165 if (element && NS_SUCCEEDED(element->GetAttribute(NS_LITERAL_STRING("name"), value))) {
3166 if (value.Equals(aAnchorName)) {
3167 content = do_QueryInterface(element);
3168 break;
3175 esm->SetContentState(content, NS_EVENT_STATE_URLTARGET);
3177 #ifdef ACCESSIBILITY
3178 nsIContent *anchorTarget = content;
3179 #endif
3181 nsIScrollableFrame* rootScroll = GetRootScrollFrameAsScrollable();
3182 if (rootScroll && rootScroll->DidHistoryRestore()) {
3183 // Scroll position restored from history trumps scrolling to anchor.
3184 aScroll = false;
3185 rootScroll->ClearDidHistoryRestore();
3188 if (content) {
3189 if (aScroll) {
3190 rv = ScrollContentIntoView(content,
3191 ScrollAxis(SCROLL_TOP, SCROLL_ALWAYS),
3192 ScrollAxis(),
3193 ANCHOR_SCROLL_FLAGS);
3194 NS_ENSURE_SUCCESS(rv, rv);
3196 nsIScrollableFrame* rootScroll = GetRootScrollFrameAsScrollable();
3197 if (rootScroll) {
3198 mLastAnchorScrolledTo = content;
3199 mLastAnchorScrollPositionY = rootScroll->GetScrollPosition().y;
3203 // Should we select the target? This action is controlled by a
3204 // preference: the default is to not select.
3205 bool selectAnchor = Preferences::GetBool("layout.selectanchor");
3207 // Even if select anchor pref is false, we must still move the
3208 // caret there. That way tabbing will start from the new
3209 // location
3210 nsRefPtr<nsIDOMRange> jumpToRange = new nsRange(mDocument);
3211 while (content && content->GetFirstChild()) {
3212 content = content->GetFirstChild();
3214 nsCOMPtr<nsIDOMNode> node(do_QueryInterface(content));
3215 NS_ASSERTION(node, "No nsIDOMNode for descendant of anchor");
3216 jumpToRange->SelectNodeContents(node);
3217 // Select the anchor
3218 nsISelection* sel = mSelection->
3219 GetSelection(nsISelectionController::SELECTION_NORMAL);
3220 if (sel) {
3221 sel->RemoveAllRanges();
3222 sel->AddRange(jumpToRange);
3223 if (!selectAnchor) {
3224 // Use a caret (collapsed selection) at the start of the anchor
3225 sel->CollapseToStart();
3228 // Selection is at anchor.
3229 // Now focus the document itself if focus is on an element within it.
3230 nsPIDOMWindow *win = mDocument->GetWindow();
3232 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
3233 if (fm && win) {
3234 nsCOMPtr<nsIDOMWindow> focusedWindow;
3235 fm->GetFocusedWindow(getter_AddRefs(focusedWindow));
3236 if (SameCOMIdentity(win, focusedWindow)) {
3237 fm->ClearFocus(focusedWindow);
3241 // If the target is an animation element, activate the animation
3242 if (content->IsNodeOfType(nsINode::eANIMATION)) {
3243 SVGContentUtils::ActivateByHyperlink(content.get());
3245 } else {
3246 rv = NS_ERROR_FAILURE;
3247 NS_NAMED_LITERAL_STRING(top, "top");
3248 if (nsContentUtils::EqualsIgnoreASCIICase(aAnchorName, top)) {
3249 // Scroll to the top/left if aAnchorName is "top" and there is no element
3250 // with such a name or id.
3251 rv = NS_OK;
3252 nsIScrollableFrame* sf = GetRootScrollFrameAsScrollable();
3253 // Check |aScroll| after setting |rv| so we set |rv| to the same
3254 // thing whether or not |aScroll| is true.
3255 if (aScroll && sf) {
3256 // Scroll to the top of the page
3257 sf->ScrollTo(nsPoint(0, 0), nsIScrollableFrame::INSTANT);
3262 #ifdef ACCESSIBILITY
3263 if (anchorTarget) {
3264 nsAccessibilityService* accService = AccService();
3265 if (accService)
3266 accService->NotifyOfAnchorJumpTo(anchorTarget);
3268 #endif
3270 return rv;
3273 nsresult
3274 PresShell::ScrollToAnchor()
3276 if (!mLastAnchorScrolledTo) {
3277 return NS_OK;
3279 NS_ASSERTION(mDidInitialize, "should have done initial reflow by now");
3281 nsIScrollableFrame* rootScroll = GetRootScrollFrameAsScrollable();
3282 if (!rootScroll ||
3283 mLastAnchorScrollPositionY != rootScroll->GetScrollPosition().y) {
3284 return NS_OK;
3286 nsresult rv = ScrollContentIntoView(mLastAnchorScrolledTo,
3287 ScrollAxis(SCROLL_TOP, SCROLL_ALWAYS),
3288 ScrollAxis(),
3289 ANCHOR_SCROLL_FLAGS);
3290 mLastAnchorScrolledTo = nullptr;
3291 return rv;
3295 * Helper (per-continuation) for ScrollContentIntoView.
3297 * @param aContainerFrame [in] the frame which aRect is relative to
3298 * @param aFrame [in] Frame whose bounds should be unioned
3299 * @param aUseWholeLineHeightForInlines [in] if true, then for inline frames
3300 * we should include the top of the line in the added rectangle
3301 * @param aRect [inout] rect into which its bounds should be unioned
3302 * @param aHaveRect [inout] whether aRect contains data yet
3303 * @param aPrevBlock [inout] the block aLines is a line iterator for
3304 * @param aLines [inout] the line iterator we're using
3305 * @param aCurLine [inout] the line to start looking from in this iterator
3307 static void
3308 AccumulateFrameBounds(nsIFrame* aContainerFrame,
3309 nsIFrame* aFrame,
3310 bool aUseWholeLineHeightForInlines,
3311 nsRect& aRect,
3312 bool& aHaveRect,
3313 nsIFrame*& aPrevBlock,
3314 nsAutoLineIterator& aLines,
3315 int32_t& aCurLine)
3317 nsIFrame* frame = aFrame;
3318 nsRect frameBounds = nsRect(nsPoint(0, 0), aFrame->GetSize());
3320 // If this is an inline frame and either the bounds height is 0 (quirks
3321 // layout model) or aUseWholeLineHeightForInlines is set, we need to
3322 // change the top of the bounds to include the whole line.
3323 if (frameBounds.height == 0 || aUseWholeLineHeightForInlines) {
3324 nsIFrame *prevFrame = aFrame;
3325 nsIFrame *f = aFrame;
3327 while (f && f->IsFrameOfType(nsIFrame::eLineParticipant) &&
3328 !f->IsTransformed() && !f->IsPositioned()) {
3329 prevFrame = f;
3330 f = prevFrame->GetParent();
3333 if (f != aFrame &&
3334 f &&
3335 f->GetType() == nsGkAtoms::blockFrame) {
3336 // find the line containing aFrame and increase the top of |offset|.
3337 if (f != aPrevBlock) {
3338 aLines = f->GetLineIterator();
3339 aPrevBlock = f;
3340 aCurLine = 0;
3342 if (aLines) {
3343 int32_t index = aLines->FindLineContaining(prevFrame, aCurLine);
3344 if (index >= 0) {
3345 aCurLine = index;
3346 nsIFrame *trash1;
3347 int32_t trash2;
3348 nsRect lineBounds;
3349 uint32_t trash3;
3351 if (NS_SUCCEEDED(aLines->GetLine(index, &trash1, &trash2,
3352 lineBounds, &trash3))) {
3353 frameBounds += frame->GetOffsetTo(f);
3354 frame = f;
3355 if (lineBounds.y < frameBounds.y) {
3356 frameBounds.height = frameBounds.YMost() - lineBounds.y;
3357 frameBounds.y = lineBounds.y;
3365 nsRect transformedBounds = nsLayoutUtils::TransformFrameRectToAncestor(frame,
3366 frameBounds, aContainerFrame);
3368 if (aHaveRect) {
3369 // We can't use nsRect::UnionRect since it drops empty rects on
3370 // the floor, and we need to include them. (Thus we need
3371 // aHaveRect to know when to drop the initial value on the floor.)
3372 aRect.UnionRectEdges(aRect, transformedBounds);
3373 } else {
3374 aHaveRect = true;
3375 aRect = transformedBounds;
3379 static bool
3380 ComputeNeedToScroll(nsIPresShell::WhenToScroll aWhenToScroll,
3381 nscoord aLineSize,
3382 nscoord aRectMin,
3383 nscoord aRectMax,
3384 nscoord aViewMin,
3385 nscoord aViewMax) {
3386 // See how the rect should be positioned vertically
3387 if (nsIPresShell::SCROLL_ALWAYS == aWhenToScroll) {
3388 // The caller wants the frame as visible as possible
3389 return true;
3390 } else if (nsIPresShell::SCROLL_IF_NOT_VISIBLE == aWhenToScroll) {
3391 // Scroll only if no part of the frame is visible in this view
3392 return aRectMax - aLineSize <= aViewMin ||
3393 aRectMin + aLineSize >= aViewMax;
3394 } else if (nsIPresShell::SCROLL_IF_NOT_FULLY_VISIBLE == aWhenToScroll) {
3395 // Scroll only if part of the frame is hidden and more can fit in view
3396 return !(aRectMin >= aViewMin && aRectMax <= aViewMax) &&
3397 std::min(aViewMax, aRectMax) - std::max(aRectMin, aViewMin) < aViewMax - aViewMin;
3399 return false;
3402 static nscoord
3403 ComputeWhereToScroll(int16_t aWhereToScroll,
3404 nscoord aOriginalCoord,
3405 nscoord aRectMin,
3406 nscoord aRectMax,
3407 nscoord aViewMin,
3408 nscoord aViewMax,
3409 nscoord* aRangeMin,
3410 nscoord* aRangeMax) {
3411 nscoord resultCoord = aOriginalCoord;
3412 // Allow the scroll operation to land anywhere that
3413 // makes the whole rectangle visible.
3414 if (nsIPresShell::SCROLL_MINIMUM == aWhereToScroll) {
3415 if (aRectMin < aViewMin) {
3416 // Scroll up so the frame's top edge is visible
3417 resultCoord = aRectMin;
3418 } else if (aRectMax > aViewMax) {
3419 // Scroll down so the frame's bottom edge is visible. Make sure the
3420 // frame's top edge is still visible
3421 resultCoord = aOriginalCoord + aRectMax - aViewMax;
3422 if (resultCoord > aRectMin) {
3423 resultCoord = aRectMin;
3426 } else {
3427 nscoord frameAlignCoord =
3428 NSToCoordRound(aRectMin + (aRectMax - aRectMin) * (aWhereToScroll / 100.0f));
3429 resultCoord = NSToCoordRound(frameAlignCoord - (aViewMax - aViewMin) * (
3430 aWhereToScroll / 100.0f));
3432 nscoord scrollPortLength = aViewMax - aViewMin;
3433 // Force the scroll range to extend to include resultCoord.
3434 *aRangeMin = std::min(resultCoord, aRectMax - scrollPortLength);
3435 *aRangeMax = std::max(resultCoord, aRectMin);
3436 return resultCoord;
3440 * This function takes a scrollable frame, a rect in the coordinate system
3441 * of the scrolled frame, and a desired percentage-based scroll
3442 * position and attempts to scroll the rect to that position in the
3443 * scrollport.
3445 * This needs to work even if aRect has a width or height of zero.
3447 static void ScrollToShowRect(nsIFrame* aFrame,
3448 nsIScrollableFrame* aFrameAsScrollable,
3449 const nsRect& aRect,
3450 nsIPresShell::ScrollAxis aVertical,
3451 nsIPresShell::ScrollAxis aHorizontal,
3452 uint32_t aFlags)
3454 nsPoint scrollPt = aFrameAsScrollable->GetScrollPosition();
3455 nsRect visibleRect(scrollPt,
3456 aFrameAsScrollable->GetScrollPositionClampingScrollPortSize());
3458 // If this is the root scroll frame, make sure to take into account the
3459 // content document fixed position margins. When set, these indicate that
3460 // chrome is obscuring the viewport.
3461 nsRect targetRect(aRect);
3462 nsIPresShell *presShell = aFrame->PresContext()->PresShell();
3463 if (aFrameAsScrollable == presShell->GetRootScrollFrameAsScrollable()) {
3464 targetRect.Inflate(presShell->GetContentDocumentFixedPositionMargins());
3467 nsSize lineSize;
3468 // Don't call GetLineScrollAmount unless we actually need it. Not only
3469 // does this save time, but it's not safe to call GetLineScrollAmount
3470 // during reflow (because it depends on font size inflation and doesn't
3471 // use the in-reflow-safe font-size inflation path). If we did call it,
3472 // it would assert and possible give the wrong result.
3473 if (aVertical.mWhenToScroll == nsIPresShell::SCROLL_IF_NOT_VISIBLE ||
3474 aHorizontal.mWhenToScroll == nsIPresShell::SCROLL_IF_NOT_VISIBLE) {
3475 lineSize = aFrameAsScrollable->GetLineScrollAmount();
3477 ScrollbarStyles ss = aFrameAsScrollable->GetScrollbarStyles();
3478 nsRect allowedRange(scrollPt, nsSize(0, 0));
3479 bool needToScroll = false;
3480 uint32_t directions = aFrameAsScrollable->GetPerceivedScrollingDirections();
3482 if (((aFlags & nsIPresShell::SCROLL_OVERFLOW_HIDDEN) ||
3483 ss.mVertical != NS_STYLE_OVERFLOW_HIDDEN) &&
3484 (!aVertical.mOnlyIfPerceivedScrollableDirection ||
3485 (directions & nsIScrollableFrame::VERTICAL))) {
3487 if (ComputeNeedToScroll(aVertical.mWhenToScroll,
3488 lineSize.height,
3489 targetRect.y,
3490 targetRect.YMost(),
3491 visibleRect.y,
3492 visibleRect.YMost())) {
3493 nscoord maxHeight;
3494 scrollPt.y = ComputeWhereToScroll(aVertical.mWhereToScroll,
3495 scrollPt.y,
3496 targetRect.y,
3497 targetRect.YMost(),
3498 visibleRect.y,
3499 visibleRect.YMost(),
3500 &allowedRange.y, &maxHeight);
3501 allowedRange.height = maxHeight - allowedRange.y;
3502 needToScroll = true;
3506 if (((aFlags & nsIPresShell::SCROLL_OVERFLOW_HIDDEN) ||
3507 ss.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN) &&
3508 (!aHorizontal.mOnlyIfPerceivedScrollableDirection ||
3509 (directions & nsIScrollableFrame::HORIZONTAL))) {
3511 if (ComputeNeedToScroll(aHorizontal.mWhenToScroll,
3512 lineSize.width,
3513 targetRect.x,
3514 targetRect.XMost(),
3515 visibleRect.x,
3516 visibleRect.XMost())) {
3517 nscoord maxWidth;
3518 scrollPt.x = ComputeWhereToScroll(aHorizontal.mWhereToScroll,
3519 scrollPt.x,
3520 targetRect.x,
3521 targetRect.XMost(),
3522 visibleRect.x,
3523 visibleRect.XMost(),
3524 &allowedRange.x, &maxWidth);
3525 allowedRange.width = maxWidth - allowedRange.x;
3526 needToScroll = true;
3530 // If we don't need to scroll, then don't try since it might cancel
3531 // a current smooth scroll operation.
3532 if (needToScroll) {
3533 nsIScrollableFrame::ScrollMode scrollMode = nsIScrollableFrame::INSTANT;
3534 if (gfxPrefs::ScrollBehaviorEnabled() && aFlags & nsIPresShell::SCROLL_SMOOTH) {
3535 scrollMode = nsIScrollableFrame::SMOOTH_MSD;
3537 aFrameAsScrollable->ScrollTo(scrollPt, scrollMode, &allowedRange);
3541 nsresult
3542 PresShell::ScrollContentIntoView(nsIContent* aContent,
3543 nsIPresShell::ScrollAxis aVertical,
3544 nsIPresShell::ScrollAxis aHorizontal,
3545 uint32_t aFlags)
3547 NS_ENSURE_TRUE(aContent, NS_ERROR_NULL_POINTER);
3548 nsCOMPtr<nsIDocument> composedDoc = aContent->GetComposedDoc();
3549 NS_ENSURE_STATE(composedDoc);
3551 NS_ASSERTION(mDidInitialize, "should have done initial reflow by now");
3553 if (mContentToScrollTo) {
3554 mContentToScrollTo->DeleteProperty(nsGkAtoms::scrolling);
3556 mContentToScrollTo = aContent;
3557 ScrollIntoViewData* data = new ScrollIntoViewData();
3558 data->mContentScrollVAxis = aVertical;
3559 data->mContentScrollHAxis = aHorizontal;
3560 data->mContentToScrollToFlags = aFlags;
3561 if (NS_FAILED(mContentToScrollTo->SetProperty(nsGkAtoms::scrolling, data,
3562 nsINode::DeleteProperty<PresShell::ScrollIntoViewData>))) {
3563 mContentToScrollTo = nullptr;
3566 // Flush layout and attempt to scroll in the process.
3567 composedDoc->SetNeedLayoutFlush();
3568 composedDoc->FlushPendingNotifications(Flush_InterruptibleLayout);
3570 // If mContentToScrollTo is non-null, that means we interrupted the reflow
3571 // (or suppressed it altogether because we're suppressing interruptible
3572 // flushes right now) and won't necessarily get the position correct, but do
3573 // a best-effort scroll here. The other option would be to do this inside
3574 // FlushPendingNotifications, but I'm not sure the repeated scrolling that
3575 // could trigger if reflows keep getting interrupted would be more desirable
3576 // than a single best-effort scroll followed by one final scroll on the first
3577 // completed reflow.
3578 if (mContentToScrollTo) {
3579 DoScrollContentIntoView();
3581 return NS_OK;
3584 void
3585 PresShell::DoScrollContentIntoView()
3587 NS_ASSERTION(mDidInitialize, "should have done initial reflow by now");
3589 nsIFrame* frame = mContentToScrollTo->GetPrimaryFrame();
3590 if (!frame) {
3591 mContentToScrollTo->DeleteProperty(nsGkAtoms::scrolling);
3592 mContentToScrollTo = nullptr;
3593 return;
3596 if (frame->GetStateBits() & NS_FRAME_FIRST_REFLOW) {
3597 // The reflow flush before this scroll got interrupted, and this frame's
3598 // coords and size are all zero, and it has no content showing anyway.
3599 // Don't bother scrolling to it. We'll try again when we finish up layout.
3600 return;
3603 // Make sure we skip 'frame' ... if it's scrollable, we should use its
3604 // scrollable ancestor as the container.
3605 nsIFrame* container =
3606 nsLayoutUtils::GetClosestFrameOfType(frame->GetParent(), nsGkAtoms::scrollFrame);
3607 if (!container) {
3608 // nothing can be scrolled
3609 return;
3612 ScrollIntoViewData* data = static_cast<ScrollIntoViewData*>(
3613 mContentToScrollTo->GetProperty(nsGkAtoms::scrolling));
3614 if (MOZ_UNLIKELY(!data)) {
3615 mContentToScrollTo = nullptr;
3616 return;
3619 // This is a two-step process.
3620 // Step 1: Find the bounds of the rect we want to scroll into view. For
3621 // example, for an inline frame we may want to scroll in the whole
3622 // line, or we may want to scroll multiple lines into view.
3623 // Step 2: Walk container frame and its ancestors and scroll them
3624 // appropriately.
3625 // frameBounds is relative to container. We're assuming
3626 // that scrollframes don't split so every continuation of frame will
3627 // be a descendant of container. (Things would still mostly work
3628 // even if that assumption was false.)
3629 nsRect frameBounds;
3630 bool haveRect = false;
3631 bool useWholeLineHeightForInlines =
3632 data->mContentScrollVAxis.mWhenToScroll != nsIPresShell::SCROLL_IF_NOT_FULLY_VISIBLE;
3633 // Reuse the same line iterator across calls to AccumulateFrameBounds. We set
3634 // it every time we detect a new block (stored in prevBlock).
3635 nsIFrame* prevBlock = nullptr;
3636 nsAutoLineIterator lines;
3637 // The last line we found a continuation on in |lines|. We assume that later
3638 // continuations cannot come on earlier lines.
3639 int32_t curLine = 0;
3640 do {
3641 AccumulateFrameBounds(container, frame, useWholeLineHeightForInlines,
3642 frameBounds, haveRect, prevBlock, lines, curLine);
3643 } while ((frame = frame->GetNextContinuation()));
3645 ScrollFrameRectIntoView(container, frameBounds, data->mContentScrollVAxis,
3646 data->mContentScrollHAxis,
3647 data->mContentToScrollToFlags);
3650 bool
3651 PresShell::ScrollFrameRectIntoView(nsIFrame* aFrame,
3652 const nsRect& aRect,
3653 nsIPresShell::ScrollAxis aVertical,
3654 nsIPresShell::ScrollAxis aHorizontal,
3655 uint32_t aFlags)
3657 bool didScroll = false;
3658 // This function needs to work even if rect has a width or height of 0.
3659 nsRect rect = aRect;
3660 nsIFrame* container = aFrame;
3661 // Walk up the frame hierarchy scrolling the rect into view and
3662 // keeping rect relative to container
3663 do {
3664 nsIScrollableFrame* sf = do_QueryFrame(container);
3665 if (sf) {
3666 nsPoint oldPosition = sf->GetScrollPosition();
3667 nsRect targetRect = rect;
3668 if (container->StyleDisplay()->mOverflowClipBox ==
3669 NS_STYLE_OVERFLOW_CLIP_BOX_CONTENT_BOX) {
3670 nsMargin padding = container->GetUsedPadding();
3671 targetRect.Inflate(padding);
3673 ScrollToShowRect(container, sf, targetRect - sf->GetScrolledFrame()->GetPosition(),
3674 aVertical, aHorizontal, aFlags);
3675 nsPoint newPosition = sf->GetScrollPosition();
3676 // If the scroll position increased, that means our content moved up,
3677 // so our rect's offset should decrease
3678 rect += oldPosition - newPosition;
3680 if (oldPosition != newPosition) {
3681 didScroll = true;
3684 // only scroll one container when this flag is set
3685 if (aFlags & nsIPresShell::SCROLL_FIRST_ANCESTOR_ONLY) {
3686 break;
3689 nsIFrame* parent;
3690 if (container->IsTransformed()) {
3691 container->GetTransformMatrix(nullptr, &parent);
3692 rect = nsLayoutUtils::TransformFrameRectToAncestor(container, rect, parent);
3693 } else {
3694 rect += container->GetPosition();
3695 parent = container->GetParent();
3697 if (!parent && !(aFlags & nsIPresShell::SCROLL_NO_PARENT_FRAMES)) {
3698 nsPoint extraOffset(0,0);
3699 parent = nsLayoutUtils::GetCrossDocParentFrame(container, &extraOffset);
3700 if (parent) {
3701 int32_t APD = container->PresContext()->AppUnitsPerDevPixel();
3702 int32_t parentAPD = parent->PresContext()->AppUnitsPerDevPixel();
3703 rect = rect.ConvertAppUnitsRoundOut(APD, parentAPD);
3704 rect += extraOffset;
3707 container = parent;
3708 } while (container);
3710 return didScroll;
3713 nsRectVisibility
3714 PresShell::GetRectVisibility(nsIFrame* aFrame,
3715 const nsRect &aRect,
3716 nscoord aMinTwips) const
3718 NS_ASSERTION(aFrame->PresContext() == GetPresContext(),
3719 "prescontext mismatch?");
3720 nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
3721 NS_ASSERTION(rootFrame,
3722 "How can someone have a frame for this presshell when there's no root?");
3723 nsIScrollableFrame* sf = GetRootScrollFrameAsScrollable();
3724 nsRect scrollPortRect;
3725 if (sf) {
3726 scrollPortRect = sf->GetScrollPortRect();
3727 nsIFrame* f = do_QueryFrame(sf);
3728 scrollPortRect += f->GetOffsetTo(rootFrame);
3729 } else {
3730 scrollPortRect = nsRect(nsPoint(0,0), rootFrame->GetSize());
3733 nsRect r = aRect + aFrame->GetOffsetTo(rootFrame);
3734 // If aRect is entirely visible then we don't need to ensure that
3735 // at least aMinTwips of it is visible
3736 if (scrollPortRect.Contains(r))
3737 return nsRectVisibility_kVisible;
3739 nsRect insetRect = scrollPortRect;
3740 insetRect.Deflate(aMinTwips, aMinTwips);
3741 if (r.YMost() <= insetRect.y)
3742 return nsRectVisibility_kAboveViewport;
3743 if (r.y >= insetRect.YMost())
3744 return nsRectVisibility_kBelowViewport;
3745 if (r.XMost() <= insetRect.x)
3746 return nsRectVisibility_kLeftOfViewport;
3747 if (r.x >= insetRect.XMost())
3748 return nsRectVisibility_kRightOfViewport;
3750 return nsRectVisibility_kVisible;
3753 class PaintTimerCallBack MOZ_FINAL : public nsITimerCallback
3755 public:
3756 explicit PaintTimerCallBack(PresShell* aShell) : mShell(aShell) {}
3758 NS_DECL_ISUPPORTS
3760 NS_IMETHODIMP Notify(nsITimer* aTimer) MOZ_FINAL
3762 mShell->SetNextPaintCompressed();
3763 mShell->AddInvalidateHiddenPresShellObserver(mShell->GetPresContext()->RefreshDriver());
3764 mShell->ScheduleViewManagerFlush();
3765 return NS_OK;
3768 private:
3769 ~PaintTimerCallBack() {}
3771 PresShell* mShell;
3774 NS_IMPL_ISUPPORTS(PaintTimerCallBack, nsITimerCallback)
3776 void
3777 PresShell::ScheduleViewManagerFlush(PaintType aType)
3779 if (aType == PAINT_DELAYED_COMPRESS) {
3780 // Delay paint for 1 second.
3781 static const uint32_t kPaintDelayPeriod = 1000;
3782 if (!mDelayedPaintTimer) {
3783 mDelayedPaintTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
3784 nsRefPtr<PaintTimerCallBack> cb = new PaintTimerCallBack(this);
3785 mDelayedPaintTimer->InitWithCallback(cb, kPaintDelayPeriod, nsITimer::TYPE_ONE_SHOT);
3787 return;
3790 nsPresContext* presContext = GetPresContext();
3791 if (presContext) {
3792 presContext->RefreshDriver()->ScheduleViewManagerFlush();
3794 if (mDocument) {
3795 mDocument->SetNeedLayoutFlush();
3799 void
3800 PresShell::DispatchSynthMouseMove(WidgetGUIEvent* aEvent,
3801 bool aFlushOnHoverChange)
3803 RestyleManager* restyleManager = mPresContext->RestyleManager();
3804 uint32_t hoverGenerationBefore = restyleManager->GetHoverGeneration();
3805 nsEventStatus status;
3806 nsView* targetView = nsView::GetViewFor(aEvent->widget);
3807 if (!targetView)
3808 return;
3809 targetView->GetViewManager()->DispatchEvent(aEvent, targetView, &status);
3810 if (MOZ_UNLIKELY(mIsDestroying)) {
3811 return;
3813 if (aFlushOnHoverChange &&
3814 hoverGenerationBefore != restyleManager->GetHoverGeneration()) {
3815 // Flush so that the resulting reflow happens now so that our caller
3816 // can suppress any synthesized mouse moves caused by that reflow.
3817 FlushPendingNotifications(Flush_Layout);
3821 void
3822 PresShell::ClearMouseCaptureOnView(nsView* aView)
3824 if (gCaptureInfo.mContent) {
3825 if (aView) {
3826 // if a view was specified, ensure that the captured content is within
3827 // this view.
3828 nsIFrame* frame = gCaptureInfo.mContent->GetPrimaryFrame();
3829 if (frame) {
3830 nsView* view = frame->GetClosestView();
3831 // if there is no view, capturing won't be handled any more, so
3832 // just release the capture.
3833 if (view) {
3834 do {
3835 if (view == aView) {
3836 NS_RELEASE(gCaptureInfo.mContent);
3837 // the view containing the captured content likely disappeared so
3838 // disable capture for now.
3839 gCaptureInfo.mAllowed = false;
3840 break;
3843 view = view->GetParent();
3844 } while (view);
3845 // return if the view wasn't found
3846 return;
3851 NS_RELEASE(gCaptureInfo.mContent);
3854 // disable mouse capture until the next mousedown as a dialog has opened
3855 // or a drag has started. Otherwise, someone could start capture during
3856 // the modal dialog or drag.
3857 gCaptureInfo.mAllowed = false;
3860 void
3861 nsIPresShell::ClearMouseCapture(nsIFrame* aFrame)
3863 if (!gCaptureInfo.mContent) {
3864 gCaptureInfo.mAllowed = false;
3865 return;
3868 // null frame argument means clear the capture
3869 if (!aFrame) {
3870 NS_RELEASE(gCaptureInfo.mContent);
3871 gCaptureInfo.mAllowed = false;
3872 return;
3875 nsIFrame* capturingFrame = gCaptureInfo.mContent->GetPrimaryFrame();
3876 if (!capturingFrame) {
3877 NS_RELEASE(gCaptureInfo.mContent);
3878 gCaptureInfo.mAllowed = false;
3879 return;
3882 if (nsLayoutUtils::IsAncestorFrameCrossDoc(aFrame, capturingFrame)) {
3883 NS_RELEASE(gCaptureInfo.mContent);
3884 gCaptureInfo.mAllowed = false;
3888 nsresult
3889 PresShell::CaptureHistoryState(nsILayoutHistoryState** aState)
3891 NS_PRECONDITION(nullptr != aState, "null state pointer");
3893 // We actually have to mess with the docshell here, since we want to
3894 // store the state back in it.
3895 // XXXbz this isn't really right, since this is being called in the
3896 // content viewer's Hide() method... by that point the docshell's
3897 // state could be wrong. We should sort out a better ownership
3898 // model for the layout history state.
3899 nsCOMPtr<nsIDocShell> docShell(mPresContext->GetDocShell());
3900 if (!docShell)
3901 return NS_ERROR_FAILURE;
3903 nsCOMPtr<nsILayoutHistoryState> historyState;
3904 docShell->GetLayoutHistoryState(getter_AddRefs(historyState));
3905 if (!historyState) {
3906 // Create the document state object
3907 historyState = NS_NewLayoutHistoryState();
3908 docShell->SetLayoutHistoryState(historyState);
3911 *aState = historyState;
3912 NS_IF_ADDREF(*aState);
3914 // Capture frame state for the entire frame hierarchy
3915 nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
3916 if (!rootFrame) return NS_OK;
3918 mFrameConstructor->CaptureFrameState(rootFrame, historyState);
3920 return NS_OK;
3923 void
3924 PresShell::UnsuppressAndInvalidate()
3926 // Note: We ignore the EnsureVisible check for resource documents, because
3927 // they won't have a docshell, so they'll always fail EnsureVisible.
3928 if ((!mDocument->IsResourceDoc() && !mPresContext->EnsureVisible()) ||
3929 mHaveShutDown) {
3930 // No point; we're about to be torn down anyway.
3931 return;
3934 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 nsContentUtils::AddScriptRunner(new nsBeforeFirstPaintDispatcher(mDocument));
3941 mPaintingSuppressed = false;
3942 nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
3943 if (rootFrame) {
3944 // let's assume that outline on a root frame is not supported
3945 rootFrame->InvalidateFrame();
3947 if (mTouchCaret) {
3948 mTouchCaret->UpdatePositionIfNeeded();
3952 // now that painting is unsuppressed, focus may be set on the document
3953 nsPIDOMWindow *win = mDocument->GetWindow();
3954 if (win)
3955 win->SetReadyForFocus();
3957 if (!mHaveShutDown) {
3958 SynthesizeMouseMove(false);
3959 ScheduleImageVisibilityUpdate();
3963 void
3964 PresShell::UnsuppressPainting()
3966 if (mPaintSuppressionTimer) {
3967 mPaintSuppressionTimer->Cancel();
3968 mPaintSuppressionTimer = nullptr;
3971 if (mIsDocumentGone || !mPaintingSuppressed)
3972 return;
3974 // If we have reflows pending, just wait until we process
3975 // the reflows and get all the frames where we want them
3976 // before actually unlocking the painting. Otherwise
3977 // go ahead and unlock now.
3978 if (!mDirtyRoots.IsEmpty())
3979 mShouldUnsuppressPainting = true;
3980 else
3981 UnsuppressAndInvalidate();
3984 // Post a request to handle an arbitrary callback after reflow has finished.
3985 nsresult
3986 PresShell::PostReflowCallback(nsIReflowCallback* aCallback)
3988 void* result = AllocateMisc(sizeof(nsCallbackEventRequest));
3989 nsCallbackEventRequest* request = (nsCallbackEventRequest*)result;
3991 request->callback = aCallback;
3992 request->next = nullptr;
3994 if (mLastCallbackEventRequest) {
3995 mLastCallbackEventRequest = mLastCallbackEventRequest->next = request;
3996 } else {
3997 mFirstCallbackEventRequest = request;
3998 mLastCallbackEventRequest = request;
4001 return NS_OK;
4004 void
4005 PresShell::CancelReflowCallback(nsIReflowCallback* aCallback)
4007 nsCallbackEventRequest* before = nullptr;
4008 nsCallbackEventRequest* node = mFirstCallbackEventRequest;
4009 while(node)
4011 nsIReflowCallback* callback = node->callback;
4013 if (callback == aCallback)
4015 nsCallbackEventRequest* toFree = node;
4016 if (node == mFirstCallbackEventRequest) {
4017 node = node->next;
4018 mFirstCallbackEventRequest = node;
4019 NS_ASSERTION(before == nullptr, "impossible");
4020 } else {
4021 node = node->next;
4022 before->next = node;
4025 if (toFree == mLastCallbackEventRequest) {
4026 mLastCallbackEventRequest = before;
4029 FreeMisc(sizeof(nsCallbackEventRequest), toFree);
4030 } else {
4031 before = node;
4032 node = node->next;
4037 void
4038 PresShell::CancelPostedReflowCallbacks()
4040 while (mFirstCallbackEventRequest) {
4041 nsCallbackEventRequest* node = mFirstCallbackEventRequest;
4042 mFirstCallbackEventRequest = node->next;
4043 if (!mFirstCallbackEventRequest) {
4044 mLastCallbackEventRequest = nullptr;
4046 nsIReflowCallback* callback = node->callback;
4047 FreeMisc(sizeof(nsCallbackEventRequest), node);
4048 if (callback) {
4049 callback->ReflowCallbackCanceled();
4054 void
4055 PresShell::HandlePostedReflowCallbacks(bool aInterruptible)
4057 bool shouldFlush = false;
4059 while (mFirstCallbackEventRequest) {
4060 nsCallbackEventRequest* node = mFirstCallbackEventRequest;
4061 mFirstCallbackEventRequest = node->next;
4062 if (!mFirstCallbackEventRequest) {
4063 mLastCallbackEventRequest = nullptr;
4065 nsIReflowCallback* callback = node->callback;
4066 FreeMisc(sizeof(nsCallbackEventRequest), node);
4067 if (callback) {
4068 if (callback->ReflowFinished()) {
4069 shouldFlush = true;
4074 mozFlushType flushType =
4075 aInterruptible ? Flush_InterruptibleLayout : Flush_Layout;
4076 if (shouldFlush && !mIsDestroying) {
4077 FlushPendingNotifications(flushType);
4081 bool
4082 PresShell::IsSafeToFlush() const
4084 // Not safe if we are reflowing or in the middle of frame construction
4085 bool isSafeToFlush = !mIsReflowing &&
4086 !mChangeNestCount;
4088 if (isSafeToFlush) {
4089 // Not safe if we are painting
4090 nsViewManager* viewManager = GetViewManager();
4091 if (viewManager) {
4092 bool isPainting = false;
4093 viewManager->IsPainting(isPainting);
4094 if (isPainting) {
4095 isSafeToFlush = false;
4100 return isSafeToFlush;
4104 void
4105 PresShell::FlushPendingNotifications(mozFlushType aType)
4107 // by default, flush animations if aType >= Flush_Style
4108 mozilla::ChangesToFlush flush(aType, aType >= Flush_Style);
4109 FlushPendingNotifications(flush);
4112 void
4113 PresShell::FlushPendingNotifications(mozilla::ChangesToFlush aFlush)
4115 if (mIsZombie) {
4116 return;
4120 * VERY IMPORTANT: If you add some sort of new flushing to this
4121 * method, make sure to add the relevant SetNeedLayoutFlush or
4122 * SetNeedStyleFlush calls on the document.
4124 mozFlushType flushType = aFlush.mFlushType;
4126 #ifdef MOZ_ENABLE_PROFILER_SPS
4127 static const char flushTypeNames[][20] = {
4128 "Content",
4129 "ContentAndNotify",
4130 "Style",
4131 "InterruptibleLayout",
4132 "Layout",
4133 "Display"
4136 // Make sure that we don't miss things added to mozFlushType!
4137 MOZ_ASSERT(static_cast<uint32_t>(flushType) <= ArrayLength(flushTypeNames));
4139 PROFILER_LABEL_PRINTF("PresShell", "Flush",
4140 js::ProfileEntry::Category::GRAPHICS, "(Flush_%s)", flushTypeNames[flushType - 1]);
4141 #endif
4143 #ifdef ACCESSIBILITY
4144 #ifdef DEBUG
4145 nsAccessibilityService* accService = GetAccService();
4146 if (accService) {
4147 NS_ASSERTION(!accService->IsProcessingRefreshDriverNotification(),
4148 "Flush during accessible tree update!");
4150 #endif
4151 #endif
4153 NS_ASSERTION(flushType >= Flush_Frames, "Why did we get called?");
4155 bool isSafeToFlush = IsSafeToFlush();
4157 // If layout could possibly trigger scripts, then it's only safe to flush if
4158 // it's safe to run script.
4159 bool hasHadScriptObject;
4160 if (mDocument->GetScriptHandlingObject(hasHadScriptObject) ||
4161 hasHadScriptObject) {
4162 isSafeToFlush = isSafeToFlush && nsContentUtils::IsSafeToRunScript();
4165 NS_ASSERTION(!isSafeToFlush || mViewManager, "Must have view manager");
4166 // Make sure the view manager stays alive.
4167 nsRefPtr<nsViewManager> viewManagerDeathGrip = mViewManager;
4168 bool didStyleFlush = false;
4169 bool didLayoutFlush = false;
4170 if (isSafeToFlush && mViewManager) {
4171 // Processing pending notifications can kill us, and some callers only
4172 // hold weak refs when calling FlushPendingNotifications(). :(
4173 nsCOMPtr<nsIPresShell> kungFuDeathGrip(this);
4175 if (mResizeEvent.IsPending()) {
4176 FireResizeEvent();
4177 if (mIsDestroying) {
4178 return;
4182 // We need to make sure external resource documents are flushed too (for
4183 // example, svg filters that reference a filter in an external document
4184 // need the frames in the external document to be constructed for the
4185 // filter to work). We only need external resources to be flushed when the
4186 // main document is flushing >= Flush_Frames, so we flush external
4187 // resources here instead of nsDocument::FlushPendingNotifications.
4188 mDocument->FlushExternalResources(flushType);
4190 // Force flushing of any pending content notifications that might have
4191 // queued up while our event was pending. That will ensure that we don't
4192 // construct frames for content right now that's still waiting to be
4193 // notified on,
4194 mDocument->FlushPendingNotifications(Flush_ContentAndNotify);
4196 // Process pending restyles, since any flush of the presshell wants
4197 // up-to-date style data.
4198 if (!mIsDestroying) {
4199 mViewManager->FlushDelayedResize(false);
4200 mPresContext->FlushPendingMediaFeatureValuesChanged();
4202 // Flush any pending update of the user font set, since that could
4203 // cause style changes (for updating ex/ch units, and to cause a
4204 // reflow).
4205 mPresContext->FlushUserFontSet();
4207 mPresContext->FlushCounterStyles();
4209 // Flush any requested SMIL samples.
4210 if (mDocument->HasAnimationController()) {
4211 mDocument->GetAnimationController()->FlushResampleRequests();
4214 if (aFlush.mFlushAnimations &&
4215 !mPresContext->StyleUpdateForAllAnimationsIsUpToDate()) {
4216 if (mPresContext->AnimationManager()) {
4217 mPresContext->AnimationManager()->
4218 FlushAnimations(CommonAnimationManager::Cannot_Throttle);
4220 if (mPresContext->TransitionManager()) {
4221 mPresContext->TransitionManager()->
4222 FlushTransitions(CommonAnimationManager::Cannot_Throttle);
4224 mPresContext->TickLastStyleUpdateForAllAnimations();
4227 // The FlushResampleRequests() above flushed style changes.
4228 if (!mIsDestroying) {
4229 nsAutoScriptBlocker scriptBlocker;
4230 mPresContext->RestyleManager()->ProcessPendingRestyles();
4234 // Dispatch any 'animationstart' events those (or earlier) restyles
4235 // queued up.
4236 if (!mIsDestroying) {
4237 mPresContext->AnimationManager()->DispatchEvents();
4240 // Process whatever XBL constructors those restyles queued up. This
4241 // ensures that onload doesn't fire too early and that we won't do extra
4242 // reflows after those constructors run.
4243 if (!mIsDestroying) {
4244 mDocument->BindingManager()->ProcessAttachedQueue();
4247 // Now those constructors or events might have posted restyle
4248 // events. At the same time, we still need up-to-date style data.
4249 // In particular, reflow depends on style being completely up to
4250 // date. If it's not, then style context reparenting, which can
4251 // happen during reflow, might suddenly pick up the new rules and
4252 // we'll end up with frames whose style doesn't match the frame
4253 // type.
4254 if (!mIsDestroying) {
4255 nsAutoScriptBlocker scriptBlocker;
4256 mPresContext->RestyleManager()->ProcessPendingRestyles();
4259 didStyleFlush = true;
4262 // There might be more pending constructors now, but we're not going to
4263 // worry about them. They can't be triggered during reflow, so we should
4264 // be good.
4266 if (flushType >= (mSuppressInterruptibleReflows ? Flush_Layout : Flush_InterruptibleLayout) &&
4267 !mIsDestroying) {
4268 didLayoutFlush = true;
4269 mFrameConstructor->RecalcQuotesAndCounters();
4270 mViewManager->FlushDelayedResize(true);
4271 if (ProcessReflowCommands(flushType < Flush_Layout) && mContentToScrollTo) {
4272 // We didn't get interrupted. Go ahead and scroll to our content
4273 DoScrollContentIntoView();
4274 if (mContentToScrollTo) {
4275 mContentToScrollTo->DeleteProperty(nsGkAtoms::scrolling);
4276 mContentToScrollTo = nullptr;
4281 if (flushType >= Flush_Layout) {
4282 if (!mIsDestroying) {
4283 mViewManager->UpdateWidgetGeometry();
4288 if (!didStyleFlush && flushType >= Flush_Style && !mIsDestroying) {
4289 mDocument->SetNeedStyleFlush();
4292 if (!didLayoutFlush && !mIsDestroying &&
4293 (flushType >=
4294 (mSuppressInterruptibleReflows ? Flush_Layout : Flush_InterruptibleLayout))) {
4295 // We suppressed this flush due to mSuppressInterruptibleReflows or
4296 // !isSafeToFlush, but the document thinks it doesn't
4297 // need to flush anymore. Let it know what's really going on.
4298 mDocument->SetNeedLayoutFlush();
4302 void
4303 PresShell::CharacterDataChanged(nsIDocument *aDocument,
4304 nsIContent* aContent,
4305 CharacterDataChangeInfo* aInfo)
4307 NS_PRECONDITION(!mIsDocumentGone, "Unexpected CharacterDataChanged");
4308 NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
4310 nsAutoCauseReflowNotifier crNotifier(this);
4312 // Call this here so it only happens for real content mutations and
4313 // not cases when the frame constructor calls its own methods to force
4314 // frame reconstruction.
4315 nsIContent *container = aContent->GetParent();
4316 uint32_t selectorFlags =
4317 container ? (container->GetFlags() & NODE_ALL_SELECTOR_FLAGS) : 0;
4318 if (selectorFlags != 0 && !aContent->IsRootOfAnonymousSubtree()) {
4319 Element* element = container->AsElement();
4320 if (aInfo->mAppend && !aContent->GetNextSibling())
4321 mPresContext->RestyleManager()->RestyleForAppend(element, aContent);
4322 else
4323 mPresContext->RestyleManager()->RestyleForInsertOrChange(element, aContent);
4326 mFrameConstructor->CharacterDataChanged(aContent, aInfo);
4327 VERIFY_STYLE_TREE;
4330 void
4331 PresShell::ContentStateChanged(nsIDocument* aDocument,
4332 nsIContent* aContent,
4333 EventStates aStateMask)
4335 NS_PRECONDITION(!mIsDocumentGone, "Unexpected ContentStateChanged");
4336 NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
4338 if (mDidInitialize) {
4339 nsAutoCauseReflowNotifier crNotifier(this);
4340 mPresContext->RestyleManager()->ContentStateChanged(aContent, aStateMask);
4341 VERIFY_STYLE_TREE;
4345 void
4346 PresShell::DocumentStatesChanged(nsIDocument* aDocument,
4347 EventStates aStateMask)
4349 NS_PRECONDITION(!mIsDocumentGone, "Unexpected DocumentStatesChanged");
4350 NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
4352 if (mDidInitialize &&
4353 mStyleSet->HasDocumentStateDependentStyle(mPresContext,
4354 mDocument->GetRootElement(),
4355 aStateMask)) {
4356 mPresContext->RestyleManager()->PostRestyleEvent(mDocument->GetRootElement(),
4357 eRestyle_Subtree,
4358 NS_STYLE_HINT_NONE);
4359 VERIFY_STYLE_TREE;
4362 if (aStateMask.HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE)) {
4363 nsIFrame* root = mFrameConstructor->GetRootFrame();
4364 if (root) {
4365 root->SchedulePaint();
4370 void
4371 PresShell::AttributeWillChange(nsIDocument* aDocument,
4372 Element* aElement,
4373 int32_t aNameSpaceID,
4374 nsIAtom* aAttribute,
4375 int32_t aModType)
4377 NS_PRECONDITION(!mIsDocumentGone, "Unexpected AttributeWillChange");
4378 NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
4380 // XXXwaterson it might be more elegant to wait until after the
4381 // initial reflow to begin observing the document. That would
4382 // squelch any other inappropriate notifications as well.
4383 if (mDidInitialize) {
4384 nsAutoCauseReflowNotifier crNotifier(this);
4385 mPresContext->RestyleManager()->AttributeWillChange(aElement, aNameSpaceID,
4386 aAttribute, aModType);
4387 VERIFY_STYLE_TREE;
4391 void
4392 PresShell::AttributeChanged(nsIDocument* aDocument,
4393 Element* aElement,
4394 int32_t aNameSpaceID,
4395 nsIAtom* aAttribute,
4396 int32_t aModType)
4398 NS_PRECONDITION(!mIsDocumentGone, "Unexpected AttributeChanged");
4399 NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
4401 // XXXwaterson it might be more elegant to wait until after the
4402 // initial reflow to begin observing the document. That would
4403 // squelch any other inappropriate notifications as well.
4404 if (mDidInitialize) {
4405 nsAutoCauseReflowNotifier crNotifier(this);
4406 mPresContext->RestyleManager()->AttributeChanged(aElement, aNameSpaceID,
4407 aAttribute, aModType);
4408 VERIFY_STYLE_TREE;
4412 void
4413 PresShell::ContentAppended(nsIDocument *aDocument,
4414 nsIContent* aContainer,
4415 nsIContent* aFirstNewContent,
4416 int32_t aNewIndexInContainer)
4418 NS_PRECONDITION(!mIsDocumentGone, "Unexpected ContentAppended");
4419 NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
4420 NS_PRECONDITION(aContainer, "must have container");
4422 if (!mDidInitialize) {
4423 return;
4426 nsAutoCauseReflowNotifier crNotifier(this);
4428 // Call this here so it only happens for real content mutations and
4429 // not cases when the frame constructor calls its own methods to force
4430 // frame reconstruction.
4431 if (aContainer->IsElement()) {
4432 // Ensure the container is an element before trying to restyle
4433 // because it can be the case that the container is a ShadowRoot
4434 // which is a document fragment.
4435 mPresContext->RestyleManager()->
4436 RestyleForAppend(aContainer->AsElement(), aFirstNewContent);
4439 mFrameConstructor->ContentAppended(aContainer, aFirstNewContent, true);
4441 if (static_cast<nsINode*>(aContainer) == static_cast<nsINode*>(aDocument) &&
4442 aFirstNewContent->NodeType() == nsIDOMNode::DOCUMENT_TYPE_NODE) {
4443 NotifyFontSizeInflationEnabledIsDirty();
4446 VERIFY_STYLE_TREE;
4449 void
4450 PresShell::ContentInserted(nsIDocument* aDocument,
4451 nsIContent* aContainer,
4452 nsIContent* aChild,
4453 int32_t aIndexInContainer)
4455 NS_PRECONDITION(!mIsDocumentGone, "Unexpected ContentInserted");
4456 NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
4458 if (!mDidInitialize) {
4459 return;
4462 nsAutoCauseReflowNotifier crNotifier(this);
4464 // Call this here so it only happens for real content mutations and
4465 // not cases when the frame constructor calls its own methods to force
4466 // frame reconstruction.
4467 if (aContainer && aContainer->IsElement()) {
4468 // Ensure the container is an element before trying to restyle
4469 // because it can be the case that the container is a ShadowRoot
4470 // which is a document fragment.
4471 mPresContext->RestyleManager()->
4472 RestyleForInsertOrChange(aContainer->AsElement(), aChild);
4475 mFrameConstructor->ContentInserted(aContainer, aChild, nullptr, true);
4477 if (((!aContainer && aDocument) ||
4478 (static_cast<nsINode*>(aContainer) == static_cast<nsINode*>(aDocument))) &&
4479 aChild->NodeType() == nsIDOMNode::DOCUMENT_TYPE_NODE) {
4480 NotifyFontSizeInflationEnabledIsDirty();
4483 VERIFY_STYLE_TREE;
4486 void
4487 PresShell::ContentRemoved(nsIDocument *aDocument,
4488 nsIContent* aContainer,
4489 nsIContent* aChild,
4490 int32_t aIndexInContainer,
4491 nsIContent* aPreviousSibling)
4493 NS_PRECONDITION(!mIsDocumentGone, "Unexpected ContentRemoved");
4494 NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
4496 // Notify the ESM that the content has been removed, so that
4497 // it can clean up any state related to the content.
4499 // XXX_jwir3: There is no null check for aDocument necessary, since, even
4500 // though by nsIMutationObserver, aDocument could be null, the
4501 // precondition check that mDocument == aDocument ensures that
4502 // aDocument will not be null (since mDocument can't be null unless
4503 // we're still intializing).
4504 mPresContext->EventStateManager()->ContentRemoved(aDocument, aChild);
4506 nsAutoCauseReflowNotifier crNotifier(this);
4508 // Call this here so it only happens for real content mutations and
4509 // not cases when the frame constructor calls its own methods to force
4510 // frame reconstruction.
4511 nsIContent* oldNextSibling;
4512 if (aContainer) {
4513 oldNextSibling = aContainer->GetChildAt(aIndexInContainer);
4514 } else {
4515 oldNextSibling = nullptr;
4518 if (aContainer && aContainer->IsElement()) {
4519 mPresContext->RestyleManager()->
4520 RestyleForRemove(aContainer->AsElement(), aChild, oldNextSibling);
4523 bool didReconstruct;
4524 mFrameConstructor->ContentRemoved(aContainer, aChild, oldNextSibling,
4525 nsCSSFrameConstructor::REMOVE_CONTENT,
4526 &didReconstruct);
4529 if (((aContainer &&
4530 static_cast<nsINode*>(aContainer) == static_cast<nsINode*>(aDocument)) ||
4531 aDocument) && aChild->NodeType() == nsIDOMNode::DOCUMENT_TYPE_NODE) {
4532 NotifyFontSizeInflationEnabledIsDirty();
4535 VERIFY_STYLE_TREE;
4538 void
4539 PresShell::NotifyCounterStylesAreDirty()
4541 nsAutoCauseReflowNotifier reflowNotifier(this);
4542 mFrameConstructor->BeginUpdate();
4543 mFrameConstructor->NotifyCounterStylesAreDirty();
4544 mFrameConstructor->EndUpdate();
4547 nsresult
4548 PresShell::ReconstructFrames(void)
4550 NS_PRECONDITION(!mFrameConstructor->GetRootFrame() || mDidInitialize,
4551 "Must not have root frame before initial reflow");
4552 if (!mDidInitialize) {
4553 // Nothing to do here
4554 return NS_OK;
4557 nsCOMPtr<nsIPresShell> kungFuDeathGrip(this);
4559 // Have to make sure that the content notifications are flushed before we
4560 // start messing with the frame model; otherwise we can get content doubling.
4561 mDocument->FlushPendingNotifications(Flush_ContentAndNotify);
4563 nsAutoCauseReflowNotifier crNotifier(this);
4564 mFrameConstructor->BeginUpdate();
4565 nsresult rv = mFrameConstructor->ReconstructDocElementHierarchy();
4566 VERIFY_STYLE_TREE;
4567 mFrameConstructor->EndUpdate();
4569 return rv;
4572 void
4573 nsIPresShell::ReconstructStyleDataInternal()
4575 nsAutoTArray<nsRefPtr<mozilla::dom::Element>,1> scopeRoots;
4576 mChangedScopeStyleRoots.SwapElements(scopeRoots);
4578 if (mStylesHaveChanged) {
4579 // If we need to restyle everything, no need to restyle individual
4580 // scoped style roots.
4581 scopeRoots.Clear();
4584 mStylesHaveChanged = false;
4586 if (mIsDestroying) {
4587 // We don't want to mess with restyles at this point
4588 return;
4591 if (mPresContext) {
4592 mPresContext->RebuildUserFontSet();
4593 mPresContext->RebuildCounterStyles();
4596 Element* root = mDocument->GetRootElement();
4597 if (!mDidInitialize) {
4598 // Nothing to do here, since we have no frames yet
4599 return;
4602 if (!root) {
4603 // No content to restyle
4604 return;
4607 RestyleManager* restyleManager = mPresContext->RestyleManager();
4608 if (scopeRoots.IsEmpty()) {
4609 // If scopeRoots is empty, we know that mStylesHaveChanged was true at
4610 // the beginning of this function, and that we need to restyle the whole
4611 // document.
4612 restyleManager->PostRestyleEvent(root, eRestyle_Subtree,
4613 NS_STYLE_HINT_NONE);
4614 } else {
4615 for (uint32_t i = 0; i < scopeRoots.Length(); i++) {
4616 Element* scopeRoot = scopeRoots[i];
4617 restyleManager->PostRestyleEvent(scopeRoot, eRestyle_Subtree,
4618 NS_STYLE_HINT_NONE);
4623 void
4624 nsIPresShell::ReconstructStyleDataExternal()
4626 ReconstructStyleDataInternal();
4629 void
4630 PresShell::RecordStyleSheetChange(nsIStyleSheet* aStyleSheet)
4632 if (mStylesHaveChanged)
4633 return;
4635 nsRefPtr<CSSStyleSheet> cssStyleSheet = do_QueryObject(aStyleSheet);
4636 if (cssStyleSheet) {
4637 Element* scopeElement = cssStyleSheet->GetScopeElement();
4638 if (scopeElement) {
4639 mChangedScopeStyleRoots.AppendElement(scopeElement);
4640 return;
4644 mStylesHaveChanged = true;
4647 void
4648 PresShell::StyleSheetAdded(nsIDocument *aDocument,
4649 nsIStyleSheet* aStyleSheet,
4650 bool aDocumentSheet)
4652 // We only care when enabled sheets are added
4653 NS_PRECONDITION(aStyleSheet, "Must have a style sheet!");
4655 if (aStyleSheet->IsApplicable() && aStyleSheet->HasRules()) {
4656 RecordStyleSheetChange(aStyleSheet);
4660 void
4661 PresShell::StyleSheetRemoved(nsIDocument *aDocument,
4662 nsIStyleSheet* aStyleSheet,
4663 bool aDocumentSheet)
4665 // We only care when enabled sheets are removed
4666 NS_PRECONDITION(aStyleSheet, "Must have a style sheet!");
4668 if (aStyleSheet->IsApplicable() && aStyleSheet->HasRules()) {
4669 RecordStyleSheetChange(aStyleSheet);
4673 void
4674 PresShell::StyleSheetApplicableStateChanged(nsIDocument *aDocument,
4675 nsIStyleSheet* aStyleSheet,
4676 bool aApplicable)
4678 if (aStyleSheet->HasRules()) {
4679 RecordStyleSheetChange(aStyleSheet);
4683 void
4684 PresShell::StyleRuleChanged(nsIDocument *aDocument,
4685 nsIStyleSheet* aStyleSheet,
4686 nsIStyleRule* aOldStyleRule,
4687 nsIStyleRule* aNewStyleRule)
4689 RecordStyleSheetChange(aStyleSheet);
4692 void
4693 PresShell::StyleRuleAdded(nsIDocument *aDocument,
4694 nsIStyleSheet* aStyleSheet,
4695 nsIStyleRule* aStyleRule)
4697 RecordStyleSheetChange(aStyleSheet);
4700 void
4701 PresShell::StyleRuleRemoved(nsIDocument *aDocument,
4702 nsIStyleSheet* aStyleSheet,
4703 nsIStyleRule* aStyleRule)
4705 RecordStyleSheetChange(aStyleSheet);
4708 nsIFrame*
4709 PresShell::GetRealPrimaryFrameFor(nsIContent* aContent) const
4711 if (aContent->GetComposedDoc() != GetDocument()) {
4712 return nullptr;
4714 nsIFrame *primaryFrame = aContent->GetPrimaryFrame();
4715 if (!primaryFrame)
4716 return nullptr;
4717 return nsPlaceholderFrame::GetRealFrameFor(primaryFrame);
4720 nsIFrame*
4721 PresShell::GetPlaceholderFrameFor(nsIFrame* aFrame) const
4723 return mFrameConstructor->GetPlaceholderFrameFor(aFrame);
4726 nsresult
4727 PresShell::RenderDocument(const nsRect& aRect, uint32_t aFlags,
4728 nscolor aBackgroundColor,
4729 gfxContext* aThebesContext)
4731 NS_ENSURE_TRUE(!(aFlags & RENDER_IS_UNTRUSTED), NS_ERROR_NOT_IMPLEMENTED);
4733 nsRootPresContext* rootPresContext = mPresContext->GetRootPresContext();
4734 if (rootPresContext) {
4735 rootPresContext->FlushWillPaintObservers();
4736 if (mIsDestroying)
4737 return NS_OK;
4740 nsAutoScriptBlocker blockScripts;
4742 // Set up the rectangle as the path in aThebesContext
4743 gfxRect r(0, 0,
4744 nsPresContext::AppUnitsToFloatCSSPixels(aRect.width),
4745 nsPresContext::AppUnitsToFloatCSSPixels(aRect.height));
4746 aThebesContext->NewPath();
4747 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
4748 aThebesContext->Rectangle(r, true);
4749 #else
4750 aThebesContext->Rectangle(r);
4751 #endif
4753 nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
4754 if (!rootFrame) {
4755 // Nothing to paint, just fill the rect
4756 aThebesContext->SetColor(gfxRGBA(aBackgroundColor));
4757 aThebesContext->Fill();
4758 return NS_OK;
4761 gfxContextAutoSaveRestore save(aThebesContext);
4763 gfxContext::GraphicsOperator oldOperator = aThebesContext->CurrentOperator();
4764 if (oldOperator == gfxContext::OPERATOR_OVER) {
4765 // Clip to the destination rectangle before we push the group,
4766 // to limit the size of the temporary surface
4767 aThebesContext->Clip();
4770 // we want the window to be composited as a single image using
4771 // whatever operator was set; set OPERATOR_OVER here, which is
4772 // either already the case, or overrides the operator in a group.
4773 // the original operator will be present when we PopGroup.
4774 // we can avoid using a temporary surface if we're using OPERATOR_OVER
4775 bool needsGroup = oldOperator != gfxContext::OPERATOR_OVER;
4777 if (needsGroup) {
4778 aThebesContext->PushGroup(NS_GET_A(aBackgroundColor) == 0xff ?
4779 gfxContentType::COLOR :
4780 gfxContentType::COLOR_ALPHA);
4781 aThebesContext->Save();
4783 if (oldOperator != gfxContext::OPERATOR_OVER) {
4784 // Clip now while we paint to the temporary surface. For
4785 // non-source-bounded operators (e.g., SOURCE), we need to do clip
4786 // here after we've pushed the group, so that eventually popping
4787 // the group and painting it will be able to clear the entire
4788 // destination surface.
4789 aThebesContext->Clip();
4790 aThebesContext->SetOperator(gfxContext::OPERATOR_OVER);
4794 aThebesContext->Translate(gfxPoint(-nsPresContext::AppUnitsToFloatCSSPixels(aRect.x),
4795 -nsPresContext::AppUnitsToFloatCSSPixels(aRect.y)));
4797 nsDeviceContext* devCtx = mPresContext->DeviceContext();
4798 gfxFloat scale = gfxFloat(devCtx->AppUnitsPerDevPixel())/nsPresContext::AppUnitsPerCSSPixel();
4799 aThebesContext->Scale(scale, scale);
4801 // Since canvas APIs use floats to set up their matrices, we may have
4802 // some slight inaccuracy here. Adjust matrix components that are
4803 // integers up to the accuracy of floats to be those integers.
4804 aThebesContext->NudgeCurrentMatrixToIntegers();
4806 AutoSaveRestoreRenderingState _(this);
4808 nsRefPtr<nsRenderingContext> rc = new nsRenderingContext();
4809 rc->Init(devCtx, aThebesContext);
4811 bool wouldFlushRetainedLayers = false;
4812 uint32_t flags = nsLayoutUtils::PAINT_IGNORE_SUPPRESSION;
4813 if (aThebesContext->CurrentMatrix().HasNonIntegerTranslation()) {
4814 flags |= nsLayoutUtils::PAINT_IN_TRANSFORM;
4816 if (!(aFlags & RENDER_ASYNC_DECODE_IMAGES)) {
4817 flags |= nsLayoutUtils::PAINT_SYNC_DECODE_IMAGES;
4819 if (aFlags & RENDER_USE_WIDGET_LAYERS) {
4820 // We only support using widget layers on display root's with widgets.
4821 nsView* view = rootFrame->GetView();
4822 if (view && view->GetWidget() &&
4823 nsLayoutUtils::GetDisplayRootFrame(rootFrame) == rootFrame) {
4824 flags |= nsLayoutUtils::PAINT_WIDGET_LAYERS;
4827 if (!(aFlags & RENDER_CARET)) {
4828 wouldFlushRetainedLayers = true;
4829 flags |= nsLayoutUtils::PAINT_HIDE_CARET;
4831 if (aFlags & RENDER_IGNORE_VIEWPORT_SCROLLING) {
4832 wouldFlushRetainedLayers = !IgnoringViewportScrolling();
4833 mRenderFlags = ChangeFlag(mRenderFlags, true, STATE_IGNORING_VIEWPORT_SCROLLING);
4835 if (aFlags & RENDER_DRAWWINDOW_NOT_FLUSHING) {
4836 mRenderFlags = ChangeFlag(mRenderFlags, true, STATE_DRAWWINDOW_NOT_FLUSHING);
4838 if (aFlags & RENDER_DOCUMENT_RELATIVE) {
4839 // XXX be smarter about this ... drawWindow might want a rect
4840 // that's "pretty close" to what our retained layer tree covers.
4841 // In that case, it wouldn't disturb normal rendering too much,
4842 // and we should allow it.
4843 wouldFlushRetainedLayers = true;
4844 flags |= nsLayoutUtils::PAINT_DOCUMENT_RELATIVE;
4847 // Don't let drawWindow blow away our retained layer tree
4848 if ((flags & nsLayoutUtils::PAINT_WIDGET_LAYERS) && wouldFlushRetainedLayers) {
4849 flags &= ~nsLayoutUtils::PAINT_WIDGET_LAYERS;
4852 nsLayoutUtils::PaintFrame(rc, rootFrame, nsRegion(aRect),
4853 aBackgroundColor, flags);
4855 // if we had to use a group, paint it to the destination now
4856 if (needsGroup) {
4857 aThebesContext->Restore();
4858 aThebesContext->PopGroupToSource();
4859 aThebesContext->Paint();
4862 return NS_OK;
4866 * Clip the display list aList to a range. Returns the clipped
4867 * rectangle surrounding the range.
4869 nsRect
4870 PresShell::ClipListToRange(nsDisplayListBuilder *aBuilder,
4871 nsDisplayList* aList,
4872 nsRange* aRange)
4874 // iterate though the display items and add up the bounding boxes of each.
4875 // This will allow the total area of the frames within the range to be
4876 // determined. To do this, remove an item from the bottom of the list, check
4877 // whether it should be part of the range, and if so, append it to the top
4878 // of the temporary list tmpList. If the item is a text frame at the end of
4879 // the selection range, clip it to the portion of the text frame that is
4880 // part of the selection. Then, append the wrapper to the top of the list.
4881 // Otherwise, just delete the item and don't append it.
4882 nsRect surfaceRect;
4883 nsDisplayList tmpList;
4885 nsDisplayItem* i;
4886 while ((i = aList->RemoveBottom())) {
4887 // itemToInsert indiciates the item that should be inserted into the
4888 // temporary list. If null, no item should be inserted.
4889 nsDisplayItem* itemToInsert = nullptr;
4890 nsIFrame* frame = i->Frame();
4891 nsIContent* content = frame->GetContent();
4892 if (content) {
4893 bool atStart = (content == aRange->GetStartParent());
4894 bool atEnd = (content == aRange->GetEndParent());
4895 if ((atStart || atEnd) && frame->GetType() == nsGkAtoms::textFrame) {
4896 int32_t frameStartOffset, frameEndOffset;
4897 frame->GetOffsets(frameStartOffset, frameEndOffset);
4899 int32_t hilightStart =
4900 atStart ? std::max(aRange->StartOffset(), frameStartOffset) : frameStartOffset;
4901 int32_t hilightEnd =
4902 atEnd ? std::min(aRange->EndOffset(), frameEndOffset) : frameEndOffset;
4903 if (hilightStart < hilightEnd) {
4904 // determine the location of the start and end edges of the range.
4905 nsPoint startPoint, endPoint;
4906 frame->GetPointFromOffset(hilightStart, &startPoint);
4907 frame->GetPointFromOffset(hilightEnd, &endPoint);
4909 // the clip rectangle is determined by taking the the start and
4910 // end points of the range, offset from the reference frame.
4911 // Because of rtl, the end point may be to the left of the
4912 // start point, so x is set to the lowest value
4913 nsRect textRect(aBuilder->ToReferenceFrame(frame), frame->GetSize());
4914 nscoord x = std::min(startPoint.x, endPoint.x);
4915 textRect.x += x;
4916 textRect.width = std::max(startPoint.x, endPoint.x) - x;
4917 surfaceRect.UnionRect(surfaceRect, textRect);
4919 DisplayItemClip newClip;
4920 newClip.SetTo(textRect);
4921 newClip.IntersectWith(i->GetClip());
4922 i->SetClip(aBuilder, newClip);
4923 itemToInsert = i;
4926 // Don't try to descend into subdocuments.
4927 // If this ever changes we'd need to add handling for subdocuments with
4928 // different zoom levels.
4929 else if (content->GetCurrentDoc() ==
4930 aRange->GetStartParent()->GetCurrentDoc()) {
4931 // if the node is within the range, append it to the temporary list
4932 bool before, after;
4933 nsresult rv =
4934 nsRange::CompareNodeToRange(content, aRange, &before, &after);
4935 if (NS_SUCCEEDED(rv) && !before && !after) {
4936 itemToInsert = i;
4937 bool snap;
4938 surfaceRect.UnionRect(surfaceRect, i->GetBounds(aBuilder, &snap));
4943 // insert the item into the list if necessary. If the item has a child
4944 // list, insert that as well
4945 nsDisplayList* sublist = i->GetSameCoordinateSystemChildren();
4946 if (itemToInsert || sublist) {
4947 tmpList.AppendToTop(itemToInsert ? itemToInsert : i);
4948 // if the item is a list, iterate over it as well
4949 if (sublist)
4950 surfaceRect.UnionRect(surfaceRect,
4951 ClipListToRange(aBuilder, sublist, aRange));
4953 else {
4954 // otherwise, just delete the item and don't readd it to the list
4955 i->~nsDisplayItem();
4959 // now add all the items back onto the original list again
4960 aList->AppendToTop(&tmpList);
4962 return surfaceRect;
4965 #ifdef DEBUG
4966 #include <stdio.h>
4968 static bool gDumpRangePaintList = false;
4969 #endif
4971 RangePaintInfo*
4972 PresShell::CreateRangePaintInfo(nsIDOMRange* aRange,
4973 nsRect& aSurfaceRect,
4974 bool aForPrimarySelection)
4976 RangePaintInfo* info = nullptr;
4978 nsRange* range = static_cast<nsRange*>(aRange);
4980 nsIFrame* ancestorFrame;
4981 nsIFrame* rootFrame = GetRootFrame();
4983 // If the start or end of the range is the document, just use the root
4984 // frame, otherwise get the common ancestor of the two endpoints of the
4985 // range.
4986 nsINode* startParent = range->GetStartParent();
4987 nsINode* endParent = range->GetEndParent();
4988 nsIDocument* doc = startParent->GetCrossShadowCurrentDoc();
4989 if (startParent == doc || endParent == doc) {
4990 ancestorFrame = rootFrame;
4992 else {
4993 nsINode* ancestor = nsContentUtils::GetCommonAncestor(startParent, endParent);
4994 NS_ASSERTION(!ancestor || ancestor->IsNodeOfType(nsINode::eCONTENT),
4995 "common ancestor is not content");
4996 if (!ancestor || !ancestor->IsNodeOfType(nsINode::eCONTENT))
4997 return nullptr;
4999 nsIContent* ancestorContent = static_cast<nsIContent*>(ancestor);
5000 ancestorFrame = ancestorContent->GetPrimaryFrame();
5002 // use the nearest ancestor frame that includes all continuations as the
5003 // root for building the display list
5004 while (ancestorFrame &&
5005 nsLayoutUtils::GetNextContinuationOrIBSplitSibling(ancestorFrame))
5006 ancestorFrame = ancestorFrame->GetParent();
5009 if (!ancestorFrame)
5010 return nullptr;
5012 info = new RangePaintInfo(range, ancestorFrame);
5014 nsRect ancestorRect = ancestorFrame->GetVisualOverflowRect();
5016 // get a display list containing the range
5017 info->mBuilder.SetIncludeAllOutOfFlows();
5018 if (aForPrimarySelection) {
5019 info->mBuilder.SetSelectedFramesOnly();
5021 info->mBuilder.EnterPresShell(ancestorFrame, ancestorRect);
5022 ancestorFrame->BuildDisplayListForStackingContext(&info->mBuilder,
5023 ancestorRect, &info->mList);
5025 #ifdef DEBUG
5026 if (gDumpRangePaintList) {
5027 fprintf(stderr, "CreateRangePaintInfo --- before ClipListToRange:\n");
5028 nsFrame::PrintDisplayList(&(info->mBuilder), info->mList);
5030 #endif
5032 nsRect rangeRect = ClipListToRange(&info->mBuilder, &info->mList, range);
5034 info->mBuilder.LeavePresShell(ancestorFrame, ancestorRect);
5036 #ifdef DEBUG
5037 if (gDumpRangePaintList) {
5038 fprintf(stderr, "CreateRangePaintInfo --- after ClipListToRange:\n");
5039 nsFrame::PrintDisplayList(&(info->mBuilder), info->mList);
5041 #endif
5043 // determine the offset of the reference frame for the display list
5044 // to the root frame. This will allow the coordinates used when painting
5045 // to all be offset from the same point
5046 info->mRootOffset = ancestorFrame->GetOffsetTo(rootFrame);
5047 rangeRect.MoveBy(info->mRootOffset);
5048 aSurfaceRect.UnionRect(aSurfaceRect, rangeRect);
5050 return info;
5053 TemporaryRef<SourceSurface>
5054 PresShell::PaintRangePaintInfo(nsTArray<nsAutoPtr<RangePaintInfo> >* aItems,
5055 nsISelection* aSelection,
5056 nsIntRegion* aRegion,
5057 nsRect aArea,
5058 nsIntPoint& aPoint,
5059 nsIntRect* aScreenRect)
5061 nsPresContext* pc = GetPresContext();
5062 if (!pc || aArea.width == 0 || aArea.height == 0)
5063 return nullptr;
5065 nsDeviceContext* deviceContext = pc->DeviceContext();
5067 // use the rectangle to create the surface
5068 nsIntRect pixelArea = aArea.ToOutsidePixels(pc->AppUnitsPerDevPixel());
5070 // if the area of the image is larger than the maximum area, scale it down
5071 float scale = 0.0;
5072 nsIntRect rootScreenRect =
5073 GetRootFrame()->GetScreenRectInAppUnits().ToNearestPixels(
5074 pc->AppUnitsPerDevPixel());
5076 // if the image is larger in one or both directions than half the size of
5077 // the available screen area, scale the image down to that size.
5078 nsRect maxSize;
5079 deviceContext->GetClientRect(maxSize);
5080 nscoord maxWidth = pc->AppUnitsToDevPixels(maxSize.width >> 1);
5081 nscoord maxHeight = pc->AppUnitsToDevPixels(maxSize.height >> 1);
5082 bool resize = (pixelArea.width > maxWidth || pixelArea.height > maxHeight);
5083 if (resize) {
5084 scale = 1.0;
5085 // divide the maximum size by the image size in both directions. Whichever
5086 // direction produces the smallest result determines how much should be
5087 // scaled.
5088 if (pixelArea.width > maxWidth)
5089 scale = std::min(scale, float(maxWidth) / pixelArea.width);
5090 if (pixelArea.height > maxHeight)
5091 scale = std::min(scale, float(maxHeight) / pixelArea.height);
5093 pixelArea.width = NSToIntFloor(float(pixelArea.width) * scale);
5094 pixelArea.height = NSToIntFloor(float(pixelArea.height) * scale);
5096 // adjust the screen position based on the rescaled size
5097 nscoord left = rootScreenRect.x + pixelArea.x;
5098 nscoord top = rootScreenRect.y + pixelArea.y;
5099 aScreenRect->x = NSToIntFloor(aPoint.x - float(aPoint.x - left) * scale);
5100 aScreenRect->y = NSToIntFloor(aPoint.y - float(aPoint.y - top) * scale);
5102 else {
5103 // move aScreenRect to the position of the surface in screen coordinates
5104 aScreenRect->MoveTo(rootScreenRect.x + pixelArea.x, rootScreenRect.y + pixelArea.y);
5106 aScreenRect->width = pixelArea.width;
5107 aScreenRect->height = pixelArea.height;
5109 RefPtr<DrawTarget> dt =
5110 gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
5111 IntSize(pixelArea.width, pixelArea.height),
5112 SurfaceFormat::B8G8R8A8);
5113 if (!dt) {
5114 return nullptr;
5117 nsRefPtr<gfxContext> ctx = new gfxContext(dt);
5118 nsRefPtr<nsRenderingContext> rc = new nsRenderingContext();
5119 rc->Init(deviceContext, ctx);
5121 if (aRegion) {
5122 // Convert aRegion from CSS pixels to dev pixels
5123 nsIntRegion region =
5124 aRegion->ToAppUnits(nsPresContext::AppUnitsPerCSSPixel())
5125 .ToOutsidePixels(pc->AppUnitsPerDevPixel());
5126 rc->SetClip(region);
5129 if (resize)
5130 rc->Scale(scale, scale);
5132 // translate so that points are relative to the surface area
5133 rc->Translate(-aArea.TopLeft());
5135 // temporarily hide the selection so that text is drawn normally. If a
5136 // selection is being rendered, use that, otherwise use the presshell's
5137 // selection.
5138 nsRefPtr<nsFrameSelection> frameSelection;
5139 if (aSelection) {
5140 frameSelection = static_cast<Selection*>(aSelection)->GetFrameSelection();
5142 else {
5143 frameSelection = FrameSelection();
5145 int16_t oldDisplaySelection = frameSelection->GetDisplaySelection();
5146 frameSelection->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN);
5148 // next, paint each range in the selection
5149 int32_t count = aItems->Length();
5150 for (int32_t i = 0; i < count; i++) {
5151 RangePaintInfo* rangeInfo = (*aItems)[i];
5152 // the display lists paint relative to the offset from the reference
5153 // frame, so translate the rendering context
5154 nsRenderingContext::AutoPushTranslation
5155 translate(rc, rangeInfo->mRootOffset);
5157 aArea.MoveBy(-rangeInfo->mRootOffset.x, -rangeInfo->mRootOffset.y);
5158 nsRegion visible(aArea);
5159 rangeInfo->mList.PaintRoot(&rangeInfo->mBuilder, rc, nsDisplayList::PAINT_DEFAULT);
5160 aArea.MoveBy(rangeInfo->mRootOffset.x, rangeInfo->mRootOffset.y);
5163 // restore the old selection display state
5164 frameSelection->SetDisplaySelection(oldDisplaySelection);
5166 return dt->Snapshot();
5169 TemporaryRef<SourceSurface>
5170 PresShell::RenderNode(nsIDOMNode* aNode,
5171 nsIntRegion* aRegion,
5172 nsIntPoint& aPoint,
5173 nsIntRect* aScreenRect)
5175 // area will hold the size of the surface needed to draw the node, measured
5176 // from the root frame.
5177 nsRect area;
5178 nsTArray<nsAutoPtr<RangePaintInfo> > rangeItems;
5180 // nothing to draw if the node isn't in a document
5181 nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
5182 if (!node->IsInDoc())
5183 return nullptr;
5185 nsRefPtr<nsRange> range = new nsRange(node);
5186 if (NS_FAILED(range->SelectNode(aNode)))
5187 return nullptr;
5189 RangePaintInfo* info = CreateRangePaintInfo(range, area, false);
5190 if (info && !rangeItems.AppendElement(info)) {
5191 delete info;
5192 return nullptr;
5195 if (aRegion) {
5196 // combine the area with the supplied region
5197 nsIntRect rrectPixels = aRegion->GetBounds();
5199 nsRect rrect = rrectPixels.ToAppUnits(nsPresContext::AppUnitsPerCSSPixel());
5200 area.IntersectRect(area, rrect);
5202 nsPresContext* pc = GetPresContext();
5203 if (!pc)
5204 return nullptr;
5206 // move the region so that it is offset from the topleft corner of the surface
5207 aRegion->MoveBy(-pc->AppUnitsToDevPixels(area.x),
5208 -pc->AppUnitsToDevPixels(area.y));
5211 return PaintRangePaintInfo(&rangeItems, nullptr, aRegion, area, aPoint,
5212 aScreenRect);
5215 TemporaryRef<SourceSurface>
5216 PresShell::RenderSelection(nsISelection* aSelection,
5217 nsIntPoint& aPoint,
5218 nsIntRect* aScreenRect)
5220 // area will hold the size of the surface needed to draw the selection,
5221 // measured from the root frame.
5222 nsRect area;
5223 nsTArray<nsAutoPtr<RangePaintInfo> > rangeItems;
5225 // iterate over each range and collect them into the rangeItems array.
5226 // This is done so that the size of selection can be determined so as
5227 // to allocate a surface area
5228 int32_t numRanges;
5229 aSelection->GetRangeCount(&numRanges);
5230 NS_ASSERTION(numRanges > 0, "RenderSelection called with no selection");
5232 for (int32_t r = 0; r < numRanges; r++)
5234 nsCOMPtr<nsIDOMRange> range;
5235 aSelection->GetRangeAt(r, getter_AddRefs(range));
5237 RangePaintInfo* info = CreateRangePaintInfo(range, area, true);
5238 if (info && !rangeItems.AppendElement(info)) {
5239 delete info;
5240 return nullptr;
5244 return PaintRangePaintInfo(&rangeItems, aSelection, nullptr, area, aPoint,
5245 aScreenRect);
5248 void
5249 PresShell::AddPrintPreviewBackgroundItem(nsDisplayListBuilder& aBuilder,
5250 nsDisplayList& aList,
5251 nsIFrame* aFrame,
5252 const nsRect& aBounds)
5254 aList.AppendNewToBottom(new (&aBuilder)
5255 nsDisplaySolidColor(&aBuilder, aFrame, aBounds, NS_RGB(115, 115, 115)));
5258 static bool
5259 AddCanvasBackgroundColor(const nsDisplayList& aList, nsIFrame* aCanvasFrame,
5260 nscolor aColor)
5262 for (nsDisplayItem* i = aList.GetBottom(); i; i = i->GetAbove()) {
5263 if (i->Frame() == aCanvasFrame &&
5264 i->GetType() == nsDisplayItem::TYPE_CANVAS_BACKGROUND_COLOR) {
5265 nsDisplayCanvasBackgroundColor* bg = static_cast<nsDisplayCanvasBackgroundColor*>(i);
5266 bg->SetExtraBackgroundColor(aColor);
5267 return true;
5269 nsDisplayList* sublist = i->GetSameCoordinateSystemChildren();
5270 if (sublist &&
5271 i->GetType() != nsDisplayItem::TYPE_BLEND_CONTAINER &&
5272 AddCanvasBackgroundColor(*sublist, aCanvasFrame, aColor))
5273 return true;
5275 return false;
5278 void
5279 PresShell::AddCanvasBackgroundColorItem(nsDisplayListBuilder& aBuilder,
5280 nsDisplayList& aList,
5281 nsIFrame* aFrame,
5282 const nsRect& aBounds,
5283 nscolor aBackstopColor,
5284 uint32_t aFlags)
5286 if (aBounds.IsEmpty()) {
5287 return;
5289 // We don't want to add an item for the canvas background color if the frame
5290 // (sub)tree we are painting doesn't include any canvas frames. There isn't
5291 // an easy way to check this directly, but if we check if the root of the
5292 // (sub)tree we are painting is a canvas frame that should cover us in all
5293 // cases (it will usually be a viewport frame when we have a canvas frame in
5294 // the (sub)tree).
5295 if (!(aFlags & nsIPresShell::FORCE_DRAW) &&
5296 !nsCSSRendering::IsCanvasFrame(aFrame)) {
5297 return;
5300 nscolor bgcolor = NS_ComposeColors(aBackstopColor, mCanvasBackgroundColor);
5301 if (NS_GET_A(bgcolor) == 0)
5302 return;
5304 // To make layers work better, we want to avoid having a big non-scrolled
5305 // color background behind a scrolled transparent background. Instead,
5306 // we'll try to move the color background into the scrolled content
5307 // by making nsDisplayCanvasBackground paint it.
5308 if (!aFrame->GetParent()) {
5309 nsIScrollableFrame* sf =
5310 aFrame->PresContext()->PresShell()->GetRootScrollFrameAsScrollable();
5311 if (sf) {
5312 nsCanvasFrame* canvasFrame = do_QueryFrame(sf->GetScrolledFrame());
5313 if (canvasFrame && canvasFrame->IsVisibleForPainting(&aBuilder)) {
5314 if (AddCanvasBackgroundColor(aList, canvasFrame, bgcolor))
5315 return;
5320 aList.AppendNewToBottom(
5321 new (&aBuilder) nsDisplaySolidColor(&aBuilder, aFrame, aBounds, bgcolor));
5324 static bool IsTransparentContainerElement(nsPresContext* aPresContext)
5326 nsCOMPtr<nsIDocShell> docShell = aPresContext->GetDocShell();
5327 if (!docShell) {
5328 return false;
5331 nsCOMPtr<nsPIDOMWindow> pwin = docShell->GetWindow();
5332 if (!pwin)
5333 return false;
5334 nsCOMPtr<Element> containerElement = pwin->GetFrameElementInternal();
5335 return containerElement &&
5336 containerElement->HasAttr(kNameSpaceID_None, nsGkAtoms::transparent);
5339 nscolor PresShell::GetDefaultBackgroundColorToDraw()
5341 if (!mPresContext || !mPresContext->GetBackgroundColorDraw()) {
5342 return NS_RGB(255,255,255);
5344 return mPresContext->DefaultBackgroundColor();
5347 void PresShell::UpdateCanvasBackground()
5349 // If we have a frame tree and it has style information that
5350 // specifies the background color of the canvas, update our local
5351 // cache of that color.
5352 nsIFrame* rootStyleFrame = FrameConstructor()->GetRootElementStyleFrame();
5353 if (rootStyleFrame) {
5354 nsStyleContext* bgStyle =
5355 nsCSSRendering::FindRootFrameBackground(rootStyleFrame);
5356 // XXX We should really be passing the canvasframe, not the root element
5357 // style frame but we don't have access to the canvasframe here. It isn't
5358 // a problem because only a few frames can return something other than true
5359 // and none of them would be a canvas frame or root element style frame.
5360 bool drawBackgroundImage;
5361 bool drawBackgroundColor;
5363 mCanvasBackgroundColor =
5364 nsCSSRendering::DetermineBackgroundColor(mPresContext, bgStyle,
5365 rootStyleFrame,
5366 drawBackgroundImage,
5367 drawBackgroundColor);
5368 if (GetPresContext()->IsCrossProcessRootContentDocument() &&
5369 !IsTransparentContainerElement(mPresContext)) {
5370 mCanvasBackgroundColor =
5371 NS_ComposeColors(GetDefaultBackgroundColorToDraw(), mCanvasBackgroundColor);
5375 // If the root element of the document (ie html) has style 'display: none'
5376 // then the document's background color does not get drawn; cache the
5377 // color we actually draw.
5378 if (!FrameConstructor()->GetRootElementFrame()) {
5379 mCanvasBackgroundColor = GetDefaultBackgroundColorToDraw();
5381 if (XRE_GetProcessType() == GeckoProcessType_Content) {
5382 if (TabChild* tabChild = TabChild::GetFrom(this)) {
5383 tabChild->SetBackgroundColor(mCanvasBackgroundColor);
5388 nscolor PresShell::ComputeBackstopColor(nsView* aDisplayRoot)
5390 nsIWidget* widget = aDisplayRoot->GetWidget();
5391 if (widget && (widget->GetTransparencyMode() != eTransparencyOpaque ||
5392 widget->WidgetPaintsBackground())) {
5393 // Within a transparent widget, so the backstop color must be
5394 // totally transparent.
5395 return NS_RGBA(0,0,0,0);
5397 // Within an opaque widget (or no widget at all), so the backstop
5398 // color must be totally opaque. The user's default background
5399 // as reported by the prescontext is guaranteed to be opaque.
5400 return GetDefaultBackgroundColorToDraw();
5403 struct PaintParams {
5404 nscolor mBackgroundColor;
5407 LayerManager* PresShell::GetLayerManager()
5409 NS_ASSERTION(mViewManager, "Should have view manager");
5411 nsView* rootView = mViewManager->GetRootView();
5412 if (rootView) {
5413 if (nsIWidget* widget = rootView->GetWidget()) {
5414 return widget->GetLayerManager();
5417 return nullptr;
5420 void PresShell::SetIgnoreViewportScrolling(bool aIgnore)
5422 if (IgnoringViewportScrolling() == aIgnore) {
5423 return;
5425 RenderingState state(this);
5426 state.mRenderFlags = ChangeFlag(state.mRenderFlags, aIgnore,
5427 STATE_IGNORING_VIEWPORT_SCROLLING);
5428 SetRenderingState(state);
5431 nsresult PresShell::SetResolution(float aXResolution, float aYResolution)
5433 if (!(aXResolution > 0.0 && aYResolution > 0.0)) {
5434 return NS_ERROR_ILLEGAL_VALUE;
5436 if (aXResolution == mXResolution && aYResolution == mYResolution) {
5437 return NS_OK;
5439 RenderingState state(this);
5440 state.mXResolution = aXResolution;
5441 state.mYResolution = aYResolution;
5442 SetRenderingState(state);
5443 return NS_OK;
5446 gfxSize PresShell::GetCumulativeResolution()
5448 gfxSize resolution = GetResolution();
5449 nsPresContext* parentCtx = GetPresContext()->GetParentPresContext();
5450 if (parentCtx) {
5451 resolution = resolution * parentCtx->PresShell()->GetCumulativeResolution();
5453 return resolution;
5456 void PresShell::SetRenderingState(const RenderingState& aState)
5458 if (mRenderFlags != aState.mRenderFlags) {
5459 // Rendering state changed in a way that forces us to flush any
5460 // retained layers we might already have.
5461 LayerManager* manager = GetLayerManager();
5462 if (manager) {
5463 FrameLayerBuilder::InvalidateAllLayers(manager);
5467 mRenderFlags = aState.mRenderFlags;
5468 mXResolution = aState.mXResolution;
5469 mYResolution = aState.mYResolution;
5472 void PresShell::SynthesizeMouseMove(bool aFromScroll)
5474 if (!sSynthMouseMove)
5475 return;
5477 if (mPaintingSuppressed || !mIsActive || !mPresContext) {
5478 return;
5481 if (!mPresContext->IsRoot()) {
5482 nsIPresShell* rootPresShell = GetRootPresShell();
5483 if (rootPresShell) {
5484 rootPresShell->SynthesizeMouseMove(aFromScroll);
5486 return;
5489 if (mMouseLocation == nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE))
5490 return;
5492 if (!mSynthMouseMoveEvent.IsPending()) {
5493 nsRefPtr<nsSynthMouseMoveEvent> ev =
5494 new nsSynthMouseMoveEvent(this, aFromScroll);
5496 if (!GetPresContext()->RefreshDriver()->AddRefreshObserver(ev,
5497 Flush_Display)) {
5498 NS_WARNING("failed to dispatch nsSynthMouseMoveEvent");
5499 return;
5502 mSynthMouseMoveEvent = ev;
5507 * Find the first floating view with a widget in a postorder traversal of the
5508 * view tree that contains the point. Thus more deeply nested floating views
5509 * are preferred over their ancestors, and floating views earlier in the
5510 * view hierarchy (i.e., added later) are preferred over their siblings.
5511 * This is adequate for finding the "topmost" floating view under a point,
5512 * given that floating views don't supporting having a specific z-index.
5514 * We cannot exit early when aPt is outside the view bounds, because floating
5515 * views aren't necessarily included in their parent's bounds, so this could
5516 * traverse the entire view hierarchy --- use carefully.
5518 static nsView* FindFloatingViewContaining(nsView* aView, nsPoint aPt)
5520 if (aView->GetVisibility() == nsViewVisibility_kHide)
5521 // No need to look into descendants.
5522 return nullptr;
5524 nsIFrame* frame = aView->GetFrame();
5525 if (frame) {
5526 if (!frame->IsVisibleConsideringAncestors(nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY) ||
5527 !frame->PresContext()->PresShell()->IsActive()) {
5528 return nullptr;
5532 for (nsView* v = aView->GetFirstChild(); v; v = v->GetNextSibling()) {
5533 nsView* r = FindFloatingViewContaining(v, v->ConvertFromParentCoords(aPt));
5534 if (r)
5535 return r;
5538 if (aView->GetFloating() && aView->HasWidget() &&
5539 aView->GetDimensions().Contains(aPt))
5540 return aView;
5542 return nullptr;
5546 * This finds the first view containing the given point in a postorder
5547 * traversal of the view tree that contains the point, assuming that the
5548 * point is not in a floating view. It assumes that only floating views
5549 * extend outside the bounds of their parents.
5551 * This methods should only be called if FindFloatingViewContaining
5552 * returns null.
5554 static nsView* FindViewContaining(nsView* aView, nsPoint aPt)
5556 if (!aView->GetDimensions().Contains(aPt) ||
5557 aView->GetVisibility() == nsViewVisibility_kHide) {
5558 return nullptr;
5561 nsIFrame* frame = aView->GetFrame();
5562 if (frame) {
5563 if (!frame->IsVisibleConsideringAncestors(nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY) ||
5564 !frame->PresContext()->PresShell()->IsActive()) {
5565 return nullptr;
5569 for (nsView* v = aView->GetFirstChild(); v; v = v->GetNextSibling()) {
5570 nsView* r = FindViewContaining(v, v->ConvertFromParentCoords(aPt));
5571 if (r)
5572 return r;
5575 return aView;
5578 void
5579 PresShell::ProcessSynthMouseMoveEvent(bool aFromScroll)
5581 // If drag session has started, we shouldn't synthesize mousemove event.
5582 nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
5583 if (dragSession) {
5584 mSynthMouseMoveEvent.Forget();
5585 return;
5588 // allow new event to be posted while handling this one only if the
5589 // source of the event is a scroll (to prevent infinite reflow loops)
5590 if (aFromScroll) {
5591 mSynthMouseMoveEvent.Forget();
5594 nsView* rootView = mViewManager ? mViewManager->GetRootView() : nullptr;
5595 if (mMouseLocation == nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE) ||
5596 !rootView || !rootView->HasWidget() || !mPresContext) {
5597 mSynthMouseMoveEvent.Forget();
5598 return;
5601 NS_ASSERTION(mPresContext->IsRoot(), "Only a root pres shell should be here");
5603 // Hold a ref to ourselves so DispatchEvent won't destroy us (since
5604 // we need to access members after we call DispatchEvent).
5605 nsCOMPtr<nsIPresShell> kungFuDeathGrip(this);
5607 #ifdef DEBUG_MOUSE_LOCATION
5608 printf("[ps=%p]synthesizing mouse move to (%d,%d)\n",
5609 this, mMouseLocation.x, mMouseLocation.y);
5610 #endif
5612 int32_t APD = mPresContext->AppUnitsPerDevPixel();
5614 // We need a widget to put in the event we are going to dispatch so we look
5615 // for a view that has a widget and the mouse location is over. We first look
5616 // for floating views, if there isn't one we use the root view. |view| holds
5617 // that view.
5618 nsView* view = nullptr;
5620 // The appunits per devpixel ratio of |view|.
5621 int32_t viewAPD;
5623 // refPoint will be mMouseLocation relative to the widget of |view|, the
5624 // widget we will put in the event we dispatch, in viewAPD appunits
5625 nsPoint refpoint(0, 0);
5627 // We always dispatch the event to the pres shell that contains the view that
5628 // the mouse is over. pointVM is the VM of that pres shell.
5629 nsViewManager *pointVM = nullptr;
5631 // This could be a bit slow (traverses entire view hierarchy)
5632 // but it's OK to do it once per synthetic mouse event
5633 view = FindFloatingViewContaining(rootView, mMouseLocation);
5634 if (!view) {
5635 view = rootView;
5636 nsView *pointView = FindViewContaining(rootView, mMouseLocation);
5637 // pointView can be null in situations related to mouse capture
5638 pointVM = (pointView ? pointView : view)->GetViewManager();
5639 refpoint = mMouseLocation + rootView->ViewToWidgetOffset();
5640 viewAPD = APD;
5641 } else {
5642 pointVM = view->GetViewManager();
5643 nsIFrame* frame = view->GetFrame();
5644 NS_ASSERTION(frame, "floating views can't be anonymous");
5645 viewAPD = frame->PresContext()->AppUnitsPerDevPixel();
5646 refpoint = mMouseLocation.ConvertAppUnits(APD, viewAPD);
5647 refpoint -= view->GetOffsetTo(rootView);
5648 refpoint += view->ViewToWidgetOffset();
5650 NS_ASSERTION(view->GetWidget(), "view should have a widget here");
5651 WidgetMouseEvent event(true, NS_MOUSE_MOVE, view->GetWidget(),
5652 WidgetMouseEvent::eSynthesized);
5653 event.refPoint = LayoutDeviceIntPoint::FromAppUnitsToNearest(refpoint, viewAPD);
5654 event.time = PR_IntervalNow();
5655 // XXX set event.modifiers ?
5656 // XXX mnakano I think that we should get the latest information from widget.
5658 nsCOMPtr<nsIPresShell> shell = pointVM->GetPresShell();
5659 if (shell) {
5660 shell->DispatchSynthMouseMove(&event, !aFromScroll);
5663 if (!aFromScroll) {
5664 mSynthMouseMoveEvent.Forget();
5668 /* static */ void
5669 PresShell::MarkImagesInListVisible(const nsDisplayList& aList)
5671 for (nsDisplayItem* item = aList.GetBottom(); item; item = item->GetAbove()) {
5672 nsDisplayList* sublist = item->GetChildren();
5673 if (sublist) {
5674 MarkImagesInListVisible(*sublist);
5675 continue;
5677 nsIFrame* f = item->Frame();
5678 // We could check the type of the display item, only a handful can hold an
5679 // image loading content.
5680 // dont bother nscomptr here, it is wasteful
5681 nsCOMPtr<nsIImageLoadingContent> content(do_QueryInterface(f->GetContent()));
5682 if (content) {
5683 // use the presshell containing the image
5684 PresShell* presShell = static_cast<PresShell*>(f->PresContext()->PresShell());
5685 uint32_t count = presShell->mVisibleImages.Count();
5686 presShell->mVisibleImages.PutEntry(content);
5687 if (presShell->mVisibleImages.Count() > count) {
5688 // content was added to mVisibleImages, so we need to increment its visible count
5689 content->IncrementVisibleCount();
5695 static PLDHashOperator
5696 RemoveAndStore(nsRefPtrHashKey<nsIImageLoadingContent>* aEntry, void* userArg)
5698 nsTArray< nsRefPtr<nsIImageLoadingContent> >* array =
5699 static_cast< nsTArray< nsRefPtr<nsIImageLoadingContent> >* >(userArg);
5700 array->AppendElement(aEntry->GetKey());
5701 return PL_DHASH_REMOVE;
5704 void
5705 PresShell::RebuildImageVisibilityDisplayList(const nsDisplayList& aList)
5707 MOZ_ASSERT(!mImageVisibilityVisited, "already visited?");
5708 mImageVisibilityVisited = true;
5709 // Remove the entries of the mVisibleImages hashtable and put them in the
5710 // beforeImageList array.
5711 nsTArray< nsRefPtr<nsIImageLoadingContent> > beforeImageList;
5712 beforeImageList.SetCapacity(mVisibleImages.Count());
5713 mVisibleImages.EnumerateEntries(RemoveAndStore, &beforeImageList);
5714 MarkImagesInListVisible(aList);
5715 for (size_t i = 0; i < beforeImageList.Length(); ++i) {
5716 beforeImageList[i]->DecrementVisibleCount();
5720 /* static */ void
5721 PresShell::ClearImageVisibilityVisited(nsView* aView, bool aClear)
5723 nsViewManager* vm = aView->GetViewManager();
5724 if (aClear) {
5725 PresShell* presShell = static_cast<PresShell*>(vm->GetPresShell());
5726 if (!presShell->mImageVisibilityVisited) {
5727 presShell->ClearVisibleImagesList();
5729 presShell->mImageVisibilityVisited = false;
5731 for (nsView* v = aView->GetFirstChild(); v; v = v->GetNextSibling()) {
5732 ClearImageVisibilityVisited(v, v->GetViewManager() != vm);
5736 static PLDHashOperator
5737 DecrementVisibleCount(nsRefPtrHashKey<nsIImageLoadingContent>* aEntry, void* userArg)
5739 aEntry->GetKey()->DecrementVisibleCount();
5740 return PL_DHASH_NEXT;
5743 void
5744 PresShell::ClearVisibleImagesList()
5746 mVisibleImages.EnumerateEntries(DecrementVisibleCount, nullptr);
5747 mVisibleImages.Clear();
5750 void
5751 PresShell::MarkImagesInSubtreeVisible(nsIFrame* aFrame, const nsRect& aRect)
5753 MOZ_ASSERT(aFrame->PresContext()->PresShell() == this, "wrong presshell");
5755 nsCOMPtr<nsIImageLoadingContent> content(do_QueryInterface(aFrame->GetContent()));
5756 if (content && aFrame->StyleVisibility()->IsVisible()) {
5757 uint32_t count = mVisibleImages.Count();
5758 mVisibleImages.PutEntry(content);
5759 if (mVisibleImages.Count() > count) {
5760 // content was added to mVisibleImages, so we need to increment its visible count
5761 content->IncrementVisibleCount();
5765 nsSubDocumentFrame* subdocFrame = do_QueryFrame(aFrame);
5766 if (subdocFrame) {
5767 nsIPresShell* presShell = subdocFrame->GetSubdocumentPresShellForPainting(
5768 nsSubDocumentFrame::IGNORE_PAINT_SUPPRESSION);
5769 if (presShell) {
5770 nsRect rect = aRect;
5771 nsIFrame* root = presShell->GetRootFrame();
5772 if (root) {
5773 rect.MoveBy(aFrame->GetOffsetToCrossDoc(root));
5774 } else {
5775 rect.MoveBy(-aFrame->GetContentRectRelativeToSelf().TopLeft());
5777 rect = rect.ConvertAppUnitsRoundOut(
5778 aFrame->PresContext()->AppUnitsPerDevPixel(),
5779 presShell->GetPresContext()->AppUnitsPerDevPixel());
5781 presShell->RebuildImageVisibility(&rect);
5783 return;
5786 nsRect rect = aRect;
5788 nsIScrollableFrame* scrollFrame = do_QueryFrame(aFrame);
5789 if (scrollFrame) {
5790 nsRect displayPort;
5791 bool usingDisplayport = nsLayoutUtils::GetDisplayPort(aFrame->GetContent(), &displayPort);
5792 if (usingDisplayport) {
5793 rect = displayPort;
5794 } else {
5795 rect = rect.Intersect(scrollFrame->GetScrollPortRect());
5797 rect = scrollFrame->ExpandRectToNearlyVisible(rect);
5800 bool preserves3DChildren = aFrame->Preserves3DChildren();
5802 // we assume all images in popups are visible elsewhere, so we skip them here
5803 const nsIFrame::ChildListIDs skip(nsIFrame::kPopupList |
5804 nsIFrame::kSelectPopupList);
5805 for (nsIFrame::ChildListIterator childLists(aFrame);
5806 !childLists.IsDone(); childLists.Next()) {
5807 if (skip.Contains(childLists.CurrentID())) {
5808 continue;
5811 nsFrameList children = childLists.CurrentList();
5812 for (nsFrameList::Enumerator e(children); !e.AtEnd(); e.Next()) {
5813 nsIFrame* child = e.get();
5815 nsRect r = rect - child->GetPosition();
5816 if (!r.IntersectRect(r, child->GetVisualOverflowRect())) {
5817 continue;
5819 if (child->IsTransformed()) {
5820 // for children of a preserve3d element we just pass down the same dirty rect
5821 if (!preserves3DChildren || !child->Preserves3D()) {
5822 const nsRect overflow = child->GetVisualOverflowRectRelativeToSelf();
5823 nsRect out;
5824 if (nsDisplayTransform::UntransformRect(r, overflow, child, nsPoint(0,0), &out)) {
5825 r = out;
5826 } else {
5827 r.SetEmpty();
5831 MarkImagesInSubtreeVisible(child, r);
5836 void
5837 PresShell::RebuildImageVisibility(nsRect* aRect)
5839 MOZ_ASSERT(!mImageVisibilityVisited, "already visited?");
5840 mImageVisibilityVisited = true;
5842 nsIFrame* rootFrame = GetRootFrame();
5843 if (!rootFrame) {
5844 return;
5847 // Remove the entries of the mVisibleImages hashtable and put them in the
5848 // beforeImageList array.
5849 nsTArray< nsRefPtr<nsIImageLoadingContent> > beforeImageList;
5850 beforeImageList.SetCapacity(mVisibleImages.Count());
5851 mVisibleImages.EnumerateEntries(RemoveAndStore, &beforeImageList);
5853 nsRect vis(nsPoint(0, 0), rootFrame->GetSize());
5854 if (aRect) {
5855 vis = *aRect;
5857 MarkImagesInSubtreeVisible(rootFrame, vis);
5859 for (size_t i = 0; i < beforeImageList.Length(); ++i) {
5860 beforeImageList[i]->DecrementVisibleCount();
5864 void
5865 PresShell::UpdateImageVisibility()
5867 MOZ_ASSERT(!mPresContext || mPresContext->IsRootContentDocument(),
5868 "updating image visibility on a non-root content document?");
5870 mUpdateImageVisibilityEvent.Revoke();
5872 if (mHaveShutDown || mIsDestroying) {
5873 return;
5876 // call update on that frame
5877 nsIFrame* rootFrame = GetRootFrame();
5878 if (!rootFrame) {
5879 ClearVisibleImagesList();
5880 return;
5883 RebuildImageVisibility();
5884 ClearImageVisibilityVisited(rootFrame->GetView(), true);
5886 #ifdef DEBUG_IMAGE_VISIBILITY_DISPLAY_LIST
5887 // This can be used to debug the frame walker by comparing beforeImageList and
5888 // mVisibleImages in RebuildImageVisibilityDisplayList to see if they produce
5889 // the same results (mVisibleImages holds the images the display list thinks
5890 // are visible, beforeImageList holds the images the frame walker thinks are
5891 // visible).
5892 nsDisplayListBuilder builder(rootFrame, nsDisplayListBuilder::IMAGE_VISIBILITY, false);
5893 nsRect updateRect(nsPoint(0, 0), rootFrame->GetSize());
5894 nsIFrame* rootScroll = GetRootScrollFrame();
5895 if (rootScroll) {
5896 nsIContent* content = rootScroll->GetContent();
5897 if (content) {
5898 nsLayoutUtils::GetDisplayPort(content, &updateRect);
5901 if (IgnoringViewportScrolling()) {
5902 builder.SetIgnoreScrollFrame(rootScroll);
5903 // The ExpandRectToNearlyVisible that the root scroll frame would do gets short
5904 // circuited due to us ignoring the root scroll frame, so we do it here.
5905 nsIScrollableFrame* rootScrollable = do_QueryFrame(rootScroll);
5906 updateRect = rootScrollable->ExpandRectToNearlyVisible(updateRect);
5909 builder.IgnorePaintSuppression();
5910 builder.EnterPresShell(rootFrame, updateRect);
5911 nsDisplayList list;
5912 rootFrame->BuildDisplayListForStackingContext(&builder, updateRect, &list);
5913 builder.LeavePresShell(rootFrame, updateRect);
5915 RebuildImageVisibilityDisplayList(list);
5917 ClearImageVisibilityVisited(rootFrame->GetView(), true);
5919 list.DeleteAll();
5920 #endif
5923 bool
5924 PresShell::AssumeAllImagesVisible()
5926 static bool sImageVisibilityEnabled = true;
5927 static bool sImageVisibilityEnabledForBrowserElementsOnly = false;
5928 static bool sImageVisibilityPrefCached = false;
5930 if (!sImageVisibilityPrefCached) {
5931 Preferences::AddBoolVarCache(&sImageVisibilityEnabled,
5932 "layout.imagevisibility.enabled", true);
5933 Preferences::AddBoolVarCache(&sImageVisibilityEnabledForBrowserElementsOnly,
5934 "layout.imagevisibility.enabled_for_browser_elements_only", false);
5935 sImageVisibilityPrefCached = true;
5938 if ((!sImageVisibilityEnabled &&
5939 !sImageVisibilityEnabledForBrowserElementsOnly) ||
5940 !mPresContext || !mDocument) {
5941 return true;
5944 // We assume all images are visible in print, print preview, chrome, xul, and
5945 // resource docs and don't keep track of them.
5946 if (mPresContext->Type() == nsPresContext::eContext_PrintPreview ||
5947 mPresContext->Type() == nsPresContext::eContext_Print ||
5948 mPresContext->IsChrome() ||
5949 mDocument->IsResourceDoc() ||
5950 mDocument->IsXUL()) {
5951 return true;
5954 if (!sImageVisibilityEnabled &&
5955 sImageVisibilityEnabledForBrowserElementsOnly) {
5956 nsCOMPtr<nsIDocShell> docshell(mPresContext->GetDocShell());
5957 if (!docshell || !docshell->GetIsInBrowserElement()) {
5958 return true;
5962 return false;
5965 void
5966 PresShell::ScheduleImageVisibilityUpdate()
5968 if (AssumeAllImagesVisible())
5969 return;
5971 if (!mPresContext->IsRootContentDocument()) {
5972 nsPresContext* presContext = mPresContext->GetToplevelContentDocumentPresContext();
5973 if (!presContext)
5974 return;
5975 MOZ_ASSERT(presContext->IsRootContentDocument(),
5976 "Didn't get a root prescontext from GetToplevelContentDocumentPresContext?");
5977 presContext->PresShell()->ScheduleImageVisibilityUpdate();
5978 return;
5981 if (mHaveShutDown || mIsDestroying)
5982 return;
5984 if (mUpdateImageVisibilityEvent.IsPending())
5985 return;
5987 nsRefPtr<nsRunnableMethod<PresShell> > ev =
5988 NS_NewRunnableMethod(this, &PresShell::UpdateImageVisibility);
5989 if (NS_SUCCEEDED(NS_DispatchToCurrentThread(ev))) {
5990 mUpdateImageVisibilityEvent = ev;
5994 void
5995 PresShell::EnsureImageInVisibleList(nsIImageLoadingContent* aImage)
5997 if (AssumeAllImagesVisible()) {
5998 aImage->IncrementVisibleCount();
5999 return;
6002 #ifdef DEBUG
6003 // if it has a frame make sure its in this presshell
6004 nsCOMPtr<nsIContent> content = do_QueryInterface(aImage);
6005 if (content) {
6006 PresShell* shell = static_cast<PresShell*>(content->OwnerDoc()->GetShell());
6007 MOZ_ASSERT(!shell || shell == this, "wrong shell");
6009 #endif
6011 if (!mVisibleImages.Contains(aImage)) {
6012 mVisibleImages.PutEntry(aImage);
6013 aImage->IncrementVisibleCount();
6017 void
6018 PresShell::RemoveImageFromVisibleList(nsIImageLoadingContent* aImage)
6020 #ifdef DEBUG
6021 // if it has a frame make sure its in this presshell
6022 nsCOMPtr<nsIContent> content = do_QueryInterface(aImage);
6023 if (content) {
6024 PresShell* shell = static_cast<PresShell*>(content->OwnerDoc()->GetShell());
6025 MOZ_ASSERT(!shell || shell == this, "wrong shell");
6027 #endif
6029 if (AssumeAllImagesVisible()) {
6030 MOZ_ASSERT(mVisibleImages.Count() == 0, "shouldn't have any images in the table");
6031 return;
6034 uint32_t count = mVisibleImages.Count();
6035 mVisibleImages.RemoveEntry(aImage);
6036 if (mVisibleImages.Count() < count) {
6037 // aImage was in the hashtable, so we need to decrement its visible count
6038 aImage->DecrementVisibleCount();
6042 class nsAutoNotifyDidPaint
6044 public:
6045 nsAutoNotifyDidPaint(PresShell* aShell, uint32_t aFlags)
6046 : mShell(aShell), mFlags(aFlags)
6049 ~nsAutoNotifyDidPaint()
6051 mShell->GetPresContext()->NotifyDidPaintForSubtree(mFlags);
6054 private:
6055 PresShell* mShell;
6056 uint32_t mFlags;
6059 class AutoUpdateHitRegion
6061 public:
6062 AutoUpdateHitRegion(PresShell* aShell, nsIFrame* aFrame)
6063 : mShell(aShell), mFrame(aFrame)
6066 ~AutoUpdateHitRegion()
6068 if (XRE_GetProcessType() != GeckoProcessType_Content ||
6069 !mFrame || !mShell) {
6070 return;
6072 TabChild* tabChild = TabChild::GetFrom(mShell);
6073 if (!tabChild || !tabChild->GetUpdateHitRegion()) {
6074 return;
6076 nsRegion region;
6077 nsDisplayListBuilder builder(mFrame,
6078 nsDisplayListBuilder::EVENT_DELIVERY,
6079 /* aBuildCert= */ false);
6080 nsDisplayList list;
6081 nsAutoTArray<nsIFrame*, 100> outFrames;
6082 nsDisplayItem::HitTestState hitTestState;
6083 nsRect bounds = mShell->GetPresContext()->GetVisibleArea();
6084 builder.EnterPresShell(mFrame, bounds);
6085 mFrame->BuildDisplayListForStackingContext(&builder, bounds, &list);
6086 builder.LeavePresShell(mFrame, bounds);
6087 list.HitTest(&builder, bounds, &hitTestState, &outFrames);
6088 list.DeleteAll();
6089 for (int32_t i = outFrames.Length() - 1; i >= 0; --i) {
6090 region.Or(region, nsLayoutUtils::TransformFrameRectToAncestor(
6091 outFrames[i], nsRect(nsPoint(0, 0), outFrames[i]->GetSize()), mFrame));
6093 tabChild->UpdateHitRegion(region);
6095 private:
6096 PresShell* mShell;
6097 nsIFrame* mFrame;
6100 void
6101 PresShell::RecordShadowStyleChange(ShadowRoot* aShadowRoot)
6103 mChangedScopeStyleRoots.AppendElement(aShadowRoot->GetHost()->AsElement());
6106 void
6107 PresShell::Paint(nsView* aViewToPaint,
6108 const nsRegion& aDirtyRegion,
6109 uint32_t aFlags)
6111 PROFILER_LABEL("PresShell", "Paint",
6112 js::ProfileEntry::Category::GRAPHICS);
6114 NS_ASSERTION(!mIsDestroying, "painting a destroyed PresShell");
6115 NS_ASSERTION(aViewToPaint, "null view");
6117 MOZ_ASSERT(!mImageVisibilityVisited, "should have been cleared");
6119 if (!mIsActive || mIsZombie) {
6120 return;
6123 nsPresContext* presContext = GetPresContext();
6124 AUTO_LAYOUT_PHASE_ENTRY_POINT(presContext, Paint);
6126 nsIFrame* frame = aViewToPaint->GetFrame();
6128 bool isRetainingManager;
6129 LayerManager* layerManager =
6130 aViewToPaint->GetWidget()->GetLayerManager(&isRetainingManager);
6131 NS_ASSERTION(layerManager, "Must be in paint event");
6132 bool shouldInvalidate = layerManager->NeedsWidgetInvalidation();
6134 nsAutoNotifyDidPaint notifyDidPaint(this, aFlags);
6135 AutoUpdateHitRegion updateHitRegion(this, frame);
6137 // Whether or not we should set first paint when painting is
6138 // suppressed is debatable. For now we'll do it because
6139 // B2G relies on first paint to configure the viewport and
6140 // we only want to do that when we have real content to paint.
6141 // See Bug 798245
6142 if (mIsFirstPaint && !mPaintingSuppressed) {
6143 layerManager->SetIsFirstPaint();
6144 mIsFirstPaint = false;
6147 layerManager->BeginTransaction();
6149 if (frame && isRetainingManager) {
6150 // Try to do an empty transaction, if the frame tree does not
6151 // need to be updated. Do not try to do an empty transaction on
6152 // a non-retained layer manager (like the BasicLayerManager that
6153 // draws the window title bar on Mac), because a) it won't work
6154 // and b) below we don't want to clear NS_FRAME_UPDATE_LAYER_TREE,
6155 // that will cause us to forget to update the real layer manager!
6157 if (!(aFlags & PAINT_LAYERS)) {
6158 if (layerManager->EndEmptyTransaction()) {
6159 return;
6161 NS_WARNING("Must complete empty transaction when compositing!");
6164 if (!(frame->GetStateBits() & NS_FRAME_UPDATE_LAYER_TREE) &&
6165 !mNextPaintCompressed) {
6166 NotifySubDocInvalidationFunc computeInvalidFunc =
6167 presContext->MayHavePaintEventListenerInSubDocument() ? nsPresContext::NotifySubDocInvalidation : 0;
6168 bool computeInvalidRect = computeInvalidFunc ||
6169 (layerManager->GetBackendType() == LayersBackend::LAYERS_BASIC);
6171 UniquePtr<LayerProperties> props;
6172 if (computeInvalidRect) {
6173 props = Move(LayerProperties::CloneFrom(layerManager->GetRoot()));
6176 MaybeSetupTransactionIdAllocator(layerManager, aViewToPaint);
6178 if (layerManager->EndEmptyTransaction((aFlags & PAINT_COMPOSITE) ?
6179 LayerManager::END_DEFAULT : LayerManager::END_NO_COMPOSITE)) {
6180 nsIntRegion invalid;
6181 if (props) {
6182 invalid = props->ComputeDifferences(layerManager->GetRoot(), computeInvalidFunc);
6183 } else {
6184 LayerProperties::ClearInvalidations(layerManager->GetRoot());
6186 if (props) {
6187 if (!invalid.IsEmpty()) {
6188 nsIntRect bounds = invalid.GetBounds();
6189 nsRect rect(presContext->DevPixelsToAppUnits(bounds.x),
6190 presContext->DevPixelsToAppUnits(bounds.y),
6191 presContext->DevPixelsToAppUnits(bounds.width),
6192 presContext->DevPixelsToAppUnits(bounds.height));
6193 if (shouldInvalidate) {
6194 aViewToPaint->GetViewManager()->InvalidateViewNoSuppression(aViewToPaint, rect);
6196 presContext->NotifyInvalidation(bounds, 0);
6198 } else if (shouldInvalidate) {
6199 aViewToPaint->GetViewManager()->InvalidateView(aViewToPaint);
6202 frame->UpdatePaintCountForPaintedPresShells();
6203 return;
6206 frame->RemoveStateBits(NS_FRAME_UPDATE_LAYER_TREE);
6208 if (frame) {
6209 frame->ClearPresShellsFromLastPaint();
6212 nscolor bgcolor = ComputeBackstopColor(aViewToPaint);
6213 uint32_t flags = nsLayoutUtils::PAINT_WIDGET_LAYERS | nsLayoutUtils::PAINT_EXISTING_TRANSACTION;
6214 if (!(aFlags & PAINT_COMPOSITE)) {
6215 flags |= nsLayoutUtils::PAINT_NO_COMPOSITE;
6217 if (mNextPaintCompressed) {
6218 flags |= nsLayoutUtils::PAINT_COMPRESSED;
6219 mNextPaintCompressed = false;
6222 if (frame) {
6223 // We can paint directly into the widget using its layer manager.
6224 nsLayoutUtils::PaintFrame(nullptr, frame, aDirtyRegion, bgcolor, flags);
6225 return;
6228 nsRefPtr<ColorLayer> root = layerManager->CreateColorLayer();
6229 if (root) {
6230 nsPresContext* pc = GetPresContext();
6231 nsIntRect bounds =
6232 pc->GetVisibleArea().ToOutsidePixels(pc->AppUnitsPerDevPixel());
6233 bgcolor = NS_ComposeColors(bgcolor, mCanvasBackgroundColor);
6234 root->SetColor(bgcolor);
6235 root->SetVisibleRegion(bounds);
6236 layerManager->SetRoot(root);
6238 MaybeSetupTransactionIdAllocator(layerManager, aViewToPaint);
6239 layerManager->EndTransaction(nullptr, nullptr, (aFlags & PAINT_COMPOSITE) ?
6240 LayerManager::END_DEFAULT : LayerManager::END_NO_COMPOSITE);
6243 // static
6244 void
6245 nsIPresShell::SetCapturingContent(nsIContent* aContent, uint8_t aFlags)
6247 // If capture was set for pointer lock, don't unlock unless we are coming
6248 // out of pointer lock explicitly.
6249 if (!aContent && gCaptureInfo.mPointerLock &&
6250 !(aFlags & CAPTURE_POINTERLOCK)) {
6251 return;
6254 NS_IF_RELEASE(gCaptureInfo.mContent);
6256 // only set capturing content if allowed or the CAPTURE_IGNOREALLOWED or
6257 // CAPTURE_POINTERLOCK flags are used.
6258 if ((aFlags & CAPTURE_IGNOREALLOWED) || gCaptureInfo.mAllowed ||
6259 (aFlags & CAPTURE_POINTERLOCK)) {
6260 if (aContent) {
6261 NS_ADDREF(gCaptureInfo.mContent = aContent);
6263 // CAPTURE_POINTERLOCK is the same as CAPTURE_RETARGETTOELEMENT & CAPTURE_IGNOREALLOWED
6264 gCaptureInfo.mRetargetToElement = ((aFlags & CAPTURE_RETARGETTOELEMENT) != 0) ||
6265 ((aFlags & CAPTURE_POINTERLOCK) != 0);
6266 gCaptureInfo.mPreventDrag = (aFlags & CAPTURE_PREVENTDRAG) != 0;
6267 gCaptureInfo.mPointerLock = (aFlags & CAPTURE_POINTERLOCK) != 0;
6271 /* static */ void
6272 nsIPresShell::SetPointerCapturingContent(uint32_t aPointerId, nsIContent* aContent)
6274 nsIContent* content = GetPointerCapturingContent(aPointerId);
6276 PointerInfo* pointerInfo = nullptr;
6277 if (!content && gActivePointersIds->Get(aPointerId, &pointerInfo) &&
6278 pointerInfo &&
6279 nsIDOMMouseEvent::MOZ_SOURCE_MOUSE == pointerInfo->mPointerType) {
6280 SetCapturingContent(aContent, CAPTURE_PREVENTDRAG);
6283 if (content) {
6284 // Releasing capture for given pointer.
6285 gPointerCaptureList->Remove(aPointerId);
6286 DispatchGotOrLostPointerCaptureEvent(false, aPointerId, content);
6287 // Need to check the state because a lostpointercapture listener
6288 // may have called SetPointerCapture
6289 if (GetPointerCapturingContent(aPointerId)) {
6290 return;
6294 gPointerCaptureList->Put(aPointerId, aContent);
6295 DispatchGotOrLostPointerCaptureEvent(true, aPointerId, aContent);
6298 /* static */ void
6299 nsIPresShell::ReleasePointerCapturingContent(uint32_t aPointerId, nsIContent* aContent)
6301 if (gActivePointersIds->Get(aPointerId)) {
6302 SetCapturingContent(nullptr, CAPTURE_PREVENTDRAG);
6305 // Releasing capture for given pointer.
6306 gPointerCaptureList->Remove(aPointerId);
6308 DispatchGotOrLostPointerCaptureEvent(false, aPointerId, aContent);
6311 /* static */ nsIContent*
6312 nsIPresShell::GetPointerCapturingContent(uint32_t aPointerId)
6314 return gPointerCaptureList->GetWeak(aPointerId);
6317 /* static */ bool
6318 nsIPresShell::GetPointerInfo(uint32_t aPointerId, bool& aActiveState)
6320 PointerInfo* pointerInfo = nullptr;
6321 if (gActivePointersIds->Get(aPointerId, &pointerInfo) && pointerInfo) {
6322 aActiveState = pointerInfo->mActiveState;
6323 return true;
6325 return false;
6328 void
6329 PresShell::UpdateActivePointerState(WidgetGUIEvent* aEvent)
6331 switch (aEvent->message) {
6332 case NS_MOUSE_ENTER:
6333 // In this case we have to know information about available mouse pointers
6334 if (WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent()) {
6335 gActivePointersIds->Put(mouseEvent->pointerId, new PointerInfo(false, mouseEvent->inputSource));
6337 break;
6338 case NS_POINTER_DOWN:
6339 // In this case we switch pointer to active state
6340 if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) {
6341 gActivePointersIds->Put(pointerEvent->pointerId, new PointerInfo(true, pointerEvent->inputSource));
6343 break;
6344 case NS_POINTER_UP:
6345 // In this case we remove information about pointer or turn off active state
6346 if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) {
6347 if(pointerEvent->inputSource != nsIDOMMouseEvent::MOZ_SOURCE_TOUCH) {
6348 gActivePointersIds->Put(pointerEvent->pointerId, new PointerInfo(false, pointerEvent->inputSource));
6349 } else {
6350 gActivePointersIds->Remove(pointerEvent->pointerId);
6353 break;
6354 case NS_MOUSE_EXIT:
6355 // In this case we have to remove information about disappeared mouse pointers
6356 if (WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent()) {
6357 gActivePointersIds->Remove(mouseEvent->pointerId);
6359 break;
6363 nsIContent*
6364 PresShell::GetCurrentEventContent()
6366 if (mCurrentEventContent &&
6367 mCurrentEventContent->GetCrossShadowCurrentDoc() != mDocument) {
6368 mCurrentEventContent = nullptr;
6369 mCurrentEventFrame = nullptr;
6371 return mCurrentEventContent;
6374 nsIFrame*
6375 PresShell::GetCurrentEventFrame()
6377 if (MOZ_UNLIKELY(mIsDestroying)) {
6378 return nullptr;
6381 // GetCurrentEventContent() makes sure the content is still in the
6382 // same document that this pres shell belongs to. If not, then the
6383 // frame shouldn't get an event, nor should we even assume its safe
6384 // to try and find the frame.
6385 nsIContent* content = GetCurrentEventContent();
6386 if (!mCurrentEventFrame && content) {
6387 mCurrentEventFrame = content->GetPrimaryFrame();
6388 MOZ_ASSERT(!mCurrentEventFrame ||
6389 mCurrentEventFrame->PresContext()->GetPresShell() == this);
6391 return mCurrentEventFrame;
6394 nsIFrame*
6395 PresShell::GetEventTargetFrame()
6397 return GetCurrentEventFrame();
6400 already_AddRefed<nsIContent>
6401 PresShell::GetEventTargetContent(WidgetEvent* aEvent)
6403 nsCOMPtr<nsIContent> content = GetCurrentEventContent();
6404 if (!content) {
6405 nsIFrame* currentEventFrame = GetCurrentEventFrame();
6406 if (currentEventFrame) {
6407 currentEventFrame->GetContentForEvent(aEvent, getter_AddRefs(content));
6408 NS_ASSERTION(!content || content->GetCrossShadowCurrentDoc() == mDocument,
6409 "handing out content from a different doc");
6412 return content.forget();
6415 void
6416 PresShell::PushCurrentEventInfo(nsIFrame* aFrame, nsIContent* aContent)
6418 if (mCurrentEventFrame || mCurrentEventContent) {
6419 mCurrentEventFrameStack.InsertElementAt(0, mCurrentEventFrame);
6420 mCurrentEventContentStack.InsertObjectAt(mCurrentEventContent, 0);
6422 mCurrentEventFrame = aFrame;
6423 mCurrentEventContent = aContent;
6426 void
6427 PresShell::PopCurrentEventInfo()
6429 mCurrentEventFrame = nullptr;
6430 mCurrentEventContent = nullptr;
6432 if (0 != mCurrentEventFrameStack.Length()) {
6433 mCurrentEventFrame = mCurrentEventFrameStack.ElementAt(0);
6434 mCurrentEventFrameStack.RemoveElementAt(0);
6435 mCurrentEventContent = mCurrentEventContentStack.ObjectAt(0);
6436 mCurrentEventContentStack.RemoveObjectAt(0);
6438 // Don't use it if it has moved to a different document.
6439 if (mCurrentEventContent &&
6440 mCurrentEventContent->GetCrossShadowCurrentDoc() != mDocument) {
6441 mCurrentEventContent = nullptr;
6442 mCurrentEventFrame = nullptr;
6447 bool PresShell::InZombieDocument(nsIContent *aContent)
6449 // If a content node points to a null document, or the document is not
6450 // attached to a window, then it is possibly in a zombie document,
6451 // about to be replaced by a newly loading document.
6452 // Such documents cannot handle DOM events.
6453 // It might actually be in a node not attached to any document,
6454 // in which case there is not parent presshell to retarget it to.
6455 nsIDocument* doc = aContent->GetComposedDoc();
6456 return !doc || !doc->GetWindow();
6459 already_AddRefed<nsPIDOMWindow>
6460 PresShell::GetRootWindow()
6462 nsCOMPtr<nsPIDOMWindow> window =
6463 do_QueryInterface(mDocument->GetWindow());
6464 if (window) {
6465 nsCOMPtr<nsPIDOMWindow> rootWindow = window->GetPrivateRoot();
6466 NS_ASSERTION(rootWindow, "nsPIDOMWindow::GetPrivateRoot() returns NULL");
6467 return rootWindow.forget();
6470 // If we don't have DOM window, we're zombie, we should find the root window
6471 // with our parent shell.
6472 nsCOMPtr<nsIPresShell> parent = GetParentPresShellForEventHandling();
6473 NS_ENSURE_TRUE(parent, nullptr);
6474 return parent->GetRootWindow();
6477 already_AddRefed<nsIPresShell>
6478 PresShell::GetParentPresShellForEventHandling()
6480 NS_ENSURE_TRUE(mPresContext, nullptr);
6482 // Now, find the parent pres shell and send the event there
6483 nsCOMPtr<nsIDocShellTreeItem> treeItem = mPresContext->GetDocShell();
6484 if (!treeItem) {
6485 treeItem = mForwardingContainer.get();
6488 // Might have gone away, or never been around to start with
6489 NS_ENSURE_TRUE(treeItem, nullptr);
6491 nsCOMPtr<nsIDocShellTreeItem> parentTreeItem;
6492 treeItem->GetParent(getter_AddRefs(parentTreeItem));
6493 nsCOMPtr<nsIDocShell> parentDocShell = do_QueryInterface(parentTreeItem);
6494 NS_ENSURE_TRUE(parentDocShell && treeItem != parentTreeItem, nullptr);
6496 nsCOMPtr<nsIPresShell> parentPresShell = parentDocShell->GetPresShell();
6497 return parentPresShell.forget();
6500 nsresult
6501 PresShell::RetargetEventToParent(WidgetGUIEvent* aEvent,
6502 nsEventStatus* aEventStatus)
6504 // Send this events straight up to the parent pres shell.
6505 // We do this for keystroke events in zombie documents or if either a frame
6506 // or a root content is not present.
6507 // That way at least the UI key bindings can work.
6509 nsCOMPtr<nsIPresShell> kungFuDeathGrip(this);
6510 nsCOMPtr<nsIPresShell> parentPresShell = GetParentPresShellForEventHandling();
6511 NS_ENSURE_TRUE(parentPresShell, NS_ERROR_FAILURE);
6513 // Fake the event as though it's from the parent pres shell's root frame.
6514 return parentPresShell->HandleEvent(parentPresShell->GetRootFrame(), aEvent, true, aEventStatus);
6517 void
6518 PresShell::DisableNonTestMouseEvents(bool aDisable)
6520 sDisableNonTestMouseEvents = aDisable;
6523 already_AddRefed<nsPIDOMWindow>
6524 PresShell::GetFocusedDOMWindowInOurWindow()
6526 nsCOMPtr<nsPIDOMWindow> rootWindow = GetRootWindow();
6527 NS_ENSURE_TRUE(rootWindow, nullptr);
6528 nsCOMPtr<nsPIDOMWindow> focusedWindow;
6529 nsFocusManager::GetFocusedDescendant(rootWindow, true,
6530 getter_AddRefs(focusedWindow));
6531 return focusedWindow.forget();
6534 void
6535 PresShell::RecordMouseLocation(WidgetGUIEvent* aEvent)
6537 if (!mPresContext)
6538 return;
6540 if (!mPresContext->IsRoot()) {
6541 PresShell* rootPresShell = GetRootPresShell();
6542 if (rootPresShell) {
6543 rootPresShell->RecordMouseLocation(aEvent);
6545 return;
6548 if ((aEvent->message == NS_MOUSE_MOVE &&
6549 aEvent->AsMouseEvent()->reason == WidgetMouseEvent::eReal) ||
6550 aEvent->message == NS_MOUSE_ENTER ||
6551 aEvent->message == NS_MOUSE_BUTTON_DOWN ||
6552 aEvent->message == NS_MOUSE_BUTTON_UP) {
6553 nsIFrame* rootFrame = GetRootFrame();
6554 if (!rootFrame) {
6555 nsView* rootView = mViewManager->GetRootView();
6556 mMouseLocation = nsLayoutUtils::TranslateWidgetToView(mPresContext,
6557 aEvent->widget, LayoutDeviceIntPoint::ToUntyped(aEvent->refPoint),
6558 rootView);
6559 } else {
6560 mMouseLocation =
6561 nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, rootFrame);
6563 #ifdef DEBUG_MOUSE_LOCATION
6564 if (aEvent->message == NS_MOUSE_ENTER)
6565 printf("[ps=%p]got mouse enter for %p\n",
6566 this, aEvent->widget);
6567 printf("[ps=%p]setting mouse location to (%d,%d)\n",
6568 this, mMouseLocation.x, mMouseLocation.y);
6569 #endif
6570 if (aEvent->message == NS_MOUSE_ENTER)
6571 SynthesizeMouseMove(false);
6572 } else if (aEvent->message == NS_MOUSE_EXIT) {
6573 // Although we only care about the mouse moving into an area for which this
6574 // pres shell doesn't receive mouse move events, we don't check which widget
6575 // the mouse exit was for since this seems to vary by platform. Hopefully
6576 // this won't matter at all since we'll get the mouse move or enter after
6577 // the mouse exit when the mouse moves from one of our widgets into another.
6578 mMouseLocation = nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
6579 #ifdef DEBUG_MOUSE_LOCATION
6580 printf("[ps=%p]got mouse exit for %p\n",
6581 this, aEvent->widget);
6582 printf("[ps=%p]clearing mouse location\n",
6583 this);
6584 #endif
6588 static void
6589 EvictTouchPoint(nsRefPtr<dom::Touch>& aTouch,
6590 nsIDocument* aLimitToDocument = nullptr)
6592 nsCOMPtr<nsINode> node(do_QueryInterface(aTouch->mTarget));
6593 if (node) {
6594 nsIDocument* doc = node->GetCurrentDoc();
6595 if (doc && (!aLimitToDocument || aLimitToDocument == doc)) {
6596 nsIPresShell* presShell = doc->GetShell();
6597 if (presShell) {
6598 nsIFrame* frame = presShell->GetRootFrame();
6599 if (frame) {
6600 nsPoint pt(aTouch->mRefPoint.x, aTouch->mRefPoint.y);
6601 nsCOMPtr<nsIWidget> widget = frame->GetView()->GetNearestWidget(&pt);
6602 if (widget) {
6603 WidgetTouchEvent event(true, NS_TOUCH_END, widget);
6604 event.widget = widget;
6605 event.time = PR_IntervalNow();
6606 event.touches.AppendElement(aTouch);
6607 nsEventStatus status;
6608 widget->DispatchEvent(&event, status);
6609 return;
6615 if (!node || !aLimitToDocument || node->OwnerDoc() == aLimitToDocument) {
6616 // We couldn't dispatch touchend. Remove the touch from gCaptureTouchList
6617 // explicitly.
6618 nsIPresShell::gCaptureTouchList->Remove(aTouch->Identifier());
6622 static PLDHashOperator
6623 AppendToTouchList(const uint32_t& aKey, nsRefPtr<dom::Touch>& aData, void *aTouchList)
6625 WidgetTouchEvent::TouchArray* touches =
6626 static_cast<WidgetTouchEvent::TouchArray*>(aTouchList);
6627 aData->mChanged = false;
6628 touches->AppendElement(aData);
6629 return PL_DHASH_NEXT;
6632 void
6633 PresShell::EvictTouches()
6635 WidgetTouchEvent::AutoTouchArray touches;
6636 gCaptureTouchList->Enumerate(&AppendToTouchList, &touches);
6637 for (uint32_t i = 0; i < touches.Length(); ++i) {
6638 EvictTouchPoint(touches[i], mDocument);
6642 static PLDHashOperator
6643 FindAnyTarget(const uint32_t& aKey, nsRefPtr<dom::Touch>& aData,
6644 void* aAnyTarget)
6646 if (aData) {
6647 dom::EventTarget* target = aData->GetTarget();
6648 if (target) {
6649 nsCOMPtr<nsIContent>* content =
6650 static_cast<nsCOMPtr<nsIContent>*>(aAnyTarget);
6651 *content = do_QueryInterface(target);
6652 return PL_DHASH_STOP;
6655 return PL_DHASH_NEXT;
6658 nsIFrame* GetNearestFrameContainingPresShell(nsIPresShell* aPresShell)
6660 nsView* view = aPresShell->GetViewManager()->GetRootView();
6661 while (view && !view->GetFrame()) {
6662 view = view->GetParent();
6665 nsIFrame* frame = nullptr;
6666 if (view) {
6667 frame = view->GetFrame();
6670 return frame;
6673 static bool
6674 FlushThrottledStyles(nsIDocument *aDocument, void *aData)
6676 nsIPresShell* shell = aDocument->GetShell();
6677 if (shell && shell->IsVisible()) {
6678 nsPresContext* presContext = shell->GetPresContext();
6679 if (presContext) {
6680 presContext->RestyleManager()->UpdateOnlyAnimationStyles();
6684 return true;
6687 static nsresult
6688 DispatchPointerFromMouseOrTouch(PresShell* aShell,
6689 nsIFrame* aFrame,
6690 WidgetGUIEvent* aEvent,
6691 bool aDontRetargetEvents,
6692 nsEventStatus* aStatus)
6694 uint32_t pointerMessage = 0;
6695 if (aEvent->mClass == eMouseEventClass) {
6696 WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
6697 // if it is not mouse then it is likely will come as touch event
6698 if (!mouseEvent->convertToPointer) {
6699 return NS_OK;
6701 int16_t button = mouseEvent->button;
6702 switch (mouseEvent->message) {
6703 case NS_MOUSE_MOVE:
6704 if (mouseEvent->buttons == 0) {
6705 button = -1;
6707 pointerMessage = NS_POINTER_MOVE;
6708 break;
6709 case NS_MOUSE_BUTTON_UP:
6710 pointerMessage = NS_POINTER_UP;
6711 break;
6712 case NS_MOUSE_BUTTON_DOWN:
6713 pointerMessage = NS_POINTER_DOWN;
6714 break;
6715 default:
6716 return NS_OK;
6719 WidgetPointerEvent event(*mouseEvent);
6720 event.message = pointerMessage;
6721 event.button = button;
6722 event.pressure = event.buttons ?
6723 mouseEvent->pressure ? mouseEvent->pressure : 0.5f :
6724 0.0f;
6725 event.convertToPointer = mouseEvent->convertToPointer = false;
6726 aShell->HandleEvent(aFrame, &event, aDontRetargetEvents, aStatus);
6727 } else if (aEvent->mClass == eTouchEventClass) {
6728 WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
6729 // loop over all touches and dispatch pointer events on each touch
6730 // copy the event
6731 switch (touchEvent->message) {
6732 case NS_TOUCH_MOVE:
6733 pointerMessage = NS_POINTER_MOVE;
6734 break;
6735 case NS_TOUCH_END:
6736 pointerMessage = NS_POINTER_UP;
6737 break;
6738 case NS_TOUCH_START:
6739 pointerMessage = NS_POINTER_DOWN;
6740 break;
6741 case NS_TOUCH_CANCEL:
6742 pointerMessage = NS_POINTER_CANCEL;
6743 break;
6744 default:
6745 return NS_OK;
6748 for (uint32_t i = 0; i < touchEvent->touches.Length(); ++i) {
6749 mozilla::dom::Touch* touch = touchEvent->touches[i];
6750 if (!touch || !touch->convertToPointer) {
6751 continue;
6754 WidgetPointerEvent event(touchEvent->mFlags.mIsTrusted, pointerMessage, touchEvent->widget);
6755 event.isPrimary = i == 0;
6756 event.pointerId = touch->Identifier();
6757 event.refPoint.x = touch->mRefPoint.x;
6758 event.refPoint.y = touch->mRefPoint.y;
6759 event.modifiers = touchEvent->modifiers;
6760 event.width = touch->RadiusX();
6761 event.height = touch->RadiusY();
6762 event.tiltX = touch->tiltX;
6763 event.tiltY = touch->tiltY;
6764 event.time = touchEvent->time;
6765 event.timeStamp = touchEvent->timeStamp;
6766 event.mFlags = touchEvent->mFlags;
6767 event.button = WidgetMouseEvent::eLeftButton;
6768 event.buttons = WidgetMouseEvent::eLeftButtonFlag;
6769 event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
6770 event.convertToPointer = touch->convertToPointer = false;
6771 aShell->HandleEvent(aFrame, &event, aDontRetargetEvents, aStatus);
6774 return NS_OK;
6777 class ReleasePointerCaptureCaller
6779 public:
6780 ReleasePointerCaptureCaller() :
6781 mPointerId(0),
6782 mContent(nullptr)
6785 ~ReleasePointerCaptureCaller()
6787 if (mContent) {
6788 nsIPresShell::ReleasePointerCapturingContent(mPointerId, mContent);
6791 void SetTarget(uint32_t aPointerId, nsIContent* aContent)
6793 mPointerId = aPointerId;
6794 mContent = aContent;
6797 private:
6798 int32_t mPointerId;
6799 nsCOMPtr<nsIContent> mContent;
6802 nsresult
6803 PresShell::HandleEvent(nsIFrame* aFrame,
6804 WidgetGUIEvent* aEvent,
6805 bool aDontRetargetEvents,
6806 nsEventStatus* aEventStatus)
6808 #ifdef MOZ_TASK_TRACER
6809 // Make touch events, mouse events and hardware key events to be the source
6810 // events of TaskTracer, and originate the rest correlation tasks from here.
6811 SourceEventType type = SourceEventType::UNKNOWN;
6812 if (WidgetTouchEvent* inputEvent = aEvent->AsTouchEvent()) {
6813 type = SourceEventType::TOUCH;
6814 } else if (WidgetMouseEvent* inputEvent = aEvent->AsMouseEvent()) {
6815 type = SourceEventType::MOUSE;
6816 } else if (WidgetKeyboardEvent* inputEvent = aEvent->AsKeyboardEvent()) {
6817 type = SourceEventType::KEY;
6819 AutoSourceEvent taskTracerEvent(type);
6820 #endif
6822 if (sPointerEventEnabled) {
6823 DispatchPointerFromMouseOrTouch(this, aFrame, aEvent, aDontRetargetEvents, aEventStatus);
6826 NS_ASSERTION(aFrame, "null frame");
6828 if (mIsDestroying ||
6829 (sDisableNonTestMouseEvents && !aEvent->mFlags.mIsSynthesizedForTests &&
6830 aEvent->HasMouseEventMessage())) {
6831 return NS_OK;
6834 RecordMouseLocation(aEvent);
6836 // Determine whether event need to be consumed by touch caret or not.
6837 if (TouchCaretPrefEnabled() || SelectionCaretPrefEnabled()) {
6838 // We have to target the focus window because regardless of where the
6839 // touch goes, we want to access the touch caret when user is typing on an
6840 // editable element.
6841 nsCOMPtr<nsPIDOMWindow> window = GetFocusedDOMWindowInOurWindow();
6842 nsCOMPtr<nsIDocument> retargetEventDoc = window ? window->GetExtantDoc() : nullptr;
6843 nsCOMPtr<nsIPresShell> presShell = retargetEventDoc ?
6844 retargetEventDoc->GetShell() :
6845 nullptr;
6847 // Bug 1057256: Touch caret should handle the event before selection carets.
6848 // Otherwise, a long tap on touch caret will be incorrectly handled by
6849 // selection carets which makes moving touch caret failed.
6850 nsRefPtr<TouchCaret> touchCaret = presShell ?
6851 presShell->GetTouchCaret() :
6852 nullptr;
6853 if (touchCaret) {
6854 *aEventStatus = touchCaret->HandleEvent(aEvent);
6855 if (*aEventStatus == nsEventStatus_eConsumeNoDefault) {
6856 // If the event is consumed by the touch caret, cancel APZC panning by
6857 // setting mMultipleActionsPrevented.
6858 aEvent->mFlags.mMultipleActionsPrevented = true;
6859 return NS_OK;
6863 nsRefPtr<SelectionCarets> selectionCaret = presShell ?
6864 presShell->GetSelectionCarets() :
6865 nullptr;
6866 if (selectionCaret) {
6867 *aEventStatus = selectionCaret->HandleEvent(aEvent);
6868 if (*aEventStatus == nsEventStatus_eConsumeNoDefault) {
6869 // If the event is consumed by the selection carets, cancel APZC panning by
6870 // setting mMultipleActionsPrevented.
6871 aEvent->mFlags.mMultipleActionsPrevented = true;
6872 return NS_OK;
6877 if (sPointerEventEnabled) {
6878 UpdateActivePointerState(aEvent);
6881 if (!nsContentUtils::IsSafeToRunScript() &&
6882 aEvent->IsAllowedToDispatchDOMEvent()) {
6883 #ifdef DEBUG
6884 if (aEvent->IsIMERelatedEvent()) {
6885 nsPrintfCString warning("%d event is discarded", aEvent->message);
6886 NS_WARNING(warning.get());
6888 #endif
6889 nsContentUtils::WarnScriptWasIgnored(GetDocument());
6890 return NS_OK;
6893 nsIContent* capturingContent =
6894 (aEvent->HasMouseEventMessage() ||
6895 aEvent->mClass == eWheelEventClass ? GetCapturingContent() : nullptr);
6897 nsCOMPtr<nsIDocument> retargetEventDoc;
6898 if (!aDontRetargetEvents) {
6899 // key and IME related events should not cross top level window boundary.
6900 // Basically, such input events should be fired only on focused widget.
6901 // However, some IMEs might need to clean up composition after focused
6902 // window is deactivated. And also some tests on MozMill want to test key
6903 // handling on deactivated window because MozMill window can be activated
6904 // during tests. So, there is no merit the events should be redirected to
6905 // active window. So, the events should be handled on the last focused
6906 // content in the last focused DOM window in same top level window.
6907 // Note, if no DOM window has been focused yet, we can discard the events.
6908 if (aEvent->IsTargetedAtFocusedWindow()) {
6909 nsCOMPtr<nsPIDOMWindow> window = GetFocusedDOMWindowInOurWindow();
6910 // No DOM window in same top level window has not been focused yet,
6911 // discard the events.
6912 if (!window) {
6913 return NS_OK;
6916 retargetEventDoc = window->GetExtantDoc();
6917 if (!retargetEventDoc)
6918 return NS_OK;
6919 } else if (capturingContent) {
6920 // if the mouse is being captured then retarget the mouse event at the
6921 // document that is being captured.
6922 retargetEventDoc = capturingContent->GetCrossShadowCurrentDoc();
6923 #ifdef ANDROID
6924 } else if (aEvent->mClass == eTouchEventClass ||
6925 (aEvent->AsMouseEvent() && aEvent->AsMouseEvent()->inputSource == nsIDOMMouseEvent::MOZ_SOURCE_TOUCH)) {
6926 retargetEventDoc = GetTouchEventTargetDocument();
6927 #endif
6930 if (retargetEventDoc) {
6931 nsCOMPtr<nsIPresShell> presShell = retargetEventDoc->GetShell();
6932 if (!presShell)
6933 return NS_OK;
6935 if (presShell != this) {
6936 nsIFrame* frame = presShell->GetRootFrame();
6937 if (!frame) {
6938 if (aEvent->message == NS_QUERY_TEXT_CONTENT ||
6939 aEvent->IsContentCommandEvent()) {
6940 return NS_OK;
6943 frame = GetNearestFrameContainingPresShell(presShell);
6946 if (!frame)
6947 return NS_OK;
6949 nsCOMPtr<nsIPresShell> shell = frame->PresContext()->GetPresShell();
6950 return shell->HandleEvent(frame, aEvent, true, aEventStatus);
6955 if (aEvent->mClass == eKeyboardEventClass &&
6956 mDocument && mDocument->EventHandlingSuppressed()) {
6957 if (aEvent->message == NS_KEY_DOWN) {
6958 mNoDelayedKeyEvents = true;
6959 } else if (!mNoDelayedKeyEvents) {
6960 DelayedEvent* event = new DelayedKeyEvent(aEvent->AsKeyboardEvent());
6961 if (!mDelayedEvents.AppendElement(event)) {
6962 delete event;
6965 return NS_OK;
6968 nsIFrame* frame = aFrame;
6970 if (aEvent->IsUsingCoordinates()) {
6971 ReleasePointerCaptureCaller releasePointerCaptureCaller;
6972 if (nsLayoutUtils::AreAsyncAnimationsEnabled() && mDocument) {
6973 if (aEvent->mClass == eTouchEventClass) {
6974 nsIDocument::UnlockPointer();
6977 nsWeakFrame weakFrame(frame);
6978 { // scope for scriptBlocker.
6979 nsAutoScriptBlocker scriptBlocker;
6980 GetRootPresShell()->GetDocument()->
6981 EnumerateSubDocuments(FlushThrottledStyles, nullptr);
6984 if (!weakFrame.IsAlive()) {
6985 frame = GetNearestFrameContainingPresShell(this);
6989 NS_WARN_IF_FALSE(frame, "Nothing to handle this event!");
6990 if (!frame)
6991 return NS_OK;
6993 nsPresContext* framePresContext = frame->PresContext();
6994 nsPresContext* rootPresContext = framePresContext->GetRootPresContext();
6995 NS_ASSERTION(rootPresContext == mPresContext->GetRootPresContext(),
6996 "How did we end up outside the connected prescontext/viewmanager hierarchy?");
6997 // If we aren't starting our event dispatch from the root frame of the root prescontext,
6998 // then someone must be capturing the mouse. In that case we don't want to search the popup
6999 // list.
7000 if (framePresContext == rootPresContext &&
7001 frame == mFrameConstructor->GetRootFrame()) {
7002 nsIFrame* popupFrame =
7003 nsLayoutUtils::GetPopupFrameForEventCoordinates(rootPresContext, aEvent);
7004 // If the popupFrame is an ancestor of the 'frame', the frame should
7005 // handle the event, otherwise, the popup should handle it.
7006 if (popupFrame &&
7007 !nsContentUtils::ContentIsCrossDocDescendantOf(
7008 framePresContext->GetPresShell()->GetDocument(),
7009 popupFrame->GetContent())) {
7010 frame = popupFrame;
7014 bool captureRetarget = false;
7015 if (capturingContent) {
7016 // If a capture is active, determine if the docshell is visible. If not,
7017 // clear the capture and target the mouse event normally instead. This
7018 // would occur if the mouse button is held down while a tab change occurs.
7019 // If the docshell is visible, look for a scrolling container.
7020 bool vis;
7021 nsCOMPtr<nsIBaseWindow> baseWin =
7022 do_QueryInterface(mPresContext->GetContainerWeak());
7023 if (baseWin && NS_SUCCEEDED(baseWin->GetVisibility(&vis)) && vis) {
7024 captureRetarget = gCaptureInfo.mRetargetToElement;
7025 if (!captureRetarget) {
7026 // A check was already done above to ensure that capturingContent is
7027 // in this presshell.
7028 NS_ASSERTION(capturingContent->GetCrossShadowCurrentDoc() == GetDocument(),
7029 "Unexpected document");
7030 nsIFrame* captureFrame = capturingContent->GetPrimaryFrame();
7031 if (captureFrame) {
7032 if (capturingContent->Tag() == nsGkAtoms::select &&
7033 capturingContent->IsHTML()) {
7034 // a dropdown <select> has a child in its selectPopupList and we should
7035 // capture on that instead.
7036 nsIFrame* childFrame = captureFrame->GetChildList(nsIFrame::kSelectPopupList).FirstChild();
7037 if (childFrame) {
7038 captureFrame = childFrame;
7042 // scrollable frames should use the scrolling container as
7043 // the root instead of the document
7044 nsIScrollableFrame* scrollFrame = do_QueryFrame(captureFrame);
7045 if (scrollFrame) {
7046 frame = scrollFrame->GetScrolledFrame();
7051 else {
7052 ClearMouseCapture(nullptr);
7053 capturingContent = nullptr;
7057 // all touch events except for touchstart use a captured target
7058 if (aEvent->mClass == eTouchEventClass &&
7059 aEvent->message != NS_TOUCH_START) {
7060 captureRetarget = true;
7063 WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
7064 bool isWindowLevelMouseExit = (aEvent->message == NS_MOUSE_EXIT) &&
7065 (mouseEvent && mouseEvent->exit == WidgetMouseEvent::eTopLevel);
7067 // Get the frame at the event point. However, don't do this if we're
7068 // capturing and retargeting the event because the captured frame will
7069 // be used instead below. Also keep using the root frame if we're dealing
7070 // with a window-level mouse exit event since we want to start sending
7071 // mouse out events at the root EventStateManager.
7072 if (!captureRetarget && !isWindowLevelMouseExit) {
7073 nsPoint eventPoint;
7074 uint32_t flags = 0;
7075 if (aEvent->message == NS_TOUCH_START) {
7076 flags |= INPUT_IGNORE_ROOT_SCROLL_FRAME;
7077 WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
7078 // if this is a continuing session, ensure that all these events are
7079 // in the same document by taking the target of the events already in
7080 // the capture list
7081 nsCOMPtr<nsIContent> anyTarget;
7082 if (gCaptureTouchList->Count() > 0 && touchEvent->touches.Length() > 1) {
7083 gCaptureTouchList->Enumerate(&FindAnyTarget, &anyTarget);
7084 } else {
7085 gPreventMouseEvents = false;
7088 for (int32_t i = touchEvent->touches.Length(); i; ) {
7089 --i;
7090 dom::Touch* touch = touchEvent->touches[i];
7092 int32_t id = touch->Identifier();
7093 if (!gCaptureTouchList->Get(id, nullptr)) {
7094 // find the target for this touch
7095 eventPoint = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent,
7096 touch->mRefPoint,
7097 frame);
7098 nsIFrame* target = FindFrameTargetedByInputEvent(aEvent,
7099 frame,
7100 eventPoint,
7101 flags);
7102 if (target && !anyTarget) {
7103 target->GetContentForEvent(aEvent, getter_AddRefs(anyTarget));
7104 while (anyTarget && !anyTarget->IsElement()) {
7105 anyTarget = anyTarget->GetParent();
7107 touch->SetTarget(anyTarget);
7108 } else {
7109 nsIFrame* newTargetFrame = nullptr;
7110 for (nsIFrame* f = target; f;
7111 f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
7112 if (f->PresContext()->Document() == anyTarget->OwnerDoc()) {
7113 newTargetFrame = f;
7114 break;
7116 // We must be in a subdocument so jump directly to the root frame.
7117 // GetParentOrPlaceholderForCrossDoc gets called immediately to
7118 // jump up to the containing document.
7119 f = f->PresContext()->GetPresShell()->GetRootFrame();
7122 // if we couldn't find a target frame in the same document as
7123 // anyTarget, remove the touch from the capture touch list, as
7124 // well as the event->touches array. touchmove events that aren't
7125 // in the captured touch list will be discarded
7126 if (!newTargetFrame) {
7127 touchEvent->touches.RemoveElementAt(i);
7128 } else {
7129 target = newTargetFrame;
7130 nsCOMPtr<nsIContent> targetContent;
7131 target->GetContentForEvent(aEvent, getter_AddRefs(targetContent));
7132 while (targetContent && !targetContent->IsElement()) {
7133 targetContent = targetContent->GetParent();
7135 touch->SetTarget(targetContent);
7138 if (target) {
7139 frame = target;
7141 } else {
7142 // This touch is an old touch, we need to ensure that is not
7143 // marked as changed and set its target correctly
7144 touch->mChanged = false;
7145 int32_t id = touch->Identifier();
7147 nsRefPtr<dom::Touch> oldTouch = gCaptureTouchList->GetWeak(id);
7148 if (oldTouch) {
7149 touch->SetTarget(oldTouch->mTarget);
7153 } else {
7154 eventPoint = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, frame);
7156 if (mouseEvent && mouseEvent->mClass == eMouseEventClass &&
7157 mouseEvent->ignoreRootScrollFrame) {
7158 flags |= INPUT_IGNORE_ROOT_SCROLL_FRAME;
7160 nsIFrame* target =
7161 FindFrameTargetedByInputEvent(aEvent, frame, eventPoint, flags);
7162 if (target) {
7163 frame = target;
7167 // if a node is capturing the mouse, check if the event needs to be
7168 // retargeted at the capturing content instead. This will be the case when
7169 // capture retargeting is being used, no frame was found or the frame's
7170 // content is not a descendant of the capturing content.
7171 if (capturingContent &&
7172 (gCaptureInfo.mRetargetToElement || !frame->GetContent() ||
7173 !nsContentUtils::ContentIsCrossDocDescendantOf(frame->GetContent(),
7174 capturingContent))) {
7175 // A check was already done above to ensure that capturingContent is
7176 // in this presshell.
7177 NS_ASSERTION(capturingContent->GetCrossShadowCurrentDoc() == GetDocument(),
7178 "Unexpected document");
7179 nsIFrame* capturingFrame = capturingContent->GetPrimaryFrame();
7180 if (capturingFrame) {
7181 frame = capturingFrame;
7185 if (aEvent->mClass == ePointerEventClass &&
7186 aEvent->message != NS_POINTER_DOWN) {
7187 if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) {
7188 uint32_t pointerId = pointerEvent->pointerId;
7189 nsIContent* pointerCapturingContent = GetPointerCapturingContent(pointerId);
7191 if (pointerCapturingContent) {
7192 if (nsIFrame* capturingFrame = pointerCapturingContent->GetPrimaryFrame()) {
7193 // If pointer capture is set, we should suppress pointerover/pointerenter events
7194 // for all elements except element which have pointer capture. (Code in EventStateManager)
7195 pointerEvent->retargetedByPointerCapture = (frame != capturingFrame);
7196 frame = capturingFrame;
7199 if (pointerEvent->message == NS_POINTER_UP ||
7200 pointerEvent->message == NS_POINTER_CANCEL) {
7201 // Implicitly releasing capture for given pointer.
7202 // LOST_POINTER_CAPTURE should be send after NS_POINTER_UP or NS_POINTER_CANCEL.
7203 releasePointerCaptureCaller.SetTarget(pointerId, pointerCapturingContent);
7209 // Suppress mouse event if it's being targeted at an element inside
7210 // a document which needs events suppressed
7211 if (aEvent->mClass == eMouseEventClass &&
7212 frame->PresContext()->Document()->EventHandlingSuppressed()) {
7213 if (aEvent->message == NS_MOUSE_BUTTON_DOWN) {
7214 mNoDelayedMouseEvents = true;
7215 } else if (!mNoDelayedMouseEvents && aEvent->message == NS_MOUSE_BUTTON_UP) {
7216 DelayedEvent* event = new DelayedMouseEvent(aEvent->AsMouseEvent());
7217 if (!mDelayedEvents.AppendElement(event)) {
7218 delete event;
7222 return NS_OK;
7225 PresShell* shell =
7226 static_cast<PresShell*>(frame->PresContext()->PresShell());
7227 switch (aEvent->message) {
7228 case NS_TOUCH_MOVE:
7229 case NS_TOUCH_CANCEL:
7230 case NS_TOUCH_END: {
7231 // get the correct shell to dispatch to
7232 WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
7233 WidgetTouchEvent::TouchArray& touches = touchEvent->touches;
7234 for (uint32_t i = 0; i < touches.Length(); ++i) {
7235 dom::Touch* touch = touches[i];
7236 if (!touch) {
7237 break;
7240 nsRefPtr<dom::Touch> oldTouch =
7241 gCaptureTouchList->GetWeak(touch->Identifier());
7242 if (!oldTouch) {
7243 break;
7246 nsCOMPtr<nsIContent> content =
7247 do_QueryInterface(oldTouch->GetTarget());
7248 if (!content) {
7249 break;
7252 nsIFrame* contentFrame = content->GetPrimaryFrame();
7253 if (!contentFrame) {
7254 break;
7257 shell = static_cast<PresShell*>(
7258 contentFrame->PresContext()->PresShell());
7259 if (shell) {
7260 break;
7263 break;
7267 // Check if we have an active EventStateManager which isn't the
7268 // EventStateManager of the current PresContext.
7269 // If that is the case, and mouse is over some ancestor document,
7270 // forward event handling to the active document.
7271 // This way content can get mouse events even when
7272 // mouse is over the chrome or outside the window.
7274 // Note, currently for backwards compatibility we don't forward mouse events
7275 // to the active document when mouse is over some subdocument.
7276 EventStateManager* activeESM =
7277 EventStateManager::GetActiveEventStateManager();
7278 if (activeESM && aEvent->HasMouseEventMessage() &&
7279 activeESM != shell->GetPresContext()->EventStateManager() &&
7280 static_cast<EventStateManager*>(activeESM)->GetPresContext()) {
7281 nsIPresShell* activeShell =
7282 static_cast<EventStateManager*>(activeESM)->GetPresContext()->
7283 GetPresShell();
7284 if (activeShell &&
7285 nsContentUtils::ContentIsCrossDocDescendantOf(activeShell->GetDocument(),
7286 shell->GetDocument())) {
7287 shell = static_cast<PresShell*>(activeShell);
7288 frame = shell->GetRootFrame();
7292 if (shell != this) {
7293 // Handle the event in the correct shell.
7294 // Prevent deletion until we're done with event handling (bug 336582).
7295 nsCOMPtr<nsIPresShell> kungFuDeathGrip(shell);
7296 // We pass the subshell's root frame as the frame to start from. This is
7297 // the only correct alternative; if the event was captured then it
7298 // must have been captured by us or some ancestor shell and we
7299 // now ask the subshell to dispatch it normally.
7300 return shell->HandlePositionedEvent(frame, aEvent, aEventStatus);
7303 return HandlePositionedEvent(frame, aEvent, aEventStatus);
7306 nsresult rv = NS_OK;
7308 if (frame) {
7309 PushCurrentEventInfo(nullptr, nullptr);
7311 // key and IME related events go to the focused frame in this DOM window.
7312 if (aEvent->IsTargetedAtFocusedContent()) {
7313 mCurrentEventContent = nullptr;
7315 nsCOMPtr<nsPIDOMWindow> window =
7316 do_QueryInterface(mDocument->GetWindow());
7317 nsCOMPtr<nsPIDOMWindow> focusedWindow;
7318 nsCOMPtr<nsIContent> eventTarget =
7319 nsFocusManager::GetFocusedDescendant(window, false,
7320 getter_AddRefs(focusedWindow));
7322 // otherwise, if there is no focused content or the focused content has
7323 // no frame, just use the root content. This ensures that key events
7324 // still get sent to the window properly if nothing is focused or if a
7325 // frame goes away while it is focused.
7326 if (!eventTarget || !eventTarget->GetPrimaryFrame()) {
7327 nsCOMPtr<nsIDOMHTMLDocument> htmlDoc = do_QueryInterface(mDocument);
7328 if (htmlDoc) {
7329 nsCOMPtr<nsIDOMHTMLElement> body;
7330 htmlDoc->GetBody(getter_AddRefs(body));
7331 eventTarget = do_QueryInterface(body);
7332 if (!eventTarget) {
7333 eventTarget = mDocument->GetRootElement();
7335 } else {
7336 eventTarget = mDocument->GetRootElement();
7340 if (aEvent->message == NS_KEY_DOWN) {
7341 NS_IF_RELEASE(gKeyDownTarget);
7342 NS_IF_ADDREF(gKeyDownTarget = eventTarget);
7344 else if ((aEvent->message == NS_KEY_PRESS || aEvent->message == NS_KEY_UP) &&
7345 gKeyDownTarget) {
7346 // If a different element is now focused for the keypress/keyup event
7347 // than what was focused during the keydown event, check if the new
7348 // focused element is not in a chrome document any more, and if so,
7349 // retarget the event back at the keydown target. This prevents a
7350 // content area from grabbing the focus from chrome in-between key
7351 // events.
7352 if (eventTarget &&
7353 nsContentUtils::IsChromeDoc(gKeyDownTarget->GetComposedDoc()) !=
7354 nsContentUtils::IsChromeDoc(eventTarget->GetComposedDoc())) {
7355 eventTarget = gKeyDownTarget;
7358 if (aEvent->message == NS_KEY_UP) {
7359 NS_RELEASE(gKeyDownTarget);
7363 mCurrentEventFrame = nullptr;
7364 nsIDocument* targetDoc = eventTarget ? eventTarget->OwnerDoc() : nullptr;
7365 if (targetDoc && targetDoc != mDocument) {
7366 PopCurrentEventInfo();
7367 nsCOMPtr<nsIPresShell> shell = targetDoc->GetShell();
7368 if (shell) {
7369 rv = static_cast<PresShell*>(shell.get())->
7370 HandleRetargetedEvent(aEvent, aEventStatus, eventTarget);
7372 return rv;
7373 } else {
7374 mCurrentEventContent = eventTarget;
7377 if (!GetCurrentEventContent() || !GetCurrentEventFrame() ||
7378 InZombieDocument(mCurrentEventContent)) {
7379 rv = RetargetEventToParent(aEvent, aEventStatus);
7380 PopCurrentEventInfo();
7381 return rv;
7383 } else {
7384 mCurrentEventFrame = frame;
7386 if (GetCurrentEventFrame()) {
7387 rv = HandleEventInternal(aEvent, aEventStatus);
7390 #ifdef DEBUG
7391 ShowEventTargetDebug();
7392 #endif
7393 PopCurrentEventInfo();
7394 } else {
7395 // Activation events need to be dispatched even if no frame was found, since
7396 // we don't want the focus to be out of sync.
7398 if (!NS_EVENT_NEEDS_FRAME(aEvent)) {
7399 mCurrentEventFrame = nullptr;
7400 return HandleEventInternal(aEvent, aEventStatus);
7402 else if (aEvent->HasKeyEventMessage()) {
7403 // Keypress events in new blank tabs should not be completely thrown away.
7404 // Retarget them -- the parent chrome shell might make use of them.
7405 return RetargetEventToParent(aEvent, aEventStatus);
7409 return rv;
7412 #ifdef ANDROID
7413 nsIDocument*
7414 PresShell::GetTouchEventTargetDocument()
7416 nsPresContext* context = GetPresContext();
7417 if (!context || !context->IsRoot()) {
7418 return nullptr;
7421 nsCOMPtr<nsIDocShellTreeItem> shellAsTreeItem = context->GetDocShell();
7422 if (!shellAsTreeItem) {
7423 return nullptr;
7426 nsCOMPtr<nsIDocShellTreeOwner> owner;
7427 shellAsTreeItem->GetTreeOwner(getter_AddRefs(owner));
7428 if (!owner) {
7429 return nullptr;
7432 // now get the primary content shell (active tab)
7433 nsCOMPtr<nsIDocShellTreeItem> item;
7434 owner->GetPrimaryContentShell(getter_AddRefs(item));
7435 nsCOMPtr<nsIDocShell> childDocShell = do_QueryInterface(item);
7436 if (!childDocShell) {
7437 return nullptr;
7440 return childDocShell->GetDocument();
7442 #endif
7444 #ifdef DEBUG
7445 void
7446 PresShell::ShowEventTargetDebug()
7448 if (nsFrame::GetShowEventTargetFrameBorder() &&
7449 GetCurrentEventFrame()) {
7450 if (mDrawEventTargetFrame) {
7451 mDrawEventTargetFrame->InvalidateFrame();
7454 mDrawEventTargetFrame = mCurrentEventFrame;
7455 mDrawEventTargetFrame->InvalidateFrame();
7458 #endif
7460 nsresult
7461 PresShell::HandlePositionedEvent(nsIFrame* aTargetFrame,
7462 WidgetGUIEvent* aEvent,
7463 nsEventStatus* aEventStatus)
7465 nsresult rv = NS_OK;
7467 PushCurrentEventInfo(nullptr, nullptr);
7469 mCurrentEventFrame = aTargetFrame;
7471 if (mCurrentEventFrame) {
7472 nsCOMPtr<nsIContent> targetElement;
7473 mCurrentEventFrame->GetContentForEvent(aEvent,
7474 getter_AddRefs(targetElement));
7476 // If there is no content for this frame, target it anyway. Some
7477 // frames can be targeted but do not have content, particularly
7478 // windows with scrolling off.
7479 if (targetElement) {
7480 // Bug 103055, bug 185889: mouse events apply to *elements*, not all
7481 // nodes. Thus we get the nearest element parent here.
7482 // XXX we leave the frame the same even if we find an element
7483 // parent, so that the text frame will receive the event (selection
7484 // and friends are the ones who care about that anyway)
7486 // We use weak pointers because during this tight loop, the node
7487 // will *not* go away. And this happens on every mousemove.
7488 while (targetElement && !targetElement->IsElement()) {
7489 targetElement = targetElement->GetParent();
7492 // If we found an element, target it. Otherwise, target *nothing*.
7493 if (!targetElement) {
7494 mCurrentEventContent = nullptr;
7495 mCurrentEventFrame = nullptr;
7496 } else if (targetElement != mCurrentEventContent) {
7497 mCurrentEventContent = targetElement;
7502 if (GetCurrentEventFrame()) {
7503 rv = HandleEventInternal(aEvent, aEventStatus);
7506 #ifdef DEBUG
7507 ShowEventTargetDebug();
7508 #endif
7509 PopCurrentEventInfo();
7510 return rv;
7513 nsresult
7514 PresShell::HandleEventWithTarget(WidgetEvent* aEvent, nsIFrame* aFrame,
7515 nsIContent* aContent, nsEventStatus* aStatus)
7517 #if DEBUG
7518 MOZ_ASSERT(!aFrame || aFrame->PresContext()->GetPresShell() == this,
7519 "wrong shell");
7520 if (aContent) {
7521 nsIDocument* doc = aContent->GetCrossShadowCurrentDoc();
7522 NS_ASSERTION(doc, "event for content that isn't in a document");
7523 NS_ASSERTION(!doc || doc->GetShell() == this, "wrong shell");
7525 #endif
7526 NS_ENSURE_STATE(!aContent || aContent->GetCrossShadowCurrentDoc() == mDocument);
7528 PushCurrentEventInfo(aFrame, aContent);
7529 nsresult rv = HandleEventInternal(aEvent, aStatus);
7530 PopCurrentEventInfo();
7531 return rv;
7534 nsresult
7535 PresShell::HandleEventInternal(WidgetEvent* aEvent, nsEventStatus* aStatus)
7537 nsRefPtr<EventStateManager> manager = mPresContext->EventStateManager();
7538 nsresult rv = NS_OK;
7540 if (!NS_EVENT_NEEDS_FRAME(aEvent) || GetCurrentEventFrame()) {
7541 bool touchIsNew = false;
7542 bool isHandlingUserInput = false;
7544 // XXX How about IME events and input events for plugins?
7545 if (aEvent->mFlags.mIsTrusted) {
7546 switch (aEvent->message) {
7547 case NS_KEY_PRESS:
7548 case NS_KEY_DOWN:
7549 case NS_KEY_UP: {
7550 nsIDocument* doc = GetCurrentEventContent() ?
7551 mCurrentEventContent->OwnerDoc() : nullptr;
7552 nsIDocument* fullscreenAncestor = nullptr;
7553 if (aEvent->AsKeyboardEvent()->keyCode == NS_VK_ESCAPE) {
7554 if ((fullscreenAncestor = nsContentUtils::GetFullscreenAncestor(doc))) {
7555 // Prevent default action on ESC key press when exiting
7556 // DOM fullscreen mode. This prevents the browser ESC key
7557 // handler from stopping all loads in the document, which
7558 // would cause <video> loads to stop.
7559 aEvent->mFlags.mDefaultPrevented = true;
7560 aEvent->mFlags.mOnlyChromeDispatch = true;
7562 if (aEvent->message == NS_KEY_UP) {
7563 // ESC key released while in DOM fullscreen mode.
7564 // If fullscreen is running in content-only mode, exit the target
7565 // doctree branch from fullscreen, otherwise fully exit all
7566 // browser windows and documents from fullscreen mode.
7567 // Note: in the content-only fullscreen case, we pass the
7568 // fullscreenAncestor since |doc| may not actually be fullscreen
7569 // here, and ExitFullscreen() has no affect when passed a
7570 // non-fullscreen document.
7571 nsIDocument::ExitFullscreen(
7572 nsContentUtils::IsFullscreenApiContentOnly() ? fullscreenAncestor : nullptr,
7573 /* async */ true);
7576 nsCOMPtr<nsIDocument> pointerLockedDoc =
7577 do_QueryReferent(EventStateManager::sPointerLockedDoc);
7578 if (pointerLockedDoc) {
7579 aEvent->mFlags.mDefaultPrevented = true;
7580 aEvent->mFlags.mOnlyChromeDispatch = true;
7581 if (aEvent->message == NS_KEY_UP) {
7582 nsIDocument::UnlockPointer();
7586 // Else not full-screen mode or key code is unrestricted, fall
7587 // through to normal handling.
7589 case NS_MOUSE_BUTTON_DOWN:
7590 case NS_MOUSE_BUTTON_UP:
7591 isHandlingUserInput = true;
7592 break;
7593 case NS_TOUCH_START: {
7594 isHandlingUserInput = true;
7595 WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
7596 // if there is only one touch in this touchstart event, assume that it is
7597 // the start of a new touch session and evict any old touches in the
7598 // queue
7599 if (touchEvent->touches.Length() == 1) {
7600 WidgetTouchEvent::AutoTouchArray touches;
7601 gCaptureTouchList->Enumerate(&AppendToTouchList, (void *)&touches);
7602 for (uint32_t i = 0; i < touches.Length(); ++i) {
7603 EvictTouchPoint(touches[i]);
7606 // Add any new touches to the queue
7607 for (uint32_t i = 0; i < touchEvent->touches.Length(); ++i) {
7608 dom::Touch* touch = touchEvent->touches[i];
7609 int32_t id = touch->Identifier();
7610 if (!gCaptureTouchList->Get(id, nullptr)) {
7611 // If it is not already in the queue, it is a new touch
7612 touch->mChanged = true;
7614 touch->mMessage = aEvent->message;
7615 gCaptureTouchList->Put(id, touch);
7617 break;
7619 case NS_TOUCH_END:
7620 isHandlingUserInput = true;
7621 // Fall through to touchcancel code
7622 case NS_TOUCH_CANCEL: {
7623 // Remove the changed touches
7624 // need to make sure we only remove touches that are ending here
7625 WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
7626 WidgetTouchEvent::TouchArray& touches = touchEvent->touches;
7627 for (uint32_t i = 0; i < touches.Length(); ++i) {
7628 dom::Touch* touch = touches[i];
7629 if (!touch) {
7630 continue;
7632 touch->mMessage = aEvent->message;
7633 touch->mChanged = true;
7635 int32_t id = touch->Identifier();
7636 nsRefPtr<dom::Touch> oldTouch = gCaptureTouchList->GetWeak(id);
7637 if (!oldTouch) {
7638 continue;
7640 nsCOMPtr<EventTarget> targetPtr = oldTouch->mTarget;
7642 mCurrentEventContent = do_QueryInterface(targetPtr);
7643 touch->SetTarget(targetPtr);
7644 gCaptureTouchList->Remove(id);
7646 // add any touches left in the touch list, but ensure changed=false
7647 gCaptureTouchList->Enumerate(&AppendToTouchList, (void *)&touches);
7648 break;
7650 case NS_TOUCH_MOVE: {
7651 // Check for touches that changed. Mark them add to queue
7652 WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
7653 WidgetTouchEvent::TouchArray& touches = touchEvent->touches;
7654 bool haveChanged = false;
7655 for (int32_t i = touches.Length(); i; ) {
7656 --i;
7657 dom::Touch* touch = touches[i];
7658 if (!touch) {
7659 continue;
7661 int32_t id = touch->Identifier();
7662 touch->mMessage = aEvent->message;
7664 nsRefPtr<dom::Touch> oldTouch = gCaptureTouchList->GetWeak(id);
7665 if (!oldTouch) {
7666 touches.RemoveElementAt(i);
7667 continue;
7669 if (!touch->Equals(oldTouch)) {
7670 touch->mChanged = true;
7671 haveChanged = true;
7674 nsCOMPtr<dom::EventTarget> targetPtr = oldTouch->mTarget;
7675 if (!targetPtr) {
7676 touches.RemoveElementAt(i);
7677 continue;
7679 touch->SetTarget(targetPtr);
7681 gCaptureTouchList->Put(id, touch);
7682 // if we're moving from touchstart to touchmove for this touch
7683 // we allow preventDefault to prevent mouse events
7684 if (oldTouch->mMessage != touch->mMessage) {
7685 touchIsNew = true;
7688 // is nothing has changed, we should just return
7689 if (!haveChanged) {
7690 if (touchIsNew) {
7691 // however, if this is the first touchmove after a touchstart,
7692 // it is special in that preventDefault is allowed on it, so
7693 // we must dispatch it to content even if nothing changed. we
7694 // arbitrarily pick the first touch point to be the "changed"
7695 // touch because firing an event with no changed events doesn't
7696 // work.
7697 for (uint32_t i = 0; i < touchEvent->touches.Length(); ++i) {
7698 if (touchEvent->touches[i]) {
7699 touchEvent->touches[i]->mChanged = true;
7700 break;
7703 } else {
7704 if (gPreventMouseEvents) {
7705 *aStatus = nsEventStatus_eConsumeNoDefault;
7707 return NS_OK;
7710 break;
7712 case NS_DRAGDROP_DROP:
7713 nsCOMPtr<nsIDragSession> session = nsContentUtils::GetDragSession();
7714 if (session) {
7715 bool onlyChromeDrop = false;
7716 session->GetOnlyChromeDrop(&onlyChromeDrop);
7717 if (onlyChromeDrop) {
7718 aEvent->mFlags.mOnlyChromeDispatch = true;
7721 break;
7725 if (aEvent->message == NS_CONTEXTMENU) {
7726 WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
7727 if (mouseEvent->context == WidgetMouseEvent::eContextMenuKey &&
7728 !AdjustContextMenuKeyEvent(mouseEvent)) {
7729 return NS_OK;
7731 if (mouseEvent->IsShift()) {
7732 aEvent->mFlags.mOnlyChromeDispatch = true;
7733 aEvent->mFlags.mRetargetToNonNativeAnonymous = true;
7737 AutoHandlingUserInputStatePusher userInpStatePusher(isHandlingUserInput,
7738 aEvent, mDocument);
7740 if (aEvent->mFlags.mIsTrusted && aEvent->message == NS_MOUSE_MOVE) {
7741 nsIPresShell::AllowMouseCapture(
7742 EventStateManager::GetActiveEventStateManager() == manager);
7745 nsAutoPopupStatePusher popupStatePusher(
7746 Event::GetEventPopupControlState(aEvent));
7748 // FIXME. If the event was reused, we need to clear the old target,
7749 // bug 329430
7750 aEvent->target = nullptr;
7752 // 1. Give event to event manager for pre event state changes and
7753 // generation of synthetic events.
7754 rv = manager->PreHandleEvent(mPresContext, aEvent, mCurrentEventFrame, aStatus);
7756 // 2. Give event to the DOM for third party and JS use.
7757 if (NS_SUCCEEDED(rv)) {
7758 bool wasHandlingKeyBoardEvent =
7759 nsContentUtils::IsHandlingKeyBoardEvent();
7760 if (aEvent->mClass == eKeyboardEventClass) {
7761 nsContentUtils::SetIsHandlingKeyBoardEvent(true);
7763 if (aEvent->IsAllowedToDispatchDOMEvent()) {
7764 MOZ_ASSERT(nsContentUtils::IsSafeToRunScript(),
7765 "Somebody changed aEvent to cause a DOM event!");
7766 nsPresShellEventCB eventCB(this);
7767 if (aEvent->mClass == eTouchEventClass) {
7768 DispatchTouchEvent(aEvent, aStatus, &eventCB, touchIsNew);
7769 } else {
7770 nsCOMPtr<nsINode> eventTarget = mCurrentEventContent.get();
7771 nsPresShellEventCB* eventCBPtr = &eventCB;
7772 if (!eventTarget) {
7773 nsCOMPtr<nsIContent> targetContent;
7774 if (mCurrentEventFrame) {
7775 rv = mCurrentEventFrame->
7776 GetContentForEvent(aEvent, getter_AddRefs(targetContent));
7778 if (NS_SUCCEEDED(rv) && targetContent) {
7779 eventTarget = do_QueryInterface(targetContent);
7780 } else if (mDocument) {
7781 eventTarget = do_QueryInterface(mDocument);
7782 // If we don't have any content, the callback wouldn't probably
7783 // do nothing.
7784 eventCBPtr = nullptr;
7787 if (eventTarget) {
7788 if (aEvent->mClass == eCompositionEventClass ||
7789 aEvent->mClass == eTextEventClass) {
7790 IMEStateManager::DispatchCompositionEvent(eventTarget,
7791 mPresContext, aEvent, aStatus, eventCBPtr);
7792 } else {
7793 EventDispatcher::Dispatch(eventTarget, mPresContext,
7794 aEvent, nullptr, aStatus, eventCBPtr);
7800 nsContentUtils::SetIsHandlingKeyBoardEvent(wasHandlingKeyBoardEvent);
7802 // 3. Give event to event manager for post event state changes and
7803 // generation of synthetic events.
7804 if (!mIsDestroying && NS_SUCCEEDED(rv)) {
7805 rv = manager->PostHandleEvent(mPresContext, aEvent,
7806 GetCurrentEventFrame(), aStatus);
7810 if (aEvent->message == NS_MOUSE_BUTTON_UP) {
7811 // reset the capturing content now that the mouse button is up
7812 SetCapturingContent(nullptr, 0);
7813 } else if (aEvent->message == NS_MOUSE_MOVE) {
7814 nsIPresShell::AllowMouseCapture(false);
7817 return rv;
7820 void
7821 nsIPresShell::DispatchGotOrLostPointerCaptureEvent(bool aIsGotCapture,
7822 uint32_t aPointerId,
7823 nsIContent* aCaptureTarget)
7825 PointerEventInit init;
7826 init.mPointerId = aPointerId;
7827 init.mBubbles = true;
7828 nsRefPtr<mozilla::dom::PointerEvent> event;
7829 event = PointerEvent::Constructor(aCaptureTarget,
7830 aIsGotCapture
7831 ? NS_LITERAL_STRING("gotpointercapture")
7832 : NS_LITERAL_STRING("lostpointercapture"),
7833 init);
7834 if (event) {
7835 bool dummy;
7836 aCaptureTarget->DispatchEvent(event->InternalDOMEvent(), &dummy);
7840 void
7841 PresShell::DispatchTouchEvent(WidgetEvent* aEvent,
7842 nsEventStatus* aStatus,
7843 nsPresShellEventCB* aEventCB,
7844 bool aTouchIsNew)
7846 // calling preventDefault on touchstart or the first touchmove for a
7847 // point prevents mouse events. calling it on the touchend should
7848 // prevent click dispatching.
7849 bool canPrevent = (aEvent->message == NS_TOUCH_START) ||
7850 (aEvent->message == NS_TOUCH_MOVE && aTouchIsNew) ||
7851 (aEvent->message == NS_TOUCH_END);
7852 bool preventDefault = false;
7853 nsEventStatus tmpStatus = nsEventStatus_eIgnore;
7854 WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
7856 // loop over all touches and dispatch events on any that have changed
7857 for (uint32_t i = 0; i < touchEvent->touches.Length(); ++i) {
7858 dom::Touch* touch = touchEvent->touches[i];
7859 if (!touch || !touch->mChanged) {
7860 continue;
7863 nsCOMPtr<EventTarget> targetPtr = touch->mTarget;
7864 nsCOMPtr<nsIContent> content = do_QueryInterface(targetPtr);
7865 if (!content) {
7866 continue;
7869 nsIDocument* doc = content->OwnerDoc();
7870 nsIContent* capturingContent = GetCapturingContent();
7871 if (capturingContent) {
7872 if (capturingContent->OwnerDoc() != doc) {
7873 // Wrong document, don't dispatch anything.
7874 continue;
7876 content = capturingContent;
7878 // copy the event
7879 WidgetTouchEvent newEvent(touchEvent->mFlags.mIsTrusted,
7880 touchEvent->message, touchEvent->widget);
7881 newEvent.AssignTouchEventData(*touchEvent, false);
7882 newEvent.target = targetPtr;
7884 nsRefPtr<PresShell> contentPresShell;
7885 if (doc == mDocument) {
7886 contentPresShell = static_cast<PresShell*>(doc->GetShell());
7887 if (contentPresShell) {
7888 //XXXsmaug huge hack. Pushing possibly capturing content,
7889 // even though event target is something else.
7890 contentPresShell->PushCurrentEventInfo(
7891 content->GetPrimaryFrame(), content);
7895 nsIPresShell *presShell = doc->GetShell();
7896 if (!presShell) {
7897 continue;
7900 nsPresContext *context = presShell->GetPresContext();
7902 tmpStatus = nsEventStatus_eIgnore;
7903 EventDispatcher::Dispatch(targetPtr, context,
7904 &newEvent, nullptr, &tmpStatus, aEventCB);
7905 if (nsEventStatus_eConsumeNoDefault == tmpStatus ||
7906 newEvent.mFlags.mMultipleActionsPrevented) {
7907 preventDefault = true;
7910 if (newEvent.mFlags.mMultipleActionsPrevented) {
7911 touchEvent->mFlags.mMultipleActionsPrevented = true;
7914 if (contentPresShell) {
7915 contentPresShell->PopCurrentEventInfo();
7919 // if preventDefault was called on any of the events dispatched
7920 // and this is touchstart, or the first touchmove, widget should consume
7921 // other events that would be associated with this touch session
7922 if (preventDefault && canPrevent) {
7923 gPreventMouseEvents = true;
7926 if (gPreventMouseEvents) {
7927 *aStatus = nsEventStatus_eConsumeNoDefault;
7928 } else {
7929 *aStatus = nsEventStatus_eIgnore;
7933 // Dispatch event to content only (NOT full processing)
7934 // See also HandleEventWithTarget which does full event processing.
7935 nsresult
7936 PresShell::HandleDOMEventWithTarget(nsIContent* aTargetContent,
7937 WidgetEvent* aEvent,
7938 nsEventStatus* aStatus)
7940 nsresult rv = NS_OK;
7942 PushCurrentEventInfo(nullptr, aTargetContent);
7944 // Bug 41013: Check if the event should be dispatched to content.
7945 // It's possible that we are in the middle of destroying the window
7946 // and the js context is out of date. This check detects the case
7947 // that caused a crash in bug 41013, but there may be a better way
7948 // to handle this situation!
7949 nsCOMPtr<nsISupports> container = mPresContext->GetContainerWeak();
7950 if (container) {
7952 // Dispatch event to content
7953 rv = EventDispatcher::Dispatch(aTargetContent, mPresContext, aEvent,
7954 nullptr, aStatus);
7957 PopCurrentEventInfo();
7958 return rv;
7961 // See the method above.
7962 nsresult
7963 PresShell::HandleDOMEventWithTarget(nsIContent* aTargetContent,
7964 nsIDOMEvent* aEvent,
7965 nsEventStatus* aStatus)
7967 nsresult rv = NS_OK;
7969 PushCurrentEventInfo(nullptr, aTargetContent);
7970 nsCOMPtr<nsISupports> container = mPresContext->GetContainerWeak();
7971 if (container) {
7972 rv = EventDispatcher::DispatchDOMEvent(aTargetContent, nullptr, aEvent,
7973 mPresContext, aStatus);
7976 PopCurrentEventInfo();
7977 return rv;
7980 bool
7981 PresShell::AdjustContextMenuKeyEvent(WidgetMouseEvent* aEvent)
7983 #ifdef MOZ_XUL
7984 // if a menu is open, open the context menu relative to the active item on the menu.
7985 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
7986 if (pm) {
7987 nsIFrame* popupFrame = pm->GetTopPopup(ePopupTypeMenu);
7988 if (popupFrame) {
7989 nsIFrame* itemFrame =
7990 (static_cast<nsMenuPopupFrame *>(popupFrame))->GetCurrentMenuItem();
7991 if (!itemFrame)
7992 itemFrame = popupFrame;
7994 nsCOMPtr<nsIWidget> widget = popupFrame->GetNearestWidget();
7995 aEvent->widget = widget;
7996 nsIntPoint widgetPoint = widget->WidgetToScreenOffset();
7997 aEvent->refPoint = LayoutDeviceIntPoint::FromUntyped(
7998 itemFrame->GetScreenRect().BottomLeft() - widgetPoint);
8000 mCurrentEventContent = itemFrame->GetContent();
8001 mCurrentEventFrame = itemFrame;
8003 return true;
8006 #endif
8008 // If we're here because of the key-equiv for showing context menus, we
8009 // have to twiddle with the NS event to make sure the context menu comes
8010 // up in the upper left of the relevant content area before we create
8011 // the DOM event. Since we never call InitMouseEvent() on the event,
8012 // the client X/Y will be 0,0. We can make use of that if the widget is null.
8013 // Use the root view manager's widget since it's most likely to have one,
8014 // and the coordinates returned by GetCurrentItemAndPositionForElement
8015 // are relative to the widget of the root of the root view manager.
8016 nsRootPresContext* rootPC = mPresContext->GetRootPresContext();
8017 aEvent->refPoint.x = 0;
8018 aEvent->refPoint.y = 0;
8019 if (rootPC) {
8020 rootPC->PresShell()->GetViewManager()->
8021 GetRootWidget(getter_AddRefs(aEvent->widget));
8023 if (aEvent->widget) {
8024 // default the refpoint to the topleft of our document
8025 nsPoint offset(0, 0);
8026 nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
8027 if (rootFrame) {
8028 nsView* view = rootFrame->GetClosestView(&offset);
8029 offset += view->GetOffsetToWidget(aEvent->widget);
8030 aEvent->refPoint =
8031 LayoutDeviceIntPoint::FromAppUnitsToNearest(offset, mPresContext->AppUnitsPerDevPixel());
8034 } else {
8035 aEvent->widget = nullptr;
8038 // see if we should use the caret position for the popup
8039 nsIntPoint caretPoint;
8040 // Beware! This may flush notifications via synchronous
8041 // ScrollSelectionIntoView.
8042 if (PrepareToUseCaretPosition(aEvent->widget, caretPoint)) {
8043 // caret position is good
8044 aEvent->refPoint = LayoutDeviceIntPoint::FromUntyped(caretPoint);
8045 return true;
8048 // If we're here because of the key-equiv for showing context menus, we
8049 // have to reset the event target to the currently focused element. Get it
8050 // from the focus controller.
8051 nsCOMPtr<nsIDOMElement> currentFocus;
8052 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
8053 if (fm)
8054 fm->GetFocusedElement(getter_AddRefs(currentFocus));
8056 // Reset event coordinates relative to focused frame in view
8057 if (currentFocus) {
8058 nsCOMPtr<nsIContent> currentPointElement;
8059 GetCurrentItemAndPositionForElement(currentFocus,
8060 getter_AddRefs(currentPointElement),
8061 aEvent->refPoint,
8062 aEvent->widget);
8063 if (currentPointElement) {
8064 mCurrentEventContent = currentPointElement;
8065 mCurrentEventFrame = nullptr;
8066 GetCurrentEventFrame();
8070 return true;
8073 // PresShell::PrepareToUseCaretPosition
8075 // This checks to see if we should use the caret position for popup context
8076 // menus. Returns true if the caret position should be used, and the
8077 // coordinates of that position is returned in aTargetPt. This function
8078 // will also scroll the window as needed to make the caret visible.
8080 // The event widget should be the widget that generated the event, and
8081 // whose coordinate system the resulting event's refPoint should be
8082 // relative to. The returned point is in device pixels realtive to the
8083 // widget passed in.
8084 bool
8085 PresShell::PrepareToUseCaretPosition(nsIWidget* aEventWidget, nsIntPoint& aTargetPt)
8087 nsresult rv;
8089 // check caret visibility
8090 nsRefPtr<nsCaret> caret = GetCaret();
8091 NS_ENSURE_TRUE(caret, false);
8093 bool caretVisible = caret->IsVisible();
8094 if (!caretVisible)
8095 return false;
8097 // caret selection, this is a temporary weak reference, so no refcounting is
8098 // needed
8099 nsISelection* domSelection = caret->GetSelection();
8100 NS_ENSURE_TRUE(domSelection, false);
8102 // since the match could be an anonymous textnode inside a
8103 // <textarea> or text <input>, we need to get the outer frame
8104 // note: frames are not refcounted
8105 nsIFrame* frame = nullptr; // may be nullptr
8106 nsCOMPtr<nsIDOMNode> node;
8107 rv = domSelection->GetFocusNode(getter_AddRefs(node));
8108 NS_ENSURE_SUCCESS(rv, false);
8109 NS_ENSURE_TRUE(node, false);
8110 nsCOMPtr<nsIContent> content(do_QueryInterface(node));
8111 if (content) {
8112 nsIContent* nonNative = content->FindFirstNonChromeOnlyAccessContent();
8113 content = nonNative;
8116 if (content) {
8117 // It seems like ScrollSelectionIntoView should be enough, but it's
8118 // not. The problem is that scrolling the selection into view when it is
8119 // below the current viewport will align the top line of the frame exactly
8120 // with the bottom of the window. This is fine, BUT, the popup event causes
8121 // the control to be re-focused which does this exact call to
8122 // ScrollContentIntoView, which has a one-pixel disagreement of whether the
8123 // frame is actually in view. The result is that the frame is aligned with
8124 // the top of the window, but the menu is still at the bottom.
8126 // Doing this call first forces the frame to be in view, eliminating the
8127 // problem. The only difference in the result is that if your cursor is in
8128 // an edit box below the current view, you'll get the edit box aligned with
8129 // the top of the window. This is arguably better behavior anyway.
8130 rv = ScrollContentIntoView(content,
8131 nsIPresShell::ScrollAxis(
8132 nsIPresShell::SCROLL_MINIMUM,
8133 nsIPresShell::SCROLL_IF_NOT_VISIBLE),
8134 nsIPresShell::ScrollAxis(
8135 nsIPresShell::SCROLL_MINIMUM,
8136 nsIPresShell::SCROLL_IF_NOT_VISIBLE),
8137 nsIPresShell::SCROLL_OVERFLOW_HIDDEN);
8138 NS_ENSURE_SUCCESS(rv, false);
8139 frame = content->GetPrimaryFrame();
8140 NS_WARN_IF_FALSE(frame, "No frame for focused content?");
8143 // Actually scroll the selection (ie caret) into view. Note that this must
8144 // be synchronous since we will be checking the caret position on the screen.
8146 // Be easy about errors, and just don't scroll in those cases. Better to have
8147 // the correct menu at a weird place than the wrong menu.
8148 // After ScrollSelectionIntoView(), the pending notifications might be
8149 // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
8150 nsCOMPtr<nsISelectionController> selCon;
8151 if (frame)
8152 frame->GetSelectionController(GetPresContext(), getter_AddRefs(selCon));
8153 else
8154 selCon = static_cast<nsISelectionController *>(this);
8155 if (selCon) {
8156 rv = selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
8157 nsISelectionController::SELECTION_FOCUS_REGION,
8158 nsISelectionController::SCROLL_SYNCHRONOUS);
8159 NS_ENSURE_SUCCESS(rv, false);
8162 nsPresContext* presContext = GetPresContext();
8164 // get caret position relative to the closest view
8165 nsRect caretCoords;
8166 nsIFrame* caretFrame = caret->GetGeometry(&caretCoords);
8167 if (!caretFrame)
8168 return false;
8169 nsPoint viewOffset;
8170 nsView* view = caretFrame->GetClosestView(&viewOffset);
8171 if (!view)
8172 return false;
8173 // and then get the caret coords relative to the event widget
8174 if (aEventWidget) {
8175 viewOffset += view->GetOffsetToWidget(aEventWidget);
8177 caretCoords.MoveBy(viewOffset);
8179 // caret coordinates are in app units, convert to pixels
8180 aTargetPt.x =
8181 presContext->AppUnitsToDevPixels(caretCoords.x + caretCoords.width);
8182 aTargetPt.y =
8183 presContext->AppUnitsToDevPixels(caretCoords.y + caretCoords.height);
8185 // make sure rounding doesn't return a pixel which is outside the caret
8186 // (e.g. one line lower)
8187 aTargetPt.y -= 1;
8189 return true;
8192 void
8193 PresShell::GetCurrentItemAndPositionForElement(nsIDOMElement *aCurrentEl,
8194 nsIContent** aTargetToUse,
8195 LayoutDeviceIntPoint& aTargetPt,
8196 nsIWidget *aRootWidget)
8198 nsCOMPtr<nsIContent> focusedContent(do_QueryInterface(aCurrentEl));
8199 ScrollContentIntoView(focusedContent,
8200 ScrollAxis(),
8201 ScrollAxis(),
8202 nsIPresShell::SCROLL_OVERFLOW_HIDDEN);
8204 nsPresContext* presContext = GetPresContext();
8206 bool istree = false, checkLineHeight = true;
8207 nscoord extraTreeY = 0;
8209 #ifdef MOZ_XUL
8210 // Set the position to just underneath the current item for multi-select
8211 // lists or just underneath the selected item for single-select lists. If
8212 // the element is not a list, or there is no selection, leave the position
8213 // as is.
8214 nsCOMPtr<nsIDOMXULSelectControlItemElement> item;
8215 nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelect =
8216 do_QueryInterface(aCurrentEl);
8217 if (multiSelect) {
8218 checkLineHeight = false;
8220 int32_t currentIndex;
8221 multiSelect->GetCurrentIndex(&currentIndex);
8222 if (currentIndex >= 0) {
8223 nsCOMPtr<nsIDOMXULElement> xulElement(do_QueryInterface(aCurrentEl));
8224 if (xulElement) {
8225 nsCOMPtr<nsIBoxObject> box;
8226 xulElement->GetBoxObject(getter_AddRefs(box));
8227 nsCOMPtr<nsITreeBoxObject> treeBox(do_QueryInterface(box));
8228 // Tree view special case (tree items have no frames)
8229 // Get the focused row and add its coordinates, which are already in pixels
8230 // XXX Boris, should we create a new interface so that this doesn't
8231 // need to know about trees? Something like nsINodelessChildCreator which
8232 // could provide the current focus coordinates?
8233 if (treeBox) {
8234 treeBox->EnsureRowIsVisible(currentIndex);
8235 int32_t firstVisibleRow, rowHeight;
8236 treeBox->GetFirstVisibleRow(&firstVisibleRow);
8237 treeBox->GetRowHeight(&rowHeight);
8239 extraTreeY += presContext->CSSPixelsToAppUnits(
8240 (currentIndex - firstVisibleRow + 1) * rowHeight);
8241 istree = true;
8243 nsCOMPtr<nsITreeColumns> cols;
8244 treeBox->GetColumns(getter_AddRefs(cols));
8245 if (cols) {
8246 nsCOMPtr<nsITreeColumn> col;
8247 cols->GetFirstColumn(getter_AddRefs(col));
8248 if (col) {
8249 nsCOMPtr<nsIDOMElement> colElement;
8250 col->GetElement(getter_AddRefs(colElement));
8251 nsCOMPtr<nsIContent> colContent(do_QueryInterface(colElement));
8252 if (colContent) {
8253 nsIFrame* frame = colContent->GetPrimaryFrame();
8254 if (frame) {
8255 extraTreeY += frame->GetSize().height;
8261 else {
8262 multiSelect->GetCurrentItem(getter_AddRefs(item));
8267 else {
8268 // don't check menulists as the selected item will be inside a popup.
8269 nsCOMPtr<nsIDOMXULMenuListElement> menulist = do_QueryInterface(aCurrentEl);
8270 if (!menulist) {
8271 nsCOMPtr<nsIDOMXULSelectControlElement> select =
8272 do_QueryInterface(aCurrentEl);
8273 if (select) {
8274 checkLineHeight = false;
8275 select->GetSelectedItem(getter_AddRefs(item));
8280 if (item)
8281 focusedContent = do_QueryInterface(item);
8282 #endif
8284 nsIFrame *frame = focusedContent->GetPrimaryFrame();
8285 if (frame) {
8286 NS_ASSERTION(frame->PresContext() == GetPresContext(),
8287 "handling event for focused content that is not in our document?");
8289 nsPoint frameOrigin(0, 0);
8291 // Get the frame's origin within its view
8292 nsView *view = frame->GetClosestView(&frameOrigin);
8293 NS_ASSERTION(view, "No view for frame");
8295 // View's origin relative the widget
8296 if (aRootWidget) {
8297 frameOrigin += view->GetOffsetToWidget(aRootWidget);
8300 // Start context menu down and to the right from top left of frame
8301 // use the lineheight. This is a good distance to move the context
8302 // menu away from the top left corner of the frame. If we always
8303 // used the frame height, the context menu could end up far away,
8304 // for example when we're focused on linked images.
8305 // On the other hand, we want to use the frame height if it's less
8306 // than the current line height, so that the context menu appears
8307 // associated with the correct frame.
8308 nscoord extra = 0;
8309 if (!istree) {
8310 extra = frame->GetSize().height;
8311 if (checkLineHeight) {
8312 nsIScrollableFrame *scrollFrame =
8313 nsLayoutUtils::GetNearestScrollableFrame(frame);
8314 if (scrollFrame) {
8315 nsSize scrollAmount = scrollFrame->GetLineScrollAmount();
8316 nsIFrame* f = do_QueryFrame(scrollFrame);
8317 int32_t APD = presContext->AppUnitsPerDevPixel();
8318 int32_t scrollAPD = f->PresContext()->AppUnitsPerDevPixel();
8319 scrollAmount = scrollAmount.ConvertAppUnits(scrollAPD, APD);
8320 if (extra > scrollAmount.height) {
8321 extra = scrollAmount.height;
8327 aTargetPt.x = presContext->AppUnitsToDevPixels(frameOrigin.x);
8328 aTargetPt.y = presContext->AppUnitsToDevPixels(
8329 frameOrigin.y + extra + extraTreeY);
8332 NS_IF_ADDREF(*aTargetToUse = focusedContent);
8335 bool
8336 PresShell::ShouldIgnoreInvalidation()
8338 return mPaintingSuppressed || !mIsActive || mIsNeverPainting;
8341 void
8342 PresShell::WillPaint()
8344 nsRootPresContext* rootPresContext = mPresContext->GetRootPresContext();
8345 if (!rootPresContext) {
8346 // In some edge cases, such as when we don't have a root frame yet,
8347 // we can't find the root prescontext. There's nothing to do in that
8348 // case.
8349 return;
8352 // Don't bother doing anything if some viewmanager in our tree is painting
8353 // while we still have painting suppressed or we are not active.
8354 if (mPaintingSuppressed || !mIsActive || !IsVisible()) {
8355 return;
8358 rootPresContext->FlushWillPaintObservers();
8359 if (mIsDestroying)
8360 return;
8362 // Process reflows, if we have them, to reduce flicker due to invalidates and
8363 // reflow being interspersed. Note that we _do_ allow this to be
8364 // interruptible; if we can't do all the reflows it's better to flicker a bit
8365 // than to freeze up.
8366 FlushPendingNotifications(ChangesToFlush(Flush_InterruptibleLayout, false));
8369 void
8370 PresShell::WillPaintWindow()
8372 nsRootPresContext* rootPresContext = mPresContext->GetRootPresContext();
8373 if (rootPresContext != mPresContext) {
8374 // This could be a popup's presshell. We don't allow plugins in popups
8375 // so there's nothing to do here.
8376 return;
8379 #ifndef XP_MACOSX
8380 rootPresContext->ApplyPluginGeometryUpdates();
8381 #endif
8384 void
8385 PresShell::DidPaintWindow()
8387 if (mDocument) {
8388 nsCOMPtr<nsPIDOMWindow> window = mDocument->GetWindow();
8389 if (window) {
8390 window->SendAfterRemotePaintIfRequested();
8394 nsRootPresContext* rootPresContext = mPresContext->GetRootPresContext();
8395 if (rootPresContext != mPresContext) {
8396 // This could be a popup's presshell. No point in notifying XPConnect
8397 // about compositing of popups.
8398 return;
8402 bool
8403 PresShell::IsVisible()
8405 if (!mViewManager)
8406 return false;
8408 nsView* view = mViewManager->GetRootView();
8409 if (!view)
8410 return true;
8412 // inner view of subdoc frame
8413 view = view->GetParent();
8414 if (!view)
8415 return true;
8417 // subdoc view
8418 view = view->GetParent();
8419 if (!view)
8420 return true;
8422 nsIFrame* frame = view->GetFrame();
8423 if (!frame)
8424 return true;
8426 return frame->IsVisibleConsideringAncestors(nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY);
8429 nsresult
8430 PresShell::GetAgentStyleSheets(nsCOMArray<nsIStyleSheet>& aSheets)
8432 aSheets.Clear();
8433 int32_t sheetCount = mStyleSet->SheetCount(nsStyleSet::eAgentSheet);
8435 for (int32_t i = 0; i < sheetCount; ++i) {
8436 nsIStyleSheet *sheet = mStyleSet->StyleSheetAt(nsStyleSet::eAgentSheet, i);
8437 if (!aSheets.AppendObject(sheet))
8438 return NS_ERROR_OUT_OF_MEMORY;
8441 return NS_OK;
8444 nsresult
8445 PresShell::SetAgentStyleSheets(const nsCOMArray<nsIStyleSheet>& aSheets)
8447 return mStyleSet->ReplaceSheets(nsStyleSet::eAgentSheet, aSheets);
8450 nsresult
8451 PresShell::AddOverrideStyleSheet(nsIStyleSheet *aSheet)
8453 return mStyleSet->PrependStyleSheet(nsStyleSet::eOverrideSheet, aSheet);
8456 nsresult
8457 PresShell::RemoveOverrideStyleSheet(nsIStyleSheet *aSheet)
8459 return mStyleSet->RemoveStyleSheet(nsStyleSet::eOverrideSheet, aSheet);
8462 static void
8463 FreezeElement(nsISupports *aSupports, void * /* unused */)
8465 nsCOMPtr<nsIObjectLoadingContent> olc(do_QueryInterface(aSupports));
8466 if (olc) {
8467 olc->StopPluginInstance();
8471 static bool
8472 FreezeSubDocument(nsIDocument *aDocument, void *aData)
8474 nsIPresShell *shell = aDocument->GetShell();
8475 if (shell)
8476 shell->Freeze();
8478 return true;
8481 void
8482 PresShell::Freeze()
8484 mUpdateImageVisibilityEvent.Revoke();
8486 MaybeReleaseCapturingContent();
8488 mDocument->EnumerateActivityObservers(FreezeElement, nullptr);
8490 if (mCaret) {
8491 SetCaretEnabled(false);
8494 mPaintingSuppressed = true;
8496 if (mDocument) {
8497 mDocument->EnumerateSubDocuments(FreezeSubDocument, nullptr);
8500 nsPresContext* presContext = GetPresContext();
8501 if (presContext &&
8502 presContext->RefreshDriver()->PresContext() == presContext) {
8503 presContext->RefreshDriver()->Freeze();
8506 mFrozen = true;
8507 if (mDocument) {
8508 UpdateImageLockingState();
8512 void
8513 PresShell::FireOrClearDelayedEvents(bool aFireEvents)
8515 mNoDelayedMouseEvents = false;
8516 mNoDelayedKeyEvents = false;
8517 if (!aFireEvents) {
8518 mDelayedEvents.Clear();
8519 return;
8522 if (mDocument) {
8523 nsCOMPtr<nsIDocument> doc = mDocument;
8524 while (!mIsDestroying && mDelayedEvents.Length() &&
8525 !doc->EventHandlingSuppressed()) {
8526 nsAutoPtr<DelayedEvent> ev(mDelayedEvents[0].forget());
8527 mDelayedEvents.RemoveElementAt(0);
8528 ev->Dispatch();
8530 if (!doc->EventHandlingSuppressed()) {
8531 mDelayedEvents.Clear();
8536 static void
8537 ThawElement(nsISupports *aSupports, void *aShell)
8539 nsCOMPtr<nsIObjectLoadingContent> olc(do_QueryInterface(aSupports));
8540 if (olc) {
8541 olc->AsyncStartPluginInstance();
8545 static bool
8546 ThawSubDocument(nsIDocument *aDocument, void *aData)
8548 nsIPresShell *shell = aDocument->GetShell();
8549 if (shell)
8550 shell->Thaw();
8552 return true;
8555 void
8556 PresShell::Thaw()
8558 nsPresContext* presContext = GetPresContext();
8559 if (presContext &&
8560 presContext->RefreshDriver()->PresContext() == presContext) {
8561 presContext->RefreshDriver()->Thaw();
8564 mDocument->EnumerateActivityObservers(ThawElement, this);
8566 if (mDocument)
8567 mDocument->EnumerateSubDocuments(ThawSubDocument, nullptr);
8569 // Get the activeness of our presshell, as this might have changed
8570 // while we were in the bfcache
8571 QueryIsActive();
8573 // We're now unfrozen
8574 mFrozen = false;
8575 UpdateImageLockingState();
8577 UnsuppressPainting();
8580 //--------------------------------------------------------
8581 // Start of protected and private methods on the PresShell
8582 //--------------------------------------------------------
8584 void
8585 PresShell::MaybeScheduleReflow()
8587 ASSERT_REFLOW_SCHEDULED_STATE();
8588 if (mReflowScheduled || mIsDestroying || mIsReflowing ||
8589 mDirtyRoots.Length() == 0)
8590 return;
8592 if (!mPresContext->HasPendingInterrupt() || !ScheduleReflowOffTimer()) {
8593 ScheduleReflow();
8596 ASSERT_REFLOW_SCHEDULED_STATE();
8599 void
8600 PresShell::ScheduleReflow()
8602 NS_PRECONDITION(!mReflowScheduled, "Why are we trying to schedule a reflow?");
8603 ASSERT_REFLOW_SCHEDULED_STATE();
8605 if (GetPresContext()->RefreshDriver()->AddLayoutFlushObserver(this)) {
8606 mReflowScheduled = true;
8609 ASSERT_REFLOW_SCHEDULED_STATE();
8612 nsresult
8613 PresShell::DidCauseReflow()
8615 NS_ASSERTION(mChangeNestCount != 0, "Unexpected call to DidCauseReflow()");
8616 --mChangeNestCount;
8617 nsContentUtils::RemoveScriptBlocker();
8619 return NS_OK;
8622 void
8623 PresShell::WillDoReflow()
8625 mPresContext->FlushUserFontSet();
8627 mPresContext->FlushCounterStyles();
8629 mFrameConstructor->BeginUpdate();
8631 mLastReflowStart = GetPerformanceNow();
8634 void
8635 PresShell::DidDoReflow(bool aInterruptible, bool aWasInterrupted)
8637 mFrameConstructor->EndUpdate();
8639 HandlePostedReflowCallbacks(aInterruptible);
8641 nsCOMPtr<nsIDocShell> docShell = mPresContext->GetDocShell();
8642 if (docShell) {
8643 DOMHighResTimeStamp now = GetPerformanceNow();
8644 docShell->NotifyReflowObservers(aInterruptible, mLastReflowStart, now);
8647 if (sSynthMouseMove) {
8648 SynthesizeMouseMove(false);
8651 if (mTouchCaret) {
8652 mTouchCaret->UpdatePositionIfNeeded();
8655 if (!aWasInterrupted) {
8656 ClearReflowOnZoomPending();
8660 DOMHighResTimeStamp
8661 PresShell::GetPerformanceNow()
8663 DOMHighResTimeStamp now = 0;
8664 nsPIDOMWindow* window = mDocument->GetInnerWindow();
8666 if (window) {
8667 nsPerformance* perf = window->GetPerformance();
8669 if (perf) {
8670 now = perf->Now();
8674 return now;
8677 static PLDHashOperator
8678 MarkFramesDirtyToRoot(nsPtrHashKey<nsIFrame>* p, void* closure)
8680 nsIFrame* target = static_cast<nsIFrame*>(closure);
8681 for (nsIFrame* f = p->GetKey(); f && !NS_SUBTREE_DIRTY(f);
8682 f = f->GetParent()) {
8683 f->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
8685 if (f == target) {
8686 break;
8690 return PL_DHASH_NEXT;
8693 void
8694 PresShell::sReflowContinueCallback(nsITimer* aTimer, void* aPresShell)
8696 nsRefPtr<PresShell> self = static_cast<PresShell*>(aPresShell);
8698 NS_PRECONDITION(aTimer == self->mReflowContinueTimer, "Unexpected timer");
8699 self->mReflowContinueTimer = nullptr;
8700 self->ScheduleReflow();
8703 bool
8704 PresShell::ScheduleReflowOffTimer()
8706 NS_PRECONDITION(!mReflowScheduled, "Shouldn't get here");
8707 ASSERT_REFLOW_SCHEDULED_STATE();
8709 if (!mReflowContinueTimer) {
8710 mReflowContinueTimer = do_CreateInstance("@mozilla.org/timer;1");
8711 if (!mReflowContinueTimer ||
8712 NS_FAILED(mReflowContinueTimer->
8713 InitWithFuncCallback(sReflowContinueCallback, this, 30,
8714 nsITimer::TYPE_ONE_SHOT))) {
8715 return false;
8718 return true;
8721 bool
8722 PresShell::DoReflow(nsIFrame* target, bool aInterruptible)
8724 if (mIsZombie) {
8725 return true;
8728 gfxTextPerfMetrics* tp = mPresContext->GetTextPerfMetrics();
8729 TimeStamp timeStart;
8730 if (tp) {
8731 tp->Accumulate();
8732 tp->reflowCount++;
8733 timeStart = TimeStamp::Now();
8736 target->SchedulePaint();
8737 nsIFrame *parent = nsLayoutUtils::GetCrossDocParentFrame(target);
8738 while (parent) {
8739 nsSVGEffects::InvalidateDirectRenderingObservers(parent);
8740 parent = nsLayoutUtils::GetCrossDocParentFrame(parent);
8743 nsAutoCString docURL("N/A");
8744 nsIURI *uri = mDocument->GetDocumentURI();
8745 if (uri)
8746 uri->GetSpec(docURL);
8748 PROFILER_LABEL_PRINTF("PresShell", "DoReflow",
8749 js::ProfileEntry::Category::GRAPHICS, "(%s)", docURL.get());
8751 if (mReflowContinueTimer) {
8752 mReflowContinueTimer->Cancel();
8753 mReflowContinueTimer = nullptr;
8756 nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
8758 nsRefPtr<nsRenderingContext> rcx = CreateReferenceRenderingContext();
8760 #ifdef DEBUG
8761 mCurrentReflowRoot = target;
8762 #endif
8764 target->WillReflow(mPresContext);
8766 // If the target frame is the root of the frame hierarchy, then
8767 // use all the available space. If it's simply a `reflow root',
8768 // then use the target frame's size as the available space.
8769 WritingMode wm = target->GetWritingMode();
8770 LogicalSize size(wm);
8771 if (target == rootFrame) {
8772 size = LogicalSize(wm, mPresContext->GetVisibleArea().Size());
8773 } else {
8774 size = target->GetLogicalSize();
8777 NS_ASSERTION(!target->GetNextInFlow() && !target->GetPrevInFlow(),
8778 "reflow roots should never split");
8780 // Don't pass size directly to the reflow state, since a
8781 // constrained height implies page/column breaking.
8782 LogicalSize reflowSize(wm, size.ISize(wm), NS_UNCONSTRAINEDSIZE);
8783 nsHTMLReflowState reflowState(mPresContext, target, rcx, reflowSize,
8784 nsHTMLReflowState::CALLER_WILL_INIT);
8786 if (rootFrame == target) {
8787 reflowState.Init(mPresContext);
8789 // When the root frame is being reflowed with unconstrained block-size
8790 // (which happens when we're called from
8791 // nsDocumentViewer::SizeToContent), we're effectively doing a
8792 // resize in the block direction, since it changes the meaning of
8793 // percentage block-sizes even if no block-sizes actually changed.
8794 // The same applies when we reflow again after that computation. This is
8795 // an unusual case, and isn't caught by nsHTMLReflowState::InitResizeFlags.
8796 bool hasUnconstrainedBSize = size.BSize(wm) == NS_UNCONSTRAINEDSIZE;
8798 if (hasUnconstrainedBSize || mLastRootReflowHadUnconstrainedBSize) {
8799 reflowState.mFlags.mVResize = true;
8802 mLastRootReflowHadUnconstrainedBSize = hasUnconstrainedBSize;
8803 } else {
8804 // Initialize reflow state with current used border and padding,
8805 // in case this was set specially by the parent frame when the reflow root
8806 // was reflowed by its parent.
8807 nsMargin currentBorder = target->GetUsedBorder();
8808 nsMargin currentPadding = target->GetUsedPadding();
8809 reflowState.Init(mPresContext, -1, -1, &currentBorder, &currentPadding);
8812 // fix the computed height
8813 NS_ASSERTION(reflowState.ComputedPhysicalMargin() == nsMargin(0, 0, 0, 0),
8814 "reflow state should not set margin for reflow roots");
8815 if (size.BSize(wm) != NS_UNCONSTRAINEDSIZE) {
8816 nscoord computedBSize =
8817 size.BSize(wm) - reflowState.ComputedLogicalBorderPadding().BStartEnd(wm);
8818 computedBSize = std::max(computedBSize, 0);
8819 reflowState.SetComputedBSize(computedBSize);
8821 NS_ASSERTION(reflowState.ComputedISize() ==
8822 size.ISize(wm) -
8823 reflowState.ComputedLogicalBorderPadding().IStartEnd(wm),
8824 "reflow state computed incorrect inline size");
8826 mPresContext->ReflowStarted(aInterruptible);
8827 mIsReflowing = true;
8829 nsReflowStatus status;
8830 nsHTMLReflowMetrics desiredSize(reflowState);
8831 target->Reflow(mPresContext, desiredSize, reflowState, status);
8833 // If an incremental reflow is initiated at a frame other than the
8834 // root frame, then its desired size had better not change! If it's
8835 // initiated at the root, then the size better not change unless its
8836 // height was unconstrained to start with.
8837 nsRect boundsRelativeToTarget = nsRect(0, 0, desiredSize.Width(), desiredSize.Height());
8838 NS_ASSERTION((target == rootFrame &&
8839 size.BSize(wm) == NS_UNCONSTRAINEDSIZE) ||
8840 (desiredSize.ISize(wm) == size.ISize(wm) &&
8841 desiredSize.BSize(wm) == size.BSize(wm)),
8842 "non-root frame's desired size changed during an "
8843 "incremental reflow");
8844 NS_ASSERTION(target == rootFrame ||
8845 desiredSize.VisualOverflow().IsEqualInterior(boundsRelativeToTarget),
8846 "non-root reflow roots must not have visible overflow");
8847 NS_ASSERTION(target == rootFrame ||
8848 desiredSize.ScrollableOverflow().IsEqualEdges(boundsRelativeToTarget),
8849 "non-root reflow roots must not have scrollable overflow");
8850 NS_ASSERTION(status == NS_FRAME_COMPLETE,
8851 "reflow roots should never split");
8853 target->SetSize(boundsRelativeToTarget.Size());
8855 // Always use boundsRelativeToTarget here, not desiredSize.GetVisualOverflowArea(),
8856 // because for root frames (where they could be different, since root frames
8857 // are allowed to have overflow) the root view bounds need to match the
8858 // viewport bounds; the view manager "window dimensions" code depends on it.
8859 nsContainerFrame::SyncFrameViewAfterReflow(mPresContext, target,
8860 target->GetView(),
8861 boundsRelativeToTarget);
8862 nsContainerFrame::SyncWindowProperties(mPresContext, target,
8863 target->GetView(), rcx);
8865 target->DidReflow(mPresContext, nullptr, nsDidReflowStatus::FINISHED);
8866 if (target == rootFrame && size.BSize(wm) == NS_UNCONSTRAINEDSIZE) {
8867 mPresContext->SetVisibleArea(boundsRelativeToTarget);
8870 #ifdef DEBUG
8871 mCurrentReflowRoot = nullptr;
8872 #endif
8874 NS_ASSERTION(mPresContext->HasPendingInterrupt() ||
8875 mFramesToDirty.Count() == 0,
8876 "Why do we need to dirty anything if not interrupted?");
8878 mIsReflowing = false;
8879 bool interrupted = mPresContext->HasPendingInterrupt();
8880 if (interrupted) {
8881 // Make sure target gets reflowed again.
8882 mFramesToDirty.EnumerateEntries(&MarkFramesDirtyToRoot, target);
8883 NS_ASSERTION(NS_SUBTREE_DIRTY(target), "Why is the target not dirty?");
8884 mDirtyRoots.AppendElement(target);
8885 mDocument->SetNeedLayoutFlush();
8887 // Clear mFramesToDirty after we've done the NS_SUBTREE_DIRTY(target)
8888 // assertion so that if it fails it's easier to see what's going on.
8889 #ifdef NOISY_INTERRUPTIBLE_REFLOW
8890 printf("mFramesToDirty.Count() == %u\n", mFramesToDirty.Count());
8891 #endif /* NOISY_INTERRUPTIBLE_REFLOW */
8892 mFramesToDirty.Clear();
8894 // Any FlushPendingNotifications with interruptible reflows
8895 // should be suppressed now. We don't want to do extra reflow work
8896 // before our reflow event happens.
8897 mSuppressInterruptibleReflows = true;
8898 MaybeScheduleReflow();
8901 #ifdef PR_LOGGING
8902 // dump text perf metrics for reflows with significant text processing
8903 if (tp) {
8904 if (tp->current.numChars > 100) {
8905 TimeDuration reflowTime = TimeStamp::Now() - timeStart;
8906 LogTextPerfStats(tp, this, tp->current,
8907 reflowTime.ToMilliseconds(), eLog_reflow, nullptr);
8909 tp->Accumulate();
8911 #endif
8913 return !interrupted;
8916 #ifdef DEBUG
8917 void
8918 PresShell::DoVerifyReflow()
8920 if (GetVerifyReflowEnable()) {
8921 // First synchronously render what we have so far so that we can
8922 // see it.
8923 nsView* rootView = mViewManager->GetRootView();
8924 mViewManager->InvalidateView(rootView);
8926 FlushPendingNotifications(Flush_Layout);
8927 mInVerifyReflow = true;
8928 bool ok = VerifyIncrementalReflow();
8929 mInVerifyReflow = false;
8930 if (VERIFY_REFLOW_ALL & gVerifyReflowFlags) {
8931 printf("ProcessReflowCommands: finished (%s)\n",
8932 ok ? "ok" : "failed");
8935 if (!mDirtyRoots.IsEmpty()) {
8936 printf("XXX yikes! reflow commands queued during verify-reflow\n");
8940 #endif
8942 // used with Telemetry metrics
8943 #define NS_LONG_REFLOW_TIME_MS 5000
8945 bool
8946 PresShell::ProcessReflowCommands(bool aInterruptible)
8948 if (mDirtyRoots.IsEmpty() && !mShouldUnsuppressPainting) {
8949 // Nothing to do; bail out
8950 return true;
8953 mozilla::TimeStamp timerStart = mozilla::TimeStamp::Now();
8954 bool interrupted = false;
8955 if (!mDirtyRoots.IsEmpty()) {
8957 #ifdef DEBUG
8958 if (VERIFY_REFLOW_DUMP_COMMANDS & gVerifyReflowFlags) {
8959 printf("ProcessReflowCommands: begin incremental reflow\n");
8961 #endif
8963 // If reflow is interruptible, then make a note of our deadline.
8964 const PRIntervalTime deadline = aInterruptible
8965 ? PR_IntervalNow() + PR_MicrosecondsToInterval(gMaxRCProcessingTime)
8966 : (PRIntervalTime)0;
8968 // Scope for the reflow entry point
8970 nsAutoScriptBlocker scriptBlocker;
8971 WillDoReflow();
8972 AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Reflow);
8973 nsViewManager::AutoDisableRefresh refreshBlocker(mViewManager);
8975 do {
8976 // Send an incremental reflow notification to the target frame.
8977 int32_t idx = mDirtyRoots.Length() - 1;
8978 nsIFrame *target = mDirtyRoots[idx];
8979 mDirtyRoots.RemoveElementAt(idx);
8981 if (!NS_SUBTREE_DIRTY(target)) {
8982 // It's not dirty anymore, which probably means the notification
8983 // was posted in the middle of a reflow (perhaps with a reflow
8984 // root in the middle). Don't do anything.
8985 continue;
8988 interrupted = !DoReflow(target, aInterruptible);
8990 // Keep going until we're out of reflow commands, or we've run
8991 // past our deadline, or we're interrupted.
8992 } while (!interrupted && !mDirtyRoots.IsEmpty() &&
8993 (!aInterruptible || PR_IntervalNow() < deadline));
8995 interrupted = !mDirtyRoots.IsEmpty();
8998 // Exiting the scriptblocker might have killed us
8999 if (!mIsDestroying) {
9000 DidDoReflow(aInterruptible, interrupted);
9003 // DidDoReflow might have killed us
9004 if (!mIsDestroying) {
9005 #ifdef DEBUG
9006 if (VERIFY_REFLOW_DUMP_COMMANDS & gVerifyReflowFlags) {
9007 printf("\nPresShell::ProcessReflowCommands() finished: this=%p\n",
9008 (void*)this);
9010 DoVerifyReflow();
9011 #endif
9013 // If any new reflow commands were enqueued during the reflow, schedule
9014 // another reflow event to process them. Note that we want to do this
9015 // after DidDoReflow(), since that method can change whether there are
9016 // dirty roots around by flushing, and there's no point in posting a
9017 // reflow event just to have the flush revoke it.
9018 if (!mDirtyRoots.IsEmpty()) {
9019 MaybeScheduleReflow();
9020 // And tell our document that we might need flushing
9021 mDocument->SetNeedLayoutFlush();
9026 if (!mIsDestroying && mShouldUnsuppressPainting &&
9027 mDirtyRoots.IsEmpty()) {
9028 // We only unlock if we're out of reflows. It's pointless
9029 // to unlock if reflows are still pending, since reflows
9030 // are just going to thrash the frames around some more. By
9031 // waiting we avoid an overeager "jitter" effect.
9032 mShouldUnsuppressPainting = false;
9033 UnsuppressAndInvalidate();
9036 if (mDocument->GetRootElement()) {
9037 TimeDuration elapsed = TimeStamp::Now() - timerStart;
9038 int32_t intElapsed = int32_t(elapsed.ToMilliseconds());
9040 Telemetry::ID id;
9041 if (mDocument->GetRootElement()->IsXUL()) {
9042 id = mIsActive
9043 ? Telemetry::XUL_FOREGROUND_REFLOW_MS
9044 : Telemetry::XUL_BACKGROUND_REFLOW_MS;
9045 } else {
9046 id = mIsActive
9047 ? Telemetry::HTML_FOREGROUND_REFLOW_MS_2
9048 : Telemetry::HTML_BACKGROUND_REFLOW_MS_2;
9050 Telemetry::Accumulate(id, intElapsed);
9051 if (intElapsed > NS_LONG_REFLOW_TIME_MS) {
9052 Telemetry::Accumulate(Telemetry::LONG_REFLOW_INTERRUPTIBLE,
9053 aInterruptible ? 1 : 0);
9057 return !interrupted;
9060 void
9061 PresShell::WindowSizeMoveDone()
9063 if (mPresContext) {
9064 EventStateManager::ClearGlobalActiveContent(nullptr);
9065 ClearMouseCapture(nullptr);
9069 #ifdef MOZ_XUL
9071 * It's better to add stuff to the |DidSetStyleContext| method of the
9072 * relevant frames than adding it here. These methods should (ideally,
9073 * anyway) go away.
9076 // Return value says whether to walk children.
9077 typedef bool (* frameWalkerFn)(nsIFrame *aFrame, void *aClosure);
9079 static bool
9080 ReResolveMenusAndTrees(nsIFrame *aFrame, void *aClosure)
9082 // Trees have a special style cache that needs to be flushed when
9083 // the theme changes.
9084 nsTreeBodyFrame *treeBody = do_QueryFrame(aFrame);
9085 if (treeBody)
9086 treeBody->ClearStyleAndImageCaches();
9088 // We deliberately don't re-resolve style on a menu's popup
9089 // sub-content, since doing so slows menus to a crawl. That means we
9090 // have to special-case them on a skin switch, and ensure that the
9091 // popup frames just get destroyed completely.
9092 nsMenuFrame* menu = do_QueryFrame(aFrame);
9093 if (menu)
9094 menu->CloseMenu(true);
9095 return true;
9098 static bool
9099 ReframeImageBoxes(nsIFrame *aFrame, void *aClosure)
9101 nsStyleChangeList *list = static_cast<nsStyleChangeList*>(aClosure);
9102 if (aFrame->GetType() == nsGkAtoms::imageBoxFrame) {
9103 list->AppendChange(aFrame, aFrame->GetContent(),
9104 NS_STYLE_HINT_FRAMECHANGE);
9105 return false; // don't walk descendants
9107 return true; // walk descendants
9110 static void
9111 WalkFramesThroughPlaceholders(nsPresContext *aPresContext, nsIFrame *aFrame,
9112 frameWalkerFn aFunc, void *aClosure)
9114 bool walkChildren = (*aFunc)(aFrame, aClosure);
9115 if (!walkChildren)
9116 return;
9118 nsIFrame::ChildListIterator lists(aFrame);
9119 for (; !lists.IsDone(); lists.Next()) {
9120 nsFrameList::Enumerator childFrames(lists.CurrentList());
9121 for (; !childFrames.AtEnd(); childFrames.Next()) {
9122 nsIFrame* child = childFrames.get();
9123 if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
9124 // only do frames that are in flow, and recur through the
9125 // out-of-flows of placeholders.
9126 WalkFramesThroughPlaceholders(aPresContext,
9127 nsPlaceholderFrame::GetRealFrameFor(child),
9128 aFunc, aClosure);
9133 #endif
9135 NS_IMETHODIMP
9136 PresShell::Observe(nsISupports* aSubject,
9137 const char* aTopic,
9138 const char16_t* aData)
9140 #ifdef MOZ_XUL
9141 if (!nsCRT::strcmp(aTopic, "chrome-flush-skin-caches")) {
9142 nsIFrame *rootFrame = mFrameConstructor->GetRootFrame();
9143 // Need to null-check because "chrome-flush-skin-caches" can happen
9144 // at interesting times during startup.
9145 if (rootFrame) {
9146 NS_ASSERTION(mViewManager, "View manager must exist");
9148 nsWeakFrame weakRoot(rootFrame);
9149 // Have to make sure that the content notifications are flushed before we
9150 // start messing with the frame model; otherwise we can get content doubling.
9151 mDocument->FlushPendingNotifications(Flush_ContentAndNotify);
9153 if (weakRoot.IsAlive()) {
9154 WalkFramesThroughPlaceholders(mPresContext, rootFrame,
9155 &ReResolveMenusAndTrees, nullptr);
9157 // Because "chrome:" URL equality is messy, reframe image box
9158 // frames (hack!).
9159 nsStyleChangeList changeList;
9160 WalkFramesThroughPlaceholders(mPresContext, rootFrame,
9161 ReframeImageBoxes, &changeList);
9162 // Mark ourselves as not safe to flush while we're doing frame
9163 // construction.
9165 nsAutoScriptBlocker scriptBlocker;
9166 ++mChangeNestCount;
9167 RestyleManager* restyleManager = mPresContext->RestyleManager();
9168 restyleManager->ProcessRestyledFrames(changeList);
9169 restyleManager->FlushOverflowChangedTracker();
9170 --mChangeNestCount;
9174 return NS_OK;
9176 #endif
9178 if (!nsCRT::strcmp(aTopic, "agent-sheet-added") && mStyleSet) {
9179 AddAgentSheet(aSubject);
9180 return NS_OK;
9183 if (!nsCRT::strcmp(aTopic, "user-sheet-added") && mStyleSet) {
9184 AddUserSheet(aSubject);
9185 return NS_OK;
9188 if (!nsCRT::strcmp(aTopic, "author-sheet-added") && mStyleSet) {
9189 AddAuthorSheet(aSubject);
9190 return NS_OK;
9193 if (!nsCRT::strcmp(aTopic, "agent-sheet-removed") && mStyleSet) {
9194 RemoveSheet(nsStyleSet::eAgentSheet, aSubject);
9195 return NS_OK;
9198 if (!nsCRT::strcmp(aTopic, "user-sheet-removed") && mStyleSet) {
9199 RemoveSheet(nsStyleSet::eUserSheet, aSubject);
9200 return NS_OK;
9203 if (!nsCRT::strcmp(aTopic, "author-sheet-removed") && mStyleSet) {
9204 RemoveSheet(nsStyleSet::eDocSheet, aSubject);
9205 return NS_OK;
9208 NS_WARNING("unrecognized topic in PresShell::Observe");
9209 return NS_ERROR_FAILURE;
9212 bool
9213 nsIPresShell::AddRefreshObserverInternal(nsARefreshObserver* aObserver,
9214 mozFlushType aFlushType)
9216 nsPresContext* presContext = GetPresContext();
9217 return presContext &&
9218 presContext->RefreshDriver()->AddRefreshObserver(aObserver, aFlushType);
9221 /* virtual */ bool
9222 nsIPresShell::AddRefreshObserverExternal(nsARefreshObserver* aObserver,
9223 mozFlushType aFlushType)
9225 return AddRefreshObserverInternal(aObserver, aFlushType);
9228 bool
9229 nsIPresShell::RemoveRefreshObserverInternal(nsARefreshObserver* aObserver,
9230 mozFlushType aFlushType)
9232 nsPresContext* presContext = GetPresContext();
9233 return presContext &&
9234 presContext->RefreshDriver()->RemoveRefreshObserver(aObserver, aFlushType);
9237 /* virtual */ bool
9238 nsIPresShell::RemoveRefreshObserverExternal(nsARefreshObserver* aObserver,
9239 mozFlushType aFlushType)
9241 return RemoveRefreshObserverInternal(aObserver, aFlushType);
9244 /* virtual */ bool
9245 nsIPresShell::AddPostRefreshObserver(nsAPostRefreshObserver* aObserver)
9247 nsPresContext* presContext = GetPresContext();
9248 if (!presContext) {
9249 return false;
9251 presContext->RefreshDriver()->AddPostRefreshObserver(aObserver);
9252 return true;
9255 /* virtual */ bool
9256 nsIPresShell::RemovePostRefreshObserver(nsAPostRefreshObserver* aObserver)
9258 nsPresContext* presContext = GetPresContext();
9259 if (!presContext) {
9260 return false;
9262 presContext->RefreshDriver()->RemovePostRefreshObserver(aObserver);
9263 return true;
9266 //------------------------------------------------------
9267 // End of protected and private methods on the PresShell
9268 //------------------------------------------------------
9270 //------------------------------------------------------------------
9271 //-- Delayed event Classes Impls
9272 //------------------------------------------------------------------
9274 PresShell::DelayedInputEvent::DelayedInputEvent() :
9275 DelayedEvent(),
9276 mEvent(nullptr)
9280 PresShell::DelayedInputEvent::~DelayedInputEvent()
9282 delete mEvent;
9285 void
9286 PresShell::DelayedInputEvent::Dispatch()
9288 if (!mEvent || !mEvent->widget) {
9289 return;
9291 nsCOMPtr<nsIWidget> widget = mEvent->widget;
9292 nsEventStatus status;
9293 widget->DispatchEvent(mEvent, status);
9296 PresShell::DelayedMouseEvent::DelayedMouseEvent(WidgetMouseEvent* aEvent) :
9297 DelayedInputEvent()
9299 WidgetMouseEvent* mouseEvent =
9300 new WidgetMouseEvent(aEvent->mFlags.mIsTrusted,
9301 aEvent->message,
9302 aEvent->widget,
9303 aEvent->reason,
9304 aEvent->context);
9305 mouseEvent->AssignMouseEventData(*aEvent, false);
9306 mEvent = mouseEvent;
9309 PresShell::DelayedKeyEvent::DelayedKeyEvent(WidgetKeyboardEvent* aEvent) :
9310 DelayedInputEvent()
9312 WidgetKeyboardEvent* keyEvent =
9313 new WidgetKeyboardEvent(aEvent->mFlags.mIsTrusted,
9314 aEvent->message,
9315 aEvent->widget);
9316 keyEvent->AssignKeyEventData(*aEvent, false);
9317 keyEvent->mFlags.mIsSynthesizedForTests = aEvent->mFlags.mIsSynthesizedForTests;
9318 mEvent = keyEvent;
9321 // Start of DEBUG only code
9323 #ifdef DEBUG
9325 static void
9326 LogVerifyMessage(nsIFrame* k1, nsIFrame* k2, const char* aMsg)
9328 nsAutoString n1, n2;
9329 if (k1) {
9330 k1->GetFrameName(n1);
9331 } else {
9332 n1.AssignLiteral(MOZ_UTF16("(null)"));
9335 if (k2) {
9336 k2->GetFrameName(n2);
9337 } else {
9338 n2.AssignLiteral(MOZ_UTF16("(null)"));
9341 printf("verifyreflow: %s %p != %s %p %s\n",
9342 NS_LossyConvertUTF16toASCII(n1).get(), (void*)k1,
9343 NS_LossyConvertUTF16toASCII(n2).get(), (void*)k2, aMsg);
9346 static void
9347 LogVerifyMessage(nsIFrame* k1, nsIFrame* k2, const char* aMsg,
9348 const nsRect& r1, const nsRect& r2)
9350 printf("VerifyReflow Error:\n");
9351 nsAutoString name;
9353 if (k1) {
9354 k1->GetFrameName(name);
9355 printf(" %s %p ", NS_LossyConvertUTF16toASCII(name).get(), (void*)k1);
9357 printf("{%d, %d, %d, %d} != \n", r1.x, r1.y, r1.width, r1.height);
9359 if (k2) {
9360 k2->GetFrameName(name);
9361 printf(" %s %p ", NS_LossyConvertUTF16toASCII(name).get(), (void*)k2);
9363 printf("{%d, %d, %d, %d}\n %s\n",
9364 r2.x, r2.y, r2.width, r2.height, aMsg);
9367 static void
9368 LogVerifyMessage(nsIFrame* k1, nsIFrame* k2, const char* aMsg,
9369 const nsIntRect& r1, const nsIntRect& r2)
9371 printf("VerifyReflow Error:\n");
9372 nsAutoString name;
9374 if (k1) {
9375 k1->GetFrameName(name);
9376 printf(" %s %p ", NS_LossyConvertUTF16toASCII(name).get(), (void*)k1);
9378 printf("{%d, %d, %d, %d} != \n", r1.x, r1.y, r1.width, r1.height);
9380 if (k2) {
9381 k2->GetFrameName(name);
9382 printf(" %s %p ", NS_LossyConvertUTF16toASCII(name).get(), (void*)k2);
9384 printf("{%d, %d, %d, %d}\n %s\n",
9385 r2.x, r2.y, r2.width, r2.height, aMsg);
9388 static bool
9389 CompareTrees(nsPresContext* aFirstPresContext, nsIFrame* aFirstFrame,
9390 nsPresContext* aSecondPresContext, nsIFrame* aSecondFrame)
9392 if (!aFirstPresContext || !aFirstFrame || !aSecondPresContext || !aSecondFrame)
9393 return true;
9394 // XXX Evil hack to reduce false positives; I can't seem to figure
9395 // out how to flush scrollbar changes correctly
9396 //if (aFirstFrame->GetType() == nsGkAtoms::scrollbarFrame)
9397 // return true;
9398 bool ok = true;
9399 nsIFrame::ChildListIterator lists1(aFirstFrame);
9400 nsIFrame::ChildListIterator lists2(aSecondFrame);
9401 do {
9402 const nsFrameList& kids1 = !lists1.IsDone() ? lists1.CurrentList() : nsFrameList();
9403 const nsFrameList& kids2 = !lists2.IsDone() ? lists2.CurrentList() : nsFrameList();
9404 int32_t l1 = kids1.GetLength();
9405 int32_t l2 = kids2.GetLength();;
9406 if (l1 != l2) {
9407 ok = false;
9408 LogVerifyMessage(kids1.FirstChild(), kids2.FirstChild(),
9409 "child counts don't match: ");
9410 printf("%d != %d\n", l1, l2);
9411 if (0 == (VERIFY_REFLOW_ALL & gVerifyReflowFlags)) {
9412 break;
9416 nsIntRect r1, r2;
9417 nsView* v1;
9418 nsView* v2;
9419 for (nsFrameList::Enumerator e1(kids1), e2(kids2);
9421 e1.Next(), e2.Next()) {
9422 nsIFrame* k1 = e1.get();
9423 nsIFrame* k2 = e2.get();
9424 if (((nullptr == k1) && (nullptr != k2)) ||
9425 ((nullptr != k1) && (nullptr == k2))) {
9426 ok = false;
9427 LogVerifyMessage(k1, k2, "child lists are different\n");
9428 break;
9430 else if (nullptr != k1) {
9431 // Verify that the frames are the same size
9432 if (!k1->GetRect().IsEqualInterior(k2->GetRect())) {
9433 ok = false;
9434 LogVerifyMessage(k1, k2, "(frame rects)", k1->GetRect(), k2->GetRect());
9437 // Make sure either both have views or neither have views; if they
9438 // do have views, make sure the views are the same size. If the
9439 // views have widgets, make sure they both do or neither does. If
9440 // they do, make sure the widgets are the same size.
9441 v1 = k1->GetView();
9442 v2 = k2->GetView();
9443 if (((nullptr == v1) && (nullptr != v2)) ||
9444 ((nullptr != v1) && (nullptr == v2))) {
9445 ok = false;
9446 LogVerifyMessage(k1, k2, "child views are not matched\n");
9448 else if (nullptr != v1) {
9449 if (!v1->GetBounds().IsEqualInterior(v2->GetBounds())) {
9450 LogVerifyMessage(k1, k2, "(view rects)", v1->GetBounds(), v2->GetBounds());
9453 nsIWidget* w1 = v1->GetWidget();
9454 nsIWidget* w2 = v2->GetWidget();
9455 if (((nullptr == w1) && (nullptr != w2)) ||
9456 ((nullptr != w1) && (nullptr == w2))) {
9457 ok = false;
9458 LogVerifyMessage(k1, k2, "child widgets are not matched\n");
9460 else if (nullptr != w1) {
9461 w1->GetBounds(r1);
9462 w2->GetBounds(r2);
9463 if (!r1.IsEqualEdges(r2)) {
9464 LogVerifyMessage(k1, k2, "(widget rects)", r1, r2);
9468 if (!ok && (0 == (VERIFY_REFLOW_ALL & gVerifyReflowFlags))) {
9469 break;
9472 // XXX Should perhaps compare their float managers.
9474 // Compare the sub-trees too
9475 if (!CompareTrees(aFirstPresContext, k1, aSecondPresContext, k2)) {
9476 ok = false;
9477 if (0 == (VERIFY_REFLOW_ALL & gVerifyReflowFlags)) {
9478 break;
9482 else {
9483 break;
9486 if (!ok && (0 == (VERIFY_REFLOW_ALL & gVerifyReflowFlags))) {
9487 break;
9490 lists1.Next();
9491 lists2.Next();
9492 if (lists1.IsDone() != lists2.IsDone() ||
9493 (!lists1.IsDone() && lists1.CurrentID() != lists2.CurrentID())) {
9494 if (0 == (VERIFY_REFLOW_ALL & gVerifyReflowFlags)) {
9495 ok = false;
9497 LogVerifyMessage(kids1.FirstChild(), kids2.FirstChild(),
9498 "child list names are not matched: ");
9499 fprintf(stdout, "%s != %s\n",
9500 !lists1.IsDone() ? mozilla::layout::ChildListName(lists1.CurrentID()) : "(null)",
9501 !lists2.IsDone() ? mozilla::layout::ChildListName(lists2.CurrentID()) : "(null)");
9502 break;
9504 } while (ok && !lists1.IsDone());
9506 return ok;
9508 #endif
9510 #if 0
9511 static nsIFrame*
9512 FindTopFrame(nsIFrame* aRoot)
9514 if (aRoot) {
9515 nsIContent* content = aRoot->GetContent();
9516 if (content) {
9517 nsIAtom* tag;
9518 content->GetTag(tag);
9519 if (nullptr != tag) {
9520 NS_RELEASE(tag);
9521 return aRoot;
9525 // Try one of the children
9526 nsIFrame* kid = aRoot->GetFirstPrincipalChild();
9527 while (nullptr != kid) {
9528 nsIFrame* result = FindTopFrame(kid);
9529 if (nullptr != result) {
9530 return result;
9532 kid = kid->GetNextSibling();
9535 return nullptr;
9537 #endif
9540 #ifdef DEBUG
9542 nsStyleSet*
9543 PresShell::CloneStyleSet(nsStyleSet* aSet)
9545 nsStyleSet *clone = new nsStyleSet();
9547 int32_t i, n = aSet->SheetCount(nsStyleSet::eOverrideSheet);
9548 for (i = 0; i < n; i++) {
9549 nsIStyleSheet* ss = aSet->StyleSheetAt(nsStyleSet::eOverrideSheet, i);
9550 if (ss)
9551 clone->AppendStyleSheet(nsStyleSet::eOverrideSheet, ss);
9554 // The document expects to insert document stylesheets itself
9555 #if 0
9556 n = aSet->SheetCount(nsStyleSet::eDocSheet);
9557 for (i = 0; i < n; i++) {
9558 nsIStyleSheet* ss = aSet->StyleSheetAt(nsStyleSet::eDocSheet, i);
9559 if (ss)
9560 clone->AddDocStyleSheet(ss, mDocument);
9562 #endif
9564 n = aSet->SheetCount(nsStyleSet::eUserSheet);
9565 for (i = 0; i < n; i++) {
9566 nsIStyleSheet* ss = aSet->StyleSheetAt(nsStyleSet::eUserSheet, i);
9567 if (ss)
9568 clone->AppendStyleSheet(nsStyleSet::eUserSheet, ss);
9571 n = aSet->SheetCount(nsStyleSet::eAgentSheet);
9572 for (i = 0; i < n; i++) {
9573 nsIStyleSheet* ss = aSet->StyleSheetAt(nsStyleSet::eAgentSheet, i);
9574 if (ss)
9575 clone->AppendStyleSheet(nsStyleSet::eAgentSheet, ss);
9577 return clone;
9580 // After an incremental reflow, we verify the correctness by doing a
9581 // full reflow into a fresh frame tree.
9582 bool
9583 PresShell::VerifyIncrementalReflow()
9585 if (VERIFY_REFLOW_NOISY & gVerifyReflowFlags) {
9586 printf("Building Verification Tree...\n");
9589 // Create a presentation context to view the new frame tree
9590 nsRefPtr<nsPresContext> cx =
9591 new nsRootPresContext(mDocument, mPresContext->IsPaginated() ?
9592 nsPresContext::eContext_PrintPreview :
9593 nsPresContext::eContext_Galley);
9594 NS_ENSURE_TRUE(cx, false);
9596 nsDeviceContext *dc = mPresContext->DeviceContext();
9597 nsresult rv = cx->Init(dc);
9598 NS_ENSURE_SUCCESS(rv, false);
9600 // Get our scrolling preference
9601 nsView* rootView = mViewManager->GetRootView();
9602 NS_ENSURE_TRUE(rootView->HasWidget(), false);
9603 nsIWidget* parentWidget = rootView->GetWidget();
9605 // Create a new view manager.
9606 nsRefPtr<nsViewManager> vm = new nsViewManager();
9607 NS_ENSURE_TRUE(vm, false);
9608 rv = vm->Init(dc);
9609 NS_ENSURE_SUCCESS(rv, false);
9611 // Create a child window of the parent that is our "root view/window"
9612 // Create a view
9613 nsRect tbounds = mPresContext->GetVisibleArea();
9614 nsView* view = vm->CreateView(tbounds, nullptr);
9615 NS_ENSURE_TRUE(view, false);
9617 //now create the widget for the view
9618 rv = view->CreateWidgetForParent(parentWidget, nullptr, true);
9619 NS_ENSURE_SUCCESS(rv, false);
9621 // Setup hierarchical relationship in view manager
9622 vm->SetRootView(view);
9624 // Make the new presentation context the same size as our
9625 // presentation context.
9626 nsRect r = mPresContext->GetVisibleArea();
9627 cx->SetVisibleArea(r);
9629 // Create a new presentation shell to view the document. Use the
9630 // exact same style information that this document has.
9631 nsAutoPtr<nsStyleSet> newSet(CloneStyleSet(mStyleSet));
9632 nsCOMPtr<nsIPresShell> sh = mDocument->CreateShell(cx, vm, newSet);
9633 NS_ENSURE_TRUE(sh, false);
9634 newSet.forget();
9635 // Note that after we create the shell, we must make sure to destroy it
9636 sh->SetVerifyReflowEnable(false); // turn off verify reflow while we're reflowing the test frame tree
9637 vm->SetPresShell(sh);
9639 nsAutoCauseReflowNotifier crNotifier(this);
9640 sh->Initialize(r.width, r.height);
9642 mDocument->BindingManager()->ProcessAttachedQueue();
9643 sh->FlushPendingNotifications(Flush_Layout);
9644 sh->SetVerifyReflowEnable(true); // turn on verify reflow again now that we're done reflowing the test frame tree
9645 // Force the non-primary presshell to unsuppress; it doesn't want to normally
9646 // because it thinks it's hidden
9647 ((PresShell*)sh.get())->mPaintingSuppressed = false;
9648 if (VERIFY_REFLOW_NOISY & gVerifyReflowFlags) {
9649 printf("Verification Tree built, comparing...\n");
9652 // Now that the document has been reflowed, use its frame tree to
9653 // compare against our frame tree.
9654 nsIFrame* root1 = mFrameConstructor->GetRootFrame();
9655 nsIFrame* root2 = sh->GetRootFrame();
9656 bool ok = CompareTrees(mPresContext, root1, cx, root2);
9657 if (!ok && (VERIFY_REFLOW_NOISY & gVerifyReflowFlags)) {
9658 printf("Verify reflow failed, primary tree:\n");
9659 root1->List(stdout, 0);
9660 printf("Verification tree:\n");
9661 root2->List(stdout, 0);
9664 #if 0
9665 // Sample code for dumping page to png
9666 // XXX Needs to be made more flexible
9667 if (!ok) {
9668 nsString stra;
9669 static int num = 0;
9670 stra.AppendLiteral("C:\\mozilla\\mozilla\\debug\\filea");
9671 stra.AppendInt(num);
9672 stra.AppendLiteral(".png");
9673 gfxUtils::WriteAsPNG(sh, stra);
9674 nsString strb;
9675 strb.AppendLiteral("C:\\mozilla\\mozilla\\debug\\fileb");
9676 strb.AppendInt(num);
9677 strb.AppendLiteral(".png");
9678 gfxUtils::WriteAsPNG(sh, strb);
9679 ++num;
9681 #endif
9683 sh->EndObservingDocument();
9684 sh->Destroy();
9685 if (VERIFY_REFLOW_NOISY & gVerifyReflowFlags) {
9686 printf("Finished Verifying Reflow...\n");
9689 return ok;
9692 // Layout debugging hooks
9693 void
9694 PresShell::ListStyleContexts(nsIFrame *aRootFrame, FILE *out, int32_t aIndent)
9696 nsStyleContext *sc = aRootFrame->StyleContext();
9697 if (sc)
9698 sc->List(out, aIndent);
9701 void
9702 PresShell::ListStyleSheets(FILE *out, int32_t aIndent)
9704 int32_t sheetCount = mStyleSet->SheetCount(nsStyleSet::eDocSheet);
9705 for (int32_t i = 0; i < sheetCount; ++i) {
9706 mStyleSet->StyleSheetAt(nsStyleSet::eDocSheet, i)->List(out, aIndent);
9707 fputs("\n", out);
9711 void
9712 PresShell::VerifyStyleTree()
9714 VERIFY_STYLE_TREE;
9716 #endif
9718 //=============================================================
9719 //=============================================================
9720 //-- Debug Reflow Counts
9721 //=============================================================
9722 //=============================================================
9723 #ifdef MOZ_REFLOW_PERF
9724 //-------------------------------------------------------------
9725 void
9726 PresShell::DumpReflows()
9728 if (mReflowCountMgr) {
9729 nsAutoCString uriStr;
9730 if (mDocument) {
9731 nsIURI *uri = mDocument->GetDocumentURI();
9732 if (uri) {
9733 uri->GetPath(uriStr);
9736 mReflowCountMgr->DisplayTotals(uriStr.get());
9737 mReflowCountMgr->DisplayHTMLTotals(uriStr.get());
9738 mReflowCountMgr->DisplayDiffsInTotals("Differences");
9742 //-------------------------------------------------------------
9743 void
9744 PresShell::CountReflows(const char * aName, nsIFrame * aFrame)
9746 if (mReflowCountMgr) {
9747 mReflowCountMgr->Add(aName, aFrame);
9751 //-------------------------------------------------------------
9752 void
9753 PresShell::PaintCount(const char * aName,
9754 nsRenderingContext* aRenderingContext,
9755 nsPresContext* aPresContext,
9756 nsIFrame * aFrame,
9757 const nsPoint& aOffset,
9758 uint32_t aColor)
9760 if (mReflowCountMgr) {
9761 mReflowCountMgr->PaintCount(aName, aRenderingContext, aPresContext,
9762 aFrame, aOffset, aColor);
9766 //-------------------------------------------------------------
9767 void
9768 PresShell::SetPaintFrameCount(bool aPaintFrameCounts)
9770 if (mReflowCountMgr) {
9771 mReflowCountMgr->SetPaintFrameCounts(aPaintFrameCounts);
9775 bool
9776 PresShell::IsPaintingFrameCounts()
9778 if (mReflowCountMgr)
9779 return mReflowCountMgr->IsPaintingFrameCounts();
9780 return false;
9783 //------------------------------------------------------------------
9784 //-- Reflow Counter Classes Impls
9785 //------------------------------------------------------------------
9787 //------------------------------------------------------------------
9788 ReflowCounter::ReflowCounter(ReflowCountMgr * aMgr) :
9789 mMgr(aMgr)
9791 ClearTotals();
9792 SetTotalsCache();
9795 //------------------------------------------------------------------
9796 ReflowCounter::~ReflowCounter()
9801 //------------------------------------------------------------------
9802 void ReflowCounter::ClearTotals()
9804 mTotal = 0;
9807 //------------------------------------------------------------------
9808 void ReflowCounter::SetTotalsCache()
9810 mCacheTotal = mTotal;
9813 //------------------------------------------------------------------
9814 void ReflowCounter::CalcDiffInTotals()
9816 mCacheTotal = mTotal - mCacheTotal;
9819 //------------------------------------------------------------------
9820 void ReflowCounter::DisplayTotals(const char * aStr)
9822 DisplayTotals(mTotal, aStr?aStr:"Totals");
9825 //------------------------------------------------------------------
9826 void ReflowCounter::DisplayDiffTotals(const char * aStr)
9828 DisplayTotals(mCacheTotal, aStr?aStr:"Diff Totals");
9831 //------------------------------------------------------------------
9832 void ReflowCounter::DisplayHTMLTotals(const char * aStr)
9834 DisplayHTMLTotals(mTotal, aStr?aStr:"Totals");
9837 //------------------------------------------------------------------
9838 void ReflowCounter::DisplayTotals(uint32_t aTotal, const char * aTitle)
9840 // figure total
9841 if (aTotal == 0) {
9842 return;
9844 ReflowCounter * gTots = (ReflowCounter *)mMgr->LookUp(kGrandTotalsStr);
9846 printf("%25s\t", aTitle);
9847 printf("%d\t", aTotal);
9848 if (gTots != this && aTotal > 0) {
9849 gTots->Add(aTotal);
9853 //------------------------------------------------------------------
9854 void ReflowCounter::DisplayHTMLTotals(uint32_t aTotal, const char * aTitle)
9856 if (aTotal == 0) {
9857 return;
9860 ReflowCounter * gTots = (ReflowCounter *)mMgr->LookUp(kGrandTotalsStr);
9861 FILE * fd = mMgr->GetOutFile();
9862 if (!fd) {
9863 return;
9866 fprintf(fd, "<tr><td><center>%s</center></td>", aTitle);
9867 fprintf(fd, "<td><center>%d</center></td></tr>\n", aTotal);
9869 if (gTots != this && aTotal > 0) {
9870 gTots->Add(aTotal);
9874 //------------------------------------------------------------------
9875 //-- ReflowCountMgr
9876 //------------------------------------------------------------------
9878 #define KEY_BUF_SIZE_FOR_PTR 24 // adequate char[] buffer to sprintf a pointer
9880 ReflowCountMgr::ReflowCountMgr()
9882 mCounts = PL_NewHashTable(10, PL_HashString, PL_CompareStrings,
9883 PL_CompareValues, nullptr, nullptr);
9884 mIndiFrameCounts = PL_NewHashTable(10, PL_HashString, PL_CompareStrings,
9885 PL_CompareValues, nullptr, nullptr);
9886 mCycledOnce = false;
9887 mDumpFrameCounts = false;
9888 mDumpFrameByFrameCounts = false;
9889 mPaintFrameByFrameCounts = false;
9892 //------------------------------------------------------------------
9893 ReflowCountMgr::~ReflowCountMgr()
9895 CleanUp();
9898 //------------------------------------------------------------------
9899 ReflowCounter * ReflowCountMgr::LookUp(const char * aName)
9901 if (nullptr != mCounts) {
9902 ReflowCounter * counter = (ReflowCounter *)PL_HashTableLookup(mCounts, aName);
9903 return counter;
9905 return nullptr;
9909 //------------------------------------------------------------------
9910 void ReflowCountMgr::Add(const char * aName, nsIFrame * aFrame)
9912 NS_ASSERTION(aName != nullptr, "Name shouldn't be null!");
9914 if (mDumpFrameCounts && nullptr != mCounts) {
9915 ReflowCounter * counter = (ReflowCounter *)PL_HashTableLookup(mCounts, aName);
9916 if (counter == nullptr) {
9917 counter = new ReflowCounter(this);
9918 char * name = NS_strdup(aName);
9919 NS_ASSERTION(name != nullptr, "null ptr");
9920 PL_HashTableAdd(mCounts, name, counter);
9922 counter->Add();
9925 if ((mDumpFrameByFrameCounts || mPaintFrameByFrameCounts) &&
9926 nullptr != mIndiFrameCounts &&
9927 aFrame != nullptr) {
9928 char key[KEY_BUF_SIZE_FOR_PTR];
9929 sprintf(key, "%p", (void*)aFrame);
9930 IndiReflowCounter * counter = (IndiReflowCounter *)PL_HashTableLookup(mIndiFrameCounts, key);
9931 if (counter == nullptr) {
9932 counter = new IndiReflowCounter(this);
9933 counter->mFrame = aFrame;
9934 counter->mName.AssignASCII(aName);
9935 PL_HashTableAdd(mIndiFrameCounts, NS_strdup(key), counter);
9937 // this eliminates extra counts from super classes
9938 if (counter != nullptr && counter->mName.EqualsASCII(aName)) {
9939 counter->mCount++;
9940 counter->mCounter.Add(1);
9945 //------------------------------------------------------------------
9946 void ReflowCountMgr::PaintCount(const char* aName,
9947 nsRenderingContext* aRenderingContext,
9948 nsPresContext* aPresContext,
9949 nsIFrame* aFrame,
9950 const nsPoint& aOffset,
9951 uint32_t aColor)
9953 if (mPaintFrameByFrameCounts &&
9954 nullptr != mIndiFrameCounts &&
9955 aFrame != nullptr) {
9956 char key[KEY_BUF_SIZE_FOR_PTR];
9957 sprintf(key, "%p", (void*)aFrame);
9958 IndiReflowCounter * counter =
9959 (IndiReflowCounter *)PL_HashTableLookup(mIndiFrameCounts, key);
9960 if (counter != nullptr && counter->mName.EqualsASCII(aName)) {
9961 aRenderingContext->PushState();
9962 aRenderingContext->Translate(aOffset);
9963 nsFont font(eFamily_serif, NS_FONT_STYLE_NORMAL,
9964 NS_FONT_WEIGHT_NORMAL, NS_FONT_STRETCH_NORMAL, 0,
9965 nsPresContext::CSSPixelsToAppUnits(11));
9967 nsRefPtr<nsFontMetrics> fm;
9968 aPresContext->DeviceContext()->GetMetricsFor(font,
9969 // We have one frame, therefore we must have a root...
9970 aPresContext->GetPresShell()->GetRootFrame()->
9971 StyleFont()->mLanguage,
9972 aPresContext->GetUserFontSet(),
9973 aPresContext->GetTextPerfMetrics(),
9974 *getter_AddRefs(fm));
9976 aRenderingContext->SetFont(fm);
9977 char buf[16];
9978 sprintf(buf, "%d", counter->mCount);
9979 nscoord x = 0, y = fm->MaxAscent();
9980 nscoord width, height = fm->MaxHeight();
9981 aRenderingContext->SetTextRunRTL(false);
9982 width = aRenderingContext->GetWidth(buf);
9984 uint32_t color;
9985 uint32_t color2;
9986 if (aColor != 0) {
9987 color = aColor;
9988 color2 = NS_RGB(0,0,0);
9989 } else {
9990 uint8_t rc = 0, gc = 0, bc = 0;
9991 if (counter->mCount < 5) {
9992 rc = 255;
9993 gc = 255;
9994 } else if ( counter->mCount < 11) {
9995 gc = 255;
9996 } else {
9997 rc = 255;
9999 color = NS_RGB(rc,gc,bc);
10000 color2 = NS_RGB(rc/2,gc/2,bc/2);
10003 nsRect rect(0,0, width+15, height+15);
10004 aRenderingContext->SetColor(NS_RGB(0,0,0));
10005 aRenderingContext->FillRect(rect);
10006 aRenderingContext->SetColor(color2);
10007 aRenderingContext->DrawString(buf, strlen(buf), x+15,y+15);
10008 aRenderingContext->SetColor(color);
10009 aRenderingContext->DrawString(buf, strlen(buf), x,y);
10011 aRenderingContext->PopState();
10016 //------------------------------------------------------------------
10017 int ReflowCountMgr::RemoveItems(PLHashEntry *he, int i, void *arg)
10019 char *str = (char *)he->key;
10020 ReflowCounter * counter = (ReflowCounter *)he->value;
10021 delete counter;
10022 NS_Free(str);
10024 return HT_ENUMERATE_REMOVE;
10027 //------------------------------------------------------------------
10028 int ReflowCountMgr::RemoveIndiItems(PLHashEntry *he, int i, void *arg)
10030 char *str = (char *)he->key;
10031 IndiReflowCounter * counter = (IndiReflowCounter *)he->value;
10032 delete counter;
10033 NS_Free(str);
10035 return HT_ENUMERATE_REMOVE;
10038 //------------------------------------------------------------------
10039 void ReflowCountMgr::CleanUp()
10041 if (nullptr != mCounts) {
10042 PL_HashTableEnumerateEntries(mCounts, RemoveItems, nullptr);
10043 PL_HashTableDestroy(mCounts);
10044 mCounts = nullptr;
10047 if (nullptr != mIndiFrameCounts) {
10048 PL_HashTableEnumerateEntries(mIndiFrameCounts, RemoveIndiItems, nullptr);
10049 PL_HashTableDestroy(mIndiFrameCounts);
10050 mIndiFrameCounts = nullptr;
10054 //------------------------------------------------------------------
10055 int ReflowCountMgr::DoSingleTotal(PLHashEntry *he, int i, void *arg)
10057 char *str = (char *)he->key;
10058 ReflowCounter * counter = (ReflowCounter *)he->value;
10060 counter->DisplayTotals(str);
10062 return HT_ENUMERATE_NEXT;
10065 //------------------------------------------------------------------
10066 void ReflowCountMgr::DoGrandTotals()
10068 if (nullptr != mCounts) {
10069 ReflowCounter * gTots = (ReflowCounter *)PL_HashTableLookup(mCounts, kGrandTotalsStr);
10070 if (gTots == nullptr) {
10071 gTots = new ReflowCounter(this);
10072 PL_HashTableAdd(mCounts, NS_strdup(kGrandTotalsStr), gTots);
10073 } else {
10074 gTots->ClearTotals();
10077 printf("\t\t\t\tTotal\n");
10078 for (uint32_t i=0;i<78;i++) {
10079 printf("-");
10081 printf("\n");
10082 PL_HashTableEnumerateEntries(mCounts, DoSingleTotal, this);
10086 static void RecurseIndiTotals(nsPresContext* aPresContext,
10087 PLHashTable * aHT,
10088 nsIFrame * aParentFrame,
10089 int32_t aLevel)
10091 if (aParentFrame == nullptr) {
10092 return;
10095 char key[KEY_BUF_SIZE_FOR_PTR];
10096 sprintf(key, "%p", (void*)aParentFrame);
10097 IndiReflowCounter * counter = (IndiReflowCounter *)PL_HashTableLookup(aHT, key);
10098 if (counter) {
10099 counter->mHasBeenOutput = true;
10100 char * name = ToNewCString(counter->mName);
10101 for (int32_t i=0;i<aLevel;i++) printf(" ");
10102 printf("%s - %p [%d][", name, (void*)aParentFrame, counter->mCount);
10103 printf("%d", counter->mCounter.GetTotal());
10104 printf("]\n");
10105 nsMemory::Free(name);
10108 nsIFrame* child = aParentFrame->GetFirstPrincipalChild();
10109 while (child) {
10110 RecurseIndiTotals(aPresContext, aHT, child, aLevel+1);
10111 child = child->GetNextSibling();
10116 //------------------------------------------------------------------
10117 int ReflowCountMgr::DoSingleIndi(PLHashEntry *he, int i, void *arg)
10119 IndiReflowCounter * counter = (IndiReflowCounter *)he->value;
10120 if (counter && !counter->mHasBeenOutput) {
10121 char * name = ToNewCString(counter->mName);
10122 printf("%s - %p [%d][", name, (void*)counter->mFrame, counter->mCount);
10123 printf("%d", counter->mCounter.GetTotal());
10124 printf("]\n");
10125 nsMemory::Free(name);
10127 return HT_ENUMERATE_NEXT;
10130 //------------------------------------------------------------------
10131 void ReflowCountMgr::DoIndiTotalsTree()
10133 if (nullptr != mCounts) {
10134 printf("\n------------------------------------------------\n");
10135 printf("-- Individual Frame Counts\n");
10136 printf("------------------------------------------------\n");
10138 if (mPresShell) {
10139 nsIFrame * rootFrame = mPresShell->FrameManager()->GetRootFrame();
10140 RecurseIndiTotals(mPresContext, mIndiFrameCounts, rootFrame, 0);
10141 printf("------------------------------------------------\n");
10142 printf("-- Individual Counts of Frames not in Root Tree\n");
10143 printf("------------------------------------------------\n");
10144 PL_HashTableEnumerateEntries(mIndiFrameCounts, DoSingleIndi, this);
10149 //------------------------------------------------------------------
10150 int ReflowCountMgr::DoSingleHTMLTotal(PLHashEntry *he, int i, void *arg)
10152 char *str = (char *)he->key;
10153 ReflowCounter * counter = (ReflowCounter *)he->value;
10155 counter->DisplayHTMLTotals(str);
10157 return HT_ENUMERATE_NEXT;
10160 //------------------------------------------------------------------
10161 void ReflowCountMgr::DoGrandHTMLTotals()
10163 if (nullptr != mCounts) {
10164 ReflowCounter * gTots = (ReflowCounter *)PL_HashTableLookup(mCounts, kGrandTotalsStr);
10165 if (gTots == nullptr) {
10166 gTots = new ReflowCounter(this);
10167 PL_HashTableAdd(mCounts, NS_strdup(kGrandTotalsStr), gTots);
10168 } else {
10169 gTots->ClearTotals();
10172 static const char * title[] = {"Class", "Reflows"};
10173 fprintf(mFD, "<tr>");
10174 for (uint32_t i=0; i < ArrayLength(title); i++) {
10175 fprintf(mFD, "<td><center><b>%s<b></center></td>", title[i]);
10177 fprintf(mFD, "</tr>\n");
10178 PL_HashTableEnumerateEntries(mCounts, DoSingleHTMLTotal, this);
10182 //------------------------------------
10183 void ReflowCountMgr::DisplayTotals(const char * aStr)
10185 #ifdef DEBUG_rods
10186 printf("%s\n", aStr?aStr:"No name");
10187 #endif
10188 if (mDumpFrameCounts) {
10189 DoGrandTotals();
10191 if (mDumpFrameByFrameCounts) {
10192 DoIndiTotalsTree();
10196 //------------------------------------
10197 void ReflowCountMgr::DisplayHTMLTotals(const char * aStr)
10199 #ifdef WIN32x // XXX NOT XP!
10200 char name[1024];
10202 char * sptr = strrchr(aStr, '/');
10203 if (sptr) {
10204 sptr++;
10205 strcpy(name, sptr);
10206 char * eptr = strrchr(name, '.');
10207 if (eptr) {
10208 *eptr = 0;
10210 strcat(name, "_stats.html");
10212 mFD = fopen(name, "w");
10213 if (mFD) {
10214 fprintf(mFD, "<html><head><title>Reflow Stats</title></head><body>\n");
10215 const char * title = aStr?aStr:"No name";
10216 fprintf(mFD, "<center><b>%s</b><br><table border=1 style=\"background-color:#e0e0e0\">", title);
10217 DoGrandHTMLTotals();
10218 fprintf(mFD, "</center></table>\n");
10219 fprintf(mFD, "</body></html>\n");
10220 fclose(mFD);
10221 mFD = nullptr;
10223 #endif // not XP!
10226 //------------------------------------------------------------------
10227 int ReflowCountMgr::DoClearTotals(PLHashEntry *he, int i, void *arg)
10229 ReflowCounter * counter = (ReflowCounter *)he->value;
10230 counter->ClearTotals();
10232 return HT_ENUMERATE_NEXT;
10235 //------------------------------------------------------------------
10236 void ReflowCountMgr::ClearTotals()
10238 PL_HashTableEnumerateEntries(mCounts, DoClearTotals, this);
10241 //------------------------------------------------------------------
10242 void ReflowCountMgr::ClearGrandTotals()
10244 if (nullptr != mCounts) {
10245 ReflowCounter * gTots = (ReflowCounter *)PL_HashTableLookup(mCounts, kGrandTotalsStr);
10246 if (gTots == nullptr) {
10247 gTots = new ReflowCounter(this);
10248 PL_HashTableAdd(mCounts, NS_strdup(kGrandTotalsStr), gTots);
10249 } else {
10250 gTots->ClearTotals();
10251 gTots->SetTotalsCache();
10256 //------------------------------------------------------------------
10257 int ReflowCountMgr::DoDisplayDiffTotals(PLHashEntry *he, int i, void *arg)
10259 bool cycledOnce = (arg != 0);
10261 char *str = (char *)he->key;
10262 ReflowCounter * counter = (ReflowCounter *)he->value;
10264 if (cycledOnce) {
10265 counter->CalcDiffInTotals();
10266 counter->DisplayDiffTotals(str);
10268 counter->SetTotalsCache();
10270 return HT_ENUMERATE_NEXT;
10273 //------------------------------------------------------------------
10274 void ReflowCountMgr::DisplayDiffsInTotals(const char * aStr)
10276 if (mCycledOnce) {
10277 printf("Differences\n");
10278 for (int32_t i=0;i<78;i++) {
10279 printf("-");
10281 printf("\n");
10282 ClearGrandTotals();
10284 PL_HashTableEnumerateEntries(mCounts, DoDisplayDiffTotals, (void *)mCycledOnce);
10286 mCycledOnce = true;
10289 #endif // MOZ_REFLOW_PERF
10291 // make a color string like #RRGGBB
10292 void ColorToString(nscolor aColor, nsAutoString &aString)
10294 char buf[8];
10296 PR_snprintf(buf, sizeof(buf), "#%02x%02x%02x",
10297 NS_GET_R(aColor), NS_GET_G(aColor), NS_GET_B(aColor));
10298 CopyASCIItoUTF16(buf, aString);
10301 nsIFrame* nsIPresShell::GetAbsoluteContainingBlock(nsIFrame *aFrame)
10303 return FrameConstructor()->GetAbsoluteContainingBlock(aFrame,
10304 nsCSSFrameConstructor::ABS_POS);
10307 #ifdef ACCESSIBILITY
10308 bool
10309 nsIPresShell::IsAccessibilityActive()
10311 return GetAccService() != nullptr;
10314 nsAccessibilityService*
10315 nsIPresShell::AccService()
10317 return GetAccService();
10319 #endif
10321 void nsIPresShell::InitializeStatics()
10323 NS_ASSERTION(!gCaptureTouchList, "InitializeStatics called multiple times!");
10324 gCaptureTouchList = new nsRefPtrHashtable<nsUint32HashKey, dom::Touch>;
10325 gPointerCaptureList = new nsRefPtrHashtable<nsUint32HashKey, nsIContent>;
10326 gActivePointersIds = new nsClassHashtable<nsUint32HashKey, PointerInfo>;
10329 void nsIPresShell::ReleaseStatics()
10331 NS_ASSERTION(gCaptureTouchList, "ReleaseStatics called without Initialize!");
10332 delete gCaptureTouchList;
10333 gCaptureTouchList = nullptr;
10334 delete gPointerCaptureList;
10335 gPointerCaptureList = nullptr;
10336 delete gActivePointersIds;
10337 gActivePointersIds = nullptr;
10340 // Asks our docshell whether we're active.
10341 void PresShell::QueryIsActive()
10343 nsCOMPtr<nsISupports> container = mPresContext->GetContainerWeak();
10344 if (mDocument) {
10345 nsIDocument* displayDoc = mDocument->GetDisplayDocument();
10346 if (displayDoc) {
10347 // Ok, we're an external resource document -- we need to use our display
10348 // document's docshell to determine "IsActive" status, since we lack
10349 // a container.
10350 NS_ABORT_IF_FALSE(!container,
10351 "external resource doc shouldn't have "
10352 "its own container");
10354 nsIPresShell* displayPresShell = displayDoc->GetShell();
10355 if (displayPresShell) {
10356 container = displayPresShell->GetPresContext()->GetContainerWeak();
10361 nsCOMPtr<nsIDocShell> docshell(do_QueryInterface(container));
10362 if (docshell) {
10363 bool isActive;
10364 nsresult rv = docshell->GetIsActive(&isActive);
10365 if (NS_SUCCEEDED(rv))
10366 SetIsActive(isActive);
10370 // Helper for propagating mIsActive changes to external resources
10371 static bool
10372 SetExternalResourceIsActive(nsIDocument* aDocument, void* aClosure)
10374 nsIPresShell* shell = aDocument->GetShell();
10375 if (shell) {
10376 shell->SetIsActive(*static_cast<bool*>(aClosure));
10378 return true;
10381 static void
10382 SetPluginIsActive(nsISupports* aSupports, void* aClosure)
10384 nsCOMPtr<nsIContent> content(do_QueryInterface(aSupports));
10385 if (!content) {
10386 return;
10389 nsIFrame *frame = content->GetPrimaryFrame();
10390 nsIObjectFrame *objectFrame = do_QueryFrame(frame);
10391 if (objectFrame) {
10392 objectFrame->SetIsDocumentActive(*static_cast<bool*>(aClosure));
10396 nsresult
10397 PresShell::SetIsActive(bool aIsActive)
10399 NS_PRECONDITION(mDocument, "should only be called with a document");
10401 mIsActive = aIsActive;
10402 nsPresContext* presContext = GetPresContext();
10403 if (presContext &&
10404 presContext->RefreshDriver()->PresContext() == presContext) {
10405 presContext->RefreshDriver()->SetThrottled(!mIsActive);
10408 // Propagate state-change to my resource documents' PresShells
10409 mDocument->EnumerateExternalResources(SetExternalResourceIsActive,
10410 &aIsActive);
10411 mDocument->EnumerateActivityObservers(SetPluginIsActive,
10412 &aIsActive);
10413 nsresult rv = UpdateImageLockingState();
10414 #ifdef ACCESSIBILITY
10415 if (aIsActive) {
10416 nsAccessibilityService* accService = AccService();
10417 if (accService) {
10418 accService->PresShellActivated(this);
10421 #endif
10423 // We have this odd special case here because remote content behaves
10424 // differently from same-process content when "hidden". In
10425 // desktop-type "browser UIs", hidden "tabs" have documents that are
10426 // part of the chrome tree. When the tabs are hidden, their content
10427 // is no longer part of the visible document tree, and the layers
10428 // for the content are naturally released.
10430 // Remote content is its own top-level tree in its subprocess. When
10431 // it's "hidden", there's no transaction in which the document
10432 // thinks it's not visible, so layers can be retained forever. This
10433 // is problematic when those layers uselessly hold on to precious
10434 // resources like directly texturable memory.
10436 // PresShell::SetIsActive() is the first C++ entry point at which we
10437 // (i) know that our parent process wants our content to be hidden;
10438 // and (ii) has easy access to the TabChild. So we use this
10439 // notification to signal the TabChild to drop its layer tree and
10440 // stop trying to repaint.
10441 if (TabChild* tab = TabChild::GetFrom(this)) {
10442 if (aIsActive) {
10443 tab->MakeVisible();
10444 if (!mIsZombie) {
10445 if (nsIFrame* root = mFrameConstructor->GetRootFrame()) {
10446 FrameLayerBuilder::InvalidateAllLayersForFrame(
10447 nsLayoutUtils::GetDisplayRootFrame(root));
10448 root->SchedulePaint();
10451 } else {
10452 tab->MakeHidden();
10456 return rv;
10460 * Determines the current image locking state. Called when one of the
10461 * dependent factors changes.
10463 nsresult
10464 PresShell::UpdateImageLockingState()
10466 // We're locked if we're both thawed and active.
10467 return mDocument->SetImageLockingState(!mFrozen && mIsActive);
10470 PresShell*
10471 PresShell::GetRootPresShell()
10473 if (mPresContext) {
10474 nsPresContext* rootPresContext = mPresContext->GetRootPresContext();
10475 if (rootPresContext) {
10476 return static_cast<PresShell*>(rootPresContext->PresShell());
10479 return nullptr;
10482 void
10483 PresShell::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
10484 nsArenaMemoryStats *aArenaObjectsSize,
10485 size_t *aPresShellSize,
10486 size_t *aStyleSetsSize,
10487 size_t *aTextRunsSize,
10488 size_t *aPresContextSize)
10490 mFrameArena.AddSizeOfExcludingThis(aMallocSizeOf, aArenaObjectsSize);
10491 *aPresShellSize += aMallocSizeOf(this);
10492 if (mCaret) {
10493 *aPresShellSize += mCaret->SizeOfIncludingThis(aMallocSizeOf);
10495 *aPresShellSize += mVisibleImages.SizeOfExcludingThis(nullptr,
10496 aMallocSizeOf,
10497 nullptr);
10498 *aPresShellSize += mFramesToDirty.SizeOfExcludingThis(nullptr,
10499 aMallocSizeOf,
10500 nullptr);
10501 *aPresShellSize += aArenaObjectsSize->mOther;
10503 *aStyleSetsSize += StyleSet()->SizeOfIncludingThis(aMallocSizeOf);
10505 *aTextRunsSize += SizeOfTextRuns(aMallocSizeOf);
10507 *aPresContextSize += mPresContext->SizeOfIncludingThis(aMallocSizeOf);
10510 size_t
10511 PresShell::SizeOfTextRuns(MallocSizeOf aMallocSizeOf) const
10513 nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
10514 if (!rootFrame) {
10515 return 0;
10518 // clear the TEXT_RUN_MEMORY_ACCOUNTED flags
10519 nsLayoutUtils::SizeOfTextRunsForFrames(rootFrame, nullptr,
10520 /* clear = */true);
10522 // collect the total memory in use for textruns
10523 return nsLayoutUtils::SizeOfTextRunsForFrames(rootFrame, aMallocSizeOf,
10524 /* clear = */false);
10527 void
10528 nsIPresShell::MarkFixedFramesForReflow(IntrinsicDirty aIntrinsicDirty)
10530 nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
10531 if (rootFrame) {
10532 const nsFrameList& childList = rootFrame->GetChildList(nsIFrame::kFixedList);
10533 for (nsFrameList::Enumerator e(childList); !e.AtEnd(); e.Next()) {
10534 FrameNeedsReflow(e.get(), aIntrinsicDirty, NS_FRAME_IS_DIRTY);
10539 void
10540 nsIPresShell::SetScrollPositionClampingScrollPortSize(nscoord aWidth, nscoord aHeight)
10542 if (!mScrollPositionClampingScrollPortSizeSet ||
10543 mScrollPositionClampingScrollPortSize.width != aWidth ||
10544 mScrollPositionClampingScrollPortSize.height != aHeight) {
10545 mScrollPositionClampingScrollPortSizeSet = true;
10546 mScrollPositionClampingScrollPortSize.width = aWidth;
10547 mScrollPositionClampingScrollPortSize.height = aHeight;
10549 if (nsIScrollableFrame* rootScrollFrame = GetRootScrollFrameAsScrollable()) {
10550 rootScrollFrame->MarkScrollbarsDirtyForReflow();
10552 MarkFixedFramesForReflow(eResize);
10556 void
10557 nsIPresShell::SetContentDocumentFixedPositionMargins(const nsMargin& aMargins)
10559 if (mContentDocumentFixedPositionMargins == aMargins) {
10560 return;
10563 mContentDocumentFixedPositionMargins = aMargins;
10565 MarkFixedFramesForReflow(eResize);
10568 void
10569 PresShell::SetupFontInflation()
10571 mFontSizeInflationEmPerLine = nsLayoutUtils::FontSizeInflationEmPerLine();
10572 mFontSizeInflationMinTwips = nsLayoutUtils::FontSizeInflationMinTwips();
10573 mFontSizeInflationLineThreshold = nsLayoutUtils::FontSizeInflationLineThreshold();
10574 mFontSizeInflationForceEnabled = nsLayoutUtils::FontSizeInflationForceEnabled();
10575 mFontSizeInflationDisabledInMasterProcess = nsLayoutUtils::FontSizeInflationDisabledInMasterProcess();
10577 NotifyFontSizeInflationEnabledIsDirty();
10580 void
10581 nsIPresShell::RecomputeFontSizeInflationEnabled()
10583 mFontSizeInflationEnabledIsDirty = false;
10585 MOZ_ASSERT(mPresContext, "our pres context should not be null");
10586 if ((FontSizeInflationEmPerLine() == 0 &&
10587 FontSizeInflationMinTwips() == 0) || mPresContext->IsChrome()) {
10588 mFontSizeInflationEnabled = false;
10589 return;
10592 // Force-enabling font inflation always trumps the heuristics here.
10593 if (!FontSizeInflationForceEnabled()) {
10594 if (TabChild* tab = TabChild::GetFrom(this)) {
10595 // We're in a child process. Cancel inflation if we're not
10596 // async-pan zoomed.
10597 if (!tab->IsAsyncPanZoomEnabled()) {
10598 mFontSizeInflationEnabled = false;
10599 return;
10601 } else if (XRE_GetProcessType() == GeckoProcessType_Default) {
10602 // We're in the master process. Cancel inflation if it's been
10603 // explicitly disabled.
10604 if (FontSizeInflationDisabledInMasterProcess()) {
10605 mFontSizeInflationEnabled = false;
10606 return;
10611 // XXXjwir3:
10612 // See bug 706918, comment 23 for more information on this particular section
10613 // of the code. We're using "screen size" in place of the size of the content
10614 // area, because on mobile, these are close or equal. This will work for our
10615 // purposes (bug 706198), but it will need to be changed in the future to be
10616 // more correct when we bring the rest of the viewport code into platform.
10617 // We actually want the size of the content area, in the event that we don't
10618 // have any metadata about the width and/or height. On mobile, the screen size
10619 // and the size of the content area are very close, or the same value.
10620 // In XUL fennec, the content area is the size of the <browser> widget, but
10621 // in native fennec, the content area is the size of the Gecko LayerView
10622 // object.
10624 // TODO:
10625 // Once bug 716575 has been resolved, this code should be changed so that it
10626 // does the right thing on all platforms.
10627 nsresult rv;
10628 nsCOMPtr<nsIScreenManager> screenMgr =
10629 do_GetService("@mozilla.org/gfx/screenmanager;1", &rv);
10630 if (!NS_SUCCEEDED(rv)) {
10631 mFontSizeInflationEnabled = false;
10632 return;
10635 nsCOMPtr<nsIScreen> screen;
10636 screenMgr->GetPrimaryScreen(getter_AddRefs(screen));
10637 if (screen) {
10638 int32_t screenLeft, screenTop, screenWidth, screenHeight;
10639 screen->GetRect(&screenLeft, &screenTop, &screenWidth, &screenHeight);
10641 nsViewportInfo vInf =
10642 nsContentUtils::GetViewportInfo(GetDocument(), ScreenIntSize(screenWidth, screenHeight));
10644 if (vInf.GetDefaultZoom() >= CSSToScreenScale(1.0f) || vInf.IsAutoSizeEnabled()) {
10645 mFontSizeInflationEnabled = false;
10646 return;
10650 mFontSizeInflationEnabled = true;
10653 bool
10654 nsIPresShell::FontSizeInflationEnabled()
10656 if (mFontSizeInflationEnabledIsDirty) {
10657 RecomputeFontSizeInflationEnabled();
10660 return mFontSizeInflationEnabled;
10663 void
10664 nsIPresShell::SetMaxLineBoxWidth(nscoord aMaxLineBoxWidth)
10666 NS_ASSERTION(aMaxLineBoxWidth >= 0, "attempting to set max line box width to a negative value");
10668 if (mMaxLineBoxWidth != aMaxLineBoxWidth) {
10669 mMaxLineBoxWidth = aMaxLineBoxWidth;
10670 mReflowOnZoomPending = true;
10671 FrameNeedsReflow(GetRootFrame(), eResize, NS_FRAME_HAS_DIRTY_CHILDREN);
10675 void
10676 PresShell::PausePainting()
10678 if (GetPresContext()->RefreshDriver()->PresContext() != GetPresContext())
10679 return;
10681 mPaintingIsFrozen = true;
10682 GetPresContext()->RefreshDriver()->Freeze();
10685 void
10686 PresShell::ResumePainting()
10688 if (GetPresContext()->RefreshDriver()->PresContext() != GetPresContext())
10689 return;
10691 mPaintingIsFrozen = false;
10692 GetPresContext()->RefreshDriver()->Thaw();