layout/base/nsPresShell.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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/.
     6  *
     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
    11  *
    12  * Modifications to Mozilla code or documentation
    13  * identified per MPL Section 3.3
    14  *
    15  * Date         Modified by     Description of modification
    16  * 05/03/2000   IBM Corp.       Observer events for reflow states
    17  */
    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/EventDispatcher.h"
    28 #include "mozilla/EventStateManager.h"
    29 #include "mozilla/EventStates.h"
    30 #include "mozilla/IMEStateManager.h"
    31 #include "mozilla/MemoryReporting.h"
    32 #include "mozilla/dom/TabChild.h"
    33 #include "mozilla/Likely.h"
    34 #include "mozilla/MouseEvents.h"
    35 #include "mozilla/TextEvents.h"
    36 #include "mozilla/TouchEvents.h"
    37 #include <algorithm>
    39 #ifdef XP_WIN
    40 #include "winuser.h"
    41 #endif
    43 #include "nsPresShell.h"
    44 #include "nsPresContext.h"
    45 #include "nsIContent.h"
    46 #include "mozilla/dom/Element.h"
    47 #include "mozilla/dom/Event.h" // for Event::GetEventPopupControlState()
    48 #include "mozilla/dom/ShadowRoot.h"
    49 #include "mozilla/dom/PointerEvent.h"
    50 #include "nsIDocument.h"
    51 #include "nsCSSStyleSheet.h"
    52 #include "nsAnimationManager.h"
    53 #include "nsNameSpaceManager.h"  // for Pref-related rule management (bugs 22963,20760,31816)
    54 #include "nsFrame.h"
    55 #include "FrameLayerBuilder.h"
    56 #include "nsViewManager.h"
    57 #include "nsView.h"
    58 #include "nsCRTGlue.h"
    59 #include "prprf.h"
    60 #include "prinrval.h"
    61 #include "nsTArray.h"
    62 #include "nsCOMArray.h"
    63 #include "nsContainerFrame.h"
    64 #include "nsISelection.h"
    65 #include "mozilla/dom/Selection.h"
    66 #include "nsGkAtoms.h"
    67 #include "nsIDOMRange.h"
    68 #include "nsIDOMDocument.h"
    69 #include "nsIDOMNode.h"
    70 #include "nsIDOMNodeList.h"
    71 #include "nsIDOMElement.h"
    72 #include "nsRange.h"
    73 #include "nsCOMPtr.h"
    74 #include "nsAutoPtr.h"
    75 #include "nsReadableUtils.h"
    76 #include "nsIPageSequenceFrame.h"
    77 #include "nsCaret.h"
    78 #include "nsIDOMHTMLDocument.h"
    79 #include "nsFrameManager.h"
    80 #include "nsXPCOM.h"
    81 #include "nsILayoutHistoryState.h"
    82 #include "nsILineIterator.h" // for ScrollContentIntoView
    83 #include "pldhash.h"
    84 #include "mozilla/dom/Touch.h"
    85 #include "mozilla/dom/PointerEventBinding.h"
    86 #include "nsIObserverService.h"
    87 #include "nsDocShell.h"        // for reflow observation
    88 #include "nsIBaseWindow.h"
    89 #include "nsError.h"
    90 #include "nsLayoutUtils.h"
    91 #include "nsViewportInfo.h"
    92 #include "nsCSSRendering.h"
    93   // for |#ifdef DEBUG| code
    94 #include "prenv.h"
    95 #include "nsDisplayList.h"
    96 #include "nsRegion.h"
    97 #include "nsRenderingContext.h"
    98 #include "nsAutoLayoutPhase.h"
    99 #ifdef MOZ_REFLOW_PERF
   100 #include "nsFontMetrics.h"
   101 #endif
   102 #include "PositionedEventTargeting.h"
   104 #include "nsIReflowCallback.h"
   106 #include "nsPIDOMWindow.h"
   107 #include "nsFocusManager.h"
   108 #include "nsIObjectFrame.h"
   109 #include "nsIObjectLoadingContent.h"
   110 #include "nsNetUtil.h"
   111 #include "nsThreadUtils.h"
   112 #include "nsStyleSheetService.h"
   113 #include "gfxImageSurface.h"
   114 #include "gfxContext.h"
   115 #include "nsSMILAnimationController.h"
   116 #include "SVGContentUtils.h"
   117 #include "nsSVGEffects.h"
   118 #include "SVGFragmentIdentifier.h"
   119 #include "nsArenaMemoryStats.h"
   121 #include "nsPerformance.h"
   122 #include "nsRefreshDriver.h"
   123 #include "nsDOMNavigationTiming.h"
   125 // Drag & Drop, Clipboard
   126 #include "nsIDocShellTreeItem.h"
   127 #include "nsIURI.h"
   128 #include "nsIScrollableFrame.h"
   129 #include "nsITimer.h"
   130 #ifdef ACCESSIBILITY
   131 #include "nsAccessibilityService.h"
   132 #include "mozilla/a11y/DocAccessible.h"
   133 #ifdef DEBUG
   134 #include "mozilla/a11y/Logging.h"
   135 #endif
   136 #endif
   138 // For style data reconstruction
   139 #include "nsStyleChangeList.h"
   140 #include "nsCSSFrameConstructor.h"
   141 #ifdef MOZ_XUL
   142 #include "nsMenuFrame.h"
   143 #include "nsTreeBodyFrame.h"
   144 #include "nsIBoxObject.h"
   145 #include "nsITreeBoxObject.h"
   146 #include "nsMenuPopupFrame.h"
   147 #include "nsITreeColumns.h"
   148 #include "nsIDOMXULMultSelectCntrlEl.h"
   149 #include "nsIDOMXULSelectCntrlItemEl.h"
   150 #include "nsIDOMXULMenuListElement.h"
   152 #endif
   154 #include "GeckoProfiler.h"
   155 #include "gfxPlatform.h"
   156 #include "Layers.h"
   157 #include "LayerTreeInvalidation.h"
   158 #include "mozilla/css/ImageLoader.h"
   159 #include "mozilla/Preferences.h"
   160 #include "mozilla/Telemetry.h"
   161 #include "nsCanvasFrame.h"
   162 #include "nsIImageLoadingContent.h"
   163 #include "nsIScreen.h"
   164 #include "nsIScreenManager.h"
   165 #include "nsPlaceholderFrame.h"
   166 #include "nsTransitionManager.h"
   167 #include "ChildIterator.h"
   168 #include "RestyleManager.h"
   169 #include "nsIDOMHTMLElement.h"
   170 #include "nsIDragSession.h"
   171 #include "nsIFrameInlines.h"
   172 #include "mozilla/gfx/2D.h"
   174 #ifdef ANDROID
   175 #include "nsIDocShellTreeOwner.h"
   176 #endif
   178 #ifdef MOZ_TASK_TRACER
   179 #include "GeckoTaskTracer.h"
   180 using namespace mozilla::tasktracer;
   181 #endif
   183 #define ANCHOR_SCROLL_FLAGS \
   184   (nsIPresShell::SCROLL_OVERFLOW_HIDDEN | nsIPresShell::SCROLL_NO_PARENT_FRAMES)
   186 using namespace mozilla;
   187 using namespace mozilla::css;
   188 using namespace mozilla::dom;
   189 using namespace mozilla::gfx;
   190 using namespace mozilla::layers;
   191 using namespace mozilla::gfx;
   193 CapturingContentInfo nsIPresShell::gCaptureInfo =
   194   { false /* mAllowed */, false /* mPointerLock */, false /* mRetargetToElement */,
   195     false /* mPreventDrag */, nullptr /* mContent */ };
   196 nsIContent* nsIPresShell::gKeyDownTarget;
   197 nsRefPtrHashtable<nsUint32HashKey, dom::Touch>* nsIPresShell::gCaptureTouchList;
   198 nsRefPtrHashtable<nsUint32HashKey, nsIContent>* nsIPresShell::gPointerCaptureList;
   199 nsClassHashtable<nsUint32HashKey, nsIPresShell::PointerInfo>* nsIPresShell::gActivePointersIds;
   200 bool nsIPresShell::gPreventMouseEvents = false;
   202 // convert a color value to a string, in the CSS format #RRGGBB
   203 // *  - initially created for bugs 31816, 20760, 22963
   204 static void ColorToString(nscolor aColor, nsAutoString &aString);
   206 // RangePaintInfo is used to paint ranges to offscreen buffers
   207 struct RangePaintInfo {
   208   nsRefPtr<nsRange> mRange;
   209   nsDisplayListBuilder mBuilder;
   210   nsDisplayList mList;
   212   // offset of builder's reference frame to the root frame
   213   nsPoint mRootOffset;
   215   RangePaintInfo(nsRange* aRange, nsIFrame* aFrame)
   216     : mRange(aRange), mBuilder(aFrame, nsDisplayListBuilder::PAINTING, false)
   217   {
   218     MOZ_COUNT_CTOR(RangePaintInfo);
   219   }
   221   ~RangePaintInfo()
   222   {
   223     mList.DeleteAll();
   224     MOZ_COUNT_DTOR(RangePaintInfo);
   225   }
   226 };
   228 #undef NOISY
   230 // ----------------------------------------------------------------------
   232 #ifdef DEBUG
   233 // Set the environment variable GECKO_VERIFY_REFLOW_FLAGS to one or
   234 // more of the following flags (comma separated) for handy debug
   235 // output.
   236 static uint32_t gVerifyReflowFlags;
   238 struct VerifyReflowFlags {
   239   const char*    name;
   240   uint32_t bit;
   241 };
   243 static const VerifyReflowFlags gFlags[] = {
   244   { "verify",                VERIFY_REFLOW_ON },
   245   { "reflow",                VERIFY_REFLOW_NOISY },
   246   { "all",                   VERIFY_REFLOW_ALL },
   247   { "list-commands",         VERIFY_REFLOW_DUMP_COMMANDS },
   248   { "noisy-commands",        VERIFY_REFLOW_NOISY_RC },
   249   { "really-noisy-commands", VERIFY_REFLOW_REALLY_NOISY_RC },
   250   { "resize",                VERIFY_REFLOW_DURING_RESIZE_REFLOW },
   251 };
   253 #define NUM_VERIFY_REFLOW_FLAGS (sizeof(gFlags) / sizeof(gFlags[0]))
   255 static void
   256 ShowVerifyReflowFlags()
   257 {
   258   printf("Here are the available GECKO_VERIFY_REFLOW_FLAGS:\n");
   259   const VerifyReflowFlags* flag = gFlags;
   260   const VerifyReflowFlags* limit = gFlags + NUM_VERIFY_REFLOW_FLAGS;
   261   while (flag < limit) {
   262     printf("  %s\n", flag->name);
   263     ++flag;
   264   }
   265   printf("Note: GECKO_VERIFY_REFLOW_FLAGS is a comma separated list of flag\n");
   266   printf("names (no whitespace)\n");
   267 }
   268 #endif
   270 //========================================================================
   271 //========================================================================
   272 //========================================================================
   273 #ifdef MOZ_REFLOW_PERF
   274 class ReflowCountMgr;
   276 static const char kGrandTotalsStr[] = "Grand Totals";
   278 // Counting Class
   279 class ReflowCounter {
   280 public:
   281   ReflowCounter(ReflowCountMgr * aMgr = nullptr);
   282   ~ReflowCounter();
   284   void ClearTotals();
   285   void DisplayTotals(const char * aStr);
   286   void DisplayDiffTotals(const char * aStr);
   287   void DisplayHTMLTotals(const char * aStr);
   289   void Add()                { mTotal++;         }
   290   void Add(uint32_t aTotal) { mTotal += aTotal; }
   292   void CalcDiffInTotals();
   293   void SetTotalsCache();
   295   void SetMgr(ReflowCountMgr * aMgr) { mMgr = aMgr; }
   297   uint32_t GetTotal() { return mTotal; }
   299 protected:
   300   void DisplayTotals(uint32_t aTotal, const char * aTitle);
   301   void DisplayHTMLTotals(uint32_t aTotal, const char * aTitle);
   303   uint32_t mTotal;
   304   uint32_t mCacheTotal;
   306   ReflowCountMgr * mMgr; // weak reference (don't delete)
   307 };
   309 // Counting Class
   310 class IndiReflowCounter {
   311 public:
   312   IndiReflowCounter(ReflowCountMgr * aMgr = nullptr)
   313     : mFrame(nullptr),
   314       mCount(0),
   315       mMgr(aMgr),
   316       mCounter(aMgr),
   317       mHasBeenOutput(false)
   318     {}
   319   virtual ~IndiReflowCounter() {}
   321   nsAutoString mName;
   322   nsIFrame *   mFrame;   // weak reference (don't delete)
   323   int32_t      mCount;
   325   ReflowCountMgr * mMgr; // weak reference (don't delete)
   327   ReflowCounter mCounter;
   328   bool          mHasBeenOutput;
   330 };
   332 //--------------------
   333 // Manager Class
   334 //--------------------
   335 class ReflowCountMgr {
   336 public:
   337   ReflowCountMgr();
   338   virtual ~ReflowCountMgr();
   340   void ClearTotals();
   341   void ClearGrandTotals();
   342   void DisplayTotals(const char * aStr);
   343   void DisplayHTMLTotals(const char * aStr);
   344   void DisplayDiffsInTotals(const char * aStr);
   346   void Add(const char * aName, nsIFrame * aFrame);
   347   ReflowCounter * LookUp(const char * aName);
   349   void PaintCount(const char *aName, nsRenderingContext* aRenderingContext,
   350                   nsPresContext *aPresContext, nsIFrame *aFrame,
   351                   const nsPoint &aOffset, uint32_t aColor);
   353   FILE * GetOutFile() { return mFD; }
   355   PLHashTable * GetIndiFrameHT() { return mIndiFrameCounts; }
   357   void SetPresContext(nsPresContext * aPresContext) { mPresContext = aPresContext; } // weak reference
   358   void SetPresShell(nsIPresShell* aPresShell) { mPresShell= aPresShell; } // weak reference
   360   void SetDumpFrameCounts(bool aVal)         { mDumpFrameCounts = aVal; }
   361   void SetDumpFrameByFrameCounts(bool aVal)  { mDumpFrameByFrameCounts = aVal; }
   362   void SetPaintFrameCounts(bool aVal)        { mPaintFrameByFrameCounts = aVal; }
   364   bool IsPaintingFrameCounts() { return mPaintFrameByFrameCounts; }
   366 protected:
   367   void DisplayTotals(uint32_t aTotal, uint32_t * aDupArray, char * aTitle);
   368   void DisplayHTMLTotals(uint32_t aTotal, uint32_t * aDupArray, char * aTitle);
   370   static int RemoveItems(PLHashEntry *he, int i, void *arg);
   371   static int RemoveIndiItems(PLHashEntry *he, int i, void *arg);
   372   void CleanUp();
   374   // stdout Output Methods
   375   static int DoSingleTotal(PLHashEntry *he, int i, void *arg);
   376   static int DoSingleIndi(PLHashEntry *he, int i, void *arg);
   378   void DoGrandTotals();
   379   void DoIndiTotalsTree();
   381   // HTML Output Methods
   382   static int DoSingleHTMLTotal(PLHashEntry *he, int i, void *arg);
   383   void DoGrandHTMLTotals();
   385   // Zero Out the Totals
   386   static int DoClearTotals(PLHashEntry *he, int i, void *arg);
   388   // Displays the Diff Totals
   389   static int DoDisplayDiffTotals(PLHashEntry *he, int i, void *arg);
   391   PLHashTable * mCounts;
   392   PLHashTable * mIndiFrameCounts;
   393   FILE * mFD;
   395   bool mDumpFrameCounts;
   396   bool mDumpFrameByFrameCounts;
   397   bool mPaintFrameByFrameCounts;
   399   bool mCycledOnce;
   401   // Root Frame for Individual Tracking
   402   nsPresContext * mPresContext;
   403   nsIPresShell*    mPresShell;
   405   // ReflowCountMgr gReflowCountMgr;
   406 };
   407 #endif
   408 //========================================================================
   410 // comment out to hide caret
   411 #define SHOW_CARET
   413 // The upper bound on the amount of time to spend reflowing, in
   414 // microseconds.  When this bound is exceeded and reflow commands are
   415 // still queued up, a reflow event is posted.  The idea is for reflow
   416 // to not hog the processor beyond the time specifed in
   417 // gMaxRCProcessingTime.  This data member is initialized from the
   418 // layout.reflow.timeslice pref.
   419 #define NS_MAX_REFLOW_TIME    1000000
   420 static int32_t gMaxRCProcessingTime = -1;
   422 struct nsCallbackEventRequest
   423 {
   424   nsIReflowCallback* callback;
   425   nsCallbackEventRequest* next;
   426 };
   428 // ----------------------------------------------------------------------------
   429 #define ASSERT_REFLOW_SCHEDULED_STATE()                                       \
   430   NS_ASSERTION(mReflowScheduled ==                                            \
   431                  GetPresContext()->RefreshDriver()->                          \
   432                    IsLayoutFlushObserver(this), "Unexpected state")
   434 class nsAutoCauseReflowNotifier
   435 {
   436 public:
   437   nsAutoCauseReflowNotifier(PresShell* aShell)
   438     : mShell(aShell)
   439   {
   440     mShell->WillCauseReflow();
   441   }
   442   ~nsAutoCauseReflowNotifier()
   443   {
   444     // This check should not be needed. Currently the only place that seem
   445     // to need it is the code that deals with bug 337586.
   446     if (!mShell->mHaveShutDown) {
   447       mShell->DidCauseReflow();
   448     }
   449     else {
   450       nsContentUtils::RemoveScriptBlocker();
   451     }
   452   }
   454   PresShell* mShell;
   455 };
   457 class MOZ_STACK_CLASS nsPresShellEventCB : public EventDispatchingCallback
   458 {
   459 public:
   460   nsPresShellEventCB(PresShell* aPresShell) : mPresShell(aPresShell) {}
   462   virtual void HandleEvent(EventChainPostVisitor& aVisitor) MOZ_OVERRIDE
   463   {
   464     if (aVisitor.mPresContext && aVisitor.mEvent->eventStructType != NS_EVENT) {
   465       if (aVisitor.mEvent->message == NS_MOUSE_BUTTON_DOWN ||
   466           aVisitor.mEvent->message == NS_MOUSE_BUTTON_UP) {
   467         // Mouse-up and mouse-down events call nsFrame::HandlePress/Release
   468         // which call GetContentOffsetsFromPoint which requires up-to-date layout.
   469         // Bring layout up-to-date now so that GetCurrentEventFrame() below
   470         // will return a real frame and we don't have to worry about
   471         // destroying it by flushing later.
   472         mPresShell->FlushPendingNotifications(Flush_Layout);
   473       } else if (aVisitor.mEvent->message == NS_WHEEL_WHEEL &&
   474                  aVisitor.mEventStatus != nsEventStatus_eConsumeNoDefault) {
   475         nsIFrame* frame = mPresShell->GetCurrentEventFrame();
   476         if (frame) {
   477           // chrome (including addons) should be able to know if content
   478           // handles both D3E "wheel" event and legacy mouse scroll events.
   479           // We should dispatch legacy mouse events before dispatching the
   480           // "wheel" event into system group.
   481           nsRefPtr<EventStateManager> esm =
   482             aVisitor.mPresContext->EventStateManager();
   483           esm->DispatchLegacyMouseScrollEvents(frame,
   484                                                aVisitor.mEvent->AsWheelEvent(),
   485                                                &aVisitor.mEventStatus);
   486         }
   487       }
   488       nsIFrame* frame = mPresShell->GetCurrentEventFrame();
   489       if (!frame &&
   490           (aVisitor.mEvent->message == NS_MOUSE_BUTTON_UP ||
   491            aVisitor.mEvent->message == NS_TOUCH_END)) {
   492         // Redirect BUTTON_UP and TOUCH_END events to the root frame to ensure
   493         // that capturing is released.
   494         frame = mPresShell->GetRootFrame();
   495       }
   496       if (frame) {
   497         frame->HandleEvent(aVisitor.mPresContext,
   498                            aVisitor.mEvent->AsGUIEvent(),
   499                            &aVisitor.mEventStatus);
   500       }
   501     }
   502   }
   504   nsRefPtr<PresShell> mPresShell;
   505 };
   507 class nsBeforeFirstPaintDispatcher : public nsRunnable
   508 {
   509 public:
   510   nsBeforeFirstPaintDispatcher(nsIDocument* aDocument)
   511   : mDocument(aDocument) {}
   513   // Fires the "before-first-paint" event so that interested parties (right now, the
   514   // mobile browser) are aware of it.
   515   NS_IMETHOD Run() MOZ_OVERRIDE
   516   {
   517     nsCOMPtr<nsIObserverService> observerService =
   518       mozilla::services::GetObserverService();
   519     if (observerService) {
   520       observerService->NotifyObservers(mDocument, "before-first-paint",
   521                                        nullptr);
   522     }
   523     return NS_OK;
   524   }
   526 private:
   527   nsCOMPtr<nsIDocument> mDocument;
   528 };
   530 bool PresShell::sDisableNonTestMouseEvents = false;
   532 #ifdef PR_LOGGING
   533 PRLogModuleInfo* PresShell::gLog;
   534 #endif
   536 #ifdef DEBUG
   537 static void
   538 VerifyStyleTree(nsPresContext* aPresContext, nsFrameManager* aFrameManager)
   539 {
   540   if (nsFrame::GetVerifyStyleTreeEnable()) {
   541     nsIFrame* rootFrame = aFrameManager->GetRootFrame();
   542     aPresContext->RestyleManager()->DebugVerifyStyleTree(rootFrame);
   543   }
   544 }
   545 #define VERIFY_STYLE_TREE ::VerifyStyleTree(mPresContext, mFrameConstructor)
   546 #else
   547 #define VERIFY_STYLE_TREE
   548 #endif
   550 static bool gVerifyReflowEnabled;
   552 bool
   553 nsIPresShell::GetVerifyReflowEnable()
   554 {
   555 #ifdef DEBUG
   556   static bool firstTime = true;
   557   if (firstTime) {
   558     firstTime = false;
   559     char* flags = PR_GetEnv("GECKO_VERIFY_REFLOW_FLAGS");
   560     if (flags) {
   561       bool error = false;
   563       for (;;) {
   564         char* comma = PL_strchr(flags, ',');
   565         if (comma)
   566           *comma = '\0';
   568         bool found = false;
   569         const VerifyReflowFlags* flag = gFlags;
   570         const VerifyReflowFlags* limit = gFlags + NUM_VERIFY_REFLOW_FLAGS;
   571         while (flag < limit) {
   572           if (PL_strcasecmp(flag->name, flags) == 0) {
   573             gVerifyReflowFlags |= flag->bit;
   574             found = true;
   575             break;
   576           }
   577           ++flag;
   578         }
   580         if (! found)
   581           error = true;
   583         if (! comma)
   584           break;
   586         *comma = ',';
   587         flags = comma + 1;
   588       }
   590       if (error)
   591         ShowVerifyReflowFlags();
   592     }
   594     if (VERIFY_REFLOW_ON & gVerifyReflowFlags) {
   595       gVerifyReflowEnabled = true;
   597       printf("Note: verifyreflow is enabled");
   598       if (VERIFY_REFLOW_NOISY & gVerifyReflowFlags) {
   599         printf(" (noisy)");
   600       }
   601       if (VERIFY_REFLOW_ALL & gVerifyReflowFlags) {
   602         printf(" (all)");
   603       }
   604       if (VERIFY_REFLOW_DUMP_COMMANDS & gVerifyReflowFlags) {
   605         printf(" (show reflow commands)");
   606       }
   607       if (VERIFY_REFLOW_NOISY_RC & gVerifyReflowFlags) {
   608         printf(" (noisy reflow commands)");
   609         if (VERIFY_REFLOW_REALLY_NOISY_RC & gVerifyReflowFlags) {
   610           printf(" (REALLY noisy reflow commands)");
   611         }
   612       }
   613       printf("\n");
   614     }
   615   }
   616 #endif
   617   return gVerifyReflowEnabled;
   618 }
   620 void
   621 PresShell::AddInvalidateHiddenPresShellObserver(nsRefreshDriver *aDriver)
   622 {
   623   if (!mHiddenInvalidationObserverRefreshDriver && !mIsDestroying && !mHaveShutDown) {
   624     aDriver->AddPresShellToInvalidateIfHidden(this);
   625     mHiddenInvalidationObserverRefreshDriver = aDriver;
   626   }
   627 }
   629 void
   630 nsIPresShell::InvalidatePresShellIfHidden()
   631 {
   632   if (!IsVisible() && mPresContext) {
   633     mPresContext->NotifyInvalidation(0);
   634   }
   635   mHiddenInvalidationObserverRefreshDriver = nullptr;
   636 }
   638 void
   639 nsIPresShell::CancelInvalidatePresShellIfHidden()
   640 {
   641   if (mHiddenInvalidationObserverRefreshDriver) {
   642     mHiddenInvalidationObserverRefreshDriver->RemovePresShellToInvalidateIfHidden(this);
   643     mHiddenInvalidationObserverRefreshDriver = nullptr;
   644   }
   645 }
   647 void
   648 nsIPresShell::SetVerifyReflowEnable(bool aEnabled)
   649 {
   650   gVerifyReflowEnabled = aEnabled;
   651 }
   653 /* virtual */ void
   654 nsIPresShell::AddWeakFrameExternal(nsWeakFrame* aWeakFrame)
   655 {
   656   AddWeakFrameInternal(aWeakFrame);
   657 }
   659 void
   660 nsIPresShell::AddWeakFrameInternal(nsWeakFrame* aWeakFrame)
   661 {
   662   if (aWeakFrame->GetFrame()) {
   663     aWeakFrame->GetFrame()->AddStateBits(NS_FRAME_EXTERNAL_REFERENCE);
   664   }
   665   aWeakFrame->SetPreviousWeakFrame(mWeakFrames);
   666   mWeakFrames = aWeakFrame;
   667 }
   669 /* virtual */ void
   670 nsIPresShell::RemoveWeakFrameExternal(nsWeakFrame* aWeakFrame)
   671 {
   672   RemoveWeakFrameInternal(aWeakFrame);
   673 }
   675 void
   676 nsIPresShell::RemoveWeakFrameInternal(nsWeakFrame* aWeakFrame)
   677 {
   678   if (mWeakFrames == aWeakFrame) {
   679     mWeakFrames = aWeakFrame->GetPreviousWeakFrame();
   680     return;
   681   }
   682   nsWeakFrame* nextWeak = mWeakFrames;
   683   while (nextWeak && nextWeak->GetPreviousWeakFrame() != aWeakFrame) {
   684     nextWeak = nextWeak->GetPreviousWeakFrame();
   685   }
   686   if (nextWeak) {
   687     nextWeak->SetPreviousWeakFrame(aWeakFrame->GetPreviousWeakFrame());
   688   }
   689 }
   691 already_AddRefed<nsFrameSelection>
   692 nsIPresShell::FrameSelection()
   693 {
   694   nsRefPtr<nsFrameSelection> ret = mSelection;
   695   return ret.forget();
   696 }
   698 //----------------------------------------------------------------------
   700 static bool sSynthMouseMove = true;
   701 static uint32_t sNextPresShellId;
   702 static bool sPointerEventEnabled = true;
   704 PresShell::PresShell()
   705   : mMouseLocation(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE)
   706 {
   707   mSelection = nullptr;
   708 #ifdef MOZ_REFLOW_PERF
   709   mReflowCountMgr = new ReflowCountMgr();
   710   mReflowCountMgr->SetPresContext(mPresContext);
   711   mReflowCountMgr->SetPresShell(this);
   712 #endif
   713 #ifdef PR_LOGGING
   714   mLoadBegin = TimeStamp::Now();
   715   if (!gLog) {
   716     gLog = PR_NewLogModule("PresShell");
   717   }
   718 #endif
   719   mSelectionFlags = nsISelectionDisplay::DISPLAY_TEXT | nsISelectionDisplay::DISPLAY_IMAGES;
   720   mIsThemeSupportDisabled = false;
   721   mIsActive = true;
   722   // FIXME/bug 735029: find a better solution to this problem
   723 #ifdef MOZ_ANDROID_OMTC
   724   // The java pan/zoom code uses this to mean approximately "request a
   725   // reset of pan/zoom state" which doesn't necessarily correspond
   726   // with the first paint of content.
   727   mIsFirstPaint = false;
   728 #else
   729   mIsFirstPaint = true;
   730 #endif
   731   mPresShellId = sNextPresShellId++;
   732   mFrozen = false;
   733 #ifdef DEBUG
   734   mPresArenaAllocCount = 0;
   735 #endif
   736   mRenderFlags = 0;
   737   mXResolution = 1.0;
   738   mYResolution = 1.0;
   739   mViewportOverridden = false;
   741   mScrollPositionClampingScrollPortSizeSet = false;
   743   mMaxLineBoxWidth = 0;
   745   static bool addedSynthMouseMove = false;
   746   if (!addedSynthMouseMove) {
   747     Preferences::AddBoolVarCache(&sSynthMouseMove,
   748                                  "layout.reflow.synthMouseMove", true);
   749     addedSynthMouseMove = true;
   750   }
   751   static bool addedPointerEventEnabled = false;
   752   if (!addedPointerEventEnabled) {
   753     Preferences::AddBoolVarCache(&sPointerEventEnabled,
   754                                  "dom.w3c_pointer_events.enabled", true);
   755     addedPointerEventEnabled = true;
   756   }
   758   mPaintingIsFrozen = false;
   759 }
   761 NS_IMPL_ISUPPORTS(PresShell, nsIPresShell, nsIDocumentObserver,
   762                   nsISelectionController,
   763                   nsISelectionDisplay, nsIObserver, nsISupportsWeakReference,
   764                   nsIMutationObserver)
   766 PresShell::~PresShell()
   767 {
   768   if (!mHaveShutDown) {
   769     NS_NOTREACHED("Someone did not call nsIPresShell::destroy");
   770     Destroy();
   771   }
   773   NS_ASSERTION(mCurrentEventContentStack.Count() == 0,
   774                "Huh, event content left on the stack in pres shell dtor!");
   775   NS_ASSERTION(mFirstCallbackEventRequest == nullptr &&
   776                mLastCallbackEventRequest == nullptr,
   777                "post-reflow queues not empty.  This means we're leaking");
   779   // Verify that if painting was frozen, but we're being removed from the tree,
   780   // that we now re-enable painting on our refresh driver, since it may need to
   781   // be re-used by another presentation.
   782   if (mPaintingIsFrozen) {
   783     mPresContext->RefreshDriver()->Thaw();
   784   }
   786 #ifdef DEBUG
   787   MOZ_ASSERT(mPresArenaAllocCount == 0,
   788              "Some pres arena objects were not freed");
   789 #endif
   791   delete mStyleSet;
   792   delete mFrameConstructor;
   794   mCurrentEventContent = nullptr;
   796   NS_IF_RELEASE(mPresContext);
   797   NS_IF_RELEASE(mDocument);
   798   NS_IF_RELEASE(mSelection);
   799 }
   801 /**
   802  * Initialize the presentation shell. Create view manager and style
   803  * manager.
   804  * Note this can't be merged into our constructor because caret initialization
   805  * calls AddRef() on us.
   806  */
   807 void
   808 PresShell::Init(nsIDocument* aDocument,
   809                 nsPresContext* aPresContext,
   810                 nsViewManager* aViewManager,
   811                 nsStyleSet* aStyleSet,
   812                 nsCompatibility aCompatMode)
   813 {
   814   NS_PRECONDITION(aDocument, "null ptr");
   815   NS_PRECONDITION(aPresContext, "null ptr");
   816   NS_PRECONDITION(aViewManager, "null ptr");
   817   NS_PRECONDITION(!mDocument, "already initialized");
   819   if (!aDocument || !aPresContext || !aViewManager || mDocument) {
   820     return;
   821   }
   823   mDocument = aDocument;
   824   NS_ADDREF(mDocument);
   825   mViewManager = aViewManager;
   827   // Create our frame constructor.
   828   mFrameConstructor = new nsCSSFrameConstructor(mDocument, this, aStyleSet);
   830   mFrameManager = mFrameConstructor;
   832   // The document viewer owns both view manager and pres shell.
   833   mViewManager->SetPresShell(this);
   835   // Bind the context to the presentation shell.
   836   mPresContext = aPresContext;
   837   NS_ADDREF(mPresContext);
   838   aPresContext->SetShell(this);
   840   // Now we can initialize the style set.
   841   aStyleSet->Init(aPresContext);
   842   mStyleSet = aStyleSet;
   844   // Notify our prescontext that it now has a compatibility mode.  Note that
   845   // this MUST happen after we set up our style set but before we create any
   846   // frames.
   847   mPresContext->CompatibilityModeChanged();
   849   // setup the preference style rules (no forced reflow), and do it
   850   // before creating any frames.
   851   SetPreferenceStyleRules(false);
   853   NS_ADDREF(mSelection = new nsFrameSelection());
   855   mSelection->Init(this, nullptr);
   857   // Important: this has to happen after the selection has been set up
   858 #ifdef SHOW_CARET
   859   // make the caret
   860   mCaret = new nsCaret();
   861   mCaret->Init(this);
   862   mOriginalCaret = mCaret;
   864   //SetCaretEnabled(true);       // make it show in browser windows
   865 #endif
   866   //set up selection to be displayed in document
   867   // Don't enable selection for print media
   868   nsPresContext::nsPresContextType type = aPresContext->Type();
   869   if (type != nsPresContext::eContext_PrintPreview &&
   870       type != nsPresContext::eContext_Print)
   871     SetDisplaySelection(nsISelectionController::SELECTION_DISABLED);
   873   if (gMaxRCProcessingTime == -1) {
   874     gMaxRCProcessingTime =
   875       Preferences::GetInt("layout.reflow.timeslice", NS_MAX_REFLOW_TIME);
   876   }
   878   {
   879     nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   880     if (os) {
   881       os->AddObserver(this, "agent-sheet-added", false);
   882       os->AddObserver(this, "user-sheet-added", false);
   883       os->AddObserver(this, "author-sheet-added", false);
   884       os->AddObserver(this, "agent-sheet-removed", false);
   885       os->AddObserver(this, "user-sheet-removed", false);
   886       os->AddObserver(this, "author-sheet-removed", false);
   887 #ifdef MOZ_XUL
   888       os->AddObserver(this, "chrome-flush-skin-caches", false);
   889 #endif
   890     }
   891   }
   893 #ifdef MOZ_REFLOW_PERF
   894     if (mReflowCountMgr) {
   895       bool paintFrameCounts =
   896         Preferences::GetBool("layout.reflow.showframecounts");
   898       bool dumpFrameCounts =
   899         Preferences::GetBool("layout.reflow.dumpframecounts");
   901       bool dumpFrameByFrameCounts =
   902         Preferences::GetBool("layout.reflow.dumpframebyframecounts");
   904       mReflowCountMgr->SetDumpFrameCounts(dumpFrameCounts);
   905       mReflowCountMgr->SetDumpFrameByFrameCounts(dumpFrameByFrameCounts);
   906       mReflowCountMgr->SetPaintFrameCounts(paintFrameCounts);
   907     }
   908 #endif
   910   if (mDocument->HasAnimationController()) {
   911     nsSMILAnimationController* animCtrl = mDocument->GetAnimationController();
   912     animCtrl->NotifyRefreshDriverCreated(GetPresContext()->RefreshDriver());
   913   }
   915   // Get our activeness from the docShell.
   916   QueryIsActive();
   918   // Setup our font inflation preferences.
   919   SetupFontInflation();
   920 }
   922 #ifdef PR_LOGGING
   923 enum TextPerfLogType {
   924   eLog_reflow,
   925   eLog_loaddone,
   926   eLog_totals
   927 };
   929 static void
   930 LogTextPerfStats(gfxTextPerfMetrics* aTextPerf,
   931                  PresShell* aPresShell,
   932                  const gfxTextPerfMetrics::TextCounts& aCounts,
   933                  float aTime, TextPerfLogType aLogType, const char* aURL)
   934 {
   935   char prefix[256];
   937   switch (aLogType) {
   938     case eLog_reflow:
   939       sprintf(prefix, "(textperf-reflow) %p time-ms: %7.0f", aPresShell, aTime);
   940       break;
   941     case eLog_loaddone:
   942       sprintf(prefix, "(textperf-loaddone) %p time-ms: %7.0f", aPresShell, aTime);
   943       break;
   944     default:
   945       MOZ_ASSERT(aLogType == eLog_totals, "unknown textperf log type");
   946       sprintf(prefix, "(textperf-totals) %p", aPresShell);
   947   }
   949   PRLogModuleInfo* tpLog = gfxPlatform::GetLog(eGfxLog_textperf);
   951   // ignore XUL contexts unless at debug level
   952   PRLogModuleLevel logLevel = PR_LOG_WARNING;
   953   if (aCounts.numContentTextRuns == 0) {
   954     logLevel = PR_LOG_DEBUG;
   955   }
   957   double hitRatio = 0.0;
   958   uint32_t lookups = aCounts.wordCacheHit + aCounts.wordCacheMiss;
   959   if (lookups) {
   960     hitRatio = double(aCounts.wordCacheHit) / double(lookups);
   961   }
   963   if (aLogType == eLog_loaddone) {
   964     PR_LOG(tpLog, logLevel,
   965            ("%s reflow: %d chars: %d "
   966             "[%s] "
   967             "content-textruns: %d chrome-textruns: %d "
   968             "max-textrun-len: %d "
   969             "word-cache-lookups: %d word-cache-hit-ratio: %4.3f "
   970             "word-cache-space: %d word-cache-long: %d "
   971             "pref-fallbacks: %d system-fallbacks: %d "
   972             "textruns-const: %d textruns-destr: %d "
   973             "cumulative-textruns-destr: %d\n",
   974             prefix, aTextPerf->reflowCount, aCounts.numChars,
   975             (aURL ? aURL : ""),
   976             aCounts.numContentTextRuns, aCounts.numChromeTextRuns,
   977             aCounts.maxTextRunLen,
   978             lookups, hitRatio,
   979             aCounts.wordCacheSpaceRules, aCounts.wordCacheLong,
   980             aCounts.fallbackPrefs, aCounts.fallbackSystem,
   981             aCounts.textrunConst, aCounts.textrunDestr,
   982             aTextPerf->cumulative.textrunDestr));
   983   } else {
   984     PR_LOG(tpLog, logLevel,
   985            ("%s reflow: %d chars: %d "
   986             "content-textruns: %d chrome-textruns: %d "
   987             "max-textrun-len: %d "
   988             "word-cache-lookups: %d word-cache-hit-ratio: %4.3f "
   989             "word-cache-space: %d word-cache-long: %d "
   990             "pref-fallbacks: %d system-fallbacks: %d "
   991             "textruns-const: %d textruns-destr: %d "
   992             "cumulative-textruns-destr: %d\n",
   993             prefix, aTextPerf->reflowCount, aCounts.numChars,
   994             aCounts.numContentTextRuns, aCounts.numChromeTextRuns,
   995             aCounts.maxTextRunLen,
   996             lookups, hitRatio,
   997             aCounts.wordCacheSpaceRules, aCounts.wordCacheLong,
   998             aCounts.fallbackPrefs, aCounts.fallbackSystem,
   999             aCounts.textrunConst, aCounts.textrunDestr,
  1000             aTextPerf->cumulative.textrunDestr));
  1003 #endif
  1005 void
  1006 PresShell::Destroy()
  1008   NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
  1009     "destroy called on presshell while scripts not blocked");
  1011   // dump out cumulative text perf metrics
  1012 #ifdef PR_LOGGING
  1013   gfxTextPerfMetrics* tp;
  1014   if (mPresContext && (tp = mPresContext->GetTextPerfMetrics())) {
  1015     tp->Accumulate();
  1016     if (tp->cumulative.numChars > 0) {
  1017       LogTextPerfStats(tp, this, tp->cumulative, 0.0, eLog_totals, nullptr);
  1020 #endif
  1022 #ifdef MOZ_REFLOW_PERF
  1023   DumpReflows();
  1024   if (mReflowCountMgr) {
  1025     delete mReflowCountMgr;
  1026     mReflowCountMgr = nullptr;
  1028 #endif
  1030   if (mHaveShutDown)
  1031     return;
  1033 #ifdef ACCESSIBILITY
  1034   if (mDocAccessible) {
  1035 #ifdef DEBUG
  1036     if (a11y::logging::IsEnabled(a11y::logging::eDocDestroy))
  1037       a11y::logging::DocDestroy("presshell destroyed", mDocument);
  1038 #endif
  1040     mDocAccessible->Shutdown();
  1041     mDocAccessible = nullptr;
  1043 #endif // ACCESSIBILITY
  1045   MaybeReleaseCapturingContent();
  1047   if (gKeyDownTarget && gKeyDownTarget->OwnerDoc() == mDocument) {
  1048     NS_RELEASE(gKeyDownTarget);
  1051   if (mContentToScrollTo) {
  1052     mContentToScrollTo->DeleteProperty(nsGkAtoms::scrolling);
  1053     mContentToScrollTo = nullptr;
  1056   if (mPresContext) {
  1057     // We need to notify the destroying the nsPresContext to ESM for
  1058     // suppressing to use from ESM.
  1059     mPresContext->EventStateManager()->NotifyDestroyPresContext(mPresContext);
  1063     nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
  1064     if (os) {
  1065       os->RemoveObserver(this, "agent-sheet-added");
  1066       os->RemoveObserver(this, "user-sheet-added");
  1067       os->RemoveObserver(this, "author-sheet-added");
  1068       os->RemoveObserver(this, "agent-sheet-removed");
  1069       os->RemoveObserver(this, "user-sheet-removed");
  1070       os->RemoveObserver(this, "author-sheet-removed");
  1071 #ifdef MOZ_XUL
  1072       os->RemoveObserver(this, "chrome-flush-skin-caches");
  1073 #endif
  1077   // If our paint suppression timer is still active, kill it.
  1078   if (mPaintSuppressionTimer) {
  1079     mPaintSuppressionTimer->Cancel();
  1080     mPaintSuppressionTimer = nullptr;
  1083   // Same for our reflow continuation timer
  1084   if (mReflowContinueTimer) {
  1085     mReflowContinueTimer->Cancel();
  1086     mReflowContinueTimer = nullptr;
  1089   if (mDelayedPaintTimer) {
  1090     mDelayedPaintTimer->Cancel();
  1091     mDelayedPaintTimer = nullptr;
  1094   mSynthMouseMoveEvent.Revoke();
  1096   mUpdateImageVisibilityEvent.Revoke();
  1098   ClearVisibleImagesList();
  1100   if (mCaret) {
  1101     mCaret->Terminate();
  1102     mCaret = nullptr;
  1105   if (mSelection) {
  1106     mSelection->DisconnectFromPresShell();
  1109   // release our pref style sheet, if we have one still
  1110   ClearPreferenceStyleRules();
  1112   mIsDestroying = true;
  1114   // We can't release all the event content in
  1115   // mCurrentEventContentStack here since there might be code on the
  1116   // stack that will release the event content too. Double release
  1117   // bad!
  1119   // The frames will be torn down, so remove them from the current
  1120   // event frame stack (since they'd be dangling references if we'd
  1121   // leave them in) and null out the mCurrentEventFrame pointer as
  1122   // well.
  1124   mCurrentEventFrame = nullptr;
  1126   int32_t i, count = mCurrentEventFrameStack.Length();
  1127   for (i = 0; i < count; i++) {
  1128     mCurrentEventFrameStack[i] = nullptr;
  1131   mFramesToDirty.Clear();
  1133   if (mViewManager) {
  1134     // Clear the view manager's weak pointer back to |this| in case it
  1135     // was leaked.
  1136     mViewManager->SetPresShell(nullptr);
  1137     mViewManager = nullptr;
  1140   mStyleSet->BeginShutdown(mPresContext);
  1141   nsRefreshDriver* rd = GetPresContext()->RefreshDriver();
  1143   // This shell must be removed from the document before the frame
  1144   // hierarchy is torn down to avoid finding deleted frames through
  1145   // this presshell while the frames are being torn down
  1146   if (mDocument) {
  1147     NS_ASSERTION(mDocument->GetShell() == this, "Wrong shell?");
  1148     mDocument->DeleteShell();
  1150     if (mDocument->HasAnimationController()) {
  1151       mDocument->GetAnimationController()->NotifyRefreshDriverDestroying(rd);
  1155   // Revoke any pending events.  We need to do this and cancel pending reflows
  1156   // before we destroy the frame manager, since apparently frame destruction
  1157   // sometimes spins the event queue when plug-ins are involved(!).
  1158   rd->RemoveLayoutFlushObserver(this);
  1159   if (mHiddenInvalidationObserverRefreshDriver) {
  1160     mHiddenInvalidationObserverRefreshDriver->RemovePresShellToInvalidateIfHidden(this);
  1163   if (rd->PresContext() == GetPresContext()) {
  1164     rd->RevokeViewManagerFlush();
  1167   mResizeEvent.Revoke();
  1168   if (mAsyncResizeTimerIsActive) {
  1169     mAsyncResizeEventTimer->Cancel();
  1170     mAsyncResizeTimerIsActive = false;
  1173   CancelAllPendingReflows();
  1174   CancelPostedReflowCallbacks();
  1176   // Destroy the frame manager. This will destroy the frame hierarchy
  1177   mFrameConstructor->WillDestroyFrameTree();
  1179   // Destroy all frame properties (whose destruction was suppressed
  1180   // while destroying the frame tree, but which might contain more
  1181   // frames within the properties.
  1182   if (mPresContext) {
  1183     // Clear out the prescontext's property table -- since our frame tree is
  1184     // now dead, we shouldn't be looking up any more properties in that table.
  1185     // We want to do this before we call SetShell() on the prescontext, so
  1186     // property destructors can usefully call GetPresShell() on the
  1187     // prescontext.
  1188     mPresContext->PropertyTable()->DeleteAll();
  1192   NS_WARN_IF_FALSE(!mWeakFrames, "Weak frames alive after destroying FrameManager");
  1193   while (mWeakFrames) {
  1194     mWeakFrames->Clear(this);
  1197   // Let the style set do its cleanup.
  1198   mStyleSet->Shutdown(mPresContext);
  1200   if (mPresContext) {
  1201     // We hold a reference to the pres context, and it holds a weak link back
  1202     // to us. To avoid the pres context having a dangling reference, set its
  1203     // pres shell to nullptr
  1204     mPresContext->SetShell(nullptr);
  1206     // Clear the link handler (weak reference) as well
  1207     mPresContext->SetLinkHandler(nullptr);
  1210   mHaveShutDown = true;
  1212   EvictTouches();
  1215 void
  1216 PresShell::MakeZombie()
  1218   mIsZombie = true;
  1219   CancelAllPendingReflows();
  1222 void
  1223 nsIPresShell::SetAuthorStyleDisabled(bool aStyleDisabled)
  1225   if (aStyleDisabled != mStyleSet->GetAuthorStyleDisabled()) {
  1226     mStyleSet->SetAuthorStyleDisabled(aStyleDisabled);
  1227     ReconstructStyleData();
  1229     nsCOMPtr<nsIObserverService> observerService =
  1230       mozilla::services::GetObserverService();
  1231     if (observerService) {
  1232       observerService->NotifyObservers(mDocument,
  1233                                        "author-style-disabled-changed",
  1234                                        nullptr);
  1239 bool
  1240 nsIPresShell::GetAuthorStyleDisabled() const
  1242   return mStyleSet->GetAuthorStyleDisabled();
  1245 nsresult
  1246 PresShell::SetPreferenceStyleRules(bool aForceReflow)
  1248   if (!mDocument) {
  1249     return NS_ERROR_NULL_POINTER;
  1252   nsPIDOMWindow *window = mDocument->GetWindow();
  1254   // If the document doesn't have a window there's no need to notify
  1255   // its presshell about changes to preferences since the document is
  1256   // in a state where it doesn't matter any more (see
  1257   // nsDocumentViewer::Close()).
  1259   if (!window) {
  1260     return NS_ERROR_NULL_POINTER;
  1263   NS_PRECONDITION(mPresContext, "presContext cannot be null");
  1264   if (mPresContext) {
  1265     // first, make sure this is not a chrome shell
  1266     if (nsContentUtils::IsInChromeDocshell(mDocument)) {
  1267       return NS_OK;
  1270 #ifdef DEBUG_attinasi
  1271     printf("Setting Preference Style Rules:\n");
  1272 #endif
  1273     // if here, we need to create rules for the prefs
  1274     // - this includes the background-color, the text-color,
  1275     //   the link color, the visited link color and the link-underlining
  1277     // first clear any exising rules
  1278     nsresult result = ClearPreferenceStyleRules();
  1280     // now the link rules (must come after the color rules, or links will not be correct color!)
  1281     // XXX - when there is both an override and agent pref stylesheet this won't matter,
  1282     //       as the color rules will be overrides and the links rules will be agent
  1283     if (NS_SUCCEEDED(result)) {
  1284       result = SetPrefLinkRules();
  1286     if (NS_SUCCEEDED(result)) {
  1287       result = SetPrefFocusRules();
  1289     if (NS_SUCCEEDED(result)) {
  1290       result = SetPrefNoScriptRule();
  1292     if (NS_SUCCEEDED(result)) {
  1293       result = SetPrefNoFramesRule();
  1295 #ifdef DEBUG_attinasi
  1296     printf( "Preference Style Rules set: error=%ld\n", (long)result);
  1297 #endif
  1299     // Note that this method never needs to force any calculation; the caller
  1300     // will recalculate style if needed
  1302     return result;
  1305   return NS_ERROR_NULL_POINTER;
  1308 nsresult PresShell::ClearPreferenceStyleRules(void)
  1310   nsresult result = NS_OK;
  1311   if (mPrefStyleSheet) {
  1312     NS_ASSERTION(mStyleSet, "null styleset entirely unexpected!");
  1313     if (mStyleSet) {
  1314       // remove the sheet from the styleset:
  1315       // - note that we have to check for success by comparing the count before and after...
  1316 #ifdef DEBUG
  1317       int32_t numBefore = mStyleSet->SheetCount(nsStyleSet::eUserSheet);
  1318       NS_ASSERTION(numBefore > 0, "no user stylesheets in styleset, but we have one!");
  1319 #endif
  1320       mStyleSet->RemoveStyleSheet(nsStyleSet::eUserSheet, mPrefStyleSheet);
  1322 #ifdef DEBUG_attinasi
  1323       NS_ASSERTION((numBefore - 1) == mStyleSet->GetNumberOfUserStyleSheets(),
  1324                    "Pref stylesheet was not removed");
  1325       printf("PrefStyleSheet removed\n");
  1326 #endif
  1327       // clear the sheet pointer: it is strictly historical now
  1328       mPrefStyleSheet = nullptr;
  1331   return result;
  1334 nsresult
  1335 PresShell::CreatePreferenceStyleSheet()
  1337   NS_ASSERTION(!mPrefStyleSheet, "prefStyleSheet already exists");
  1338   mPrefStyleSheet = new nsCSSStyleSheet(CORS_NONE);
  1339   nsCOMPtr<nsIURI> uri;
  1340   nsresult rv = NS_NewURI(getter_AddRefs(uri), "about:PreferenceStyleSheet", nullptr);
  1341   if (NS_FAILED(rv)) {
  1342     mPrefStyleSheet = nullptr;
  1343     return rv;
  1345   NS_ASSERTION(uri, "null but no error");
  1346   mPrefStyleSheet->SetURIs(uri, uri, uri);
  1347   mPrefStyleSheet->SetComplete();
  1348   uint32_t index;
  1349   rv =
  1350     mPrefStyleSheet->InsertRuleInternal(NS_LITERAL_STRING("@namespace svg url(http://www.w3.org/2000/svg);"),
  1351                                         0, &index);
  1352   if (NS_FAILED(rv)) {
  1353     mPrefStyleSheet = nullptr;
  1354     return rv;
  1356   rv =
  1357     mPrefStyleSheet->InsertRuleInternal(NS_LITERAL_STRING("@namespace url(http://www.w3.org/1999/xhtml);"),
  1358                                         0, &index);
  1359   if (NS_FAILED(rv)) {
  1360     mPrefStyleSheet = nullptr;
  1361     return rv;
  1364   mStyleSet->AppendStyleSheet(nsStyleSet::eUserSheet, mPrefStyleSheet);
  1365   return NS_OK;
  1368 // XXX We want these after the @namespace rules.  Does order matter
  1369 // for these rules, or can we call StyleRule::StyleRuleCount()
  1370 // and just "append"?
  1371 static uint32_t sInsertPrefSheetRulesAt = 2;
  1373 nsresult
  1374 PresShell::SetPrefNoScriptRule()
  1376   nsresult rv = NS_OK;
  1378   // also handle the case where print is done from print preview
  1379   // see bug #342439 for more details
  1380   nsIDocument* doc = mDocument;
  1381   if (doc->IsStaticDocument()) {
  1382     doc = doc->GetOriginalDocument();
  1385   bool scriptEnabled = doc->IsScriptEnabled();
  1386   if (scriptEnabled) {
  1387     if (!mPrefStyleSheet) {
  1388       rv = CreatePreferenceStyleSheet();
  1389       NS_ENSURE_SUCCESS(rv, rv);
  1392     uint32_t index = 0;
  1393     mPrefStyleSheet->
  1394       InsertRuleInternal(NS_LITERAL_STRING("noscript{display:none!important}"),
  1395                          sInsertPrefSheetRulesAt, &index);
  1398   return rv;
  1401 nsresult PresShell::SetPrefNoFramesRule(void)
  1403   NS_ASSERTION(mPresContext,"null prescontext not allowed");
  1404   if (!mPresContext) {
  1405     return NS_ERROR_FAILURE;
  1408   nsresult rv = NS_OK;
  1410   if (!mPrefStyleSheet) {
  1411     rv = CreatePreferenceStyleSheet();
  1412     NS_ENSURE_SUCCESS(rv, rv);
  1415   NS_ASSERTION(mPrefStyleSheet, "prefstylesheet should not be null");
  1417   bool allowSubframes = true;
  1418   nsCOMPtr<nsIDocShell> docShell(mPresContext->GetDocShell());
  1419   if (docShell) {
  1420     docShell->GetAllowSubframes(&allowSubframes);
  1422   if (!allowSubframes) {
  1423     uint32_t index = 0;
  1424     rv = mPrefStyleSheet->
  1425       InsertRuleInternal(NS_LITERAL_STRING("noframes{display:block}"),
  1426                          sInsertPrefSheetRulesAt, &index);
  1427     NS_ENSURE_SUCCESS(rv, rv);
  1428     rv = mPrefStyleSheet->
  1429       InsertRuleInternal(NS_LITERAL_STRING("frame, frameset, iframe {display:none!important}"),
  1430                          sInsertPrefSheetRulesAt, &index);
  1432   return rv;
  1435 nsresult PresShell::SetPrefLinkRules(void)
  1437   NS_ASSERTION(mPresContext,"null prescontext not allowed");
  1438   if (!mPresContext) {
  1439     return NS_ERROR_FAILURE;
  1442   nsresult rv = NS_OK;
  1444   if (!mPrefStyleSheet) {
  1445     rv = CreatePreferenceStyleSheet();
  1446     NS_ENSURE_SUCCESS(rv, rv);
  1449   NS_ASSERTION(mPrefStyleSheet, "prefstylesheet should not be null");
  1451   // support default link colors:
  1452   //   this means the link colors need to be overridable,
  1453   //   which they are if we put them in the agent stylesheet,
  1454   //   though if using an override sheet this will cause authors grief still
  1455   //   In the agent stylesheet, they are !important when we are ignoring document colors
  1457   nscolor linkColor(mPresContext->DefaultLinkColor());
  1458   nscolor activeColor(mPresContext->DefaultActiveLinkColor());
  1459   nscolor visitedColor(mPresContext->DefaultVisitedLinkColor());
  1461   NS_NAMED_LITERAL_STRING(ruleClose, "}");
  1462   uint32_t index = 0;
  1463   nsAutoString strColor;
  1465   // insert a rule to color links: '*|*:link {color: #RRGGBB [!important];}'
  1466   ColorToString(linkColor, strColor);
  1467   rv = mPrefStyleSheet->
  1468     InsertRuleInternal(NS_LITERAL_STRING("*|*:link{color:") +
  1469                        strColor + ruleClose,
  1470                        sInsertPrefSheetRulesAt, &index);
  1471   NS_ENSURE_SUCCESS(rv, rv);
  1473   // - visited links: '*|*:visited {color: #RRGGBB [!important];}'
  1474   ColorToString(visitedColor, strColor);
  1475   rv = mPrefStyleSheet->
  1476     InsertRuleInternal(NS_LITERAL_STRING("*|*:visited{color:") +
  1477                        strColor + ruleClose,
  1478                        sInsertPrefSheetRulesAt, &index);
  1479   NS_ENSURE_SUCCESS(rv, rv);
  1481   // - active links: '*|*:-moz-any-link:active {color: #RRGGBB [!important];}'
  1482   ColorToString(activeColor, strColor);
  1483   rv = mPrefStyleSheet->
  1484     InsertRuleInternal(NS_LITERAL_STRING("*|*:-moz-any-link:active{color:") +
  1485                        strColor + ruleClose,
  1486                        sInsertPrefSheetRulesAt, &index);
  1487   NS_ENSURE_SUCCESS(rv, rv);
  1489   bool underlineLinks =
  1490     mPresContext->GetCachedBoolPref(kPresContext_UnderlineLinks);
  1492   if (underlineLinks) {
  1493     // create a rule to make underlining happen
  1494     //  '*|*:-moz-any-link {text-decoration:[underline|none];}'
  1495     // no need for important, we want these to be overridable
  1496     // NOTE: these must go in the agent stylesheet or they cannot be
  1497     //       overridden by authors
  1498     rv = mPrefStyleSheet->
  1499       InsertRuleInternal(NS_LITERAL_STRING("*|*:-moz-any-link:not(svg|a){text-decoration:underline}"),
  1500                          sInsertPrefSheetRulesAt, &index);
  1501   } else {
  1502     rv = mPrefStyleSheet->
  1503       InsertRuleInternal(NS_LITERAL_STRING("*|*:-moz-any-link{text-decoration:none}"),
  1504                          sInsertPrefSheetRulesAt, &index);
  1507   return rv;
  1510 nsresult PresShell::SetPrefFocusRules(void)
  1512   NS_ASSERTION(mPresContext,"null prescontext not allowed");
  1513   nsresult result = NS_OK;
  1515   if (!mPresContext)
  1516     result = NS_ERROR_FAILURE;
  1518   if (NS_SUCCEEDED(result) && !mPrefStyleSheet)
  1519     result = CreatePreferenceStyleSheet();
  1521   if (NS_SUCCEEDED(result)) {
  1522     NS_ASSERTION(mPrefStyleSheet, "prefstylesheet should not be null");
  1524     if (mPresContext->GetUseFocusColors()) {
  1525       nscolor focusBackground(mPresContext->FocusBackgroundColor());
  1526       nscolor focusText(mPresContext->FocusTextColor());
  1528       // insert a rule to make focus the preferred color
  1529       uint32_t index = 0;
  1530       nsAutoString strRule, strColor;
  1532       ///////////////////////////////////////////////////////////////
  1533       // - focus: '*:focus
  1534       ColorToString(focusText,strColor);
  1535       strRule.AppendLiteral("*:focus,*:focus>font {color: ");
  1536       strRule.Append(strColor);
  1537       strRule.AppendLiteral(" !important; background-color: ");
  1538       ColorToString(focusBackground,strColor);
  1539       strRule.Append(strColor);
  1540       strRule.AppendLiteral(" !important; } ");
  1541       // insert the rules
  1542       result = mPrefStyleSheet->
  1543         InsertRuleInternal(strRule, sInsertPrefSheetRulesAt, &index);
  1545     uint8_t focusRingWidth = mPresContext->FocusRingWidth();
  1546     bool focusRingOnAnything = mPresContext->GetFocusRingOnAnything();
  1547     uint8_t focusRingStyle = mPresContext->GetFocusRingStyle();
  1549     if ((NS_SUCCEEDED(result) && focusRingWidth != 1 && focusRingWidth <= 4 ) || focusRingOnAnything) {
  1550       uint32_t index = 0;
  1551       nsAutoString strRule;
  1552       if (!focusRingOnAnything)
  1553         strRule.AppendLiteral("*|*:link:focus, *|*:visited");    // If we only want focus rings on the normal things like links
  1554       strRule.AppendLiteral(":focus {outline: ");     // For example 3px dotted WindowText (maximum 4)
  1555       strRule.AppendInt(focusRingWidth);
  1556       if (focusRingStyle == 0) // solid
  1557         strRule.AppendLiteral("px solid -moz-mac-focusring !important; -moz-outline-radius: 3px; outline-offset: 1px; } ");
  1558       else // dotted
  1559         strRule.AppendLiteral("px dotted WindowText !important; } ");
  1560       // insert the rules
  1561       result = mPrefStyleSheet->
  1562         InsertRuleInternal(strRule, sInsertPrefSheetRulesAt, &index);
  1563       NS_ENSURE_SUCCESS(result, result);
  1564       if (focusRingWidth != 1) {
  1565         // If the focus ring width is different from the default, fix buttons with rings
  1566         strRule.AssignLiteral("button::-moz-focus-inner, input[type=\"reset\"]::-moz-focus-inner,");
  1567         strRule.AppendLiteral("input[type=\"button\"]::-moz-focus-inner, ");
  1568         strRule.AppendLiteral("input[type=\"submit\"]::-moz-focus-inner { padding: 1px 2px 1px 2px; border: ");
  1569         strRule.AppendInt(focusRingWidth);
  1570         if (focusRingStyle == 0) // solid
  1571           strRule.AppendLiteral("px solid transparent !important; } ");
  1572         else
  1573           strRule.AppendLiteral("px dotted transparent !important; } ");
  1574         result = mPrefStyleSheet->
  1575           InsertRuleInternal(strRule, sInsertPrefSheetRulesAt, &index);
  1576         NS_ENSURE_SUCCESS(result, result);
  1578         strRule.AssignLiteral("button:focus::-moz-focus-inner, input[type=\"reset\"]:focus::-moz-focus-inner,");
  1579         strRule.AppendLiteral("input[type=\"button\"]:focus::-moz-focus-inner, input[type=\"submit\"]:focus::-moz-focus-inner {");
  1580         strRule.AppendLiteral("border-color: ButtonText !important; }");
  1581         result = mPrefStyleSheet->
  1582           InsertRuleInternal(strRule, sInsertPrefSheetRulesAt, &index);
  1586   return result;
  1589 void
  1590 PresShell::AddUserSheet(nsISupports* aSheet)
  1592   // Make sure this does what nsDocumentViewer::CreateStyleSet does wrt
  1593   // ordering. We want this new sheet to come after all the existing stylesheet
  1594   // service sheets, but before other user sheets; see nsIStyleSheetService.idl
  1595   // for the ordering.  Just remove and readd all the nsStyleSheetService
  1596   // sheets.
  1597   nsCOMPtr<nsIStyleSheetService> dummy =
  1598     do_GetService(NS_STYLESHEETSERVICE_CONTRACTID);
  1600   mStyleSet->BeginUpdate();
  1602   nsStyleSheetService *sheetService = nsStyleSheetService::gInstance;
  1603   nsCOMArray<nsIStyleSheet> & userSheets = *sheetService->UserStyleSheets();
  1604   int32_t i;
  1605   // Iterate forwards when removing so the searches for RemoveStyleSheet are as
  1606   // short as possible.
  1607   for (i = 0; i < userSheets.Count(); ++i) {
  1608     mStyleSet->RemoveStyleSheet(nsStyleSet::eUserSheet, userSheets[i]);
  1611   // Now iterate backwards, so that the order of userSheets will be the same as
  1612   // the order of sheets from it in the style set.
  1613   for (i = userSheets.Count() - 1; i >= 0; --i) {
  1614     mStyleSet->PrependStyleSheet(nsStyleSet::eUserSheet, userSheets[i]);
  1617   mStyleSet->EndUpdate();
  1619   ReconstructStyleData();
  1622 void
  1623 PresShell::AddAgentSheet(nsISupports* aSheet)
  1625   // Make sure this does what nsDocumentViewer::CreateStyleSet does
  1626   // wrt ordering.
  1627   nsCOMPtr<nsIStyleSheet> sheet = do_QueryInterface(aSheet);
  1628   if (!sheet) {
  1629     return;
  1632   mStyleSet->AppendStyleSheet(nsStyleSet::eAgentSheet, sheet);
  1633   ReconstructStyleData();
  1636 void
  1637 PresShell::AddAuthorSheet(nsISupports* aSheet)
  1639   nsCOMPtr<nsIStyleSheet> sheet = do_QueryInterface(aSheet);
  1640   if (!sheet) {
  1641     return;
  1644   // Document specific "additional" Author sheets should be stronger than the ones
  1645   // added with the StyleSheetService.
  1646   nsIStyleSheet* firstAuthorSheet = mDocument->FirstAdditionalAuthorSheet();
  1647   if (firstAuthorSheet) {
  1648     mStyleSet->InsertStyleSheetBefore(nsStyleSet::eDocSheet, sheet, firstAuthorSheet);
  1649   } else {
  1650     mStyleSet->AppendStyleSheet(nsStyleSet::eDocSheet, sheet);
  1653   ReconstructStyleData();
  1656 void
  1657 PresShell::RemoveSheet(nsStyleSet::sheetType aType, nsISupports* aSheet)
  1659   nsCOMPtr<nsIStyleSheet> sheet = do_QueryInterface(aSheet);
  1660   if (!sheet) {
  1661     return;
  1664   mStyleSet->RemoveStyleSheet(aType, sheet);
  1665   ReconstructStyleData();
  1668 NS_IMETHODIMP
  1669 PresShell::SetDisplaySelection(int16_t aToggle)
  1671   mSelection->SetDisplaySelection(aToggle);
  1672   return NS_OK;
  1675 NS_IMETHODIMP
  1676 PresShell::GetDisplaySelection(int16_t *aToggle)
  1678   *aToggle = mSelection->GetDisplaySelection();
  1679   return NS_OK;
  1682 NS_IMETHODIMP
  1683 PresShell::GetSelection(SelectionType aType, nsISelection **aSelection)
  1685   if (!aSelection || !mSelection)
  1686     return NS_ERROR_NULL_POINTER;
  1688   *aSelection = mSelection->GetSelection(aType);
  1690   if (!(*aSelection))
  1691     return NS_ERROR_INVALID_ARG;
  1693   NS_ADDREF(*aSelection);
  1695   return NS_OK;
  1698 Selection*
  1699 PresShell::GetCurrentSelection(SelectionType aType)
  1701   if (!mSelection)
  1702     return nullptr;
  1704   return mSelection->GetSelection(aType);
  1707 NS_IMETHODIMP
  1708 PresShell::ScrollSelectionIntoView(SelectionType aType, SelectionRegion aRegion,
  1709                                    int16_t aFlags)
  1711   if (!mSelection)
  1712     return NS_ERROR_NULL_POINTER;
  1714   return mSelection->ScrollSelectionIntoView(aType, aRegion, aFlags);
  1717 NS_IMETHODIMP
  1718 PresShell::RepaintSelection(SelectionType aType)
  1720   if (!mSelection)
  1721     return NS_ERROR_NULL_POINTER;
  1723   return mSelection->RepaintSelection(aType);
  1726 // Make shell be a document observer
  1727 void
  1728 PresShell::BeginObservingDocument()
  1730   if (mDocument && !mIsDestroying) {
  1731     mDocument->AddObserver(this);
  1732     if (mIsDocumentGone) {
  1733       NS_WARNING("Adding a presshell that was disconnected from the document "
  1734                  "as a document observer?  Sounds wrong...");
  1735       mIsDocumentGone = false;
  1740 // Make shell stop being a document observer
  1741 void
  1742 PresShell::EndObservingDocument()
  1744   // XXXbz do we need to tell the frame constructor that the document
  1745   // is gone, perhaps?  Except for printing it's NOT gone, sometimes.
  1746   mIsDocumentGone = true;
  1747   if (mDocument) {
  1748     mDocument->RemoveObserver(this);
  1752 #ifdef DEBUG_kipp
  1753 char* nsPresShell_ReflowStackPointerTop;
  1754 #endif
  1756 nsresult
  1757 PresShell::Initialize(nscoord aWidth, nscoord aHeight)
  1759   if (mIsDestroying) {
  1760     return NS_OK;
  1763   if (!mDocument) {
  1764     // Nothing to do
  1765     return NS_OK;
  1768   mozilla::TimeStamp timerStart = mozilla::TimeStamp::Now();
  1770   NS_ASSERTION(!mDidInitialize, "Why are we being called?");
  1772   nsCOMPtr<nsIPresShell> kungFuDeathGrip(this);
  1773   mDidInitialize = true;
  1775 #ifdef DEBUG
  1776   if (VERIFY_REFLOW_NOISY_RC & gVerifyReflowFlags) {
  1777     if (mDocument) {
  1778       nsIURI *uri = mDocument->GetDocumentURI();
  1779       if (uri) {
  1780         nsAutoCString url;
  1781         uri->GetSpec(url);
  1782         printf("*** PresShell::Initialize (this=%p, url='%s')\n", (void*)this, url.get());
  1786 #endif
  1788   if (mCaret)
  1789     mCaret->EraseCaret();
  1791   // XXX Do a full invalidate at the beginning so that invalidates along
  1792   // the way don't have region accumulation issues?
  1794   mPresContext->SetVisibleArea(nsRect(0, 0, aWidth, aHeight));
  1796   // Get the root frame from the frame manager
  1797   // XXXbz it would be nice to move this somewhere else... like frame manager
  1798   // Init(), say.  But we need to make sure our views are all set up by the
  1799   // time we do this!
  1800   nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
  1801   NS_ASSERTION(!rootFrame, "How did that happen, exactly?");
  1802   if (!rootFrame) {
  1803     nsAutoScriptBlocker scriptBlocker;
  1804     mFrameConstructor->BeginUpdate();
  1805     rootFrame = mFrameConstructor->ConstructRootFrame();
  1806     mFrameConstructor->SetRootFrame(rootFrame);
  1807     mFrameConstructor->EndUpdate();
  1810   NS_ENSURE_STATE(!mHaveShutDown);
  1812   if (!rootFrame) {
  1813     return NS_ERROR_OUT_OF_MEMORY;
  1816   for (nsIFrame* f = rootFrame; f; f = nsLayoutUtils::GetCrossDocParentFrame(f)) {
  1817     if (f->GetStateBits() & NS_FRAME_NO_COMPONENT_ALPHA) {
  1818       f->InvalidateFrameSubtree();
  1819       f->RemoveStateBits(NS_FRAME_NO_COMPONENT_ALPHA);
  1823   Element *root = mDocument->GetRootElement();
  1825   if (root) {
  1827       nsAutoCauseReflowNotifier reflowNotifier(this);
  1828       mFrameConstructor->BeginUpdate();
  1830       // Have the style sheet processor construct frame for the root
  1831       // content object down
  1832       mFrameConstructor->ContentInserted(nullptr, root, nullptr, false);
  1833       VERIFY_STYLE_TREE;
  1835       // Something in mFrameConstructor->ContentInserted may have caused
  1836       // Destroy() to get called, bug 337586.
  1837       NS_ENSURE_STATE(!mHaveShutDown);
  1839       mFrameConstructor->EndUpdate();
  1842     // nsAutoScriptBlocker going out of scope may have killed us too
  1843     NS_ENSURE_STATE(!mHaveShutDown);
  1845     // Run the XBL binding constructors for any new frames we've constructed
  1846     mDocument->BindingManager()->ProcessAttachedQueue();
  1848     // Constructors may have killed us too
  1849     NS_ENSURE_STATE(!mHaveShutDown);
  1851     // Now flush out pending restyles before we actually reflow, in
  1852     // case XBL constructors changed styles somewhere.
  1854       nsAutoScriptBlocker scriptBlocker;
  1855       mPresContext->RestyleManager()->ProcessPendingRestyles();
  1858     // And that might have run _more_ XBL constructors
  1859     NS_ENSURE_STATE(!mHaveShutDown);
  1862   NS_ASSERTION(rootFrame, "How did that happen?");
  1864   // Note: when the frame was created above it had the NS_FRAME_IS_DIRTY bit
  1865   // set, but XBL processing could have caused a reflow which clears it.
  1866   if (MOZ_LIKELY(rootFrame->GetStateBits() & NS_FRAME_IS_DIRTY)) {
  1867     // Unset the DIRTY bits so that FrameNeedsReflow() will work right.
  1868     rootFrame->RemoveStateBits(NS_FRAME_IS_DIRTY |
  1869                                NS_FRAME_HAS_DIRTY_CHILDREN);
  1870     NS_ASSERTION(!mDirtyRoots.Contains(rootFrame),
  1871                  "Why is the root in mDirtyRoots already?");
  1872     FrameNeedsReflow(rootFrame, eResize, NS_FRAME_IS_DIRTY);
  1873     NS_ASSERTION(mDirtyRoots.Contains(rootFrame),
  1874                  "Should be in mDirtyRoots now");
  1875     NS_ASSERTION(mReflowScheduled, "Why no reflow scheduled?");
  1878   // Restore our root scroll position now if we're getting here after EndLoad
  1879   // got called, since this is our one chance to do it.  Note that we need not
  1880   // have reflowed for this to work; when the scrollframe is finally reflowed
  1881   // it'll pick up the position we store in it here.
  1882   if (!mDocumentLoading) {
  1883     RestoreRootScrollPosition();
  1886   // For printing, we just immediately unsuppress.
  1887   if (!mPresContext->IsPaginated()) {
  1888     // Kick off a one-shot timer based off our pref value.  When this timer
  1889     // fires, if painting is still locked down, then we will go ahead and
  1890     // trigger a full invalidate and allow painting to proceed normally.
  1891     mPaintingSuppressed = true;
  1892     // Don't suppress painting if the document isn't loading.
  1893     nsIDocument::ReadyState readyState = mDocument->GetReadyStateEnum();
  1894     if (readyState != nsIDocument::READYSTATE_COMPLETE) {
  1895       mPaintSuppressionTimer = do_CreateInstance("@mozilla.org/timer;1");
  1897     if (!mPaintSuppressionTimer) {
  1898       mPaintingSuppressed = false;
  1899     } else {
  1900       // Initialize the timer.
  1902       // Default to PAINTLOCK_EVENT_DELAY if we can't get the pref value.
  1903       int32_t delay =
  1904         Preferences::GetInt("nglayout.initialpaint.delay",
  1905                             PAINTLOCK_EVENT_DELAY);
  1907       mPaintSuppressionTimer->InitWithFuncCallback(sPaintSuppressionCallback,
  1908                                                    this, delay,
  1909                                                    nsITimer::TYPE_ONE_SHOT);
  1913   if (root && root->IsXUL()) {
  1914     mozilla::Telemetry::AccumulateTimeDelta(Telemetry::XUL_INITIAL_FRAME_CONSTRUCTION,
  1915                                             timerStart);
  1918   return NS_OK; //XXX this needs to be real. MMP
  1921 void
  1922 PresShell::sPaintSuppressionCallback(nsITimer *aTimer, void* aPresShell)
  1924   nsRefPtr<PresShell> self = static_cast<PresShell*>(aPresShell);
  1925   if (self)
  1926     self->UnsuppressPainting();
  1929 void
  1930 PresShell::AsyncResizeEventCallback(nsITimer* aTimer, void* aPresShell)
  1932   static_cast<PresShell*>(aPresShell)->FireResizeEvent();
  1935 nsresult
  1936 PresShell::ResizeReflowOverride(nscoord aWidth, nscoord aHeight)
  1938   mViewportOverridden = true;
  1939   return ResizeReflowIgnoreOverride(aWidth, aHeight);
  1942 nsresult
  1943 PresShell::ResizeReflow(nscoord aWidth, nscoord aHeight)
  1945   if (mViewportOverridden) {
  1946     // The viewport has been overridden, and this reflow request
  1947     // didn't ask to ignore the override.  Pretend it didn't happen.
  1948     return NS_OK;
  1950   return ResizeReflowIgnoreOverride(aWidth, aHeight);
  1953 nsresult
  1954 PresShell::ResizeReflowIgnoreOverride(nscoord aWidth, nscoord aHeight)
  1956   NS_PRECONDITION(!mIsReflowing, "Shouldn't be in reflow here!");
  1957   NS_PRECONDITION(aWidth != NS_UNCONSTRAINEDSIZE,
  1958                   "shouldn't use unconstrained widths anymore");
  1960   // If we don't have a root frame yet, that means we haven't had our initial
  1961   // reflow... If that's the case, and aWidth or aHeight is unconstrained,
  1962   // ignore them altogether.
  1963   nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
  1964   if (!rootFrame && aHeight == NS_UNCONSTRAINEDSIZE) {
  1965     // We can't do the work needed for SizeToContent without a root
  1966     // frame, and we want to return before setting the visible area.
  1967     return NS_ERROR_NOT_AVAILABLE;
  1970   mPresContext->SetVisibleArea(nsRect(0, 0, aWidth, aHeight));
  1972   // There isn't anything useful we can do if the initial reflow hasn't happened.
  1973   if (!rootFrame) {
  1974     return NS_OK;
  1977   nsRefPtr<nsViewManager> viewManagerDeathGrip = mViewManager;
  1978   // Take this ref after viewManager so it'll make sure to go away first.
  1979   nsCOMPtr<nsIPresShell> kungFuDeathGrip(this);
  1981   if (!GetPresContext()->SupressingResizeReflow()) {
  1982     // Have to make sure that the content notifications are flushed before we
  1983     // start messing with the frame model; otherwise we can get content doubling.
  1984     mDocument->FlushPendingNotifications(Flush_ContentAndNotify);
  1986     // Make sure style is up to date
  1988       nsAutoScriptBlocker scriptBlocker;
  1989       mPresContext->RestyleManager()->ProcessPendingRestyles();
  1992     rootFrame = mFrameConstructor->GetRootFrame();
  1993     if (!mIsDestroying && rootFrame) {
  1994       // XXX Do a full invalidate at the beginning so that invalidates along
  1995       // the way don't have region accumulation issues?
  1998         nsAutoCauseReflowNotifier crNotifier(this);
  1999         WillDoReflow();
  2001         // Kick off a top-down reflow
  2002         AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Reflow);
  2003         nsViewManager::AutoDisableRefresh refreshBlocker(mViewManager);
  2005         mDirtyRoots.RemoveElement(rootFrame);
  2006         DoReflow(rootFrame, true);
  2009       DidDoReflow(true, false);
  2013   rootFrame = mFrameConstructor->GetRootFrame();
  2014   if (aHeight == NS_UNCONSTRAINEDSIZE && rootFrame) {
  2015     mPresContext->SetVisibleArea(
  2016       nsRect(0, 0, aWidth, rootFrame->GetRect().height));
  2019   if (!mIsDestroying && !mResizeEvent.IsPending() &&
  2020       !mAsyncResizeTimerIsActive) {
  2021     if (mInResize) {
  2022       if (!mAsyncResizeEventTimer) {
  2023         mAsyncResizeEventTimer = do_CreateInstance("@mozilla.org/timer;1");
  2025       if (mAsyncResizeEventTimer) {
  2026         mAsyncResizeTimerIsActive = true;
  2027         mAsyncResizeEventTimer->InitWithFuncCallback(AsyncResizeEventCallback,
  2028                                                      this, 15,
  2029                                                      nsITimer::TYPE_ONE_SHOT);
  2031     } else {
  2032       nsRefPtr<nsRunnableMethod<PresShell> > resizeEvent =
  2033         NS_NewRunnableMethod(this, &PresShell::FireResizeEvent);
  2034       if (NS_SUCCEEDED(NS_DispatchToCurrentThread(resizeEvent))) {
  2035         mResizeEvent = resizeEvent;
  2036         mDocument->SetNeedStyleFlush();
  2041   return NS_OK; //XXX this needs to be real. MMP
  2044 void
  2045 PresShell::FireResizeEvent()
  2047   if (mAsyncResizeTimerIsActive) {
  2048     mAsyncResizeTimerIsActive = false;
  2049     mAsyncResizeEventTimer->Cancel();
  2051   mResizeEvent.Revoke();
  2053   if (mIsDocumentGone)
  2054     return;
  2056   //Send resize event from here.
  2057   WidgetEvent event(true, NS_RESIZE_EVENT);
  2058   nsEventStatus status = nsEventStatus_eIgnore;
  2060   nsPIDOMWindow *window = mDocument->GetWindow();
  2061   if (window) {
  2062     nsCOMPtr<nsIPresShell> kungFuDeathGrip(this);
  2063     mInResize = true;
  2064     EventDispatcher::Dispatch(window, mPresContext, &event, nullptr, &status);
  2065     mInResize = false;
  2069 void
  2070 PresShell::SetIgnoreFrameDestruction(bool aIgnore)
  2072   if (mDocument) {
  2073     // We need to tell the ImageLoader to drop all its references to frames
  2074     // because they're about to go away and it won't get notifications of that.
  2075     mDocument->StyleImageLoader()->ClearFrames();
  2077   mIgnoreFrameDestruction = aIgnore;
  2080 void
  2081 PresShell::NotifyDestroyingFrame(nsIFrame* aFrame)
  2083   if (!mIgnoreFrameDestruction) {
  2084     mDocument->StyleImageLoader()->DropRequestsForFrame(aFrame);
  2086     mFrameConstructor->NotifyDestroyingFrame(aFrame);
  2088     for (int32_t idx = mDirtyRoots.Length(); idx; ) {
  2089       --idx;
  2090       if (mDirtyRoots[idx] == aFrame) {
  2091         mDirtyRoots.RemoveElementAt(idx);
  2095     // Remove frame properties
  2096     mPresContext->NotifyDestroyingFrame(aFrame);
  2098     if (aFrame == mCurrentEventFrame) {
  2099       mCurrentEventContent = aFrame->GetContent();
  2100       mCurrentEventFrame = nullptr;
  2103   #ifdef DEBUG
  2104     if (aFrame == mDrawEventTargetFrame) {
  2105       mDrawEventTargetFrame = nullptr;
  2107   #endif
  2109     for (unsigned int i=0; i < mCurrentEventFrameStack.Length(); i++) {
  2110       if (aFrame == mCurrentEventFrameStack.ElementAt(i)) {
  2111         //One of our stack frames was deleted.  Get its content so that when we
  2112         //pop it we can still get its new frame from its content
  2113         nsIContent *currentEventContent = aFrame->GetContent();
  2114         mCurrentEventContentStack.ReplaceObjectAt(currentEventContent, i);
  2115         mCurrentEventFrameStack[i] = nullptr;
  2119     mFramesToDirty.RemoveEntry(aFrame);
  2120   } else {
  2121     // We must delete this property in situ so that its destructor removes the
  2122     // frame from FrameLayerBuilder::DisplayItemData::mFrameList -- otherwise
  2123     // the DisplayItemData destructor will use the destroyed frame when it
  2124     // tries to remove it from the (array) value of this property.
  2125     mPresContext->PropertyTable()->
  2126       Delete(aFrame, FrameLayerBuilder::LayerManagerDataProperty());
  2130 already_AddRefed<nsCaret> PresShell::GetCaret() const
  2132   nsRefPtr<nsCaret> caret = mCaret;
  2133   return caret.forget();
  2136 void PresShell::MaybeInvalidateCaretPosition()
  2138   if (mCaret) {
  2139     mCaret->InvalidateOutsideCaret();
  2143 void PresShell::SetCaret(nsCaret *aNewCaret)
  2145   mCaret = aNewCaret;
  2148 void PresShell::RestoreCaret()
  2150   mCaret = mOriginalCaret;
  2153 NS_IMETHODIMP PresShell::SetCaretEnabled(bool aInEnable)
  2155   bool oldEnabled = mCaretEnabled;
  2157   mCaretEnabled = aInEnable;
  2159   if (mCaret && (mCaretEnabled != oldEnabled))
  2161 /*  Don't change the caret's selection here! This was an evil side-effect of SetCaretEnabled()
  2162     nsCOMPtr<nsIDOMSelection> domSel;
  2163     if (NS_SUCCEEDED(GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(domSel))) && domSel)
  2164       mCaret->SetCaretDOMSelection(domSel);
  2165 */
  2166     mCaret->SetCaretVisible(mCaretEnabled);
  2169   return NS_OK;
  2172 NS_IMETHODIMP PresShell::SetCaretReadOnly(bool aReadOnly)
  2174   if (mCaret)
  2175     mCaret->SetCaretReadOnly(aReadOnly);
  2176   return NS_OK;
  2179 NS_IMETHODIMP PresShell::GetCaretEnabled(bool *aOutEnabled)
  2181   NS_ENSURE_ARG_POINTER(aOutEnabled);
  2182   *aOutEnabled = mCaretEnabled;
  2183   return NS_OK;
  2186 NS_IMETHODIMP PresShell::SetCaretVisibilityDuringSelection(bool aVisibility)
  2188   if (mCaret)
  2189     mCaret->SetVisibilityDuringSelection(aVisibility);
  2190   return NS_OK;
  2193 NS_IMETHODIMP PresShell::GetCaretVisible(bool *aOutIsVisible)
  2195   *aOutIsVisible = false;
  2196   if (mCaret) {
  2197     nsresult rv = mCaret->GetCaretVisible(aOutIsVisible);
  2198     NS_ENSURE_SUCCESS(rv,rv);
  2200   return NS_OK;
  2203 NS_IMETHODIMP PresShell::SetSelectionFlags(int16_t aInEnable)
  2205   mSelectionFlags = aInEnable;
  2206   return NS_OK;
  2209 NS_IMETHODIMP PresShell::GetSelectionFlags(int16_t *aOutEnable)
  2211   if (!aOutEnable)
  2212     return NS_ERROR_INVALID_ARG;
  2213   *aOutEnable = mSelectionFlags;
  2214   return NS_OK;
  2217 //implementation of nsISelectionController
  2219 NS_IMETHODIMP
  2220 PresShell::CharacterMove(bool aForward, bool aExtend)
  2222   return mSelection->CharacterMove(aForward, aExtend);
  2225 NS_IMETHODIMP
  2226 PresShell::CharacterExtendForDelete()
  2228   return mSelection->CharacterExtendForDelete();
  2231 NS_IMETHODIMP
  2232 PresShell::CharacterExtendForBackspace()
  2234   return mSelection->CharacterExtendForBackspace();
  2237 NS_IMETHODIMP
  2238 PresShell::WordMove(bool aForward, bool aExtend)
  2240   nsresult result = mSelection->WordMove(aForward, aExtend);
  2241 // if we can't go down/up any more we must then move caret completely to
  2242 // end/beginning respectively.
  2243   if (NS_FAILED(result))
  2244     result = CompleteMove(aForward, aExtend);
  2245   return result;
  2248 NS_IMETHODIMP
  2249 PresShell::WordExtendForDelete(bool aForward)
  2251   return mSelection->WordExtendForDelete(aForward);
  2254 NS_IMETHODIMP
  2255 PresShell::LineMove(bool aForward, bool aExtend)
  2257   nsresult result = mSelection->LineMove(aForward, aExtend);
  2258 // if we can't go down/up any more we must then move caret completely to
  2259 // end/beginning respectively.
  2260   if (NS_FAILED(result))
  2261     result = CompleteMove(aForward,aExtend);
  2262   return result;
  2265 NS_IMETHODIMP
  2266 PresShell::IntraLineMove(bool aForward, bool aExtend)
  2268   return mSelection->IntraLineMove(aForward, aExtend);
  2273 NS_IMETHODIMP
  2274 PresShell::PageMove(bool aForward, bool aExtend)
  2276   nsIScrollableFrame *scrollableFrame =
  2277     GetFrameToScrollAsScrollable(nsIPresShell::eVertical);
  2278   if (!scrollableFrame)
  2279     return NS_OK;
  2281   mSelection->CommonPageMove(aForward, aExtend, scrollableFrame);
  2282   // After ScrollSelectionIntoView(), the pending notifications might be
  2283   // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
  2284   return ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
  2285                                  nsISelectionController::SELECTION_FOCUS_REGION,
  2286                                  nsISelectionController::SCROLL_SYNCHRONOUS);
  2291 NS_IMETHODIMP
  2292 PresShell::ScrollPage(bool aForward)
  2294   nsIScrollableFrame* scrollFrame =
  2295     GetFrameToScrollAsScrollable(nsIPresShell::eVertical);
  2296   if (scrollFrame) {
  2297     scrollFrame->ScrollBy(nsIntPoint(0, aForward ? 1 : -1),
  2298                           nsIScrollableFrame::PAGES,
  2299                           nsIScrollableFrame::SMOOTH);
  2301   return NS_OK;
  2304 NS_IMETHODIMP
  2305 PresShell::ScrollLine(bool aForward)
  2307   nsIScrollableFrame* scrollFrame =
  2308     GetFrameToScrollAsScrollable(nsIPresShell::eVertical);
  2309   if (scrollFrame) {
  2310     int32_t lineCount = Preferences::GetInt("toolkit.scrollbox.verticalScrollDistance",
  2311                                             NS_DEFAULT_VERTICAL_SCROLL_DISTANCE);
  2312     scrollFrame->ScrollBy(nsIntPoint(0, aForward ? lineCount : -lineCount),
  2313                           nsIScrollableFrame::LINES,
  2314                           nsIScrollableFrame::SMOOTH);
  2316   return NS_OK;
  2319 NS_IMETHODIMP
  2320 PresShell::ScrollCharacter(bool aRight)
  2322   nsIScrollableFrame* scrollFrame =
  2323     GetFrameToScrollAsScrollable(nsIPresShell::eHorizontal);
  2324   if (scrollFrame) {
  2325     int32_t h = Preferences::GetInt("toolkit.scrollbox.horizontalScrollDistance",
  2326                                     NS_DEFAULT_HORIZONTAL_SCROLL_DISTANCE);
  2327     scrollFrame->ScrollBy(nsIntPoint(aRight ? h : -h, 0),
  2328                           nsIScrollableFrame::LINES,
  2329                           nsIScrollableFrame::SMOOTH);
  2331   return NS_OK;
  2334 NS_IMETHODIMP
  2335 PresShell::CompleteScroll(bool aForward)
  2337   nsIScrollableFrame* scrollFrame =
  2338     GetFrameToScrollAsScrollable(nsIPresShell::eVertical);
  2339   if (scrollFrame) {
  2340     scrollFrame->ScrollBy(nsIntPoint(0, aForward ? 1 : -1),
  2341                           nsIScrollableFrame::WHOLE,
  2342                           nsIScrollableFrame::SMOOTH);
  2344   return NS_OK;
  2347 NS_IMETHODIMP
  2348 PresShell::CompleteMove(bool aForward, bool aExtend)
  2350   // Beware! This may flush notifications via synchronous
  2351   // ScrollSelectionIntoView.
  2352   nsIContent* limiter = mSelection->GetAncestorLimiter();
  2353   nsIFrame* frame = limiter ? limiter->GetPrimaryFrame()
  2354                             : FrameConstructor()->GetRootElementFrame();
  2355   if (!frame)
  2356     return NS_ERROR_FAILURE;
  2357   nsIFrame::CaretPosition pos =
  2358     frame->GetExtremeCaretPosition(!aForward);
  2359   mSelection->HandleClick(pos.mResultContent, pos.mContentOffset,
  2360                           pos.mContentOffset, aExtend, false, aForward);
  2361   if (limiter) {
  2362     // HandleClick resets ancestorLimiter, so set it again.
  2363     mSelection->SetAncestorLimiter(limiter);
  2366   // After ScrollSelectionIntoView(), the pending notifications might be
  2367   // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
  2368   return ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
  2369                                  nsISelectionController::SELECTION_FOCUS_REGION,
  2370                                  nsISelectionController::SCROLL_SYNCHRONOUS);
  2373 NS_IMETHODIMP
  2374 PresShell::SelectAll()
  2376   return mSelection->SelectAll();
  2379 static void
  2380 DoCheckVisibility(nsPresContext* aPresContext,
  2381                   nsIContent* aNode,
  2382                   int16_t aStartOffset,
  2383                   int16_t aEndOffset,
  2384                   bool* aRetval)
  2386   nsIFrame* frame = aNode->GetPrimaryFrame();
  2387   if (!frame) {
  2388     // No frame to look at so it must not be visible.
  2389     return;
  2392   // Start process now to go through all frames to find startOffset. Then check
  2393   // chars after that to see if anything until EndOffset is visible.
  2394   bool finished = false;
  2395   frame->CheckVisibility(aPresContext, aStartOffset, aEndOffset, true,
  2396                          &finished, aRetval);
  2397   // Don't worry about other return value.
  2400 NS_IMETHODIMP
  2401 PresShell::CheckVisibility(nsIDOMNode *node, int16_t startOffset, int16_t EndOffset, bool *_retval)
  2403   if (!node || startOffset>EndOffset || !_retval || startOffset<0 || EndOffset<0)
  2404     return NS_ERROR_INVALID_ARG;
  2405   *_retval = false; //initialize return parameter
  2406   nsCOMPtr<nsIContent> content(do_QueryInterface(node));
  2407   if (!content)
  2408     return NS_ERROR_FAILURE;
  2410   DoCheckVisibility(mPresContext, content, startOffset, EndOffset, _retval);
  2411   return NS_OK;
  2414 nsresult
  2415 PresShell::CheckVisibilityContent(nsIContent* aNode, int16_t aStartOffset,
  2416                                   int16_t aEndOffset, bool* aRetval)
  2418   if (!aNode || aStartOffset > aEndOffset || !aRetval ||
  2419       aStartOffset < 0 || aEndOffset < 0) {
  2420     return NS_ERROR_INVALID_ARG;
  2423   *aRetval = false;
  2424   DoCheckVisibility(mPresContext, aNode, aStartOffset, aEndOffset, aRetval);
  2425   return NS_OK;
  2428 //end implementations nsISelectionController
  2430 nsIFrame*
  2431 nsIPresShell::GetRootFrameExternal() const
  2433   return mFrameConstructor->GetRootFrame();
  2436 nsIFrame*
  2437 nsIPresShell::GetRootScrollFrame() const
  2439   nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
  2440   // Ensure root frame is a viewport frame
  2441   if (!rootFrame || nsGkAtoms::viewportFrame != rootFrame->GetType())
  2442     return nullptr;
  2443   nsIFrame* theFrame = rootFrame->GetFirstPrincipalChild();
  2444   if (!theFrame || nsGkAtoms::scrollFrame != theFrame->GetType())
  2445     return nullptr;
  2446   return theFrame;
  2449 nsIScrollableFrame*
  2450 nsIPresShell::GetRootScrollFrameAsScrollable() const
  2452   nsIFrame* frame = GetRootScrollFrame();
  2453   if (!frame)
  2454     return nullptr;
  2455   nsIScrollableFrame* scrollableFrame = do_QueryFrame(frame);
  2456   NS_ASSERTION(scrollableFrame,
  2457                "All scroll frames must implement nsIScrollableFrame");
  2458   return scrollableFrame;
  2461 nsIScrollableFrame*
  2462 nsIPresShell::GetRootScrollFrameAsScrollableExternal() const
  2464   return GetRootScrollFrameAsScrollable();
  2467 nsIPageSequenceFrame*
  2468 PresShell::GetPageSequenceFrame() const
  2470   nsIFrame* frame = mFrameConstructor->GetPageSequenceFrame();
  2471   return do_QueryFrame(frame);
  2474 void
  2475 PresShell::BeginUpdate(nsIDocument *aDocument, nsUpdateType aUpdateType)
  2477 #ifdef DEBUG
  2478   mUpdateCount++;
  2479 #endif
  2480   mFrameConstructor->BeginUpdate();
  2482   if (aUpdateType & UPDATE_STYLE)
  2483     mStyleSet->BeginUpdate();
  2486 void
  2487 PresShell::EndUpdate(nsIDocument *aDocument, nsUpdateType aUpdateType)
  2489 #ifdef DEBUG
  2490   NS_PRECONDITION(0 != mUpdateCount, "too many EndUpdate's");
  2491   --mUpdateCount;
  2492 #endif
  2494   if (aUpdateType & UPDATE_STYLE) {
  2495     mStyleSet->EndUpdate();
  2496     if (mStylesHaveChanged || !mChangedScopeStyleRoots.IsEmpty())
  2497       ReconstructStyleData();
  2500   mFrameConstructor->EndUpdate();
  2503 void
  2504 PresShell::RestoreRootScrollPosition()
  2506   nsIScrollableFrame* scrollableFrame = GetRootScrollFrameAsScrollable();
  2507   if (scrollableFrame) {
  2508     scrollableFrame->ScrollToRestoredPosition();
  2512 void
  2513 PresShell::BeginLoad(nsIDocument *aDocument)
  2515   mDocumentLoading = true;
  2517 #ifdef PR_LOGGING
  2518   gfxTextPerfMetrics *tp = nullptr;
  2519   if (mPresContext) {
  2520     tp = mPresContext->GetTextPerfMetrics();
  2523   bool shouldLog = gLog && PR_LOG_TEST(gLog, PR_LOG_DEBUG);
  2524   if (shouldLog || tp) {
  2525     mLoadBegin = TimeStamp::Now();
  2528   if (shouldLog) {
  2529     nsIURI* uri = mDocument->GetDocumentURI();
  2530     nsAutoCString spec;
  2531     if (uri) {
  2532       uri->GetSpec(spec);
  2534     PR_LOG(gLog, PR_LOG_DEBUG,
  2535            ("(presshell) %p load begin [%s]\n",
  2536             this, spec.get()));
  2538 #endif
  2541 void
  2542 PresShell::EndLoad(nsIDocument *aDocument)
  2544   NS_PRECONDITION(aDocument == mDocument, "Wrong document");
  2546   RestoreRootScrollPosition();
  2548   mDocumentLoading = false;
  2551 void
  2552 PresShell::LoadComplete()
  2554 #ifdef PR_LOGGING
  2555   gfxTextPerfMetrics *tp = nullptr;
  2556   if (mPresContext) {
  2557     tp = mPresContext->GetTextPerfMetrics();
  2560   // log load
  2561   bool shouldLog = gLog && PR_LOG_TEST(gLog, PR_LOG_DEBUG);
  2562   if (shouldLog || tp) {
  2563     TimeDuration loadTime = TimeStamp::Now() - mLoadBegin;
  2564     nsIURI* uri = mDocument->GetDocumentURI();
  2565     nsAutoCString spec;
  2566     if (uri) {
  2567       uri->GetSpec(spec);
  2569     if (shouldLog) {
  2570       PR_LOG(gLog, PR_LOG_DEBUG,
  2571              ("(presshell) %p load done time-ms: %9.2f [%s]\n",
  2572               this, loadTime.ToMilliseconds(), spec.get()));
  2574     if (tp) {
  2575       tp->Accumulate();
  2576       if (tp->cumulative.numChars > 0) {
  2577         LogTextPerfStats(tp, this, tp->cumulative, loadTime.ToMilliseconds(),
  2578                          eLog_loaddone, spec.get());
  2582 #endif
  2585 #ifdef DEBUG
  2586 void
  2587 PresShell::VerifyHasDirtyRootAncestor(nsIFrame* aFrame)
  2589   // XXXbz due to bug 372769, can't actually assert anything here...
  2590   return;
  2592   // XXXbz shouldn't need this part; remove it once FrameNeedsReflow
  2593   // handles the root frame correctly.
  2594   if (!aFrame->GetParent()) {
  2595     return;
  2598   // Make sure that there is a reflow root ancestor of |aFrame| that's
  2599   // in mDirtyRoots already.
  2600   while (aFrame && (aFrame->GetStateBits() & NS_FRAME_HAS_DIRTY_CHILDREN)) {
  2601     if (((aFrame->GetStateBits() & NS_FRAME_REFLOW_ROOT) ||
  2602          !aFrame->GetParent()) &&
  2603         mDirtyRoots.Contains(aFrame)) {
  2604       return;
  2607     aFrame = aFrame->GetParent();
  2609   NS_NOTREACHED("Frame has dirty bits set but isn't scheduled to be "
  2610                 "reflowed?");
  2612 #endif
  2614 void
  2615 PresShell::FrameNeedsReflow(nsIFrame *aFrame, IntrinsicDirty aIntrinsicDirty,
  2616                             nsFrameState aBitToAdd)
  2618   NS_PRECONDITION(aBitToAdd == NS_FRAME_IS_DIRTY ||
  2619                   aBitToAdd == NS_FRAME_HAS_DIRTY_CHILDREN ||
  2620                   !aBitToAdd,
  2621                   "Unexpected bits being added");
  2622   NS_PRECONDITION(!(aIntrinsicDirty == eStyleChange &&
  2623                     aBitToAdd == NS_FRAME_HAS_DIRTY_CHILDREN),
  2624                   "bits don't correspond to style change reason");
  2626   NS_ASSERTION(!mIsReflowing, "can't mark frame dirty during reflow");
  2628   // If we've not yet done the initial reflow, then don't bother
  2629   // enqueuing a reflow command yet.
  2630   if (! mDidInitialize)
  2631     return;
  2633   // If we're already destroying, don't bother with this either.
  2634   if (mIsDestroying)
  2635     return;
  2637 #ifdef DEBUG
  2638   //printf("gShellCounter: %d\n", gShellCounter++);
  2639   if (mInVerifyReflow)
  2640     return;
  2642   if (VERIFY_REFLOW_NOISY_RC & gVerifyReflowFlags) {
  2643     printf("\nPresShell@%p: frame %p needs reflow\n", (void*)this, (void*)aFrame);
  2644     if (VERIFY_REFLOW_REALLY_NOISY_RC & gVerifyReflowFlags) {
  2645       printf("Current content model:\n");
  2646       Element *rootElement = mDocument->GetRootElement();
  2647       if (rootElement) {
  2648         rootElement->List(stdout, 0);
  2652 #endif
  2654   nsAutoTArray<nsIFrame*, 4> subtrees;
  2655   subtrees.AppendElement(aFrame);
  2657   do {
  2658     nsIFrame *subtreeRoot = subtrees.ElementAt(subtrees.Length() - 1);
  2659     subtrees.RemoveElementAt(subtrees.Length() - 1);
  2661     // Grab |wasDirty| now so we can go ahead and update the bits on
  2662     // subtreeRoot.
  2663     bool wasDirty = NS_SUBTREE_DIRTY(subtreeRoot);
  2664     subtreeRoot->AddStateBits(aBitToAdd);
  2666     // Now if subtreeRoot is a reflow root we can cut off this reflow at it if
  2667     // the bit being added is NS_FRAME_HAS_DIRTY_CHILDREN.
  2668     bool targetFrameDirty = (aBitToAdd == NS_FRAME_IS_DIRTY);
  2670 #define FRAME_IS_REFLOW_ROOT(_f)                   \
  2671   ((_f->GetStateBits() & NS_FRAME_REFLOW_ROOT) &&  \
  2672    (_f != subtreeRoot || !targetFrameDirty))
  2675     // Mark the intrinsic widths as dirty on the frame, all of its ancestors,
  2676     // and all of its descendants, if needed:
  2678     if (aIntrinsicDirty != eResize) {
  2679       // Mark argument and all ancestors dirty. (Unless we hit a reflow
  2680       // root that should contain the reflow.  That root could be
  2681       // subtreeRoot itself if it's not dirty, or it could be some
  2682       // ancestor of subtreeRoot.)
  2683       for (nsIFrame *a = subtreeRoot;
  2684            a && !FRAME_IS_REFLOW_ROOT(a);
  2685            a = a->GetParent())
  2686         a->MarkIntrinsicWidthsDirty();
  2689     if (aIntrinsicDirty == eStyleChange) {
  2690       // Mark all descendants dirty (using an nsTArray stack rather than
  2691       // recursion).
  2692       // Note that nsHTMLReflowState::InitResizeFlags has some similar
  2693       // code; see comments there for how and why it differs.
  2694       nsAutoTArray<nsIFrame*, 32> stack;
  2695       stack.AppendElement(subtreeRoot);
  2697       do {
  2698         nsIFrame *f = stack.ElementAt(stack.Length() - 1);
  2699         stack.RemoveElementAt(stack.Length() - 1);
  2701         if (f->GetType() == nsGkAtoms::placeholderFrame) {
  2702           nsIFrame *oof = nsPlaceholderFrame::GetRealFrameForPlaceholder(f);
  2703           if (!nsLayoutUtils::IsProperAncestorFrame(subtreeRoot, oof)) {
  2704             // We have another distinct subtree we need to mark.
  2705             subtrees.AppendElement(oof);
  2709         nsIFrame::ChildListIterator lists(f);
  2710         for (; !lists.IsDone(); lists.Next()) {
  2711           nsFrameList::Enumerator childFrames(lists.CurrentList());
  2712           for (; !childFrames.AtEnd(); childFrames.Next()) {
  2713             nsIFrame* kid = childFrames.get();
  2714             kid->MarkIntrinsicWidthsDirty();
  2715             stack.AppendElement(kid);
  2718       } while (stack.Length() != 0);
  2721     // Skip setting dirty bits up the tree if we weren't given a bit to add.
  2722     if (!aBitToAdd) {
  2723       continue;
  2726     // Set NS_FRAME_HAS_DIRTY_CHILDREN bits (via nsIFrame::ChildIsDirty)
  2727     // up the tree until we reach either a frame that's already dirty or
  2728     // a reflow root.
  2729     nsIFrame *f = subtreeRoot;
  2730     for (;;) {
  2731       if (FRAME_IS_REFLOW_ROOT(f) || !f->GetParent()) {
  2732         // we've hit a reflow root or the root frame
  2733         if (!wasDirty) {
  2734           mDirtyRoots.AppendElement(f);
  2735           mDocument->SetNeedLayoutFlush();
  2737 #ifdef DEBUG
  2738         else {
  2739           VerifyHasDirtyRootAncestor(f);
  2741 #endif
  2743         break;
  2746       nsIFrame *child = f;
  2747       f = f->GetParent();
  2748       wasDirty = NS_SUBTREE_DIRTY(f);
  2749       f->ChildIsDirty(child);
  2750       NS_ASSERTION(f->GetStateBits() & NS_FRAME_HAS_DIRTY_CHILDREN,
  2751                    "ChildIsDirty didn't do its job");
  2752       if (wasDirty) {
  2753         // This frame was already marked dirty.
  2754 #ifdef DEBUG
  2755         VerifyHasDirtyRootAncestor(f);
  2756 #endif
  2757         break;
  2760   } while (subtrees.Length() != 0);
  2762   MaybeScheduleReflow();
  2765 void
  2766 PresShell::FrameNeedsToContinueReflow(nsIFrame *aFrame)
  2768   NS_ASSERTION(mIsReflowing, "Must be in reflow when marking path dirty.");
  2769   NS_PRECONDITION(mCurrentReflowRoot, "Must have a current reflow root here");
  2770   NS_ASSERTION(aFrame == mCurrentReflowRoot ||
  2771                nsLayoutUtils::IsProperAncestorFrame(mCurrentReflowRoot, aFrame),
  2772                "Frame passed in is not the descendant of mCurrentReflowRoot");
  2773   NS_ASSERTION(aFrame->GetStateBits() & NS_FRAME_IN_REFLOW,
  2774                "Frame passed in not in reflow?");
  2776   mFramesToDirty.PutEntry(aFrame);
  2779 nsIScrollableFrame*
  2780 nsIPresShell::GetFrameToScrollAsScrollable(
  2781                 nsIPresShell::ScrollDirection aDirection)
  2783   nsIScrollableFrame* scrollFrame = nullptr;
  2785   nsCOMPtr<nsIContent> focusedContent;
  2786   nsIFocusManager* fm = nsFocusManager::GetFocusManager();
  2787   if (fm && mDocument) {
  2788     nsCOMPtr<nsIDOMWindow> window = do_QueryInterface(mDocument->GetWindow());
  2790     nsCOMPtr<nsIDOMElement> focusedElement;
  2791     fm->GetFocusedElementForWindow(window, false, nullptr, getter_AddRefs(focusedElement));
  2792     focusedContent = do_QueryInterface(focusedElement);
  2794   if (!focusedContent && mSelection) {
  2795     nsISelection* domSelection = mSelection->
  2796       GetSelection(nsISelectionController::SELECTION_NORMAL);
  2797     if (domSelection) {
  2798       nsCOMPtr<nsIDOMNode> focusedNode;
  2799       domSelection->GetFocusNode(getter_AddRefs(focusedNode));
  2800       focusedContent = do_QueryInterface(focusedNode);
  2803   if (focusedContent) {
  2804     nsIFrame* startFrame = focusedContent->GetPrimaryFrame();
  2805     if (startFrame) {
  2806       scrollFrame = startFrame->GetScrollTargetFrame();
  2807       if (scrollFrame) {
  2808         startFrame = scrollFrame->GetScrolledFrame();
  2810       if (aDirection == nsIPresShell::eEither) {
  2811         scrollFrame =
  2812           nsLayoutUtils::GetNearestScrollableFrame(startFrame);
  2813       } else {
  2814         scrollFrame =
  2815           nsLayoutUtils::GetNearestScrollableFrameForDirection(startFrame,
  2816             aDirection == eVertical ? nsLayoutUtils::eVertical :
  2817                                       nsLayoutUtils::eHorizontal);
  2821   if (!scrollFrame) {
  2822     scrollFrame = GetRootScrollFrameAsScrollable();
  2824   return scrollFrame;
  2827 void
  2828 PresShell::CancelAllPendingReflows()
  2830   mDirtyRoots.Clear();
  2832   if (mReflowScheduled) {
  2833     GetPresContext()->RefreshDriver()->RemoveLayoutFlushObserver(this);
  2834     mReflowScheduled = false;
  2837   ASSERT_REFLOW_SCHEDULED_STATE();
  2840 nsresult
  2841 PresShell::RecreateFramesFor(nsIContent* aContent)
  2843   NS_ENSURE_TRUE(mPresContext, NS_ERROR_FAILURE);
  2844   if (!mDidInitialize) {
  2845     // Nothing to do here.  In fact, if we proceed and aContent is the
  2846     // root we will crash.
  2847     return NS_OK;
  2850   // Don't call RecreateFramesForContent since that is not exported and we want
  2851   // to keep the number of entrypoints down.
  2853   NS_ASSERTION(mViewManager, "Should have view manager");
  2855   // Have to make sure that the content notifications are flushed before we
  2856   // start messing with the frame model; otherwise we can get content doubling.
  2857   mDocument->FlushPendingNotifications(Flush_ContentAndNotify);
  2859   nsAutoScriptBlocker scriptBlocker;
  2861   nsStyleChangeList changeList;
  2862   changeList.AppendChange(nullptr, aContent, nsChangeHint_ReconstructFrame);
  2864   // Mark ourselves as not safe to flush while we're doing frame construction.
  2865   ++mChangeNestCount;
  2866   RestyleManager* restyleManager = mPresContext->RestyleManager();
  2867   nsresult rv = restyleManager->ProcessRestyledFrames(changeList);
  2868   restyleManager->FlushOverflowChangedTracker();
  2869   --mChangeNestCount;
  2871   return rv;
  2874 void
  2875 nsIPresShell::PostRecreateFramesFor(Element* aElement)
  2877   mPresContext->RestyleManager()->PostRestyleEvent(aElement, nsRestyleHint(0),
  2878                                                    nsChangeHint_ReconstructFrame);
  2881 void
  2882 nsIPresShell::RestyleForAnimation(Element* aElement, nsRestyleHint aHint)
  2884   mPresContext->RestyleManager()->PostAnimationRestyleEvent(aElement, aHint,
  2885                                                             NS_STYLE_HINT_NONE);
  2888 void
  2889 nsIPresShell::SetForwardingContainer(const WeakPtr<nsDocShell> &aContainer)
  2891   mForwardingContainer = aContainer;
  2894 void
  2895 PresShell::ClearFrameRefs(nsIFrame* aFrame)
  2897   mPresContext->EventStateManager()->ClearFrameRefs(aFrame);
  2899   nsWeakFrame* weakFrame = mWeakFrames;
  2900   while (weakFrame) {
  2901     nsWeakFrame* prev = weakFrame->GetPreviousWeakFrame();
  2902     if (weakFrame->GetFrame() == aFrame) {
  2903       // This removes weakFrame from mWeakFrames.
  2904       weakFrame->Clear(this);
  2906     weakFrame = prev;
  2910 already_AddRefed<nsRenderingContext>
  2911 PresShell::CreateReferenceRenderingContext()
  2913   nsDeviceContext* devCtx = mPresContext->DeviceContext();
  2914   nsRefPtr<nsRenderingContext> rc;
  2915   if (mPresContext->IsScreen()) {
  2916     rc = new nsRenderingContext();
  2917     rc->Init(devCtx, gfxPlatform::GetPlatform()->ScreenReferenceSurface());
  2918   } else {
  2919     rc = devCtx->CreateRenderingContext();
  2922   MOZ_ASSERT(rc, "shouldn't break promise to return non-null");
  2923   return rc.forget();
  2926 nsresult
  2927 PresShell::GoToAnchor(const nsAString& aAnchorName, bool aScroll)
  2929   if (!mDocument) {
  2930     return NS_ERROR_FAILURE;
  2933   const Element *root = mDocument->GetRootElement();
  2934   if (root && root->IsSVG(nsGkAtoms::svg)) {
  2935     // We need to execute this even if there is an empty anchor name
  2936     // so that any existing SVG fragment identifier effect is removed
  2937     if (SVGFragmentIdentifier::ProcessFragmentIdentifier(mDocument, aAnchorName)) {
  2938       return NS_OK;
  2942   // Hold a reference to the ESM in case event dispatch tears us down.
  2943   nsRefPtr<EventStateManager> esm = mPresContext->EventStateManager();
  2945   if (aAnchorName.IsEmpty()) {
  2946     NS_ASSERTION(!aScroll, "can't scroll to empty anchor name");
  2947     esm->SetContentState(nullptr, NS_EVENT_STATE_URLTARGET);
  2948     return NS_OK;
  2951   nsCOMPtr<nsIDOMHTMLDocument> htmlDoc = do_QueryInterface(mDocument);
  2952   nsresult rv = NS_OK;
  2953   nsCOMPtr<nsIContent> content;
  2955   // Search for an element with a matching "id" attribute
  2956   if (mDocument) {
  2957     content = mDocument->GetElementById(aAnchorName);
  2960   // Search for an anchor element with a matching "name" attribute
  2961   if (!content && htmlDoc) {
  2962     nsCOMPtr<nsIDOMNodeList> list;
  2963     // Find a matching list of named nodes
  2964     rv = htmlDoc->GetElementsByName(aAnchorName, getter_AddRefs(list));
  2965     if (NS_SUCCEEDED(rv) && list) {
  2966       uint32_t i;
  2967       // Loop through the named nodes looking for the first anchor
  2968       for (i = 0; true; i++) {
  2969         nsCOMPtr<nsIDOMNode> node;
  2970         rv = list->Item(i, getter_AddRefs(node));
  2971         if (!node) {  // End of list
  2972           break;
  2974         // Ensure it's an anchor element
  2975         content = do_QueryInterface(node);
  2976         if (content) {
  2977           if (content->Tag() == nsGkAtoms::a && content->IsHTML()) {
  2978             break;
  2980           content = nullptr;
  2986   // Search for anchor in the HTML namespace with a matching name
  2987   if (!content && !htmlDoc)
  2989     nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(mDocument);
  2990     nsCOMPtr<nsIDOMNodeList> list;
  2991     NS_NAMED_LITERAL_STRING(nameSpace, "http://www.w3.org/1999/xhtml");
  2992     // Get the list of anchor elements
  2993     rv = doc->GetElementsByTagNameNS(nameSpace, NS_LITERAL_STRING("a"), getter_AddRefs(list));
  2994     if (NS_SUCCEEDED(rv) && list) {
  2995       uint32_t i;
  2996       // Loop through the named nodes looking for the first anchor
  2997       for (i = 0; true; i++) {
  2998         nsCOMPtr<nsIDOMNode> node;
  2999         rv = list->Item(i, getter_AddRefs(node));
  3000         if (!node) { // End of list
  3001           break;
  3003         // Compare the name attribute
  3004         nsCOMPtr<nsIDOMElement> element = do_QueryInterface(node);
  3005         nsAutoString value;
  3006         if (element && NS_SUCCEEDED(element->GetAttribute(NS_LITERAL_STRING("name"), value))) {
  3007           if (value.Equals(aAnchorName)) {
  3008             content = do_QueryInterface(element);
  3009             break;
  3016   esm->SetContentState(content, NS_EVENT_STATE_URLTARGET);
  3018 #ifdef ACCESSIBILITY
  3019   nsIContent *anchorTarget = content;
  3020 #endif
  3022   nsIScrollableFrame* rootScroll = GetRootScrollFrameAsScrollable();
  3023   if (rootScroll && rootScroll->DidHistoryRestore()) {
  3024     // Scroll position restored from history trumps scrolling to anchor.
  3025     aScroll = false;
  3026     rootScroll->ClearDidHistoryRestore();
  3029   if (content) {
  3030     if (aScroll) {
  3031       rv = ScrollContentIntoView(content,
  3032                                  ScrollAxis(SCROLL_TOP, SCROLL_ALWAYS),
  3033                                  ScrollAxis(),
  3034                                  ANCHOR_SCROLL_FLAGS);
  3035       NS_ENSURE_SUCCESS(rv, rv);
  3037       nsIScrollableFrame* rootScroll = GetRootScrollFrameAsScrollable();
  3038       if (rootScroll) {
  3039         mLastAnchorScrolledTo = content;
  3040         mLastAnchorScrollPositionY = rootScroll->GetScrollPosition().y;
  3044     // Should we select the target? This action is controlled by a
  3045     // preference: the default is to not select.
  3046     bool selectAnchor = Preferences::GetBool("layout.selectanchor");
  3048     // Even if select anchor pref is false, we must still move the
  3049     // caret there. That way tabbing will start from the new
  3050     // location
  3051     nsRefPtr<nsIDOMRange> jumpToRange = new nsRange(mDocument);
  3052     while (content && content->GetFirstChild()) {
  3053       content = content->GetFirstChild();
  3055     nsCOMPtr<nsIDOMNode> node(do_QueryInterface(content));
  3056     NS_ASSERTION(node, "No nsIDOMNode for descendant of anchor");
  3057     jumpToRange->SelectNodeContents(node);
  3058     // Select the anchor
  3059     nsISelection* sel = mSelection->
  3060       GetSelection(nsISelectionController::SELECTION_NORMAL);
  3061     if (sel) {
  3062       sel->RemoveAllRanges();
  3063       sel->AddRange(jumpToRange);
  3064       if (!selectAnchor) {
  3065         // Use a caret (collapsed selection) at the start of the anchor
  3066         sel->CollapseToStart();
  3069     // Selection is at anchor.
  3070     // Now focus the document itself if focus is on an element within it.
  3071     nsPIDOMWindow *win = mDocument->GetWindow();
  3073     nsIFocusManager* fm = nsFocusManager::GetFocusManager();
  3074     if (fm && win) {
  3075       nsCOMPtr<nsIDOMWindow> focusedWindow;
  3076       fm->GetFocusedWindow(getter_AddRefs(focusedWindow));
  3077       if (SameCOMIdentity(win, focusedWindow)) {
  3078         fm->ClearFocus(focusedWindow);
  3082     // If the target is an animation element, activate the animation
  3083     if (content->IsNodeOfType(nsINode::eANIMATION)) {
  3084       SVGContentUtils::ActivateByHyperlink(content.get());
  3086   } else {
  3087     rv = NS_ERROR_FAILURE;
  3088     NS_NAMED_LITERAL_STRING(top, "top");
  3089     if (nsContentUtils::EqualsIgnoreASCIICase(aAnchorName, top)) {
  3090       // Scroll to the top/left if aAnchorName is "top" and there is no element
  3091       // with such a name or id.
  3092       rv = NS_OK;
  3093       nsIScrollableFrame* sf = GetRootScrollFrameAsScrollable();
  3094       // Check |aScroll| after setting |rv| so we set |rv| to the same
  3095       // thing whether or not |aScroll| is true.
  3096       if (aScroll && sf) {
  3097         // Scroll to the top of the page
  3098         sf->ScrollTo(nsPoint(0, 0), nsIScrollableFrame::INSTANT);
  3103 #ifdef ACCESSIBILITY
  3104   if (anchorTarget) {
  3105     nsAccessibilityService* accService = AccService();
  3106     if (accService)
  3107       accService->NotifyOfAnchorJumpTo(anchorTarget);
  3109 #endif
  3111   return rv;
  3114 nsresult
  3115 PresShell::ScrollToAnchor()
  3117   if (!mLastAnchorScrolledTo) {
  3118     return NS_OK;
  3120   NS_ASSERTION(mDidInitialize, "should have done initial reflow by now");
  3122   nsIScrollableFrame* rootScroll = GetRootScrollFrameAsScrollable();
  3123   if (!rootScroll ||
  3124       mLastAnchorScrollPositionY != rootScroll->GetScrollPosition().y) {
  3125     return NS_OK;
  3127   nsresult rv = ScrollContentIntoView(mLastAnchorScrolledTo,
  3128                                       ScrollAxis(SCROLL_TOP, SCROLL_ALWAYS),
  3129                                       ScrollAxis(),
  3130                                       ANCHOR_SCROLL_FLAGS);
  3131   mLastAnchorScrolledTo = nullptr;
  3132   return rv;
  3135 /*
  3136  * Helper (per-continuation) for ScrollContentIntoView.
  3138  * @param aContainerFrame [in] the frame which aRect is relative to
  3139  * @param aFrame [in] Frame whose bounds should be unioned
  3140  * @param aUseWholeLineHeightForInlines [in] if true, then for inline frames
  3141  * we should include the top of the line in the added rectangle
  3142  * @param aRect [inout] rect into which its bounds should be unioned
  3143  * @param aHaveRect [inout] whether aRect contains data yet
  3144  * @param aPrevBlock [inout] the block aLines is a line iterator for
  3145  * @param aLines [inout] the line iterator we're using
  3146  * @param aCurLine [inout] the line to start looking from in this iterator
  3147  */
  3148 static void
  3149 AccumulateFrameBounds(nsIFrame* aContainerFrame,
  3150                       nsIFrame* aFrame,
  3151                       bool aUseWholeLineHeightForInlines,
  3152                       nsRect& aRect,
  3153                       bool& aHaveRect,
  3154                       nsIFrame*& aPrevBlock,
  3155                       nsAutoLineIterator& aLines,
  3156                       int32_t& aCurLine)
  3158   nsIFrame* frame = aFrame;
  3159   nsRect frameBounds = nsRect(nsPoint(0, 0), aFrame->GetSize());
  3161   // If this is an inline frame and either the bounds height is 0 (quirks
  3162   // layout model) or aUseWholeLineHeightForInlines is set, we need to
  3163   // change the top of the bounds to include the whole line.
  3164   if (frameBounds.height == 0 || aUseWholeLineHeightForInlines) {
  3165     nsIFrame *prevFrame = aFrame;
  3166     nsIFrame *f = aFrame;
  3168     while (f && f->IsFrameOfType(nsIFrame::eLineParticipant) &&
  3169            !f->IsTransformed() && !f->IsPositioned()) {
  3170       prevFrame = f;
  3171       f = prevFrame->GetParent();
  3174     if (f != aFrame &&
  3175         f &&
  3176         f->GetType() == nsGkAtoms::blockFrame) {
  3177       // find the line containing aFrame and increase the top of |offset|.
  3178       if (f != aPrevBlock) {
  3179         aLines = f->GetLineIterator();
  3180         aPrevBlock = f;
  3181         aCurLine = 0;
  3183       if (aLines) {
  3184         int32_t index = aLines->FindLineContaining(prevFrame, aCurLine);
  3185         if (index >= 0) {
  3186           aCurLine = index;
  3187           nsIFrame *trash1;
  3188           int32_t trash2;
  3189           nsRect lineBounds;
  3190           uint32_t trash3;
  3192           if (NS_SUCCEEDED(aLines->GetLine(index, &trash1, &trash2,
  3193                                            lineBounds, &trash3))) {
  3194             frameBounds += frame->GetOffsetTo(f);
  3195             frame = f;
  3196             if (lineBounds.y < frameBounds.y) {
  3197               frameBounds.height = frameBounds.YMost() - lineBounds.y;
  3198               frameBounds.y = lineBounds.y;
  3206   nsRect transformedBounds = nsLayoutUtils::TransformFrameRectToAncestor(frame,
  3207     frameBounds, aContainerFrame);
  3209   if (aHaveRect) {
  3210     // We can't use nsRect::UnionRect since it drops empty rects on
  3211     // the floor, and we need to include them.  (Thus we need
  3212     // aHaveRect to know when to drop the initial value on the floor.)
  3213     aRect.UnionRectEdges(aRect, transformedBounds);
  3214   } else {
  3215     aHaveRect = true;
  3216     aRect = transformedBounds;
  3220 static bool
  3221 ComputeNeedToScroll(nsIPresShell::WhenToScroll aWhenToScroll,
  3222                     nscoord                    aLineSize,
  3223                     nscoord                    aRectMin,
  3224                     nscoord                    aRectMax,
  3225                     nscoord                    aViewMin,
  3226                     nscoord                    aViewMax) {
  3227   // See how the rect should be positioned vertically
  3228   if (nsIPresShell::SCROLL_ALWAYS == aWhenToScroll) {
  3229     // The caller wants the frame as visible as possible
  3230     return true;
  3231   } else if (nsIPresShell::SCROLL_IF_NOT_VISIBLE == aWhenToScroll) {
  3232     // Scroll only if no part of the frame is visible in this view
  3233     return aRectMax - aLineSize <= aViewMin ||
  3234            aRectMin + aLineSize >= aViewMax;
  3235   } else if (nsIPresShell::SCROLL_IF_NOT_FULLY_VISIBLE == aWhenToScroll) {
  3236     // Scroll only if part of the frame is hidden and more can fit in view
  3237     return !(aRectMin >= aViewMin && aRectMax <= aViewMax) &&
  3238       std::min(aViewMax, aRectMax) - std::max(aRectMin, aViewMin) < aViewMax - aViewMin;
  3240   return false;
  3243 static nscoord
  3244 ComputeWhereToScroll(int16_t aWhereToScroll,
  3245                      nscoord aOriginalCoord,
  3246                      nscoord aRectMin,
  3247                      nscoord aRectMax,
  3248                      nscoord aViewMin,
  3249                      nscoord aViewMax,
  3250                      nscoord* aRangeMin,
  3251                      nscoord* aRangeMax) {
  3252   nscoord resultCoord = aOriginalCoord;
  3253   // Allow the scroll operation to land anywhere that
  3254   // makes the whole rectangle visible.
  3255   if (nsIPresShell::SCROLL_MINIMUM == aWhereToScroll) {
  3256     if (aRectMin < aViewMin) {
  3257       // Scroll up so the frame's top edge is visible
  3258       resultCoord = aRectMin;
  3259     } else if (aRectMax > aViewMax) {
  3260       // Scroll down so the frame's bottom edge is visible. Make sure the
  3261       // frame's top edge is still visible
  3262       resultCoord = aOriginalCoord + aRectMax - aViewMax;
  3263       if (resultCoord > aRectMin) {
  3264         resultCoord = aRectMin;
  3267   } else {
  3268     nscoord frameAlignCoord =
  3269       NSToCoordRound(aRectMin + (aRectMax - aRectMin) * (aWhereToScroll / 100.0f));
  3270     resultCoord =  NSToCoordRound(frameAlignCoord - (aViewMax - aViewMin) * (
  3271                                   aWhereToScroll / 100.0f));
  3273   nscoord scrollPortLength = aViewMax - aViewMin;
  3274   // Force the scroll range to extend to include resultCoord.
  3275   *aRangeMin = std::min(resultCoord, aRectMax - scrollPortLength);
  3276   *aRangeMax = std::max(resultCoord, aRectMin);
  3277   return resultCoord;
  3280 /**
  3281  * This function takes a scrollable frame, a rect in the coordinate system
  3282  * of the scrolled frame, and a desired percentage-based scroll
  3283  * position and attempts to scroll the rect to that position in the
  3284  * scrollport.
  3286  * This needs to work even if aRect has a width or height of zero.
  3287  */
  3288 static void ScrollToShowRect(nsIFrame*                aFrame,
  3289                              nsIScrollableFrame*      aFrameAsScrollable,
  3290                              const nsRect&            aRect,
  3291                              nsIPresShell::ScrollAxis aVertical,
  3292                              nsIPresShell::ScrollAxis aHorizontal,
  3293                              uint32_t                 aFlags)
  3295   nsPoint scrollPt = aFrameAsScrollable->GetScrollPosition();
  3296   nsRect visibleRect(scrollPt,
  3297                      aFrameAsScrollable->GetScrollPositionClampingScrollPortSize());
  3299   // If this is the root scroll frame, make sure to take into account the
  3300   // content document fixed position margins. When set, these indicate that
  3301   // chrome is obscuring the viewport.
  3302   nsRect targetRect(aRect);
  3303   nsIPresShell *presShell = aFrame->PresContext()->PresShell();
  3304   if (aFrameAsScrollable == presShell->GetRootScrollFrameAsScrollable()) {
  3305     targetRect.Inflate(presShell->GetContentDocumentFixedPositionMargins());
  3308   nsSize lineSize;
  3309   // Don't call GetLineScrollAmount unless we actually need it. Not only
  3310   // does this save time, but it's not safe to call GetLineScrollAmount
  3311   // during reflow (because it depends on font size inflation and doesn't
  3312   // use the in-reflow-safe font-size inflation path). If we did call it,
  3313   // it would assert and possible give the wrong result.
  3314   if (aVertical.mWhenToScroll == nsIPresShell::SCROLL_IF_NOT_VISIBLE ||
  3315       aHorizontal.mWhenToScroll == nsIPresShell::SCROLL_IF_NOT_VISIBLE) {
  3316     lineSize = aFrameAsScrollable->GetLineScrollAmount();
  3318   ScrollbarStyles ss = aFrameAsScrollable->GetScrollbarStyles();
  3319   nsRect allowedRange(scrollPt, nsSize(0, 0));
  3320   bool needToScroll = false;
  3321   uint32_t directions = aFrameAsScrollable->GetPerceivedScrollingDirections();
  3323   if (((aFlags & nsIPresShell::SCROLL_OVERFLOW_HIDDEN) ||
  3324        ss.mVertical != NS_STYLE_OVERFLOW_HIDDEN) &&
  3325       (!aVertical.mOnlyIfPerceivedScrollableDirection ||
  3326        (directions & nsIScrollableFrame::VERTICAL))) {
  3328     if (ComputeNeedToScroll(aVertical.mWhenToScroll,
  3329                             lineSize.height,
  3330                             targetRect.y,
  3331                             targetRect.YMost(),
  3332                             visibleRect.y,
  3333                             visibleRect.YMost())) {
  3334       nscoord maxHeight;
  3335       scrollPt.y = ComputeWhereToScroll(aVertical.mWhereToScroll,
  3336                                         scrollPt.y,
  3337                                         targetRect.y,
  3338                                         targetRect.YMost(),
  3339                                         visibleRect.y,
  3340                                         visibleRect.YMost(),
  3341                                         &allowedRange.y, &maxHeight);
  3342       allowedRange.height = maxHeight - allowedRange.y;
  3343       needToScroll = true;
  3347   if (((aFlags & nsIPresShell::SCROLL_OVERFLOW_HIDDEN) ||
  3348        ss.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN) &&
  3349       (!aHorizontal.mOnlyIfPerceivedScrollableDirection ||
  3350        (directions & nsIScrollableFrame::HORIZONTAL))) {
  3352     if (ComputeNeedToScroll(aHorizontal.mWhenToScroll,
  3353                             lineSize.width,
  3354                             targetRect.x,
  3355                             targetRect.XMost(),
  3356                             visibleRect.x,
  3357                             visibleRect.XMost())) {
  3358       nscoord maxWidth;
  3359       scrollPt.x = ComputeWhereToScroll(aHorizontal.mWhereToScroll,
  3360                                         scrollPt.x,
  3361                                         targetRect.x,
  3362                                         targetRect.XMost(),
  3363                                         visibleRect.x,
  3364                                         visibleRect.XMost(),
  3365                                         &allowedRange.x, &maxWidth);
  3366       allowedRange.width = maxWidth - allowedRange.x;
  3367       needToScroll = true;
  3371   // If we don't need to scroll, then don't try since it might cancel
  3372   // a current smooth scroll operation.
  3373   if (needToScroll) {
  3374     aFrameAsScrollable->ScrollTo(scrollPt, nsIScrollableFrame::INSTANT, &allowedRange);
  3378 nsresult
  3379 PresShell::ScrollContentIntoView(nsIContent*              aContent,
  3380                                  nsIPresShell::ScrollAxis aVertical,
  3381                                  nsIPresShell::ScrollAxis aHorizontal,
  3382                                  uint32_t                 aFlags)
  3384   NS_ENSURE_TRUE(aContent, NS_ERROR_NULL_POINTER);
  3385   nsCOMPtr<nsIDocument> currentDoc = aContent->GetCurrentDoc();
  3386   NS_ENSURE_STATE(currentDoc);
  3388   NS_ASSERTION(mDidInitialize, "should have done initial reflow by now");
  3390   if (mContentToScrollTo) {
  3391     mContentToScrollTo->DeleteProperty(nsGkAtoms::scrolling);
  3393   mContentToScrollTo = aContent;
  3394   ScrollIntoViewData* data = new ScrollIntoViewData();
  3395   data->mContentScrollVAxis = aVertical;
  3396   data->mContentScrollHAxis = aHorizontal;
  3397   data->mContentToScrollToFlags = aFlags;
  3398   if (NS_FAILED(mContentToScrollTo->SetProperty(nsGkAtoms::scrolling, data,
  3399                                                 nsINode::DeleteProperty<PresShell::ScrollIntoViewData>))) {
  3400     mContentToScrollTo = nullptr;
  3403   // Flush layout and attempt to scroll in the process.
  3404   currentDoc->SetNeedLayoutFlush();
  3405   currentDoc->FlushPendingNotifications(Flush_InterruptibleLayout);
  3407   // If mContentToScrollTo is non-null, that means we interrupted the reflow
  3408   // (or suppressed it altogether because we're suppressing interruptible
  3409   // flushes right now) and won't necessarily get the position correct, but do
  3410   // a best-effort scroll here.  The other option would be to do this inside
  3411   // FlushPendingNotifications, but I'm not sure the repeated scrolling that
  3412   // could trigger if reflows keep getting interrupted would be more desirable
  3413   // than a single best-effort scroll followed by one final scroll on the first
  3414   // completed reflow.
  3415   if (mContentToScrollTo) {
  3416     DoScrollContentIntoView();
  3418   return NS_OK;
  3421 void
  3422 PresShell::DoScrollContentIntoView()
  3424   NS_ASSERTION(mDidInitialize, "should have done initial reflow by now");
  3426   nsIFrame* frame = mContentToScrollTo->GetPrimaryFrame();
  3427   if (!frame) {
  3428     mContentToScrollTo->DeleteProperty(nsGkAtoms::scrolling);
  3429     mContentToScrollTo = nullptr;
  3430     return;
  3433   if (frame->GetStateBits() & NS_FRAME_FIRST_REFLOW) {
  3434     // The reflow flush before this scroll got interrupted, and this frame's
  3435     // coords and size are all zero, and it has no content showing anyway.
  3436     // Don't bother scrolling to it.  We'll try again when we finish up layout.
  3437     return;
  3440   // Make sure we skip 'frame' ... if it's scrollable, we should use its
  3441   // scrollable ancestor as the container.
  3442   nsIFrame* container =
  3443     nsLayoutUtils::GetClosestFrameOfType(frame->GetParent(), nsGkAtoms::scrollFrame);
  3444   if (!container) {
  3445     // nothing can be scrolled
  3446     return;
  3449   ScrollIntoViewData* data = static_cast<ScrollIntoViewData*>(
  3450     mContentToScrollTo->GetProperty(nsGkAtoms::scrolling));
  3451   if (MOZ_UNLIKELY(!data)) {
  3452     mContentToScrollTo = nullptr;
  3453     return;
  3456   // This is a two-step process.
  3457   // Step 1: Find the bounds of the rect we want to scroll into view.  For
  3458   //         example, for an inline frame we may want to scroll in the whole
  3459   //         line, or we may want to scroll multiple lines into view.
  3460   // Step 2: Walk container frame and its ancestors and scroll them
  3461   //         appropriately.
  3462   // frameBounds is relative to container. We're assuming
  3463   // that scrollframes don't split so every continuation of frame will
  3464   // be a descendant of container. (Things would still mostly work
  3465   // even if that assumption was false.)
  3466   nsRect frameBounds;
  3467   bool haveRect = false;
  3468   bool useWholeLineHeightForInlines =
  3469     data->mContentScrollVAxis.mWhenToScroll != nsIPresShell::SCROLL_IF_NOT_FULLY_VISIBLE;
  3470   // Reuse the same line iterator across calls to AccumulateFrameBounds.  We set
  3471   // it every time we detect a new block (stored in prevBlock).
  3472   nsIFrame* prevBlock = nullptr;
  3473   nsAutoLineIterator lines;
  3474   // The last line we found a continuation on in |lines|.  We assume that later
  3475   // continuations cannot come on earlier lines.
  3476   int32_t curLine = 0;
  3477   do {
  3478     AccumulateFrameBounds(container, frame, useWholeLineHeightForInlines,
  3479                           frameBounds, haveRect, prevBlock, lines, curLine);
  3480   } while ((frame = frame->GetNextContinuation()));
  3482   ScrollFrameRectIntoView(container, frameBounds, data->mContentScrollVAxis,
  3483                           data->mContentScrollHAxis,
  3484                           data->mContentToScrollToFlags);
  3487 bool
  3488 PresShell::ScrollFrameRectIntoView(nsIFrame*                aFrame,
  3489                                    const nsRect&            aRect,
  3490                                    nsIPresShell::ScrollAxis aVertical,
  3491                                    nsIPresShell::ScrollAxis aHorizontal,
  3492                                    uint32_t                 aFlags)
  3494   bool didScroll = false;
  3495   // This function needs to work even if rect has a width or height of 0.
  3496   nsRect rect = aRect;
  3497   nsIFrame* container = aFrame;
  3498   // Walk up the frame hierarchy scrolling the rect into view and
  3499   // keeping rect relative to container
  3500   do {
  3501     nsIScrollableFrame* sf = do_QueryFrame(container);
  3502     if (sf) {
  3503       nsPoint oldPosition = sf->GetScrollPosition();
  3504       nsRect targetRect = rect;
  3505       if (container->StyleDisplay()->mOverflowClipBox ==
  3506             NS_STYLE_OVERFLOW_CLIP_BOX_CONTENT_BOX) {
  3507         nsMargin padding = container->GetUsedPadding();
  3508         targetRect.Inflate(padding);
  3510       ScrollToShowRect(container, sf, targetRect - sf->GetScrolledFrame()->GetPosition(),
  3511                        aVertical, aHorizontal, aFlags);
  3512       nsPoint newPosition = sf->GetScrollPosition();
  3513       // If the scroll position increased, that means our content moved up,
  3514       // so our rect's offset should decrease
  3515       rect += oldPosition - newPosition;
  3517       if (oldPosition != newPosition) {
  3518         didScroll = true;
  3521       // only scroll one container when this flag is set
  3522       if (aFlags & nsIPresShell::SCROLL_FIRST_ANCESTOR_ONLY) {
  3523         break;
  3526     nsIFrame* parent;
  3527     if (container->IsTransformed()) {
  3528       container->GetTransformMatrix(nullptr, &parent);
  3529       rect = nsLayoutUtils::TransformFrameRectToAncestor(container, rect, parent);
  3530     } else {
  3531       rect += container->GetPosition();
  3532       parent = container->GetParent();
  3534     if (!parent && !(aFlags & nsIPresShell::SCROLL_NO_PARENT_FRAMES)) {
  3535       nsPoint extraOffset(0,0);
  3536       parent = nsLayoutUtils::GetCrossDocParentFrame(container, &extraOffset);
  3537       if (parent) {
  3538         int32_t APD = container->PresContext()->AppUnitsPerDevPixel();
  3539         int32_t parentAPD = parent->PresContext()->AppUnitsPerDevPixel();
  3540         rect = rect.ConvertAppUnitsRoundOut(APD, parentAPD);
  3541         rect += extraOffset;
  3544     container = parent;
  3545   } while (container);
  3547   return didScroll;
  3550 nsRectVisibility
  3551 PresShell::GetRectVisibility(nsIFrame* aFrame,
  3552                              const nsRect &aRect,
  3553                              nscoord aMinTwips) const
  3555   NS_ASSERTION(aFrame->PresContext() == GetPresContext(),
  3556                "prescontext mismatch?");
  3557   nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
  3558   NS_ASSERTION(rootFrame,
  3559                "How can someone have a frame for this presshell when there's no root?");
  3560   nsIScrollableFrame* sf = GetRootScrollFrameAsScrollable();
  3561   nsRect scrollPortRect;
  3562   if (sf) {
  3563     scrollPortRect = sf->GetScrollPortRect();
  3564     nsIFrame* f = do_QueryFrame(sf);
  3565     scrollPortRect += f->GetOffsetTo(rootFrame);
  3566   } else {
  3567     scrollPortRect = nsRect(nsPoint(0,0), rootFrame->GetSize());
  3570   nsRect r = aRect + aFrame->GetOffsetTo(rootFrame);
  3571   // If aRect is entirely visible then we don't need to ensure that
  3572   // at least aMinTwips of it is visible
  3573   if (scrollPortRect.Contains(r))
  3574     return nsRectVisibility_kVisible;
  3576   nsRect insetRect = scrollPortRect;
  3577   insetRect.Deflate(aMinTwips, aMinTwips);
  3578   if (r.YMost() <= insetRect.y)
  3579     return nsRectVisibility_kAboveViewport;
  3580   if (r.y >= insetRect.YMost())
  3581     return nsRectVisibility_kBelowViewport;
  3582   if (r.XMost() <= insetRect.x)
  3583     return nsRectVisibility_kLeftOfViewport;
  3584   if (r.x >= insetRect.XMost())
  3585     return nsRectVisibility_kRightOfViewport;
  3587   return nsRectVisibility_kVisible;
  3590 class PaintTimerCallBack MOZ_FINAL : public nsITimerCallback
  3592 public:
  3593   PaintTimerCallBack(PresShell* aShell) : mShell(aShell) {}
  3595   NS_DECL_ISUPPORTS
  3597   NS_IMETHODIMP Notify(nsITimer* aTimer) MOZ_FINAL
  3599     mShell->SetNextPaintCompressed();
  3600     mShell->AddInvalidateHiddenPresShellObserver(mShell->GetPresContext()->RefreshDriver());
  3601     mShell->ScheduleViewManagerFlush();
  3602     return NS_OK;
  3605 private:
  3606   PresShell* mShell;
  3607 };
  3609 NS_IMPL_ISUPPORTS(PaintTimerCallBack, nsITimerCallback)
  3611 void
  3612 PresShell::ScheduleViewManagerFlush(PaintType aType)
  3614   if (aType == PAINT_DELAYED_COMPRESS) {
  3615     // Delay paint for 1 second.
  3616     static const uint32_t kPaintDelayPeriod = 1000;
  3617     if (!mDelayedPaintTimer) {
  3618       mDelayedPaintTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
  3619       nsRefPtr<PaintTimerCallBack> cb = new PaintTimerCallBack(this);
  3620       mDelayedPaintTimer->InitWithCallback(cb, kPaintDelayPeriod, nsITimer::TYPE_ONE_SHOT);
  3622     return;
  3625   nsPresContext* presContext = GetPresContext();
  3626   if (presContext) {
  3627     presContext->RefreshDriver()->ScheduleViewManagerFlush();
  3629   if (mDocument) {
  3630     mDocument->SetNeedLayoutFlush();
  3634 void
  3635 PresShell::DispatchSynthMouseMove(WidgetGUIEvent* aEvent,
  3636                                   bool aFlushOnHoverChange)
  3638   RestyleManager* restyleManager = mPresContext->RestyleManager();
  3639   uint32_t hoverGenerationBefore = restyleManager->GetHoverGeneration();
  3640   nsEventStatus status;
  3641   nsView* targetView = nsView::GetViewFor(aEvent->widget);
  3642   if (!targetView)
  3643     return;
  3644   targetView->GetViewManager()->DispatchEvent(aEvent, targetView, &status);
  3645   if (MOZ_UNLIKELY(mIsDestroying)) {
  3646     return;
  3648   if (aFlushOnHoverChange &&
  3649       hoverGenerationBefore != restyleManager->GetHoverGeneration()) {
  3650     // Flush so that the resulting reflow happens now so that our caller
  3651     // can suppress any synthesized mouse moves caused by that reflow.
  3652     FlushPendingNotifications(Flush_Layout);
  3656 void
  3657 PresShell::ClearMouseCaptureOnView(nsView* aView)
  3659   if (gCaptureInfo.mContent) {
  3660     if (aView) {
  3661       // if a view was specified, ensure that the captured content is within
  3662       // this view.
  3663       nsIFrame* frame = gCaptureInfo.mContent->GetPrimaryFrame();
  3664       if (frame) {
  3665         nsView* view = frame->GetClosestView();
  3666         // if there is no view, capturing won't be handled any more, so
  3667         // just release the capture.
  3668         if (view) {
  3669           do {
  3670             if (view == aView) {
  3671               NS_RELEASE(gCaptureInfo.mContent);
  3672               // the view containing the captured content likely disappeared so
  3673               // disable capture for now.
  3674               gCaptureInfo.mAllowed = false;
  3675               break;
  3678             view = view->GetParent();
  3679           } while (view);
  3680           // return if the view wasn't found
  3681           return;
  3686     NS_RELEASE(gCaptureInfo.mContent);
  3689   // disable mouse capture until the next mousedown as a dialog has opened
  3690   // or a drag has started. Otherwise, someone could start capture during
  3691   // the modal dialog or drag.
  3692   gCaptureInfo.mAllowed = false;
  3695 void
  3696 nsIPresShell::ClearMouseCapture(nsIFrame* aFrame)
  3698   if (!gCaptureInfo.mContent) {
  3699     gCaptureInfo.mAllowed = false;
  3700     return;
  3703   // null frame argument means clear the capture
  3704   if (!aFrame) {
  3705     NS_RELEASE(gCaptureInfo.mContent);
  3706     gCaptureInfo.mAllowed = false;
  3707     return;
  3710   nsIFrame* capturingFrame = gCaptureInfo.mContent->GetPrimaryFrame();
  3711   if (!capturingFrame) {
  3712     NS_RELEASE(gCaptureInfo.mContent);
  3713     gCaptureInfo.mAllowed = false;
  3714     return;
  3717   if (nsLayoutUtils::IsAncestorFrameCrossDoc(aFrame, capturingFrame)) {
  3718     NS_RELEASE(gCaptureInfo.mContent);
  3719     gCaptureInfo.mAllowed = false;
  3723 nsresult
  3724 PresShell::CaptureHistoryState(nsILayoutHistoryState** aState)
  3726   NS_PRECONDITION(nullptr != aState, "null state pointer");
  3728   // We actually have to mess with the docshell here, since we want to
  3729   // store the state back in it.
  3730   // XXXbz this isn't really right, since this is being called in the
  3731   // content viewer's Hide() method...  by that point the docshell's
  3732   // state could be wrong.  We should sort out a better ownership
  3733   // model for the layout history state.
  3734   nsCOMPtr<nsIDocShell> docShell(mPresContext->GetDocShell());
  3735   if (!docShell)
  3736     return NS_ERROR_FAILURE;
  3738   nsCOMPtr<nsILayoutHistoryState> historyState;
  3739   docShell->GetLayoutHistoryState(getter_AddRefs(historyState));
  3740   if (!historyState) {
  3741     // Create the document state object
  3742     historyState = NS_NewLayoutHistoryState();
  3743     docShell->SetLayoutHistoryState(historyState);
  3746   *aState = historyState;
  3747   NS_IF_ADDREF(*aState);
  3749   // Capture frame state for the entire frame hierarchy
  3750   nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
  3751   if (!rootFrame) return NS_OK;
  3753   mFrameConstructor->CaptureFrameState(rootFrame, historyState);
  3755   return NS_OK;
  3758 void
  3759 PresShell::UnsuppressAndInvalidate()
  3761   // Note: We ignore the EnsureVisible check for resource documents, because
  3762   // they won't have a docshell, so they'll always fail EnsureVisible.
  3763   if ((!mDocument->IsResourceDoc() && !mPresContext->EnsureVisible()) ||
  3764       mHaveShutDown) {
  3765     // No point; we're about to be torn down anyway.
  3766     return;
  3769   if (!mDocument->IsResourceDoc()) {
  3770     // Notify observers that a new page is about to be drawn. Execute this
  3771     // as soon as it is safe to run JS, which is guaranteed to be before we
  3772     // go back to the event loop and actually draw the page.
  3773     nsContentUtils::AddScriptRunner(new nsBeforeFirstPaintDispatcher(mDocument));
  3776   mPaintingSuppressed = false;
  3777   nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
  3778   if (rootFrame) {
  3779     // let's assume that outline on a root frame is not supported
  3780     rootFrame->InvalidateFrame();
  3782     if (mCaretEnabled && mCaret) {
  3783       mCaret->CheckCaretDrawingState();
  3787   // now that painting is unsuppressed, focus may be set on the document
  3788   nsPIDOMWindow *win = mDocument->GetWindow();
  3789   if (win)
  3790     win->SetReadyForFocus();
  3792   if (!mHaveShutDown) {
  3793     SynthesizeMouseMove(false);
  3794     ScheduleImageVisibilityUpdate();
  3798 void
  3799 PresShell::UnsuppressPainting()
  3801   if (mPaintSuppressionTimer) {
  3802     mPaintSuppressionTimer->Cancel();
  3803     mPaintSuppressionTimer = nullptr;
  3806   if (mIsDocumentGone || !mPaintingSuppressed)
  3807     return;
  3809   // If we have reflows pending, just wait until we process
  3810   // the reflows and get all the frames where we want them
  3811   // before actually unlocking the painting.  Otherwise
  3812   // go ahead and unlock now.
  3813   if (!mDirtyRoots.IsEmpty())
  3814     mShouldUnsuppressPainting = true;
  3815   else
  3816     UnsuppressAndInvalidate();
  3819 // Post a request to handle an arbitrary callback after reflow has finished.
  3820 nsresult
  3821 PresShell::PostReflowCallback(nsIReflowCallback* aCallback)
  3823   void* result = AllocateMisc(sizeof(nsCallbackEventRequest));
  3824   nsCallbackEventRequest* request = (nsCallbackEventRequest*)result;
  3826   request->callback = aCallback;
  3827   request->next = nullptr;
  3829   if (mLastCallbackEventRequest) {
  3830     mLastCallbackEventRequest = mLastCallbackEventRequest->next = request;
  3831   } else {
  3832     mFirstCallbackEventRequest = request;
  3833     mLastCallbackEventRequest = request;
  3836   return NS_OK;
  3839 void
  3840 PresShell::CancelReflowCallback(nsIReflowCallback* aCallback)
  3842    nsCallbackEventRequest* before = nullptr;
  3843    nsCallbackEventRequest* node = mFirstCallbackEventRequest;
  3844    while(node)
  3846       nsIReflowCallback* callback = node->callback;
  3848       if (callback == aCallback)
  3850         nsCallbackEventRequest* toFree = node;
  3851         if (node == mFirstCallbackEventRequest) {
  3852           node = node->next;
  3853           mFirstCallbackEventRequest = node;
  3854           NS_ASSERTION(before == nullptr, "impossible");
  3855         } else {
  3856           node = node->next;
  3857           before->next = node;
  3860         if (toFree == mLastCallbackEventRequest) {
  3861           mLastCallbackEventRequest = before;
  3864         FreeMisc(sizeof(nsCallbackEventRequest), toFree);
  3865       } else {
  3866         before = node;
  3867         node = node->next;
  3872 void
  3873 PresShell::CancelPostedReflowCallbacks()
  3875   while (mFirstCallbackEventRequest) {
  3876     nsCallbackEventRequest* node = mFirstCallbackEventRequest;
  3877     mFirstCallbackEventRequest = node->next;
  3878     if (!mFirstCallbackEventRequest) {
  3879       mLastCallbackEventRequest = nullptr;
  3881     nsIReflowCallback* callback = node->callback;
  3882     FreeMisc(sizeof(nsCallbackEventRequest), node);
  3883     if (callback) {
  3884       callback->ReflowCallbackCanceled();
  3889 void
  3890 PresShell::HandlePostedReflowCallbacks(bool aInterruptible)
  3892    bool shouldFlush = false;
  3894    while (mFirstCallbackEventRequest) {
  3895      nsCallbackEventRequest* node = mFirstCallbackEventRequest;
  3896      mFirstCallbackEventRequest = node->next;
  3897      if (!mFirstCallbackEventRequest) {
  3898        mLastCallbackEventRequest = nullptr;
  3900      nsIReflowCallback* callback = node->callback;
  3901      FreeMisc(sizeof(nsCallbackEventRequest), node);
  3902      if (callback) {
  3903        if (callback->ReflowFinished()) {
  3904          shouldFlush = true;
  3909    mozFlushType flushType =
  3910      aInterruptible ? Flush_InterruptibleLayout : Flush_Layout;
  3911    if (shouldFlush && !mIsDestroying) {
  3912      FlushPendingNotifications(flushType);
  3916 bool
  3917 PresShell::IsSafeToFlush() const
  3919   // Not safe if we are reflowing or in the middle of frame construction
  3920   bool isSafeToFlush = !mIsReflowing &&
  3921                          !mChangeNestCount;
  3923   if (isSafeToFlush) {
  3924     // Not safe if we are painting
  3925     nsViewManager* viewManager = GetViewManager();
  3926     if (viewManager) {
  3927       bool isPainting = false;
  3928       viewManager->IsPainting(isPainting);
  3929       if (isPainting) {
  3930         isSafeToFlush = false;
  3935   return isSafeToFlush;
  3939 void
  3940 PresShell::FlushPendingNotifications(mozFlushType aType)
  3942   // by default, flush animations if aType >= Flush_Style
  3943   mozilla::ChangesToFlush flush(aType, aType >= Flush_Style);
  3944   FlushPendingNotifications(flush);
  3947 void
  3948 PresShell::FlushPendingNotifications(mozilla::ChangesToFlush aFlush)
  3950   if (mIsZombie) {
  3951     return;
  3954   /**
  3955    * VERY IMPORTANT: If you add some sort of new flushing to this
  3956    * method, make sure to add the relevant SetNeedLayoutFlush or
  3957    * SetNeedStyleFlush calls on the document.
  3958    */
  3959   mozFlushType flushType = aFlush.mFlushType;
  3961 #ifdef MOZ_ENABLE_PROFILER_SPS
  3962   static const char flushTypeNames[][20] = {
  3963     "Content",
  3964     "ContentAndNotify",
  3965     "Style",
  3966     "InterruptibleLayout",
  3967     "Layout",
  3968     "Display"
  3969   };
  3971   // Make sure that we don't miss things added to mozFlushType!
  3972   MOZ_ASSERT(static_cast<uint32_t>(flushType) <= ArrayLength(flushTypeNames));
  3974   PROFILER_LABEL_PRINTF("layout", "Flush", "(Flush_%s)",
  3975                       flushTypeNames[flushType - 1]);
  3976 #endif
  3978 #ifdef ACCESSIBILITY
  3979 #ifdef DEBUG
  3980   nsAccessibilityService* accService = GetAccService();
  3981   if (accService) {
  3982     NS_ASSERTION(!accService->IsProcessingRefreshDriverNotification(),
  3983                  "Flush during accessible tree update!");
  3985 #endif
  3986 #endif
  3988   NS_ASSERTION(flushType >= Flush_Frames, "Why did we get called?");
  3990   bool isSafeToFlush = IsSafeToFlush();
  3992   // If layout could possibly trigger scripts, then it's only safe to flush if
  3993   // it's safe to run script.
  3994   bool hasHadScriptObject;
  3995   if (mDocument->GetScriptHandlingObject(hasHadScriptObject) ||
  3996       hasHadScriptObject) {
  3997     isSafeToFlush = isSafeToFlush && nsContentUtils::IsSafeToRunScript();
  4000   NS_ASSERTION(!isSafeToFlush || mViewManager, "Must have view manager");
  4001   // Make sure the view manager stays alive.
  4002   nsRefPtr<nsViewManager> viewManagerDeathGrip = mViewManager;
  4003   if (isSafeToFlush && mViewManager) {
  4004     // Processing pending notifications can kill us, and some callers only
  4005     // hold weak refs when calling FlushPendingNotifications().  :(
  4006     nsCOMPtr<nsIPresShell> kungFuDeathGrip(this);
  4008     if (mResizeEvent.IsPending()) {
  4009       FireResizeEvent();
  4010       if (mIsDestroying) {
  4011         return;
  4015     // We need to make sure external resource documents are flushed too (for
  4016     // example, svg filters that reference a filter in an external document
  4017     // need the frames in the external document to be constructed for the
  4018     // filter to work). We only need external resources to be flushed when the
  4019     // main document is flushing >= Flush_Frames, so we flush external
  4020     // resources here instead of nsDocument::FlushPendingNotifications.
  4021     mDocument->FlushExternalResources(flushType);
  4023     // Force flushing of any pending content notifications that might have
  4024     // queued up while our event was pending.  That will ensure that we don't
  4025     // construct frames for content right now that's still waiting to be
  4026     // notified on,
  4027     mDocument->FlushPendingNotifications(Flush_ContentAndNotify);
  4029     // Process pending restyles, since any flush of the presshell wants
  4030     // up-to-date style data.
  4031     if (!mIsDestroying) {
  4032       mViewManager->FlushDelayedResize(false);
  4033       mPresContext->FlushPendingMediaFeatureValuesChanged();
  4035       // Flush any pending update of the user font set, since that could
  4036       // cause style changes (for updating ex/ch units, and to cause a
  4037       // reflow).
  4038       mPresContext->FlushUserFontSet();
  4040       // Flush any requested SMIL samples.
  4041       if (mDocument->HasAnimationController()) {
  4042         mDocument->GetAnimationController()->FlushResampleRequests();
  4045       if (aFlush.mFlushAnimations &&
  4046           nsLayoutUtils::AreAsyncAnimationsEnabled() &&
  4047           !mPresContext->StyleUpdateForAllAnimationsIsUpToDate()) {
  4048         mPresContext->AnimationManager()->
  4049           FlushAnimations(CommonAnimationManager::Cannot_Throttle);
  4050         mPresContext->TransitionManager()->
  4051           FlushTransitions(CommonAnimationManager::Cannot_Throttle);
  4052         mPresContext->TickLastStyleUpdateForAllAnimations();
  4055       // The FlushResampleRequests() above flushed style changes.
  4056       if (!mIsDestroying) {
  4057         nsAutoScriptBlocker scriptBlocker;
  4058         mPresContext->RestyleManager()->ProcessPendingRestyles();
  4062     // Dispatch any 'animationstart' events those (or earlier) restyles
  4063     // queued up.
  4064     if (!mIsDestroying) {
  4065       mPresContext->AnimationManager()->DispatchEvents();
  4068     // Process whatever XBL constructors those restyles queued up.  This
  4069     // ensures that onload doesn't fire too early and that we won't do extra
  4070     // reflows after those constructors run.
  4071     if (!mIsDestroying) {
  4072       mDocument->BindingManager()->ProcessAttachedQueue();
  4075     // Now those constructors or events might have posted restyle
  4076     // events.  At the same time, we still need up-to-date style data.
  4077     // In particular, reflow depends on style being completely up to
  4078     // date.  If it's not, then style context reparenting, which can
  4079     // happen during reflow, might suddenly pick up the new rules and
  4080     // we'll end up with frames whose style doesn't match the frame
  4081     // type.
  4082     if (!mIsDestroying) {
  4083       nsAutoScriptBlocker scriptBlocker;
  4084       mPresContext->RestyleManager()->ProcessPendingRestyles();
  4088     // There might be more pending constructors now, but we're not going to
  4089     // worry about them.  They can't be triggered during reflow, so we should
  4090     // be good.
  4092     if (flushType >= (mSuppressInterruptibleReflows ? Flush_Layout : Flush_InterruptibleLayout) &&
  4093         !mIsDestroying) {
  4094       mFrameConstructor->RecalcQuotesAndCounters();
  4095       mViewManager->FlushDelayedResize(true);
  4096       if (ProcessReflowCommands(flushType < Flush_Layout) && mContentToScrollTo) {
  4097         // We didn't get interrupted.  Go ahead and scroll to our content
  4098         DoScrollContentIntoView();
  4099         if (mContentToScrollTo) {
  4100           mContentToScrollTo->DeleteProperty(nsGkAtoms::scrolling);
  4101           mContentToScrollTo = nullptr;
  4104     } else if (!mIsDestroying && mSuppressInterruptibleReflows &&
  4105                flushType == Flush_InterruptibleLayout) {
  4106       // We suppressed this flush, but the document thinks it doesn't
  4107       // need to flush anymore.  Let it know what's really going on.
  4108       mDocument->SetNeedLayoutFlush();
  4111     if (flushType >= Flush_Layout) {
  4112       if (!mIsDestroying) {
  4113         mViewManager->UpdateWidgetGeometry();
  4119 void
  4120 PresShell::CharacterDataWillChange(nsIDocument *aDocument,
  4121                                    nsIContent*  aContent,
  4122                                    CharacterDataChangeInfo* aInfo)
  4124   NS_PRECONDITION(!mIsDocumentGone, "Unexpected CharacterDataChanged");
  4125   NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
  4127   if (mCaret) {
  4128     // Invalidate the caret's current location before we call into the frame
  4129     // constructor. It is important to do this now, and not wait until the
  4130     // resulting reflow, because this call causes continuation frames of the
  4131     // text frame the caret is in to forget what part of the content they
  4132     // refer to, making it hard for them to return the correct continuation
  4133     // frame to the caret.
  4134     //
  4135     // It's also important to do this before the content actually changes, since
  4136     // in bidi text the caret needs to look at the content to determine its
  4137     // position and shape.
  4138     mCaret->InvalidateOutsideCaret();
  4142 void
  4143 PresShell::CharacterDataChanged(nsIDocument *aDocument,
  4144                                 nsIContent*  aContent,
  4145                                 CharacterDataChangeInfo* aInfo)
  4147   NS_PRECONDITION(!mIsDocumentGone, "Unexpected CharacterDataChanged");
  4148   NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
  4150   nsAutoCauseReflowNotifier crNotifier(this);
  4152   // Call this here so it only happens for real content mutations and
  4153   // not cases when the frame constructor calls its own methods to force
  4154   // frame reconstruction.
  4155   nsIContent *container = aContent->GetParent();
  4156   uint32_t selectorFlags =
  4157     container ? (container->GetFlags() & NODE_ALL_SELECTOR_FLAGS) : 0;
  4158   if (selectorFlags != 0 && !aContent->IsRootOfAnonymousSubtree()) {
  4159     Element* element = container->AsElement();
  4160     if (aInfo->mAppend && !aContent->GetNextSibling())
  4161       mPresContext->RestyleManager()->RestyleForAppend(element, aContent);
  4162     else
  4163       mPresContext->RestyleManager()->RestyleForInsertOrChange(element, aContent);
  4166   mFrameConstructor->CharacterDataChanged(aContent, aInfo);
  4167   VERIFY_STYLE_TREE;
  4170 void
  4171 PresShell::ContentStateChanged(nsIDocument* aDocument,
  4172                                nsIContent* aContent,
  4173                                EventStates aStateMask)
  4175   NS_PRECONDITION(!mIsDocumentGone, "Unexpected ContentStateChanged");
  4176   NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
  4178   if (mDidInitialize) {
  4179     nsAutoCauseReflowNotifier crNotifier(this);
  4180     mPresContext->RestyleManager()->ContentStateChanged(aContent, aStateMask);
  4181     VERIFY_STYLE_TREE;
  4185 void
  4186 PresShell::DocumentStatesChanged(nsIDocument* aDocument,
  4187                                  EventStates aStateMask)
  4189   NS_PRECONDITION(!mIsDocumentGone, "Unexpected DocumentStatesChanged");
  4190   NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
  4192   if (mDidInitialize &&
  4193       mStyleSet->HasDocumentStateDependentStyle(mPresContext,
  4194                                                 mDocument->GetRootElement(),
  4195                                                 aStateMask)) {
  4196     mPresContext->RestyleManager()->PostRestyleEvent(mDocument->GetRootElement(),
  4197                                                      eRestyle_Subtree,
  4198                                                      NS_STYLE_HINT_NONE);
  4199     VERIFY_STYLE_TREE;
  4202   if (aStateMask.HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE)) {
  4203     nsIFrame* root = mFrameConstructor->GetRootFrame();
  4204     if (root) {
  4205       root->SchedulePaint();
  4210 void
  4211 PresShell::AttributeWillChange(nsIDocument* aDocument,
  4212                                Element*     aElement,
  4213                                int32_t      aNameSpaceID,
  4214                                nsIAtom*     aAttribute,
  4215                                int32_t      aModType)
  4217   NS_PRECONDITION(!mIsDocumentGone, "Unexpected AttributeWillChange");
  4218   NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
  4220   // XXXwaterson it might be more elegant to wait until after the
  4221   // initial reflow to begin observing the document. That would
  4222   // squelch any other inappropriate notifications as well.
  4223   if (mDidInitialize) {
  4224     nsAutoCauseReflowNotifier crNotifier(this);
  4225     mPresContext->RestyleManager()->AttributeWillChange(aElement, aNameSpaceID,
  4226                                                         aAttribute, aModType);
  4227     VERIFY_STYLE_TREE;
  4231 void
  4232 PresShell::AttributeChanged(nsIDocument* aDocument,
  4233                             Element*     aElement,
  4234                             int32_t      aNameSpaceID,
  4235                             nsIAtom*     aAttribute,
  4236                             int32_t      aModType)
  4238   NS_PRECONDITION(!mIsDocumentGone, "Unexpected AttributeChanged");
  4239   NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
  4241   // XXXwaterson it might be more elegant to wait until after the
  4242   // initial reflow to begin observing the document. That would
  4243   // squelch any other inappropriate notifications as well.
  4244   if (mDidInitialize) {
  4245     nsAutoCauseReflowNotifier crNotifier(this);
  4246     mPresContext->RestyleManager()->AttributeChanged(aElement, aNameSpaceID,
  4247                                                      aAttribute, aModType);
  4248     VERIFY_STYLE_TREE;
  4252 void
  4253 PresShell::ContentAppended(nsIDocument *aDocument,
  4254                            nsIContent* aContainer,
  4255                            nsIContent* aFirstNewContent,
  4256                            int32_t     aNewIndexInContainer)
  4258   NS_PRECONDITION(!mIsDocumentGone, "Unexpected ContentAppended");
  4259   NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
  4260   NS_PRECONDITION(aContainer, "must have container");
  4262   if (!mDidInitialize) {
  4263     return;
  4266   nsAutoCauseReflowNotifier crNotifier(this);
  4268   // Call this here so it only happens for real content mutations and
  4269   // not cases when the frame constructor calls its own methods to force
  4270   // frame reconstruction.
  4271   if (aContainer->IsElement()) {
  4272     // Ensure the container is an element before trying to restyle
  4273     // because it can be the case that the container is a ShadowRoot
  4274     // which is a document fragment.
  4275     mPresContext->RestyleManager()->
  4276       RestyleForAppend(aContainer->AsElement(), aFirstNewContent);
  4279   mFrameConstructor->ContentAppended(aContainer, aFirstNewContent, true);
  4281   if (static_cast<nsINode*>(aContainer) == static_cast<nsINode*>(aDocument) &&
  4282       aFirstNewContent->NodeType() == nsIDOMNode::DOCUMENT_TYPE_NODE) {
  4283     NotifyFontSizeInflationEnabledIsDirty();
  4286   VERIFY_STYLE_TREE;
  4289 void
  4290 PresShell::ContentInserted(nsIDocument* aDocument,
  4291                            nsIContent*  aContainer,
  4292                            nsIContent*  aChild,
  4293                            int32_t      aIndexInContainer)
  4295   NS_PRECONDITION(!mIsDocumentGone, "Unexpected ContentInserted");
  4296   NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
  4298   if (!mDidInitialize) {
  4299     return;
  4302   nsAutoCauseReflowNotifier crNotifier(this);
  4304   // Call this here so it only happens for real content mutations and
  4305   // not cases when the frame constructor calls its own methods to force
  4306   // frame reconstruction.
  4307   if (aContainer && aContainer->IsElement()) {
  4308     // Ensure the container is an element before trying to restyle
  4309     // because it can be the case that the container is a ShadowRoot
  4310     // which is a document fragment.
  4311     mPresContext->RestyleManager()->
  4312       RestyleForInsertOrChange(aContainer->AsElement(), aChild);
  4315   mFrameConstructor->ContentInserted(aContainer, aChild, nullptr, true);
  4317   if (((!aContainer && aDocument) ||
  4318       (static_cast<nsINode*>(aContainer) == static_cast<nsINode*>(aDocument))) &&
  4319       aChild->NodeType() == nsIDOMNode::DOCUMENT_TYPE_NODE) {
  4320     NotifyFontSizeInflationEnabledIsDirty();
  4323   VERIFY_STYLE_TREE;
  4326 void
  4327 PresShell::ContentRemoved(nsIDocument *aDocument,
  4328                           nsIContent* aContainer,
  4329                           nsIContent* aChild,
  4330                           int32_t     aIndexInContainer,
  4331                           nsIContent* aPreviousSibling)
  4333   NS_PRECONDITION(!mIsDocumentGone, "Unexpected ContentRemoved");
  4334   NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
  4336   // Make sure that the caret doesn't leave a turd where the child used to be.
  4337   if (mCaret) {
  4338     mCaret->InvalidateOutsideCaret();
  4341   // Notify the ESM that the content has been removed, so that
  4342   // it can clean up any state related to the content.
  4344   // XXX_jwir3: There is no null check for aDocument necessary, since, even
  4345   //            though by nsIMutationObserver, aDocument could be null, the
  4346   //            precondition check that mDocument == aDocument ensures that
  4347   //            aDocument will not be null (since mDocument can't be null unless
  4348   //            we're still intializing).
  4349   mPresContext->EventStateManager()->ContentRemoved(aDocument, aChild);
  4351   nsAutoCauseReflowNotifier crNotifier(this);
  4353   // Call this here so it only happens for real content mutations and
  4354   // not cases when the frame constructor calls its own methods to force
  4355   // frame reconstruction.
  4356   nsIContent* oldNextSibling;
  4357   if (aContainer) {
  4358     oldNextSibling = aContainer->GetChildAt(aIndexInContainer);
  4359   } else {
  4360     oldNextSibling = nullptr;
  4363   if (aContainer && aContainer->IsElement()) {
  4364     mPresContext->RestyleManager()->
  4365       RestyleForRemove(aContainer->AsElement(), aChild, oldNextSibling);
  4368   bool didReconstruct;
  4369   mFrameConstructor->ContentRemoved(aContainer, aChild, oldNextSibling,
  4370                                     nsCSSFrameConstructor::REMOVE_CONTENT,
  4371                                     &didReconstruct);
  4374   if (((aContainer &&
  4375       static_cast<nsINode*>(aContainer) == static_cast<nsINode*>(aDocument)) ||
  4376       aDocument) && aChild->NodeType() == nsIDOMNode::DOCUMENT_TYPE_NODE) {
  4377     NotifyFontSizeInflationEnabledIsDirty();
  4380   VERIFY_STYLE_TREE;
  4383 nsresult
  4384 PresShell::ReconstructFrames(void)
  4386   NS_PRECONDITION(!mFrameConstructor->GetRootFrame() || mDidInitialize,
  4387                   "Must not have root frame before initial reflow");
  4388   if (!mDidInitialize) {
  4389     // Nothing to do here
  4390     return NS_OK;
  4393   nsCOMPtr<nsIPresShell> kungFuDeathGrip(this);
  4395   // Have to make sure that the content notifications are flushed before we
  4396   // start messing with the frame model; otherwise we can get content doubling.
  4397   mDocument->FlushPendingNotifications(Flush_ContentAndNotify);
  4399   nsAutoCauseReflowNotifier crNotifier(this);
  4400   mFrameConstructor->BeginUpdate();
  4401   nsresult rv = mFrameConstructor->ReconstructDocElementHierarchy();
  4402   VERIFY_STYLE_TREE;
  4403   mFrameConstructor->EndUpdate();
  4405   return rv;
  4408 void
  4409 nsIPresShell::ReconstructStyleDataInternal()
  4411   nsAutoTArray<nsRefPtr<mozilla::dom::Element>,1> scopeRoots;
  4412   mChangedScopeStyleRoots.SwapElements(scopeRoots);
  4414   if (mStylesHaveChanged) {
  4415     // If we need to restyle everything, no need to restyle individual
  4416     // scoped style roots.
  4417     scopeRoots.Clear();
  4420   mStylesHaveChanged = false;
  4422   if (mIsDestroying) {
  4423     // We don't want to mess with restyles at this point
  4424     return;
  4427   if (mPresContext) {
  4428     mPresContext->RebuildUserFontSet();
  4431   Element* root = mDocument->GetRootElement();
  4432   if (!mDidInitialize) {
  4433     // Nothing to do here, since we have no frames yet
  4434     return;
  4437   if (!root) {
  4438     // No content to restyle
  4439     return;
  4442   RestyleManager* restyleManager = mPresContext->RestyleManager();
  4443   if (scopeRoots.IsEmpty()) {
  4444     // If scopeRoots is empty, we know that mStylesHaveChanged was true at
  4445     // the beginning of this function, and that we need to restyle the whole
  4446     // document.
  4447     restyleManager->PostRestyleEvent(root, eRestyle_Subtree,
  4448                                      NS_STYLE_HINT_NONE);
  4449   } else {
  4450     for (uint32_t i = 0; i < scopeRoots.Length(); i++) {
  4451       Element* scopeRoot = scopeRoots[i];
  4452       restyleManager->PostRestyleEvent(scopeRoot, eRestyle_Subtree,
  4453                                        NS_STYLE_HINT_NONE);
  4458 void
  4459 nsIPresShell::ReconstructStyleDataExternal()
  4461   ReconstructStyleDataInternal();
  4464 void
  4465 PresShell::RecordStyleSheetChange(nsIStyleSheet* aStyleSheet)
  4467   if (mStylesHaveChanged)
  4468     return;
  4470   nsRefPtr<nsCSSStyleSheet> cssStyleSheet = do_QueryObject(aStyleSheet);
  4471   if (cssStyleSheet) {
  4472     Element* scopeElement = cssStyleSheet->GetScopeElement();
  4473     if (scopeElement) {
  4474       mChangedScopeStyleRoots.AppendElement(scopeElement);
  4475       return;
  4479   mStylesHaveChanged = true;
  4482 void
  4483 PresShell::StyleSheetAdded(nsIDocument *aDocument,
  4484                            nsIStyleSheet* aStyleSheet,
  4485                            bool aDocumentSheet)
  4487   // We only care when enabled sheets are added
  4488   NS_PRECONDITION(aStyleSheet, "Must have a style sheet!");
  4490   if (aStyleSheet->IsApplicable() && aStyleSheet->HasRules()) {
  4491     RecordStyleSheetChange(aStyleSheet);
  4495 void
  4496 PresShell::StyleSheetRemoved(nsIDocument *aDocument,
  4497                              nsIStyleSheet* aStyleSheet,
  4498                              bool aDocumentSheet)
  4500   // We only care when enabled sheets are removed
  4501   NS_PRECONDITION(aStyleSheet, "Must have a style sheet!");
  4503   if (aStyleSheet->IsApplicable() && aStyleSheet->HasRules()) {
  4504     RecordStyleSheetChange(aStyleSheet);
  4508 void
  4509 PresShell::StyleSheetApplicableStateChanged(nsIDocument *aDocument,
  4510                                             nsIStyleSheet* aStyleSheet,
  4511                                             bool aApplicable)
  4513   if (aStyleSheet->HasRules()) {
  4514     RecordStyleSheetChange(aStyleSheet);
  4518 void
  4519 PresShell::StyleRuleChanged(nsIDocument *aDocument,
  4520                             nsIStyleSheet* aStyleSheet,
  4521                             nsIStyleRule* aOldStyleRule,
  4522                             nsIStyleRule* aNewStyleRule)
  4524   RecordStyleSheetChange(aStyleSheet);
  4527 void
  4528 PresShell::StyleRuleAdded(nsIDocument *aDocument,
  4529                           nsIStyleSheet* aStyleSheet,
  4530                           nsIStyleRule* aStyleRule)
  4532   RecordStyleSheetChange(aStyleSheet);
  4535 void
  4536 PresShell::StyleRuleRemoved(nsIDocument *aDocument,
  4537                             nsIStyleSheet* aStyleSheet,
  4538                             nsIStyleRule* aStyleRule)
  4540   RecordStyleSheetChange(aStyleSheet);
  4543 nsIFrame*
  4544 PresShell::GetRealPrimaryFrameFor(nsIContent* aContent) const
  4546   if (aContent->GetDocument() != GetDocument()) {
  4547     return nullptr;
  4549   nsIFrame *primaryFrame = aContent->GetPrimaryFrame();
  4550   if (!primaryFrame)
  4551     return nullptr;
  4552   return nsPlaceholderFrame::GetRealFrameFor(primaryFrame);
  4555 nsIFrame*
  4556 PresShell::GetPlaceholderFrameFor(nsIFrame* aFrame) const
  4558   return mFrameConstructor->GetPlaceholderFrameFor(aFrame);
  4561 nsresult
  4562 PresShell::RenderDocument(const nsRect& aRect, uint32_t aFlags,
  4563                           nscolor aBackgroundColor,
  4564                           gfxContext* aThebesContext)
  4566   NS_ENSURE_TRUE(!(aFlags & RENDER_IS_UNTRUSTED), NS_ERROR_NOT_IMPLEMENTED);
  4568   nsRootPresContext* rootPresContext = mPresContext->GetRootPresContext();
  4569   if (rootPresContext) {
  4570     rootPresContext->FlushWillPaintObservers();
  4571     if (mIsDestroying)
  4572       return NS_OK;
  4575   nsAutoScriptBlocker blockScripts;
  4577   // Set up the rectangle as the path in aThebesContext
  4578   gfxRect r(0, 0,
  4579             nsPresContext::AppUnitsToFloatCSSPixels(aRect.width),
  4580             nsPresContext::AppUnitsToFloatCSSPixels(aRect.height));
  4581   aThebesContext->NewPath();
  4582 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
  4583   aThebesContext->Rectangle(r, true);
  4584 #else
  4585   aThebesContext->Rectangle(r);
  4586 #endif
  4588   nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
  4589   if (!rootFrame) {
  4590     // Nothing to paint, just fill the rect
  4591     aThebesContext->SetColor(gfxRGBA(aBackgroundColor));
  4592     aThebesContext->Fill();
  4593     return NS_OK;
  4596   gfxContextAutoSaveRestore save(aThebesContext);
  4598   gfxContext::GraphicsOperator oldOperator = aThebesContext->CurrentOperator();
  4599   if (oldOperator == gfxContext::OPERATOR_OVER) {
  4600     // Clip to the destination rectangle before we push the group,
  4601     // to limit the size of the temporary surface
  4602     aThebesContext->Clip();
  4605   // we want the window to be composited as a single image using
  4606   // whatever operator was set; set OPERATOR_OVER here, which is
  4607   // either already the case, or overrides the operator in a group.
  4608   // the original operator will be present when we PopGroup.
  4609   // we can avoid using a temporary surface if we're using OPERATOR_OVER
  4610   bool needsGroup = oldOperator != gfxContext::OPERATOR_OVER;
  4612   if (needsGroup) {
  4613     aThebesContext->PushGroup(NS_GET_A(aBackgroundColor) == 0xff ?
  4614                               gfxContentType::COLOR :
  4615                               gfxContentType::COLOR_ALPHA);
  4616     aThebesContext->Save();
  4618     if (oldOperator != gfxContext::OPERATOR_OVER) {
  4619       // Clip now while we paint to the temporary surface. For
  4620       // non-source-bounded operators (e.g., SOURCE), we need to do clip
  4621       // here after we've pushed the group, so that eventually popping
  4622       // the group and painting it will be able to clear the entire
  4623       // destination surface.
  4624       aThebesContext->Clip();
  4625       aThebesContext->SetOperator(gfxContext::OPERATOR_OVER);
  4629   aThebesContext->Translate(gfxPoint(-nsPresContext::AppUnitsToFloatCSSPixels(aRect.x),
  4630                                      -nsPresContext::AppUnitsToFloatCSSPixels(aRect.y)));
  4632   nsDeviceContext* devCtx = mPresContext->DeviceContext();
  4633   gfxFloat scale = gfxFloat(devCtx->AppUnitsPerDevPixel())/nsPresContext::AppUnitsPerCSSPixel();
  4634   aThebesContext->Scale(scale, scale);
  4636   // Since canvas APIs use floats to set up their matrices, we may have
  4637   // some slight inaccuracy here. Adjust matrix components that are
  4638   // integers up to the accuracy of floats to be those integers.
  4639   aThebesContext->NudgeCurrentMatrixToIntegers();
  4641   AutoSaveRestoreRenderingState _(this);
  4643   nsRefPtr<nsRenderingContext> rc = new nsRenderingContext();
  4644   rc->Init(devCtx, aThebesContext);
  4646   bool wouldFlushRetainedLayers = false;
  4647   uint32_t flags = nsLayoutUtils::PAINT_IGNORE_SUPPRESSION;
  4648   if (aThebesContext->CurrentMatrix().HasNonIntegerTranslation()) {
  4649     flags |= nsLayoutUtils::PAINT_IN_TRANSFORM;
  4651   if (!(aFlags & RENDER_ASYNC_DECODE_IMAGES)) {
  4652     flags |= nsLayoutUtils::PAINT_SYNC_DECODE_IMAGES;
  4654   if (aFlags & RENDER_USE_WIDGET_LAYERS) {
  4655     // We only support using widget layers on display root's with widgets.
  4656     nsView* view = rootFrame->GetView();
  4657     if (view && view->GetWidget() &&
  4658         nsLayoutUtils::GetDisplayRootFrame(rootFrame) == rootFrame) {
  4659       flags |= nsLayoutUtils::PAINT_WIDGET_LAYERS;
  4662   if (!(aFlags & RENDER_CARET)) {
  4663     wouldFlushRetainedLayers = true;
  4664     flags |= nsLayoutUtils::PAINT_HIDE_CARET;
  4666   if (aFlags & RENDER_IGNORE_VIEWPORT_SCROLLING) {
  4667     wouldFlushRetainedLayers = !IgnoringViewportScrolling();
  4668     mRenderFlags = ChangeFlag(mRenderFlags, true, STATE_IGNORING_VIEWPORT_SCROLLING);
  4670   if (aFlags & RENDER_DRAWWINDOW_NOT_FLUSHING) {
  4671     mRenderFlags = ChangeFlag(mRenderFlags, true, STATE_DRAWWINDOW_NOT_FLUSHING);
  4673   if (aFlags & RENDER_DOCUMENT_RELATIVE) {
  4674     // XXX be smarter about this ... drawWindow might want a rect
  4675     // that's "pretty close" to what our retained layer tree covers.
  4676     // In that case, it wouldn't disturb normal rendering too much,
  4677     // and we should allow it.
  4678     wouldFlushRetainedLayers = true;
  4679     flags |= nsLayoutUtils::PAINT_DOCUMENT_RELATIVE;
  4682   // Don't let drawWindow blow away our retained layer tree
  4683   if ((flags & nsLayoutUtils::PAINT_WIDGET_LAYERS) && wouldFlushRetainedLayers) {
  4684     flags &= ~nsLayoutUtils::PAINT_WIDGET_LAYERS;
  4687   nsLayoutUtils::PaintFrame(rc, rootFrame, nsRegion(aRect),
  4688                             aBackgroundColor, flags);
  4690   // if we had to use a group, paint it to the destination now
  4691   if (needsGroup) {
  4692     aThebesContext->Restore();
  4693     aThebesContext->PopGroupToSource();
  4694     aThebesContext->Paint();
  4697   return NS_OK;
  4700 /*
  4701  * Clip the display list aList to a range. Returns the clipped
  4702  * rectangle surrounding the range.
  4703  */
  4704 nsRect
  4705 PresShell::ClipListToRange(nsDisplayListBuilder *aBuilder,
  4706                            nsDisplayList* aList,
  4707                            nsRange* aRange)
  4709   // iterate though the display items and add up the bounding boxes of each.
  4710   // This will allow the total area of the frames within the range to be
  4711   // determined. To do this, remove an item from the bottom of the list, check
  4712   // whether it should be part of the range, and if so, append it to the top
  4713   // of the temporary list tmpList. If the item is a text frame at the end of
  4714   // the selection range, clip it to the portion of the text frame that is
  4715   // part of the selection. Then, append the wrapper to the top of the list.
  4716   // Otherwise, just delete the item and don't append it.
  4717   nsRect surfaceRect;
  4718   nsDisplayList tmpList;
  4720   nsDisplayItem* i;
  4721   while ((i = aList->RemoveBottom())) {
  4722     // itemToInsert indiciates the item that should be inserted into the
  4723     // temporary list. If null, no item should be inserted.
  4724     nsDisplayItem* itemToInsert = nullptr;
  4725     nsIFrame* frame = i->Frame();
  4726     nsIContent* content = frame->GetContent();
  4727     if (content) {
  4728       bool atStart = (content == aRange->GetStartParent());
  4729       bool atEnd = (content == aRange->GetEndParent());
  4730       if ((atStart || atEnd) && frame->GetType() == nsGkAtoms::textFrame) {
  4731         int32_t frameStartOffset, frameEndOffset;
  4732         frame->GetOffsets(frameStartOffset, frameEndOffset);
  4734         int32_t hilightStart =
  4735           atStart ? std::max(aRange->StartOffset(), frameStartOffset) : frameStartOffset;
  4736         int32_t hilightEnd =
  4737           atEnd ? std::min(aRange->EndOffset(), frameEndOffset) : frameEndOffset;
  4738         if (hilightStart < hilightEnd) {
  4739           // determine the location of the start and end edges of the range.
  4740           nsPoint startPoint, endPoint;
  4741           frame->GetPointFromOffset(hilightStart, &startPoint);
  4742           frame->GetPointFromOffset(hilightEnd, &endPoint);
  4744           // the clip rectangle is determined by taking the the start and
  4745           // end points of the range, offset from the reference frame.
  4746           // Because of rtl, the end point may be to the left of the
  4747           // start point, so x is set to the lowest value
  4748           nsRect textRect(aBuilder->ToReferenceFrame(frame), frame->GetSize());
  4749           nscoord x = std::min(startPoint.x, endPoint.x);
  4750           textRect.x += x;
  4751           textRect.width = std::max(startPoint.x, endPoint.x) - x;
  4752           surfaceRect.UnionRect(surfaceRect, textRect);
  4754           DisplayItemClip newClip;
  4755           newClip.SetTo(textRect);
  4756           newClip.IntersectWith(i->GetClip());
  4757           i->SetClip(aBuilder, newClip);
  4758           itemToInsert = i;
  4761       // Don't try to descend into subdocuments.
  4762       // If this ever changes we'd need to add handling for subdocuments with
  4763       // different zoom levels.
  4764       else if (content->GetCurrentDoc() ==
  4765                  aRange->GetStartParent()->GetCurrentDoc()) {
  4766         // if the node is within the range, append it to the temporary list
  4767         bool before, after;
  4768         nsresult rv =
  4769           nsRange::CompareNodeToRange(content, aRange, &before, &after);
  4770         if (NS_SUCCEEDED(rv) && !before && !after) {
  4771           itemToInsert = i;
  4772           bool snap;
  4773           surfaceRect.UnionRect(surfaceRect, i->GetBounds(aBuilder, &snap));
  4778     // insert the item into the list if necessary. If the item has a child
  4779     // list, insert that as well
  4780     nsDisplayList* sublist = i->GetSameCoordinateSystemChildren();
  4781     if (itemToInsert || sublist) {
  4782       tmpList.AppendToTop(itemToInsert ? itemToInsert : i);
  4783       // if the item is a list, iterate over it as well
  4784       if (sublist)
  4785         surfaceRect.UnionRect(surfaceRect,
  4786           ClipListToRange(aBuilder, sublist, aRange));
  4788     else {
  4789       // otherwise, just delete the item and don't readd it to the list
  4790       i->~nsDisplayItem();
  4794   // now add all the items back onto the original list again
  4795   aList->AppendToTop(&tmpList);
  4797   return surfaceRect;
  4800 #ifdef DEBUG
  4801 #include <stdio.h>
  4803 static bool gDumpRangePaintList = false;
  4804 #endif
  4806 RangePaintInfo*
  4807 PresShell::CreateRangePaintInfo(nsIDOMRange* aRange,
  4808                                 nsRect& aSurfaceRect,
  4809                                 bool aForPrimarySelection)
  4811   RangePaintInfo* info = nullptr;
  4813   nsRange* range = static_cast<nsRange*>(aRange);
  4815   nsIFrame* ancestorFrame;
  4816   nsIFrame* rootFrame = GetRootFrame();
  4818   // If the start or end of the range is the document, just use the root
  4819   // frame, otherwise get the common ancestor of the two endpoints of the
  4820   // range.
  4821   nsINode* startParent = range->GetStartParent();
  4822   nsINode* endParent = range->GetEndParent();
  4823   nsIDocument* doc = startParent->GetCurrentDoc();
  4824   if (startParent == doc || endParent == doc) {
  4825     ancestorFrame = rootFrame;
  4827   else {
  4828     nsINode* ancestor = nsContentUtils::GetCommonAncestor(startParent, endParent);
  4829     NS_ASSERTION(!ancestor || ancestor->IsNodeOfType(nsINode::eCONTENT),
  4830                  "common ancestor is not content");
  4831     if (!ancestor || !ancestor->IsNodeOfType(nsINode::eCONTENT))
  4832       return nullptr;
  4834     nsIContent* ancestorContent = static_cast<nsIContent*>(ancestor);
  4835     ancestorFrame = ancestorContent->GetPrimaryFrame();
  4837     // use the nearest ancestor frame that includes all continuations as the
  4838     // root for building the display list
  4839     while (ancestorFrame &&
  4840            nsLayoutUtils::GetNextContinuationOrIBSplitSibling(ancestorFrame))
  4841       ancestorFrame = ancestorFrame->GetParent();
  4844   if (!ancestorFrame)
  4845     return nullptr;
  4847   info = new RangePaintInfo(range, ancestorFrame);
  4849   nsRect ancestorRect = ancestorFrame->GetVisualOverflowRect();
  4851   // get a display list containing the range
  4852   info->mBuilder.SetIncludeAllOutOfFlows();
  4853   if (aForPrimarySelection) {
  4854     info->mBuilder.SetSelectedFramesOnly();
  4856   info->mBuilder.EnterPresShell(ancestorFrame, ancestorRect);
  4857   ancestorFrame->BuildDisplayListForStackingContext(&info->mBuilder,
  4858                                                     ancestorRect, &info->mList);
  4860 #ifdef DEBUG
  4861   if (gDumpRangePaintList) {
  4862     fprintf(stderr, "CreateRangePaintInfo --- before ClipListToRange:\n");
  4863     nsFrame::PrintDisplayList(&(info->mBuilder), info->mList);
  4865 #endif
  4867   nsRect rangeRect = ClipListToRange(&info->mBuilder, &info->mList, range);
  4869   info->mBuilder.LeavePresShell(ancestorFrame, ancestorRect);
  4871 #ifdef DEBUG
  4872   if (gDumpRangePaintList) {
  4873     fprintf(stderr, "CreateRangePaintInfo --- after ClipListToRange:\n");
  4874     nsFrame::PrintDisplayList(&(info->mBuilder), info->mList);
  4876 #endif
  4878   // determine the offset of the reference frame for the display list
  4879   // to the root frame. This will allow the coordinates used when painting
  4880   // to all be offset from the same point
  4881   info->mRootOffset = ancestorFrame->GetOffsetTo(rootFrame);
  4882   rangeRect.MoveBy(info->mRootOffset);
  4883   aSurfaceRect.UnionRect(aSurfaceRect, rangeRect);
  4885   return info;
  4888 TemporaryRef<SourceSurface>
  4889 PresShell::PaintRangePaintInfo(nsTArray<nsAutoPtr<RangePaintInfo> >* aItems,
  4890                                nsISelection* aSelection,
  4891                                nsIntRegion* aRegion,
  4892                                nsRect aArea,
  4893                                nsIntPoint& aPoint,
  4894                                nsIntRect* aScreenRect)
  4896   nsPresContext* pc = GetPresContext();
  4897   if (!pc || aArea.width == 0 || aArea.height == 0)
  4898     return nullptr;
  4900   nsDeviceContext* deviceContext = pc->DeviceContext();
  4902   // use the rectangle to create the surface
  4903   nsIntRect pixelArea = aArea.ToOutsidePixels(pc->AppUnitsPerDevPixel());
  4905   // if the area of the image is larger than the maximum area, scale it down
  4906   float scale = 0.0;
  4907   nsIntRect rootScreenRect =
  4908     GetRootFrame()->GetScreenRectInAppUnits().ToNearestPixels(
  4909       pc->AppUnitsPerDevPixel());
  4911   // if the image is larger in one or both directions than half the size of
  4912   // the available screen area, scale the image down to that size.
  4913   nsRect maxSize;
  4914   deviceContext->GetClientRect(maxSize);
  4915   nscoord maxWidth = pc->AppUnitsToDevPixels(maxSize.width >> 1);
  4916   nscoord maxHeight = pc->AppUnitsToDevPixels(maxSize.height >> 1);
  4917   bool resize = (pixelArea.width > maxWidth || pixelArea.height > maxHeight);
  4918   if (resize) {
  4919     scale = 1.0;
  4920     // divide the maximum size by the image size in both directions. Whichever
  4921     // direction produces the smallest result determines how much should be
  4922     // scaled.
  4923     if (pixelArea.width > maxWidth)
  4924       scale = std::min(scale, float(maxWidth) / pixelArea.width);
  4925     if (pixelArea.height > maxHeight)
  4926       scale = std::min(scale, float(maxHeight) / pixelArea.height);
  4928     pixelArea.width = NSToIntFloor(float(pixelArea.width) * scale);
  4929     pixelArea.height = NSToIntFloor(float(pixelArea.height) * scale);
  4931     // adjust the screen position based on the rescaled size
  4932     nscoord left = rootScreenRect.x + pixelArea.x;
  4933     nscoord top = rootScreenRect.y + pixelArea.y;
  4934     aScreenRect->x = NSToIntFloor(aPoint.x - float(aPoint.x - left) * scale);
  4935     aScreenRect->y = NSToIntFloor(aPoint.y - float(aPoint.y - top) * scale);
  4937   else {
  4938     // move aScreenRect to the position of the surface in screen coordinates
  4939     aScreenRect->MoveTo(rootScreenRect.x + pixelArea.x, rootScreenRect.y + pixelArea.y);
  4941   aScreenRect->width = pixelArea.width;
  4942   aScreenRect->height = pixelArea.height;
  4944   RefPtr<DrawTarget> dt =
  4945    gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
  4946                                  IntSize(pixelArea.width, pixelArea.height),
  4947                                  SurfaceFormat::B8G8R8A8);
  4948   if (!dt) {
  4949     return nullptr;
  4952   nsRefPtr<gfxContext> ctx = new gfxContext(dt);
  4953   nsRefPtr<nsRenderingContext> rc = new nsRenderingContext();
  4954   rc->Init(deviceContext, ctx);
  4956   if (aRegion) {
  4957     // Convert aRegion from CSS pixels to dev pixels
  4958     nsIntRegion region =
  4959       aRegion->ToAppUnits(nsPresContext::AppUnitsPerCSSPixel())
  4960         .ToOutsidePixels(pc->AppUnitsPerDevPixel());
  4961     rc->SetClip(region);
  4964   if (resize)
  4965     rc->Scale(scale, scale);
  4967   // translate so that points are relative to the surface area
  4968   rc->Translate(-aArea.TopLeft());
  4970   // temporarily hide the selection so that text is drawn normally. If a
  4971   // selection is being rendered, use that, otherwise use the presshell's
  4972   // selection.
  4973   nsRefPtr<nsFrameSelection> frameSelection;
  4974   if (aSelection) {
  4975     frameSelection = static_cast<Selection*>(aSelection)->GetFrameSelection();
  4977   else {
  4978     frameSelection = FrameSelection();
  4980   int16_t oldDisplaySelection = frameSelection->GetDisplaySelection();
  4981   frameSelection->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN);
  4983   // next, paint each range in the selection
  4984   int32_t count = aItems->Length();
  4985   for (int32_t i = 0; i < count; i++) {
  4986     RangePaintInfo* rangeInfo = (*aItems)[i];
  4987     // the display lists paint relative to the offset from the reference
  4988     // frame, so translate the rendering context
  4989     nsRenderingContext::AutoPushTranslation
  4990       translate(rc, rangeInfo->mRootOffset);
  4992     aArea.MoveBy(-rangeInfo->mRootOffset.x, -rangeInfo->mRootOffset.y);
  4993     nsRegion visible(aArea);
  4994     rangeInfo->mList.ComputeVisibilityForRoot(&rangeInfo->mBuilder, &visible);
  4995     rangeInfo->mList.PaintRoot(&rangeInfo->mBuilder, rc, nsDisplayList::PAINT_DEFAULT);
  4996     aArea.MoveBy(rangeInfo->mRootOffset.x, rangeInfo->mRootOffset.y);
  4999   // restore the old selection display state
  5000   frameSelection->SetDisplaySelection(oldDisplaySelection);
  5002   return dt->Snapshot();
  5005 TemporaryRef<SourceSurface>
  5006 PresShell::RenderNode(nsIDOMNode* aNode,
  5007                       nsIntRegion* aRegion,
  5008                       nsIntPoint& aPoint,
  5009                       nsIntRect* aScreenRect)
  5011   // area will hold the size of the surface needed to draw the node, measured
  5012   // from the root frame.
  5013   nsRect area;
  5014   nsTArray<nsAutoPtr<RangePaintInfo> > rangeItems;
  5016   // nothing to draw if the node isn't in a document
  5017   nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
  5018   if (!node->IsInDoc())
  5019     return nullptr;
  5021   nsRefPtr<nsRange> range = new nsRange(node);
  5022   if (NS_FAILED(range->SelectNode(aNode)))
  5023     return nullptr;
  5025   RangePaintInfo* info = CreateRangePaintInfo(range, area, false);
  5026   if (info && !rangeItems.AppendElement(info)) {
  5027     delete info;
  5028     return nullptr;
  5031   if (aRegion) {
  5032     // combine the area with the supplied region
  5033     nsIntRect rrectPixels = aRegion->GetBounds();
  5035     nsRect rrect = rrectPixels.ToAppUnits(nsPresContext::AppUnitsPerCSSPixel());
  5036     area.IntersectRect(area, rrect);
  5038     nsPresContext* pc = GetPresContext();
  5039     if (!pc)
  5040       return nullptr;
  5042     // move the region so that it is offset from the topleft corner of the surface
  5043     aRegion->MoveBy(-pc->AppUnitsToDevPixels(area.x),
  5044                     -pc->AppUnitsToDevPixels(area.y));
  5047   return PaintRangePaintInfo(&rangeItems, nullptr, aRegion, area, aPoint,
  5048                              aScreenRect);
  5051 TemporaryRef<SourceSurface>
  5052 PresShell::RenderSelection(nsISelection* aSelection,
  5053                            nsIntPoint& aPoint,
  5054                            nsIntRect* aScreenRect)
  5056   // area will hold the size of the surface needed to draw the selection,
  5057   // measured from the root frame.
  5058   nsRect area;
  5059   nsTArray<nsAutoPtr<RangePaintInfo> > rangeItems;
  5061   // iterate over each range and collect them into the rangeItems array.
  5062   // This is done so that the size of selection can be determined so as
  5063   // to allocate a surface area
  5064   int32_t numRanges;
  5065   aSelection->GetRangeCount(&numRanges);
  5066   NS_ASSERTION(numRanges > 0, "RenderSelection called with no selection");
  5068   for (int32_t r = 0; r < numRanges; r++)
  5070     nsCOMPtr<nsIDOMRange> range;
  5071     aSelection->GetRangeAt(r, getter_AddRefs(range));
  5073     RangePaintInfo* info = CreateRangePaintInfo(range, area, true);
  5074     if (info && !rangeItems.AppendElement(info)) {
  5075       delete info;
  5076       return nullptr;
  5080   return PaintRangePaintInfo(&rangeItems, aSelection, nullptr, area, aPoint,
  5081                              aScreenRect);
  5084 void
  5085 PresShell::AddPrintPreviewBackgroundItem(nsDisplayListBuilder& aBuilder,
  5086                                          nsDisplayList&        aList,
  5087                                          nsIFrame*             aFrame,
  5088                                          const nsRect&         aBounds)
  5090   aList.AppendNewToBottom(new (&aBuilder)
  5091     nsDisplaySolidColor(&aBuilder, aFrame, aBounds, NS_RGB(115, 115, 115)));
  5094 static bool
  5095 AddCanvasBackgroundColor(const nsDisplayList& aList, nsIFrame* aCanvasFrame,
  5096                          nscolor aColor)
  5098   for (nsDisplayItem* i = aList.GetBottom(); i; i = i->GetAbove()) {
  5099     if (i->Frame() == aCanvasFrame &&
  5100         i->GetType() == nsDisplayItem::TYPE_CANVAS_BACKGROUND_COLOR) {
  5101       nsDisplayCanvasBackgroundColor* bg = static_cast<nsDisplayCanvasBackgroundColor*>(i);
  5102       bg->SetExtraBackgroundColor(aColor);
  5103       return true;
  5105     nsDisplayList* sublist = i->GetSameCoordinateSystemChildren();
  5106     if (sublist && AddCanvasBackgroundColor(*sublist, aCanvasFrame, aColor))
  5107       return true;
  5109   return false;
  5112 void
  5113 PresShell::AddCanvasBackgroundColorItem(nsDisplayListBuilder& aBuilder,
  5114                                         nsDisplayList&        aList,
  5115                                         nsIFrame*             aFrame,
  5116                                         const nsRect&         aBounds,
  5117                                         nscolor               aBackstopColor,
  5118                                         uint32_t              aFlags)
  5120   if (aBounds.IsEmpty()) {
  5121     return;
  5123   // We don't want to add an item for the canvas background color if the frame
  5124   // (sub)tree we are painting doesn't include any canvas frames. There isn't
  5125   // an easy way to check this directly, but if we check if the root of the
  5126   // (sub)tree we are painting is a canvas frame that should cover us in all
  5127   // cases (it will usually be a viewport frame when we have a canvas frame in
  5128   // the (sub)tree).
  5129   if (!(aFlags & nsIPresShell::FORCE_DRAW) &&
  5130       !nsCSSRendering::IsCanvasFrame(aFrame)) {
  5131     return;
  5134   nscolor bgcolor = NS_ComposeColors(aBackstopColor, mCanvasBackgroundColor);
  5135   if (NS_GET_A(bgcolor) == 0)
  5136     return;
  5138   // To make layers work better, we want to avoid having a big non-scrolled
  5139   // color background behind a scrolled transparent background. Instead,
  5140   // we'll try to move the color background into the scrolled content
  5141   // by making nsDisplayCanvasBackground paint it.
  5142   if (!aFrame->GetParent()) {
  5143     nsIScrollableFrame* sf =
  5144       aFrame->PresContext()->PresShell()->GetRootScrollFrameAsScrollable();
  5145     if (sf) {
  5146       nsCanvasFrame* canvasFrame = do_QueryFrame(sf->GetScrolledFrame());
  5147       if (canvasFrame && canvasFrame->IsVisibleForPainting(&aBuilder)) {
  5148         if (AddCanvasBackgroundColor(aList, canvasFrame, bgcolor))
  5149           return;
  5154   aList.AppendNewToBottom(
  5155     new (&aBuilder) nsDisplaySolidColor(&aBuilder, aFrame, aBounds, bgcolor));
  5158 static bool IsTransparentContainerElement(nsPresContext* aPresContext)
  5160   nsCOMPtr<nsIDocShellTreeItem> docShellItem = aPresContext->GetDocShell();
  5161   nsCOMPtr<nsPIDOMWindow> pwin(do_GetInterface(docShellItem));
  5162   if (!pwin)
  5163     return false;
  5164   nsCOMPtr<nsIContent> containerElement =
  5165     do_QueryInterface(pwin->GetFrameElementInternal());
  5166   return containerElement &&
  5167          containerElement->HasAttr(kNameSpaceID_None, nsGkAtoms::transparent);
  5170 nscolor PresShell::GetDefaultBackgroundColorToDraw()
  5172   if (!mPresContext || !mPresContext->GetBackgroundColorDraw()) {
  5173     return NS_RGB(255,255,255);
  5175   return mPresContext->DefaultBackgroundColor();
  5178 void PresShell::UpdateCanvasBackground()
  5180   // If we have a frame tree and it has style information that
  5181   // specifies the background color of the canvas, update our local
  5182   // cache of that color.
  5183   nsIFrame* rootStyleFrame = FrameConstructor()->GetRootElementStyleFrame();
  5184   if (rootStyleFrame) {
  5185     nsStyleContext* bgStyle =
  5186       nsCSSRendering::FindRootFrameBackground(rootStyleFrame);
  5187     // XXX We should really be passing the canvasframe, not the root element
  5188     // style frame but we don't have access to the canvasframe here. It isn't
  5189     // a problem because only a few frames can return something other than true
  5190     // and none of them would be a canvas frame or root element style frame.
  5191     bool drawBackgroundImage;
  5192     bool drawBackgroundColor;
  5194     mCanvasBackgroundColor =
  5195       nsCSSRendering::DetermineBackgroundColor(mPresContext, bgStyle,
  5196                                                rootStyleFrame,
  5197                                                drawBackgroundImage,
  5198                                                drawBackgroundColor);
  5199     if (GetPresContext()->IsCrossProcessRootContentDocument() &&
  5200         !IsTransparentContainerElement(mPresContext)) {
  5201       mCanvasBackgroundColor =
  5202         NS_ComposeColors(GetDefaultBackgroundColorToDraw(), mCanvasBackgroundColor);
  5206   // If the root element of the document (ie html) has style 'display: none'
  5207   // then the document's background color does not get drawn; cache the
  5208   // color we actually draw.
  5209   if (!FrameConstructor()->GetRootElementFrame()) {
  5210     mCanvasBackgroundColor = GetDefaultBackgroundColorToDraw();
  5212   if (XRE_GetProcessType() == GeckoProcessType_Content) {
  5213     if (TabChild* tabChild = TabChild::GetFrom(this)) {
  5214       tabChild->SetBackgroundColor(mCanvasBackgroundColor);
  5219 nscolor PresShell::ComputeBackstopColor(nsView* aDisplayRoot)
  5221   nsIWidget* widget = aDisplayRoot->GetWidget();
  5222   if (widget && (widget->GetTransparencyMode() != eTransparencyOpaque ||
  5223                  widget->WidgetPaintsBackground())) {
  5224     // Within a transparent widget, so the backstop color must be
  5225     // totally transparent.
  5226     return NS_RGBA(0,0,0,0);
  5228   // Within an opaque widget (or no widget at all), so the backstop
  5229   // color must be totally opaque. The user's default background
  5230   // as reported by the prescontext is guaranteed to be opaque.
  5231   return GetDefaultBackgroundColorToDraw();
  5234 struct PaintParams {
  5235   nscolor mBackgroundColor;
  5236 };
  5238 LayerManager* PresShell::GetLayerManager()
  5240   NS_ASSERTION(mViewManager, "Should have view manager");
  5242   nsView* rootView = mViewManager->GetRootView();
  5243   if (rootView) {
  5244     if (nsIWidget* widget = rootView->GetWidget()) {
  5245       return widget->GetLayerManager();
  5248   return nullptr;
  5251 void PresShell::SetIgnoreViewportScrolling(bool aIgnore)
  5253   if (IgnoringViewportScrolling() == aIgnore) {
  5254     return;
  5256   RenderingState state(this);
  5257   state.mRenderFlags = ChangeFlag(state.mRenderFlags, aIgnore,
  5258                                   STATE_IGNORING_VIEWPORT_SCROLLING);
  5259   SetRenderingState(state);
  5262 nsresult PresShell::SetResolution(float aXResolution, float aYResolution)
  5264   if (!(aXResolution > 0.0 && aYResolution > 0.0)) {
  5265     return NS_ERROR_ILLEGAL_VALUE;
  5267   if (aXResolution == mXResolution && aYResolution == mYResolution) {
  5268     return NS_OK;
  5270   RenderingState state(this);
  5271   state.mXResolution = aXResolution;
  5272   state.mYResolution = aYResolution;
  5273   SetRenderingState(state);
  5274   return NS_OK;
  5277 gfxSize PresShell::GetCumulativeResolution()
  5279   gfxSize resolution = GetResolution();
  5280   nsPresContext* parentCtx = GetPresContext()->GetParentPresContext();
  5281   if (parentCtx) {
  5282     resolution = resolution * parentCtx->PresShell()->GetCumulativeResolution();
  5284   return resolution;
  5287 void PresShell::SetRenderingState(const RenderingState& aState)
  5289   if (mRenderFlags != aState.mRenderFlags) {
  5290     // Rendering state changed in a way that forces us to flush any
  5291     // retained layers we might already have.
  5292     LayerManager* manager = GetLayerManager();
  5293     if (manager) {
  5294       FrameLayerBuilder::InvalidateAllLayers(manager);
  5298   mRenderFlags = aState.mRenderFlags;
  5299   mXResolution = aState.mXResolution;
  5300   mYResolution = aState.mYResolution;
  5303 void PresShell::SynthesizeMouseMove(bool aFromScroll)
  5305   if (!sSynthMouseMove)
  5306     return;
  5308   if (mPaintingSuppressed || !mIsActive || !mPresContext) {
  5309     return;
  5312   if (!mPresContext->IsRoot()) {
  5313     nsIPresShell* rootPresShell = GetRootPresShell();
  5314     if (rootPresShell) {
  5315       rootPresShell->SynthesizeMouseMove(aFromScroll);
  5317     return;
  5320   if (mMouseLocation == nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE))
  5321     return;
  5323   if (!mSynthMouseMoveEvent.IsPending()) {
  5324     nsRefPtr<nsSynthMouseMoveEvent> ev =
  5325         new nsSynthMouseMoveEvent(this, aFromScroll);
  5327     if (!GetPresContext()->RefreshDriver()->AddRefreshObserver(ev,
  5328                                                                Flush_Display)) {
  5329       NS_WARNING("failed to dispatch nsSynthMouseMoveEvent");
  5330       return;
  5333     mSynthMouseMoveEvent = ev;
  5337 /**
  5338  * Find the first floating view with a widget in a postorder traversal of the
  5339  * view tree that contains the point. Thus more deeply nested floating views
  5340  * are preferred over their ancestors, and floating views earlier in the
  5341  * view hierarchy (i.e., added later) are preferred over their siblings.
  5342  * This is adequate for finding the "topmost" floating view under a point,
  5343  * given that floating views don't supporting having a specific z-index.
  5345  * We cannot exit early when aPt is outside the view bounds, because floating
  5346  * views aren't necessarily included in their parent's bounds, so this could
  5347  * traverse the entire view hierarchy --- use carefully.
  5348  */
  5349 static nsView* FindFloatingViewContaining(nsView* aView, nsPoint aPt)
  5351   if (aView->GetVisibility() == nsViewVisibility_kHide)
  5352     // No need to look into descendants.
  5353     return nullptr;
  5355   nsIFrame* frame = aView->GetFrame();
  5356   if (frame) {
  5357     if (!frame->IsVisibleConsideringAncestors(nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY) ||
  5358         !frame->PresContext()->PresShell()->IsActive()) {
  5359       return nullptr;
  5363   for (nsView* v = aView->GetFirstChild(); v; v = v->GetNextSibling()) {
  5364     nsView* r = FindFloatingViewContaining(v, v->ConvertFromParentCoords(aPt));
  5365     if (r)
  5366       return r;
  5369   if (aView->GetFloating() && aView->HasWidget() &&
  5370       aView->GetDimensions().Contains(aPt))
  5371     return aView;
  5373   return nullptr;
  5376 /*
  5377  * This finds the first view containing the given point in a postorder
  5378  * traversal of the view tree that contains the point, assuming that the
  5379  * point is not in a floating view.  It assumes that only floating views
  5380  * extend outside the bounds of their parents.
  5382  * This methods should only be called if FindFloatingViewContaining
  5383  * returns null.
  5384  */
  5385 static nsView* FindViewContaining(nsView* aView, nsPoint aPt)
  5387   if (!aView->GetDimensions().Contains(aPt) ||
  5388       aView->GetVisibility() == nsViewVisibility_kHide) {
  5389     return nullptr;
  5392   nsIFrame* frame = aView->GetFrame();
  5393   if (frame) {
  5394     if (!frame->IsVisibleConsideringAncestors(nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY) ||
  5395         !frame->PresContext()->PresShell()->IsActive()) {
  5396       return nullptr;
  5400   for (nsView* v = aView->GetFirstChild(); v; v = v->GetNextSibling()) {
  5401     nsView* r = FindViewContaining(v, v->ConvertFromParentCoords(aPt));
  5402     if (r)
  5403       return r;
  5406   return aView;
  5409 void
  5410 PresShell::ProcessSynthMouseMoveEvent(bool aFromScroll)
  5412   // If drag session has started, we shouldn't synthesize mousemove event.
  5413   nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
  5414   if (dragSession) {
  5415     mSynthMouseMoveEvent.Forget();
  5416     return;
  5419   // allow new event to be posted while handling this one only if the
  5420   // source of the event is a scroll (to prevent infinite reflow loops)
  5421   if (aFromScroll) {
  5422     mSynthMouseMoveEvent.Forget();
  5425   nsView* rootView = mViewManager ? mViewManager->GetRootView() : nullptr;
  5426   if (mMouseLocation == nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE) ||
  5427       !rootView || !rootView->HasWidget() || !mPresContext) {
  5428     mSynthMouseMoveEvent.Forget();
  5429     return;
  5432   NS_ASSERTION(mPresContext->IsRoot(), "Only a root pres shell should be here");
  5434   // Hold a ref to ourselves so DispatchEvent won't destroy us (since
  5435   // we need to access members after we call DispatchEvent).
  5436   nsCOMPtr<nsIPresShell> kungFuDeathGrip(this);
  5438 #ifdef DEBUG_MOUSE_LOCATION
  5439   printf("[ps=%p]synthesizing mouse move to (%d,%d)\n",
  5440          this, mMouseLocation.x, mMouseLocation.y);
  5441 #endif
  5443   int32_t APD = mPresContext->AppUnitsPerDevPixel();
  5445   // We need a widget to put in the event we are going to dispatch so we look
  5446   // for a view that has a widget and the mouse location is over. We first look
  5447   // for floating views, if there isn't one we use the root view. |view| holds
  5448   // that view.
  5449   nsView* view = nullptr;
  5451   // The appunits per devpixel ratio of |view|.
  5452   int32_t viewAPD;
  5454   // refPoint will be mMouseLocation relative to the widget of |view|, the
  5455   // widget we will put in the event we dispatch, in viewAPD appunits
  5456   nsPoint refpoint(0, 0);
  5458   // We always dispatch the event to the pres shell that contains the view that
  5459   // the mouse is over. pointVM is the VM of that pres shell.
  5460   nsViewManager *pointVM = nullptr;
  5462   // This could be a bit slow (traverses entire view hierarchy)
  5463   // but it's OK to do it once per synthetic mouse event
  5464   view = FindFloatingViewContaining(rootView, mMouseLocation);
  5465   if (!view) {
  5466     view = rootView;
  5467     nsView *pointView = FindViewContaining(rootView, mMouseLocation);
  5468     // pointView can be null in situations related to mouse capture
  5469     pointVM = (pointView ? pointView : view)->GetViewManager();
  5470     refpoint = mMouseLocation + rootView->ViewToWidgetOffset();
  5471     viewAPD = APD;
  5472   } else {
  5473     pointVM = view->GetViewManager();
  5474     nsIFrame* frame = view->GetFrame();
  5475     NS_ASSERTION(frame, "floating views can't be anonymous");
  5476     viewAPD = frame->PresContext()->AppUnitsPerDevPixel();
  5477     refpoint = mMouseLocation.ConvertAppUnits(APD, viewAPD);
  5478     refpoint -= view->GetOffsetTo(rootView);
  5479     refpoint += view->ViewToWidgetOffset();
  5481   NS_ASSERTION(view->GetWidget(), "view should have a widget here");
  5482   WidgetMouseEvent event(true, NS_MOUSE_MOVE, view->GetWidget(),
  5483                          WidgetMouseEvent::eSynthesized);
  5484   event.refPoint = LayoutDeviceIntPoint::FromAppUnitsToNearest(refpoint, viewAPD);
  5485   event.time = PR_IntervalNow();
  5486   // XXX set event.modifiers ?
  5487   // XXX mnakano I think that we should get the latest information from widget.
  5489   nsCOMPtr<nsIPresShell> shell = pointVM->GetPresShell();
  5490   if (shell) {
  5491     shell->DispatchSynthMouseMove(&event, !aFromScroll);
  5494   if (!aFromScroll) {
  5495     mSynthMouseMoveEvent.Forget();
  5499 /* static */ void
  5500 PresShell::MarkImagesInListVisible(const nsDisplayList& aList)
  5502   for (nsDisplayItem* item = aList.GetBottom(); item; item = item->GetAbove()) {
  5503     nsDisplayList* sublist = item->GetChildren();
  5504     if (sublist) {
  5505       MarkImagesInListVisible(*sublist);
  5506       continue;
  5508     nsIFrame* f = item->Frame();
  5509     // We could check the type of the display item, only a handful can hold an
  5510     // image loading content.
  5511     // dont bother nscomptr here, it is wasteful
  5512     nsCOMPtr<nsIImageLoadingContent> content(do_QueryInterface(f->GetContent()));
  5513     if (content) {
  5514       // use the presshell containing the image
  5515       PresShell* presShell = static_cast<PresShell*>(f->PresContext()->PresShell());
  5516       uint32_t count = presShell->mVisibleImages.Count();
  5517       presShell->mVisibleImages.PutEntry(content);
  5518       if (presShell->mVisibleImages.Count() > count) {
  5519         // content was added to mVisibleImages, so we need to increment its visible count
  5520         content->IncrementVisibleCount();
  5526 static PLDHashOperator
  5527 RemoveAndStore(nsRefPtrHashKey<nsIImageLoadingContent>* aEntry, void* userArg)
  5529   nsTArray< nsRefPtr<nsIImageLoadingContent> >* array =
  5530     static_cast< nsTArray< nsRefPtr<nsIImageLoadingContent> >* >(userArg);
  5531   array->AppendElement(aEntry->GetKey());
  5532   return PL_DHASH_REMOVE;
  5535 void
  5536 PresShell::RebuildImageVisibility(const nsDisplayList& aList)
  5538   MOZ_ASSERT(!mImageVisibilityVisited, "already visited?");
  5539   mImageVisibilityVisited = true;
  5540   // Remove the entries of the mVisibleImages hashtable and put them in the
  5541   // beforeImageList array.
  5542   nsTArray< nsRefPtr<nsIImageLoadingContent> > beforeImageList;
  5543   beforeImageList.SetCapacity(mVisibleImages.Count());
  5544   mVisibleImages.EnumerateEntries(RemoveAndStore, &beforeImageList);
  5545   MarkImagesInListVisible(aList);
  5546   for (uint32_t i = 0; i < beforeImageList.Length(); ++i) {
  5547     beforeImageList[i]->DecrementVisibleCount();
  5551 /* static */ void
  5552 PresShell::ClearImageVisibilityVisited(nsView* aView, bool aClear)
  5554   nsViewManager* vm = aView->GetViewManager();
  5555   if (aClear) {
  5556     PresShell* presShell = static_cast<PresShell*>(vm->GetPresShell());
  5557     if (!presShell->mImageVisibilityVisited) {
  5558       presShell->ClearVisibleImagesList();
  5560     presShell->mImageVisibilityVisited = false;
  5562   for (nsView* v = aView->GetFirstChild(); v; v = v->GetNextSibling()) {
  5563     ClearImageVisibilityVisited(v, v->GetViewManager() != vm);
  5567 static PLDHashOperator
  5568 DecrementVisibleCount(nsRefPtrHashKey<nsIImageLoadingContent>* aEntry, void* userArg)
  5570   aEntry->GetKey()->DecrementVisibleCount();
  5571   return PL_DHASH_NEXT;
  5574 void
  5575 PresShell::ClearVisibleImagesList()
  5577   mVisibleImages.EnumerateEntries(DecrementVisibleCount, nullptr);
  5578   mVisibleImages.Clear();
  5581 void
  5582 PresShell::UpdateImageVisibility()
  5584   MOZ_ASSERT(!mPresContext || mPresContext->IsRootContentDocument(),
  5585     "updating image visibility on a non-root content document?");
  5587   mUpdateImageVisibilityEvent.Revoke();
  5589   if (mHaveShutDown || mIsDestroying) {
  5590     return;
  5593   // call update on that frame
  5594   nsIFrame* rootFrame = GetRootFrame();
  5595   if (!rootFrame) {
  5596     ClearVisibleImagesList();
  5597     return;
  5600   // We could walk the frame tree directly and skip creating a display list for
  5601   // better perf.
  5602   nsRect updateRect(nsPoint(0, 0), rootFrame->GetSize());
  5603   nsDisplayListBuilder builder(rootFrame, nsDisplayListBuilder::IMAGE_VISIBILITY, true);
  5604   builder.IgnorePaintSuppression();
  5605   builder.EnterPresShell(rootFrame, updateRect);
  5606   nsDisplayList list;
  5607   rootFrame->BuildDisplayListForStackingContext(&builder, updateRect, &list);
  5608   builder.LeavePresShell(rootFrame, updateRect);
  5610   RebuildImageVisibility(list);
  5612   ClearImageVisibilityVisited(rootFrame->GetView(), true);
  5614   list.DeleteAll();
  5617 bool
  5618 PresShell::AssumeAllImagesVisible()
  5620   static bool sImageVisibilityEnabled = true;
  5621   static bool sImageVisibilityEnabledForBrowserElementsOnly = false;
  5622   static bool sImageVisibilityPrefCached = false;
  5624   if (!sImageVisibilityPrefCached) {
  5625     Preferences::AddBoolVarCache(&sImageVisibilityEnabled,
  5626       "layout.imagevisibility.enabled", true);
  5627     Preferences::AddBoolVarCache(&sImageVisibilityEnabledForBrowserElementsOnly,
  5628       "layout.imagevisibility.enabled_for_browser_elements_only", false);
  5629     sImageVisibilityPrefCached = true;
  5632   if ((!sImageVisibilityEnabled &&
  5633        !sImageVisibilityEnabledForBrowserElementsOnly) ||
  5634       !mPresContext || !mDocument) {
  5635     return true;
  5638   // We assume all images are visible in print, print preview, chrome, xul, and
  5639   // resource docs and don't keep track of them.
  5640   if (mPresContext->Type() == nsPresContext::eContext_PrintPreview ||
  5641       mPresContext->Type() == nsPresContext::eContext_Print ||
  5642       mPresContext->IsChrome() ||
  5643       mDocument->IsResourceDoc() ||
  5644       mDocument->IsXUL()) {
  5645     return true;
  5648   if (!sImageVisibilityEnabled &&
  5649       sImageVisibilityEnabledForBrowserElementsOnly) {
  5650     nsCOMPtr<nsIDocShell> docshell(mPresContext->GetDocShell());
  5651     if (!docshell || !docshell->GetIsInBrowserElement()) {
  5652       return true;
  5656   return false;
  5659 void
  5660 PresShell::ScheduleImageVisibilityUpdate()
  5662   if (AssumeAllImagesVisible())
  5663     return;
  5665   if (!mPresContext->IsRootContentDocument()) {
  5666     nsPresContext* presContext = mPresContext->GetToplevelContentDocumentPresContext();
  5667     if (!presContext)
  5668       return;
  5669     MOZ_ASSERT(presContext->IsRootContentDocument(),
  5670       "Didn't get a root prescontext from GetToplevelContentDocumentPresContext?");
  5671     presContext->PresShell()->ScheduleImageVisibilityUpdate();
  5672     return;
  5675   if (mHaveShutDown || mIsDestroying)
  5676     return;
  5678   if (mUpdateImageVisibilityEvent.IsPending())
  5679     return;
  5681   nsRefPtr<nsRunnableMethod<PresShell> > ev =
  5682     NS_NewRunnableMethod(this, &PresShell::UpdateImageVisibility);
  5683   if (NS_SUCCEEDED(NS_DispatchToCurrentThread(ev))) {
  5684     mUpdateImageVisibilityEvent = ev;
  5688 void
  5689 PresShell::EnsureImageInVisibleList(nsIImageLoadingContent* aImage)
  5691   if (AssumeAllImagesVisible()) {
  5692     aImage->IncrementVisibleCount();
  5693     return;
  5696 #ifdef DEBUG
  5697   // if it has a frame make sure its in this presshell
  5698   nsCOMPtr<nsIContent> content = do_QueryInterface(aImage);
  5699   if (content) {
  5700     PresShell* shell = static_cast<PresShell*>(content->OwnerDoc()->GetShell());
  5701     MOZ_ASSERT(!shell || shell == this, "wrong shell");
  5703 #endif
  5705   if (!mVisibleImages.Contains(aImage)) {
  5706     mVisibleImages.PutEntry(aImage);
  5707     aImage->IncrementVisibleCount();
  5711 void
  5712 PresShell::RemoveImageFromVisibleList(nsIImageLoadingContent* aImage)
  5714 #ifdef DEBUG
  5715   // if it has a frame make sure its in this presshell
  5716   nsCOMPtr<nsIContent> content = do_QueryInterface(aImage);
  5717   if (content) {
  5718     PresShell* shell = static_cast<PresShell*>(content->OwnerDoc()->GetShell());
  5719     MOZ_ASSERT(!shell || shell == this, "wrong shell");
  5721 #endif
  5723   if (AssumeAllImagesVisible()) {
  5724     MOZ_ASSERT(mVisibleImages.Count() == 0, "shouldn't have any images in the table");
  5725     return;
  5728   uint32_t count = mVisibleImages.Count();
  5729   mVisibleImages.RemoveEntry(aImage);
  5730   if (mVisibleImages.Count() < count) {
  5731     // aImage was in the hashtable, so we need to decrement its visible count
  5732     aImage->DecrementVisibleCount();
  5736 class nsAutoNotifyDidPaint
  5738 public:
  5739   nsAutoNotifyDidPaint(PresShell* aShell, uint32_t aFlags)
  5740     : mShell(aShell), mFlags(aFlags)
  5743   ~nsAutoNotifyDidPaint()
  5745     mShell->GetPresContext()->NotifyDidPaintForSubtree(mFlags);
  5748 private:
  5749   PresShell* mShell;
  5750   uint32_t mFlags;
  5751 };
  5753 class AutoUpdateHitRegion
  5755 public:
  5756   AutoUpdateHitRegion(PresShell* aShell, nsIFrame* aFrame)
  5757     : mShell(aShell), mFrame(aFrame)
  5760   ~AutoUpdateHitRegion()
  5762     if (XRE_GetProcessType() != GeckoProcessType_Content ||
  5763         !mFrame || !mShell) {
  5764       return;
  5766     TabChild* tabChild = TabChild::GetFrom(mShell);
  5767     if (!tabChild || !tabChild->GetUpdateHitRegion()) {
  5768       return;
  5770     nsRegion region;
  5771     nsDisplayListBuilder builder(mFrame,
  5772                                  nsDisplayListBuilder::EVENT_DELIVERY,
  5773                                  /* aBuildCert= */ false);
  5774     nsDisplayList list;
  5775     nsAutoTArray<nsIFrame*, 100> outFrames;
  5776     nsDisplayItem::HitTestState hitTestState;
  5777     nsRect bounds = mShell->GetPresContext()->GetVisibleArea();
  5778     builder.EnterPresShell(mFrame, bounds);
  5779     mFrame->BuildDisplayListForStackingContext(&builder, bounds, &list);
  5780     builder.LeavePresShell(mFrame, bounds);
  5781     list.HitTest(&builder, bounds, &hitTestState, &outFrames);
  5782     list.DeleteAll();
  5783     for (int32_t i = outFrames.Length() - 1; i >= 0; --i) {
  5784       region.Or(region, nsLayoutUtils::TransformFrameRectToAncestor(
  5785                   outFrames[i], nsRect(nsPoint(0, 0), outFrames[i]->GetSize()), mFrame));
  5787     tabChild->UpdateHitRegion(region);
  5789 private:
  5790   PresShell* mShell;
  5791   nsIFrame* mFrame;
  5792 };
  5794 void
  5795 PresShell::RestyleShadowRoot(ShadowRoot* aShadowRoot)
  5797   // Mark the children of the ShadowRoot as style changed but not
  5798   // the ShadowRoot itself because it is a document fragment and does not
  5799   // have a frame.
  5800   ExplicitChildIterator iterator(aShadowRoot);
  5801   for (nsIContent* child = iterator.GetNextChild();
  5802        child;
  5803        child = iterator.GetNextChild()) {
  5804     if (child->IsElement()) {
  5805       mChangedScopeStyleRoots.AppendElement(child->AsElement());
  5810 void
  5811 PresShell::Paint(nsView*        aViewToPaint,
  5812                  const nsRegion& aDirtyRegion,
  5813                  uint32_t        aFlags)
  5815   PROFILER_LABEL("Paint", "PresShell::Paint");
  5816   NS_ASSERTION(!mIsDestroying, "painting a destroyed PresShell");
  5817   NS_ASSERTION(aViewToPaint, "null view");
  5819   MOZ_ASSERT(!mImageVisibilityVisited, "should have been cleared");
  5821   if (!mIsActive || mIsZombie) {
  5822     return;
  5825   nsPresContext* presContext = GetPresContext();
  5826   AUTO_LAYOUT_PHASE_ENTRY_POINT(presContext, Paint);
  5828   nsIFrame* frame = aViewToPaint->GetFrame();
  5830   bool isRetainingManager;
  5831   LayerManager* layerManager =
  5832     aViewToPaint->GetWidget()->GetLayerManager(&isRetainingManager);
  5833   NS_ASSERTION(layerManager, "Must be in paint event");
  5834   bool shouldInvalidate = layerManager->NeedsWidgetInvalidation();
  5836   nsAutoNotifyDidPaint notifyDidPaint(this, aFlags);
  5837   AutoUpdateHitRegion updateHitRegion(this, frame);
  5839   // Whether or not we should set first paint when painting is
  5840   // suppressed is debatable. For now we'll do it because
  5841   // B2G relies on first paint to configure the viewport and
  5842   // we only want to do that when we have real content to paint.
  5843   // See Bug 798245
  5844   if (mIsFirstPaint && !mPaintingSuppressed) {
  5845     layerManager->SetIsFirstPaint();
  5846     mIsFirstPaint = false;
  5849   layerManager->BeginTransaction();
  5851   if (frame && isRetainingManager) {
  5852     // Try to do an empty transaction, if the frame tree does not
  5853     // need to be updated. Do not try to do an empty transaction on
  5854     // a non-retained layer manager (like the BasicLayerManager that
  5855     // draws the window title bar on Mac), because a) it won't work
  5856     // and b) below we don't want to clear NS_FRAME_UPDATE_LAYER_TREE,
  5857     // that will cause us to forget to update the real layer manager!
  5859     if (!(aFlags & PAINT_LAYERS)) {
  5860       if (layerManager->EndEmptyTransaction()) {
  5861         return;
  5863       NS_WARNING("Must complete empty transaction when compositing!");
  5866     if (!(frame->GetStateBits() & NS_FRAME_UPDATE_LAYER_TREE) &&
  5867         !mNextPaintCompressed) {
  5868       NotifySubDocInvalidationFunc computeInvalidFunc =
  5869         presContext->MayHavePaintEventListenerInSubDocument() ? nsPresContext::NotifySubDocInvalidation : 0;
  5870       bool computeInvalidRect = computeInvalidFunc ||
  5871                                 (layerManager->GetBackendType() == LayersBackend::LAYERS_BASIC);
  5873       nsAutoPtr<LayerProperties> props(computeInvalidRect ?
  5874                                          LayerProperties::CloneFrom(layerManager->GetRoot()) :
  5875                                          nullptr);
  5877       if (layerManager->EndEmptyTransaction((aFlags & PAINT_COMPOSITE) ?
  5878             LayerManager::END_DEFAULT : LayerManager::END_NO_COMPOSITE)) {
  5879         nsIntRegion invalid;
  5880         if (props) {
  5881           invalid = props->ComputeDifferences(layerManager->GetRoot(), computeInvalidFunc);
  5882         } else {
  5883           LayerProperties::ClearInvalidations(layerManager->GetRoot());
  5885         if (props) {
  5886           if (!invalid.IsEmpty()) {
  5887             nsIntRect bounds = invalid.GetBounds();
  5888             nsRect rect(presContext->DevPixelsToAppUnits(bounds.x),
  5889                         presContext->DevPixelsToAppUnits(bounds.y),
  5890                         presContext->DevPixelsToAppUnits(bounds.width),
  5891                         presContext->DevPixelsToAppUnits(bounds.height));
  5892             if (shouldInvalidate) {
  5893               aViewToPaint->GetViewManager()->InvalidateViewNoSuppression(aViewToPaint, rect);
  5895             presContext->NotifyInvalidation(bounds, 0);
  5897         } else if (shouldInvalidate) {
  5898           aViewToPaint->GetViewManager()->InvalidateView(aViewToPaint);
  5901         frame->UpdatePaintCountForPaintedPresShells();
  5902         return;
  5905     frame->RemoveStateBits(NS_FRAME_UPDATE_LAYER_TREE);
  5907   if (frame) {
  5908     frame->ClearPresShellsFromLastPaint();
  5911   nscolor bgcolor = ComputeBackstopColor(aViewToPaint);
  5912   uint32_t flags = nsLayoutUtils::PAINT_WIDGET_LAYERS | nsLayoutUtils::PAINT_EXISTING_TRANSACTION;
  5913   if (!(aFlags & PAINT_COMPOSITE)) {
  5914     flags |= nsLayoutUtils::PAINT_NO_COMPOSITE;
  5916   if (mNextPaintCompressed) {
  5917     flags |= nsLayoutUtils::PAINT_COMPRESSED;
  5918     mNextPaintCompressed = false;
  5921   if (frame) {
  5922     // We can paint directly into the widget using its layer manager.
  5923     nsLayoutUtils::PaintFrame(nullptr, frame, aDirtyRegion, bgcolor, flags);
  5924     return;
  5927   nsRefPtr<ColorLayer> root = layerManager->CreateColorLayer();
  5928   if (root) {
  5929     nsPresContext* pc = GetPresContext();
  5930     nsIntRect bounds =
  5931       pc->GetVisibleArea().ToOutsidePixels(pc->AppUnitsPerDevPixel());
  5932     bgcolor = NS_ComposeColors(bgcolor, mCanvasBackgroundColor);
  5933     root->SetColor(bgcolor);
  5934     root->SetVisibleRegion(bounds);
  5935     layerManager->SetRoot(root);
  5937   layerManager->EndTransaction(nullptr, nullptr, (aFlags & PAINT_COMPOSITE) ?
  5938     LayerManager::END_DEFAULT : LayerManager::END_NO_COMPOSITE);
  5941 // static
  5942 void
  5943 nsIPresShell::SetCapturingContent(nsIContent* aContent, uint8_t aFlags)
  5945   // If capture was set for pointer lock, don't unlock unless we are coming
  5946   // out of pointer lock explicitly.
  5947   if (!aContent && gCaptureInfo.mPointerLock &&
  5948       !(aFlags & CAPTURE_POINTERLOCK)) {
  5949     return;
  5952   NS_IF_RELEASE(gCaptureInfo.mContent);
  5954   // only set capturing content if allowed or the CAPTURE_IGNOREALLOWED or
  5955   // CAPTURE_POINTERLOCK flags are used.
  5956   if ((aFlags & CAPTURE_IGNOREALLOWED) || gCaptureInfo.mAllowed ||
  5957       (aFlags & CAPTURE_POINTERLOCK)) {
  5958     if (aContent) {
  5959       NS_ADDREF(gCaptureInfo.mContent = aContent);
  5961     // CAPTURE_POINTERLOCK is the same as CAPTURE_RETARGETTOELEMENT & CAPTURE_IGNOREALLOWED
  5962     gCaptureInfo.mRetargetToElement = ((aFlags & CAPTURE_RETARGETTOELEMENT) != 0) ||
  5963                                       ((aFlags & CAPTURE_POINTERLOCK) != 0);
  5964     gCaptureInfo.mPreventDrag = (aFlags & CAPTURE_PREVENTDRAG) != 0;
  5965     gCaptureInfo.mPointerLock = (aFlags & CAPTURE_POINTERLOCK) != 0;
  5969 /* static */ void
  5970 nsIPresShell::SetPointerCapturingContent(uint32_t aPointerId, nsIContent* aContent)
  5972   nsIContent* content = GetPointerCapturingContent(aPointerId);
  5974   PointerInfo* pointerInfo = nullptr;
  5975   if (!content && gActivePointersIds->Get(aPointerId, &pointerInfo) &&
  5976       pointerInfo &&
  5977       nsIDOMMouseEvent::MOZ_SOURCE_MOUSE == pointerInfo->mPointerType) {
  5978     SetCapturingContent(aContent, CAPTURE_PREVENTDRAG);
  5981   if (content) {
  5982     // Releasing capture for given pointer.
  5983     gPointerCaptureList->Remove(aPointerId);
  5984     DispatchGotOrLostPointerCaptureEvent(false, aPointerId, content);
  5985     // Need to check the state because a lostpointercapture listener
  5986     // may have called SetPointerCapture
  5987     if (GetPointerCapturingContent(aPointerId)) {
  5988       return;
  5992   gPointerCaptureList->Put(aPointerId, aContent);
  5993   DispatchGotOrLostPointerCaptureEvent(true, aPointerId, aContent);
  5996 /* static */ void
  5997 nsIPresShell::ReleasePointerCapturingContent(uint32_t aPointerId, nsIContent* aContent)
  5999   if (gActivePointersIds->Get(aPointerId)) {
  6000     SetCapturingContent(nullptr, CAPTURE_PREVENTDRAG);
  6003   // Releasing capture for given pointer.
  6004   gPointerCaptureList->Remove(aPointerId);
  6006   DispatchGotOrLostPointerCaptureEvent(false, aPointerId, aContent);
  6009 /* static */ nsIContent*
  6010 nsIPresShell::GetPointerCapturingContent(uint32_t aPointerId)
  6012   return gPointerCaptureList->GetWeak(aPointerId);
  6015 /* static */ bool
  6016 nsIPresShell::GetPointerInfo(uint32_t aPointerId, bool& aActiveState)
  6018   PointerInfo* pointerInfo = nullptr;
  6019   if (gActivePointersIds->Get(aPointerId, &pointerInfo) && pointerInfo) {
  6020     aActiveState = pointerInfo->mActiveState;
  6021     return true;
  6023   return false;
  6026 void
  6027 PresShell::UpdateActivePointerState(WidgetGUIEvent* aEvent)
  6029   switch (aEvent->message) {
  6030   case NS_MOUSE_ENTER:
  6031     // In this case we have to know information about available mouse pointers
  6032     if (WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent()) {
  6033       gActivePointersIds->Put(mouseEvent->pointerId, new PointerInfo(false, mouseEvent->inputSource));
  6035     break;
  6036   case NS_POINTER_DOWN:
  6037     // In this case we switch pointer to active state
  6038     if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) {
  6039       gActivePointersIds->Put(pointerEvent->pointerId, new PointerInfo(true, pointerEvent->inputSource));
  6041     break;
  6042   case NS_POINTER_UP:
  6043     // In this case we remove information about pointer or turn off active state
  6044     if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) {
  6045       if(pointerEvent->inputSource != nsIDOMMouseEvent::MOZ_SOURCE_TOUCH) {
  6046         gActivePointersIds->Put(pointerEvent->pointerId, new PointerInfo(false, pointerEvent->inputSource));
  6047       } else {
  6048         gActivePointersIds->Remove(pointerEvent->pointerId);
  6051     break;
  6052   case NS_MOUSE_EXIT:
  6053     // In this case we have to remove information about disappeared mouse pointers
  6054     if (WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent()) {
  6055       gActivePointersIds->Remove(mouseEvent->pointerId);
  6057     break;
  6061 nsIContent*
  6062 PresShell::GetCurrentEventContent()
  6064   if (mCurrentEventContent &&
  6065       mCurrentEventContent->GetCurrentDoc() != mDocument) {
  6066     mCurrentEventContent = nullptr;
  6067     mCurrentEventFrame = nullptr;
  6069   return mCurrentEventContent;
  6072 nsIFrame*
  6073 PresShell::GetCurrentEventFrame()
  6075   if (MOZ_UNLIKELY(mIsDestroying)) {
  6076     return nullptr;
  6079   // GetCurrentEventContent() makes sure the content is still in the
  6080   // same document that this pres shell belongs to. If not, then the
  6081   // frame shouldn't get an event, nor should we even assume its safe
  6082   // to try and find the frame.
  6083   nsIContent* content = GetCurrentEventContent();
  6084   if (!mCurrentEventFrame && content) {
  6085     mCurrentEventFrame = content->GetPrimaryFrame();
  6086     MOZ_ASSERT(!mCurrentEventFrame ||
  6087                mCurrentEventFrame->PresContext()->GetPresShell() == this);
  6089   return mCurrentEventFrame;
  6092 nsIFrame*
  6093 PresShell::GetEventTargetFrame()
  6095   return GetCurrentEventFrame();
  6098 already_AddRefed<nsIContent>
  6099 PresShell::GetEventTargetContent(WidgetEvent* aEvent)
  6101   nsCOMPtr<nsIContent> content = GetCurrentEventContent();
  6102   if (!content) {
  6103     nsIFrame* currentEventFrame = GetCurrentEventFrame();
  6104     if (currentEventFrame) {
  6105       currentEventFrame->GetContentForEvent(aEvent, getter_AddRefs(content));
  6106       NS_ASSERTION(!content || content->GetCurrentDoc() == mDocument,
  6107                    "handing out content from a different doc");
  6110   return content.forget();
  6113 void
  6114 PresShell::PushCurrentEventInfo(nsIFrame* aFrame, nsIContent* aContent)
  6116   if (mCurrentEventFrame || mCurrentEventContent) {
  6117     mCurrentEventFrameStack.InsertElementAt(0, mCurrentEventFrame);
  6118     mCurrentEventContentStack.InsertObjectAt(mCurrentEventContent, 0);
  6120   mCurrentEventFrame = aFrame;
  6121   mCurrentEventContent = aContent;
  6124 void
  6125 PresShell::PopCurrentEventInfo()
  6127   mCurrentEventFrame = nullptr;
  6128   mCurrentEventContent = nullptr;
  6130   if (0 != mCurrentEventFrameStack.Length()) {
  6131     mCurrentEventFrame = mCurrentEventFrameStack.ElementAt(0);
  6132     mCurrentEventFrameStack.RemoveElementAt(0);
  6133     mCurrentEventContent = mCurrentEventContentStack.ObjectAt(0);
  6134     mCurrentEventContentStack.RemoveObjectAt(0);
  6136     // Don't use it if it has moved to a different document.
  6137     if (mCurrentEventContent &&
  6138         mCurrentEventContent->GetCurrentDoc() != mDocument) {
  6139       mCurrentEventContent = nullptr;
  6140       mCurrentEventFrame = nullptr;
  6145 bool PresShell::InZombieDocument(nsIContent *aContent)
  6147   // If a content node points to a null document, or the document is not
  6148   // attached to a window, then it is possibly in a zombie document,
  6149   // about to be replaced by a newly loading document.
  6150   // Such documents cannot handle DOM events.
  6151   // It might actually be in a node not attached to any document,
  6152   // in which case there is not parent presshell to retarget it to.
  6153   nsIDocument *doc = aContent->GetDocument();
  6154   return !doc || !doc->GetWindow();
  6157 already_AddRefed<nsPIDOMWindow>
  6158 PresShell::GetRootWindow()
  6160   nsCOMPtr<nsPIDOMWindow> window =
  6161     do_QueryInterface(mDocument->GetWindow());
  6162   if (window) {
  6163     nsCOMPtr<nsPIDOMWindow> rootWindow = window->GetPrivateRoot();
  6164     NS_ASSERTION(rootWindow, "nsPIDOMWindow::GetPrivateRoot() returns NULL");
  6165     return rootWindow.forget();
  6168   // If we don't have DOM window, we're zombie, we should find the root window
  6169   // with our parent shell.
  6170   nsCOMPtr<nsIPresShell> parent = GetParentPresShellForEventHandling();
  6171   NS_ENSURE_TRUE(parent, nullptr);
  6172   return parent->GetRootWindow();
  6175 already_AddRefed<nsIPresShell>
  6176 PresShell::GetParentPresShellForEventHandling()
  6178   NS_ENSURE_TRUE(mPresContext, nullptr);
  6180   // Now, find the parent pres shell and send the event there
  6181   nsCOMPtr<nsIDocShellTreeItem> treeItem = mPresContext->GetDocShell();
  6182   if (!treeItem) {
  6183     treeItem = mForwardingContainer.get();
  6186   // Might have gone away, or never been around to start with
  6187   NS_ENSURE_TRUE(treeItem, nullptr);
  6189   nsCOMPtr<nsIDocShellTreeItem> parentTreeItem;
  6190   treeItem->GetParent(getter_AddRefs(parentTreeItem));
  6191   nsCOMPtr<nsIDocShell> parentDocShell = do_QueryInterface(parentTreeItem);
  6192   NS_ENSURE_TRUE(parentDocShell && treeItem != parentTreeItem, nullptr);
  6194   nsCOMPtr<nsIPresShell> parentPresShell = parentDocShell->GetPresShell();
  6195   return parentPresShell.forget();
  6198 nsresult
  6199 PresShell::RetargetEventToParent(WidgetGUIEvent* aEvent,
  6200                                  nsEventStatus* aEventStatus)
  6202   // Send this events straight up to the parent pres shell.
  6203   // We do this for keystroke events in zombie documents or if either a frame
  6204   // or a root content is not present.
  6205   // That way at least the UI key bindings can work.
  6207   nsCOMPtr<nsIPresShell> kungFuDeathGrip(this);
  6208   nsCOMPtr<nsIPresShell> parentPresShell = GetParentPresShellForEventHandling();
  6209   NS_ENSURE_TRUE(parentPresShell, NS_ERROR_FAILURE);
  6211   // Fake the event as though it's from the parent pres shell's root frame.
  6212   return parentPresShell->HandleEvent(parentPresShell->GetRootFrame(), aEvent, true, aEventStatus);
  6215 void
  6216 PresShell::DisableNonTestMouseEvents(bool aDisable)
  6218   sDisableNonTestMouseEvents = aDisable;
  6221 already_AddRefed<nsPIDOMWindow>
  6222 PresShell::GetFocusedDOMWindowInOurWindow()
  6224   nsCOMPtr<nsPIDOMWindow> rootWindow = GetRootWindow();
  6225   NS_ENSURE_TRUE(rootWindow, nullptr);
  6226   nsCOMPtr<nsPIDOMWindow> focusedWindow;
  6227   nsFocusManager::GetFocusedDescendant(rootWindow, true,
  6228                                        getter_AddRefs(focusedWindow));
  6229   return focusedWindow.forget();
  6232 void
  6233 PresShell::RecordMouseLocation(WidgetGUIEvent* aEvent)
  6235   if (!mPresContext)
  6236     return;
  6238   if (!mPresContext->IsRoot()) {
  6239     PresShell* rootPresShell = GetRootPresShell();
  6240     if (rootPresShell) {
  6241       rootPresShell->RecordMouseLocation(aEvent);
  6243     return;
  6246   if ((aEvent->message == NS_MOUSE_MOVE &&
  6247        aEvent->AsMouseEvent()->reason == WidgetMouseEvent::eReal) ||
  6248       aEvent->message == NS_MOUSE_ENTER ||
  6249       aEvent->message == NS_MOUSE_BUTTON_DOWN ||
  6250       aEvent->message == NS_MOUSE_BUTTON_UP) {
  6251     nsIFrame* rootFrame = GetRootFrame();
  6252     if (!rootFrame) {
  6253       nsView* rootView = mViewManager->GetRootView();
  6254       mMouseLocation = nsLayoutUtils::TranslateWidgetToView(mPresContext,
  6255         aEvent->widget, LayoutDeviceIntPoint::ToUntyped(aEvent->refPoint),
  6256         rootView);
  6257     } else {
  6258       mMouseLocation =
  6259         nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, rootFrame);
  6261 #ifdef DEBUG_MOUSE_LOCATION
  6262     if (aEvent->message == NS_MOUSE_ENTER)
  6263       printf("[ps=%p]got mouse enter for %p\n",
  6264              this, aEvent->widget);
  6265     printf("[ps=%p]setting mouse location to (%d,%d)\n",
  6266            this, mMouseLocation.x, mMouseLocation.y);
  6267 #endif
  6268     if (aEvent->message == NS_MOUSE_ENTER)
  6269       SynthesizeMouseMove(false);
  6270   } else if (aEvent->message == NS_MOUSE_EXIT) {
  6271     // Although we only care about the mouse moving into an area for which this
  6272     // pres shell doesn't receive mouse move events, we don't check which widget
  6273     // the mouse exit was for since this seems to vary by platform.  Hopefully
  6274     // this won't matter at all since we'll get the mouse move or enter after
  6275     // the mouse exit when the mouse moves from one of our widgets into another.
  6276     mMouseLocation = nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
  6277 #ifdef DEBUG_MOUSE_LOCATION
  6278     printf("[ps=%p]got mouse exit for %p\n",
  6279            this, aEvent->widget);
  6280     printf("[ps=%p]clearing mouse location\n",
  6281            this);
  6282 #endif
  6286 static void
  6287 EvictTouchPoint(nsRefPtr<dom::Touch>& aTouch,
  6288                 nsIDocument* aLimitToDocument = nullptr)
  6290   nsCOMPtr<nsINode> node(do_QueryInterface(aTouch->mTarget));
  6291   if (node) {
  6292     nsIDocument* doc = node->GetCurrentDoc();
  6293     if (doc && (!aLimitToDocument || aLimitToDocument == doc)) {
  6294       nsIPresShell* presShell = doc->GetShell();
  6295       if (presShell) {
  6296         nsIFrame* frame = presShell->GetRootFrame();
  6297         if (frame) {
  6298           nsPoint pt(aTouch->mRefPoint.x, aTouch->mRefPoint.y);
  6299           nsCOMPtr<nsIWidget> widget = frame->GetView()->GetNearestWidget(&pt);
  6300           if (widget) {
  6301             WidgetTouchEvent event(true, NS_TOUCH_END, widget);
  6302             event.widget = widget;
  6303             event.time = PR_IntervalNow();
  6304             event.touches.AppendElement(aTouch);
  6305             nsEventStatus status;
  6306             widget->DispatchEvent(&event, status);
  6307             return;
  6313   if (!node || !aLimitToDocument || node->OwnerDoc() == aLimitToDocument) {
  6314     // We couldn't dispatch touchend. Remove the touch from gCaptureTouchList
  6315     // explicitly.
  6316     nsIPresShell::gCaptureTouchList->Remove(aTouch->Identifier());
  6320 static PLDHashOperator
  6321 AppendToTouchList(const uint32_t& aKey, nsRefPtr<dom::Touch>& aData, void *aTouchList)
  6323   nsTArray< nsRefPtr<dom::Touch> >* touches =
  6324     static_cast<nsTArray< nsRefPtr<dom::Touch> >*>(aTouchList);
  6325   aData->mChanged = false;
  6326   touches->AppendElement(aData);
  6327   return PL_DHASH_NEXT;
  6330 void
  6331 PresShell::EvictTouches()
  6333   nsTArray< nsRefPtr<dom::Touch> > touches;
  6334   gCaptureTouchList->Enumerate(&AppendToTouchList, &touches);
  6335   for (uint32_t i = 0; i < touches.Length(); ++i) {
  6336     EvictTouchPoint(touches[i], mDocument);
  6340 static PLDHashOperator
  6341 FindAnyTarget(const uint32_t& aKey, nsRefPtr<dom::Touch>& aData,
  6342               void* aAnyTarget)
  6344   if (aData) {
  6345     dom::EventTarget* target = aData->Target();
  6346     if (target) {
  6347       nsCOMPtr<nsIContent>* content =
  6348         static_cast<nsCOMPtr<nsIContent>*>(aAnyTarget);
  6349       *content = do_QueryInterface(target);
  6350       return PL_DHASH_STOP;
  6353   return PL_DHASH_NEXT;
  6356 nsIFrame* GetNearestFrameContainingPresShell(nsIPresShell* aPresShell)
  6358   nsView* view = aPresShell->GetViewManager()->GetRootView();
  6359   while (view && !view->GetFrame()) {
  6360     view = view->GetParent();
  6363   nsIFrame* frame = nullptr;
  6364   if (view) {
  6365     frame = view->GetFrame();
  6368   return frame;
  6371 static bool
  6372 FlushThrottledStyles(nsIDocument *aDocument, void *aData)
  6374   nsIPresShell* shell = aDocument->GetShell();
  6375   if (shell && shell->IsVisible()) {
  6376     nsPresContext* presContext = shell->GetPresContext();
  6377     if (presContext) {
  6378       presContext->TransitionManager()->UpdateAllThrottledStyles();
  6379       presContext->AnimationManager()->UpdateAllThrottledStyles();
  6383   return true;
  6386 static nsresult
  6387 DispatchPointerFromMouseOrTouch(PresShell* aShell,
  6388                                 nsIFrame* aFrame,
  6389                                 WidgetGUIEvent* aEvent,
  6390                                 bool aDontRetargetEvents,
  6391                                 nsEventStatus* aStatus)
  6393   uint32_t pointerMessage = 0;
  6394   if (aEvent->eventStructType == NS_MOUSE_EVENT) {
  6395     WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
  6396     // if it is not mouse then it is likely will come as touch event
  6397     if (!mouseEvent->convertToPointer) {
  6398       return NS_OK;
  6400     int16_t button = mouseEvent->button;
  6401     switch (mouseEvent->message) {
  6402     case NS_MOUSE_MOVE:
  6403       if (mouseEvent->buttons == 0) {
  6404         button = -1;
  6406       pointerMessage = NS_POINTER_MOVE;
  6407       break;
  6408     case NS_MOUSE_BUTTON_UP:
  6409       pointerMessage = NS_POINTER_UP;
  6410       break;
  6411     case NS_MOUSE_BUTTON_DOWN:
  6412       pointerMessage = NS_POINTER_DOWN;
  6413       break;
  6414     default:
  6415       return NS_OK;
  6418     WidgetPointerEvent event(*mouseEvent);
  6419     event.message = pointerMessage;
  6420     event.button = button;
  6421     event.pressure = event.buttons ?
  6422                      mouseEvent->pressure ? mouseEvent->pressure : 0.5f :
  6423                      0.0f;
  6424     event.convertToPointer = mouseEvent->convertToPointer = false;
  6425     aShell->HandleEvent(aFrame, &event, aDontRetargetEvents, aStatus);
  6426   } else if (aEvent->eventStructType == NS_TOUCH_EVENT) {
  6427     WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
  6428     // loop over all touches and dispatch pointer events on each touch
  6429     // copy the event
  6430     switch (touchEvent->message) {
  6431     case NS_TOUCH_MOVE:
  6432       pointerMessage = NS_POINTER_MOVE;
  6433       break;
  6434     case NS_TOUCH_END:
  6435       pointerMessage = NS_POINTER_UP;
  6436       break;
  6437     case NS_TOUCH_START:
  6438       pointerMessage = NS_POINTER_DOWN;
  6439       break;
  6440     case NS_TOUCH_CANCEL:
  6441       pointerMessage = NS_POINTER_CANCEL;
  6442       break;
  6443     default:
  6444       return NS_OK;
  6447     for (uint32_t i = 0; i < touchEvent->touches.Length(); ++i) {
  6448       mozilla::dom::Touch* touch = touchEvent->touches[i];
  6449       if (!touch || !touch->convertToPointer) {
  6450         continue;
  6453       WidgetPointerEvent event(touchEvent->mFlags.mIsTrusted, pointerMessage, touchEvent->widget);
  6454       event.isPrimary = i == 0;
  6455       event.pointerId = touch->Identifier();
  6456       event.refPoint.x = touch->mRefPoint.x;
  6457       event.refPoint.y = touch->mRefPoint.y;
  6458       event.modifiers = touchEvent->modifiers;
  6459       event.width = touch->RadiusX();
  6460       event.height = touch->RadiusY();
  6461       event.tiltX = touch->tiltX;
  6462       event.tiltY = touch->tiltY;
  6463       event.time = touchEvent->time;
  6464       event.mFlags = touchEvent->mFlags;
  6465       event.button = WidgetMouseEvent::eLeftButton;
  6466       event.buttons = WidgetMouseEvent::eLeftButtonFlag;
  6467       event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
  6468       event.convertToPointer = touch->convertToPointer = false;
  6469       aShell->HandleEvent(aFrame, &event, aDontRetargetEvents, aStatus);
  6472   return NS_OK;
  6475 class ReleasePointerCaptureCaller
  6477 public:
  6478   ReleasePointerCaptureCaller() :
  6479     mPointerId(0),
  6480     mContent(nullptr)
  6483   ~ReleasePointerCaptureCaller()
  6485     if (mContent) {
  6486       nsIPresShell::ReleasePointerCapturingContent(mPointerId, mContent);
  6489   void SetTarget(uint32_t aPointerId, nsIContent* aContent)
  6491     mPointerId = aPointerId;
  6492     mContent = aContent;
  6495 private:
  6496   int32_t mPointerId;
  6497   nsCOMPtr<nsIContent> mContent;
  6498 };
  6500 nsresult
  6501 PresShell::HandleEvent(nsIFrame* aFrame,
  6502                        WidgetGUIEvent* aEvent,
  6503                        bool aDontRetargetEvents,
  6504                        nsEventStatus* aEventStatus)
  6506 #ifdef MOZ_TASK_TRACER
  6507   // Make touch events, mouse events and hardware key events to be the source
  6508   // events of TaskTracer, and originate the rest correlation tasks from here.
  6509   SourceEventType type = SourceEventType::UNKNOWN;
  6510   if (WidgetTouchEvent* inputEvent = aEvent->AsTouchEvent()) {
  6511     type = SourceEventType::TOUCH;
  6512   } else if (WidgetMouseEvent* inputEvent = aEvent->AsMouseEvent()) {
  6513     type = SourceEventType::MOUSE;
  6514   } else if (WidgetKeyboardEvent* inputEvent = aEvent->AsKeyboardEvent()) {
  6515     type = SourceEventType::KEY;
  6517   AutoSourceEvent taskTracerEvent(type);
  6518 #endif
  6520   if (sPointerEventEnabled) {
  6521     DispatchPointerFromMouseOrTouch(this, aFrame, aEvent, aDontRetargetEvents, aEventStatus);
  6524   NS_ASSERTION(aFrame, "null frame");
  6526   if (mIsDestroying ||
  6527       (sDisableNonTestMouseEvents && !aEvent->mFlags.mIsSynthesizedForTests &&
  6528        aEvent->HasMouseEventMessage())) {
  6529     return NS_OK;
  6532   RecordMouseLocation(aEvent);
  6533   if (sPointerEventEnabled) {
  6534     UpdateActivePointerState(aEvent);
  6537   if (!nsContentUtils::IsSafeToRunScript())
  6538     return NS_OK;
  6540   nsIContent* capturingContent =
  6541     (aEvent->HasMouseEventMessage() ||
  6542      aEvent->eventStructType == NS_WHEEL_EVENT ? GetCapturingContent() :
  6543                                                  nullptr);
  6545   nsCOMPtr<nsIDocument> retargetEventDoc;
  6546   if (!aDontRetargetEvents) {
  6547     // key and IME related events should not cross top level window boundary.
  6548     // Basically, such input events should be fired only on focused widget.
  6549     // However, some IMEs might need to clean up composition after focused
  6550     // window is deactivated.  And also some tests on MozMill want to test key
  6551     // handling on deactivated window because MozMill window can be activated
  6552     // during tests.  So, there is no merit the events should be redirected to
  6553     // active window.  So, the events should be handled on the last focused
  6554     // content in the last focused DOM window in same top level window.
  6555     // Note, if no DOM window has been focused yet, we can discard the events.
  6556     if (aEvent->IsTargetedAtFocusedWindow()) {
  6557       nsCOMPtr<nsPIDOMWindow> window = GetFocusedDOMWindowInOurWindow();
  6558       // No DOM window in same top level window has not been focused yet,
  6559       // discard the events.
  6560       if (!window) {
  6561         return NS_OK;
  6564       retargetEventDoc = window->GetExtantDoc();
  6565       if (!retargetEventDoc)
  6566         return NS_OK;
  6567     } else if (capturingContent) {
  6568       // if the mouse is being captured then retarget the mouse event at the
  6569       // document that is being captured.
  6570       retargetEventDoc = capturingContent->GetCurrentDoc();
  6571 #ifdef ANDROID
  6572     } else if (aEvent->eventStructType == NS_TOUCH_EVENT) {
  6573       retargetEventDoc = GetTouchEventTargetDocument();
  6574 #endif
  6577     if (retargetEventDoc) {
  6578       nsCOMPtr<nsIPresShell> presShell = retargetEventDoc->GetShell();
  6579       if (!presShell)
  6580         return NS_OK;
  6582       if (presShell != this) {
  6583         nsIFrame* frame = presShell->GetRootFrame();
  6584         if (!frame) {
  6585           if (aEvent->message == NS_QUERY_TEXT_CONTENT ||
  6586               aEvent->IsContentCommandEvent()) {
  6587             return NS_OK;
  6590           frame = GetNearestFrameContainingPresShell(presShell);
  6593         if (!frame)
  6594           return NS_OK;
  6596         nsCOMPtr<nsIPresShell> shell = frame->PresContext()->GetPresShell();
  6597         return shell->HandleEvent(frame, aEvent, true, aEventStatus);
  6602   if (aEvent->eventStructType == NS_KEY_EVENT &&
  6603       mDocument && mDocument->EventHandlingSuppressed()) {
  6604     if (aEvent->message == NS_KEY_DOWN) {
  6605       mNoDelayedKeyEvents = true;
  6606     } else if (!mNoDelayedKeyEvents) {
  6607       DelayedEvent* event = new DelayedKeyEvent(aEvent->AsKeyboardEvent());
  6608       if (!mDelayedEvents.AppendElement(event)) {
  6609         delete event;
  6612     return NS_OK;
  6615   nsIFrame* frame = aFrame;
  6617   if (aEvent->IsUsingCoordinates()) {
  6618     ReleasePointerCaptureCaller releasePointerCaptureCaller;
  6619     if (nsLayoutUtils::AreAsyncAnimationsEnabled() && mDocument) {
  6620       if (aEvent->eventStructType == NS_TOUCH_EVENT) {
  6621         nsIDocument::UnlockPointer();
  6624       {  // scope for scriptBlocker.
  6625         nsAutoScriptBlocker scriptBlocker;
  6626         GetRootPresShell()->GetDocument()->
  6627           EnumerateSubDocuments(FlushThrottledStyles, nullptr);
  6629       frame = GetNearestFrameContainingPresShell(this);
  6632     NS_WARN_IF_FALSE(frame, "Nothing to handle this event!");
  6633     if (!frame)
  6634       return NS_OK;
  6636     nsPresContext* framePresContext = frame->PresContext();
  6637     nsPresContext* rootPresContext = framePresContext->GetRootPresContext();
  6638     NS_ASSERTION(rootPresContext == mPresContext->GetRootPresContext(),
  6639                  "How did we end up outside the connected prescontext/viewmanager hierarchy?");
  6640     // If we aren't starting our event dispatch from the root frame of the root prescontext,
  6641     // then someone must be capturing the mouse. In that case we don't want to search the popup
  6642     // list.
  6643     if (framePresContext == rootPresContext &&
  6644         frame == mFrameConstructor->GetRootFrame()) {
  6645       nsIFrame* popupFrame =
  6646         nsLayoutUtils::GetPopupFrameForEventCoordinates(rootPresContext, aEvent);
  6647       // If the popupFrame is an ancestor of the 'frame', the frame should
  6648       // handle the event, otherwise, the popup should handle it.
  6649       if (popupFrame &&
  6650           !nsContentUtils::ContentIsCrossDocDescendantOf(
  6651              framePresContext->GetPresShell()->GetDocument(),
  6652              popupFrame->GetContent())) {
  6653         frame = popupFrame;
  6657     bool captureRetarget = false;
  6658     if (capturingContent) {
  6659       // If a capture is active, determine if the docshell is visible. If not,
  6660       // clear the capture and target the mouse event normally instead. This
  6661       // would occur if the mouse button is held down while a tab change occurs.
  6662       // If the docshell is visible, look for a scrolling container.
  6663       bool vis;
  6664       nsCOMPtr<nsIBaseWindow> baseWin =
  6665         do_QueryInterface(mPresContext->GetContainerWeak());
  6666       if (baseWin && NS_SUCCEEDED(baseWin->GetVisibility(&vis)) && vis) {
  6667         captureRetarget = gCaptureInfo.mRetargetToElement;
  6668         if (!captureRetarget) {
  6669           // A check was already done above to ensure that capturingContent is
  6670           // in this presshell.
  6671           NS_ASSERTION(capturingContent->GetCurrentDoc() == GetDocument(),
  6672                        "Unexpected document");
  6673           nsIFrame* captureFrame = capturingContent->GetPrimaryFrame();
  6674           if (captureFrame) {
  6675             if (capturingContent->Tag() == nsGkAtoms::select &&
  6676                 capturingContent->IsHTML()) {
  6677               // a dropdown <select> has a child in its selectPopupList and we should
  6678               // capture on that instead.
  6679               nsIFrame* childFrame = captureFrame->GetChildList(nsIFrame::kSelectPopupList).FirstChild();
  6680               if (childFrame) {
  6681                 captureFrame = childFrame;
  6685             // scrollable frames should use the scrolling container as
  6686             // the root instead of the document
  6687             nsIScrollableFrame* scrollFrame = do_QueryFrame(captureFrame);
  6688             if (scrollFrame) {
  6689               frame = scrollFrame->GetScrolledFrame();
  6694       else {
  6695         ClearMouseCapture(nullptr);
  6696         capturingContent = nullptr;
  6700     // all touch events except for touchstart use a captured target
  6701     if (aEvent->eventStructType == NS_TOUCH_EVENT &&
  6702         aEvent->message != NS_TOUCH_START) {
  6703       captureRetarget = true;
  6706     WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
  6707     bool isWindowLevelMouseExit = (aEvent->message == NS_MOUSE_EXIT) &&
  6708       (mouseEvent && mouseEvent->exit == WidgetMouseEvent::eTopLevel);
  6710     // Get the frame at the event point. However, don't do this if we're
  6711     // capturing and retargeting the event because the captured frame will
  6712     // be used instead below. Also keep using the root frame if we're dealing
  6713     // with a window-level mouse exit event since we want to start sending
  6714     // mouse out events at the root EventStateManager.
  6715     if (!captureRetarget && !isWindowLevelMouseExit) {
  6716       nsPoint eventPoint;
  6717       uint32_t flags = 0;
  6718       if (aEvent->message == NS_TOUCH_START) {
  6719         flags |= INPUT_IGNORE_ROOT_SCROLL_FRAME;
  6720         WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
  6721         // if this is a continuing session, ensure that all these events are
  6722         // in the same document by taking the target of the events already in
  6723         // the capture list
  6724         nsCOMPtr<nsIContent> anyTarget;
  6725         if (gCaptureTouchList->Count() > 0 && touchEvent->touches.Length() > 1) {
  6726           gCaptureTouchList->Enumerate(&FindAnyTarget, &anyTarget);
  6727         } else {
  6728           gPreventMouseEvents = false;
  6731         for (int32_t i = touchEvent->touches.Length(); i; ) {
  6732           --i;
  6733           dom::Touch* touch = touchEvent->touches[i];
  6735           int32_t id = touch->Identifier();
  6736           if (!gCaptureTouchList->Get(id, nullptr)) {
  6737             // find the target for this touch
  6738             eventPoint = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent,
  6739                                                               touch->mRefPoint,
  6740                                                               frame);
  6741             nsIFrame* target = FindFrameTargetedByInputEvent(aEvent,
  6742                                                              frame,
  6743                                                              eventPoint,
  6744                                                              flags);
  6745             if (target && !anyTarget) {
  6746               target->GetContentForEvent(aEvent, getter_AddRefs(anyTarget));
  6747               while (anyTarget && !anyTarget->IsElement()) {
  6748                 anyTarget = anyTarget->GetParent();
  6750               touch->SetTarget(anyTarget);
  6751             } else {
  6752               nsIFrame* newTargetFrame = nullptr;
  6753               for (nsIFrame* f = target; f;
  6754                    f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
  6755                 if (f->PresContext()->Document() == anyTarget->OwnerDoc()) {
  6756                   newTargetFrame = f;
  6757                   break;
  6759                 // We must be in a subdocument so jump directly to the root frame.
  6760                 // GetParentOrPlaceholderForCrossDoc gets called immediately to
  6761                 // jump up to the containing document.
  6762                 f = f->PresContext()->GetPresShell()->GetRootFrame();
  6765               // if we couldn't find a target frame in the same document as
  6766               // anyTarget, remove the touch from the capture touch list, as
  6767               // well as the event->touches array. touchmove events that aren't
  6768               // in the captured touch list will be discarded
  6769               if (!newTargetFrame) {
  6770                 touchEvent->touches.RemoveElementAt(i);
  6771               } else {
  6772                 target = newTargetFrame;
  6773                 nsCOMPtr<nsIContent> targetContent;
  6774                 target->GetContentForEvent(aEvent, getter_AddRefs(targetContent));
  6775                 while (targetContent && !targetContent->IsElement()) {
  6776                   targetContent = targetContent->GetParent();
  6778                 touch->SetTarget(targetContent);
  6781             if (target) {
  6782               frame = target;
  6784           } else {
  6785             // This touch is an old touch, we need to ensure that is not
  6786             // marked as changed and set its target correctly
  6787             touch->mChanged = false;
  6788             int32_t id = touch->Identifier();
  6790             nsRefPtr<dom::Touch> oldTouch = gCaptureTouchList->GetWeak(id);
  6791             if (oldTouch) {
  6792               touch->SetTarget(oldTouch->mTarget);
  6796       } else {
  6797         eventPoint = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, frame);
  6799       if (mouseEvent && mouseEvent->eventStructType == NS_MOUSE_EVENT &&
  6800           mouseEvent->ignoreRootScrollFrame) {
  6801         flags |= INPUT_IGNORE_ROOT_SCROLL_FRAME;
  6803       nsIFrame* target =
  6804         FindFrameTargetedByInputEvent(aEvent, frame, eventPoint, flags);
  6805       if (target) {
  6806         frame = target;
  6810     // if a node is capturing the mouse, check if the event needs to be
  6811     // retargeted at the capturing content instead. This will be the case when
  6812     // capture retargeting is being used, no frame was found or the frame's
  6813     // content is not a descendant of the capturing content.
  6814     if (capturingContent &&
  6815         (gCaptureInfo.mRetargetToElement || !frame->GetContent() ||
  6816          !nsContentUtils::ContentIsCrossDocDescendantOf(frame->GetContent(),
  6817                                                         capturingContent))) {
  6818       // A check was already done above to ensure that capturingContent is
  6819       // in this presshell.
  6820       NS_ASSERTION(capturingContent->GetCurrentDoc() == GetDocument(),
  6821                    "Unexpected document");
  6822       nsIFrame* capturingFrame = capturingContent->GetPrimaryFrame();
  6823       if (capturingFrame) {
  6824         frame = capturingFrame;
  6828     if (aEvent->eventStructType == NS_POINTER_EVENT &&
  6829         aEvent->message != NS_POINTER_DOWN) {
  6830       if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) {
  6831         uint32_t pointerId = pointerEvent->pointerId;
  6832         nsIContent* pointerCapturingContent = GetPointerCapturingContent(pointerId);
  6834         if (pointerCapturingContent) {
  6835           if (nsIFrame* capturingFrame = pointerCapturingContent->GetPrimaryFrame()) {
  6836             frame = capturingFrame;
  6839           if (pointerEvent->message == NS_POINTER_UP ||
  6840               pointerEvent->message == NS_POINTER_CANCEL) {
  6841             // Implicitly releasing capture for given pointer.
  6842             // LOST_POINTER_CAPTURE should be send after NS_POINTER_UP or NS_POINTER_CANCEL.
  6843             releasePointerCaptureCaller.SetTarget(pointerId, pointerCapturingContent);
  6849     // Suppress mouse event if it's being targeted at an element inside
  6850     // a document which needs events suppressed
  6851     if (aEvent->eventStructType == NS_MOUSE_EVENT &&
  6852         frame->PresContext()->Document()->EventHandlingSuppressed()) {
  6853       if (aEvent->message == NS_MOUSE_BUTTON_DOWN) {
  6854         mNoDelayedMouseEvents = true;
  6855       } else if (!mNoDelayedMouseEvents && aEvent->message == NS_MOUSE_BUTTON_UP) {
  6856         DelayedEvent* event = new DelayedMouseEvent(aEvent->AsMouseEvent());
  6857         if (!mDelayedEvents.AppendElement(event)) {
  6858           delete event;
  6862       return NS_OK;
  6865     PresShell* shell =
  6866         static_cast<PresShell*>(frame->PresContext()->PresShell());
  6867     switch (aEvent->message) {
  6868       case NS_TOUCH_MOVE:
  6869       case NS_TOUCH_CANCEL:
  6870       case NS_TOUCH_END: {
  6871         // get the correct shell to dispatch to
  6872         WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
  6873         nsTArray< nsRefPtr<dom::Touch> >& touches = touchEvent->touches;
  6874         for (uint32_t i = 0; i < touches.Length(); ++i) {
  6875           dom::Touch* touch = touches[i];
  6876           if (!touch) {
  6877             break;
  6880           nsRefPtr<dom::Touch> oldTouch =
  6881             gCaptureTouchList->GetWeak(touch->Identifier());
  6882           if (!oldTouch) {
  6883             break;
  6886           nsCOMPtr<nsIContent> content =
  6887             do_QueryInterface(oldTouch->Target());
  6888           if (!content) {
  6889             break;
  6892           nsIFrame* contentFrame = content->GetPrimaryFrame();
  6893           if (!contentFrame) {
  6894             break;
  6897           shell = static_cast<PresShell*>(
  6898                       contentFrame->PresContext()->PresShell());
  6899           if (shell) {
  6900             break;
  6903         break;
  6907     // Check if we have an active EventStateManager which isn't the
  6908     // EventStateManager of the current PresContext.
  6909     // If that is the case, and mouse is over some ancestor document,
  6910     // forward event handling to the active document.
  6911     // This way content can get mouse events even when
  6912     // mouse is over the chrome or outside the window.
  6913     //
  6914     // Note, currently for backwards compatibility we don't forward mouse events
  6915     // to the active document when mouse is over some subdocument.
  6916     EventStateManager* activeESM =
  6917       EventStateManager::GetActiveEventStateManager();
  6918     if (activeESM && aEvent->HasMouseEventMessage() &&
  6919         activeESM != shell->GetPresContext()->EventStateManager() &&
  6920         static_cast<EventStateManager*>(activeESM)->GetPresContext()) {
  6921       nsIPresShell* activeShell =
  6922         static_cast<EventStateManager*>(activeESM)->GetPresContext()->
  6923           GetPresShell();
  6924       if (activeShell &&
  6925           nsContentUtils::ContentIsCrossDocDescendantOf(activeShell->GetDocument(),
  6926                                                         shell->GetDocument())) {
  6927         shell = static_cast<PresShell*>(activeShell);
  6928         frame = shell->GetRootFrame();
  6932     if (shell != this) {
  6933       // Handle the event in the correct shell.
  6934       // Prevent deletion until we're done with event handling (bug 336582).
  6935       nsCOMPtr<nsIPresShell> kungFuDeathGrip(shell);
  6936       // We pass the subshell's root frame as the frame to start from. This is
  6937       // the only correct alternative; if the event was captured then it
  6938       // must have been captured by us or some ancestor shell and we
  6939       // now ask the subshell to dispatch it normally.
  6940       return shell->HandlePositionedEvent(frame, aEvent, aEventStatus);
  6943     return HandlePositionedEvent(frame, aEvent, aEventStatus);
  6946   nsresult rv = NS_OK;
  6948   if (frame) {
  6949     PushCurrentEventInfo(nullptr, nullptr);
  6951     // key and IME related events go to the focused frame in this DOM window.
  6952     if (aEvent->IsTargetedAtFocusedContent()) {
  6953       mCurrentEventContent = nullptr;
  6955       nsCOMPtr<nsPIDOMWindow> window =
  6956         do_QueryInterface(mDocument->GetWindow());
  6957       nsCOMPtr<nsPIDOMWindow> focusedWindow;
  6958       nsCOMPtr<nsIContent> eventTarget =
  6959         nsFocusManager::GetFocusedDescendant(window, false,
  6960                                              getter_AddRefs(focusedWindow));
  6962       // otherwise, if there is no focused content or the focused content has
  6963       // no frame, just use the root content. This ensures that key events
  6964       // still get sent to the window properly if nothing is focused or if a
  6965       // frame goes away while it is focused.
  6966       if (!eventTarget || !eventTarget->GetPrimaryFrame()) {
  6967         nsCOMPtr<nsIDOMHTMLDocument> htmlDoc = do_QueryInterface(mDocument);
  6968         if (htmlDoc) {
  6969           nsCOMPtr<nsIDOMHTMLElement> body;
  6970           htmlDoc->GetBody(getter_AddRefs(body));
  6971           eventTarget = do_QueryInterface(body);
  6972           if (!eventTarget) {
  6973             eventTarget = mDocument->GetRootElement();
  6975         } else {
  6976           eventTarget = mDocument->GetRootElement();
  6980       if (aEvent->message == NS_KEY_DOWN) {
  6981         NS_IF_RELEASE(gKeyDownTarget);
  6982         NS_IF_ADDREF(gKeyDownTarget = eventTarget);
  6984       else if ((aEvent->message == NS_KEY_PRESS || aEvent->message == NS_KEY_UP) &&
  6985                gKeyDownTarget) {
  6986         // If a different element is now focused for the keypress/keyup event
  6987         // than what was focused during the keydown event, check if the new
  6988         // focused element is not in a chrome document any more, and if so,
  6989         // retarget the event back at the keydown target. This prevents a
  6990         // content area from grabbing the focus from chrome in-between key
  6991         // events.
  6992         if (eventTarget &&
  6993             nsContentUtils::IsChromeDoc(gKeyDownTarget->GetCurrentDoc()) !=
  6994             nsContentUtils::IsChromeDoc(eventTarget->GetCurrentDoc())) {
  6995           eventTarget = gKeyDownTarget;
  6998         if (aEvent->message == NS_KEY_UP) {
  6999           NS_RELEASE(gKeyDownTarget);
  7003       mCurrentEventFrame = nullptr;
  7004       nsIDocument* targetDoc = eventTarget ? eventTarget->OwnerDoc() : nullptr;
  7005       if (targetDoc && targetDoc != mDocument) {
  7006         PopCurrentEventInfo();
  7007         nsCOMPtr<nsIPresShell> shell = targetDoc->GetShell();
  7008         if (shell) {
  7009           rv = static_cast<PresShell*>(shell.get())->
  7010             HandleRetargetedEvent(aEvent, aEventStatus, eventTarget);
  7012         return rv;
  7013       } else {
  7014         mCurrentEventContent = eventTarget;
  7017       if (!GetCurrentEventContent() || !GetCurrentEventFrame() ||
  7018           InZombieDocument(mCurrentEventContent)) {
  7019         rv = RetargetEventToParent(aEvent, aEventStatus);
  7020         PopCurrentEventInfo();
  7021         return rv;
  7023     } else {
  7024       mCurrentEventFrame = frame;
  7026     if (GetCurrentEventFrame()) {
  7027       rv = HandleEventInternal(aEvent, aEventStatus);
  7030 #ifdef DEBUG
  7031     ShowEventTargetDebug();
  7032 #endif
  7033     PopCurrentEventInfo();
  7034   } else {
  7035     // Activation events need to be dispatched even if no frame was found, since
  7036     // we don't want the focus to be out of sync.
  7038     if (!NS_EVENT_NEEDS_FRAME(aEvent)) {
  7039       mCurrentEventFrame = nullptr;
  7040       return HandleEventInternal(aEvent, aEventStatus);
  7042     else if (aEvent->HasKeyEventMessage()) {
  7043       // Keypress events in new blank tabs should not be completely thrown away.
  7044       // Retarget them -- the parent chrome shell might make use of them.
  7045       return RetargetEventToParent(aEvent, aEventStatus);
  7049   return rv;
  7052 #ifdef ANDROID
  7053 nsIDocument*
  7054 PresShell::GetTouchEventTargetDocument()
  7056   nsPresContext* context = GetPresContext();
  7057   if (!context || !context->IsRoot()) {
  7058     return nullptr;
  7061   nsCOMPtr<nsIDocShellTreeItem> shellAsTreeItem = context->GetDocShell();
  7062   if (!shellAsTreeItem) {
  7063     return nullptr;
  7066   nsCOMPtr<nsIDocShellTreeOwner> owner;
  7067   shellAsTreeItem->GetTreeOwner(getter_AddRefs(owner));
  7068   if (!owner) {
  7069     return nullptr;
  7072   // now get the primary content shell (active tab)
  7073   nsCOMPtr<nsIDocShellTreeItem> item;
  7074   owner->GetPrimaryContentShell(getter_AddRefs(item));
  7075   nsCOMPtr<nsIDocShell> childDocShell = do_QueryInterface(item);
  7076   nsCOMPtr<nsIDocument> result = do_GetInterface(childDocShell);
  7077   return result;
  7079 #endif
  7081 #ifdef DEBUG
  7082 void
  7083 PresShell::ShowEventTargetDebug()
  7085   if (nsFrame::GetShowEventTargetFrameBorder() &&
  7086       GetCurrentEventFrame()) {
  7087     if (mDrawEventTargetFrame) {
  7088       mDrawEventTargetFrame->InvalidateFrame();
  7091     mDrawEventTargetFrame = mCurrentEventFrame;
  7092     mDrawEventTargetFrame->InvalidateFrame();
  7095 #endif
  7097 nsresult
  7098 PresShell::HandlePositionedEvent(nsIFrame* aTargetFrame,
  7099                                  WidgetGUIEvent* aEvent,
  7100                                  nsEventStatus* aEventStatus)
  7102   nsresult rv = NS_OK;
  7104   PushCurrentEventInfo(nullptr, nullptr);
  7106   mCurrentEventFrame = aTargetFrame;
  7108   if (mCurrentEventFrame) {
  7109     nsCOMPtr<nsIContent> targetElement;
  7110     mCurrentEventFrame->GetContentForEvent(aEvent,
  7111                                            getter_AddRefs(targetElement));
  7113     // If there is no content for this frame, target it anyway.  Some
  7114     // frames can be targeted but do not have content, particularly
  7115     // windows with scrolling off.
  7116     if (targetElement) {
  7117       // Bug 103055, bug 185889: mouse events apply to *elements*, not all
  7118       // nodes.  Thus we get the nearest element parent here.
  7119       // XXX we leave the frame the same even if we find an element
  7120       // parent, so that the text frame will receive the event (selection
  7121       // and friends are the ones who care about that anyway)
  7122       //
  7123       // We use weak pointers because during this tight loop, the node
  7124       // will *not* go away.  And this happens on every mousemove.
  7125       while (targetElement && !targetElement->IsElement()) {
  7126         targetElement = targetElement->GetParent();
  7129       // If we found an element, target it.  Otherwise, target *nothing*.
  7130       if (!targetElement) {
  7131         mCurrentEventContent = nullptr;
  7132         mCurrentEventFrame = nullptr;
  7133       } else if (targetElement != mCurrentEventContent) {
  7134         mCurrentEventContent = targetElement;
  7139   if (GetCurrentEventFrame()) {
  7140     rv = HandleEventInternal(aEvent, aEventStatus);
  7143 #ifdef DEBUG
  7144   ShowEventTargetDebug();
  7145 #endif
  7146   PopCurrentEventInfo();
  7147   return rv;
  7150 nsresult
  7151 PresShell::HandleEventWithTarget(WidgetEvent* aEvent, nsIFrame* aFrame,
  7152                                  nsIContent* aContent, nsEventStatus* aStatus)
  7154 #if DEBUG
  7155   MOZ_ASSERT(!aFrame || aFrame->PresContext()->GetPresShell() == this,
  7156              "wrong shell");
  7157   if (aContent) {
  7158     nsIDocument* doc = aContent->GetCurrentDoc();
  7159     NS_ASSERTION(doc, "event for content that isn't in a document");
  7160     NS_ASSERTION(!doc || doc->GetShell() == this, "wrong shell");
  7162 #endif
  7163   NS_ENSURE_STATE(!aContent || aContent->GetCurrentDoc() == mDocument);
  7165   PushCurrentEventInfo(aFrame, aContent);
  7166   nsresult rv = HandleEventInternal(aEvent, aStatus);
  7167   PopCurrentEventInfo();
  7168   return rv;
  7171 nsresult
  7172 PresShell::HandleEventInternal(WidgetEvent* aEvent, nsEventStatus* aStatus)
  7174   nsRefPtr<EventStateManager> manager = mPresContext->EventStateManager();
  7175   nsresult rv = NS_OK;
  7177   if (!NS_EVENT_NEEDS_FRAME(aEvent) || GetCurrentEventFrame()) {
  7178     bool touchIsNew = false;
  7179     bool isHandlingUserInput = false;
  7181     // XXX How about IME events and input events for plugins?
  7182     if (aEvent->mFlags.mIsTrusted) {
  7183       switch (aEvent->message) {
  7184       case NS_KEY_PRESS:
  7185       case NS_KEY_DOWN:
  7186       case NS_KEY_UP: {
  7187         nsIDocument* doc = GetCurrentEventContent() ?
  7188                            mCurrentEventContent->OwnerDoc() : nullptr;
  7189         nsIDocument* fullscreenAncestor = nullptr;
  7190         if (aEvent->AsKeyboardEvent()->keyCode == NS_VK_ESCAPE) {
  7191           if ((fullscreenAncestor = nsContentUtils::GetFullscreenAncestor(doc))) {
  7192             // Prevent default action on ESC key press when exiting
  7193             // DOM fullscreen mode. This prevents the browser ESC key
  7194             // handler from stopping all loads in the document, which
  7195             // would cause <video> loads to stop.
  7196             aEvent->mFlags.mDefaultPrevented = true;
  7197             aEvent->mFlags.mOnlyChromeDispatch = true;
  7199             if (aEvent->message == NS_KEY_UP) {
  7200               // ESC key released while in DOM fullscreen mode.
  7201               // If fullscreen is running in content-only mode, exit the target
  7202               // doctree branch from fullscreen, otherwise fully exit all
  7203               // browser windows and documents from fullscreen mode.
  7204               // Note: in the content-only fullscreen case, we pass the
  7205               // fullscreenAncestor since |doc| may not actually be fullscreen
  7206               // here, and ExitFullscreen() has no affect when passed a
  7207               // non-fullscreen document.
  7208               nsIDocument::ExitFullscreen(
  7209                 nsContentUtils::IsFullscreenApiContentOnly() ? fullscreenAncestor : nullptr,
  7210                 /* async */ true);
  7213           nsCOMPtr<nsIDocument> pointerLockedDoc =
  7214             do_QueryReferent(EventStateManager::sPointerLockedDoc);
  7215           if (pointerLockedDoc) {
  7216             aEvent->mFlags.mDefaultPrevented = true;
  7217             aEvent->mFlags.mOnlyChromeDispatch = true;
  7218             if (aEvent->message == NS_KEY_UP) {
  7219               nsIDocument::UnlockPointer();
  7223         // Else not full-screen mode or key code is unrestricted, fall
  7224         // through to normal handling.
  7226       case NS_MOUSE_BUTTON_DOWN:
  7227       case NS_MOUSE_BUTTON_UP:
  7228         isHandlingUserInput = true;
  7229         break;
  7230       case NS_TOUCH_START: {
  7231         WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
  7232         // if there is only one touch in this touchstart event, assume that it is
  7233         // the start of a new touch session and evict any old touches in the
  7234         // queue
  7235         if (touchEvent->touches.Length() == 1) {
  7236           nsTArray< nsRefPtr<dom::Touch> > touches;
  7237           gCaptureTouchList->Enumerate(&AppendToTouchList, (void *)&touches);
  7238           for (uint32_t i = 0; i < touches.Length(); ++i) {
  7239             EvictTouchPoint(touches[i]);
  7242         // Add any new touches to the queue
  7243         for (uint32_t i = 0; i < touchEvent->touches.Length(); ++i) {
  7244           dom::Touch* touch = touchEvent->touches[i];
  7245           int32_t id = touch->Identifier();
  7246           if (!gCaptureTouchList->Get(id, nullptr)) {
  7247             // If it is not already in the queue, it is a new touch
  7248             touch->mChanged = true;
  7250           touch->mMessage = aEvent->message;
  7251           gCaptureTouchList->Put(id, touch);
  7253         break;
  7255       case NS_TOUCH_CANCEL:
  7256       case NS_TOUCH_END: {
  7257         // Remove the changed touches
  7258         // need to make sure we only remove touches that are ending here
  7259         WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
  7260         nsTArray< nsRefPtr<dom::Touch> >& touches = touchEvent->touches;
  7261         for (uint32_t i = 0; i < touches.Length(); ++i) {
  7262           dom::Touch* touch = touches[i];
  7263           if (!touch) {
  7264             continue;
  7266           touch->mMessage = aEvent->message;
  7267           touch->mChanged = true;
  7269           int32_t id = touch->Identifier();
  7270           nsRefPtr<dom::Touch> oldTouch = gCaptureTouchList->GetWeak(id);
  7271           if (!oldTouch) {
  7272             continue;
  7274           nsCOMPtr<EventTarget> targetPtr = oldTouch->mTarget;
  7276           mCurrentEventContent = do_QueryInterface(targetPtr);
  7277           touch->SetTarget(targetPtr);
  7278           gCaptureTouchList->Remove(id);
  7280         // add any touches left in the touch list, but ensure changed=false
  7281         gCaptureTouchList->Enumerate(&AppendToTouchList, (void *)&touches);
  7282         break;
  7284       case NS_TOUCH_MOVE: {
  7285         // Check for touches that changed. Mark them add to queue
  7286         WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
  7287         nsTArray< nsRefPtr<dom::Touch> >& touches = touchEvent->touches;
  7288         bool haveChanged = false;
  7289         for (int32_t i = touches.Length(); i; ) {
  7290           --i;
  7291           dom::Touch* touch = touches[i];
  7292           if (!touch) {
  7293             continue;
  7295           int32_t id = touch->Identifier();
  7296           touch->mMessage = aEvent->message;
  7298           nsRefPtr<dom::Touch> oldTouch = gCaptureTouchList->GetWeak(id);
  7299           if (!oldTouch) {
  7300             touches.RemoveElementAt(i);
  7301             continue;
  7303           if (!touch->Equals(oldTouch)) {
  7304             touch->mChanged = true;
  7305             haveChanged = true;
  7308           nsCOMPtr<dom::EventTarget> targetPtr = oldTouch->mTarget;
  7309           if (!targetPtr) {
  7310             touches.RemoveElementAt(i);
  7311             continue;
  7313           touch->SetTarget(targetPtr);
  7315           gCaptureTouchList->Put(id, touch);
  7316           // if we're moving from touchstart to touchmove for this touch
  7317           // we allow preventDefault to prevent mouse events
  7318           if (oldTouch->mMessage != touch->mMessage) {
  7319             touchIsNew = true;
  7322         // is nothing has changed, we should just return
  7323         if (!haveChanged) {
  7324           if (gPreventMouseEvents) {
  7325               *aStatus = nsEventStatus_eConsumeNoDefault;
  7327           return NS_OK;
  7329         break;
  7331       case NS_DRAGDROP_DROP:
  7332         nsCOMPtr<nsIDragSession> session = nsContentUtils::GetDragSession();
  7333         if (session) {
  7334           bool onlyChromeDrop = false;
  7335           session->GetOnlyChromeDrop(&onlyChromeDrop);
  7336           if (onlyChromeDrop) {
  7337             aEvent->mFlags.mOnlyChromeDispatch = true;
  7340         break;
  7344     if (aEvent->message == NS_CONTEXTMENU) {
  7345       WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
  7346       if (mouseEvent->context == WidgetMouseEvent::eContextMenuKey &&
  7347           !AdjustContextMenuKeyEvent(mouseEvent)) {
  7348         return NS_OK;
  7350       if (mouseEvent->IsShift()) {
  7351         aEvent->mFlags.mOnlyChromeDispatch = true;
  7352         aEvent->mFlags.mRetargetToNonNativeAnonymous = true;
  7356     AutoHandlingUserInputStatePusher userInpStatePusher(isHandlingUserInput,
  7357                                                         aEvent, mDocument);
  7359     if (aEvent->mFlags.mIsTrusted && aEvent->message == NS_MOUSE_MOVE) {
  7360       nsIPresShell::AllowMouseCapture(
  7361         EventStateManager::GetActiveEventStateManager() == manager);
  7364     nsAutoPopupStatePusher popupStatePusher(
  7365                              Event::GetEventPopupControlState(aEvent));
  7367     // FIXME. If the event was reused, we need to clear the old target,
  7368     // bug 329430
  7369     aEvent->target = nullptr;
  7371     // 1. Give event to event manager for pre event state changes and
  7372     //    generation of synthetic events.
  7373     rv = manager->PreHandleEvent(mPresContext, aEvent, mCurrentEventFrame, aStatus);
  7375     // 2. Give event to the DOM for third party and JS use.
  7376     if (NS_SUCCEEDED(rv)) {
  7377       bool wasHandlingKeyBoardEvent =
  7378         nsContentUtils::IsHandlingKeyBoardEvent();
  7379       if (aEvent->eventStructType == NS_KEY_EVENT) {
  7380         nsContentUtils::SetIsHandlingKeyBoardEvent(true);
  7382       if (aEvent->IsAllowedToDispatchDOMEvent()) {
  7383         nsPresShellEventCB eventCB(this);
  7384         if (aEvent->eventStructType == NS_TOUCH_EVENT) {
  7385           DispatchTouchEvent(aEvent, aStatus, &eventCB, touchIsNew);
  7386         } else {
  7387           nsCOMPtr<nsINode> eventTarget = mCurrentEventContent.get();
  7388           nsPresShellEventCB* eventCBPtr = &eventCB;
  7389           if (!eventTarget) {
  7390             nsCOMPtr<nsIContent> targetContent;
  7391             if (mCurrentEventFrame) {
  7392               rv = mCurrentEventFrame->
  7393                      GetContentForEvent(aEvent, getter_AddRefs(targetContent));
  7395             if (NS_SUCCEEDED(rv) && targetContent) {
  7396               eventTarget = do_QueryInterface(targetContent);
  7397             } else if (mDocument) {
  7398               eventTarget = do_QueryInterface(mDocument);
  7399               // If we don't have any content, the callback wouldn't probably
  7400               // do nothing.
  7401               eventCBPtr = nullptr;
  7404           if (eventTarget) {
  7405             if (aEvent->eventStructType == NS_COMPOSITION_EVENT ||
  7406                 aEvent->eventStructType == NS_TEXT_EVENT) {
  7407               IMEStateManager::DispatchCompositionEvent(eventTarget,
  7408                 mPresContext, aEvent, aStatus, eventCBPtr);
  7409             } else {
  7410               EventDispatcher::Dispatch(eventTarget, mPresContext,
  7411                                         aEvent, nullptr, aStatus, eventCBPtr);
  7417       nsContentUtils::SetIsHandlingKeyBoardEvent(wasHandlingKeyBoardEvent);
  7419       // 3. Give event to event manager for post event state changes and
  7420       //    generation of synthetic events.
  7421       if (!mIsDestroying && NS_SUCCEEDED(rv)) {
  7422         rv = manager->PostHandleEvent(mPresContext, aEvent,
  7423                                       GetCurrentEventFrame(), aStatus);
  7427     if (aEvent->message == NS_MOUSE_BUTTON_UP) {
  7428       // reset the capturing content now that the mouse button is up
  7429       SetCapturingContent(nullptr, 0);
  7430     } else if (aEvent->message == NS_MOUSE_MOVE) {
  7431       nsIPresShell::AllowMouseCapture(false);
  7434   return rv;
  7437 void
  7438 nsIPresShell::DispatchGotOrLostPointerCaptureEvent(bool aIsGotCapture,
  7439                                                    uint32_t aPointerId,
  7440                                                    nsIContent* aCaptureTarget)
  7442   PointerEventInit init;
  7443   init.mPointerId = aPointerId;
  7444   init.mBubbles = true;
  7445   nsRefPtr<mozilla::dom::PointerEvent> event;
  7446   event = PointerEvent::Constructor(aCaptureTarget,
  7447                                     aIsGotCapture
  7448                                       ? NS_LITERAL_STRING("gotpointercapture")
  7449                                       : NS_LITERAL_STRING("lostpointercapture"),
  7450                                     init);
  7451   if (event) {
  7452     bool dummy;
  7453     aCaptureTarget->DispatchEvent(event->InternalDOMEvent(), &dummy);
  7457 void
  7458 PresShell::DispatchTouchEvent(WidgetEvent* aEvent,
  7459                               nsEventStatus* aStatus,
  7460                               nsPresShellEventCB* aEventCB,
  7461                               bool aTouchIsNew)
  7463   // calling preventDefault on touchstart or the first touchmove for a
  7464   // point prevents mouse events
  7465   bool canPrevent = aEvent->message == NS_TOUCH_START ||
  7466               (aEvent->message == NS_TOUCH_MOVE && aTouchIsNew);
  7467   bool preventDefault = false;
  7468   nsEventStatus tmpStatus = nsEventStatus_eIgnore;
  7469   WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
  7471   // loop over all touches and dispatch events on any that have changed
  7472   for (uint32_t i = 0; i < touchEvent->touches.Length(); ++i) {
  7473     dom::Touch* touch = touchEvent->touches[i];
  7474     if (!touch || !touch->mChanged) {
  7475       continue;
  7478     nsCOMPtr<EventTarget> targetPtr = touch->mTarget;
  7479     nsCOMPtr<nsIContent> content = do_QueryInterface(targetPtr);
  7480     if (!content) {
  7481       continue;
  7484     nsIDocument* doc = content->OwnerDoc();
  7485     nsIContent* capturingContent = GetCapturingContent();
  7486     if (capturingContent) {
  7487       if (capturingContent->OwnerDoc() != doc) {
  7488         // Wrong document, don't dispatch anything.
  7489         continue;
  7491       content = capturingContent;
  7493     // copy the event
  7494     WidgetTouchEvent newEvent(touchEvent->mFlags.mIsTrusted,
  7495                               touchEvent->message, touchEvent->widget);
  7496     newEvent.AssignTouchEventData(*touchEvent, false);
  7497     newEvent.target = targetPtr;
  7499     nsRefPtr<PresShell> contentPresShell;
  7500     if (doc == mDocument) {
  7501       contentPresShell = static_cast<PresShell*>(doc->GetShell());
  7502       if (contentPresShell) {
  7503         //XXXsmaug huge hack. Pushing possibly capturing content,
  7504         //         even though event target is something else.
  7505         contentPresShell->PushCurrentEventInfo(
  7506             content->GetPrimaryFrame(), content);
  7510     nsIPresShell *presShell = doc->GetShell();
  7511     if (!presShell) {
  7512       continue;
  7515     nsPresContext *context = presShell->GetPresContext();
  7517     tmpStatus = nsEventStatus_eIgnore;
  7518     EventDispatcher::Dispatch(targetPtr, context,
  7519                               &newEvent, nullptr, &tmpStatus, aEventCB);
  7520     if (nsEventStatus_eConsumeNoDefault == tmpStatus ||
  7521         newEvent.mFlags.mMultipleActionsPrevented) {
  7522       preventDefault = true;
  7525     if (newEvent.mFlags.mMultipleActionsPrevented) {
  7526       touchEvent->mFlags.mMultipleActionsPrevented = true;
  7529     if (contentPresShell) {
  7530       contentPresShell->PopCurrentEventInfo();
  7534   // if preventDefault was called on any of the events dispatched
  7535   // and this is touchstart, or the first touchmove, widget should consume
  7536   // other events that would be associated with this touch session
  7537   if (preventDefault && canPrevent) {
  7538     gPreventMouseEvents = true;
  7541   if (gPreventMouseEvents) {
  7542     *aStatus = nsEventStatus_eConsumeNoDefault;
  7543   } else {
  7544     *aStatus = nsEventStatus_eIgnore;
  7548 // Dispatch event to content only (NOT full processing)
  7549 // See also HandleEventWithTarget which does full event processing.
  7550 nsresult
  7551 PresShell::HandleDOMEventWithTarget(nsIContent* aTargetContent,
  7552                                     WidgetEvent* aEvent,
  7553                                     nsEventStatus* aStatus)
  7555   nsresult rv = NS_OK;
  7557   PushCurrentEventInfo(nullptr, aTargetContent);
  7559   // Bug 41013: Check if the event should be dispatched to content.
  7560   // It's possible that we are in the middle of destroying the window
  7561   // and the js context is out of date. This check detects the case
  7562   // that caused a crash in bug 41013, but there may be a better way
  7563   // to handle this situation!
  7564   nsCOMPtr<nsISupports> container = mPresContext->GetContainerWeak();
  7565   if (container) {
  7567     // Dispatch event to content
  7568     rv = EventDispatcher::Dispatch(aTargetContent, mPresContext, aEvent,
  7569                                    nullptr, aStatus);
  7572   PopCurrentEventInfo();
  7573   return rv;
  7576 // See the method above.
  7577 nsresult
  7578 PresShell::HandleDOMEventWithTarget(nsIContent* aTargetContent,
  7579                                     nsIDOMEvent* aEvent,
  7580                                     nsEventStatus* aStatus)
  7582   nsresult rv = NS_OK;
  7584   PushCurrentEventInfo(nullptr, aTargetContent);
  7585   nsCOMPtr<nsISupports> container = mPresContext->GetContainerWeak();
  7586   if (container) {
  7587     rv = EventDispatcher::DispatchDOMEvent(aTargetContent, nullptr, aEvent,
  7588                                            mPresContext, aStatus);
  7591   PopCurrentEventInfo();
  7592   return rv;
  7595 bool
  7596 PresShell::AdjustContextMenuKeyEvent(WidgetMouseEvent* aEvent)
  7598 #ifdef MOZ_XUL
  7599   // if a menu is open, open the context menu relative to the active item on the menu.
  7600   nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
  7601   if (pm) {
  7602     nsIFrame* popupFrame = pm->GetTopPopup(ePopupTypeMenu);
  7603     if (popupFrame) {
  7604       nsIFrame* itemFrame =
  7605         (static_cast<nsMenuPopupFrame *>(popupFrame))->GetCurrentMenuItem();
  7606       if (!itemFrame)
  7607         itemFrame = popupFrame;
  7609       nsCOMPtr<nsIWidget> widget = popupFrame->GetNearestWidget();
  7610       aEvent->widget = widget;
  7611       nsIntPoint widgetPoint = widget->WidgetToScreenOffset();
  7612       aEvent->refPoint = LayoutDeviceIntPoint::FromUntyped(
  7613         itemFrame->GetScreenRect().BottomLeft() - widgetPoint);
  7615       mCurrentEventContent = itemFrame->GetContent();
  7616       mCurrentEventFrame = itemFrame;
  7618       return true;
  7621 #endif
  7623   // If we're here because of the key-equiv for showing context menus, we
  7624   // have to twiddle with the NS event to make sure the context menu comes
  7625   // up in the upper left of the relevant content area before we create
  7626   // the DOM event. Since we never call InitMouseEvent() on the event,
  7627   // the client X/Y will be 0,0. We can make use of that if the widget is null.
  7628   // Use the root view manager's widget since it's most likely to have one,
  7629   // and the coordinates returned by GetCurrentItemAndPositionForElement
  7630   // are relative to the widget of the root of the root view manager.
  7631   nsRootPresContext* rootPC = mPresContext->GetRootPresContext();
  7632   aEvent->refPoint.x = 0;
  7633   aEvent->refPoint.y = 0;
  7634   if (rootPC) {
  7635     rootPC->PresShell()->GetViewManager()->
  7636       GetRootWidget(getter_AddRefs(aEvent->widget));
  7638     if (aEvent->widget) {
  7639       // default the refpoint to the topleft of our document
  7640       nsPoint offset(0, 0);
  7641       nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
  7642       if (rootFrame) {
  7643         nsView* view = rootFrame->GetClosestView(&offset);
  7644         offset += view->GetOffsetToWidget(aEvent->widget);
  7645         aEvent->refPoint =
  7646           LayoutDeviceIntPoint::FromAppUnitsToNearest(offset, mPresContext->AppUnitsPerDevPixel());
  7649   } else {
  7650     aEvent->widget = nullptr;
  7653   // see if we should use the caret position for the popup
  7654   nsIntPoint caretPoint;
  7655   // Beware! This may flush notifications via synchronous
  7656   // ScrollSelectionIntoView.
  7657   if (PrepareToUseCaretPosition(aEvent->widget, caretPoint)) {
  7658     // caret position is good
  7659     aEvent->refPoint = LayoutDeviceIntPoint::FromUntyped(caretPoint);
  7660     return true;
  7663   // If we're here because of the key-equiv for showing context menus, we
  7664   // have to reset the event target to the currently focused element. Get it
  7665   // from the focus controller.
  7666   nsCOMPtr<nsIDOMElement> currentFocus;
  7667   nsIFocusManager* fm = nsFocusManager::GetFocusManager();
  7668   if (fm)
  7669     fm->GetFocusedElement(getter_AddRefs(currentFocus));
  7671   // Reset event coordinates relative to focused frame in view
  7672   if (currentFocus) {
  7673     nsCOMPtr<nsIContent> currentPointElement;
  7674     GetCurrentItemAndPositionForElement(currentFocus,
  7675                                         getter_AddRefs(currentPointElement),
  7676                                         aEvent->refPoint,
  7677                                         aEvent->widget);
  7678     if (currentPointElement) {
  7679       mCurrentEventContent = currentPointElement;
  7680       mCurrentEventFrame = nullptr;
  7681       GetCurrentEventFrame();
  7685   return true;
  7688 // PresShell::PrepareToUseCaretPosition
  7689 //
  7690 //    This checks to see if we should use the caret position for popup context
  7691 //    menus. Returns true if the caret position should be used, and the
  7692 //    coordinates of that position is returned in aTargetPt. This function
  7693 //    will also scroll the window as needed to make the caret visible.
  7694 //
  7695 //    The event widget should be the widget that generated the event, and
  7696 //    whose coordinate system the resulting event's refPoint should be
  7697 //    relative to.  The returned point is in device pixels realtive to the
  7698 //    widget passed in.
  7699 bool
  7700 PresShell::PrepareToUseCaretPosition(nsIWidget* aEventWidget, nsIntPoint& aTargetPt)
  7702   nsresult rv;
  7704   // check caret visibility
  7705   nsRefPtr<nsCaret> caret = GetCaret();
  7706   NS_ENSURE_TRUE(caret, false);
  7708   bool caretVisible = false;
  7709   rv = caret->GetCaretVisible(&caretVisible);
  7710   if (NS_FAILED(rv) || ! caretVisible)
  7711     return false;
  7713   // caret selection, this is a temporary weak reference, so no refcounting is
  7714   // needed
  7715   nsISelection* domSelection = caret->GetCaretDOMSelection();
  7716   NS_ENSURE_TRUE(domSelection, false);
  7718   // since the match could be an anonymous textnode inside a
  7719   // <textarea> or text <input>, we need to get the outer frame
  7720   // note: frames are not refcounted
  7721   nsIFrame* frame = nullptr; // may be nullptr
  7722   nsCOMPtr<nsIDOMNode> node;
  7723   rv = domSelection->GetFocusNode(getter_AddRefs(node));
  7724   NS_ENSURE_SUCCESS(rv, false);
  7725   NS_ENSURE_TRUE(node, false);
  7726   nsCOMPtr<nsIContent> content(do_QueryInterface(node));
  7727   if (content) {
  7728     nsIContent* nonNative = content->FindFirstNonChromeOnlyAccessContent();
  7729     content = nonNative;
  7732   if (content) {
  7733     // It seems like ScrollSelectionIntoView should be enough, but it's
  7734     // not. The problem is that scrolling the selection into view when it is
  7735     // below the current viewport will align the top line of the frame exactly
  7736     // with the bottom of the window. This is fine, BUT, the popup event causes
  7737     // the control to be re-focused which does this exact call to
  7738     // ScrollContentIntoView, which has a one-pixel disagreement of whether the
  7739     // frame is actually in view. The result is that the frame is aligned with
  7740     // the top of the window, but the menu is still at the bottom.
  7741     //
  7742     // Doing this call first forces the frame to be in view, eliminating the
  7743     // problem. The only difference in the result is that if your cursor is in
  7744     // an edit box below the current view, you'll get the edit box aligned with
  7745     // the top of the window. This is arguably better behavior anyway.
  7746     rv = ScrollContentIntoView(content,
  7747                                nsIPresShell::ScrollAxis(
  7748                                  nsIPresShell::SCROLL_MINIMUM,
  7749                                  nsIPresShell::SCROLL_IF_NOT_VISIBLE),
  7750                                nsIPresShell::ScrollAxis(
  7751                                  nsIPresShell::SCROLL_MINIMUM,
  7752                                  nsIPresShell::SCROLL_IF_NOT_VISIBLE),
  7753                                nsIPresShell::SCROLL_OVERFLOW_HIDDEN);
  7754     NS_ENSURE_SUCCESS(rv, false);
  7755     frame = content->GetPrimaryFrame();
  7756     NS_WARN_IF_FALSE(frame, "No frame for focused content?");
  7759   // Actually scroll the selection (ie caret) into view. Note that this must
  7760   // be synchronous since we will be checking the caret position on the screen.
  7761   //
  7762   // Be easy about errors, and just don't scroll in those cases. Better to have
  7763   // the correct menu at a weird place than the wrong menu.
  7764   // After ScrollSelectionIntoView(), the pending notifications might be
  7765   // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
  7766   nsCOMPtr<nsISelectionController> selCon;
  7767   if (frame)
  7768     frame->GetSelectionController(GetPresContext(), getter_AddRefs(selCon));
  7769   else
  7770     selCon = static_cast<nsISelectionController *>(this);
  7771   if (selCon) {
  7772     rv = selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
  7773                                          nsISelectionController::SELECTION_FOCUS_REGION,
  7774                                          nsISelectionController::SCROLL_SYNCHRONOUS);
  7775     NS_ENSURE_SUCCESS(rv, false);
  7778   nsPresContext* presContext = GetPresContext();
  7780   // get caret position relative to the closest view
  7781   nsRect caretCoords;
  7782   nsIFrame* caretFrame = caret->GetGeometry(domSelection, &caretCoords);
  7783   if (!caretFrame)
  7784     return false;
  7785   nsPoint viewOffset;
  7786   nsView* view = caretFrame->GetClosestView(&viewOffset);
  7787   if (!view)
  7788     return false;
  7789   // and then get the caret coords relative to the event widget
  7790   if (aEventWidget) {
  7791     viewOffset += view->GetOffsetToWidget(aEventWidget);
  7793   caretCoords.MoveBy(viewOffset);
  7795   // caret coordinates are in app units, convert to pixels
  7796   aTargetPt.x =
  7797     presContext->AppUnitsToDevPixels(caretCoords.x + caretCoords.width);
  7798   aTargetPt.y =
  7799     presContext->AppUnitsToDevPixels(caretCoords.y + caretCoords.height);
  7801   // make sure rounding doesn't return a pixel which is outside the caret
  7802   // (e.g. one line lower)
  7803   aTargetPt.y -= 1;
  7805   return true;
  7808 void
  7809 PresShell::GetCurrentItemAndPositionForElement(nsIDOMElement *aCurrentEl,
  7810                                                nsIContent** aTargetToUse,
  7811                                                LayoutDeviceIntPoint& aTargetPt,
  7812                                                nsIWidget *aRootWidget)
  7814   nsCOMPtr<nsIContent> focusedContent(do_QueryInterface(aCurrentEl));
  7815   ScrollContentIntoView(focusedContent,
  7816                         ScrollAxis(),
  7817                         ScrollAxis(),
  7818                         nsIPresShell::SCROLL_OVERFLOW_HIDDEN);
  7820   nsPresContext* presContext = GetPresContext();
  7822   bool istree = false, checkLineHeight = true;
  7823   nscoord extraTreeY = 0;
  7825 #ifdef MOZ_XUL
  7826   // Set the position to just underneath the current item for multi-select
  7827   // lists or just underneath the selected item for single-select lists. If
  7828   // the element is not a list, or there is no selection, leave the position
  7829   // as is.
  7830   nsCOMPtr<nsIDOMXULSelectControlItemElement> item;
  7831   nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelect =
  7832     do_QueryInterface(aCurrentEl);
  7833   if (multiSelect) {
  7834     checkLineHeight = false;
  7836     int32_t currentIndex;
  7837     multiSelect->GetCurrentIndex(&currentIndex);
  7838     if (currentIndex >= 0) {
  7839       nsCOMPtr<nsIDOMXULElement> xulElement(do_QueryInterface(aCurrentEl));
  7840       if (xulElement) {
  7841         nsCOMPtr<nsIBoxObject> box;
  7842         xulElement->GetBoxObject(getter_AddRefs(box));
  7843         nsCOMPtr<nsITreeBoxObject> treeBox(do_QueryInterface(box));
  7844         // Tree view special case (tree items have no frames)
  7845         // Get the focused row and add its coordinates, which are already in pixels
  7846         // XXX Boris, should we create a new interface so that this doesn't
  7847         // need to know about trees? Something like nsINodelessChildCreator which
  7848         // could provide the current focus coordinates?
  7849         if (treeBox) {
  7850           treeBox->EnsureRowIsVisible(currentIndex);
  7851           int32_t firstVisibleRow, rowHeight;
  7852           treeBox->GetFirstVisibleRow(&firstVisibleRow);
  7853           treeBox->GetRowHeight(&rowHeight);
  7855           extraTreeY += presContext->CSSPixelsToAppUnits(
  7856                           (currentIndex - firstVisibleRow + 1) * rowHeight);
  7857           istree = true;
  7859           nsCOMPtr<nsITreeColumns> cols;
  7860           treeBox->GetColumns(getter_AddRefs(cols));
  7861           if (cols) {
  7862             nsCOMPtr<nsITreeColumn> col;
  7863             cols->GetFirstColumn(getter_AddRefs(col));
  7864             if (col) {
  7865               nsCOMPtr<nsIDOMElement> colElement;
  7866               col->GetElement(getter_AddRefs(colElement));
  7867               nsCOMPtr<nsIContent> colContent(do_QueryInterface(colElement));
  7868               if (colContent) {
  7869                 nsIFrame* frame = colContent->GetPrimaryFrame();
  7870                 if (frame) {
  7871                   extraTreeY += frame->GetSize().height;
  7877         else {
  7878           multiSelect->GetCurrentItem(getter_AddRefs(item));
  7883   else {
  7884     // don't check menulists as the selected item will be inside a popup.
  7885     nsCOMPtr<nsIDOMXULMenuListElement> menulist = do_QueryInterface(aCurrentEl);
  7886     if (!menulist) {
  7887       nsCOMPtr<nsIDOMXULSelectControlElement> select =
  7888         do_QueryInterface(aCurrentEl);
  7889       if (select) {
  7890         checkLineHeight = false;
  7891         select->GetSelectedItem(getter_AddRefs(item));
  7896   if (item)
  7897     focusedContent = do_QueryInterface(item);
  7898 #endif
  7900   nsIFrame *frame = focusedContent->GetPrimaryFrame();
  7901   if (frame) {
  7902     NS_ASSERTION(frame->PresContext() == GetPresContext(),
  7903       "handling event for focused content that is not in our document?");
  7905     nsPoint frameOrigin(0, 0);
  7907     // Get the frame's origin within its view
  7908     nsView *view = frame->GetClosestView(&frameOrigin);
  7909     NS_ASSERTION(view, "No view for frame");
  7911     // View's origin relative the widget
  7912     if (aRootWidget) {
  7913       frameOrigin += view->GetOffsetToWidget(aRootWidget);
  7916     // Start context menu down and to the right from top left of frame
  7917     // use the lineheight. This is a good distance to move the context
  7918     // menu away from the top left corner of the frame. If we always
  7919     // used the frame height, the context menu could end up far away,
  7920     // for example when we're focused on linked images.
  7921     // On the other hand, we want to use the frame height if it's less
  7922     // than the current line height, so that the context menu appears
  7923     // associated with the correct frame.
  7924     nscoord extra = 0;
  7925     if (!istree) {
  7926       extra = frame->GetSize().height;
  7927       if (checkLineHeight) {
  7928         nsIScrollableFrame *scrollFrame =
  7929           nsLayoutUtils::GetNearestScrollableFrame(frame);
  7930         if (scrollFrame) {
  7931           nsSize scrollAmount = scrollFrame->GetLineScrollAmount();
  7932           nsIFrame* f = do_QueryFrame(scrollFrame);
  7933           int32_t APD = presContext->AppUnitsPerDevPixel();
  7934           int32_t scrollAPD = f->PresContext()->AppUnitsPerDevPixel();
  7935           scrollAmount = scrollAmount.ConvertAppUnits(scrollAPD, APD);
  7936           if (extra > scrollAmount.height) {
  7937             extra = scrollAmount.height;
  7943     aTargetPt.x = presContext->AppUnitsToDevPixels(frameOrigin.x);
  7944     aTargetPt.y = presContext->AppUnitsToDevPixels(
  7945                     frameOrigin.y + extra + extraTreeY);
  7948   NS_IF_ADDREF(*aTargetToUse = focusedContent);
  7951 bool
  7952 PresShell::ShouldIgnoreInvalidation()
  7954   return mPaintingSuppressed || !mIsActive || mIsNeverPainting;
  7957 void
  7958 PresShell::WillPaint()
  7960   nsRootPresContext* rootPresContext = mPresContext->GetRootPresContext();
  7961   if (!rootPresContext) {
  7962     // In some edge cases, such as when we don't have a root frame yet,
  7963     // we can't find the root prescontext. There's nothing to do in that
  7964     // case.
  7965     return;
  7968   // Don't bother doing anything if some viewmanager in our tree is painting
  7969   // while we still have painting suppressed or we are not active.
  7970   if (mPaintingSuppressed || !mIsActive || !IsVisible()) {
  7971     return;
  7974   rootPresContext->FlushWillPaintObservers();
  7975   if (mIsDestroying)
  7976     return;
  7978   // Process reflows, if we have them, to reduce flicker due to invalidates and
  7979   // reflow being interspersed.  Note that we _do_ allow this to be
  7980   // interruptible; if we can't do all the reflows it's better to flicker a bit
  7981   // than to freeze up.
  7982   FlushPendingNotifications(ChangesToFlush(Flush_InterruptibleLayout, false));
  7985 void
  7986 PresShell::WillPaintWindow()
  7988   nsRootPresContext* rootPresContext = mPresContext->GetRootPresContext();
  7989   if (rootPresContext != mPresContext) {
  7990     // This could be a popup's presshell. We don't allow plugins in popups
  7991     // so there's nothing to do here.
  7992     return;
  7995 #ifndef XP_MACOSX
  7996   rootPresContext->ApplyPluginGeometryUpdates();
  7997 #endif
  8000 void
  8001 PresShell::DidPaintWindow()
  8003   nsRootPresContext* rootPresContext = mPresContext->GetRootPresContext();
  8004   if (rootPresContext != mPresContext) {
  8005     // This could be a popup's presshell. No point in notifying XPConnect
  8006     // about compositing of popups.
  8007     return;
  8010   if (nsContentUtils::XPConnect()) {
  8011     nsContentUtils::XPConnect()->NotifyDidPaint();
  8015 bool
  8016 PresShell::IsVisible()
  8018   if (!mViewManager)
  8019     return false;
  8021   nsView* view = mViewManager->GetRootView();
  8022   if (!view)
  8023     return true;
  8025   // inner view of subdoc frame
  8026   view = view->GetParent();
  8027   if (!view)
  8028     return true;
  8030   // subdoc view
  8031   view = view->GetParent();
  8032   if (!view)
  8033     return true;
  8035   nsIFrame* frame = view->GetFrame();
  8036   if (!frame)
  8037     return true;
  8039   return frame->IsVisibleConsideringAncestors(nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY);
  8042 nsresult
  8043 PresShell::GetAgentStyleSheets(nsCOMArray<nsIStyleSheet>& aSheets)
  8045   aSheets.Clear();
  8046   int32_t sheetCount = mStyleSet->SheetCount(nsStyleSet::eAgentSheet);
  8048   for (int32_t i = 0; i < sheetCount; ++i) {
  8049     nsIStyleSheet *sheet = mStyleSet->StyleSheetAt(nsStyleSet::eAgentSheet, i);
  8050     if (!aSheets.AppendObject(sheet))
  8051       return NS_ERROR_OUT_OF_MEMORY;
  8054   return NS_OK;
  8057 nsresult
  8058 PresShell::SetAgentStyleSheets(const nsCOMArray<nsIStyleSheet>& aSheets)
  8060   return mStyleSet->ReplaceSheets(nsStyleSet::eAgentSheet, aSheets);
  8063 nsresult
  8064 PresShell::AddOverrideStyleSheet(nsIStyleSheet *aSheet)
  8066   return mStyleSet->PrependStyleSheet(nsStyleSet::eOverrideSheet, aSheet);
  8069 nsresult
  8070 PresShell::RemoveOverrideStyleSheet(nsIStyleSheet *aSheet)
  8072   return mStyleSet->RemoveStyleSheet(nsStyleSet::eOverrideSheet, aSheet);
  8075 static void
  8076 FreezeElement(nsIContent *aContent, void * /* unused */)
  8078   nsCOMPtr<nsIObjectLoadingContent> olc(do_QueryInterface(aContent));
  8079   if (olc) {
  8080     olc->StopPluginInstance();
  8084 static bool
  8085 FreezeSubDocument(nsIDocument *aDocument, void *aData)
  8087   nsIPresShell *shell = aDocument->GetShell();
  8088   if (shell)
  8089     shell->Freeze();
  8091   return true;
  8094 void
  8095 PresShell::Freeze()
  8097   mUpdateImageVisibilityEvent.Revoke();
  8099   MaybeReleaseCapturingContent();
  8101   mDocument->EnumerateFreezableElements(FreezeElement, nullptr);
  8103   if (mCaret) {
  8104     mCaret->SetCaretVisible(false);
  8107   mPaintingSuppressed = true;
  8109   if (mDocument) {
  8110     mDocument->EnumerateSubDocuments(FreezeSubDocument, nullptr);
  8113   nsPresContext* presContext = GetPresContext();
  8114   if (presContext &&
  8115       presContext->RefreshDriver()->PresContext() == presContext) {
  8116     presContext->RefreshDriver()->Freeze();
  8119   mFrozen = true;
  8120   if (mDocument) {
  8121     UpdateImageLockingState();
  8125 void
  8126 PresShell::FireOrClearDelayedEvents(bool aFireEvents)
  8128   mNoDelayedMouseEvents = false;
  8129   mNoDelayedKeyEvents = false;
  8130   if (!aFireEvents) {
  8131     mDelayedEvents.Clear();
  8132     return;
  8135   if (mDocument) {
  8136     nsCOMPtr<nsIDocument> doc = mDocument;
  8137     while (!mIsDestroying && mDelayedEvents.Length() &&
  8138            !doc->EventHandlingSuppressed()) {
  8139       nsAutoPtr<DelayedEvent> ev(mDelayedEvents[0].forget());
  8140       mDelayedEvents.RemoveElementAt(0);
  8141       ev->Dispatch();
  8143     if (!doc->EventHandlingSuppressed()) {
  8144       mDelayedEvents.Clear();
  8149 static void
  8150 ThawElement(nsIContent *aContent, void *aShell)
  8152   nsCOMPtr<nsIObjectLoadingContent> olc(do_QueryInterface(aContent));
  8153   if (olc) {
  8154     olc->AsyncStartPluginInstance();
  8158 static bool
  8159 ThawSubDocument(nsIDocument *aDocument, void *aData)
  8161   nsIPresShell *shell = aDocument->GetShell();
  8162   if (shell)
  8163     shell->Thaw();
  8165   return true;
  8168 void
  8169 PresShell::Thaw()
  8171   nsPresContext* presContext = GetPresContext();
  8172   if (presContext &&
  8173       presContext->RefreshDriver()->PresContext() == presContext) {
  8174     presContext->RefreshDriver()->Thaw();
  8177   mDocument->EnumerateFreezableElements(ThawElement, this);
  8179   if (mDocument)
  8180     mDocument->EnumerateSubDocuments(ThawSubDocument, nullptr);
  8182   // Get the activeness of our presshell, as this might have changed
  8183   // while we were in the bfcache
  8184   QueryIsActive();
  8186   // We're now unfrozen
  8187   mFrozen = false;
  8188   UpdateImageLockingState();
  8190   UnsuppressPainting();
  8193 //--------------------------------------------------------
  8194 // Start of protected and private methods on the PresShell
  8195 //--------------------------------------------------------
  8197 void
  8198 PresShell::MaybeScheduleReflow()
  8200   ASSERT_REFLOW_SCHEDULED_STATE();
  8201   if (mReflowScheduled || mIsDestroying || mIsReflowing ||
  8202       mDirtyRoots.Length() == 0)
  8203     return;
  8205   if (!mPresContext->HasPendingInterrupt() || !ScheduleReflowOffTimer()) {
  8206     ScheduleReflow();
  8209   ASSERT_REFLOW_SCHEDULED_STATE();
  8212 void
  8213 PresShell::ScheduleReflow()
  8215   NS_PRECONDITION(!mReflowScheduled, "Why are we trying to schedule a reflow?");
  8216   ASSERT_REFLOW_SCHEDULED_STATE();
  8218   if (GetPresContext()->RefreshDriver()->AddLayoutFlushObserver(this)) {
  8219     mReflowScheduled = true;
  8222   ASSERT_REFLOW_SCHEDULED_STATE();
  8225 nsresult
  8226 PresShell::DidCauseReflow()
  8228   NS_ASSERTION(mChangeNestCount != 0, "Unexpected call to DidCauseReflow()");
  8229   --mChangeNestCount;
  8230   nsContentUtils::RemoveScriptBlocker();
  8232   return NS_OK;
  8235 void
  8236 PresShell::WillDoReflow()
  8238   // We just reflowed, tell the caret that its frame might have moved.
  8239   // XXXbz that comment makes no sense
  8240   if (mCaret) {
  8241     mCaret->InvalidateOutsideCaret();
  8244   mPresContext->FlushUserFontSet();
  8246   mFrameConstructor->BeginUpdate();
  8248   mLastReflowStart = GetPerformanceNow();
  8251 void
  8252 PresShell::DidDoReflow(bool aInterruptible, bool aWasInterrupted)
  8254   mFrameConstructor->EndUpdate();
  8256   HandlePostedReflowCallbacks(aInterruptible);
  8258   nsCOMPtr<nsIDocShell> docShell = mPresContext->GetDocShell();
  8259   if (docShell) {
  8260     DOMHighResTimeStamp now = GetPerformanceNow();
  8261     docShell->NotifyReflowObservers(aInterruptible, mLastReflowStart, now);
  8264   if (sSynthMouseMove) {
  8265     SynthesizeMouseMove(false);
  8267   if (mCaret) {
  8268     // Update the caret's position now to account for any changes created by
  8269     // the reflow.
  8270     mCaret->InvalidateOutsideCaret();
  8271     mCaret->UpdateCaretPosition();
  8274   if (!aWasInterrupted) {
  8275     ClearReflowOnZoomPending();
  8279 DOMHighResTimeStamp
  8280 PresShell::GetPerformanceNow()
  8282   DOMHighResTimeStamp now = 0;
  8283   nsPIDOMWindow* window = mDocument->GetInnerWindow();
  8285   if (window) {
  8286     nsPerformance* perf = window->GetPerformance();
  8288     if (perf) {
  8289       now = perf->Now();
  8293   return now;
  8296 static PLDHashOperator
  8297 MarkFramesDirtyToRoot(nsPtrHashKey<nsIFrame>* p, void* closure)
  8299   nsIFrame* target = static_cast<nsIFrame*>(closure);
  8300   for (nsIFrame* f = p->GetKey(); f && !NS_SUBTREE_DIRTY(f);
  8301        f = f->GetParent()) {
  8302     f->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
  8304     if (f == target) {
  8305       break;
  8309   return PL_DHASH_NEXT;
  8312 void
  8313 PresShell::sReflowContinueCallback(nsITimer* aTimer, void* aPresShell)
  8315   nsRefPtr<PresShell> self = static_cast<PresShell*>(aPresShell);
  8317   NS_PRECONDITION(aTimer == self->mReflowContinueTimer, "Unexpected timer");
  8318   self->mReflowContinueTimer = nullptr;
  8319   self->ScheduleReflow();
  8322 bool
  8323 PresShell::ScheduleReflowOffTimer()
  8325   NS_PRECONDITION(!mReflowScheduled, "Shouldn't get here");
  8326   ASSERT_REFLOW_SCHEDULED_STATE();
  8328   if (!mReflowContinueTimer) {
  8329     mReflowContinueTimer = do_CreateInstance("@mozilla.org/timer;1");
  8330     if (!mReflowContinueTimer ||
  8331         NS_FAILED(mReflowContinueTimer->
  8332                     InitWithFuncCallback(sReflowContinueCallback, this, 30,
  8333                                          nsITimer::TYPE_ONE_SHOT))) {
  8334       return false;
  8337   return true;
  8340 bool
  8341 PresShell::DoReflow(nsIFrame* target, bool aInterruptible)
  8343   if (mIsZombie) {
  8344     return true;
  8347   gfxTextPerfMetrics* tp = mPresContext->GetTextPerfMetrics();
  8348   TimeStamp timeStart;
  8349   if (tp) {
  8350     tp->Accumulate();
  8351     tp->reflowCount++;
  8352     timeStart = TimeStamp::Now();
  8355   target->SchedulePaint();
  8356   nsIFrame *parent = nsLayoutUtils::GetCrossDocParentFrame(target);
  8357   while (parent) {
  8358     nsSVGEffects::InvalidateDirectRenderingObservers(parent);
  8359     parent = nsLayoutUtils::GetCrossDocParentFrame(parent);
  8362   nsAutoCString docURL("N/A");
  8363   nsIURI *uri = mDocument->GetDocumentURI();
  8364   if (uri)
  8365     uri->GetSpec(docURL);
  8366   PROFILER_LABEL_PRINTF("layout", "DoReflow", "(%s)", docURL.get());
  8368   if (mReflowContinueTimer) {
  8369     mReflowContinueTimer->Cancel();
  8370     mReflowContinueTimer = nullptr;
  8373   nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
  8375   nsRefPtr<nsRenderingContext> rcx = CreateReferenceRenderingContext();
  8377 #ifdef DEBUG
  8378   mCurrentReflowRoot = target;
  8379 #endif
  8381   target->WillReflow(mPresContext);
  8383   // If the target frame is the root of the frame hierarchy, then
  8384   // use all the available space. If it's simply a `reflow root',
  8385   // then use the target frame's size as the available space.
  8386   nsSize size;
  8387   if (target == rootFrame) {
  8388      size = mPresContext->GetVisibleArea().Size();
  8389   } else {
  8390      size = target->GetSize();
  8393   NS_ASSERTION(!target->GetNextInFlow() && !target->GetPrevInFlow(),
  8394                "reflow roots should never split");
  8396   // Don't pass size directly to the reflow state, since a
  8397   // constrained height implies page/column breaking.
  8398   nsSize reflowSize(size.width, NS_UNCONSTRAINEDSIZE);
  8399   nsHTMLReflowState reflowState(mPresContext, target, rcx, reflowSize,
  8400                                 nsHTMLReflowState::CALLER_WILL_INIT);
  8402   if (rootFrame == target) {
  8403     reflowState.Init(mPresContext);
  8405     // When the root frame is being reflowed with unconstrained height
  8406     // (which happens when we're called from
  8407     // nsDocumentViewer::SizeToContent), we're effectively doing a
  8408     // vertical resize, since it changes the meaning of percentage
  8409     // heights even if no heights actually changed.  The same applies
  8410     // when we reflow again after that computation.  This is an unusual
  8411     // case, and isn't caught by nsHTMLReflowState::InitResizeFlags.
  8412     bool hasUnconstrainedHeight = size.height == NS_UNCONSTRAINEDSIZE;
  8414     if (hasUnconstrainedHeight || mLastRootReflowHadUnconstrainedHeight) {
  8415       reflowState.mFlags.mVResize = true;
  8418     mLastRootReflowHadUnconstrainedHeight = hasUnconstrainedHeight;
  8419   } else {
  8420     // Initialize reflow state with current used border and padding,
  8421     // in case this was set specially by the parent frame when the reflow root
  8422     // was reflowed by its parent.
  8423     nsMargin currentBorder = target->GetUsedBorder();
  8424     nsMargin currentPadding = target->GetUsedPadding();
  8425     reflowState.Init(mPresContext, -1, -1, &currentBorder, &currentPadding);
  8428   // fix the computed height
  8429   NS_ASSERTION(reflowState.ComputedPhysicalMargin() == nsMargin(0, 0, 0, 0),
  8430                "reflow state should not set margin for reflow roots");
  8431   if (size.height != NS_UNCONSTRAINEDSIZE) {
  8432     nscoord computedHeight =
  8433       size.height - reflowState.ComputedPhysicalBorderPadding().TopBottom();
  8434     computedHeight = std::max(computedHeight, 0);
  8435     reflowState.SetComputedHeight(computedHeight);
  8437   NS_ASSERTION(reflowState.ComputedWidth() ==
  8438                  size.width -
  8439                    reflowState.ComputedPhysicalBorderPadding().LeftRight(),
  8440                "reflow state computed incorrect width");
  8442   mPresContext->ReflowStarted(aInterruptible);
  8443   mIsReflowing = true;
  8445   nsReflowStatus status;
  8446   nsHTMLReflowMetrics desiredSize(reflowState);
  8447   target->Reflow(mPresContext, desiredSize, reflowState, status);
  8449   // If an incremental reflow is initiated at a frame other than the
  8450   // root frame, then its desired size had better not change!  If it's
  8451   // initiated at the root, then the size better not change unless its
  8452   // height was unconstrained to start with.
  8453   nsRect boundsRelativeToTarget = nsRect(0, 0, desiredSize.Width(), desiredSize.Height());
  8454   NS_ASSERTION((target == rootFrame && size.height == NS_UNCONSTRAINEDSIZE) ||
  8455                (desiredSize.Width() == size.width &&
  8456                 desiredSize.Height() == size.height),
  8457                "non-root frame's desired size changed during an "
  8458                "incremental reflow");
  8459   NS_ASSERTION(target == rootFrame ||
  8460                desiredSize.VisualOverflow().IsEqualInterior(boundsRelativeToTarget),
  8461                "non-root reflow roots must not have visible overflow");
  8462   NS_ASSERTION(target == rootFrame ||
  8463                desiredSize.ScrollableOverflow().IsEqualEdges(boundsRelativeToTarget),
  8464                "non-root reflow roots must not have scrollable overflow");
  8465   NS_ASSERTION(status == NS_FRAME_COMPLETE,
  8466                "reflow roots should never split");
  8468   target->SetSize(boundsRelativeToTarget.Size());
  8470   // Always use boundsRelativeToTarget here, not desiredSize.GetVisualOverflowArea(),
  8471   // because for root frames (where they could be different, since root frames
  8472   // are allowed to have overflow) the root view bounds need to match the
  8473   // viewport bounds; the view manager "window dimensions" code depends on it.
  8474   nsContainerFrame::SyncFrameViewAfterReflow(mPresContext, target,
  8475                                              target->GetView(),
  8476                                              boundsRelativeToTarget);
  8477   nsContainerFrame::SyncWindowProperties(mPresContext, target,
  8478                                          target->GetView(), rcx);
  8480   target->DidReflow(mPresContext, nullptr, nsDidReflowStatus::FINISHED);
  8481   if (target == rootFrame && size.height == NS_UNCONSTRAINEDSIZE) {
  8482     mPresContext->SetVisibleArea(boundsRelativeToTarget);
  8485 #ifdef DEBUG
  8486   mCurrentReflowRoot = nullptr;
  8487 #endif
  8489   NS_ASSERTION(mPresContext->HasPendingInterrupt() ||
  8490                mFramesToDirty.Count() == 0,
  8491                "Why do we need to dirty anything if not interrupted?");
  8493   mIsReflowing = false;
  8494   bool interrupted = mPresContext->HasPendingInterrupt();
  8495   if (interrupted) {
  8496     // Make sure target gets reflowed again.
  8497     mFramesToDirty.EnumerateEntries(&MarkFramesDirtyToRoot, target);
  8498     NS_ASSERTION(NS_SUBTREE_DIRTY(target), "Why is the target not dirty?");
  8499     mDirtyRoots.AppendElement(target);
  8500     mDocument->SetNeedLayoutFlush();
  8502     // Clear mFramesToDirty after we've done the NS_SUBTREE_DIRTY(target)
  8503     // assertion so that if it fails it's easier to see what's going on.
  8504 #ifdef NOISY_INTERRUPTIBLE_REFLOW
  8505     printf("mFramesToDirty.Count() == %u\n", mFramesToDirty.Count());
  8506 #endif /* NOISY_INTERRUPTIBLE_REFLOW */
  8507     mFramesToDirty.Clear();
  8509     // Any FlushPendingNotifications with interruptible reflows
  8510     // should be suppressed now. We don't want to do extra reflow work
  8511     // before our reflow event happens.
  8512     mSuppressInterruptibleReflows = true;
  8513     MaybeScheduleReflow();
  8516 #ifdef PR_LOGGING
  8517   // dump text perf metrics for reflows with significant text processing
  8518   if (tp) {
  8519     if (tp->current.numChars > 100) {
  8520       TimeDuration reflowTime = TimeStamp::Now() - timeStart;
  8521       LogTextPerfStats(tp, this, tp->current,
  8522                        reflowTime.ToMilliseconds(), eLog_reflow, nullptr);
  8524     tp->Accumulate();
  8526 #endif
  8528   return !interrupted;
  8531 #ifdef DEBUG
  8532 void
  8533 PresShell::DoVerifyReflow()
  8535   if (GetVerifyReflowEnable()) {
  8536     // First synchronously render what we have so far so that we can
  8537     // see it.
  8538     nsView* rootView = mViewManager->GetRootView();
  8539     mViewManager->InvalidateView(rootView);
  8541     FlushPendingNotifications(Flush_Layout);
  8542     mInVerifyReflow = true;
  8543     bool ok = VerifyIncrementalReflow();
  8544     mInVerifyReflow = false;
  8545     if (VERIFY_REFLOW_ALL & gVerifyReflowFlags) {
  8546       printf("ProcessReflowCommands: finished (%s)\n",
  8547              ok ? "ok" : "failed");
  8550     if (!mDirtyRoots.IsEmpty()) {
  8551       printf("XXX yikes! reflow commands queued during verify-reflow\n");
  8555 #endif
  8557 // used with Telemetry metrics
  8558 #define NS_LONG_REFLOW_TIME_MS    5000
  8560 bool
  8561 PresShell::ProcessReflowCommands(bool aInterruptible)
  8563   if (mDirtyRoots.IsEmpty() && !mShouldUnsuppressPainting) {
  8564     // Nothing to do; bail out
  8565     return true;
  8568   mozilla::TimeStamp timerStart = mozilla::TimeStamp::Now();
  8569   bool interrupted = false;
  8570   if (!mDirtyRoots.IsEmpty()) {
  8572 #ifdef DEBUG
  8573     if (VERIFY_REFLOW_DUMP_COMMANDS & gVerifyReflowFlags) {
  8574       printf("ProcessReflowCommands: begin incremental reflow\n");
  8576 #endif
  8578     // If reflow is interruptible, then make a note of our deadline.
  8579     const PRIntervalTime deadline = aInterruptible
  8580         ? PR_IntervalNow() + PR_MicrosecondsToInterval(gMaxRCProcessingTime)
  8581         : (PRIntervalTime)0;
  8583     // Scope for the reflow entry point
  8585       nsAutoScriptBlocker scriptBlocker;
  8586       WillDoReflow();
  8587       AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Reflow);
  8588       nsViewManager::AutoDisableRefresh refreshBlocker(mViewManager);
  8590       do {
  8591         // Send an incremental reflow notification to the target frame.
  8592         int32_t idx = mDirtyRoots.Length() - 1;
  8593         nsIFrame *target = mDirtyRoots[idx];
  8594         mDirtyRoots.RemoveElementAt(idx);
  8596         if (!NS_SUBTREE_DIRTY(target)) {
  8597           // It's not dirty anymore, which probably means the notification
  8598           // was posted in the middle of a reflow (perhaps with a reflow
  8599           // root in the middle).  Don't do anything.
  8600           continue;
  8603         interrupted = !DoReflow(target, aInterruptible);
  8605         // Keep going until we're out of reflow commands, or we've run
  8606         // past our deadline, or we're interrupted.
  8607       } while (!interrupted && !mDirtyRoots.IsEmpty() &&
  8608                (!aInterruptible || PR_IntervalNow() < deadline));
  8610       interrupted = !mDirtyRoots.IsEmpty();
  8613     // Exiting the scriptblocker might have killed us
  8614     if (!mIsDestroying) {
  8615       DidDoReflow(aInterruptible, interrupted);
  8618     // DidDoReflow might have killed us
  8619     if (!mIsDestroying) {
  8620 #ifdef DEBUG
  8621       if (VERIFY_REFLOW_DUMP_COMMANDS & gVerifyReflowFlags) {
  8622         printf("\nPresShell::ProcessReflowCommands() finished: this=%p\n",
  8623                (void*)this);
  8625       DoVerifyReflow();
  8626 #endif
  8628       // If any new reflow commands were enqueued during the reflow, schedule
  8629       // another reflow event to process them.  Note that we want to do this
  8630       // after DidDoReflow(), since that method can change whether there are
  8631       // dirty roots around by flushing, and there's no point in posting a
  8632       // reflow event just to have the flush revoke it.
  8633       if (!mDirtyRoots.IsEmpty()) {
  8634         MaybeScheduleReflow();
  8635         // And tell our document that we might need flushing
  8636         mDocument->SetNeedLayoutFlush();
  8641   if (!mIsDestroying && mShouldUnsuppressPainting &&
  8642       mDirtyRoots.IsEmpty()) {
  8643     // We only unlock if we're out of reflows.  It's pointless
  8644     // to unlock if reflows are still pending, since reflows
  8645     // are just going to thrash the frames around some more.  By
  8646     // waiting we avoid an overeager "jitter" effect.
  8647     mShouldUnsuppressPainting = false;
  8648     UnsuppressAndInvalidate();
  8651   if (mDocument->GetRootElement()) {
  8652     TimeDuration elapsed = TimeStamp::Now() - timerStart;
  8653     int32_t intElapsed = int32_t(elapsed.ToMilliseconds());
  8655     Telemetry::ID id;
  8656     if (mDocument->GetRootElement()->IsXUL()) {
  8657       id = mIsActive
  8658         ? Telemetry::XUL_FOREGROUND_REFLOW_MS
  8659         : Telemetry::XUL_BACKGROUND_REFLOW_MS;
  8660     } else {
  8661       id = mIsActive
  8662         ? Telemetry::HTML_FOREGROUND_REFLOW_MS_2
  8663         : Telemetry::HTML_BACKGROUND_REFLOW_MS_2;
  8665     Telemetry::Accumulate(id, intElapsed);
  8666     if (intElapsed > NS_LONG_REFLOW_TIME_MS) {
  8667       Telemetry::Accumulate(Telemetry::LONG_REFLOW_INTERRUPTIBLE,
  8668                             aInterruptible ? 1 : 0);
  8672   return !interrupted;
  8675 void
  8676 PresShell::WindowSizeMoveDone()
  8678   if (mPresContext) {
  8679     EventStateManager::ClearGlobalActiveContent(nullptr);
  8680     ClearMouseCapture(nullptr);
  8684 #ifdef MOZ_XUL
  8685 /*
  8686  * It's better to add stuff to the |DidSetStyleContext| method of the
  8687  * relevant frames than adding it here.  These methods should (ideally,
  8688  * anyway) go away.
  8689  */
  8691 // Return value says whether to walk children.
  8692 typedef bool (* frameWalkerFn)(nsIFrame *aFrame, void *aClosure);
  8694 static bool
  8695 ReResolveMenusAndTrees(nsIFrame *aFrame, void *aClosure)
  8697   // Trees have a special style cache that needs to be flushed when
  8698   // the theme changes.
  8699   nsTreeBodyFrame *treeBody = do_QueryFrame(aFrame);
  8700   if (treeBody)
  8701     treeBody->ClearStyleAndImageCaches();
  8703   // We deliberately don't re-resolve style on a menu's popup
  8704   // sub-content, since doing so slows menus to a crawl.  That means we
  8705   // have to special-case them on a skin switch, and ensure that the
  8706   // popup frames just get destroyed completely.
  8707   nsMenuFrame* menu = do_QueryFrame(aFrame);
  8708   if (menu)
  8709     menu->CloseMenu(true);
  8710   return true;
  8713 static bool
  8714 ReframeImageBoxes(nsIFrame *aFrame, void *aClosure)
  8716   nsStyleChangeList *list = static_cast<nsStyleChangeList*>(aClosure);
  8717   if (aFrame->GetType() == nsGkAtoms::imageBoxFrame) {
  8718     list->AppendChange(aFrame, aFrame->GetContent(),
  8719                        NS_STYLE_HINT_FRAMECHANGE);
  8720     return false; // don't walk descendants
  8722   return true; // walk descendants
  8725 static void
  8726 WalkFramesThroughPlaceholders(nsPresContext *aPresContext, nsIFrame *aFrame,
  8727                               frameWalkerFn aFunc, void *aClosure)
  8729   bool walkChildren = (*aFunc)(aFrame, aClosure);
  8730   if (!walkChildren)
  8731     return;
  8733   nsIFrame::ChildListIterator lists(aFrame);
  8734   for (; !lists.IsDone(); lists.Next()) {
  8735     nsFrameList::Enumerator childFrames(lists.CurrentList());
  8736     for (; !childFrames.AtEnd(); childFrames.Next()) {
  8737       nsIFrame* child = childFrames.get();
  8738       if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
  8739         // only do frames that are in flow, and recur through the
  8740         // out-of-flows of placeholders.
  8741         WalkFramesThroughPlaceholders(aPresContext,
  8742                                       nsPlaceholderFrame::GetRealFrameFor(child),
  8743                                       aFunc, aClosure);
  8748 #endif
  8750 NS_IMETHODIMP
  8751 PresShell::Observe(nsISupports* aSubject,
  8752                    const char* aTopic,
  8753                    const char16_t* aData)
  8755 #ifdef MOZ_XUL
  8756   if (!nsCRT::strcmp(aTopic, "chrome-flush-skin-caches")) {
  8757     nsIFrame *rootFrame = mFrameConstructor->GetRootFrame();
  8758     // Need to null-check because "chrome-flush-skin-caches" can happen
  8759     // at interesting times during startup.
  8760     if (rootFrame) {
  8761       NS_ASSERTION(mViewManager, "View manager must exist");
  8763       nsWeakFrame weakRoot(rootFrame);
  8764       // Have to make sure that the content notifications are flushed before we
  8765       // start messing with the frame model; otherwise we can get content doubling.
  8766       mDocument->FlushPendingNotifications(Flush_ContentAndNotify);
  8768       if (weakRoot.IsAlive()) {
  8769         WalkFramesThroughPlaceholders(mPresContext, rootFrame,
  8770                                       &ReResolveMenusAndTrees, nullptr);
  8772         // Because "chrome:" URL equality is messy, reframe image box
  8773         // frames (hack!).
  8774         nsStyleChangeList changeList;
  8775         WalkFramesThroughPlaceholders(mPresContext, rootFrame,
  8776                                       ReframeImageBoxes, &changeList);
  8777         // Mark ourselves as not safe to flush while we're doing frame
  8778         // construction.
  8780           nsAutoScriptBlocker scriptBlocker;
  8781           ++mChangeNestCount;
  8782           RestyleManager* restyleManager = mPresContext->RestyleManager();
  8783           restyleManager->ProcessRestyledFrames(changeList);
  8784           restyleManager->FlushOverflowChangedTracker();
  8785           --mChangeNestCount;
  8789     return NS_OK;
  8791 #endif
  8793   if (!nsCRT::strcmp(aTopic, "agent-sheet-added") && mStyleSet) {
  8794     AddAgentSheet(aSubject);
  8795     return NS_OK;
  8798   if (!nsCRT::strcmp(aTopic, "user-sheet-added") && mStyleSet) {
  8799     AddUserSheet(aSubject);
  8800     return NS_OK;
  8803   if (!nsCRT::strcmp(aTopic, "author-sheet-added") && mStyleSet) {
  8804     AddAuthorSheet(aSubject);
  8805     return NS_OK;
  8808   if (!nsCRT::strcmp(aTopic, "agent-sheet-removed") && mStyleSet) {
  8809     RemoveSheet(nsStyleSet::eAgentSheet, aSubject);
  8810     return NS_OK;
  8813   if (!nsCRT::strcmp(aTopic, "user-sheet-removed") && mStyleSet) {
  8814     RemoveSheet(nsStyleSet::eUserSheet, aSubject);
  8815     return NS_OK;
  8818   if (!nsCRT::strcmp(aTopic, "author-sheet-removed") && mStyleSet) {
  8819     RemoveSheet(nsStyleSet::eDocSheet, aSubject);
  8820     return NS_OK;
  8823   NS_WARNING("unrecognized topic in PresShell::Observe");
  8824   return NS_ERROR_FAILURE;
  8827 bool
  8828 nsIPresShell::AddRefreshObserverInternal(nsARefreshObserver* aObserver,
  8829                                          mozFlushType aFlushType)
  8831   nsPresContext* presContext = GetPresContext();
  8832   return presContext &&
  8833       presContext->RefreshDriver()->AddRefreshObserver(aObserver, aFlushType);
  8836 /* virtual */ bool
  8837 nsIPresShell::AddRefreshObserverExternal(nsARefreshObserver* aObserver,
  8838                                          mozFlushType aFlushType)
  8840   return AddRefreshObserverInternal(aObserver, aFlushType);
  8843 bool
  8844 nsIPresShell::RemoveRefreshObserverInternal(nsARefreshObserver* aObserver,
  8845                                             mozFlushType aFlushType)
  8847   nsPresContext* presContext = GetPresContext();
  8848   return presContext &&
  8849       presContext->RefreshDriver()->RemoveRefreshObserver(aObserver, aFlushType);
  8852 /* virtual */ bool
  8853 nsIPresShell::RemoveRefreshObserverExternal(nsARefreshObserver* aObserver,
  8854                                             mozFlushType aFlushType)
  8856   return RemoveRefreshObserverInternal(aObserver, aFlushType);
  8859 /* virtual */ bool
  8860 nsIPresShell::AddPostRefreshObserver(nsAPostRefreshObserver* aObserver)
  8862   nsPresContext* presContext = GetPresContext();
  8863   if (!presContext) {
  8864     return false;
  8866   presContext->RefreshDriver()->AddPostRefreshObserver(aObserver);
  8867   return true;
  8870 /* virtual */ bool
  8871 nsIPresShell::RemovePostRefreshObserver(nsAPostRefreshObserver* aObserver)
  8873   nsPresContext* presContext = GetPresContext();
  8874   if (!presContext) {
  8875     return false;
  8877   presContext->RefreshDriver()->RemovePostRefreshObserver(aObserver);
  8878   return true;
  8881 //------------------------------------------------------
  8882 // End of protected and private methods on the PresShell
  8883 //------------------------------------------------------
  8885 //------------------------------------------------------------------
  8886 //-- Delayed event Classes Impls
  8887 //------------------------------------------------------------------
  8889 PresShell::DelayedInputEvent::DelayedInputEvent() :
  8890   DelayedEvent(),
  8891   mEvent(nullptr)
  8895 PresShell::DelayedInputEvent::~DelayedInputEvent()
  8897   delete mEvent;
  8900 void
  8901 PresShell::DelayedInputEvent::Dispatch()
  8903   if (!mEvent || !mEvent->widget) {
  8904     return;
  8906   nsCOMPtr<nsIWidget> widget = mEvent->widget;
  8907   nsEventStatus status;
  8908   widget->DispatchEvent(mEvent, status);
  8911 PresShell::DelayedMouseEvent::DelayedMouseEvent(WidgetMouseEvent* aEvent) :
  8912   DelayedInputEvent()
  8914   WidgetMouseEvent* mouseEvent =
  8915     new WidgetMouseEvent(aEvent->mFlags.mIsTrusted,
  8916                          aEvent->message,
  8917                          aEvent->widget,
  8918                          aEvent->reason,
  8919                          aEvent->context);
  8920   mouseEvent->AssignMouseEventData(*aEvent, false);
  8921   mEvent = mouseEvent;
  8924 PresShell::DelayedKeyEvent::DelayedKeyEvent(WidgetKeyboardEvent* aEvent) :
  8925   DelayedInputEvent()
  8927   WidgetKeyboardEvent* keyEvent =
  8928     new WidgetKeyboardEvent(aEvent->mFlags.mIsTrusted,
  8929                             aEvent->message,
  8930                             aEvent->widget);
  8931   keyEvent->AssignKeyEventData(*aEvent, false);
  8932   keyEvent->mFlags.mIsSynthesizedForTests = aEvent->mFlags.mIsSynthesizedForTests;
  8933   mEvent = keyEvent;
  8936 // Start of DEBUG only code
  8938 #ifdef DEBUG
  8940 static void
  8941 LogVerifyMessage(nsIFrame* k1, nsIFrame* k2, const char* aMsg)
  8943   nsAutoString n1, n2;
  8944   if (k1) {
  8945     k1->GetFrameName(n1);
  8946   } else {
  8947     n1.Assign(NS_LITERAL_STRING("(null)"));
  8950   if (k2) {
  8951     k2->GetFrameName(n2);
  8952   } else {
  8953     n2.Assign(NS_LITERAL_STRING("(null)"));
  8956   printf("verifyreflow: %s %p != %s %p  %s\n",
  8957          NS_LossyConvertUTF16toASCII(n1).get(), (void*)k1,
  8958          NS_LossyConvertUTF16toASCII(n2).get(), (void*)k2, aMsg);
  8961 static void
  8962 LogVerifyMessage(nsIFrame* k1, nsIFrame* k2, const char* aMsg,
  8963                  const nsRect& r1, const nsRect& r2)
  8965   printf("VerifyReflow Error:\n");
  8966   nsAutoString name;
  8968   if (k1) {
  8969     k1->GetFrameName(name);
  8970     printf("  %s %p ", NS_LossyConvertUTF16toASCII(name).get(), (void*)k1);
  8972   printf("{%d, %d, %d, %d} != \n", r1.x, r1.y, r1.width, r1.height);
  8974   if (k2) {
  8975     k2->GetFrameName(name);
  8976     printf("  %s %p ", NS_LossyConvertUTF16toASCII(name).get(), (void*)k2);
  8978   printf("{%d, %d, %d, %d}\n  %s\n",
  8979          r2.x, r2.y, r2.width, r2.height, aMsg);
  8982 static void
  8983 LogVerifyMessage(nsIFrame* k1, nsIFrame* k2, const char* aMsg,
  8984                  const nsIntRect& r1, const nsIntRect& r2)
  8986   printf("VerifyReflow Error:\n");
  8987   nsAutoString name;
  8989   if (k1) {
  8990     k1->GetFrameName(name);
  8991     printf("  %s %p ", NS_LossyConvertUTF16toASCII(name).get(), (void*)k1);
  8993   printf("{%d, %d, %d, %d} != \n", r1.x, r1.y, r1.width, r1.height);
  8995   if (k2) {
  8996     k2->GetFrameName(name);
  8997     printf("  %s %p ", NS_LossyConvertUTF16toASCII(name).get(), (void*)k2);
  8999   printf("{%d, %d, %d, %d}\n  %s\n",
  9000          r2.x, r2.y, r2.width, r2.height, aMsg);
  9003 static bool
  9004 CompareTrees(nsPresContext* aFirstPresContext, nsIFrame* aFirstFrame,
  9005              nsPresContext* aSecondPresContext, nsIFrame* aSecondFrame)
  9007   if (!aFirstPresContext || !aFirstFrame || !aSecondPresContext || !aSecondFrame)
  9008     return true;
  9009   // XXX Evil hack to reduce false positives; I can't seem to figure
  9010   // out how to flush scrollbar changes correctly
  9011   //if (aFirstFrame->GetType() == nsGkAtoms::scrollbarFrame)
  9012   //  return true;
  9013   bool ok = true;
  9014   nsIFrame::ChildListIterator lists1(aFirstFrame);
  9015   nsIFrame::ChildListIterator lists2(aSecondFrame);
  9016   do {
  9017     const nsFrameList& kids1 = !lists1.IsDone() ? lists1.CurrentList() : nsFrameList();
  9018     const nsFrameList& kids2 = !lists2.IsDone() ? lists2.CurrentList() : nsFrameList();
  9019     int32_t l1 = kids1.GetLength();
  9020     int32_t l2 = kids2.GetLength();;
  9021     if (l1 != l2) {
  9022       ok = false;
  9023       LogVerifyMessage(kids1.FirstChild(), kids2.FirstChild(),
  9024                        "child counts don't match: ");
  9025       printf("%d != %d\n", l1, l2);
  9026       if (0 == (VERIFY_REFLOW_ALL & gVerifyReflowFlags)) {
  9027         break;
  9031     nsIntRect r1, r2;
  9032     nsView* v1, *v2;
  9033     for (nsFrameList::Enumerator e1(kids1), e2(kids2);
  9035          e1.Next(), e2.Next()) {
  9036       nsIFrame* k1 = e1.get();
  9037       nsIFrame* k2 = e2.get();
  9038       if (((nullptr == k1) && (nullptr != k2)) ||
  9039           ((nullptr != k1) && (nullptr == k2))) {
  9040         ok = false;
  9041         LogVerifyMessage(k1, k2, "child lists are different\n");
  9042         break;
  9044       else if (nullptr != k1) {
  9045         // Verify that the frames are the same size
  9046         if (!k1->GetRect().IsEqualInterior(k2->GetRect())) {
  9047           ok = false;
  9048           LogVerifyMessage(k1, k2, "(frame rects)", k1->GetRect(), k2->GetRect());
  9051         // Make sure either both have views or neither have views; if they
  9052         // do have views, make sure the views are the same size. If the
  9053         // views have widgets, make sure they both do or neither does. If
  9054         // they do, make sure the widgets are the same size.
  9055         v1 = k1->GetView();
  9056         v2 = k2->GetView();
  9057         if (((nullptr == v1) && (nullptr != v2)) ||
  9058             ((nullptr != v1) && (nullptr == v2))) {
  9059           ok = false;
  9060           LogVerifyMessage(k1, k2, "child views are not matched\n");
  9062         else if (nullptr != v1) {
  9063           if (!v1->GetBounds().IsEqualInterior(v2->GetBounds())) {
  9064             LogVerifyMessage(k1, k2, "(view rects)", v1->GetBounds(), v2->GetBounds());
  9067           nsIWidget* w1 = v1->GetWidget();
  9068           nsIWidget* w2 = v2->GetWidget();
  9069           if (((nullptr == w1) && (nullptr != w2)) ||
  9070               ((nullptr != w1) && (nullptr == w2))) {
  9071             ok = false;
  9072             LogVerifyMessage(k1, k2, "child widgets are not matched\n");
  9074           else if (nullptr != w1) {
  9075             w1->GetBounds(r1);
  9076             w2->GetBounds(r2);
  9077             if (!r1.IsEqualEdges(r2)) {
  9078               LogVerifyMessage(k1, k2, "(widget rects)", r1, r2);
  9082         if (!ok && (0 == (VERIFY_REFLOW_ALL & gVerifyReflowFlags))) {
  9083           break;
  9086         // XXX Should perhaps compare their float managers.
  9088         // Compare the sub-trees too
  9089         if (!CompareTrees(aFirstPresContext, k1, aSecondPresContext, k2)) {
  9090           ok = false;
  9091           if (0 == (VERIFY_REFLOW_ALL & gVerifyReflowFlags)) {
  9092             break;
  9096       else {
  9097         break;
  9100     if (!ok && (0 == (VERIFY_REFLOW_ALL & gVerifyReflowFlags))) {
  9101       break;
  9104     lists1.Next();
  9105     lists2.Next();
  9106     if (lists1.IsDone() != lists2.IsDone() ||
  9107         (!lists1.IsDone() && lists1.CurrentID() != lists2.CurrentID())) {
  9108       if (0 == (VERIFY_REFLOW_ALL & gVerifyReflowFlags)) {
  9109         ok = false;
  9111       LogVerifyMessage(kids1.FirstChild(), kids2.FirstChild(),
  9112                        "child list names are not matched: ");
  9113       fprintf(stdout, "%s != %s\n",
  9114               !lists1.IsDone() ? mozilla::layout::ChildListName(lists1.CurrentID()) : "(null)",
  9115               !lists2.IsDone() ? mozilla::layout::ChildListName(lists2.CurrentID()) : "(null)");
  9116       break;
  9118   } while (ok && !lists1.IsDone());
  9120   return ok;
  9122 #endif
  9124 #if 0
  9125 static nsIFrame*
  9126 FindTopFrame(nsIFrame* aRoot)
  9128   if (aRoot) {
  9129     nsIContent* content = aRoot->GetContent();
  9130     if (content) {
  9131       nsIAtom* tag;
  9132       content->GetTag(tag);
  9133       if (nullptr != tag) {
  9134         NS_RELEASE(tag);
  9135         return aRoot;
  9139     // Try one of the children
  9140     nsIFrame* kid = aRoot->GetFirstPrincipalChild();
  9141     while (nullptr != kid) {
  9142       nsIFrame* result = FindTopFrame(kid);
  9143       if (nullptr != result) {
  9144         return result;
  9146       kid = kid->GetNextSibling();
  9149   return nullptr;
  9151 #endif
  9154 #ifdef DEBUG
  9156 nsStyleSet*
  9157 PresShell::CloneStyleSet(nsStyleSet* aSet)
  9159   nsStyleSet *clone = new nsStyleSet();
  9161   int32_t i, n = aSet->SheetCount(nsStyleSet::eOverrideSheet);
  9162   for (i = 0; i < n; i++) {
  9163     nsIStyleSheet* ss = aSet->StyleSheetAt(nsStyleSet::eOverrideSheet, i);
  9164     if (ss)
  9165       clone->AppendStyleSheet(nsStyleSet::eOverrideSheet, ss);
  9168   // The document expects to insert document stylesheets itself
  9169 #if 0
  9170   n = aSet->SheetCount(nsStyleSet::eDocSheet);
  9171   for (i = 0; i < n; i++) {
  9172     nsIStyleSheet* ss = aSet->StyleSheetAt(nsStyleSet::eDocSheet, i);
  9173     if (ss)
  9174       clone->AddDocStyleSheet(ss, mDocument);
  9176 #endif
  9178   n = aSet->SheetCount(nsStyleSet::eUserSheet);
  9179   for (i = 0; i < n; i++) {
  9180     nsIStyleSheet* ss = aSet->StyleSheetAt(nsStyleSet::eUserSheet, i);
  9181     if (ss)
  9182       clone->AppendStyleSheet(nsStyleSet::eUserSheet, ss);
  9185   n = aSet->SheetCount(nsStyleSet::eAgentSheet);
  9186   for (i = 0; i < n; i++) {
  9187     nsIStyleSheet* ss = aSet->StyleSheetAt(nsStyleSet::eAgentSheet, i);
  9188     if (ss)
  9189       clone->AppendStyleSheet(nsStyleSet::eAgentSheet, ss);
  9191   return clone;
  9194 #ifdef DEBUG_Eli
  9195 static nsresult
  9196 DumpToPNG(nsIPresShell* shell, nsAString& name) {
  9197   int32_t width=1000, height=1000;
  9198   nsRect r(0, 0, shell->GetPresContext()->DevPixelsToAppUnits(width),
  9199                  shell->GetPresContext()->DevPixelsToAppUnits(height));
  9201   nsRefPtr<gfxImageSurface> imgSurface =
  9202      new gfxImageSurface(gfxIntSize(width, height),
  9203                          gfxImageFormat::ARGB32);
  9205   nsRefPtr<gfxContext> imgContext = new gfxContext(imgSurface);
  9207   nsRefPtr<gfxASurface> surface =
  9208     gfxPlatform::GetPlatform()->
  9209     CreateOffscreenSurface(IntSize(width, height),
  9210       gfxASurface::ContentFromFormat(gfxImageFormat::ARGB32));
  9211   NS_ENSURE_TRUE(surface, NS_ERROR_OUT_OF_MEMORY);
  9213   nsRefPtr<gfxContext> context = new gfxContext(surface);
  9215   shell->RenderDocument(r, 0, NS_RGB(255, 255, 0), context);
  9217   imgContext->DrawSurface(surface, gfxSize(width, height));
  9219   nsCOMPtr<imgIEncoder> encoder = do_CreateInstance("@mozilla.org/image/encoder;2?type=image/png");
  9220   NS_ENSURE_TRUE(encoder, NS_ERROR_FAILURE);
  9221   encoder->InitFromData(imgSurface->Data(), imgSurface->Stride() * height,
  9222                         width, height, imgSurface->Stride(),
  9223                         imgIEncoder::INPUT_FORMAT_HOSTARGB, EmptyString());
  9225   // XXX not sure if this is the right way to write to a file
  9226   nsCOMPtr<nsIFile> file = do_CreateInstance("@mozilla.org/file/local;1");
  9227   NS_ENSURE_TRUE(file, NS_ERROR_FAILURE);
  9228   rv = file->InitWithPath(name);
  9229   NS_ENSURE_SUCCESS(rv, rv);
  9231   uint64_t length64;
  9232   rv = encoder->Available(&length64);
  9233   NS_ENSURE_SUCCESS(rv, rv);
  9234   if (length64 > UINT32_MAX)
  9235     return NS_ERROR_FILE_TOO_BIG;
  9237   uint32_t length = (uint32_t)length64;
  9239   nsCOMPtr<nsIOutputStream> outputStream;
  9240   rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), file);
  9241   NS_ENSURE_SUCCESS(rv, rv);
  9243   nsCOMPtr<nsIOutputStream> bufferedOutputStream;
  9244   rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream),
  9245                                   outputStream, length);
  9246   NS_ENSURE_SUCCESS(rv, rv);
  9248   uint32_t numWritten;
  9249   rv = bufferedOutputStream->WriteFrom(encoder, length, &numWritten);
  9250   NS_ENSURE_SUCCESS(rv, rv);
  9252   return NS_OK;
  9254 #endif
  9256 // After an incremental reflow, we verify the correctness by doing a
  9257 // full reflow into a fresh frame tree.
  9258 bool
  9259 PresShell::VerifyIncrementalReflow()
  9261    if (VERIFY_REFLOW_NOISY & gVerifyReflowFlags) {
  9262      printf("Building Verification Tree...\n");
  9265   // Create a presentation context to view the new frame tree
  9266   nsRefPtr<nsPresContext> cx =
  9267        new nsRootPresContext(mDocument, mPresContext->IsPaginated() ?
  9268                                         nsPresContext::eContext_PrintPreview :
  9269                                         nsPresContext::eContext_Galley);
  9270   NS_ENSURE_TRUE(cx, false);
  9272   nsDeviceContext *dc = mPresContext->DeviceContext();
  9273   nsresult rv = cx->Init(dc);
  9274   NS_ENSURE_SUCCESS(rv, false);
  9276   // Get our scrolling preference
  9277   nsView* rootView = mViewManager->GetRootView();
  9278   NS_ENSURE_TRUE(rootView->HasWidget(), false);
  9279   nsIWidget* parentWidget = rootView->GetWidget();
  9281   // Create a new view manager.
  9282   nsRefPtr<nsViewManager> vm = new nsViewManager();
  9283   NS_ENSURE_TRUE(vm, false);
  9284   rv = vm->Init(dc);
  9285   NS_ENSURE_SUCCESS(rv, false);
  9287   // Create a child window of the parent that is our "root view/window"
  9288   // Create a view
  9289   nsRect tbounds = mPresContext->GetVisibleArea();
  9290   nsView* view = vm->CreateView(tbounds, nullptr);
  9291   NS_ENSURE_TRUE(view, false);
  9293   //now create the widget for the view
  9294   rv = view->CreateWidgetForParent(parentWidget, nullptr, true);
  9295   NS_ENSURE_SUCCESS(rv, false);
  9297   // Setup hierarchical relationship in view manager
  9298   vm->SetRootView(view);
  9300   // Make the new presentation context the same size as our
  9301   // presentation context.
  9302   nsRect r = mPresContext->GetVisibleArea();
  9303   cx->SetVisibleArea(r);
  9305   // Create a new presentation shell to view the document. Use the
  9306   // exact same style information that this document has.
  9307   nsAutoPtr<nsStyleSet> newSet(CloneStyleSet(mStyleSet));
  9308   nsCOMPtr<nsIPresShell> sh = mDocument->CreateShell(cx, vm, newSet);
  9309   NS_ENSURE_TRUE(sh, false);
  9310   newSet.forget();
  9311   // Note that after we create the shell, we must make sure to destroy it
  9312   sh->SetVerifyReflowEnable(false); // turn off verify reflow while we're reflowing the test frame tree
  9313   vm->SetPresShell(sh);
  9315     nsAutoCauseReflowNotifier crNotifier(this);
  9316     sh->Initialize(r.width, r.height);
  9318   mDocument->BindingManager()->ProcessAttachedQueue();
  9319   sh->FlushPendingNotifications(Flush_Layout);
  9320   sh->SetVerifyReflowEnable(true);  // turn on verify reflow again now that we're done reflowing the test frame tree
  9321   // Force the non-primary presshell to unsuppress; it doesn't want to normally
  9322   // because it thinks it's hidden
  9323   ((PresShell*)sh.get())->mPaintingSuppressed = false;
  9324   if (VERIFY_REFLOW_NOISY & gVerifyReflowFlags) {
  9325      printf("Verification Tree built, comparing...\n");
  9328   // Now that the document has been reflowed, use its frame tree to
  9329   // compare against our frame tree.
  9330   nsIFrame* root1 = mFrameConstructor->GetRootFrame();
  9331   nsIFrame* root2 = sh->GetRootFrame();
  9332   bool ok = CompareTrees(mPresContext, root1, cx, root2);
  9333   if (!ok && (VERIFY_REFLOW_NOISY & gVerifyReflowFlags)) {
  9334     printf("Verify reflow failed, primary tree:\n");
  9335     root1->List(stdout, 0);
  9336     printf("Verification tree:\n");
  9337     root2->List(stdout, 0);
  9340 #ifdef DEBUG_Eli
  9341   // Sample code for dumping page to png
  9342   // XXX Needs to be made more flexible
  9343   if (!ok) {
  9344     nsString stra;
  9345     static int num = 0;
  9346     stra.AppendLiteral("C:\\mozilla\\mozilla\\debug\\filea");
  9347     stra.AppendInt(num);
  9348     stra.AppendLiteral(".png");
  9349     DumpToPNG(sh, stra);
  9350     nsString strb;
  9351     strb.AppendLiteral("C:\\mozilla\\mozilla\\debug\\fileb");
  9352     strb.AppendInt(num);
  9353     strb.AppendLiteral(".png");
  9354     DumpToPNG(this, strb);
  9355     ++num;
  9357 #endif
  9359   sh->EndObservingDocument();
  9360   sh->Destroy();
  9361   if (VERIFY_REFLOW_NOISY & gVerifyReflowFlags) {
  9362     printf("Finished Verifying Reflow...\n");
  9365   return ok;
  9368 // Layout debugging hooks
  9369 void
  9370 PresShell::ListStyleContexts(nsIFrame *aRootFrame, FILE *out, int32_t aIndent)
  9372   nsStyleContext *sc = aRootFrame->StyleContext();
  9373   if (sc)
  9374     sc->List(out, aIndent);
  9377 void
  9378 PresShell::ListStyleSheets(FILE *out, int32_t aIndent)
  9380   int32_t sheetCount = mStyleSet->SheetCount(nsStyleSet::eDocSheet);
  9381   for (int32_t i = 0; i < sheetCount; ++i) {
  9382     mStyleSet->StyleSheetAt(nsStyleSet::eDocSheet, i)->List(out, aIndent);
  9383     fputs("\n", out);
  9387 void
  9388 PresShell::VerifyStyleTree()
  9390   VERIFY_STYLE_TREE;
  9392 #endif
  9394 //=============================================================
  9395 //=============================================================
  9396 //-- Debug Reflow Counts
  9397 //=============================================================
  9398 //=============================================================
  9399 #ifdef MOZ_REFLOW_PERF
  9400 //-------------------------------------------------------------
  9401 void
  9402 PresShell::DumpReflows()
  9404   if (mReflowCountMgr) {
  9405     nsAutoCString uriStr;
  9406     if (mDocument) {
  9407       nsIURI *uri = mDocument->GetDocumentURI();
  9408       if (uri) {
  9409         uri->GetPath(uriStr);
  9412     mReflowCountMgr->DisplayTotals(uriStr.get());
  9413     mReflowCountMgr->DisplayHTMLTotals(uriStr.get());
  9414     mReflowCountMgr->DisplayDiffsInTotals("Differences");
  9418 //-------------------------------------------------------------
  9419 void
  9420 PresShell::CountReflows(const char * aName, nsIFrame * aFrame)
  9422   if (mReflowCountMgr) {
  9423     mReflowCountMgr->Add(aName, aFrame);
  9427 //-------------------------------------------------------------
  9428 void
  9429 PresShell::PaintCount(const char * aName,
  9430                       nsRenderingContext* aRenderingContext,
  9431                       nsPresContext* aPresContext,
  9432                       nsIFrame * aFrame,
  9433                       const nsPoint& aOffset,
  9434                       uint32_t aColor)
  9436   if (mReflowCountMgr) {
  9437     mReflowCountMgr->PaintCount(aName, aRenderingContext, aPresContext,
  9438                                 aFrame, aOffset, aColor);
  9442 //-------------------------------------------------------------
  9443 void
  9444 PresShell::SetPaintFrameCount(bool aPaintFrameCounts)
  9446   if (mReflowCountMgr) {
  9447     mReflowCountMgr->SetPaintFrameCounts(aPaintFrameCounts);
  9451 bool
  9452 PresShell::IsPaintingFrameCounts()
  9454   if (mReflowCountMgr)
  9455     return mReflowCountMgr->IsPaintingFrameCounts();
  9456   return false;
  9459 //------------------------------------------------------------------
  9460 //-- Reflow Counter Classes Impls
  9461 //------------------------------------------------------------------
  9463 //------------------------------------------------------------------
  9464 ReflowCounter::ReflowCounter(ReflowCountMgr * aMgr) :
  9465   mMgr(aMgr)
  9467   ClearTotals();
  9468   SetTotalsCache();
  9471 //------------------------------------------------------------------
  9472 ReflowCounter::~ReflowCounter()
  9477 //------------------------------------------------------------------
  9478 void ReflowCounter::ClearTotals()
  9480   mTotal = 0;
  9483 //------------------------------------------------------------------
  9484 void ReflowCounter::SetTotalsCache()
  9486   mCacheTotal = mTotal;
  9489 //------------------------------------------------------------------
  9490 void ReflowCounter::CalcDiffInTotals()
  9492   mCacheTotal = mTotal - mCacheTotal;
  9495 //------------------------------------------------------------------
  9496 void ReflowCounter::DisplayTotals(const char * aStr)
  9498   DisplayTotals(mTotal, aStr?aStr:"Totals");
  9501 //------------------------------------------------------------------
  9502 void ReflowCounter::DisplayDiffTotals(const char * aStr)
  9504   DisplayTotals(mCacheTotal, aStr?aStr:"Diff Totals");
  9507 //------------------------------------------------------------------
  9508 void ReflowCounter::DisplayHTMLTotals(const char * aStr)
  9510   DisplayHTMLTotals(mTotal, aStr?aStr:"Totals");
  9513 //------------------------------------------------------------------
  9514 void ReflowCounter::DisplayTotals(uint32_t aTotal, const char * aTitle)
  9516   // figure total
  9517   if (aTotal == 0) {
  9518     return;
  9520   ReflowCounter * gTots = (ReflowCounter *)mMgr->LookUp(kGrandTotalsStr);
  9522   printf("%25s\t", aTitle);
  9523   printf("%d\t", aTotal);
  9524   if (gTots != this && aTotal > 0) {
  9525     gTots->Add(aTotal);
  9529 //------------------------------------------------------------------
  9530 void ReflowCounter::DisplayHTMLTotals(uint32_t aTotal, const char * aTitle)
  9532   if (aTotal == 0) {
  9533     return;
  9536   ReflowCounter * gTots = (ReflowCounter *)mMgr->LookUp(kGrandTotalsStr);
  9537   FILE * fd = mMgr->GetOutFile();
  9538   if (!fd) {
  9539     return;
  9542   fprintf(fd, "<tr><td><center>%s</center></td>", aTitle);
  9543   fprintf(fd, "<td><center>%d</center></td></tr>\n", aTotal);
  9545   if (gTots != this && aTotal > 0) {
  9546     gTots->Add(aTotal);
  9550 //------------------------------------------------------------------
  9551 //-- ReflowCountMgr
  9552 //------------------------------------------------------------------
  9554 #define KEY_BUF_SIZE_FOR_PTR  24 // adequate char[] buffer to sprintf a pointer
  9556 ReflowCountMgr::ReflowCountMgr()
  9558   mCounts = PL_NewHashTable(10, PL_HashString, PL_CompareStrings,
  9559                                 PL_CompareValues, nullptr, nullptr);
  9560   mIndiFrameCounts = PL_NewHashTable(10, PL_HashString, PL_CompareStrings,
  9561                                      PL_CompareValues, nullptr, nullptr);
  9562   mCycledOnce              = false;
  9563   mDumpFrameCounts         = false;
  9564   mDumpFrameByFrameCounts  = false;
  9565   mPaintFrameByFrameCounts = false;
  9568 //------------------------------------------------------------------
  9569 ReflowCountMgr::~ReflowCountMgr()
  9571   CleanUp();
  9574 //------------------------------------------------------------------
  9575 ReflowCounter * ReflowCountMgr::LookUp(const char * aName)
  9577   if (nullptr != mCounts) {
  9578     ReflowCounter * counter = (ReflowCounter *)PL_HashTableLookup(mCounts, aName);
  9579     return counter;
  9581   return nullptr;
  9585 //------------------------------------------------------------------
  9586 void ReflowCountMgr::Add(const char * aName, nsIFrame * aFrame)
  9588   NS_ASSERTION(aName != nullptr, "Name shouldn't be null!");
  9590   if (mDumpFrameCounts && nullptr != mCounts) {
  9591     ReflowCounter * counter = (ReflowCounter *)PL_HashTableLookup(mCounts, aName);
  9592     if (counter == nullptr) {
  9593       counter = new ReflowCounter(this);
  9594       char * name = NS_strdup(aName);
  9595       NS_ASSERTION(name != nullptr, "null ptr");
  9596       PL_HashTableAdd(mCounts, name, counter);
  9598     counter->Add();
  9601   if ((mDumpFrameByFrameCounts || mPaintFrameByFrameCounts) &&
  9602       nullptr != mIndiFrameCounts &&
  9603       aFrame != nullptr) {
  9604     char key[KEY_BUF_SIZE_FOR_PTR];
  9605     sprintf(key, "%p", (void*)aFrame);
  9606     IndiReflowCounter * counter = (IndiReflowCounter *)PL_HashTableLookup(mIndiFrameCounts, key);
  9607     if (counter == nullptr) {
  9608       counter = new IndiReflowCounter(this);
  9609       counter->mFrame = aFrame;
  9610       counter->mName.AssignASCII(aName);
  9611       PL_HashTableAdd(mIndiFrameCounts, NS_strdup(key), counter);
  9613     // this eliminates extra counts from super classes
  9614     if (counter != nullptr && counter->mName.EqualsASCII(aName)) {
  9615       counter->mCount++;
  9616       counter->mCounter.Add(1);
  9621 //------------------------------------------------------------------
  9622 void ReflowCountMgr::PaintCount(const char*     aName,
  9623                                 nsRenderingContext* aRenderingContext,
  9624                                 nsPresContext*  aPresContext,
  9625                                 nsIFrame*       aFrame,
  9626                                 const nsPoint&  aOffset,
  9627                                 uint32_t        aColor)
  9629   if (mPaintFrameByFrameCounts &&
  9630       nullptr != mIndiFrameCounts &&
  9631       aFrame != nullptr) {
  9632     char key[KEY_BUF_SIZE_FOR_PTR];
  9633     sprintf(key, "%p", (void*)aFrame);
  9634     IndiReflowCounter * counter =
  9635       (IndiReflowCounter *)PL_HashTableLookup(mIndiFrameCounts, key);
  9636     if (counter != nullptr && counter->mName.EqualsASCII(aName)) {
  9637       aRenderingContext->PushState();
  9638       aRenderingContext->Translate(aOffset);
  9639       nsFont font("Times", NS_FONT_STYLE_NORMAL, NS_FONT_VARIANT_NORMAL,
  9640                   NS_FONT_WEIGHT_NORMAL, NS_FONT_STRETCH_NORMAL, 0,
  9641                   nsPresContext::CSSPixelsToAppUnits(11));
  9643       nsRefPtr<nsFontMetrics> fm;
  9644       aPresContext->DeviceContext()->GetMetricsFor(font,
  9645         // We have one frame, therefore we must have a root...
  9646         aPresContext->GetPresShell()->GetRootFrame()->
  9647           StyleFont()->mLanguage,
  9648         aPresContext->GetUserFontSet(),
  9649         aPresContext->GetTextPerfMetrics(),
  9650         *getter_AddRefs(fm));
  9652       aRenderingContext->SetFont(fm);
  9653       char buf[16];
  9654       sprintf(buf, "%d", counter->mCount);
  9655       nscoord x = 0, y = fm->MaxAscent();
  9656       nscoord width, height = fm->MaxHeight();
  9657       aRenderingContext->SetTextRunRTL(false);
  9658       width = aRenderingContext->GetWidth(buf);
  9660       uint32_t color;
  9661       uint32_t color2;
  9662       if (aColor != 0) {
  9663         color  = aColor;
  9664         color2 = NS_RGB(0,0,0);
  9665       } else {
  9666         uint8_t rc = 0, gc = 0, bc = 0;
  9667         if (counter->mCount < 5) {
  9668           rc = 255;
  9669           gc = 255;
  9670         } else if ( counter->mCount < 11) {
  9671           gc = 255;
  9672         } else {
  9673           rc = 255;
  9675         color  = NS_RGB(rc,gc,bc);
  9676         color2 = NS_RGB(rc/2,gc/2,bc/2);
  9679       nsRect rect(0,0, width+15, height+15);
  9680       aRenderingContext->SetColor(NS_RGB(0,0,0));
  9681       aRenderingContext->FillRect(rect);
  9682       aRenderingContext->SetColor(color2);
  9683       aRenderingContext->DrawString(buf, strlen(buf), x+15,y+15);
  9684       aRenderingContext->SetColor(color);
  9685       aRenderingContext->DrawString(buf, strlen(buf), x,y);
  9687       aRenderingContext->PopState();
  9692 //------------------------------------------------------------------
  9693 int ReflowCountMgr::RemoveItems(PLHashEntry *he, int i, void *arg)
  9695   char *str = (char *)he->key;
  9696   ReflowCounter * counter = (ReflowCounter *)he->value;
  9697   delete counter;
  9698   NS_Free(str);
  9700   return HT_ENUMERATE_REMOVE;
  9703 //------------------------------------------------------------------
  9704 int ReflowCountMgr::RemoveIndiItems(PLHashEntry *he, int i, void *arg)
  9706   char *str = (char *)he->key;
  9707   IndiReflowCounter * counter = (IndiReflowCounter *)he->value;
  9708   delete counter;
  9709   NS_Free(str);
  9711   return HT_ENUMERATE_REMOVE;
  9714 //------------------------------------------------------------------
  9715 void ReflowCountMgr::CleanUp()
  9717   if (nullptr != mCounts) {
  9718     PL_HashTableEnumerateEntries(mCounts, RemoveItems, nullptr);
  9719     PL_HashTableDestroy(mCounts);
  9720     mCounts = nullptr;
  9723   if (nullptr != mIndiFrameCounts) {
  9724     PL_HashTableEnumerateEntries(mIndiFrameCounts, RemoveIndiItems, nullptr);
  9725     PL_HashTableDestroy(mIndiFrameCounts);
  9726     mIndiFrameCounts = nullptr;
  9730 //------------------------------------------------------------------
  9731 int ReflowCountMgr::DoSingleTotal(PLHashEntry *he, int i, void *arg)
  9733   char *str = (char *)he->key;
  9734   ReflowCounter * counter = (ReflowCounter *)he->value;
  9736   counter->DisplayTotals(str);
  9738   return HT_ENUMERATE_NEXT;
  9741 //------------------------------------------------------------------
  9742 void ReflowCountMgr::DoGrandTotals()
  9744   if (nullptr != mCounts) {
  9745     ReflowCounter * gTots = (ReflowCounter *)PL_HashTableLookup(mCounts, kGrandTotalsStr);
  9746     if (gTots == nullptr) {
  9747       gTots = new ReflowCounter(this);
  9748       PL_HashTableAdd(mCounts, NS_strdup(kGrandTotalsStr), gTots);
  9749     } else {
  9750       gTots->ClearTotals();
  9753     printf("\t\t\t\tTotal\n");
  9754     for (uint32_t i=0;i<78;i++) {
  9755       printf("-");
  9757     printf("\n");
  9758     PL_HashTableEnumerateEntries(mCounts, DoSingleTotal, this);
  9762 static void RecurseIndiTotals(nsPresContext* aPresContext,
  9763                               PLHashTable *   aHT,
  9764                               nsIFrame *      aParentFrame,
  9765                               int32_t         aLevel)
  9767   if (aParentFrame == nullptr) {
  9768     return;
  9771   char key[KEY_BUF_SIZE_FOR_PTR];
  9772   sprintf(key, "%p", (void*)aParentFrame);
  9773   IndiReflowCounter * counter = (IndiReflowCounter *)PL_HashTableLookup(aHT, key);
  9774   if (counter) {
  9775     counter->mHasBeenOutput = true;
  9776     char * name = ToNewCString(counter->mName);
  9777     for (int32_t i=0;i<aLevel;i++) printf(" ");
  9778     printf("%s - %p   [%d][", name, (void*)aParentFrame, counter->mCount);
  9779     printf("%d", counter->mCounter.GetTotal());
  9780     printf("]\n");
  9781     nsMemory::Free(name);
  9784   nsIFrame* child = aParentFrame->GetFirstPrincipalChild();
  9785   while (child) {
  9786     RecurseIndiTotals(aPresContext, aHT, child, aLevel+1);
  9787     child = child->GetNextSibling();
  9792 //------------------------------------------------------------------
  9793 int ReflowCountMgr::DoSingleIndi(PLHashEntry *he, int i, void *arg)
  9795   IndiReflowCounter * counter = (IndiReflowCounter *)he->value;
  9796   if (counter && !counter->mHasBeenOutput) {
  9797     char * name = ToNewCString(counter->mName);
  9798     printf("%s - %p   [%d][", name, (void*)counter->mFrame, counter->mCount);
  9799     printf("%d", counter->mCounter.GetTotal());
  9800     printf("]\n");
  9801     nsMemory::Free(name);
  9803   return HT_ENUMERATE_NEXT;
  9806 //------------------------------------------------------------------
  9807 void ReflowCountMgr::DoIndiTotalsTree()
  9809   if (nullptr != mCounts) {
  9810     printf("\n------------------------------------------------\n");
  9811     printf("-- Individual Frame Counts\n");
  9812     printf("------------------------------------------------\n");
  9814     if (mPresShell) {
  9815       nsIFrame * rootFrame = mPresShell->FrameManager()->GetRootFrame();
  9816       RecurseIndiTotals(mPresContext, mIndiFrameCounts, rootFrame, 0);
  9817       printf("------------------------------------------------\n");
  9818       printf("-- Individual Counts of Frames not in Root Tree\n");
  9819       printf("------------------------------------------------\n");
  9820       PL_HashTableEnumerateEntries(mIndiFrameCounts, DoSingleIndi, this);
  9825 //------------------------------------------------------------------
  9826 int ReflowCountMgr::DoSingleHTMLTotal(PLHashEntry *he, int i, void *arg)
  9828   char *str = (char *)he->key;
  9829   ReflowCounter * counter = (ReflowCounter *)he->value;
  9831   counter->DisplayHTMLTotals(str);
  9833   return HT_ENUMERATE_NEXT;
  9836 //------------------------------------------------------------------
  9837 void ReflowCountMgr::DoGrandHTMLTotals()
  9839   if (nullptr != mCounts) {
  9840     ReflowCounter * gTots = (ReflowCounter *)PL_HashTableLookup(mCounts, kGrandTotalsStr);
  9841     if (gTots == nullptr) {
  9842       gTots = new ReflowCounter(this);
  9843       PL_HashTableAdd(mCounts, NS_strdup(kGrandTotalsStr), gTots);
  9844     } else {
  9845       gTots->ClearTotals();
  9848     static const char * title[] = {"Class", "Reflows"};
  9849     fprintf(mFD, "<tr>");
  9850     for (uint32_t i=0; i < ArrayLength(title); i++) {
  9851       fprintf(mFD, "<td><center><b>%s<b></center></td>", title[i]);
  9853     fprintf(mFD, "</tr>\n");
  9854     PL_HashTableEnumerateEntries(mCounts, DoSingleHTMLTotal, this);
  9858 //------------------------------------
  9859 void ReflowCountMgr::DisplayTotals(const char * aStr)
  9861 #ifdef DEBUG_rods
  9862   printf("%s\n", aStr?aStr:"No name");
  9863 #endif
  9864   if (mDumpFrameCounts) {
  9865     DoGrandTotals();
  9867   if (mDumpFrameByFrameCounts) {
  9868     DoIndiTotalsTree();
  9872 //------------------------------------
  9873 void ReflowCountMgr::DisplayHTMLTotals(const char * aStr)
  9875 #ifdef WIN32x // XXX NOT XP!
  9876   char name[1024];
  9878   char * sptr = strrchr(aStr, '/');
  9879   if (sptr) {
  9880     sptr++;
  9881     strcpy(name, sptr);
  9882     char * eptr = strrchr(name, '.');
  9883     if (eptr) {
  9884       *eptr = 0;
  9886     strcat(name, "_stats.html");
  9888   mFD = fopen(name, "w");
  9889   if (mFD) {
  9890     fprintf(mFD, "<html><head><title>Reflow Stats</title></head><body>\n");
  9891     const char * title = aStr?aStr:"No name";
  9892     fprintf(mFD, "<center><b>%s</b><br><table border=1 style=\"background-color:#e0e0e0\">", title);
  9893     DoGrandHTMLTotals();
  9894     fprintf(mFD, "</center></table>\n");
  9895     fprintf(mFD, "</body></html>\n");
  9896     fclose(mFD);
  9897     mFD = nullptr;
  9899 #endif // not XP!
  9902 //------------------------------------------------------------------
  9903 int ReflowCountMgr::DoClearTotals(PLHashEntry *he, int i, void *arg)
  9905   ReflowCounter * counter = (ReflowCounter *)he->value;
  9906   counter->ClearTotals();
  9908   return HT_ENUMERATE_NEXT;
  9911 //------------------------------------------------------------------
  9912 void ReflowCountMgr::ClearTotals()
  9914   PL_HashTableEnumerateEntries(mCounts, DoClearTotals, this);
  9917 //------------------------------------------------------------------
  9918 void ReflowCountMgr::ClearGrandTotals()
  9920   if (nullptr != mCounts) {
  9921     ReflowCounter * gTots = (ReflowCounter *)PL_HashTableLookup(mCounts, kGrandTotalsStr);
  9922     if (gTots == nullptr) {
  9923       gTots = new ReflowCounter(this);
  9924       PL_HashTableAdd(mCounts, NS_strdup(kGrandTotalsStr), gTots);
  9925     } else {
  9926       gTots->ClearTotals();
  9927       gTots->SetTotalsCache();
  9932 //------------------------------------------------------------------
  9933 int ReflowCountMgr::DoDisplayDiffTotals(PLHashEntry *he, int i, void *arg)
  9935   bool cycledOnce = (arg != 0);
  9937   char *str = (char *)he->key;
  9938   ReflowCounter * counter = (ReflowCounter *)he->value;
  9940   if (cycledOnce) {
  9941     counter->CalcDiffInTotals();
  9942     counter->DisplayDiffTotals(str);
  9944   counter->SetTotalsCache();
  9946   return HT_ENUMERATE_NEXT;
  9949 //------------------------------------------------------------------
  9950 void ReflowCountMgr::DisplayDiffsInTotals(const char * aStr)
  9952   if (mCycledOnce) {
  9953     printf("Differences\n");
  9954     for (int32_t i=0;i<78;i++) {
  9955       printf("-");
  9957     printf("\n");
  9958     ClearGrandTotals();
  9960   PL_HashTableEnumerateEntries(mCounts, DoDisplayDiffTotals, (void *)mCycledOnce);
  9962   mCycledOnce = true;
  9965 #endif // MOZ_REFLOW_PERF
  9967 // make a color string like #RRGGBB
  9968 void ColorToString(nscolor aColor, nsAutoString &aString)
  9970   char buf[8];
  9972   PR_snprintf(buf, sizeof(buf), "#%02x%02x%02x",
  9973               NS_GET_R(aColor), NS_GET_G(aColor), NS_GET_B(aColor));
  9974   CopyASCIItoUTF16(buf, aString);
  9977 nsIFrame* nsIPresShell::GetAbsoluteContainingBlock(nsIFrame *aFrame)
  9979   return FrameConstructor()->GetAbsoluteContainingBlock(aFrame,
  9980       nsCSSFrameConstructor::ABS_POS);
  9983 #ifdef ACCESSIBILITY
  9984 bool
  9985 nsIPresShell::IsAccessibilityActive()
  9987   return GetAccService() != nullptr;
  9990 nsAccessibilityService*
  9991 nsIPresShell::AccService()
  9993   return GetAccService();
  9995 #endif
  9997 void nsIPresShell::InitializeStatics()
  9999   NS_ASSERTION(!gCaptureTouchList, "InitializeStatics called multiple times!");
 10000   gCaptureTouchList = new nsRefPtrHashtable<nsUint32HashKey, dom::Touch>;
 10001   gPointerCaptureList = new nsRefPtrHashtable<nsUint32HashKey, nsIContent>;
 10002   gActivePointersIds = new nsClassHashtable<nsUint32HashKey, PointerInfo>;
 10005 void nsIPresShell::ReleaseStatics()
 10007   NS_ASSERTION(gCaptureTouchList, "ReleaseStatics called without Initialize!");
 10008   delete gCaptureTouchList;
 10009   gCaptureTouchList = nullptr;
 10010   delete gPointerCaptureList;
 10011   gPointerCaptureList = nullptr;
 10012   delete gActivePointersIds;
 10013   gActivePointersIds = nullptr;
 10016 // Asks our docshell whether we're active.
 10017 void PresShell::QueryIsActive()
 10019   nsCOMPtr<nsISupports> container = mPresContext->GetContainerWeak();
 10020   if (mDocument) {
 10021     nsIDocument* displayDoc = mDocument->GetDisplayDocument();
 10022     if (displayDoc) {
 10023       // Ok, we're an external resource document -- we need to use our display
 10024       // document's docshell to determine "IsActive" status, since we lack
 10025       // a container.
 10026       NS_ABORT_IF_FALSE(!container,
 10027                         "external resource doc shouldn't have "
 10028                         "its own container");
 10030       nsIPresShell* displayPresShell = displayDoc->GetShell();
 10031       if (displayPresShell) {
 10032         container = displayPresShell->GetPresContext()->GetContainerWeak();
 10037   nsCOMPtr<nsIDocShell> docshell(do_QueryInterface(container));
 10038   if (docshell) {
 10039     bool isActive;
 10040     nsresult rv = docshell->GetIsActive(&isActive);
 10041     if (NS_SUCCEEDED(rv))
 10042       SetIsActive(isActive);
 10046 // Helper for propagating mIsActive changes to external resources
 10047 static bool
 10048 SetExternalResourceIsActive(nsIDocument* aDocument, void* aClosure)
 10050   nsIPresShell* shell = aDocument->GetShell();
 10051   if (shell) {
 10052     shell->SetIsActive(*static_cast<bool*>(aClosure));
 10054   return true;
 10057 static void
 10058 SetPluginIsActive(nsIContent* aContent, void* aClosure)
 10060   nsIFrame *frame = aContent->GetPrimaryFrame();
 10061   nsIObjectFrame *objectFrame = do_QueryFrame(frame);
 10062   if (objectFrame) {
 10063     objectFrame->SetIsDocumentActive(*static_cast<bool*>(aClosure));
 10067 nsresult
 10068 PresShell::SetIsActive(bool aIsActive)
 10070   NS_PRECONDITION(mDocument, "should only be called with a document");
 10072   mIsActive = aIsActive;
 10073   nsPresContext* presContext = GetPresContext();
 10074   if (presContext &&
 10075       presContext->RefreshDriver()->PresContext() == presContext) {
 10076     presContext->RefreshDriver()->SetThrottled(!mIsActive);
 10079   // Propagate state-change to my resource documents' PresShells
 10080   mDocument->EnumerateExternalResources(SetExternalResourceIsActive,
 10081                                         &aIsActive);
 10082   mDocument->EnumerateFreezableElements(SetPluginIsActive,
 10083                                         &aIsActive);
 10084   nsresult rv = UpdateImageLockingState();
 10085 #ifdef ACCESSIBILITY
 10086   if (aIsActive) {
 10087     nsAccessibilityService* accService = AccService();
 10088     if (accService) {
 10089       accService->PresShellActivated(this);
 10092 #endif
 10094   // We have this odd special case here because remote content behaves
 10095   // differently from same-process content when "hidden".  In
 10096   // desktop-type "browser UIs", hidden "tabs" have documents that are
 10097   // part of the chrome tree.  When the tabs are hidden, their content
 10098   // is no longer part of the visible document tree, and the layers
 10099   // for the content are naturally released.
 10100   //
 10101   // Remote content is its own top-level tree in its subprocess.  When
 10102   // it's "hidden", there's no transaction in which the document
 10103   // thinks it's not visible, so layers can be retained forever.  This
 10104   // is problematic when those layers uselessly hold on to precious
 10105   // resources like directly texturable memory.
 10106   //
 10107   // PresShell::SetIsActive() is the first C++ entry point at which we
 10108   // (i) know that our parent process wants our content to be hidden;
 10109   // and (ii) has easy access to the TabChild.  So we use this
 10110   // notification to signal the TabChild to drop its layer tree and
 10111   // stop trying to repaint.
 10112   if (TabChild* tab = TabChild::GetFrom(this)) {
 10113     if (aIsActive) {
 10114       tab->MakeVisible();
 10115       if (!mIsZombie) {
 10116         if (nsIFrame* root = mFrameConstructor->GetRootFrame()) {
 10117           FrameLayerBuilder::InvalidateAllLayersForFrame(
 10118             nsLayoutUtils::GetDisplayRootFrame(root));
 10119           root->SchedulePaint();
 10122     } else {
 10123       tab->MakeHidden();
 10127   return rv;
 10130 /*
 10131  * Determines the current image locking state. Called when one of the
 10132  * dependent factors changes.
 10133  */
 10134 nsresult
 10135 PresShell::UpdateImageLockingState()
 10137   // We're locked if we're both thawed and active.
 10138   return mDocument->SetImageLockingState(!mFrozen && mIsActive);
 10141 PresShell*
 10142 PresShell::GetRootPresShell()
 10144   if (mPresContext) {
 10145     nsPresContext* rootPresContext = mPresContext->GetRootPresContext();
 10146     if (rootPresContext) {
 10147       return static_cast<PresShell*>(rootPresContext->PresShell());
 10150   return nullptr;
 10153 void
 10154 PresShell::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
 10155                                   nsArenaMemoryStats *aArenaObjectsSize,
 10156                                   size_t *aPresShellSize,
 10157                                   size_t *aStyleSetsSize,
 10158                                   size_t *aTextRunsSize,
 10159                                   size_t *aPresContextSize)
 10161   mFrameArena.AddSizeOfExcludingThis(aMallocSizeOf, aArenaObjectsSize);
 10162   *aPresShellSize += aMallocSizeOf(this);
 10163   *aPresShellSize += aArenaObjectsSize->mOther;
 10165   *aStyleSetsSize += StyleSet()->SizeOfIncludingThis(aMallocSizeOf);
 10167   *aTextRunsSize += SizeOfTextRuns(aMallocSizeOf);
 10169   *aPresContextSize += mPresContext->SizeOfIncludingThis(aMallocSizeOf);
 10172 size_t
 10173 PresShell::SizeOfTextRuns(MallocSizeOf aMallocSizeOf) const
 10175   nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
 10176   if (!rootFrame) {
 10177     return 0;
 10180   // clear the TEXT_RUN_MEMORY_ACCOUNTED flags
 10181   nsLayoutUtils::SizeOfTextRunsForFrames(rootFrame, nullptr,
 10182                                          /* clear = */true);
 10184   // collect the total memory in use for textruns
 10185   return nsLayoutUtils::SizeOfTextRunsForFrames(rootFrame, aMallocSizeOf,
 10186                                                 /* clear = */false);
 10189 void
 10190 nsIPresShell::MarkFixedFramesForReflow(IntrinsicDirty aIntrinsicDirty)
 10192   nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
 10193   if (rootFrame) {
 10194     const nsFrameList& childList = rootFrame->GetChildList(nsIFrame::kFixedList);
 10195     for (nsFrameList::Enumerator e(childList); !e.AtEnd(); e.Next()) {
 10196       FrameNeedsReflow(e.get(), aIntrinsicDirty, NS_FRAME_IS_DIRTY);
 10201 void
 10202 nsIPresShell::SetScrollPositionClampingScrollPortSize(nscoord aWidth, nscoord aHeight)
 10204   if (!mScrollPositionClampingScrollPortSizeSet ||
 10205       mScrollPositionClampingScrollPortSize.width != aWidth ||
 10206       mScrollPositionClampingScrollPortSize.height != aHeight) {
 10207     mScrollPositionClampingScrollPortSizeSet = true;
 10208     mScrollPositionClampingScrollPortSize.width = aWidth;
 10209     mScrollPositionClampingScrollPortSize.height = aHeight;
 10211     MarkFixedFramesForReflow(eResize);
 10215 void
 10216 nsIPresShell::SetContentDocumentFixedPositionMargins(const nsMargin& aMargins)
 10218   if (mContentDocumentFixedPositionMargins == aMargins) {
 10219     return;
 10222   mContentDocumentFixedPositionMargins = aMargins;
 10224   MarkFixedFramesForReflow(eResize);
 10227 void
 10228 PresShell::SetupFontInflation()
 10230   mFontSizeInflationEmPerLine = nsLayoutUtils::FontSizeInflationEmPerLine();
 10231   mFontSizeInflationMinTwips = nsLayoutUtils::FontSizeInflationMinTwips();
 10232   mFontSizeInflationLineThreshold = nsLayoutUtils::FontSizeInflationLineThreshold();
 10233   mFontSizeInflationForceEnabled = nsLayoutUtils::FontSizeInflationForceEnabled();
 10234   mFontSizeInflationDisabledInMasterProcess = nsLayoutUtils::FontSizeInflationDisabledInMasterProcess();
 10236   NotifyFontSizeInflationEnabledIsDirty();
 10239 void
 10240 nsIPresShell::RecomputeFontSizeInflationEnabled()
 10242   mFontSizeInflationEnabledIsDirty = false;
 10244   MOZ_ASSERT(mPresContext, "our pres context should not be null");
 10245   if ((FontSizeInflationEmPerLine() == 0 &&
 10246       FontSizeInflationMinTwips() == 0) || mPresContext->IsChrome()) {
 10247     mFontSizeInflationEnabled = false;
 10248     return;
 10251   // Force-enabling font inflation always trumps the heuristics here.
 10252   if (!FontSizeInflationForceEnabled()) {
 10253     if (TabChild* tab = TabChild::GetFrom(this)) {
 10254       // We're in a child process.  Cancel inflation if we're not
 10255       // async-pan zoomed.
 10256       if (!tab->IsAsyncPanZoomEnabled()) {
 10257         mFontSizeInflationEnabled = false;
 10258         return;
 10260     } else if (XRE_GetProcessType() == GeckoProcessType_Default) {
 10261       // We're in the master process.  Cancel inflation if it's been
 10262       // explicitly disabled.
 10263       if (FontSizeInflationDisabledInMasterProcess()) {
 10264         mFontSizeInflationEnabled = false;
 10265         return;
 10270   // XXXjwir3:
 10271   // See bug 706918, comment 23 for more information on this particular section
 10272   // of the code. We're using "screen size" in place of the size of the content
 10273   // area, because on mobile, these are close or equal. This will work for our
 10274   // purposes (bug 706198), but it will need to be changed in the future to be
 10275   // more correct when we bring the rest of the viewport code into platform.
 10276   // We actually want the size of the content area, in the event that we don't
 10277   // have any metadata about the width and/or height. On mobile, the screen size
 10278   // and the size of the content area are very close, or the same value.
 10279   // In XUL fennec, the content area is the size of the <browser> widget, but
 10280   // in native fennec, the content area is the size of the Gecko LayerView
 10281   // object.
 10283   // TODO:
 10284   // Once bug 716575 has been resolved, this code should be changed so that it
 10285   // does the right thing on all platforms.
 10286   nsresult rv;
 10287   nsCOMPtr<nsIScreenManager> screenMgr =
 10288     do_GetService("@mozilla.org/gfx/screenmanager;1", &rv);
 10289   if (!NS_SUCCEEDED(rv)) {
 10290     mFontSizeInflationEnabled = false;
 10291     return;
 10294   nsCOMPtr<nsIScreen> screen;
 10295   screenMgr->GetPrimaryScreen(getter_AddRefs(screen));
 10296   if (screen) {
 10297     int32_t screenLeft, screenTop, screenWidth, screenHeight;
 10298     screen->GetRect(&screenLeft, &screenTop, &screenWidth, &screenHeight);
 10300     nsViewportInfo vInf =
 10301       nsContentUtils::GetViewportInfo(GetDocument(), ScreenIntSize(screenWidth, screenHeight));
 10303     if (vInf.GetDefaultZoom() >= CSSToScreenScale(1.0f) || vInf.IsAutoSizeEnabled()) {
 10304       mFontSizeInflationEnabled = false;
 10305       return;
 10309   mFontSizeInflationEnabled = true;
 10312 bool
 10313 nsIPresShell::FontSizeInflationEnabled()
 10315   if (mFontSizeInflationEnabledIsDirty) {
 10316     RecomputeFontSizeInflationEnabled();
 10319   return mFontSizeInflationEnabled;
 10322 void
 10323 nsIPresShell::SetMaxLineBoxWidth(nscoord aMaxLineBoxWidth)
 10325   NS_ASSERTION(aMaxLineBoxWidth >= 0, "attempting to set max line box width to a negative value");
 10327   if (mMaxLineBoxWidth != aMaxLineBoxWidth) {
 10328     mMaxLineBoxWidth = aMaxLineBoxWidth;
 10329     mReflowOnZoomPending = true;
 10330     FrameNeedsReflow(GetRootFrame(), eResize, NS_FRAME_HAS_DIRTY_CHILDREN);
 10334 void
 10335 PresShell::PausePainting()
 10337   if (GetPresContext()->RefreshDriver()->PresContext() != GetPresContext())
 10338     return;
 10340   mPaintingIsFrozen = true;
 10341   GetPresContext()->RefreshDriver()->Freeze();
 10344 void
 10345 PresShell::ResumePainting()
 10347   if (GetPresContext()->RefreshDriver()->PresContext() != GetPresContext())
 10348     return;
 10350   mPaintingIsFrozen = false;
 10351   GetPresContext()->RefreshDriver()->Thaw();

mercurial