michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * vim: set ts=2 sw=2 et tw=78: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. michael@0: * michael@0: * This Original Code has been modified by IBM Corporation. michael@0: * Modifications made by IBM described herein are michael@0: * Copyright (c) International Business Machines michael@0: * Corporation, 2000 michael@0: * michael@0: * Modifications to Mozilla code or documentation michael@0: * identified per MPL Section 3.3 michael@0: * michael@0: * Date Modified by Description of modification michael@0: * 05/03/2000 IBM Corp. Observer events for reflow states michael@0: */ michael@0: michael@0: /* a presentation of a document, part 2 */ michael@0: michael@0: #ifdef MOZ_LOGGING michael@0: #define FORCE_PR_LOG /* Allow logging in the release build */ michael@0: #endif michael@0: #include "prlog.h" michael@0: michael@0: #include "mozilla/ArrayUtils.h" michael@0: #include "mozilla/EventDispatcher.h" michael@0: #include "mozilla/EventStateManager.h" michael@0: #include "mozilla/EventStates.h" michael@0: #include "mozilla/IMEStateManager.h" michael@0: #include "mozilla/MemoryReporting.h" michael@0: #include "mozilla/dom/TabChild.h" michael@0: #include "mozilla/Likely.h" michael@0: #include "mozilla/MouseEvents.h" michael@0: #include "mozilla/TextEvents.h" michael@0: #include "mozilla/TouchEvents.h" michael@0: #include michael@0: michael@0: #ifdef XP_WIN michael@0: #include "winuser.h" michael@0: #endif michael@0: michael@0: #include "nsPresShell.h" michael@0: #include "nsPresContext.h" michael@0: #include "nsIContent.h" michael@0: #include "mozilla/dom/Element.h" michael@0: #include "mozilla/dom/Event.h" // for Event::GetEventPopupControlState() michael@0: #include "mozilla/dom/ShadowRoot.h" michael@0: #include "mozilla/dom/PointerEvent.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsCSSStyleSheet.h" michael@0: #include "nsAnimationManager.h" michael@0: #include "nsNameSpaceManager.h" // for Pref-related rule management (bugs 22963,20760,31816) michael@0: #include "nsFrame.h" michael@0: #include "FrameLayerBuilder.h" michael@0: #include "nsViewManager.h" michael@0: #include "nsView.h" michael@0: #include "nsCRTGlue.h" michael@0: #include "prprf.h" michael@0: #include "prinrval.h" michael@0: #include "nsTArray.h" michael@0: #include "nsCOMArray.h" michael@0: #include "nsContainerFrame.h" michael@0: #include "nsISelection.h" michael@0: #include "mozilla/dom/Selection.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "nsIDOMRange.h" michael@0: #include "nsIDOMDocument.h" michael@0: #include "nsIDOMNode.h" michael@0: #include "nsIDOMNodeList.h" michael@0: #include "nsIDOMElement.h" michael@0: #include "nsRange.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsAutoPtr.h" michael@0: #include "nsReadableUtils.h" michael@0: #include "nsIPageSequenceFrame.h" michael@0: #include "nsCaret.h" michael@0: #include "nsIDOMHTMLDocument.h" michael@0: #include "nsFrameManager.h" michael@0: #include "nsXPCOM.h" michael@0: #include "nsILayoutHistoryState.h" michael@0: #include "nsILineIterator.h" // for ScrollContentIntoView michael@0: #include "pldhash.h" michael@0: #include "mozilla/dom/Touch.h" michael@0: #include "mozilla/dom/PointerEventBinding.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsDocShell.h" // for reflow observation michael@0: #include "nsIBaseWindow.h" michael@0: #include "nsError.h" michael@0: #include "nsLayoutUtils.h" michael@0: #include "nsViewportInfo.h" michael@0: #include "nsCSSRendering.h" michael@0: // for |#ifdef DEBUG| code michael@0: #include "prenv.h" michael@0: #include "nsDisplayList.h" michael@0: #include "nsRegion.h" michael@0: #include "nsRenderingContext.h" michael@0: #include "nsAutoLayoutPhase.h" michael@0: #ifdef MOZ_REFLOW_PERF michael@0: #include "nsFontMetrics.h" michael@0: #endif michael@0: #include "PositionedEventTargeting.h" michael@0: michael@0: #include "nsIReflowCallback.h" michael@0: michael@0: #include "nsPIDOMWindow.h" michael@0: #include "nsFocusManager.h" michael@0: #include "nsIObjectFrame.h" michael@0: #include "nsIObjectLoadingContent.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsStyleSheetService.h" michael@0: #include "gfxImageSurface.h" michael@0: #include "gfxContext.h" michael@0: #include "nsSMILAnimationController.h" michael@0: #include "SVGContentUtils.h" michael@0: #include "nsSVGEffects.h" michael@0: #include "SVGFragmentIdentifier.h" michael@0: #include "nsArenaMemoryStats.h" michael@0: michael@0: #include "nsPerformance.h" michael@0: #include "nsRefreshDriver.h" michael@0: #include "nsDOMNavigationTiming.h" michael@0: michael@0: // Drag & Drop, Clipboard michael@0: #include "nsIDocShellTreeItem.h" michael@0: #include "nsIURI.h" michael@0: #include "nsIScrollableFrame.h" michael@0: #include "nsITimer.h" michael@0: #ifdef ACCESSIBILITY michael@0: #include "nsAccessibilityService.h" michael@0: #include "mozilla/a11y/DocAccessible.h" michael@0: #ifdef DEBUG michael@0: #include "mozilla/a11y/Logging.h" michael@0: #endif michael@0: #endif michael@0: michael@0: // For style data reconstruction michael@0: #include "nsStyleChangeList.h" michael@0: #include "nsCSSFrameConstructor.h" michael@0: #ifdef MOZ_XUL michael@0: #include "nsMenuFrame.h" michael@0: #include "nsTreeBodyFrame.h" michael@0: #include "nsIBoxObject.h" michael@0: #include "nsITreeBoxObject.h" michael@0: #include "nsMenuPopupFrame.h" michael@0: #include "nsITreeColumns.h" michael@0: #include "nsIDOMXULMultSelectCntrlEl.h" michael@0: #include "nsIDOMXULSelectCntrlItemEl.h" michael@0: #include "nsIDOMXULMenuListElement.h" michael@0: michael@0: #endif michael@0: michael@0: #include "GeckoProfiler.h" michael@0: #include "gfxPlatform.h" michael@0: #include "Layers.h" michael@0: #include "LayerTreeInvalidation.h" michael@0: #include "mozilla/css/ImageLoader.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "mozilla/Telemetry.h" michael@0: #include "nsCanvasFrame.h" michael@0: #include "nsIImageLoadingContent.h" michael@0: #include "nsIScreen.h" michael@0: #include "nsIScreenManager.h" michael@0: #include "nsPlaceholderFrame.h" michael@0: #include "nsTransitionManager.h" michael@0: #include "ChildIterator.h" michael@0: #include "RestyleManager.h" michael@0: #include "nsIDOMHTMLElement.h" michael@0: #include "nsIDragSession.h" michael@0: #include "nsIFrameInlines.h" michael@0: #include "mozilla/gfx/2D.h" michael@0: michael@0: #ifdef ANDROID michael@0: #include "nsIDocShellTreeOwner.h" michael@0: #endif michael@0: michael@0: #ifdef MOZ_TASK_TRACER michael@0: #include "GeckoTaskTracer.h" michael@0: using namespace mozilla::tasktracer; michael@0: #endif michael@0: michael@0: #define ANCHOR_SCROLL_FLAGS \ michael@0: (nsIPresShell::SCROLL_OVERFLOW_HIDDEN | nsIPresShell::SCROLL_NO_PARENT_FRAMES) michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::css; michael@0: using namespace mozilla::dom; michael@0: using namespace mozilla::gfx; michael@0: using namespace mozilla::layers; michael@0: using namespace mozilla::gfx; michael@0: michael@0: CapturingContentInfo nsIPresShell::gCaptureInfo = michael@0: { false /* mAllowed */, false /* mPointerLock */, false /* mRetargetToElement */, michael@0: false /* mPreventDrag */, nullptr /* mContent */ }; michael@0: nsIContent* nsIPresShell::gKeyDownTarget; michael@0: nsRefPtrHashtable* nsIPresShell::gCaptureTouchList; michael@0: nsRefPtrHashtable* nsIPresShell::gPointerCaptureList; michael@0: nsClassHashtable* nsIPresShell::gActivePointersIds; michael@0: bool nsIPresShell::gPreventMouseEvents = false; michael@0: michael@0: // convert a color value to a string, in the CSS format #RRGGBB michael@0: // * - initially created for bugs 31816, 20760, 22963 michael@0: static void ColorToString(nscolor aColor, nsAutoString &aString); michael@0: michael@0: // RangePaintInfo is used to paint ranges to offscreen buffers michael@0: struct RangePaintInfo { michael@0: nsRefPtr mRange; michael@0: nsDisplayListBuilder mBuilder; michael@0: nsDisplayList mList; michael@0: michael@0: // offset of builder's reference frame to the root frame michael@0: nsPoint mRootOffset; michael@0: michael@0: RangePaintInfo(nsRange* aRange, nsIFrame* aFrame) michael@0: : mRange(aRange), mBuilder(aFrame, nsDisplayListBuilder::PAINTING, false) michael@0: { michael@0: MOZ_COUNT_CTOR(RangePaintInfo); michael@0: } michael@0: michael@0: ~RangePaintInfo() michael@0: { michael@0: mList.DeleteAll(); michael@0: MOZ_COUNT_DTOR(RangePaintInfo); michael@0: } michael@0: }; michael@0: michael@0: #undef NOISY michael@0: michael@0: // ---------------------------------------------------------------------- michael@0: michael@0: #ifdef DEBUG michael@0: // Set the environment variable GECKO_VERIFY_REFLOW_FLAGS to one or michael@0: // more of the following flags (comma separated) for handy debug michael@0: // output. michael@0: static uint32_t gVerifyReflowFlags; michael@0: michael@0: struct VerifyReflowFlags { michael@0: const char* name; michael@0: uint32_t bit; michael@0: }; michael@0: michael@0: static const VerifyReflowFlags gFlags[] = { michael@0: { "verify", VERIFY_REFLOW_ON }, michael@0: { "reflow", VERIFY_REFLOW_NOISY }, michael@0: { "all", VERIFY_REFLOW_ALL }, michael@0: { "list-commands", VERIFY_REFLOW_DUMP_COMMANDS }, michael@0: { "noisy-commands", VERIFY_REFLOW_NOISY_RC }, michael@0: { "really-noisy-commands", VERIFY_REFLOW_REALLY_NOISY_RC }, michael@0: { "resize", VERIFY_REFLOW_DURING_RESIZE_REFLOW }, michael@0: }; michael@0: michael@0: #define NUM_VERIFY_REFLOW_FLAGS (sizeof(gFlags) / sizeof(gFlags[0])) michael@0: michael@0: static void michael@0: ShowVerifyReflowFlags() michael@0: { michael@0: printf("Here are the available GECKO_VERIFY_REFLOW_FLAGS:\n"); michael@0: const VerifyReflowFlags* flag = gFlags; michael@0: const VerifyReflowFlags* limit = gFlags + NUM_VERIFY_REFLOW_FLAGS; michael@0: while (flag < limit) { michael@0: printf(" %s\n", flag->name); michael@0: ++flag; michael@0: } michael@0: printf("Note: GECKO_VERIFY_REFLOW_FLAGS is a comma separated list of flag\n"); michael@0: printf("names (no whitespace)\n"); michael@0: } michael@0: #endif michael@0: michael@0: //======================================================================== michael@0: //======================================================================== michael@0: //======================================================================== michael@0: #ifdef MOZ_REFLOW_PERF michael@0: class ReflowCountMgr; michael@0: michael@0: static const char kGrandTotalsStr[] = "Grand Totals"; michael@0: michael@0: // Counting Class michael@0: class ReflowCounter { michael@0: public: michael@0: ReflowCounter(ReflowCountMgr * aMgr = nullptr); michael@0: ~ReflowCounter(); michael@0: michael@0: void ClearTotals(); michael@0: void DisplayTotals(const char * aStr); michael@0: void DisplayDiffTotals(const char * aStr); michael@0: void DisplayHTMLTotals(const char * aStr); michael@0: michael@0: void Add() { mTotal++; } michael@0: void Add(uint32_t aTotal) { mTotal += aTotal; } michael@0: michael@0: void CalcDiffInTotals(); michael@0: void SetTotalsCache(); michael@0: michael@0: void SetMgr(ReflowCountMgr * aMgr) { mMgr = aMgr; } michael@0: michael@0: uint32_t GetTotal() { return mTotal; } michael@0: michael@0: protected: michael@0: void DisplayTotals(uint32_t aTotal, const char * aTitle); michael@0: void DisplayHTMLTotals(uint32_t aTotal, const char * aTitle); michael@0: michael@0: uint32_t mTotal; michael@0: uint32_t mCacheTotal; michael@0: michael@0: ReflowCountMgr * mMgr; // weak reference (don't delete) michael@0: }; michael@0: michael@0: // Counting Class michael@0: class IndiReflowCounter { michael@0: public: michael@0: IndiReflowCounter(ReflowCountMgr * aMgr = nullptr) michael@0: : mFrame(nullptr), michael@0: mCount(0), michael@0: mMgr(aMgr), michael@0: mCounter(aMgr), michael@0: mHasBeenOutput(false) michael@0: {} michael@0: virtual ~IndiReflowCounter() {} michael@0: michael@0: nsAutoString mName; michael@0: nsIFrame * mFrame; // weak reference (don't delete) michael@0: int32_t mCount; michael@0: michael@0: ReflowCountMgr * mMgr; // weak reference (don't delete) michael@0: michael@0: ReflowCounter mCounter; michael@0: bool mHasBeenOutput; michael@0: michael@0: }; michael@0: michael@0: //-------------------- michael@0: // Manager Class michael@0: //-------------------- michael@0: class ReflowCountMgr { michael@0: public: michael@0: ReflowCountMgr(); michael@0: virtual ~ReflowCountMgr(); michael@0: michael@0: void ClearTotals(); michael@0: void ClearGrandTotals(); michael@0: void DisplayTotals(const char * aStr); michael@0: void DisplayHTMLTotals(const char * aStr); michael@0: void DisplayDiffsInTotals(const char * aStr); michael@0: michael@0: void Add(const char * aName, nsIFrame * aFrame); michael@0: ReflowCounter * LookUp(const char * aName); michael@0: michael@0: void PaintCount(const char *aName, nsRenderingContext* aRenderingContext, michael@0: nsPresContext *aPresContext, nsIFrame *aFrame, michael@0: const nsPoint &aOffset, uint32_t aColor); michael@0: michael@0: FILE * GetOutFile() { return mFD; } michael@0: michael@0: PLHashTable * GetIndiFrameHT() { return mIndiFrameCounts; } michael@0: michael@0: void SetPresContext(nsPresContext * aPresContext) { mPresContext = aPresContext; } // weak reference michael@0: void SetPresShell(nsIPresShell* aPresShell) { mPresShell= aPresShell; } // weak reference michael@0: michael@0: void SetDumpFrameCounts(bool aVal) { mDumpFrameCounts = aVal; } michael@0: void SetDumpFrameByFrameCounts(bool aVal) { mDumpFrameByFrameCounts = aVal; } michael@0: void SetPaintFrameCounts(bool aVal) { mPaintFrameByFrameCounts = aVal; } michael@0: michael@0: bool IsPaintingFrameCounts() { return mPaintFrameByFrameCounts; } michael@0: michael@0: protected: michael@0: void DisplayTotals(uint32_t aTotal, uint32_t * aDupArray, char * aTitle); michael@0: void DisplayHTMLTotals(uint32_t aTotal, uint32_t * aDupArray, char * aTitle); michael@0: michael@0: static int RemoveItems(PLHashEntry *he, int i, void *arg); michael@0: static int RemoveIndiItems(PLHashEntry *he, int i, void *arg); michael@0: void CleanUp(); michael@0: michael@0: // stdout Output Methods michael@0: static int DoSingleTotal(PLHashEntry *he, int i, void *arg); michael@0: static int DoSingleIndi(PLHashEntry *he, int i, void *arg); michael@0: michael@0: void DoGrandTotals(); michael@0: void DoIndiTotalsTree(); michael@0: michael@0: // HTML Output Methods michael@0: static int DoSingleHTMLTotal(PLHashEntry *he, int i, void *arg); michael@0: void DoGrandHTMLTotals(); michael@0: michael@0: // Zero Out the Totals michael@0: static int DoClearTotals(PLHashEntry *he, int i, void *arg); michael@0: michael@0: // Displays the Diff Totals michael@0: static int DoDisplayDiffTotals(PLHashEntry *he, int i, void *arg); michael@0: michael@0: PLHashTable * mCounts; michael@0: PLHashTable * mIndiFrameCounts; michael@0: FILE * mFD; michael@0: michael@0: bool mDumpFrameCounts; michael@0: bool mDumpFrameByFrameCounts; michael@0: bool mPaintFrameByFrameCounts; michael@0: michael@0: bool mCycledOnce; michael@0: michael@0: // Root Frame for Individual Tracking michael@0: nsPresContext * mPresContext; michael@0: nsIPresShell* mPresShell; michael@0: michael@0: // ReflowCountMgr gReflowCountMgr; michael@0: }; michael@0: #endif michael@0: //======================================================================== michael@0: michael@0: // comment out to hide caret michael@0: #define SHOW_CARET michael@0: michael@0: // The upper bound on the amount of time to spend reflowing, in michael@0: // microseconds. When this bound is exceeded and reflow commands are michael@0: // still queued up, a reflow event is posted. The idea is for reflow michael@0: // to not hog the processor beyond the time specifed in michael@0: // gMaxRCProcessingTime. This data member is initialized from the michael@0: // layout.reflow.timeslice pref. michael@0: #define NS_MAX_REFLOW_TIME 1000000 michael@0: static int32_t gMaxRCProcessingTime = -1; michael@0: michael@0: struct nsCallbackEventRequest michael@0: { michael@0: nsIReflowCallback* callback; michael@0: nsCallbackEventRequest* next; michael@0: }; michael@0: michael@0: // ---------------------------------------------------------------------------- michael@0: #define ASSERT_REFLOW_SCHEDULED_STATE() \ michael@0: NS_ASSERTION(mReflowScheduled == \ michael@0: GetPresContext()->RefreshDriver()-> \ michael@0: IsLayoutFlushObserver(this), "Unexpected state") michael@0: michael@0: class nsAutoCauseReflowNotifier michael@0: { michael@0: public: michael@0: nsAutoCauseReflowNotifier(PresShell* aShell) michael@0: : mShell(aShell) michael@0: { michael@0: mShell->WillCauseReflow(); michael@0: } michael@0: ~nsAutoCauseReflowNotifier() michael@0: { michael@0: // This check should not be needed. Currently the only place that seem michael@0: // to need it is the code that deals with bug 337586. michael@0: if (!mShell->mHaveShutDown) { michael@0: mShell->DidCauseReflow(); michael@0: } michael@0: else { michael@0: nsContentUtils::RemoveScriptBlocker(); michael@0: } michael@0: } michael@0: michael@0: PresShell* mShell; michael@0: }; michael@0: michael@0: class MOZ_STACK_CLASS nsPresShellEventCB : public EventDispatchingCallback michael@0: { michael@0: public: michael@0: nsPresShellEventCB(PresShell* aPresShell) : mPresShell(aPresShell) {} michael@0: michael@0: virtual void HandleEvent(EventChainPostVisitor& aVisitor) MOZ_OVERRIDE michael@0: { michael@0: if (aVisitor.mPresContext && aVisitor.mEvent->eventStructType != NS_EVENT) { michael@0: if (aVisitor.mEvent->message == NS_MOUSE_BUTTON_DOWN || michael@0: aVisitor.mEvent->message == NS_MOUSE_BUTTON_UP) { michael@0: // Mouse-up and mouse-down events call nsFrame::HandlePress/Release michael@0: // which call GetContentOffsetsFromPoint which requires up-to-date layout. michael@0: // Bring layout up-to-date now so that GetCurrentEventFrame() below michael@0: // will return a real frame and we don't have to worry about michael@0: // destroying it by flushing later. michael@0: mPresShell->FlushPendingNotifications(Flush_Layout); michael@0: } else if (aVisitor.mEvent->message == NS_WHEEL_WHEEL && michael@0: aVisitor.mEventStatus != nsEventStatus_eConsumeNoDefault) { michael@0: nsIFrame* frame = mPresShell->GetCurrentEventFrame(); michael@0: if (frame) { michael@0: // chrome (including addons) should be able to know if content michael@0: // handles both D3E "wheel" event and legacy mouse scroll events. michael@0: // We should dispatch legacy mouse events before dispatching the michael@0: // "wheel" event into system group. michael@0: nsRefPtr esm = michael@0: aVisitor.mPresContext->EventStateManager(); michael@0: esm->DispatchLegacyMouseScrollEvents(frame, michael@0: aVisitor.mEvent->AsWheelEvent(), michael@0: &aVisitor.mEventStatus); michael@0: } michael@0: } michael@0: nsIFrame* frame = mPresShell->GetCurrentEventFrame(); michael@0: if (!frame && michael@0: (aVisitor.mEvent->message == NS_MOUSE_BUTTON_UP || michael@0: aVisitor.mEvent->message == NS_TOUCH_END)) { michael@0: // Redirect BUTTON_UP and TOUCH_END events to the root frame to ensure michael@0: // that capturing is released. michael@0: frame = mPresShell->GetRootFrame(); michael@0: } michael@0: if (frame) { michael@0: frame->HandleEvent(aVisitor.mPresContext, michael@0: aVisitor.mEvent->AsGUIEvent(), michael@0: &aVisitor.mEventStatus); michael@0: } michael@0: } michael@0: } michael@0: michael@0: nsRefPtr mPresShell; michael@0: }; michael@0: michael@0: class nsBeforeFirstPaintDispatcher : public nsRunnable michael@0: { michael@0: public: michael@0: nsBeforeFirstPaintDispatcher(nsIDocument* aDocument) michael@0: : mDocument(aDocument) {} michael@0: michael@0: // Fires the "before-first-paint" event so that interested parties (right now, the michael@0: // mobile browser) are aware of it. michael@0: NS_IMETHOD Run() MOZ_OVERRIDE michael@0: { michael@0: nsCOMPtr observerService = michael@0: mozilla::services::GetObserverService(); michael@0: if (observerService) { michael@0: observerService->NotifyObservers(mDocument, "before-first-paint", michael@0: nullptr); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: nsCOMPtr mDocument; michael@0: }; michael@0: michael@0: bool PresShell::sDisableNonTestMouseEvents = false; michael@0: michael@0: #ifdef PR_LOGGING michael@0: PRLogModuleInfo* PresShell::gLog; michael@0: #endif michael@0: michael@0: #ifdef DEBUG michael@0: static void michael@0: VerifyStyleTree(nsPresContext* aPresContext, nsFrameManager* aFrameManager) michael@0: { michael@0: if (nsFrame::GetVerifyStyleTreeEnable()) { michael@0: nsIFrame* rootFrame = aFrameManager->GetRootFrame(); michael@0: aPresContext->RestyleManager()->DebugVerifyStyleTree(rootFrame); michael@0: } michael@0: } michael@0: #define VERIFY_STYLE_TREE ::VerifyStyleTree(mPresContext, mFrameConstructor) michael@0: #else michael@0: #define VERIFY_STYLE_TREE michael@0: #endif michael@0: michael@0: static bool gVerifyReflowEnabled; michael@0: michael@0: bool michael@0: nsIPresShell::GetVerifyReflowEnable() michael@0: { michael@0: #ifdef DEBUG michael@0: static bool firstTime = true; michael@0: if (firstTime) { michael@0: firstTime = false; michael@0: char* flags = PR_GetEnv("GECKO_VERIFY_REFLOW_FLAGS"); michael@0: if (flags) { michael@0: bool error = false; michael@0: michael@0: for (;;) { michael@0: char* comma = PL_strchr(flags, ','); michael@0: if (comma) michael@0: *comma = '\0'; michael@0: michael@0: bool found = false; michael@0: const VerifyReflowFlags* flag = gFlags; michael@0: const VerifyReflowFlags* limit = gFlags + NUM_VERIFY_REFLOW_FLAGS; michael@0: while (flag < limit) { michael@0: if (PL_strcasecmp(flag->name, flags) == 0) { michael@0: gVerifyReflowFlags |= flag->bit; michael@0: found = true; michael@0: break; michael@0: } michael@0: ++flag; michael@0: } michael@0: michael@0: if (! found) michael@0: error = true; michael@0: michael@0: if (! comma) michael@0: break; michael@0: michael@0: *comma = ','; michael@0: flags = comma + 1; michael@0: } michael@0: michael@0: if (error) michael@0: ShowVerifyReflowFlags(); michael@0: } michael@0: michael@0: if (VERIFY_REFLOW_ON & gVerifyReflowFlags) { michael@0: gVerifyReflowEnabled = true; michael@0: michael@0: printf("Note: verifyreflow is enabled"); michael@0: if (VERIFY_REFLOW_NOISY & gVerifyReflowFlags) { michael@0: printf(" (noisy)"); michael@0: } michael@0: if (VERIFY_REFLOW_ALL & gVerifyReflowFlags) { michael@0: printf(" (all)"); michael@0: } michael@0: if (VERIFY_REFLOW_DUMP_COMMANDS & gVerifyReflowFlags) { michael@0: printf(" (show reflow commands)"); michael@0: } michael@0: if (VERIFY_REFLOW_NOISY_RC & gVerifyReflowFlags) { michael@0: printf(" (noisy reflow commands)"); michael@0: if (VERIFY_REFLOW_REALLY_NOISY_RC & gVerifyReflowFlags) { michael@0: printf(" (REALLY noisy reflow commands)"); michael@0: } michael@0: } michael@0: printf("\n"); michael@0: } michael@0: } michael@0: #endif michael@0: return gVerifyReflowEnabled; michael@0: } michael@0: michael@0: void michael@0: PresShell::AddInvalidateHiddenPresShellObserver(nsRefreshDriver *aDriver) michael@0: { michael@0: if (!mHiddenInvalidationObserverRefreshDriver && !mIsDestroying && !mHaveShutDown) { michael@0: aDriver->AddPresShellToInvalidateIfHidden(this); michael@0: mHiddenInvalidationObserverRefreshDriver = aDriver; michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsIPresShell::InvalidatePresShellIfHidden() michael@0: { michael@0: if (!IsVisible() && mPresContext) { michael@0: mPresContext->NotifyInvalidation(0); michael@0: } michael@0: mHiddenInvalidationObserverRefreshDriver = nullptr; michael@0: } michael@0: michael@0: void michael@0: nsIPresShell::CancelInvalidatePresShellIfHidden() michael@0: { michael@0: if (mHiddenInvalidationObserverRefreshDriver) { michael@0: mHiddenInvalidationObserverRefreshDriver->RemovePresShellToInvalidateIfHidden(this); michael@0: mHiddenInvalidationObserverRefreshDriver = nullptr; michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsIPresShell::SetVerifyReflowEnable(bool aEnabled) michael@0: { michael@0: gVerifyReflowEnabled = aEnabled; michael@0: } michael@0: michael@0: /* virtual */ void michael@0: nsIPresShell::AddWeakFrameExternal(nsWeakFrame* aWeakFrame) michael@0: { michael@0: AddWeakFrameInternal(aWeakFrame); michael@0: } michael@0: michael@0: void michael@0: nsIPresShell::AddWeakFrameInternal(nsWeakFrame* aWeakFrame) michael@0: { michael@0: if (aWeakFrame->GetFrame()) { michael@0: aWeakFrame->GetFrame()->AddStateBits(NS_FRAME_EXTERNAL_REFERENCE); michael@0: } michael@0: aWeakFrame->SetPreviousWeakFrame(mWeakFrames); michael@0: mWeakFrames = aWeakFrame; michael@0: } michael@0: michael@0: /* virtual */ void michael@0: nsIPresShell::RemoveWeakFrameExternal(nsWeakFrame* aWeakFrame) michael@0: { michael@0: RemoveWeakFrameInternal(aWeakFrame); michael@0: } michael@0: michael@0: void michael@0: nsIPresShell::RemoveWeakFrameInternal(nsWeakFrame* aWeakFrame) michael@0: { michael@0: if (mWeakFrames == aWeakFrame) { michael@0: mWeakFrames = aWeakFrame->GetPreviousWeakFrame(); michael@0: return; michael@0: } michael@0: nsWeakFrame* nextWeak = mWeakFrames; michael@0: while (nextWeak && nextWeak->GetPreviousWeakFrame() != aWeakFrame) { michael@0: nextWeak = nextWeak->GetPreviousWeakFrame(); michael@0: } michael@0: if (nextWeak) { michael@0: nextWeak->SetPreviousWeakFrame(aWeakFrame->GetPreviousWeakFrame()); michael@0: } michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsIPresShell::FrameSelection() michael@0: { michael@0: nsRefPtr ret = mSelection; michael@0: return ret.forget(); michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: michael@0: static bool sSynthMouseMove = true; michael@0: static uint32_t sNextPresShellId; michael@0: static bool sPointerEventEnabled = true; michael@0: michael@0: PresShell::PresShell() michael@0: : mMouseLocation(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE) michael@0: { michael@0: mSelection = nullptr; michael@0: #ifdef MOZ_REFLOW_PERF michael@0: mReflowCountMgr = new ReflowCountMgr(); michael@0: mReflowCountMgr->SetPresContext(mPresContext); michael@0: mReflowCountMgr->SetPresShell(this); michael@0: #endif michael@0: #ifdef PR_LOGGING michael@0: mLoadBegin = TimeStamp::Now(); michael@0: if (!gLog) { michael@0: gLog = PR_NewLogModule("PresShell"); michael@0: } michael@0: #endif michael@0: mSelectionFlags = nsISelectionDisplay::DISPLAY_TEXT | nsISelectionDisplay::DISPLAY_IMAGES; michael@0: mIsThemeSupportDisabled = false; michael@0: mIsActive = true; michael@0: // FIXME/bug 735029: find a better solution to this problem michael@0: #ifdef MOZ_ANDROID_OMTC michael@0: // The java pan/zoom code uses this to mean approximately "request a michael@0: // reset of pan/zoom state" which doesn't necessarily correspond michael@0: // with the first paint of content. michael@0: mIsFirstPaint = false; michael@0: #else michael@0: mIsFirstPaint = true; michael@0: #endif michael@0: mPresShellId = sNextPresShellId++; michael@0: mFrozen = false; michael@0: #ifdef DEBUG michael@0: mPresArenaAllocCount = 0; michael@0: #endif michael@0: mRenderFlags = 0; michael@0: mXResolution = 1.0; michael@0: mYResolution = 1.0; michael@0: mViewportOverridden = false; michael@0: michael@0: mScrollPositionClampingScrollPortSizeSet = false; michael@0: michael@0: mMaxLineBoxWidth = 0; michael@0: michael@0: static bool addedSynthMouseMove = false; michael@0: if (!addedSynthMouseMove) { michael@0: Preferences::AddBoolVarCache(&sSynthMouseMove, michael@0: "layout.reflow.synthMouseMove", true); michael@0: addedSynthMouseMove = true; michael@0: } michael@0: static bool addedPointerEventEnabled = false; michael@0: if (!addedPointerEventEnabled) { michael@0: Preferences::AddBoolVarCache(&sPointerEventEnabled, michael@0: "dom.w3c_pointer_events.enabled", true); michael@0: addedPointerEventEnabled = true; michael@0: } michael@0: michael@0: mPaintingIsFrozen = false; michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(PresShell, nsIPresShell, nsIDocumentObserver, michael@0: nsISelectionController, michael@0: nsISelectionDisplay, nsIObserver, nsISupportsWeakReference, michael@0: nsIMutationObserver) michael@0: michael@0: PresShell::~PresShell() michael@0: { michael@0: if (!mHaveShutDown) { michael@0: NS_NOTREACHED("Someone did not call nsIPresShell::destroy"); michael@0: Destroy(); michael@0: } michael@0: michael@0: NS_ASSERTION(mCurrentEventContentStack.Count() == 0, michael@0: "Huh, event content left on the stack in pres shell dtor!"); michael@0: NS_ASSERTION(mFirstCallbackEventRequest == nullptr && michael@0: mLastCallbackEventRequest == nullptr, michael@0: "post-reflow queues not empty. This means we're leaking"); michael@0: michael@0: // Verify that if painting was frozen, but we're being removed from the tree, michael@0: // that we now re-enable painting on our refresh driver, since it may need to michael@0: // be re-used by another presentation. michael@0: if (mPaintingIsFrozen) { michael@0: mPresContext->RefreshDriver()->Thaw(); michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: MOZ_ASSERT(mPresArenaAllocCount == 0, michael@0: "Some pres arena objects were not freed"); michael@0: #endif michael@0: michael@0: delete mStyleSet; michael@0: delete mFrameConstructor; michael@0: michael@0: mCurrentEventContent = nullptr; michael@0: michael@0: NS_IF_RELEASE(mPresContext); michael@0: NS_IF_RELEASE(mDocument); michael@0: NS_IF_RELEASE(mSelection); michael@0: } michael@0: michael@0: /** michael@0: * Initialize the presentation shell. Create view manager and style michael@0: * manager. michael@0: * Note this can't be merged into our constructor because caret initialization michael@0: * calls AddRef() on us. michael@0: */ michael@0: void michael@0: PresShell::Init(nsIDocument* aDocument, michael@0: nsPresContext* aPresContext, michael@0: nsViewManager* aViewManager, michael@0: nsStyleSet* aStyleSet, michael@0: nsCompatibility aCompatMode) michael@0: { michael@0: NS_PRECONDITION(aDocument, "null ptr"); michael@0: NS_PRECONDITION(aPresContext, "null ptr"); michael@0: NS_PRECONDITION(aViewManager, "null ptr"); michael@0: NS_PRECONDITION(!mDocument, "already initialized"); michael@0: michael@0: if (!aDocument || !aPresContext || !aViewManager || mDocument) { michael@0: return; michael@0: } michael@0: michael@0: mDocument = aDocument; michael@0: NS_ADDREF(mDocument); michael@0: mViewManager = aViewManager; michael@0: michael@0: // Create our frame constructor. michael@0: mFrameConstructor = new nsCSSFrameConstructor(mDocument, this, aStyleSet); michael@0: michael@0: mFrameManager = mFrameConstructor; michael@0: michael@0: // The document viewer owns both view manager and pres shell. michael@0: mViewManager->SetPresShell(this); michael@0: michael@0: // Bind the context to the presentation shell. michael@0: mPresContext = aPresContext; michael@0: NS_ADDREF(mPresContext); michael@0: aPresContext->SetShell(this); michael@0: michael@0: // Now we can initialize the style set. michael@0: aStyleSet->Init(aPresContext); michael@0: mStyleSet = aStyleSet; michael@0: michael@0: // Notify our prescontext that it now has a compatibility mode. Note that michael@0: // this MUST happen after we set up our style set but before we create any michael@0: // frames. michael@0: mPresContext->CompatibilityModeChanged(); michael@0: michael@0: // setup the preference style rules (no forced reflow), and do it michael@0: // before creating any frames. michael@0: SetPreferenceStyleRules(false); michael@0: michael@0: NS_ADDREF(mSelection = new nsFrameSelection()); michael@0: michael@0: mSelection->Init(this, nullptr); michael@0: michael@0: // Important: this has to happen after the selection has been set up michael@0: #ifdef SHOW_CARET michael@0: // make the caret michael@0: mCaret = new nsCaret(); michael@0: mCaret->Init(this); michael@0: mOriginalCaret = mCaret; michael@0: michael@0: //SetCaretEnabled(true); // make it show in browser windows michael@0: #endif michael@0: //set up selection to be displayed in document michael@0: // Don't enable selection for print media michael@0: nsPresContext::nsPresContextType type = aPresContext->Type(); michael@0: if (type != nsPresContext::eContext_PrintPreview && michael@0: type != nsPresContext::eContext_Print) michael@0: SetDisplaySelection(nsISelectionController::SELECTION_DISABLED); michael@0: michael@0: if (gMaxRCProcessingTime == -1) { michael@0: gMaxRCProcessingTime = michael@0: Preferences::GetInt("layout.reflow.timeslice", NS_MAX_REFLOW_TIME); michael@0: } michael@0: michael@0: { michael@0: nsCOMPtr os = mozilla::services::GetObserverService(); michael@0: if (os) { michael@0: os->AddObserver(this, "agent-sheet-added", false); michael@0: os->AddObserver(this, "user-sheet-added", false); michael@0: os->AddObserver(this, "author-sheet-added", false); michael@0: os->AddObserver(this, "agent-sheet-removed", false); michael@0: os->AddObserver(this, "user-sheet-removed", false); michael@0: os->AddObserver(this, "author-sheet-removed", false); michael@0: #ifdef MOZ_XUL michael@0: os->AddObserver(this, "chrome-flush-skin-caches", false); michael@0: #endif michael@0: } michael@0: } michael@0: michael@0: #ifdef MOZ_REFLOW_PERF michael@0: if (mReflowCountMgr) { michael@0: bool paintFrameCounts = michael@0: Preferences::GetBool("layout.reflow.showframecounts"); michael@0: michael@0: bool dumpFrameCounts = michael@0: Preferences::GetBool("layout.reflow.dumpframecounts"); michael@0: michael@0: bool dumpFrameByFrameCounts = michael@0: Preferences::GetBool("layout.reflow.dumpframebyframecounts"); michael@0: michael@0: mReflowCountMgr->SetDumpFrameCounts(dumpFrameCounts); michael@0: mReflowCountMgr->SetDumpFrameByFrameCounts(dumpFrameByFrameCounts); michael@0: mReflowCountMgr->SetPaintFrameCounts(paintFrameCounts); michael@0: } michael@0: #endif michael@0: michael@0: if (mDocument->HasAnimationController()) { michael@0: nsSMILAnimationController* animCtrl = mDocument->GetAnimationController(); michael@0: animCtrl->NotifyRefreshDriverCreated(GetPresContext()->RefreshDriver()); michael@0: } michael@0: michael@0: // Get our activeness from the docShell. michael@0: QueryIsActive(); michael@0: michael@0: // Setup our font inflation preferences. michael@0: SetupFontInflation(); michael@0: } michael@0: michael@0: #ifdef PR_LOGGING michael@0: enum TextPerfLogType { michael@0: eLog_reflow, michael@0: eLog_loaddone, michael@0: eLog_totals michael@0: }; michael@0: michael@0: static void michael@0: LogTextPerfStats(gfxTextPerfMetrics* aTextPerf, michael@0: PresShell* aPresShell, michael@0: const gfxTextPerfMetrics::TextCounts& aCounts, michael@0: float aTime, TextPerfLogType aLogType, const char* aURL) michael@0: { michael@0: char prefix[256]; michael@0: michael@0: switch (aLogType) { michael@0: case eLog_reflow: michael@0: sprintf(prefix, "(textperf-reflow) %p time-ms: %7.0f", aPresShell, aTime); michael@0: break; michael@0: case eLog_loaddone: michael@0: sprintf(prefix, "(textperf-loaddone) %p time-ms: %7.0f", aPresShell, aTime); michael@0: break; michael@0: default: michael@0: MOZ_ASSERT(aLogType == eLog_totals, "unknown textperf log type"); michael@0: sprintf(prefix, "(textperf-totals) %p", aPresShell); michael@0: } michael@0: michael@0: PRLogModuleInfo* tpLog = gfxPlatform::GetLog(eGfxLog_textperf); michael@0: michael@0: // ignore XUL contexts unless at debug level michael@0: PRLogModuleLevel logLevel = PR_LOG_WARNING; michael@0: if (aCounts.numContentTextRuns == 0) { michael@0: logLevel = PR_LOG_DEBUG; michael@0: } michael@0: michael@0: double hitRatio = 0.0; michael@0: uint32_t lookups = aCounts.wordCacheHit + aCounts.wordCacheMiss; michael@0: if (lookups) { michael@0: hitRatio = double(aCounts.wordCacheHit) / double(lookups); michael@0: } michael@0: michael@0: if (aLogType == eLog_loaddone) { michael@0: PR_LOG(tpLog, logLevel, michael@0: ("%s reflow: %d chars: %d " michael@0: "[%s] " michael@0: "content-textruns: %d chrome-textruns: %d " michael@0: "max-textrun-len: %d " michael@0: "word-cache-lookups: %d word-cache-hit-ratio: %4.3f " michael@0: "word-cache-space: %d word-cache-long: %d " michael@0: "pref-fallbacks: %d system-fallbacks: %d " michael@0: "textruns-const: %d textruns-destr: %d " michael@0: "cumulative-textruns-destr: %d\n", michael@0: prefix, aTextPerf->reflowCount, aCounts.numChars, michael@0: (aURL ? aURL : ""), michael@0: aCounts.numContentTextRuns, aCounts.numChromeTextRuns, michael@0: aCounts.maxTextRunLen, michael@0: lookups, hitRatio, michael@0: aCounts.wordCacheSpaceRules, aCounts.wordCacheLong, michael@0: aCounts.fallbackPrefs, aCounts.fallbackSystem, michael@0: aCounts.textrunConst, aCounts.textrunDestr, michael@0: aTextPerf->cumulative.textrunDestr)); michael@0: } else { michael@0: PR_LOG(tpLog, logLevel, michael@0: ("%s reflow: %d chars: %d " michael@0: "content-textruns: %d chrome-textruns: %d " michael@0: "max-textrun-len: %d " michael@0: "word-cache-lookups: %d word-cache-hit-ratio: %4.3f " michael@0: "word-cache-space: %d word-cache-long: %d " michael@0: "pref-fallbacks: %d system-fallbacks: %d " michael@0: "textruns-const: %d textruns-destr: %d " michael@0: "cumulative-textruns-destr: %d\n", michael@0: prefix, aTextPerf->reflowCount, aCounts.numChars, michael@0: aCounts.numContentTextRuns, aCounts.numChromeTextRuns, michael@0: aCounts.maxTextRunLen, michael@0: lookups, hitRatio, michael@0: aCounts.wordCacheSpaceRules, aCounts.wordCacheLong, michael@0: aCounts.fallbackPrefs, aCounts.fallbackSystem, michael@0: aCounts.textrunConst, aCounts.textrunDestr, michael@0: aTextPerf->cumulative.textrunDestr)); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: void michael@0: PresShell::Destroy() michael@0: { michael@0: NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(), michael@0: "destroy called on presshell while scripts not blocked"); michael@0: michael@0: // dump out cumulative text perf metrics michael@0: #ifdef PR_LOGGING michael@0: gfxTextPerfMetrics* tp; michael@0: if (mPresContext && (tp = mPresContext->GetTextPerfMetrics())) { michael@0: tp->Accumulate(); michael@0: if (tp->cumulative.numChars > 0) { michael@0: LogTextPerfStats(tp, this, tp->cumulative, 0.0, eLog_totals, nullptr); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: #ifdef MOZ_REFLOW_PERF michael@0: DumpReflows(); michael@0: if (mReflowCountMgr) { michael@0: delete mReflowCountMgr; michael@0: mReflowCountMgr = nullptr; michael@0: } michael@0: #endif michael@0: michael@0: if (mHaveShutDown) michael@0: return; michael@0: michael@0: #ifdef ACCESSIBILITY michael@0: if (mDocAccessible) { michael@0: #ifdef DEBUG michael@0: if (a11y::logging::IsEnabled(a11y::logging::eDocDestroy)) michael@0: a11y::logging::DocDestroy("presshell destroyed", mDocument); michael@0: #endif michael@0: michael@0: mDocAccessible->Shutdown(); michael@0: mDocAccessible = nullptr; michael@0: } michael@0: #endif // ACCESSIBILITY michael@0: michael@0: MaybeReleaseCapturingContent(); michael@0: michael@0: if (gKeyDownTarget && gKeyDownTarget->OwnerDoc() == mDocument) { michael@0: NS_RELEASE(gKeyDownTarget); michael@0: } michael@0: michael@0: if (mContentToScrollTo) { michael@0: mContentToScrollTo->DeleteProperty(nsGkAtoms::scrolling); michael@0: mContentToScrollTo = nullptr; michael@0: } michael@0: michael@0: if (mPresContext) { michael@0: // We need to notify the destroying the nsPresContext to ESM for michael@0: // suppressing to use from ESM. michael@0: mPresContext->EventStateManager()->NotifyDestroyPresContext(mPresContext); michael@0: } michael@0: michael@0: { michael@0: nsCOMPtr os = mozilla::services::GetObserverService(); michael@0: if (os) { michael@0: os->RemoveObserver(this, "agent-sheet-added"); michael@0: os->RemoveObserver(this, "user-sheet-added"); michael@0: os->RemoveObserver(this, "author-sheet-added"); michael@0: os->RemoveObserver(this, "agent-sheet-removed"); michael@0: os->RemoveObserver(this, "user-sheet-removed"); michael@0: os->RemoveObserver(this, "author-sheet-removed"); michael@0: #ifdef MOZ_XUL michael@0: os->RemoveObserver(this, "chrome-flush-skin-caches"); michael@0: #endif michael@0: } michael@0: } michael@0: michael@0: // If our paint suppression timer is still active, kill it. michael@0: if (mPaintSuppressionTimer) { michael@0: mPaintSuppressionTimer->Cancel(); michael@0: mPaintSuppressionTimer = nullptr; michael@0: } michael@0: michael@0: // Same for our reflow continuation timer michael@0: if (mReflowContinueTimer) { michael@0: mReflowContinueTimer->Cancel(); michael@0: mReflowContinueTimer = nullptr; michael@0: } michael@0: michael@0: if (mDelayedPaintTimer) { michael@0: mDelayedPaintTimer->Cancel(); michael@0: mDelayedPaintTimer = nullptr; michael@0: } michael@0: michael@0: mSynthMouseMoveEvent.Revoke(); michael@0: michael@0: mUpdateImageVisibilityEvent.Revoke(); michael@0: michael@0: ClearVisibleImagesList(); michael@0: michael@0: if (mCaret) { michael@0: mCaret->Terminate(); michael@0: mCaret = nullptr; michael@0: } michael@0: michael@0: if (mSelection) { michael@0: mSelection->DisconnectFromPresShell(); michael@0: } michael@0: michael@0: // release our pref style sheet, if we have one still michael@0: ClearPreferenceStyleRules(); michael@0: michael@0: mIsDestroying = true; michael@0: michael@0: // We can't release all the event content in michael@0: // mCurrentEventContentStack here since there might be code on the michael@0: // stack that will release the event content too. Double release michael@0: // bad! michael@0: michael@0: // The frames will be torn down, so remove them from the current michael@0: // event frame stack (since they'd be dangling references if we'd michael@0: // leave them in) and null out the mCurrentEventFrame pointer as michael@0: // well. michael@0: michael@0: mCurrentEventFrame = nullptr; michael@0: michael@0: int32_t i, count = mCurrentEventFrameStack.Length(); michael@0: for (i = 0; i < count; i++) { michael@0: mCurrentEventFrameStack[i] = nullptr; michael@0: } michael@0: michael@0: mFramesToDirty.Clear(); michael@0: michael@0: if (mViewManager) { michael@0: // Clear the view manager's weak pointer back to |this| in case it michael@0: // was leaked. michael@0: mViewManager->SetPresShell(nullptr); michael@0: mViewManager = nullptr; michael@0: } michael@0: michael@0: mStyleSet->BeginShutdown(mPresContext); michael@0: nsRefreshDriver* rd = GetPresContext()->RefreshDriver(); michael@0: michael@0: // This shell must be removed from the document before the frame michael@0: // hierarchy is torn down to avoid finding deleted frames through michael@0: // this presshell while the frames are being torn down michael@0: if (mDocument) { michael@0: NS_ASSERTION(mDocument->GetShell() == this, "Wrong shell?"); michael@0: mDocument->DeleteShell(); michael@0: michael@0: if (mDocument->HasAnimationController()) { michael@0: mDocument->GetAnimationController()->NotifyRefreshDriverDestroying(rd); michael@0: } michael@0: } michael@0: michael@0: // Revoke any pending events. We need to do this and cancel pending reflows michael@0: // before we destroy the frame manager, since apparently frame destruction michael@0: // sometimes spins the event queue when plug-ins are involved(!). michael@0: rd->RemoveLayoutFlushObserver(this); michael@0: if (mHiddenInvalidationObserverRefreshDriver) { michael@0: mHiddenInvalidationObserverRefreshDriver->RemovePresShellToInvalidateIfHidden(this); michael@0: } michael@0: michael@0: if (rd->PresContext() == GetPresContext()) { michael@0: rd->RevokeViewManagerFlush(); michael@0: } michael@0: michael@0: mResizeEvent.Revoke(); michael@0: if (mAsyncResizeTimerIsActive) { michael@0: mAsyncResizeEventTimer->Cancel(); michael@0: mAsyncResizeTimerIsActive = false; michael@0: } michael@0: michael@0: CancelAllPendingReflows(); michael@0: CancelPostedReflowCallbacks(); michael@0: michael@0: // Destroy the frame manager. This will destroy the frame hierarchy michael@0: mFrameConstructor->WillDestroyFrameTree(); michael@0: michael@0: // Destroy all frame properties (whose destruction was suppressed michael@0: // while destroying the frame tree, but which might contain more michael@0: // frames within the properties. michael@0: if (mPresContext) { michael@0: // Clear out the prescontext's property table -- since our frame tree is michael@0: // now dead, we shouldn't be looking up any more properties in that table. michael@0: // We want to do this before we call SetShell() on the prescontext, so michael@0: // property destructors can usefully call GetPresShell() on the michael@0: // prescontext. michael@0: mPresContext->PropertyTable()->DeleteAll(); michael@0: } michael@0: michael@0: michael@0: NS_WARN_IF_FALSE(!mWeakFrames, "Weak frames alive after destroying FrameManager"); michael@0: while (mWeakFrames) { michael@0: mWeakFrames->Clear(this); michael@0: } michael@0: michael@0: // Let the style set do its cleanup. michael@0: mStyleSet->Shutdown(mPresContext); michael@0: michael@0: if (mPresContext) { michael@0: // We hold a reference to the pres context, and it holds a weak link back michael@0: // to us. To avoid the pres context having a dangling reference, set its michael@0: // pres shell to nullptr michael@0: mPresContext->SetShell(nullptr); michael@0: michael@0: // Clear the link handler (weak reference) as well michael@0: mPresContext->SetLinkHandler(nullptr); michael@0: } michael@0: michael@0: mHaveShutDown = true; michael@0: michael@0: EvictTouches(); michael@0: } michael@0: michael@0: void michael@0: PresShell::MakeZombie() michael@0: { michael@0: mIsZombie = true; michael@0: CancelAllPendingReflows(); michael@0: } michael@0: michael@0: void michael@0: nsIPresShell::SetAuthorStyleDisabled(bool aStyleDisabled) michael@0: { michael@0: if (aStyleDisabled != mStyleSet->GetAuthorStyleDisabled()) { michael@0: mStyleSet->SetAuthorStyleDisabled(aStyleDisabled); michael@0: ReconstructStyleData(); michael@0: michael@0: nsCOMPtr observerService = michael@0: mozilla::services::GetObserverService(); michael@0: if (observerService) { michael@0: observerService->NotifyObservers(mDocument, michael@0: "author-style-disabled-changed", michael@0: nullptr); michael@0: } michael@0: } michael@0: } michael@0: michael@0: bool michael@0: nsIPresShell::GetAuthorStyleDisabled() const michael@0: { michael@0: return mStyleSet->GetAuthorStyleDisabled(); michael@0: } michael@0: michael@0: nsresult michael@0: PresShell::SetPreferenceStyleRules(bool aForceReflow) michael@0: { michael@0: if (!mDocument) { michael@0: return NS_ERROR_NULL_POINTER; michael@0: } michael@0: michael@0: nsPIDOMWindow *window = mDocument->GetWindow(); michael@0: michael@0: // If the document doesn't have a window there's no need to notify michael@0: // its presshell about changes to preferences since the document is michael@0: // in a state where it doesn't matter any more (see michael@0: // nsDocumentViewer::Close()). michael@0: michael@0: if (!window) { michael@0: return NS_ERROR_NULL_POINTER; michael@0: } michael@0: michael@0: NS_PRECONDITION(mPresContext, "presContext cannot be null"); michael@0: if (mPresContext) { michael@0: // first, make sure this is not a chrome shell michael@0: if (nsContentUtils::IsInChromeDocshell(mDocument)) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: #ifdef DEBUG_attinasi michael@0: printf("Setting Preference Style Rules:\n"); michael@0: #endif michael@0: // if here, we need to create rules for the prefs michael@0: // - this includes the background-color, the text-color, michael@0: // the link color, the visited link color and the link-underlining michael@0: michael@0: // first clear any exising rules michael@0: nsresult result = ClearPreferenceStyleRules(); michael@0: michael@0: // now the link rules (must come after the color rules, or links will not be correct color!) michael@0: // XXX - when there is both an override and agent pref stylesheet this won't matter, michael@0: // as the color rules will be overrides and the links rules will be agent michael@0: if (NS_SUCCEEDED(result)) { michael@0: result = SetPrefLinkRules(); michael@0: } michael@0: if (NS_SUCCEEDED(result)) { michael@0: result = SetPrefFocusRules(); michael@0: } michael@0: if (NS_SUCCEEDED(result)) { michael@0: result = SetPrefNoScriptRule(); michael@0: } michael@0: if (NS_SUCCEEDED(result)) { michael@0: result = SetPrefNoFramesRule(); michael@0: } michael@0: #ifdef DEBUG_attinasi michael@0: printf( "Preference Style Rules set: error=%ld\n", (long)result); michael@0: #endif michael@0: michael@0: // Note that this method never needs to force any calculation; the caller michael@0: // will recalculate style if needed michael@0: michael@0: return result; michael@0: } michael@0: michael@0: return NS_ERROR_NULL_POINTER; michael@0: } michael@0: michael@0: nsresult PresShell::ClearPreferenceStyleRules(void) michael@0: { michael@0: nsresult result = NS_OK; michael@0: if (mPrefStyleSheet) { michael@0: NS_ASSERTION(mStyleSet, "null styleset entirely unexpected!"); michael@0: if (mStyleSet) { michael@0: // remove the sheet from the styleset: michael@0: // - note that we have to check for success by comparing the count before and after... michael@0: #ifdef DEBUG michael@0: int32_t numBefore = mStyleSet->SheetCount(nsStyleSet::eUserSheet); michael@0: NS_ASSERTION(numBefore > 0, "no user stylesheets in styleset, but we have one!"); michael@0: #endif michael@0: mStyleSet->RemoveStyleSheet(nsStyleSet::eUserSheet, mPrefStyleSheet); michael@0: michael@0: #ifdef DEBUG_attinasi michael@0: NS_ASSERTION((numBefore - 1) == mStyleSet->GetNumberOfUserStyleSheets(), michael@0: "Pref stylesheet was not removed"); michael@0: printf("PrefStyleSheet removed\n"); michael@0: #endif michael@0: // clear the sheet pointer: it is strictly historical now michael@0: mPrefStyleSheet = nullptr; michael@0: } michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: nsresult michael@0: PresShell::CreatePreferenceStyleSheet() michael@0: { michael@0: NS_ASSERTION(!mPrefStyleSheet, "prefStyleSheet already exists"); michael@0: mPrefStyleSheet = new nsCSSStyleSheet(CORS_NONE); michael@0: nsCOMPtr uri; michael@0: nsresult rv = NS_NewURI(getter_AddRefs(uri), "about:PreferenceStyleSheet", nullptr); michael@0: if (NS_FAILED(rv)) { michael@0: mPrefStyleSheet = nullptr; michael@0: return rv; michael@0: } michael@0: NS_ASSERTION(uri, "null but no error"); michael@0: mPrefStyleSheet->SetURIs(uri, uri, uri); michael@0: mPrefStyleSheet->SetComplete(); michael@0: uint32_t index; michael@0: rv = michael@0: mPrefStyleSheet->InsertRuleInternal(NS_LITERAL_STRING("@namespace svg url(http://www.w3.org/2000/svg);"), michael@0: 0, &index); michael@0: if (NS_FAILED(rv)) { michael@0: mPrefStyleSheet = nullptr; michael@0: return rv; michael@0: } michael@0: rv = michael@0: mPrefStyleSheet->InsertRuleInternal(NS_LITERAL_STRING("@namespace url(http://www.w3.org/1999/xhtml);"), michael@0: 0, &index); michael@0: if (NS_FAILED(rv)) { michael@0: mPrefStyleSheet = nullptr; michael@0: return rv; michael@0: } michael@0: michael@0: mStyleSet->AppendStyleSheet(nsStyleSet::eUserSheet, mPrefStyleSheet); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // XXX We want these after the @namespace rules. Does order matter michael@0: // for these rules, or can we call StyleRule::StyleRuleCount() michael@0: // and just "append"? michael@0: static uint32_t sInsertPrefSheetRulesAt = 2; michael@0: michael@0: nsresult michael@0: PresShell::SetPrefNoScriptRule() michael@0: { michael@0: nsresult rv = NS_OK; michael@0: michael@0: // also handle the case where print is done from print preview michael@0: // see bug #342439 for more details michael@0: nsIDocument* doc = mDocument; michael@0: if (doc->IsStaticDocument()) { michael@0: doc = doc->GetOriginalDocument(); michael@0: } michael@0: michael@0: bool scriptEnabled = doc->IsScriptEnabled(); michael@0: if (scriptEnabled) { michael@0: if (!mPrefStyleSheet) { michael@0: rv = CreatePreferenceStyleSheet(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: uint32_t index = 0; michael@0: mPrefStyleSheet-> michael@0: InsertRuleInternal(NS_LITERAL_STRING("noscript{display:none!important}"), michael@0: sInsertPrefSheetRulesAt, &index); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: nsresult PresShell::SetPrefNoFramesRule(void) michael@0: { michael@0: NS_ASSERTION(mPresContext,"null prescontext not allowed"); michael@0: if (!mPresContext) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsresult rv = NS_OK; michael@0: michael@0: if (!mPrefStyleSheet) { michael@0: rv = CreatePreferenceStyleSheet(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: NS_ASSERTION(mPrefStyleSheet, "prefstylesheet should not be null"); michael@0: michael@0: bool allowSubframes = true; michael@0: nsCOMPtr docShell(mPresContext->GetDocShell()); michael@0: if (docShell) { michael@0: docShell->GetAllowSubframes(&allowSubframes); michael@0: } michael@0: if (!allowSubframes) { michael@0: uint32_t index = 0; michael@0: rv = mPrefStyleSheet-> michael@0: InsertRuleInternal(NS_LITERAL_STRING("noframes{display:block}"), michael@0: sInsertPrefSheetRulesAt, &index); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = mPrefStyleSheet-> michael@0: InsertRuleInternal(NS_LITERAL_STRING("frame, frameset, iframe {display:none!important}"), michael@0: sInsertPrefSheetRulesAt, &index); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: nsresult PresShell::SetPrefLinkRules(void) michael@0: { michael@0: NS_ASSERTION(mPresContext,"null prescontext not allowed"); michael@0: if (!mPresContext) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsresult rv = NS_OK; michael@0: michael@0: if (!mPrefStyleSheet) { michael@0: rv = CreatePreferenceStyleSheet(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: NS_ASSERTION(mPrefStyleSheet, "prefstylesheet should not be null"); michael@0: michael@0: // support default link colors: michael@0: // this means the link colors need to be overridable, michael@0: // which they are if we put them in the agent stylesheet, michael@0: // though if using an override sheet this will cause authors grief still michael@0: // In the agent stylesheet, they are !important when we are ignoring document colors michael@0: michael@0: nscolor linkColor(mPresContext->DefaultLinkColor()); michael@0: nscolor activeColor(mPresContext->DefaultActiveLinkColor()); michael@0: nscolor visitedColor(mPresContext->DefaultVisitedLinkColor()); michael@0: michael@0: NS_NAMED_LITERAL_STRING(ruleClose, "}"); michael@0: uint32_t index = 0; michael@0: nsAutoString strColor; michael@0: michael@0: // insert a rule to color links: '*|*:link {color: #RRGGBB [!important];}' michael@0: ColorToString(linkColor, strColor); michael@0: rv = mPrefStyleSheet-> michael@0: InsertRuleInternal(NS_LITERAL_STRING("*|*:link{color:") + michael@0: strColor + ruleClose, michael@0: sInsertPrefSheetRulesAt, &index); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // - visited links: '*|*:visited {color: #RRGGBB [!important];}' michael@0: ColorToString(visitedColor, strColor); michael@0: rv = mPrefStyleSheet-> michael@0: InsertRuleInternal(NS_LITERAL_STRING("*|*:visited{color:") + michael@0: strColor + ruleClose, michael@0: sInsertPrefSheetRulesAt, &index); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // - active links: '*|*:-moz-any-link:active {color: #RRGGBB [!important];}' michael@0: ColorToString(activeColor, strColor); michael@0: rv = mPrefStyleSheet-> michael@0: InsertRuleInternal(NS_LITERAL_STRING("*|*:-moz-any-link:active{color:") + michael@0: strColor + ruleClose, michael@0: sInsertPrefSheetRulesAt, &index); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: bool underlineLinks = michael@0: mPresContext->GetCachedBoolPref(kPresContext_UnderlineLinks); michael@0: michael@0: if (underlineLinks) { michael@0: // create a rule to make underlining happen michael@0: // '*|*:-moz-any-link {text-decoration:[underline|none];}' michael@0: // no need for important, we want these to be overridable michael@0: // NOTE: these must go in the agent stylesheet or they cannot be michael@0: // overridden by authors michael@0: rv = mPrefStyleSheet-> michael@0: InsertRuleInternal(NS_LITERAL_STRING("*|*:-moz-any-link:not(svg|a){text-decoration:underline}"), michael@0: sInsertPrefSheetRulesAt, &index); michael@0: } else { michael@0: rv = mPrefStyleSheet-> michael@0: InsertRuleInternal(NS_LITERAL_STRING("*|*:-moz-any-link{text-decoration:none}"), michael@0: sInsertPrefSheetRulesAt, &index); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: nsresult PresShell::SetPrefFocusRules(void) michael@0: { michael@0: NS_ASSERTION(mPresContext,"null prescontext not allowed"); michael@0: nsresult result = NS_OK; michael@0: michael@0: if (!mPresContext) michael@0: result = NS_ERROR_FAILURE; michael@0: michael@0: if (NS_SUCCEEDED(result) && !mPrefStyleSheet) michael@0: result = CreatePreferenceStyleSheet(); michael@0: michael@0: if (NS_SUCCEEDED(result)) { michael@0: NS_ASSERTION(mPrefStyleSheet, "prefstylesheet should not be null"); michael@0: michael@0: if (mPresContext->GetUseFocusColors()) { michael@0: nscolor focusBackground(mPresContext->FocusBackgroundColor()); michael@0: nscolor focusText(mPresContext->FocusTextColor()); michael@0: michael@0: // insert a rule to make focus the preferred color michael@0: uint32_t index = 0; michael@0: nsAutoString strRule, strColor; michael@0: michael@0: /////////////////////////////////////////////////////////////// michael@0: // - focus: '*:focus michael@0: ColorToString(focusText,strColor); michael@0: strRule.AppendLiteral("*:focus,*:focus>font {color: "); michael@0: strRule.Append(strColor); michael@0: strRule.AppendLiteral(" !important; background-color: "); michael@0: ColorToString(focusBackground,strColor); michael@0: strRule.Append(strColor); michael@0: strRule.AppendLiteral(" !important; } "); michael@0: // insert the rules michael@0: result = mPrefStyleSheet-> michael@0: InsertRuleInternal(strRule, sInsertPrefSheetRulesAt, &index); michael@0: } michael@0: uint8_t focusRingWidth = mPresContext->FocusRingWidth(); michael@0: bool focusRingOnAnything = mPresContext->GetFocusRingOnAnything(); michael@0: uint8_t focusRingStyle = mPresContext->GetFocusRingStyle(); michael@0: michael@0: if ((NS_SUCCEEDED(result) && focusRingWidth != 1 && focusRingWidth <= 4 ) || focusRingOnAnything) { michael@0: uint32_t index = 0; michael@0: nsAutoString strRule; michael@0: if (!focusRingOnAnything) michael@0: strRule.AppendLiteral("*|*:link:focus, *|*:visited"); // If we only want focus rings on the normal things like links michael@0: strRule.AppendLiteral(":focus {outline: "); // For example 3px dotted WindowText (maximum 4) michael@0: strRule.AppendInt(focusRingWidth); michael@0: if (focusRingStyle == 0) // solid michael@0: strRule.AppendLiteral("px solid -moz-mac-focusring !important; -moz-outline-radius: 3px; outline-offset: 1px; } "); michael@0: else // dotted michael@0: strRule.AppendLiteral("px dotted WindowText !important; } "); michael@0: // insert the rules michael@0: result = mPrefStyleSheet-> michael@0: InsertRuleInternal(strRule, sInsertPrefSheetRulesAt, &index); michael@0: NS_ENSURE_SUCCESS(result, result); michael@0: if (focusRingWidth != 1) { michael@0: // If the focus ring width is different from the default, fix buttons with rings michael@0: strRule.AssignLiteral("button::-moz-focus-inner, input[type=\"reset\"]::-moz-focus-inner,"); michael@0: strRule.AppendLiteral("input[type=\"button\"]::-moz-focus-inner, "); michael@0: strRule.AppendLiteral("input[type=\"submit\"]::-moz-focus-inner { padding: 1px 2px 1px 2px; border: "); michael@0: strRule.AppendInt(focusRingWidth); michael@0: if (focusRingStyle == 0) // solid michael@0: strRule.AppendLiteral("px solid transparent !important; } "); michael@0: else michael@0: strRule.AppendLiteral("px dotted transparent !important; } "); michael@0: result = mPrefStyleSheet-> michael@0: InsertRuleInternal(strRule, sInsertPrefSheetRulesAt, &index); michael@0: NS_ENSURE_SUCCESS(result, result); michael@0: michael@0: strRule.AssignLiteral("button:focus::-moz-focus-inner, input[type=\"reset\"]:focus::-moz-focus-inner,"); michael@0: strRule.AppendLiteral("input[type=\"button\"]:focus::-moz-focus-inner, input[type=\"submit\"]:focus::-moz-focus-inner {"); michael@0: strRule.AppendLiteral("border-color: ButtonText !important; }"); michael@0: result = mPrefStyleSheet-> michael@0: InsertRuleInternal(strRule, sInsertPrefSheetRulesAt, &index); michael@0: } michael@0: } michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: void michael@0: PresShell::AddUserSheet(nsISupports* aSheet) michael@0: { michael@0: // Make sure this does what nsDocumentViewer::CreateStyleSet does wrt michael@0: // ordering. We want this new sheet to come after all the existing stylesheet michael@0: // service sheets, but before other user sheets; see nsIStyleSheetService.idl michael@0: // for the ordering. Just remove and readd all the nsStyleSheetService michael@0: // sheets. michael@0: nsCOMPtr dummy = michael@0: do_GetService(NS_STYLESHEETSERVICE_CONTRACTID); michael@0: michael@0: mStyleSet->BeginUpdate(); michael@0: michael@0: nsStyleSheetService *sheetService = nsStyleSheetService::gInstance; michael@0: nsCOMArray & userSheets = *sheetService->UserStyleSheets(); michael@0: int32_t i; michael@0: // Iterate forwards when removing so the searches for RemoveStyleSheet are as michael@0: // short as possible. michael@0: for (i = 0; i < userSheets.Count(); ++i) { michael@0: mStyleSet->RemoveStyleSheet(nsStyleSet::eUserSheet, userSheets[i]); michael@0: } michael@0: michael@0: // Now iterate backwards, so that the order of userSheets will be the same as michael@0: // the order of sheets from it in the style set. michael@0: for (i = userSheets.Count() - 1; i >= 0; --i) { michael@0: mStyleSet->PrependStyleSheet(nsStyleSet::eUserSheet, userSheets[i]); michael@0: } michael@0: michael@0: mStyleSet->EndUpdate(); michael@0: michael@0: ReconstructStyleData(); michael@0: } michael@0: michael@0: void michael@0: PresShell::AddAgentSheet(nsISupports* aSheet) michael@0: { michael@0: // Make sure this does what nsDocumentViewer::CreateStyleSet does michael@0: // wrt ordering. michael@0: nsCOMPtr sheet = do_QueryInterface(aSheet); michael@0: if (!sheet) { michael@0: return; michael@0: } michael@0: michael@0: mStyleSet->AppendStyleSheet(nsStyleSet::eAgentSheet, sheet); michael@0: ReconstructStyleData(); michael@0: } michael@0: michael@0: void michael@0: PresShell::AddAuthorSheet(nsISupports* aSheet) michael@0: { michael@0: nsCOMPtr sheet = do_QueryInterface(aSheet); michael@0: if (!sheet) { michael@0: return; michael@0: } michael@0: michael@0: // Document specific "additional" Author sheets should be stronger than the ones michael@0: // added with the StyleSheetService. michael@0: nsIStyleSheet* firstAuthorSheet = mDocument->FirstAdditionalAuthorSheet(); michael@0: if (firstAuthorSheet) { michael@0: mStyleSet->InsertStyleSheetBefore(nsStyleSet::eDocSheet, sheet, firstAuthorSheet); michael@0: } else { michael@0: mStyleSet->AppendStyleSheet(nsStyleSet::eDocSheet, sheet); michael@0: } michael@0: michael@0: ReconstructStyleData(); michael@0: } michael@0: michael@0: void michael@0: PresShell::RemoveSheet(nsStyleSet::sheetType aType, nsISupports* aSheet) michael@0: { michael@0: nsCOMPtr sheet = do_QueryInterface(aSheet); michael@0: if (!sheet) { michael@0: return; michael@0: } michael@0: michael@0: mStyleSet->RemoveStyleSheet(aType, sheet); michael@0: ReconstructStyleData(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: PresShell::SetDisplaySelection(int16_t aToggle) michael@0: { michael@0: mSelection->SetDisplaySelection(aToggle); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: PresShell::GetDisplaySelection(int16_t *aToggle) michael@0: { michael@0: *aToggle = mSelection->GetDisplaySelection(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: PresShell::GetSelection(SelectionType aType, nsISelection **aSelection) michael@0: { michael@0: if (!aSelection || !mSelection) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: *aSelection = mSelection->GetSelection(aType); michael@0: michael@0: if (!(*aSelection)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: NS_ADDREF(*aSelection); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: Selection* michael@0: PresShell::GetCurrentSelection(SelectionType aType) michael@0: { michael@0: if (!mSelection) michael@0: return nullptr; michael@0: michael@0: return mSelection->GetSelection(aType); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: PresShell::ScrollSelectionIntoView(SelectionType aType, SelectionRegion aRegion, michael@0: int16_t aFlags) michael@0: { michael@0: if (!mSelection) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: return mSelection->ScrollSelectionIntoView(aType, aRegion, aFlags); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: PresShell::RepaintSelection(SelectionType aType) michael@0: { michael@0: if (!mSelection) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: return mSelection->RepaintSelection(aType); michael@0: } michael@0: michael@0: // Make shell be a document observer michael@0: void michael@0: PresShell::BeginObservingDocument() michael@0: { michael@0: if (mDocument && !mIsDestroying) { michael@0: mDocument->AddObserver(this); michael@0: if (mIsDocumentGone) { michael@0: NS_WARNING("Adding a presshell that was disconnected from the document " michael@0: "as a document observer? Sounds wrong..."); michael@0: mIsDocumentGone = false; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Make shell stop being a document observer michael@0: void michael@0: PresShell::EndObservingDocument() michael@0: { michael@0: // XXXbz do we need to tell the frame constructor that the document michael@0: // is gone, perhaps? Except for printing it's NOT gone, sometimes. michael@0: mIsDocumentGone = true; michael@0: if (mDocument) { michael@0: mDocument->RemoveObserver(this); michael@0: } michael@0: } michael@0: michael@0: #ifdef DEBUG_kipp michael@0: char* nsPresShell_ReflowStackPointerTop; michael@0: #endif michael@0: michael@0: nsresult michael@0: PresShell::Initialize(nscoord aWidth, nscoord aHeight) michael@0: { michael@0: if (mIsDestroying) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (!mDocument) { michael@0: // Nothing to do michael@0: return NS_OK; michael@0: } michael@0: michael@0: mozilla::TimeStamp timerStart = mozilla::TimeStamp::Now(); michael@0: michael@0: NS_ASSERTION(!mDidInitialize, "Why are we being called?"); michael@0: michael@0: nsCOMPtr kungFuDeathGrip(this); michael@0: mDidInitialize = true; michael@0: michael@0: #ifdef DEBUG michael@0: if (VERIFY_REFLOW_NOISY_RC & gVerifyReflowFlags) { michael@0: if (mDocument) { michael@0: nsIURI *uri = mDocument->GetDocumentURI(); michael@0: if (uri) { michael@0: nsAutoCString url; michael@0: uri->GetSpec(url); michael@0: printf("*** PresShell::Initialize (this=%p, url='%s')\n", (void*)this, url.get()); michael@0: } michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: if (mCaret) michael@0: mCaret->EraseCaret(); michael@0: michael@0: // XXX Do a full invalidate at the beginning so that invalidates along michael@0: // the way don't have region accumulation issues? michael@0: michael@0: mPresContext->SetVisibleArea(nsRect(0, 0, aWidth, aHeight)); michael@0: michael@0: // Get the root frame from the frame manager michael@0: // XXXbz it would be nice to move this somewhere else... like frame manager michael@0: // Init(), say. But we need to make sure our views are all set up by the michael@0: // time we do this! michael@0: nsIFrame* rootFrame = mFrameConstructor->GetRootFrame(); michael@0: NS_ASSERTION(!rootFrame, "How did that happen, exactly?"); michael@0: if (!rootFrame) { michael@0: nsAutoScriptBlocker scriptBlocker; michael@0: mFrameConstructor->BeginUpdate(); michael@0: rootFrame = mFrameConstructor->ConstructRootFrame(); michael@0: mFrameConstructor->SetRootFrame(rootFrame); michael@0: mFrameConstructor->EndUpdate(); michael@0: } michael@0: michael@0: NS_ENSURE_STATE(!mHaveShutDown); michael@0: michael@0: if (!rootFrame) { michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: for (nsIFrame* f = rootFrame; f; f = nsLayoutUtils::GetCrossDocParentFrame(f)) { michael@0: if (f->GetStateBits() & NS_FRAME_NO_COMPONENT_ALPHA) { michael@0: f->InvalidateFrameSubtree(); michael@0: f->RemoveStateBits(NS_FRAME_NO_COMPONENT_ALPHA); michael@0: } michael@0: } michael@0: michael@0: Element *root = mDocument->GetRootElement(); michael@0: michael@0: if (root) { michael@0: { michael@0: nsAutoCauseReflowNotifier reflowNotifier(this); michael@0: mFrameConstructor->BeginUpdate(); michael@0: michael@0: // Have the style sheet processor construct frame for the root michael@0: // content object down michael@0: mFrameConstructor->ContentInserted(nullptr, root, nullptr, false); michael@0: VERIFY_STYLE_TREE; michael@0: michael@0: // Something in mFrameConstructor->ContentInserted may have caused michael@0: // Destroy() to get called, bug 337586. michael@0: NS_ENSURE_STATE(!mHaveShutDown); michael@0: michael@0: mFrameConstructor->EndUpdate(); michael@0: } michael@0: michael@0: // nsAutoScriptBlocker going out of scope may have killed us too michael@0: NS_ENSURE_STATE(!mHaveShutDown); michael@0: michael@0: // Run the XBL binding constructors for any new frames we've constructed michael@0: mDocument->BindingManager()->ProcessAttachedQueue(); michael@0: michael@0: // Constructors may have killed us too michael@0: NS_ENSURE_STATE(!mHaveShutDown); michael@0: michael@0: // Now flush out pending restyles before we actually reflow, in michael@0: // case XBL constructors changed styles somewhere. michael@0: { michael@0: nsAutoScriptBlocker scriptBlocker; michael@0: mPresContext->RestyleManager()->ProcessPendingRestyles(); michael@0: } michael@0: michael@0: // And that might have run _more_ XBL constructors michael@0: NS_ENSURE_STATE(!mHaveShutDown); michael@0: } michael@0: michael@0: NS_ASSERTION(rootFrame, "How did that happen?"); michael@0: michael@0: // Note: when the frame was created above it had the NS_FRAME_IS_DIRTY bit michael@0: // set, but XBL processing could have caused a reflow which clears it. michael@0: if (MOZ_LIKELY(rootFrame->GetStateBits() & NS_FRAME_IS_DIRTY)) { michael@0: // Unset the DIRTY bits so that FrameNeedsReflow() will work right. michael@0: rootFrame->RemoveStateBits(NS_FRAME_IS_DIRTY | michael@0: NS_FRAME_HAS_DIRTY_CHILDREN); michael@0: NS_ASSERTION(!mDirtyRoots.Contains(rootFrame), michael@0: "Why is the root in mDirtyRoots already?"); michael@0: FrameNeedsReflow(rootFrame, eResize, NS_FRAME_IS_DIRTY); michael@0: NS_ASSERTION(mDirtyRoots.Contains(rootFrame), michael@0: "Should be in mDirtyRoots now"); michael@0: NS_ASSERTION(mReflowScheduled, "Why no reflow scheduled?"); michael@0: } michael@0: michael@0: // Restore our root scroll position now if we're getting here after EndLoad michael@0: // got called, since this is our one chance to do it. Note that we need not michael@0: // have reflowed for this to work; when the scrollframe is finally reflowed michael@0: // it'll pick up the position we store in it here. michael@0: if (!mDocumentLoading) { michael@0: RestoreRootScrollPosition(); michael@0: } michael@0: michael@0: // For printing, we just immediately unsuppress. michael@0: if (!mPresContext->IsPaginated()) { michael@0: // Kick off a one-shot timer based off our pref value. When this timer michael@0: // fires, if painting is still locked down, then we will go ahead and michael@0: // trigger a full invalidate and allow painting to proceed normally. michael@0: mPaintingSuppressed = true; michael@0: // Don't suppress painting if the document isn't loading. michael@0: nsIDocument::ReadyState readyState = mDocument->GetReadyStateEnum(); michael@0: if (readyState != nsIDocument::READYSTATE_COMPLETE) { michael@0: mPaintSuppressionTimer = do_CreateInstance("@mozilla.org/timer;1"); michael@0: } michael@0: if (!mPaintSuppressionTimer) { michael@0: mPaintingSuppressed = false; michael@0: } else { michael@0: // Initialize the timer. michael@0: michael@0: // Default to PAINTLOCK_EVENT_DELAY if we can't get the pref value. michael@0: int32_t delay = michael@0: Preferences::GetInt("nglayout.initialpaint.delay", michael@0: PAINTLOCK_EVENT_DELAY); michael@0: michael@0: mPaintSuppressionTimer->InitWithFuncCallback(sPaintSuppressionCallback, michael@0: this, delay, michael@0: nsITimer::TYPE_ONE_SHOT); michael@0: } michael@0: } michael@0: michael@0: if (root && root->IsXUL()) { michael@0: mozilla::Telemetry::AccumulateTimeDelta(Telemetry::XUL_INITIAL_FRAME_CONSTRUCTION, michael@0: timerStart); michael@0: } michael@0: michael@0: return NS_OK; //XXX this needs to be real. MMP michael@0: } michael@0: michael@0: void michael@0: PresShell::sPaintSuppressionCallback(nsITimer *aTimer, void* aPresShell) michael@0: { michael@0: nsRefPtr self = static_cast(aPresShell); michael@0: if (self) michael@0: self->UnsuppressPainting(); michael@0: } michael@0: michael@0: void michael@0: PresShell::AsyncResizeEventCallback(nsITimer* aTimer, void* aPresShell) michael@0: { michael@0: static_cast(aPresShell)->FireResizeEvent(); michael@0: } michael@0: michael@0: nsresult michael@0: PresShell::ResizeReflowOverride(nscoord aWidth, nscoord aHeight) michael@0: { michael@0: mViewportOverridden = true; michael@0: return ResizeReflowIgnoreOverride(aWidth, aHeight); michael@0: } michael@0: michael@0: nsresult michael@0: PresShell::ResizeReflow(nscoord aWidth, nscoord aHeight) michael@0: { michael@0: if (mViewportOverridden) { michael@0: // The viewport has been overridden, and this reflow request michael@0: // didn't ask to ignore the override. Pretend it didn't happen. michael@0: return NS_OK; michael@0: } michael@0: return ResizeReflowIgnoreOverride(aWidth, aHeight); michael@0: } michael@0: michael@0: nsresult michael@0: PresShell::ResizeReflowIgnoreOverride(nscoord aWidth, nscoord aHeight) michael@0: { michael@0: NS_PRECONDITION(!mIsReflowing, "Shouldn't be in reflow here!"); michael@0: NS_PRECONDITION(aWidth != NS_UNCONSTRAINEDSIZE, michael@0: "shouldn't use unconstrained widths anymore"); michael@0: michael@0: // If we don't have a root frame yet, that means we haven't had our initial michael@0: // reflow... If that's the case, and aWidth or aHeight is unconstrained, michael@0: // ignore them altogether. michael@0: nsIFrame* rootFrame = mFrameConstructor->GetRootFrame(); michael@0: if (!rootFrame && aHeight == NS_UNCONSTRAINEDSIZE) { michael@0: // We can't do the work needed for SizeToContent without a root michael@0: // frame, and we want to return before setting the visible area. michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: mPresContext->SetVisibleArea(nsRect(0, 0, aWidth, aHeight)); michael@0: michael@0: // There isn't anything useful we can do if the initial reflow hasn't happened. michael@0: if (!rootFrame) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsRefPtr viewManagerDeathGrip = mViewManager; michael@0: // Take this ref after viewManager so it'll make sure to go away first. michael@0: nsCOMPtr kungFuDeathGrip(this); michael@0: michael@0: if (!GetPresContext()->SupressingResizeReflow()) { michael@0: // Have to make sure that the content notifications are flushed before we michael@0: // start messing with the frame model; otherwise we can get content doubling. michael@0: mDocument->FlushPendingNotifications(Flush_ContentAndNotify); michael@0: michael@0: // Make sure style is up to date michael@0: { michael@0: nsAutoScriptBlocker scriptBlocker; michael@0: mPresContext->RestyleManager()->ProcessPendingRestyles(); michael@0: } michael@0: michael@0: rootFrame = mFrameConstructor->GetRootFrame(); michael@0: if (!mIsDestroying && rootFrame) { michael@0: // XXX Do a full invalidate at the beginning so that invalidates along michael@0: // the way don't have region accumulation issues? michael@0: michael@0: { michael@0: nsAutoCauseReflowNotifier crNotifier(this); michael@0: WillDoReflow(); michael@0: michael@0: // Kick off a top-down reflow michael@0: AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Reflow); michael@0: nsViewManager::AutoDisableRefresh refreshBlocker(mViewManager); michael@0: michael@0: mDirtyRoots.RemoveElement(rootFrame); michael@0: DoReflow(rootFrame, true); michael@0: } michael@0: michael@0: DidDoReflow(true, false); michael@0: } michael@0: } michael@0: michael@0: rootFrame = mFrameConstructor->GetRootFrame(); michael@0: if (aHeight == NS_UNCONSTRAINEDSIZE && rootFrame) { michael@0: mPresContext->SetVisibleArea( michael@0: nsRect(0, 0, aWidth, rootFrame->GetRect().height)); michael@0: } michael@0: michael@0: if (!mIsDestroying && !mResizeEvent.IsPending() && michael@0: !mAsyncResizeTimerIsActive) { michael@0: if (mInResize) { michael@0: if (!mAsyncResizeEventTimer) { michael@0: mAsyncResizeEventTimer = do_CreateInstance("@mozilla.org/timer;1"); michael@0: } michael@0: if (mAsyncResizeEventTimer) { michael@0: mAsyncResizeTimerIsActive = true; michael@0: mAsyncResizeEventTimer->InitWithFuncCallback(AsyncResizeEventCallback, michael@0: this, 15, michael@0: nsITimer::TYPE_ONE_SHOT); michael@0: } michael@0: } else { michael@0: nsRefPtr > resizeEvent = michael@0: NS_NewRunnableMethod(this, &PresShell::FireResizeEvent); michael@0: if (NS_SUCCEEDED(NS_DispatchToCurrentThread(resizeEvent))) { michael@0: mResizeEvent = resizeEvent; michael@0: mDocument->SetNeedStyleFlush(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: return NS_OK; //XXX this needs to be real. MMP michael@0: } michael@0: michael@0: void michael@0: PresShell::FireResizeEvent() michael@0: { michael@0: if (mAsyncResizeTimerIsActive) { michael@0: mAsyncResizeTimerIsActive = false; michael@0: mAsyncResizeEventTimer->Cancel(); michael@0: } michael@0: mResizeEvent.Revoke(); michael@0: michael@0: if (mIsDocumentGone) michael@0: return; michael@0: michael@0: //Send resize event from here. michael@0: WidgetEvent event(true, NS_RESIZE_EVENT); michael@0: nsEventStatus status = nsEventStatus_eIgnore; michael@0: michael@0: nsPIDOMWindow *window = mDocument->GetWindow(); michael@0: if (window) { michael@0: nsCOMPtr kungFuDeathGrip(this); michael@0: mInResize = true; michael@0: EventDispatcher::Dispatch(window, mPresContext, &event, nullptr, &status); michael@0: mInResize = false; michael@0: } michael@0: } michael@0: michael@0: void michael@0: PresShell::SetIgnoreFrameDestruction(bool aIgnore) michael@0: { michael@0: if (mDocument) { michael@0: // We need to tell the ImageLoader to drop all its references to frames michael@0: // because they're about to go away and it won't get notifications of that. michael@0: mDocument->StyleImageLoader()->ClearFrames(); michael@0: } michael@0: mIgnoreFrameDestruction = aIgnore; michael@0: } michael@0: michael@0: void michael@0: PresShell::NotifyDestroyingFrame(nsIFrame* aFrame) michael@0: { michael@0: if (!mIgnoreFrameDestruction) { michael@0: mDocument->StyleImageLoader()->DropRequestsForFrame(aFrame); michael@0: michael@0: mFrameConstructor->NotifyDestroyingFrame(aFrame); michael@0: michael@0: for (int32_t idx = mDirtyRoots.Length(); idx; ) { michael@0: --idx; michael@0: if (mDirtyRoots[idx] == aFrame) { michael@0: mDirtyRoots.RemoveElementAt(idx); michael@0: } michael@0: } michael@0: michael@0: // Remove frame properties michael@0: mPresContext->NotifyDestroyingFrame(aFrame); michael@0: michael@0: if (aFrame == mCurrentEventFrame) { michael@0: mCurrentEventContent = aFrame->GetContent(); michael@0: mCurrentEventFrame = nullptr; michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: if (aFrame == mDrawEventTargetFrame) { michael@0: mDrawEventTargetFrame = nullptr; michael@0: } michael@0: #endif michael@0: michael@0: for (unsigned int i=0; i < mCurrentEventFrameStack.Length(); i++) { michael@0: if (aFrame == mCurrentEventFrameStack.ElementAt(i)) { michael@0: //One of our stack frames was deleted. Get its content so that when we michael@0: //pop it we can still get its new frame from its content michael@0: nsIContent *currentEventContent = aFrame->GetContent(); michael@0: mCurrentEventContentStack.ReplaceObjectAt(currentEventContent, i); michael@0: mCurrentEventFrameStack[i] = nullptr; michael@0: } michael@0: } michael@0: michael@0: mFramesToDirty.RemoveEntry(aFrame); michael@0: } else { michael@0: // We must delete this property in situ so that its destructor removes the michael@0: // frame from FrameLayerBuilder::DisplayItemData::mFrameList -- otherwise michael@0: // the DisplayItemData destructor will use the destroyed frame when it michael@0: // tries to remove it from the (array) value of this property. michael@0: mPresContext->PropertyTable()-> michael@0: Delete(aFrame, FrameLayerBuilder::LayerManagerDataProperty()); michael@0: } michael@0: } michael@0: michael@0: already_AddRefed PresShell::GetCaret() const michael@0: { michael@0: nsRefPtr caret = mCaret; michael@0: return caret.forget(); michael@0: } michael@0: michael@0: void PresShell::MaybeInvalidateCaretPosition() michael@0: { michael@0: if (mCaret) { michael@0: mCaret->InvalidateOutsideCaret(); michael@0: } michael@0: } michael@0: michael@0: void PresShell::SetCaret(nsCaret *aNewCaret) michael@0: { michael@0: mCaret = aNewCaret; michael@0: } michael@0: michael@0: void PresShell::RestoreCaret() michael@0: { michael@0: mCaret = mOriginalCaret; michael@0: } michael@0: michael@0: NS_IMETHODIMP PresShell::SetCaretEnabled(bool aInEnable) michael@0: { michael@0: bool oldEnabled = mCaretEnabled; michael@0: michael@0: mCaretEnabled = aInEnable; michael@0: michael@0: if (mCaret && (mCaretEnabled != oldEnabled)) michael@0: { michael@0: /* Don't change the caret's selection here! This was an evil side-effect of SetCaretEnabled() michael@0: nsCOMPtr domSel; michael@0: if (NS_SUCCEEDED(GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(domSel))) && domSel) michael@0: mCaret->SetCaretDOMSelection(domSel); michael@0: */ michael@0: mCaret->SetCaretVisible(mCaretEnabled); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP PresShell::SetCaretReadOnly(bool aReadOnly) michael@0: { michael@0: if (mCaret) michael@0: mCaret->SetCaretReadOnly(aReadOnly); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP PresShell::GetCaretEnabled(bool *aOutEnabled) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aOutEnabled); michael@0: *aOutEnabled = mCaretEnabled; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP PresShell::SetCaretVisibilityDuringSelection(bool aVisibility) michael@0: { michael@0: if (mCaret) michael@0: mCaret->SetVisibilityDuringSelection(aVisibility); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP PresShell::GetCaretVisible(bool *aOutIsVisible) michael@0: { michael@0: *aOutIsVisible = false; michael@0: if (mCaret) { michael@0: nsresult rv = mCaret->GetCaretVisible(aOutIsVisible); michael@0: NS_ENSURE_SUCCESS(rv,rv); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP PresShell::SetSelectionFlags(int16_t aInEnable) michael@0: { michael@0: mSelectionFlags = aInEnable; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP PresShell::GetSelectionFlags(int16_t *aOutEnable) michael@0: { michael@0: if (!aOutEnable) michael@0: return NS_ERROR_INVALID_ARG; michael@0: *aOutEnable = mSelectionFlags; michael@0: return NS_OK; michael@0: } michael@0: michael@0: //implementation of nsISelectionController michael@0: michael@0: NS_IMETHODIMP michael@0: PresShell::CharacterMove(bool aForward, bool aExtend) michael@0: { michael@0: return mSelection->CharacterMove(aForward, aExtend); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: PresShell::CharacterExtendForDelete() michael@0: { michael@0: return mSelection->CharacterExtendForDelete(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: PresShell::CharacterExtendForBackspace() michael@0: { michael@0: return mSelection->CharacterExtendForBackspace(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: PresShell::WordMove(bool aForward, bool aExtend) michael@0: { michael@0: nsresult result = mSelection->WordMove(aForward, aExtend); michael@0: // if we can't go down/up any more we must then move caret completely to michael@0: // end/beginning respectively. michael@0: if (NS_FAILED(result)) michael@0: result = CompleteMove(aForward, aExtend); michael@0: return result; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: PresShell::WordExtendForDelete(bool aForward) michael@0: { michael@0: return mSelection->WordExtendForDelete(aForward); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: PresShell::LineMove(bool aForward, bool aExtend) michael@0: { michael@0: nsresult result = mSelection->LineMove(aForward, aExtend); michael@0: // if we can't go down/up any more we must then move caret completely to michael@0: // end/beginning respectively. michael@0: if (NS_FAILED(result)) michael@0: result = CompleteMove(aForward,aExtend); michael@0: return result; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: PresShell::IntraLineMove(bool aForward, bool aExtend) michael@0: { michael@0: return mSelection->IntraLineMove(aForward, aExtend); michael@0: } michael@0: michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: PresShell::PageMove(bool aForward, bool aExtend) michael@0: { michael@0: nsIScrollableFrame *scrollableFrame = michael@0: GetFrameToScrollAsScrollable(nsIPresShell::eVertical); michael@0: if (!scrollableFrame) michael@0: return NS_OK; michael@0: michael@0: mSelection->CommonPageMove(aForward, aExtend, scrollableFrame); michael@0: // After ScrollSelectionIntoView(), the pending notifications might be michael@0: // flushed and PresShell/PresContext/Frames may be dead. See bug 418470. michael@0: return ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL, michael@0: nsISelectionController::SELECTION_FOCUS_REGION, michael@0: nsISelectionController::SCROLL_SYNCHRONOUS); michael@0: } michael@0: michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: PresShell::ScrollPage(bool aForward) michael@0: { michael@0: nsIScrollableFrame* scrollFrame = michael@0: GetFrameToScrollAsScrollable(nsIPresShell::eVertical); michael@0: if (scrollFrame) { michael@0: scrollFrame->ScrollBy(nsIntPoint(0, aForward ? 1 : -1), michael@0: nsIScrollableFrame::PAGES, michael@0: nsIScrollableFrame::SMOOTH); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: PresShell::ScrollLine(bool aForward) michael@0: { michael@0: nsIScrollableFrame* scrollFrame = michael@0: GetFrameToScrollAsScrollable(nsIPresShell::eVertical); michael@0: if (scrollFrame) { michael@0: int32_t lineCount = Preferences::GetInt("toolkit.scrollbox.verticalScrollDistance", michael@0: NS_DEFAULT_VERTICAL_SCROLL_DISTANCE); michael@0: scrollFrame->ScrollBy(nsIntPoint(0, aForward ? lineCount : -lineCount), michael@0: nsIScrollableFrame::LINES, michael@0: nsIScrollableFrame::SMOOTH); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: PresShell::ScrollCharacter(bool aRight) michael@0: { michael@0: nsIScrollableFrame* scrollFrame = michael@0: GetFrameToScrollAsScrollable(nsIPresShell::eHorizontal); michael@0: if (scrollFrame) { michael@0: int32_t h = Preferences::GetInt("toolkit.scrollbox.horizontalScrollDistance", michael@0: NS_DEFAULT_HORIZONTAL_SCROLL_DISTANCE); michael@0: scrollFrame->ScrollBy(nsIntPoint(aRight ? h : -h, 0), michael@0: nsIScrollableFrame::LINES, michael@0: nsIScrollableFrame::SMOOTH); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: PresShell::CompleteScroll(bool aForward) michael@0: { michael@0: nsIScrollableFrame* scrollFrame = michael@0: GetFrameToScrollAsScrollable(nsIPresShell::eVertical); michael@0: if (scrollFrame) { michael@0: scrollFrame->ScrollBy(nsIntPoint(0, aForward ? 1 : -1), michael@0: nsIScrollableFrame::WHOLE, michael@0: nsIScrollableFrame::SMOOTH); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: PresShell::CompleteMove(bool aForward, bool aExtend) michael@0: { michael@0: // Beware! This may flush notifications via synchronous michael@0: // ScrollSelectionIntoView. michael@0: nsIContent* limiter = mSelection->GetAncestorLimiter(); michael@0: nsIFrame* frame = limiter ? limiter->GetPrimaryFrame() michael@0: : FrameConstructor()->GetRootElementFrame(); michael@0: if (!frame) michael@0: return NS_ERROR_FAILURE; michael@0: nsIFrame::CaretPosition pos = michael@0: frame->GetExtremeCaretPosition(!aForward); michael@0: mSelection->HandleClick(pos.mResultContent, pos.mContentOffset, michael@0: pos.mContentOffset, aExtend, false, aForward); michael@0: if (limiter) { michael@0: // HandleClick resets ancestorLimiter, so set it again. michael@0: mSelection->SetAncestorLimiter(limiter); michael@0: } michael@0: michael@0: // After ScrollSelectionIntoView(), the pending notifications might be michael@0: // flushed and PresShell/PresContext/Frames may be dead. See bug 418470. michael@0: return ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL, michael@0: nsISelectionController::SELECTION_FOCUS_REGION, michael@0: nsISelectionController::SCROLL_SYNCHRONOUS); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: PresShell::SelectAll() michael@0: { michael@0: return mSelection->SelectAll(); michael@0: } michael@0: michael@0: static void michael@0: DoCheckVisibility(nsPresContext* aPresContext, michael@0: nsIContent* aNode, michael@0: int16_t aStartOffset, michael@0: int16_t aEndOffset, michael@0: bool* aRetval) michael@0: { michael@0: nsIFrame* frame = aNode->GetPrimaryFrame(); michael@0: if (!frame) { michael@0: // No frame to look at so it must not be visible. michael@0: return; michael@0: } michael@0: michael@0: // Start process now to go through all frames to find startOffset. Then check michael@0: // chars after that to see if anything until EndOffset is visible. michael@0: bool finished = false; michael@0: frame->CheckVisibility(aPresContext, aStartOffset, aEndOffset, true, michael@0: &finished, aRetval); michael@0: // Don't worry about other return value. michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: PresShell::CheckVisibility(nsIDOMNode *node, int16_t startOffset, int16_t EndOffset, bool *_retval) michael@0: { michael@0: if (!node || startOffset>EndOffset || !_retval || startOffset<0 || EndOffset<0) michael@0: return NS_ERROR_INVALID_ARG; michael@0: *_retval = false; //initialize return parameter michael@0: nsCOMPtr content(do_QueryInterface(node)); michael@0: if (!content) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: DoCheckVisibility(mPresContext, content, startOffset, EndOffset, _retval); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: PresShell::CheckVisibilityContent(nsIContent* aNode, int16_t aStartOffset, michael@0: int16_t aEndOffset, bool* aRetval) michael@0: { michael@0: if (!aNode || aStartOffset > aEndOffset || !aRetval || michael@0: aStartOffset < 0 || aEndOffset < 0) { michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: *aRetval = false; michael@0: DoCheckVisibility(mPresContext, aNode, aStartOffset, aEndOffset, aRetval); michael@0: return NS_OK; michael@0: } michael@0: michael@0: //end implementations nsISelectionController michael@0: michael@0: nsIFrame* michael@0: nsIPresShell::GetRootFrameExternal() const michael@0: { michael@0: return mFrameConstructor->GetRootFrame(); michael@0: } michael@0: michael@0: nsIFrame* michael@0: nsIPresShell::GetRootScrollFrame() const michael@0: { michael@0: nsIFrame* rootFrame = mFrameConstructor->GetRootFrame(); michael@0: // Ensure root frame is a viewport frame michael@0: if (!rootFrame || nsGkAtoms::viewportFrame != rootFrame->GetType()) michael@0: return nullptr; michael@0: nsIFrame* theFrame = rootFrame->GetFirstPrincipalChild(); michael@0: if (!theFrame || nsGkAtoms::scrollFrame != theFrame->GetType()) michael@0: return nullptr; michael@0: return theFrame; michael@0: } michael@0: michael@0: nsIScrollableFrame* michael@0: nsIPresShell::GetRootScrollFrameAsScrollable() const michael@0: { michael@0: nsIFrame* frame = GetRootScrollFrame(); michael@0: if (!frame) michael@0: return nullptr; michael@0: nsIScrollableFrame* scrollableFrame = do_QueryFrame(frame); michael@0: NS_ASSERTION(scrollableFrame, michael@0: "All scroll frames must implement nsIScrollableFrame"); michael@0: return scrollableFrame; michael@0: } michael@0: michael@0: nsIScrollableFrame* michael@0: nsIPresShell::GetRootScrollFrameAsScrollableExternal() const michael@0: { michael@0: return GetRootScrollFrameAsScrollable(); michael@0: } michael@0: michael@0: nsIPageSequenceFrame* michael@0: PresShell::GetPageSequenceFrame() const michael@0: { michael@0: nsIFrame* frame = mFrameConstructor->GetPageSequenceFrame(); michael@0: return do_QueryFrame(frame); michael@0: } michael@0: michael@0: void michael@0: PresShell::BeginUpdate(nsIDocument *aDocument, nsUpdateType aUpdateType) michael@0: { michael@0: #ifdef DEBUG michael@0: mUpdateCount++; michael@0: #endif michael@0: mFrameConstructor->BeginUpdate(); michael@0: michael@0: if (aUpdateType & UPDATE_STYLE) michael@0: mStyleSet->BeginUpdate(); michael@0: } michael@0: michael@0: void michael@0: PresShell::EndUpdate(nsIDocument *aDocument, nsUpdateType aUpdateType) michael@0: { michael@0: #ifdef DEBUG michael@0: NS_PRECONDITION(0 != mUpdateCount, "too many EndUpdate's"); michael@0: --mUpdateCount; michael@0: #endif michael@0: michael@0: if (aUpdateType & UPDATE_STYLE) { michael@0: mStyleSet->EndUpdate(); michael@0: if (mStylesHaveChanged || !mChangedScopeStyleRoots.IsEmpty()) michael@0: ReconstructStyleData(); michael@0: } michael@0: michael@0: mFrameConstructor->EndUpdate(); michael@0: } michael@0: michael@0: void michael@0: PresShell::RestoreRootScrollPosition() michael@0: { michael@0: nsIScrollableFrame* scrollableFrame = GetRootScrollFrameAsScrollable(); michael@0: if (scrollableFrame) { michael@0: scrollableFrame->ScrollToRestoredPosition(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: PresShell::BeginLoad(nsIDocument *aDocument) michael@0: { michael@0: mDocumentLoading = true; michael@0: michael@0: #ifdef PR_LOGGING michael@0: gfxTextPerfMetrics *tp = nullptr; michael@0: if (mPresContext) { michael@0: tp = mPresContext->GetTextPerfMetrics(); michael@0: } michael@0: michael@0: bool shouldLog = gLog && PR_LOG_TEST(gLog, PR_LOG_DEBUG); michael@0: if (shouldLog || tp) { michael@0: mLoadBegin = TimeStamp::Now(); michael@0: } michael@0: michael@0: if (shouldLog) { michael@0: nsIURI* uri = mDocument->GetDocumentURI(); michael@0: nsAutoCString spec; michael@0: if (uri) { michael@0: uri->GetSpec(spec); michael@0: } michael@0: PR_LOG(gLog, PR_LOG_DEBUG, michael@0: ("(presshell) %p load begin [%s]\n", michael@0: this, spec.get())); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: void michael@0: PresShell::EndLoad(nsIDocument *aDocument) michael@0: { michael@0: NS_PRECONDITION(aDocument == mDocument, "Wrong document"); michael@0: michael@0: RestoreRootScrollPosition(); michael@0: michael@0: mDocumentLoading = false; michael@0: } michael@0: michael@0: void michael@0: PresShell::LoadComplete() michael@0: { michael@0: #ifdef PR_LOGGING michael@0: gfxTextPerfMetrics *tp = nullptr; michael@0: if (mPresContext) { michael@0: tp = mPresContext->GetTextPerfMetrics(); michael@0: } michael@0: michael@0: // log load michael@0: bool shouldLog = gLog && PR_LOG_TEST(gLog, PR_LOG_DEBUG); michael@0: if (shouldLog || tp) { michael@0: TimeDuration loadTime = TimeStamp::Now() - mLoadBegin; michael@0: nsIURI* uri = mDocument->GetDocumentURI(); michael@0: nsAutoCString spec; michael@0: if (uri) { michael@0: uri->GetSpec(spec); michael@0: } michael@0: if (shouldLog) { michael@0: PR_LOG(gLog, PR_LOG_DEBUG, michael@0: ("(presshell) %p load done time-ms: %9.2f [%s]\n", michael@0: this, loadTime.ToMilliseconds(), spec.get())); michael@0: } michael@0: if (tp) { michael@0: tp->Accumulate(); michael@0: if (tp->cumulative.numChars > 0) { michael@0: LogTextPerfStats(tp, this, tp->cumulative, loadTime.ToMilliseconds(), michael@0: eLog_loaddone, spec.get()); michael@0: } michael@0: } michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: void michael@0: PresShell::VerifyHasDirtyRootAncestor(nsIFrame* aFrame) michael@0: { michael@0: // XXXbz due to bug 372769, can't actually assert anything here... michael@0: return; michael@0: michael@0: // XXXbz shouldn't need this part; remove it once FrameNeedsReflow michael@0: // handles the root frame correctly. michael@0: if (!aFrame->GetParent()) { michael@0: return; michael@0: } michael@0: michael@0: // Make sure that there is a reflow root ancestor of |aFrame| that's michael@0: // in mDirtyRoots already. michael@0: while (aFrame && (aFrame->GetStateBits() & NS_FRAME_HAS_DIRTY_CHILDREN)) { michael@0: if (((aFrame->GetStateBits() & NS_FRAME_REFLOW_ROOT) || michael@0: !aFrame->GetParent()) && michael@0: mDirtyRoots.Contains(aFrame)) { michael@0: return; michael@0: } michael@0: michael@0: aFrame = aFrame->GetParent(); michael@0: } michael@0: NS_NOTREACHED("Frame has dirty bits set but isn't scheduled to be " michael@0: "reflowed?"); michael@0: } michael@0: #endif michael@0: michael@0: void michael@0: PresShell::FrameNeedsReflow(nsIFrame *aFrame, IntrinsicDirty aIntrinsicDirty, michael@0: nsFrameState aBitToAdd) michael@0: { michael@0: NS_PRECONDITION(aBitToAdd == NS_FRAME_IS_DIRTY || michael@0: aBitToAdd == NS_FRAME_HAS_DIRTY_CHILDREN || michael@0: !aBitToAdd, michael@0: "Unexpected bits being added"); michael@0: NS_PRECONDITION(!(aIntrinsicDirty == eStyleChange && michael@0: aBitToAdd == NS_FRAME_HAS_DIRTY_CHILDREN), michael@0: "bits don't correspond to style change reason"); michael@0: michael@0: NS_ASSERTION(!mIsReflowing, "can't mark frame dirty during reflow"); michael@0: michael@0: // If we've not yet done the initial reflow, then don't bother michael@0: // enqueuing a reflow command yet. michael@0: if (! mDidInitialize) michael@0: return; michael@0: michael@0: // If we're already destroying, don't bother with this either. michael@0: if (mIsDestroying) michael@0: return; michael@0: michael@0: #ifdef DEBUG michael@0: //printf("gShellCounter: %d\n", gShellCounter++); michael@0: if (mInVerifyReflow) michael@0: return; michael@0: michael@0: if (VERIFY_REFLOW_NOISY_RC & gVerifyReflowFlags) { michael@0: printf("\nPresShell@%p: frame %p needs reflow\n", (void*)this, (void*)aFrame); michael@0: if (VERIFY_REFLOW_REALLY_NOISY_RC & gVerifyReflowFlags) { michael@0: printf("Current content model:\n"); michael@0: Element *rootElement = mDocument->GetRootElement(); michael@0: if (rootElement) { michael@0: rootElement->List(stdout, 0); michael@0: } michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: nsAutoTArray subtrees; michael@0: subtrees.AppendElement(aFrame); michael@0: michael@0: do { michael@0: nsIFrame *subtreeRoot = subtrees.ElementAt(subtrees.Length() - 1); michael@0: subtrees.RemoveElementAt(subtrees.Length() - 1); michael@0: michael@0: // Grab |wasDirty| now so we can go ahead and update the bits on michael@0: // subtreeRoot. michael@0: bool wasDirty = NS_SUBTREE_DIRTY(subtreeRoot); michael@0: subtreeRoot->AddStateBits(aBitToAdd); michael@0: michael@0: // Now if subtreeRoot is a reflow root we can cut off this reflow at it if michael@0: // the bit being added is NS_FRAME_HAS_DIRTY_CHILDREN. michael@0: bool targetFrameDirty = (aBitToAdd == NS_FRAME_IS_DIRTY); michael@0: michael@0: #define FRAME_IS_REFLOW_ROOT(_f) \ michael@0: ((_f->GetStateBits() & NS_FRAME_REFLOW_ROOT) && \ michael@0: (_f != subtreeRoot || !targetFrameDirty)) michael@0: michael@0: michael@0: // Mark the intrinsic widths as dirty on the frame, all of its ancestors, michael@0: // and all of its descendants, if needed: michael@0: michael@0: if (aIntrinsicDirty != eResize) { michael@0: // Mark argument and all ancestors dirty. (Unless we hit a reflow michael@0: // root that should contain the reflow. That root could be michael@0: // subtreeRoot itself if it's not dirty, or it could be some michael@0: // ancestor of subtreeRoot.) michael@0: for (nsIFrame *a = subtreeRoot; michael@0: a && !FRAME_IS_REFLOW_ROOT(a); michael@0: a = a->GetParent()) michael@0: a->MarkIntrinsicWidthsDirty(); michael@0: } michael@0: michael@0: if (aIntrinsicDirty == eStyleChange) { michael@0: // Mark all descendants dirty (using an nsTArray stack rather than michael@0: // recursion). michael@0: // Note that nsHTMLReflowState::InitResizeFlags has some similar michael@0: // code; see comments there for how and why it differs. michael@0: nsAutoTArray stack; michael@0: stack.AppendElement(subtreeRoot); michael@0: michael@0: do { michael@0: nsIFrame *f = stack.ElementAt(stack.Length() - 1); michael@0: stack.RemoveElementAt(stack.Length() - 1); michael@0: michael@0: if (f->GetType() == nsGkAtoms::placeholderFrame) { michael@0: nsIFrame *oof = nsPlaceholderFrame::GetRealFrameForPlaceholder(f); michael@0: if (!nsLayoutUtils::IsProperAncestorFrame(subtreeRoot, oof)) { michael@0: // We have another distinct subtree we need to mark. michael@0: subtrees.AppendElement(oof); michael@0: } michael@0: } michael@0: michael@0: nsIFrame::ChildListIterator lists(f); michael@0: for (; !lists.IsDone(); lists.Next()) { michael@0: nsFrameList::Enumerator childFrames(lists.CurrentList()); michael@0: for (; !childFrames.AtEnd(); childFrames.Next()) { michael@0: nsIFrame* kid = childFrames.get(); michael@0: kid->MarkIntrinsicWidthsDirty(); michael@0: stack.AppendElement(kid); michael@0: } michael@0: } michael@0: } while (stack.Length() != 0); michael@0: } michael@0: michael@0: // Skip setting dirty bits up the tree if we weren't given a bit to add. michael@0: if (!aBitToAdd) { michael@0: continue; michael@0: } michael@0: michael@0: // Set NS_FRAME_HAS_DIRTY_CHILDREN bits (via nsIFrame::ChildIsDirty) michael@0: // up the tree until we reach either a frame that's already dirty or michael@0: // a reflow root. michael@0: nsIFrame *f = subtreeRoot; michael@0: for (;;) { michael@0: if (FRAME_IS_REFLOW_ROOT(f) || !f->GetParent()) { michael@0: // we've hit a reflow root or the root frame michael@0: if (!wasDirty) { michael@0: mDirtyRoots.AppendElement(f); michael@0: mDocument->SetNeedLayoutFlush(); michael@0: } michael@0: #ifdef DEBUG michael@0: else { michael@0: VerifyHasDirtyRootAncestor(f); michael@0: } michael@0: #endif michael@0: michael@0: break; michael@0: } michael@0: michael@0: nsIFrame *child = f; michael@0: f = f->GetParent(); michael@0: wasDirty = NS_SUBTREE_DIRTY(f); michael@0: f->ChildIsDirty(child); michael@0: NS_ASSERTION(f->GetStateBits() & NS_FRAME_HAS_DIRTY_CHILDREN, michael@0: "ChildIsDirty didn't do its job"); michael@0: if (wasDirty) { michael@0: // This frame was already marked dirty. michael@0: #ifdef DEBUG michael@0: VerifyHasDirtyRootAncestor(f); michael@0: #endif michael@0: break; michael@0: } michael@0: } michael@0: } while (subtrees.Length() != 0); michael@0: michael@0: MaybeScheduleReflow(); michael@0: } michael@0: michael@0: void michael@0: PresShell::FrameNeedsToContinueReflow(nsIFrame *aFrame) michael@0: { michael@0: NS_ASSERTION(mIsReflowing, "Must be in reflow when marking path dirty."); michael@0: NS_PRECONDITION(mCurrentReflowRoot, "Must have a current reflow root here"); michael@0: NS_ASSERTION(aFrame == mCurrentReflowRoot || michael@0: nsLayoutUtils::IsProperAncestorFrame(mCurrentReflowRoot, aFrame), michael@0: "Frame passed in is not the descendant of mCurrentReflowRoot"); michael@0: NS_ASSERTION(aFrame->GetStateBits() & NS_FRAME_IN_REFLOW, michael@0: "Frame passed in not in reflow?"); michael@0: michael@0: mFramesToDirty.PutEntry(aFrame); michael@0: } michael@0: michael@0: nsIScrollableFrame* michael@0: nsIPresShell::GetFrameToScrollAsScrollable( michael@0: nsIPresShell::ScrollDirection aDirection) michael@0: { michael@0: nsIScrollableFrame* scrollFrame = nullptr; michael@0: michael@0: nsCOMPtr focusedContent; michael@0: nsIFocusManager* fm = nsFocusManager::GetFocusManager(); michael@0: if (fm && mDocument) { michael@0: nsCOMPtr window = do_QueryInterface(mDocument->GetWindow()); michael@0: michael@0: nsCOMPtr focusedElement; michael@0: fm->GetFocusedElementForWindow(window, false, nullptr, getter_AddRefs(focusedElement)); michael@0: focusedContent = do_QueryInterface(focusedElement); michael@0: } michael@0: if (!focusedContent && mSelection) { michael@0: nsISelection* domSelection = mSelection-> michael@0: GetSelection(nsISelectionController::SELECTION_NORMAL); michael@0: if (domSelection) { michael@0: nsCOMPtr focusedNode; michael@0: domSelection->GetFocusNode(getter_AddRefs(focusedNode)); michael@0: focusedContent = do_QueryInterface(focusedNode); michael@0: } michael@0: } michael@0: if (focusedContent) { michael@0: nsIFrame* startFrame = focusedContent->GetPrimaryFrame(); michael@0: if (startFrame) { michael@0: scrollFrame = startFrame->GetScrollTargetFrame(); michael@0: if (scrollFrame) { michael@0: startFrame = scrollFrame->GetScrolledFrame(); michael@0: } michael@0: if (aDirection == nsIPresShell::eEither) { michael@0: scrollFrame = michael@0: nsLayoutUtils::GetNearestScrollableFrame(startFrame); michael@0: } else { michael@0: scrollFrame = michael@0: nsLayoutUtils::GetNearestScrollableFrameForDirection(startFrame, michael@0: aDirection == eVertical ? nsLayoutUtils::eVertical : michael@0: nsLayoutUtils::eHorizontal); michael@0: } michael@0: } michael@0: } michael@0: if (!scrollFrame) { michael@0: scrollFrame = GetRootScrollFrameAsScrollable(); michael@0: } michael@0: return scrollFrame; michael@0: } michael@0: michael@0: void michael@0: PresShell::CancelAllPendingReflows() michael@0: { michael@0: mDirtyRoots.Clear(); michael@0: michael@0: if (mReflowScheduled) { michael@0: GetPresContext()->RefreshDriver()->RemoveLayoutFlushObserver(this); michael@0: mReflowScheduled = false; michael@0: } michael@0: michael@0: ASSERT_REFLOW_SCHEDULED_STATE(); michael@0: } michael@0: michael@0: nsresult michael@0: PresShell::RecreateFramesFor(nsIContent* aContent) michael@0: { michael@0: NS_ENSURE_TRUE(mPresContext, NS_ERROR_FAILURE); michael@0: if (!mDidInitialize) { michael@0: // Nothing to do here. In fact, if we proceed and aContent is the michael@0: // root we will crash. michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Don't call RecreateFramesForContent since that is not exported and we want michael@0: // to keep the number of entrypoints down. michael@0: michael@0: NS_ASSERTION(mViewManager, "Should have view manager"); michael@0: michael@0: // Have to make sure that the content notifications are flushed before we michael@0: // start messing with the frame model; otherwise we can get content doubling. michael@0: mDocument->FlushPendingNotifications(Flush_ContentAndNotify); michael@0: michael@0: nsAutoScriptBlocker scriptBlocker; michael@0: michael@0: nsStyleChangeList changeList; michael@0: changeList.AppendChange(nullptr, aContent, nsChangeHint_ReconstructFrame); michael@0: michael@0: // Mark ourselves as not safe to flush while we're doing frame construction. michael@0: ++mChangeNestCount; michael@0: RestyleManager* restyleManager = mPresContext->RestyleManager(); michael@0: nsresult rv = restyleManager->ProcessRestyledFrames(changeList); michael@0: restyleManager->FlushOverflowChangedTracker(); michael@0: --mChangeNestCount; michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: void michael@0: nsIPresShell::PostRecreateFramesFor(Element* aElement) michael@0: { michael@0: mPresContext->RestyleManager()->PostRestyleEvent(aElement, nsRestyleHint(0), michael@0: nsChangeHint_ReconstructFrame); michael@0: } michael@0: michael@0: void michael@0: nsIPresShell::RestyleForAnimation(Element* aElement, nsRestyleHint aHint) michael@0: { michael@0: mPresContext->RestyleManager()->PostAnimationRestyleEvent(aElement, aHint, michael@0: NS_STYLE_HINT_NONE); michael@0: } michael@0: michael@0: void michael@0: nsIPresShell::SetForwardingContainer(const WeakPtr &aContainer) michael@0: { michael@0: mForwardingContainer = aContainer; michael@0: } michael@0: michael@0: void michael@0: PresShell::ClearFrameRefs(nsIFrame* aFrame) michael@0: { michael@0: mPresContext->EventStateManager()->ClearFrameRefs(aFrame); michael@0: michael@0: nsWeakFrame* weakFrame = mWeakFrames; michael@0: while (weakFrame) { michael@0: nsWeakFrame* prev = weakFrame->GetPreviousWeakFrame(); michael@0: if (weakFrame->GetFrame() == aFrame) { michael@0: // This removes weakFrame from mWeakFrames. michael@0: weakFrame->Clear(this); michael@0: } michael@0: weakFrame = prev; michael@0: } michael@0: } michael@0: michael@0: already_AddRefed michael@0: PresShell::CreateReferenceRenderingContext() michael@0: { michael@0: nsDeviceContext* devCtx = mPresContext->DeviceContext(); michael@0: nsRefPtr rc; michael@0: if (mPresContext->IsScreen()) { michael@0: rc = new nsRenderingContext(); michael@0: rc->Init(devCtx, gfxPlatform::GetPlatform()->ScreenReferenceSurface()); michael@0: } else { michael@0: rc = devCtx->CreateRenderingContext(); michael@0: } michael@0: michael@0: MOZ_ASSERT(rc, "shouldn't break promise to return non-null"); michael@0: return rc.forget(); michael@0: } michael@0: michael@0: nsresult michael@0: PresShell::GoToAnchor(const nsAString& aAnchorName, bool aScroll) michael@0: { michael@0: if (!mDocument) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: const Element *root = mDocument->GetRootElement(); michael@0: if (root && root->IsSVG(nsGkAtoms::svg)) { michael@0: // We need to execute this even if there is an empty anchor name michael@0: // so that any existing SVG fragment identifier effect is removed michael@0: if (SVGFragmentIdentifier::ProcessFragmentIdentifier(mDocument, aAnchorName)) { michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: // Hold a reference to the ESM in case event dispatch tears us down. michael@0: nsRefPtr esm = mPresContext->EventStateManager(); michael@0: michael@0: if (aAnchorName.IsEmpty()) { michael@0: NS_ASSERTION(!aScroll, "can't scroll to empty anchor name"); michael@0: esm->SetContentState(nullptr, NS_EVENT_STATE_URLTARGET); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsCOMPtr htmlDoc = do_QueryInterface(mDocument); michael@0: nsresult rv = NS_OK; michael@0: nsCOMPtr content; michael@0: michael@0: // Search for an element with a matching "id" attribute michael@0: if (mDocument) { michael@0: content = mDocument->GetElementById(aAnchorName); michael@0: } michael@0: michael@0: // Search for an anchor element with a matching "name" attribute michael@0: if (!content && htmlDoc) { michael@0: nsCOMPtr list; michael@0: // Find a matching list of named nodes michael@0: rv = htmlDoc->GetElementsByName(aAnchorName, getter_AddRefs(list)); michael@0: if (NS_SUCCEEDED(rv) && list) { michael@0: uint32_t i; michael@0: // Loop through the named nodes looking for the first anchor michael@0: for (i = 0; true; i++) { michael@0: nsCOMPtr node; michael@0: rv = list->Item(i, getter_AddRefs(node)); michael@0: if (!node) { // End of list michael@0: break; michael@0: } michael@0: // Ensure it's an anchor element michael@0: content = do_QueryInterface(node); michael@0: if (content) { michael@0: if (content->Tag() == nsGkAtoms::a && content->IsHTML()) { michael@0: break; michael@0: } michael@0: content = nullptr; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Search for anchor in the HTML namespace with a matching name michael@0: if (!content && !htmlDoc) michael@0: { michael@0: nsCOMPtr doc = do_QueryInterface(mDocument); michael@0: nsCOMPtr list; michael@0: NS_NAMED_LITERAL_STRING(nameSpace, "http://www.w3.org/1999/xhtml"); michael@0: // Get the list of anchor elements michael@0: rv = doc->GetElementsByTagNameNS(nameSpace, NS_LITERAL_STRING("a"), getter_AddRefs(list)); michael@0: if (NS_SUCCEEDED(rv) && list) { michael@0: uint32_t i; michael@0: // Loop through the named nodes looking for the first anchor michael@0: for (i = 0; true; i++) { michael@0: nsCOMPtr node; michael@0: rv = list->Item(i, getter_AddRefs(node)); michael@0: if (!node) { // End of list michael@0: break; michael@0: } michael@0: // Compare the name attribute michael@0: nsCOMPtr element = do_QueryInterface(node); michael@0: nsAutoString value; michael@0: if (element && NS_SUCCEEDED(element->GetAttribute(NS_LITERAL_STRING("name"), value))) { michael@0: if (value.Equals(aAnchorName)) { michael@0: content = do_QueryInterface(element); michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: esm->SetContentState(content, NS_EVENT_STATE_URLTARGET); michael@0: michael@0: #ifdef ACCESSIBILITY michael@0: nsIContent *anchorTarget = content; michael@0: #endif michael@0: michael@0: nsIScrollableFrame* rootScroll = GetRootScrollFrameAsScrollable(); michael@0: if (rootScroll && rootScroll->DidHistoryRestore()) { michael@0: // Scroll position restored from history trumps scrolling to anchor. michael@0: aScroll = false; michael@0: rootScroll->ClearDidHistoryRestore(); michael@0: } michael@0: michael@0: if (content) { michael@0: if (aScroll) { michael@0: rv = ScrollContentIntoView(content, michael@0: ScrollAxis(SCROLL_TOP, SCROLL_ALWAYS), michael@0: ScrollAxis(), michael@0: ANCHOR_SCROLL_FLAGS); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsIScrollableFrame* rootScroll = GetRootScrollFrameAsScrollable(); michael@0: if (rootScroll) { michael@0: mLastAnchorScrolledTo = content; michael@0: mLastAnchorScrollPositionY = rootScroll->GetScrollPosition().y; michael@0: } michael@0: } michael@0: michael@0: // Should we select the target? This action is controlled by a michael@0: // preference: the default is to not select. michael@0: bool selectAnchor = Preferences::GetBool("layout.selectanchor"); michael@0: michael@0: // Even if select anchor pref is false, we must still move the michael@0: // caret there. That way tabbing will start from the new michael@0: // location michael@0: nsRefPtr jumpToRange = new nsRange(mDocument); michael@0: while (content && content->GetFirstChild()) { michael@0: content = content->GetFirstChild(); michael@0: } michael@0: nsCOMPtr node(do_QueryInterface(content)); michael@0: NS_ASSERTION(node, "No nsIDOMNode for descendant of anchor"); michael@0: jumpToRange->SelectNodeContents(node); michael@0: // Select the anchor michael@0: nsISelection* sel = mSelection-> michael@0: GetSelection(nsISelectionController::SELECTION_NORMAL); michael@0: if (sel) { michael@0: sel->RemoveAllRanges(); michael@0: sel->AddRange(jumpToRange); michael@0: if (!selectAnchor) { michael@0: // Use a caret (collapsed selection) at the start of the anchor michael@0: sel->CollapseToStart(); michael@0: } michael@0: } michael@0: // Selection is at anchor. michael@0: // Now focus the document itself if focus is on an element within it. michael@0: nsPIDOMWindow *win = mDocument->GetWindow(); michael@0: michael@0: nsIFocusManager* fm = nsFocusManager::GetFocusManager(); michael@0: if (fm && win) { michael@0: nsCOMPtr focusedWindow; michael@0: fm->GetFocusedWindow(getter_AddRefs(focusedWindow)); michael@0: if (SameCOMIdentity(win, focusedWindow)) { michael@0: fm->ClearFocus(focusedWindow); michael@0: } michael@0: } michael@0: michael@0: // If the target is an animation element, activate the animation michael@0: if (content->IsNodeOfType(nsINode::eANIMATION)) { michael@0: SVGContentUtils::ActivateByHyperlink(content.get()); michael@0: } michael@0: } else { michael@0: rv = NS_ERROR_FAILURE; michael@0: NS_NAMED_LITERAL_STRING(top, "top"); michael@0: if (nsContentUtils::EqualsIgnoreASCIICase(aAnchorName, top)) { michael@0: // Scroll to the top/left if aAnchorName is "top" and there is no element michael@0: // with such a name or id. michael@0: rv = NS_OK; michael@0: nsIScrollableFrame* sf = GetRootScrollFrameAsScrollable(); michael@0: // Check |aScroll| after setting |rv| so we set |rv| to the same michael@0: // thing whether or not |aScroll| is true. michael@0: if (aScroll && sf) { michael@0: // Scroll to the top of the page michael@0: sf->ScrollTo(nsPoint(0, 0), nsIScrollableFrame::INSTANT); michael@0: } michael@0: } michael@0: } michael@0: michael@0: #ifdef ACCESSIBILITY michael@0: if (anchorTarget) { michael@0: nsAccessibilityService* accService = AccService(); michael@0: if (accService) michael@0: accService->NotifyOfAnchorJumpTo(anchorTarget); michael@0: } michael@0: #endif michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: PresShell::ScrollToAnchor() michael@0: { michael@0: if (!mLastAnchorScrolledTo) { michael@0: return NS_OK; michael@0: } michael@0: NS_ASSERTION(mDidInitialize, "should have done initial reflow by now"); michael@0: michael@0: nsIScrollableFrame* rootScroll = GetRootScrollFrameAsScrollable(); michael@0: if (!rootScroll || michael@0: mLastAnchorScrollPositionY != rootScroll->GetScrollPosition().y) { michael@0: return NS_OK; michael@0: } michael@0: nsresult rv = ScrollContentIntoView(mLastAnchorScrolledTo, michael@0: ScrollAxis(SCROLL_TOP, SCROLL_ALWAYS), michael@0: ScrollAxis(), michael@0: ANCHOR_SCROLL_FLAGS); michael@0: mLastAnchorScrolledTo = nullptr; michael@0: return rv; michael@0: } michael@0: michael@0: /* michael@0: * Helper (per-continuation) for ScrollContentIntoView. michael@0: * michael@0: * @param aContainerFrame [in] the frame which aRect is relative to michael@0: * @param aFrame [in] Frame whose bounds should be unioned michael@0: * @param aUseWholeLineHeightForInlines [in] if true, then for inline frames michael@0: * we should include the top of the line in the added rectangle michael@0: * @param aRect [inout] rect into which its bounds should be unioned michael@0: * @param aHaveRect [inout] whether aRect contains data yet michael@0: * @param aPrevBlock [inout] the block aLines is a line iterator for michael@0: * @param aLines [inout] the line iterator we're using michael@0: * @param aCurLine [inout] the line to start looking from in this iterator michael@0: */ michael@0: static void michael@0: AccumulateFrameBounds(nsIFrame* aContainerFrame, michael@0: nsIFrame* aFrame, michael@0: bool aUseWholeLineHeightForInlines, michael@0: nsRect& aRect, michael@0: bool& aHaveRect, michael@0: nsIFrame*& aPrevBlock, michael@0: nsAutoLineIterator& aLines, michael@0: int32_t& aCurLine) michael@0: { michael@0: nsIFrame* frame = aFrame; michael@0: nsRect frameBounds = nsRect(nsPoint(0, 0), aFrame->GetSize()); michael@0: michael@0: // If this is an inline frame and either the bounds height is 0 (quirks michael@0: // layout model) or aUseWholeLineHeightForInlines is set, we need to michael@0: // change the top of the bounds to include the whole line. michael@0: if (frameBounds.height == 0 || aUseWholeLineHeightForInlines) { michael@0: nsIFrame *prevFrame = aFrame; michael@0: nsIFrame *f = aFrame; michael@0: michael@0: while (f && f->IsFrameOfType(nsIFrame::eLineParticipant) && michael@0: !f->IsTransformed() && !f->IsPositioned()) { michael@0: prevFrame = f; michael@0: f = prevFrame->GetParent(); michael@0: } michael@0: michael@0: if (f != aFrame && michael@0: f && michael@0: f->GetType() == nsGkAtoms::blockFrame) { michael@0: // find the line containing aFrame and increase the top of |offset|. michael@0: if (f != aPrevBlock) { michael@0: aLines = f->GetLineIterator(); michael@0: aPrevBlock = f; michael@0: aCurLine = 0; michael@0: } michael@0: if (aLines) { michael@0: int32_t index = aLines->FindLineContaining(prevFrame, aCurLine); michael@0: if (index >= 0) { michael@0: aCurLine = index; michael@0: nsIFrame *trash1; michael@0: int32_t trash2; michael@0: nsRect lineBounds; michael@0: uint32_t trash3; michael@0: michael@0: if (NS_SUCCEEDED(aLines->GetLine(index, &trash1, &trash2, michael@0: lineBounds, &trash3))) { michael@0: frameBounds += frame->GetOffsetTo(f); michael@0: frame = f; michael@0: if (lineBounds.y < frameBounds.y) { michael@0: frameBounds.height = frameBounds.YMost() - lineBounds.y; michael@0: frameBounds.y = lineBounds.y; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: nsRect transformedBounds = nsLayoutUtils::TransformFrameRectToAncestor(frame, michael@0: frameBounds, aContainerFrame); michael@0: michael@0: if (aHaveRect) { michael@0: // We can't use nsRect::UnionRect since it drops empty rects on michael@0: // the floor, and we need to include them. (Thus we need michael@0: // aHaveRect to know when to drop the initial value on the floor.) michael@0: aRect.UnionRectEdges(aRect, transformedBounds); michael@0: } else { michael@0: aHaveRect = true; michael@0: aRect = transformedBounds; michael@0: } michael@0: } michael@0: michael@0: static bool michael@0: ComputeNeedToScroll(nsIPresShell::WhenToScroll aWhenToScroll, michael@0: nscoord aLineSize, michael@0: nscoord aRectMin, michael@0: nscoord aRectMax, michael@0: nscoord aViewMin, michael@0: nscoord aViewMax) { michael@0: // See how the rect should be positioned vertically michael@0: if (nsIPresShell::SCROLL_ALWAYS == aWhenToScroll) { michael@0: // The caller wants the frame as visible as possible michael@0: return true; michael@0: } else if (nsIPresShell::SCROLL_IF_NOT_VISIBLE == aWhenToScroll) { michael@0: // Scroll only if no part of the frame is visible in this view michael@0: return aRectMax - aLineSize <= aViewMin || michael@0: aRectMin + aLineSize >= aViewMax; michael@0: } else if (nsIPresShell::SCROLL_IF_NOT_FULLY_VISIBLE == aWhenToScroll) { michael@0: // Scroll only if part of the frame is hidden and more can fit in view michael@0: return !(aRectMin >= aViewMin && aRectMax <= aViewMax) && michael@0: std::min(aViewMax, aRectMax) - std::max(aRectMin, aViewMin) < aViewMax - aViewMin; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: static nscoord michael@0: ComputeWhereToScroll(int16_t aWhereToScroll, michael@0: nscoord aOriginalCoord, michael@0: nscoord aRectMin, michael@0: nscoord aRectMax, michael@0: nscoord aViewMin, michael@0: nscoord aViewMax, michael@0: nscoord* aRangeMin, michael@0: nscoord* aRangeMax) { michael@0: nscoord resultCoord = aOriginalCoord; michael@0: // Allow the scroll operation to land anywhere that michael@0: // makes the whole rectangle visible. michael@0: if (nsIPresShell::SCROLL_MINIMUM == aWhereToScroll) { michael@0: if (aRectMin < aViewMin) { michael@0: // Scroll up so the frame's top edge is visible michael@0: resultCoord = aRectMin; michael@0: } else if (aRectMax > aViewMax) { michael@0: // Scroll down so the frame's bottom edge is visible. Make sure the michael@0: // frame's top edge is still visible michael@0: resultCoord = aOriginalCoord + aRectMax - aViewMax; michael@0: if (resultCoord > aRectMin) { michael@0: resultCoord = aRectMin; michael@0: } michael@0: } michael@0: } else { michael@0: nscoord frameAlignCoord = michael@0: NSToCoordRound(aRectMin + (aRectMax - aRectMin) * (aWhereToScroll / 100.0f)); michael@0: resultCoord = NSToCoordRound(frameAlignCoord - (aViewMax - aViewMin) * ( michael@0: aWhereToScroll / 100.0f)); michael@0: } michael@0: nscoord scrollPortLength = aViewMax - aViewMin; michael@0: // Force the scroll range to extend to include resultCoord. michael@0: *aRangeMin = std::min(resultCoord, aRectMax - scrollPortLength); michael@0: *aRangeMax = std::max(resultCoord, aRectMin); michael@0: return resultCoord; michael@0: } michael@0: michael@0: /** michael@0: * This function takes a scrollable frame, a rect in the coordinate system michael@0: * of the scrolled frame, and a desired percentage-based scroll michael@0: * position and attempts to scroll the rect to that position in the michael@0: * scrollport. michael@0: * michael@0: * This needs to work even if aRect has a width or height of zero. michael@0: */ michael@0: static void ScrollToShowRect(nsIFrame* aFrame, michael@0: nsIScrollableFrame* aFrameAsScrollable, michael@0: const nsRect& aRect, michael@0: nsIPresShell::ScrollAxis aVertical, michael@0: nsIPresShell::ScrollAxis aHorizontal, michael@0: uint32_t aFlags) michael@0: { michael@0: nsPoint scrollPt = aFrameAsScrollable->GetScrollPosition(); michael@0: nsRect visibleRect(scrollPt, michael@0: aFrameAsScrollable->GetScrollPositionClampingScrollPortSize()); michael@0: michael@0: // If this is the root scroll frame, make sure to take into account the michael@0: // content document fixed position margins. When set, these indicate that michael@0: // chrome is obscuring the viewport. michael@0: nsRect targetRect(aRect); michael@0: nsIPresShell *presShell = aFrame->PresContext()->PresShell(); michael@0: if (aFrameAsScrollable == presShell->GetRootScrollFrameAsScrollable()) { michael@0: targetRect.Inflate(presShell->GetContentDocumentFixedPositionMargins()); michael@0: } michael@0: michael@0: nsSize lineSize; michael@0: // Don't call GetLineScrollAmount unless we actually need it. Not only michael@0: // does this save time, but it's not safe to call GetLineScrollAmount michael@0: // during reflow (because it depends on font size inflation and doesn't michael@0: // use the in-reflow-safe font-size inflation path). If we did call it, michael@0: // it would assert and possible give the wrong result. michael@0: if (aVertical.mWhenToScroll == nsIPresShell::SCROLL_IF_NOT_VISIBLE || michael@0: aHorizontal.mWhenToScroll == nsIPresShell::SCROLL_IF_NOT_VISIBLE) { michael@0: lineSize = aFrameAsScrollable->GetLineScrollAmount(); michael@0: } michael@0: ScrollbarStyles ss = aFrameAsScrollable->GetScrollbarStyles(); michael@0: nsRect allowedRange(scrollPt, nsSize(0, 0)); michael@0: bool needToScroll = false; michael@0: uint32_t directions = aFrameAsScrollable->GetPerceivedScrollingDirections(); michael@0: michael@0: if (((aFlags & nsIPresShell::SCROLL_OVERFLOW_HIDDEN) || michael@0: ss.mVertical != NS_STYLE_OVERFLOW_HIDDEN) && michael@0: (!aVertical.mOnlyIfPerceivedScrollableDirection || michael@0: (directions & nsIScrollableFrame::VERTICAL))) { michael@0: michael@0: if (ComputeNeedToScroll(aVertical.mWhenToScroll, michael@0: lineSize.height, michael@0: targetRect.y, michael@0: targetRect.YMost(), michael@0: visibleRect.y, michael@0: visibleRect.YMost())) { michael@0: nscoord maxHeight; michael@0: scrollPt.y = ComputeWhereToScroll(aVertical.mWhereToScroll, michael@0: scrollPt.y, michael@0: targetRect.y, michael@0: targetRect.YMost(), michael@0: visibleRect.y, michael@0: visibleRect.YMost(), michael@0: &allowedRange.y, &maxHeight); michael@0: allowedRange.height = maxHeight - allowedRange.y; michael@0: needToScroll = true; michael@0: } michael@0: } michael@0: michael@0: if (((aFlags & nsIPresShell::SCROLL_OVERFLOW_HIDDEN) || michael@0: ss.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN) && michael@0: (!aHorizontal.mOnlyIfPerceivedScrollableDirection || michael@0: (directions & nsIScrollableFrame::HORIZONTAL))) { michael@0: michael@0: if (ComputeNeedToScroll(aHorizontal.mWhenToScroll, michael@0: lineSize.width, michael@0: targetRect.x, michael@0: targetRect.XMost(), michael@0: visibleRect.x, michael@0: visibleRect.XMost())) { michael@0: nscoord maxWidth; michael@0: scrollPt.x = ComputeWhereToScroll(aHorizontal.mWhereToScroll, michael@0: scrollPt.x, michael@0: targetRect.x, michael@0: targetRect.XMost(), michael@0: visibleRect.x, michael@0: visibleRect.XMost(), michael@0: &allowedRange.x, &maxWidth); michael@0: allowedRange.width = maxWidth - allowedRange.x; michael@0: needToScroll = true; michael@0: } michael@0: } michael@0: michael@0: // If we don't need to scroll, then don't try since it might cancel michael@0: // a current smooth scroll operation. michael@0: if (needToScroll) { michael@0: aFrameAsScrollable->ScrollTo(scrollPt, nsIScrollableFrame::INSTANT, &allowedRange); michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: PresShell::ScrollContentIntoView(nsIContent* aContent, michael@0: nsIPresShell::ScrollAxis aVertical, michael@0: nsIPresShell::ScrollAxis aHorizontal, michael@0: uint32_t aFlags) michael@0: { michael@0: NS_ENSURE_TRUE(aContent, NS_ERROR_NULL_POINTER); michael@0: nsCOMPtr currentDoc = aContent->GetCurrentDoc(); michael@0: NS_ENSURE_STATE(currentDoc); michael@0: michael@0: NS_ASSERTION(mDidInitialize, "should have done initial reflow by now"); michael@0: michael@0: if (mContentToScrollTo) { michael@0: mContentToScrollTo->DeleteProperty(nsGkAtoms::scrolling); michael@0: } michael@0: mContentToScrollTo = aContent; michael@0: ScrollIntoViewData* data = new ScrollIntoViewData(); michael@0: data->mContentScrollVAxis = aVertical; michael@0: data->mContentScrollHAxis = aHorizontal; michael@0: data->mContentToScrollToFlags = aFlags; michael@0: if (NS_FAILED(mContentToScrollTo->SetProperty(nsGkAtoms::scrolling, data, michael@0: nsINode::DeleteProperty))) { michael@0: mContentToScrollTo = nullptr; michael@0: } michael@0: michael@0: // Flush layout and attempt to scroll in the process. michael@0: currentDoc->SetNeedLayoutFlush(); michael@0: currentDoc->FlushPendingNotifications(Flush_InterruptibleLayout); michael@0: michael@0: // If mContentToScrollTo is non-null, that means we interrupted the reflow michael@0: // (or suppressed it altogether because we're suppressing interruptible michael@0: // flushes right now) and won't necessarily get the position correct, but do michael@0: // a best-effort scroll here. The other option would be to do this inside michael@0: // FlushPendingNotifications, but I'm not sure the repeated scrolling that michael@0: // could trigger if reflows keep getting interrupted would be more desirable michael@0: // than a single best-effort scroll followed by one final scroll on the first michael@0: // completed reflow. michael@0: if (mContentToScrollTo) { michael@0: DoScrollContentIntoView(); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: PresShell::DoScrollContentIntoView() michael@0: { michael@0: NS_ASSERTION(mDidInitialize, "should have done initial reflow by now"); michael@0: michael@0: nsIFrame* frame = mContentToScrollTo->GetPrimaryFrame(); michael@0: if (!frame) { michael@0: mContentToScrollTo->DeleteProperty(nsGkAtoms::scrolling); michael@0: mContentToScrollTo = nullptr; michael@0: return; michael@0: } michael@0: michael@0: if (frame->GetStateBits() & NS_FRAME_FIRST_REFLOW) { michael@0: // The reflow flush before this scroll got interrupted, and this frame's michael@0: // coords and size are all zero, and it has no content showing anyway. michael@0: // Don't bother scrolling to it. We'll try again when we finish up layout. michael@0: return; michael@0: } michael@0: michael@0: // Make sure we skip 'frame' ... if it's scrollable, we should use its michael@0: // scrollable ancestor as the container. michael@0: nsIFrame* container = michael@0: nsLayoutUtils::GetClosestFrameOfType(frame->GetParent(), nsGkAtoms::scrollFrame); michael@0: if (!container) { michael@0: // nothing can be scrolled michael@0: return; michael@0: } michael@0: michael@0: ScrollIntoViewData* data = static_cast( michael@0: mContentToScrollTo->GetProperty(nsGkAtoms::scrolling)); michael@0: if (MOZ_UNLIKELY(!data)) { michael@0: mContentToScrollTo = nullptr; michael@0: return; michael@0: } michael@0: michael@0: // This is a two-step process. michael@0: // Step 1: Find the bounds of the rect we want to scroll into view. For michael@0: // example, for an inline frame we may want to scroll in the whole michael@0: // line, or we may want to scroll multiple lines into view. michael@0: // Step 2: Walk container frame and its ancestors and scroll them michael@0: // appropriately. michael@0: // frameBounds is relative to container. We're assuming michael@0: // that scrollframes don't split so every continuation of frame will michael@0: // be a descendant of container. (Things would still mostly work michael@0: // even if that assumption was false.) michael@0: nsRect frameBounds; michael@0: bool haveRect = false; michael@0: bool useWholeLineHeightForInlines = michael@0: data->mContentScrollVAxis.mWhenToScroll != nsIPresShell::SCROLL_IF_NOT_FULLY_VISIBLE; michael@0: // Reuse the same line iterator across calls to AccumulateFrameBounds. We set michael@0: // it every time we detect a new block (stored in prevBlock). michael@0: nsIFrame* prevBlock = nullptr; michael@0: nsAutoLineIterator lines; michael@0: // The last line we found a continuation on in |lines|. We assume that later michael@0: // continuations cannot come on earlier lines. michael@0: int32_t curLine = 0; michael@0: do { michael@0: AccumulateFrameBounds(container, frame, useWholeLineHeightForInlines, michael@0: frameBounds, haveRect, prevBlock, lines, curLine); michael@0: } while ((frame = frame->GetNextContinuation())); michael@0: michael@0: ScrollFrameRectIntoView(container, frameBounds, data->mContentScrollVAxis, michael@0: data->mContentScrollHAxis, michael@0: data->mContentToScrollToFlags); michael@0: } michael@0: michael@0: bool michael@0: PresShell::ScrollFrameRectIntoView(nsIFrame* aFrame, michael@0: const nsRect& aRect, michael@0: nsIPresShell::ScrollAxis aVertical, michael@0: nsIPresShell::ScrollAxis aHorizontal, michael@0: uint32_t aFlags) michael@0: { michael@0: bool didScroll = false; michael@0: // This function needs to work even if rect has a width or height of 0. michael@0: nsRect rect = aRect; michael@0: nsIFrame* container = aFrame; michael@0: // Walk up the frame hierarchy scrolling the rect into view and michael@0: // keeping rect relative to container michael@0: do { michael@0: nsIScrollableFrame* sf = do_QueryFrame(container); michael@0: if (sf) { michael@0: nsPoint oldPosition = sf->GetScrollPosition(); michael@0: nsRect targetRect = rect; michael@0: if (container->StyleDisplay()->mOverflowClipBox == michael@0: NS_STYLE_OVERFLOW_CLIP_BOX_CONTENT_BOX) { michael@0: nsMargin padding = container->GetUsedPadding(); michael@0: targetRect.Inflate(padding); michael@0: } michael@0: ScrollToShowRect(container, sf, targetRect - sf->GetScrolledFrame()->GetPosition(), michael@0: aVertical, aHorizontal, aFlags); michael@0: nsPoint newPosition = sf->GetScrollPosition(); michael@0: // If the scroll position increased, that means our content moved up, michael@0: // so our rect's offset should decrease michael@0: rect += oldPosition - newPosition; michael@0: michael@0: if (oldPosition != newPosition) { michael@0: didScroll = true; michael@0: } michael@0: michael@0: // only scroll one container when this flag is set michael@0: if (aFlags & nsIPresShell::SCROLL_FIRST_ANCESTOR_ONLY) { michael@0: break; michael@0: } michael@0: } michael@0: nsIFrame* parent; michael@0: if (container->IsTransformed()) { michael@0: container->GetTransformMatrix(nullptr, &parent); michael@0: rect = nsLayoutUtils::TransformFrameRectToAncestor(container, rect, parent); michael@0: } else { michael@0: rect += container->GetPosition(); michael@0: parent = container->GetParent(); michael@0: } michael@0: if (!parent && !(aFlags & nsIPresShell::SCROLL_NO_PARENT_FRAMES)) { michael@0: nsPoint extraOffset(0,0); michael@0: parent = nsLayoutUtils::GetCrossDocParentFrame(container, &extraOffset); michael@0: if (parent) { michael@0: int32_t APD = container->PresContext()->AppUnitsPerDevPixel(); michael@0: int32_t parentAPD = parent->PresContext()->AppUnitsPerDevPixel(); michael@0: rect = rect.ConvertAppUnitsRoundOut(APD, parentAPD); michael@0: rect += extraOffset; michael@0: } michael@0: } michael@0: container = parent; michael@0: } while (container); michael@0: michael@0: return didScroll; michael@0: } michael@0: michael@0: nsRectVisibility michael@0: PresShell::GetRectVisibility(nsIFrame* aFrame, michael@0: const nsRect &aRect, michael@0: nscoord aMinTwips) const michael@0: { michael@0: NS_ASSERTION(aFrame->PresContext() == GetPresContext(), michael@0: "prescontext mismatch?"); michael@0: nsIFrame* rootFrame = mFrameConstructor->GetRootFrame(); michael@0: NS_ASSERTION(rootFrame, michael@0: "How can someone have a frame for this presshell when there's no root?"); michael@0: nsIScrollableFrame* sf = GetRootScrollFrameAsScrollable(); michael@0: nsRect scrollPortRect; michael@0: if (sf) { michael@0: scrollPortRect = sf->GetScrollPortRect(); michael@0: nsIFrame* f = do_QueryFrame(sf); michael@0: scrollPortRect += f->GetOffsetTo(rootFrame); michael@0: } else { michael@0: scrollPortRect = nsRect(nsPoint(0,0), rootFrame->GetSize()); michael@0: } michael@0: michael@0: nsRect r = aRect + aFrame->GetOffsetTo(rootFrame); michael@0: // If aRect is entirely visible then we don't need to ensure that michael@0: // at least aMinTwips of it is visible michael@0: if (scrollPortRect.Contains(r)) michael@0: return nsRectVisibility_kVisible; michael@0: michael@0: nsRect insetRect = scrollPortRect; michael@0: insetRect.Deflate(aMinTwips, aMinTwips); michael@0: if (r.YMost() <= insetRect.y) michael@0: return nsRectVisibility_kAboveViewport; michael@0: if (r.y >= insetRect.YMost()) michael@0: return nsRectVisibility_kBelowViewport; michael@0: if (r.XMost() <= insetRect.x) michael@0: return nsRectVisibility_kLeftOfViewport; michael@0: if (r.x >= insetRect.XMost()) michael@0: return nsRectVisibility_kRightOfViewport; michael@0: michael@0: return nsRectVisibility_kVisible; michael@0: } michael@0: michael@0: class PaintTimerCallBack MOZ_FINAL : public nsITimerCallback michael@0: { michael@0: public: michael@0: PaintTimerCallBack(PresShell* aShell) : mShell(aShell) {} michael@0: michael@0: NS_DECL_ISUPPORTS michael@0: michael@0: NS_IMETHODIMP Notify(nsITimer* aTimer) MOZ_FINAL michael@0: { michael@0: mShell->SetNextPaintCompressed(); michael@0: mShell->AddInvalidateHiddenPresShellObserver(mShell->GetPresContext()->RefreshDriver()); michael@0: mShell->ScheduleViewManagerFlush(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: PresShell* mShell; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(PaintTimerCallBack, nsITimerCallback) michael@0: michael@0: void michael@0: PresShell::ScheduleViewManagerFlush(PaintType aType) michael@0: { michael@0: if (aType == PAINT_DELAYED_COMPRESS) { michael@0: // Delay paint for 1 second. michael@0: static const uint32_t kPaintDelayPeriod = 1000; michael@0: if (!mDelayedPaintTimer) { michael@0: mDelayedPaintTimer = do_CreateInstance(NS_TIMER_CONTRACTID); michael@0: nsRefPtr cb = new PaintTimerCallBack(this); michael@0: mDelayedPaintTimer->InitWithCallback(cb, kPaintDelayPeriod, nsITimer::TYPE_ONE_SHOT); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: nsPresContext* presContext = GetPresContext(); michael@0: if (presContext) { michael@0: presContext->RefreshDriver()->ScheduleViewManagerFlush(); michael@0: } michael@0: if (mDocument) { michael@0: mDocument->SetNeedLayoutFlush(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: PresShell::DispatchSynthMouseMove(WidgetGUIEvent* aEvent, michael@0: bool aFlushOnHoverChange) michael@0: { michael@0: RestyleManager* restyleManager = mPresContext->RestyleManager(); michael@0: uint32_t hoverGenerationBefore = restyleManager->GetHoverGeneration(); michael@0: nsEventStatus status; michael@0: nsView* targetView = nsView::GetViewFor(aEvent->widget); michael@0: if (!targetView) michael@0: return; michael@0: targetView->GetViewManager()->DispatchEvent(aEvent, targetView, &status); michael@0: if (MOZ_UNLIKELY(mIsDestroying)) { michael@0: return; michael@0: } michael@0: if (aFlushOnHoverChange && michael@0: hoverGenerationBefore != restyleManager->GetHoverGeneration()) { michael@0: // Flush so that the resulting reflow happens now so that our caller michael@0: // can suppress any synthesized mouse moves caused by that reflow. michael@0: FlushPendingNotifications(Flush_Layout); michael@0: } michael@0: } michael@0: michael@0: void michael@0: PresShell::ClearMouseCaptureOnView(nsView* aView) michael@0: { michael@0: if (gCaptureInfo.mContent) { michael@0: if (aView) { michael@0: // if a view was specified, ensure that the captured content is within michael@0: // this view. michael@0: nsIFrame* frame = gCaptureInfo.mContent->GetPrimaryFrame(); michael@0: if (frame) { michael@0: nsView* view = frame->GetClosestView(); michael@0: // if there is no view, capturing won't be handled any more, so michael@0: // just release the capture. michael@0: if (view) { michael@0: do { michael@0: if (view == aView) { michael@0: NS_RELEASE(gCaptureInfo.mContent); michael@0: // the view containing the captured content likely disappeared so michael@0: // disable capture for now. michael@0: gCaptureInfo.mAllowed = false; michael@0: break; michael@0: } michael@0: michael@0: view = view->GetParent(); michael@0: } while (view); michael@0: // return if the view wasn't found michael@0: return; michael@0: } michael@0: } michael@0: } michael@0: michael@0: NS_RELEASE(gCaptureInfo.mContent); michael@0: } michael@0: michael@0: // disable mouse capture until the next mousedown as a dialog has opened michael@0: // or a drag has started. Otherwise, someone could start capture during michael@0: // the modal dialog or drag. michael@0: gCaptureInfo.mAllowed = false; michael@0: } michael@0: michael@0: void michael@0: nsIPresShell::ClearMouseCapture(nsIFrame* aFrame) michael@0: { michael@0: if (!gCaptureInfo.mContent) { michael@0: gCaptureInfo.mAllowed = false; michael@0: return; michael@0: } michael@0: michael@0: // null frame argument means clear the capture michael@0: if (!aFrame) { michael@0: NS_RELEASE(gCaptureInfo.mContent); michael@0: gCaptureInfo.mAllowed = false; michael@0: return; michael@0: } michael@0: michael@0: nsIFrame* capturingFrame = gCaptureInfo.mContent->GetPrimaryFrame(); michael@0: if (!capturingFrame) { michael@0: NS_RELEASE(gCaptureInfo.mContent); michael@0: gCaptureInfo.mAllowed = false; michael@0: return; michael@0: } michael@0: michael@0: if (nsLayoutUtils::IsAncestorFrameCrossDoc(aFrame, capturingFrame)) { michael@0: NS_RELEASE(gCaptureInfo.mContent); michael@0: gCaptureInfo.mAllowed = false; michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: PresShell::CaptureHistoryState(nsILayoutHistoryState** aState) michael@0: { michael@0: NS_PRECONDITION(nullptr != aState, "null state pointer"); michael@0: michael@0: // We actually have to mess with the docshell here, since we want to michael@0: // store the state back in it. michael@0: // XXXbz this isn't really right, since this is being called in the michael@0: // content viewer's Hide() method... by that point the docshell's michael@0: // state could be wrong. We should sort out a better ownership michael@0: // model for the layout history state. michael@0: nsCOMPtr docShell(mPresContext->GetDocShell()); michael@0: if (!docShell) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsCOMPtr historyState; michael@0: docShell->GetLayoutHistoryState(getter_AddRefs(historyState)); michael@0: if (!historyState) { michael@0: // Create the document state object michael@0: historyState = NS_NewLayoutHistoryState(); michael@0: docShell->SetLayoutHistoryState(historyState); michael@0: } michael@0: michael@0: *aState = historyState; michael@0: NS_IF_ADDREF(*aState); michael@0: michael@0: // Capture frame state for the entire frame hierarchy michael@0: nsIFrame* rootFrame = mFrameConstructor->GetRootFrame(); michael@0: if (!rootFrame) return NS_OK; michael@0: michael@0: mFrameConstructor->CaptureFrameState(rootFrame, historyState); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: PresShell::UnsuppressAndInvalidate() michael@0: { michael@0: // Note: We ignore the EnsureVisible check for resource documents, because michael@0: // they won't have a docshell, so they'll always fail EnsureVisible. michael@0: if ((!mDocument->IsResourceDoc() && !mPresContext->EnsureVisible()) || michael@0: mHaveShutDown) { michael@0: // No point; we're about to be torn down anyway. michael@0: return; michael@0: } michael@0: michael@0: if (!mDocument->IsResourceDoc()) { michael@0: // Notify observers that a new page is about to be drawn. Execute this michael@0: // as soon as it is safe to run JS, which is guaranteed to be before we michael@0: // go back to the event loop and actually draw the page. michael@0: nsContentUtils::AddScriptRunner(new nsBeforeFirstPaintDispatcher(mDocument)); michael@0: } michael@0: michael@0: mPaintingSuppressed = false; michael@0: nsIFrame* rootFrame = mFrameConstructor->GetRootFrame(); michael@0: if (rootFrame) { michael@0: // let's assume that outline on a root frame is not supported michael@0: rootFrame->InvalidateFrame(); michael@0: michael@0: if (mCaretEnabled && mCaret) { michael@0: mCaret->CheckCaretDrawingState(); michael@0: } michael@0: } michael@0: michael@0: // now that painting is unsuppressed, focus may be set on the document michael@0: nsPIDOMWindow *win = mDocument->GetWindow(); michael@0: if (win) michael@0: win->SetReadyForFocus(); michael@0: michael@0: if (!mHaveShutDown) { michael@0: SynthesizeMouseMove(false); michael@0: ScheduleImageVisibilityUpdate(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: PresShell::UnsuppressPainting() michael@0: { michael@0: if (mPaintSuppressionTimer) { michael@0: mPaintSuppressionTimer->Cancel(); michael@0: mPaintSuppressionTimer = nullptr; michael@0: } michael@0: michael@0: if (mIsDocumentGone || !mPaintingSuppressed) michael@0: return; michael@0: michael@0: // If we have reflows pending, just wait until we process michael@0: // the reflows and get all the frames where we want them michael@0: // before actually unlocking the painting. Otherwise michael@0: // go ahead and unlock now. michael@0: if (!mDirtyRoots.IsEmpty()) michael@0: mShouldUnsuppressPainting = true; michael@0: else michael@0: UnsuppressAndInvalidate(); michael@0: } michael@0: michael@0: // Post a request to handle an arbitrary callback after reflow has finished. michael@0: nsresult michael@0: PresShell::PostReflowCallback(nsIReflowCallback* aCallback) michael@0: { michael@0: void* result = AllocateMisc(sizeof(nsCallbackEventRequest)); michael@0: nsCallbackEventRequest* request = (nsCallbackEventRequest*)result; michael@0: michael@0: request->callback = aCallback; michael@0: request->next = nullptr; michael@0: michael@0: if (mLastCallbackEventRequest) { michael@0: mLastCallbackEventRequest = mLastCallbackEventRequest->next = request; michael@0: } else { michael@0: mFirstCallbackEventRequest = request; michael@0: mLastCallbackEventRequest = request; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: PresShell::CancelReflowCallback(nsIReflowCallback* aCallback) michael@0: { michael@0: nsCallbackEventRequest* before = nullptr; michael@0: nsCallbackEventRequest* node = mFirstCallbackEventRequest; michael@0: while(node) michael@0: { michael@0: nsIReflowCallback* callback = node->callback; michael@0: michael@0: if (callback == aCallback) michael@0: { michael@0: nsCallbackEventRequest* toFree = node; michael@0: if (node == mFirstCallbackEventRequest) { michael@0: node = node->next; michael@0: mFirstCallbackEventRequest = node; michael@0: NS_ASSERTION(before == nullptr, "impossible"); michael@0: } else { michael@0: node = node->next; michael@0: before->next = node; michael@0: } michael@0: michael@0: if (toFree == mLastCallbackEventRequest) { michael@0: mLastCallbackEventRequest = before; michael@0: } michael@0: michael@0: FreeMisc(sizeof(nsCallbackEventRequest), toFree); michael@0: } else { michael@0: before = node; michael@0: node = node->next; michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: PresShell::CancelPostedReflowCallbacks() michael@0: { michael@0: while (mFirstCallbackEventRequest) { michael@0: nsCallbackEventRequest* node = mFirstCallbackEventRequest; michael@0: mFirstCallbackEventRequest = node->next; michael@0: if (!mFirstCallbackEventRequest) { michael@0: mLastCallbackEventRequest = nullptr; michael@0: } michael@0: nsIReflowCallback* callback = node->callback; michael@0: FreeMisc(sizeof(nsCallbackEventRequest), node); michael@0: if (callback) { michael@0: callback->ReflowCallbackCanceled(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: PresShell::HandlePostedReflowCallbacks(bool aInterruptible) michael@0: { michael@0: bool shouldFlush = false; michael@0: michael@0: while (mFirstCallbackEventRequest) { michael@0: nsCallbackEventRequest* node = mFirstCallbackEventRequest; michael@0: mFirstCallbackEventRequest = node->next; michael@0: if (!mFirstCallbackEventRequest) { michael@0: mLastCallbackEventRequest = nullptr; michael@0: } michael@0: nsIReflowCallback* callback = node->callback; michael@0: FreeMisc(sizeof(nsCallbackEventRequest), node); michael@0: if (callback) { michael@0: if (callback->ReflowFinished()) { michael@0: shouldFlush = true; michael@0: } michael@0: } michael@0: } michael@0: michael@0: mozFlushType flushType = michael@0: aInterruptible ? Flush_InterruptibleLayout : Flush_Layout; michael@0: if (shouldFlush && !mIsDestroying) { michael@0: FlushPendingNotifications(flushType); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: PresShell::IsSafeToFlush() const michael@0: { michael@0: // Not safe if we are reflowing or in the middle of frame construction michael@0: bool isSafeToFlush = !mIsReflowing && michael@0: !mChangeNestCount; michael@0: michael@0: if (isSafeToFlush) { michael@0: // Not safe if we are painting michael@0: nsViewManager* viewManager = GetViewManager(); michael@0: if (viewManager) { michael@0: bool isPainting = false; michael@0: viewManager->IsPainting(isPainting); michael@0: if (isPainting) { michael@0: isSafeToFlush = false; michael@0: } michael@0: } michael@0: } michael@0: michael@0: return isSafeToFlush; michael@0: } michael@0: michael@0: michael@0: void michael@0: PresShell::FlushPendingNotifications(mozFlushType aType) michael@0: { michael@0: // by default, flush animations if aType >= Flush_Style michael@0: mozilla::ChangesToFlush flush(aType, aType >= Flush_Style); michael@0: FlushPendingNotifications(flush); michael@0: } michael@0: michael@0: void michael@0: PresShell::FlushPendingNotifications(mozilla::ChangesToFlush aFlush) michael@0: { michael@0: if (mIsZombie) { michael@0: return; michael@0: } michael@0: michael@0: /** michael@0: * VERY IMPORTANT: If you add some sort of new flushing to this michael@0: * method, make sure to add the relevant SetNeedLayoutFlush or michael@0: * SetNeedStyleFlush calls on the document. michael@0: */ michael@0: mozFlushType flushType = aFlush.mFlushType; michael@0: michael@0: #ifdef MOZ_ENABLE_PROFILER_SPS michael@0: static const char flushTypeNames[][20] = { michael@0: "Content", michael@0: "ContentAndNotify", michael@0: "Style", michael@0: "InterruptibleLayout", michael@0: "Layout", michael@0: "Display" michael@0: }; michael@0: michael@0: // Make sure that we don't miss things added to mozFlushType! michael@0: MOZ_ASSERT(static_cast(flushType) <= ArrayLength(flushTypeNames)); michael@0: michael@0: PROFILER_LABEL_PRINTF("layout", "Flush", "(Flush_%s)", michael@0: flushTypeNames[flushType - 1]); michael@0: #endif michael@0: michael@0: #ifdef ACCESSIBILITY michael@0: #ifdef DEBUG michael@0: nsAccessibilityService* accService = GetAccService(); michael@0: if (accService) { michael@0: NS_ASSERTION(!accService->IsProcessingRefreshDriverNotification(), michael@0: "Flush during accessible tree update!"); michael@0: } michael@0: #endif michael@0: #endif michael@0: michael@0: NS_ASSERTION(flushType >= Flush_Frames, "Why did we get called?"); michael@0: michael@0: bool isSafeToFlush = IsSafeToFlush(); michael@0: michael@0: // If layout could possibly trigger scripts, then it's only safe to flush if michael@0: // it's safe to run script. michael@0: bool hasHadScriptObject; michael@0: if (mDocument->GetScriptHandlingObject(hasHadScriptObject) || michael@0: hasHadScriptObject) { michael@0: isSafeToFlush = isSafeToFlush && nsContentUtils::IsSafeToRunScript(); michael@0: } michael@0: michael@0: NS_ASSERTION(!isSafeToFlush || mViewManager, "Must have view manager"); michael@0: // Make sure the view manager stays alive. michael@0: nsRefPtr viewManagerDeathGrip = mViewManager; michael@0: if (isSafeToFlush && mViewManager) { michael@0: // Processing pending notifications can kill us, and some callers only michael@0: // hold weak refs when calling FlushPendingNotifications(). :( michael@0: nsCOMPtr kungFuDeathGrip(this); michael@0: michael@0: if (mResizeEvent.IsPending()) { michael@0: FireResizeEvent(); michael@0: if (mIsDestroying) { michael@0: return; michael@0: } michael@0: } michael@0: michael@0: // We need to make sure external resource documents are flushed too (for michael@0: // example, svg filters that reference a filter in an external document michael@0: // need the frames in the external document to be constructed for the michael@0: // filter to work). We only need external resources to be flushed when the michael@0: // main document is flushing >= Flush_Frames, so we flush external michael@0: // resources here instead of nsDocument::FlushPendingNotifications. michael@0: mDocument->FlushExternalResources(flushType); michael@0: michael@0: // Force flushing of any pending content notifications that might have michael@0: // queued up while our event was pending. That will ensure that we don't michael@0: // construct frames for content right now that's still waiting to be michael@0: // notified on, michael@0: mDocument->FlushPendingNotifications(Flush_ContentAndNotify); michael@0: michael@0: // Process pending restyles, since any flush of the presshell wants michael@0: // up-to-date style data. michael@0: if (!mIsDestroying) { michael@0: mViewManager->FlushDelayedResize(false); michael@0: mPresContext->FlushPendingMediaFeatureValuesChanged(); michael@0: michael@0: // Flush any pending update of the user font set, since that could michael@0: // cause style changes (for updating ex/ch units, and to cause a michael@0: // reflow). michael@0: mPresContext->FlushUserFontSet(); michael@0: michael@0: // Flush any requested SMIL samples. michael@0: if (mDocument->HasAnimationController()) { michael@0: mDocument->GetAnimationController()->FlushResampleRequests(); michael@0: } michael@0: michael@0: if (aFlush.mFlushAnimations && michael@0: nsLayoutUtils::AreAsyncAnimationsEnabled() && michael@0: !mPresContext->StyleUpdateForAllAnimationsIsUpToDate()) { michael@0: mPresContext->AnimationManager()-> michael@0: FlushAnimations(CommonAnimationManager::Cannot_Throttle); michael@0: mPresContext->TransitionManager()-> michael@0: FlushTransitions(CommonAnimationManager::Cannot_Throttle); michael@0: mPresContext->TickLastStyleUpdateForAllAnimations(); michael@0: } michael@0: michael@0: // The FlushResampleRequests() above flushed style changes. michael@0: if (!mIsDestroying) { michael@0: nsAutoScriptBlocker scriptBlocker; michael@0: mPresContext->RestyleManager()->ProcessPendingRestyles(); michael@0: } michael@0: } michael@0: michael@0: // Dispatch any 'animationstart' events those (or earlier) restyles michael@0: // queued up. michael@0: if (!mIsDestroying) { michael@0: mPresContext->AnimationManager()->DispatchEvents(); michael@0: } michael@0: michael@0: // Process whatever XBL constructors those restyles queued up. This michael@0: // ensures that onload doesn't fire too early and that we won't do extra michael@0: // reflows after those constructors run. michael@0: if (!mIsDestroying) { michael@0: mDocument->BindingManager()->ProcessAttachedQueue(); michael@0: } michael@0: michael@0: // Now those constructors or events might have posted restyle michael@0: // events. At the same time, we still need up-to-date style data. michael@0: // In particular, reflow depends on style being completely up to michael@0: // date. If it's not, then style context reparenting, which can michael@0: // happen during reflow, might suddenly pick up the new rules and michael@0: // we'll end up with frames whose style doesn't match the frame michael@0: // type. michael@0: if (!mIsDestroying) { michael@0: nsAutoScriptBlocker scriptBlocker; michael@0: mPresContext->RestyleManager()->ProcessPendingRestyles(); michael@0: } michael@0: michael@0: michael@0: // There might be more pending constructors now, but we're not going to michael@0: // worry about them. They can't be triggered during reflow, so we should michael@0: // be good. michael@0: michael@0: if (flushType >= (mSuppressInterruptibleReflows ? Flush_Layout : Flush_InterruptibleLayout) && michael@0: !mIsDestroying) { michael@0: mFrameConstructor->RecalcQuotesAndCounters(); michael@0: mViewManager->FlushDelayedResize(true); michael@0: if (ProcessReflowCommands(flushType < Flush_Layout) && mContentToScrollTo) { michael@0: // We didn't get interrupted. Go ahead and scroll to our content michael@0: DoScrollContentIntoView(); michael@0: if (mContentToScrollTo) { michael@0: mContentToScrollTo->DeleteProperty(nsGkAtoms::scrolling); michael@0: mContentToScrollTo = nullptr; michael@0: } michael@0: } michael@0: } else if (!mIsDestroying && mSuppressInterruptibleReflows && michael@0: flushType == Flush_InterruptibleLayout) { michael@0: // We suppressed this flush, but the document thinks it doesn't michael@0: // need to flush anymore. Let it know what's really going on. michael@0: mDocument->SetNeedLayoutFlush(); michael@0: } michael@0: michael@0: if (flushType >= Flush_Layout) { michael@0: if (!mIsDestroying) { michael@0: mViewManager->UpdateWidgetGeometry(); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: PresShell::CharacterDataWillChange(nsIDocument *aDocument, michael@0: nsIContent* aContent, michael@0: CharacterDataChangeInfo* aInfo) michael@0: { michael@0: NS_PRECONDITION(!mIsDocumentGone, "Unexpected CharacterDataChanged"); michael@0: NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument"); michael@0: michael@0: if (mCaret) { michael@0: // Invalidate the caret's current location before we call into the frame michael@0: // constructor. It is important to do this now, and not wait until the michael@0: // resulting reflow, because this call causes continuation frames of the michael@0: // text frame the caret is in to forget what part of the content they michael@0: // refer to, making it hard for them to return the correct continuation michael@0: // frame to the caret. michael@0: // michael@0: // It's also important to do this before the content actually changes, since michael@0: // in bidi text the caret needs to look at the content to determine its michael@0: // position and shape. michael@0: mCaret->InvalidateOutsideCaret(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: PresShell::CharacterDataChanged(nsIDocument *aDocument, michael@0: nsIContent* aContent, michael@0: CharacterDataChangeInfo* aInfo) michael@0: { michael@0: NS_PRECONDITION(!mIsDocumentGone, "Unexpected CharacterDataChanged"); michael@0: NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument"); michael@0: michael@0: nsAutoCauseReflowNotifier crNotifier(this); michael@0: michael@0: // Call this here so it only happens for real content mutations and michael@0: // not cases when the frame constructor calls its own methods to force michael@0: // frame reconstruction. michael@0: nsIContent *container = aContent->GetParent(); michael@0: uint32_t selectorFlags = michael@0: container ? (container->GetFlags() & NODE_ALL_SELECTOR_FLAGS) : 0; michael@0: if (selectorFlags != 0 && !aContent->IsRootOfAnonymousSubtree()) { michael@0: Element* element = container->AsElement(); michael@0: if (aInfo->mAppend && !aContent->GetNextSibling()) michael@0: mPresContext->RestyleManager()->RestyleForAppend(element, aContent); michael@0: else michael@0: mPresContext->RestyleManager()->RestyleForInsertOrChange(element, aContent); michael@0: } michael@0: michael@0: mFrameConstructor->CharacterDataChanged(aContent, aInfo); michael@0: VERIFY_STYLE_TREE; michael@0: } michael@0: michael@0: void michael@0: PresShell::ContentStateChanged(nsIDocument* aDocument, michael@0: nsIContent* aContent, michael@0: EventStates aStateMask) michael@0: { michael@0: NS_PRECONDITION(!mIsDocumentGone, "Unexpected ContentStateChanged"); michael@0: NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument"); michael@0: michael@0: if (mDidInitialize) { michael@0: nsAutoCauseReflowNotifier crNotifier(this); michael@0: mPresContext->RestyleManager()->ContentStateChanged(aContent, aStateMask); michael@0: VERIFY_STYLE_TREE; michael@0: } michael@0: } michael@0: michael@0: void michael@0: PresShell::DocumentStatesChanged(nsIDocument* aDocument, michael@0: EventStates aStateMask) michael@0: { michael@0: NS_PRECONDITION(!mIsDocumentGone, "Unexpected DocumentStatesChanged"); michael@0: NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument"); michael@0: michael@0: if (mDidInitialize && michael@0: mStyleSet->HasDocumentStateDependentStyle(mPresContext, michael@0: mDocument->GetRootElement(), michael@0: aStateMask)) { michael@0: mPresContext->RestyleManager()->PostRestyleEvent(mDocument->GetRootElement(), michael@0: eRestyle_Subtree, michael@0: NS_STYLE_HINT_NONE); michael@0: VERIFY_STYLE_TREE; michael@0: } michael@0: michael@0: if (aStateMask.HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE)) { michael@0: nsIFrame* root = mFrameConstructor->GetRootFrame(); michael@0: if (root) { michael@0: root->SchedulePaint(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: PresShell::AttributeWillChange(nsIDocument* aDocument, michael@0: Element* aElement, michael@0: int32_t aNameSpaceID, michael@0: nsIAtom* aAttribute, michael@0: int32_t aModType) michael@0: { michael@0: NS_PRECONDITION(!mIsDocumentGone, "Unexpected AttributeWillChange"); michael@0: NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument"); michael@0: michael@0: // XXXwaterson it might be more elegant to wait until after the michael@0: // initial reflow to begin observing the document. That would michael@0: // squelch any other inappropriate notifications as well. michael@0: if (mDidInitialize) { michael@0: nsAutoCauseReflowNotifier crNotifier(this); michael@0: mPresContext->RestyleManager()->AttributeWillChange(aElement, aNameSpaceID, michael@0: aAttribute, aModType); michael@0: VERIFY_STYLE_TREE; michael@0: } michael@0: } michael@0: michael@0: void michael@0: PresShell::AttributeChanged(nsIDocument* aDocument, michael@0: Element* aElement, michael@0: int32_t aNameSpaceID, michael@0: nsIAtom* aAttribute, michael@0: int32_t aModType) michael@0: { michael@0: NS_PRECONDITION(!mIsDocumentGone, "Unexpected AttributeChanged"); michael@0: NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument"); michael@0: michael@0: // XXXwaterson it might be more elegant to wait until after the michael@0: // initial reflow to begin observing the document. That would michael@0: // squelch any other inappropriate notifications as well. michael@0: if (mDidInitialize) { michael@0: nsAutoCauseReflowNotifier crNotifier(this); michael@0: mPresContext->RestyleManager()->AttributeChanged(aElement, aNameSpaceID, michael@0: aAttribute, aModType); michael@0: VERIFY_STYLE_TREE; michael@0: } michael@0: } michael@0: michael@0: void michael@0: PresShell::ContentAppended(nsIDocument *aDocument, michael@0: nsIContent* aContainer, michael@0: nsIContent* aFirstNewContent, michael@0: int32_t aNewIndexInContainer) michael@0: { michael@0: NS_PRECONDITION(!mIsDocumentGone, "Unexpected ContentAppended"); michael@0: NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument"); michael@0: NS_PRECONDITION(aContainer, "must have container"); michael@0: michael@0: if (!mDidInitialize) { michael@0: return; michael@0: } michael@0: michael@0: nsAutoCauseReflowNotifier crNotifier(this); michael@0: michael@0: // Call this here so it only happens for real content mutations and michael@0: // not cases when the frame constructor calls its own methods to force michael@0: // frame reconstruction. michael@0: if (aContainer->IsElement()) { michael@0: // Ensure the container is an element before trying to restyle michael@0: // because it can be the case that the container is a ShadowRoot michael@0: // which is a document fragment. michael@0: mPresContext->RestyleManager()-> michael@0: RestyleForAppend(aContainer->AsElement(), aFirstNewContent); michael@0: } michael@0: michael@0: mFrameConstructor->ContentAppended(aContainer, aFirstNewContent, true); michael@0: michael@0: if (static_cast(aContainer) == static_cast(aDocument) && michael@0: aFirstNewContent->NodeType() == nsIDOMNode::DOCUMENT_TYPE_NODE) { michael@0: NotifyFontSizeInflationEnabledIsDirty(); michael@0: } michael@0: michael@0: VERIFY_STYLE_TREE; michael@0: } michael@0: michael@0: void michael@0: PresShell::ContentInserted(nsIDocument* aDocument, michael@0: nsIContent* aContainer, michael@0: nsIContent* aChild, michael@0: int32_t aIndexInContainer) michael@0: { michael@0: NS_PRECONDITION(!mIsDocumentGone, "Unexpected ContentInserted"); michael@0: NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument"); michael@0: michael@0: if (!mDidInitialize) { michael@0: return; michael@0: } michael@0: michael@0: nsAutoCauseReflowNotifier crNotifier(this); michael@0: michael@0: // Call this here so it only happens for real content mutations and michael@0: // not cases when the frame constructor calls its own methods to force michael@0: // frame reconstruction. michael@0: if (aContainer && aContainer->IsElement()) { michael@0: // Ensure the container is an element before trying to restyle michael@0: // because it can be the case that the container is a ShadowRoot michael@0: // which is a document fragment. michael@0: mPresContext->RestyleManager()-> michael@0: RestyleForInsertOrChange(aContainer->AsElement(), aChild); michael@0: } michael@0: michael@0: mFrameConstructor->ContentInserted(aContainer, aChild, nullptr, true); michael@0: michael@0: if (((!aContainer && aDocument) || michael@0: (static_cast(aContainer) == static_cast(aDocument))) && michael@0: aChild->NodeType() == nsIDOMNode::DOCUMENT_TYPE_NODE) { michael@0: NotifyFontSizeInflationEnabledIsDirty(); michael@0: } michael@0: michael@0: VERIFY_STYLE_TREE; michael@0: } michael@0: michael@0: void michael@0: PresShell::ContentRemoved(nsIDocument *aDocument, michael@0: nsIContent* aContainer, michael@0: nsIContent* aChild, michael@0: int32_t aIndexInContainer, michael@0: nsIContent* aPreviousSibling) michael@0: { michael@0: NS_PRECONDITION(!mIsDocumentGone, "Unexpected ContentRemoved"); michael@0: NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument"); michael@0: michael@0: // Make sure that the caret doesn't leave a turd where the child used to be. michael@0: if (mCaret) { michael@0: mCaret->InvalidateOutsideCaret(); michael@0: } michael@0: michael@0: // Notify the ESM that the content has been removed, so that michael@0: // it can clean up any state related to the content. michael@0: michael@0: // XXX_jwir3: There is no null check for aDocument necessary, since, even michael@0: // though by nsIMutationObserver, aDocument could be null, the michael@0: // precondition check that mDocument == aDocument ensures that michael@0: // aDocument will not be null (since mDocument can't be null unless michael@0: // we're still intializing). michael@0: mPresContext->EventStateManager()->ContentRemoved(aDocument, aChild); michael@0: michael@0: nsAutoCauseReflowNotifier crNotifier(this); michael@0: michael@0: // Call this here so it only happens for real content mutations and michael@0: // not cases when the frame constructor calls its own methods to force michael@0: // frame reconstruction. michael@0: nsIContent* oldNextSibling; michael@0: if (aContainer) { michael@0: oldNextSibling = aContainer->GetChildAt(aIndexInContainer); michael@0: } else { michael@0: oldNextSibling = nullptr; michael@0: } michael@0: michael@0: if (aContainer && aContainer->IsElement()) { michael@0: mPresContext->RestyleManager()-> michael@0: RestyleForRemove(aContainer->AsElement(), aChild, oldNextSibling); michael@0: } michael@0: michael@0: bool didReconstruct; michael@0: mFrameConstructor->ContentRemoved(aContainer, aChild, oldNextSibling, michael@0: nsCSSFrameConstructor::REMOVE_CONTENT, michael@0: &didReconstruct); michael@0: michael@0: michael@0: if (((aContainer && michael@0: static_cast(aContainer) == static_cast(aDocument)) || michael@0: aDocument) && aChild->NodeType() == nsIDOMNode::DOCUMENT_TYPE_NODE) { michael@0: NotifyFontSizeInflationEnabledIsDirty(); michael@0: } michael@0: michael@0: VERIFY_STYLE_TREE; michael@0: } michael@0: michael@0: nsresult michael@0: PresShell::ReconstructFrames(void) michael@0: { michael@0: NS_PRECONDITION(!mFrameConstructor->GetRootFrame() || mDidInitialize, michael@0: "Must not have root frame before initial reflow"); michael@0: if (!mDidInitialize) { michael@0: // Nothing to do here michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsCOMPtr kungFuDeathGrip(this); michael@0: michael@0: // Have to make sure that the content notifications are flushed before we michael@0: // start messing with the frame model; otherwise we can get content doubling. michael@0: mDocument->FlushPendingNotifications(Flush_ContentAndNotify); michael@0: michael@0: nsAutoCauseReflowNotifier crNotifier(this); michael@0: mFrameConstructor->BeginUpdate(); michael@0: nsresult rv = mFrameConstructor->ReconstructDocElementHierarchy(); michael@0: VERIFY_STYLE_TREE; michael@0: mFrameConstructor->EndUpdate(); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: void michael@0: nsIPresShell::ReconstructStyleDataInternal() michael@0: { michael@0: nsAutoTArray,1> scopeRoots; michael@0: mChangedScopeStyleRoots.SwapElements(scopeRoots); michael@0: michael@0: if (mStylesHaveChanged) { michael@0: // If we need to restyle everything, no need to restyle individual michael@0: // scoped style roots. michael@0: scopeRoots.Clear(); michael@0: } michael@0: michael@0: mStylesHaveChanged = false; michael@0: michael@0: if (mIsDestroying) { michael@0: // We don't want to mess with restyles at this point michael@0: return; michael@0: } michael@0: michael@0: if (mPresContext) { michael@0: mPresContext->RebuildUserFontSet(); michael@0: } michael@0: michael@0: Element* root = mDocument->GetRootElement(); michael@0: if (!mDidInitialize) { michael@0: // Nothing to do here, since we have no frames yet michael@0: return; michael@0: } michael@0: michael@0: if (!root) { michael@0: // No content to restyle michael@0: return; michael@0: } michael@0: michael@0: RestyleManager* restyleManager = mPresContext->RestyleManager(); michael@0: if (scopeRoots.IsEmpty()) { michael@0: // If scopeRoots is empty, we know that mStylesHaveChanged was true at michael@0: // the beginning of this function, and that we need to restyle the whole michael@0: // document. michael@0: restyleManager->PostRestyleEvent(root, eRestyle_Subtree, michael@0: NS_STYLE_HINT_NONE); michael@0: } else { michael@0: for (uint32_t i = 0; i < scopeRoots.Length(); i++) { michael@0: Element* scopeRoot = scopeRoots[i]; michael@0: restyleManager->PostRestyleEvent(scopeRoot, eRestyle_Subtree, michael@0: NS_STYLE_HINT_NONE); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsIPresShell::ReconstructStyleDataExternal() michael@0: { michael@0: ReconstructStyleDataInternal(); michael@0: } michael@0: michael@0: void michael@0: PresShell::RecordStyleSheetChange(nsIStyleSheet* aStyleSheet) michael@0: { michael@0: if (mStylesHaveChanged) michael@0: return; michael@0: michael@0: nsRefPtr cssStyleSheet = do_QueryObject(aStyleSheet); michael@0: if (cssStyleSheet) { michael@0: Element* scopeElement = cssStyleSheet->GetScopeElement(); michael@0: if (scopeElement) { michael@0: mChangedScopeStyleRoots.AppendElement(scopeElement); michael@0: return; michael@0: } michael@0: } michael@0: michael@0: mStylesHaveChanged = true; michael@0: } michael@0: michael@0: void michael@0: PresShell::StyleSheetAdded(nsIDocument *aDocument, michael@0: nsIStyleSheet* aStyleSheet, michael@0: bool aDocumentSheet) michael@0: { michael@0: // We only care when enabled sheets are added michael@0: NS_PRECONDITION(aStyleSheet, "Must have a style sheet!"); michael@0: michael@0: if (aStyleSheet->IsApplicable() && aStyleSheet->HasRules()) { michael@0: RecordStyleSheetChange(aStyleSheet); michael@0: } michael@0: } michael@0: michael@0: void michael@0: PresShell::StyleSheetRemoved(nsIDocument *aDocument, michael@0: nsIStyleSheet* aStyleSheet, michael@0: bool aDocumentSheet) michael@0: { michael@0: // We only care when enabled sheets are removed michael@0: NS_PRECONDITION(aStyleSheet, "Must have a style sheet!"); michael@0: michael@0: if (aStyleSheet->IsApplicable() && aStyleSheet->HasRules()) { michael@0: RecordStyleSheetChange(aStyleSheet); michael@0: } michael@0: } michael@0: michael@0: void michael@0: PresShell::StyleSheetApplicableStateChanged(nsIDocument *aDocument, michael@0: nsIStyleSheet* aStyleSheet, michael@0: bool aApplicable) michael@0: { michael@0: if (aStyleSheet->HasRules()) { michael@0: RecordStyleSheetChange(aStyleSheet); michael@0: } michael@0: } michael@0: michael@0: void michael@0: PresShell::StyleRuleChanged(nsIDocument *aDocument, michael@0: nsIStyleSheet* aStyleSheet, michael@0: nsIStyleRule* aOldStyleRule, michael@0: nsIStyleRule* aNewStyleRule) michael@0: { michael@0: RecordStyleSheetChange(aStyleSheet); michael@0: } michael@0: michael@0: void michael@0: PresShell::StyleRuleAdded(nsIDocument *aDocument, michael@0: nsIStyleSheet* aStyleSheet, michael@0: nsIStyleRule* aStyleRule) michael@0: { michael@0: RecordStyleSheetChange(aStyleSheet); michael@0: } michael@0: michael@0: void michael@0: PresShell::StyleRuleRemoved(nsIDocument *aDocument, michael@0: nsIStyleSheet* aStyleSheet, michael@0: nsIStyleRule* aStyleRule) michael@0: { michael@0: RecordStyleSheetChange(aStyleSheet); michael@0: } michael@0: michael@0: nsIFrame* michael@0: PresShell::GetRealPrimaryFrameFor(nsIContent* aContent) const michael@0: { michael@0: if (aContent->GetDocument() != GetDocument()) { michael@0: return nullptr; michael@0: } michael@0: nsIFrame *primaryFrame = aContent->GetPrimaryFrame(); michael@0: if (!primaryFrame) michael@0: return nullptr; michael@0: return nsPlaceholderFrame::GetRealFrameFor(primaryFrame); michael@0: } michael@0: michael@0: nsIFrame* michael@0: PresShell::GetPlaceholderFrameFor(nsIFrame* aFrame) const michael@0: { michael@0: return mFrameConstructor->GetPlaceholderFrameFor(aFrame); michael@0: } michael@0: michael@0: nsresult michael@0: PresShell::RenderDocument(const nsRect& aRect, uint32_t aFlags, michael@0: nscolor aBackgroundColor, michael@0: gfxContext* aThebesContext) michael@0: { michael@0: NS_ENSURE_TRUE(!(aFlags & RENDER_IS_UNTRUSTED), NS_ERROR_NOT_IMPLEMENTED); michael@0: michael@0: nsRootPresContext* rootPresContext = mPresContext->GetRootPresContext(); michael@0: if (rootPresContext) { michael@0: rootPresContext->FlushWillPaintObservers(); michael@0: if (mIsDestroying) michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsAutoScriptBlocker blockScripts; michael@0: michael@0: // Set up the rectangle as the path in aThebesContext michael@0: gfxRect r(0, 0, michael@0: nsPresContext::AppUnitsToFloatCSSPixels(aRect.width), michael@0: nsPresContext::AppUnitsToFloatCSSPixels(aRect.height)); michael@0: aThebesContext->NewPath(); michael@0: #ifdef MOZ_GFX_OPTIMIZE_MOBILE michael@0: aThebesContext->Rectangle(r, true); michael@0: #else michael@0: aThebesContext->Rectangle(r); michael@0: #endif michael@0: michael@0: nsIFrame* rootFrame = mFrameConstructor->GetRootFrame(); michael@0: if (!rootFrame) { michael@0: // Nothing to paint, just fill the rect michael@0: aThebesContext->SetColor(gfxRGBA(aBackgroundColor)); michael@0: aThebesContext->Fill(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: gfxContextAutoSaveRestore save(aThebesContext); michael@0: michael@0: gfxContext::GraphicsOperator oldOperator = aThebesContext->CurrentOperator(); michael@0: if (oldOperator == gfxContext::OPERATOR_OVER) { michael@0: // Clip to the destination rectangle before we push the group, michael@0: // to limit the size of the temporary surface michael@0: aThebesContext->Clip(); michael@0: } michael@0: michael@0: // we want the window to be composited as a single image using michael@0: // whatever operator was set; set OPERATOR_OVER here, which is michael@0: // either already the case, or overrides the operator in a group. michael@0: // the original operator will be present when we PopGroup. michael@0: // we can avoid using a temporary surface if we're using OPERATOR_OVER michael@0: bool needsGroup = oldOperator != gfxContext::OPERATOR_OVER; michael@0: michael@0: if (needsGroup) { michael@0: aThebesContext->PushGroup(NS_GET_A(aBackgroundColor) == 0xff ? michael@0: gfxContentType::COLOR : michael@0: gfxContentType::COLOR_ALPHA); michael@0: aThebesContext->Save(); michael@0: michael@0: if (oldOperator != gfxContext::OPERATOR_OVER) { michael@0: // Clip now while we paint to the temporary surface. For michael@0: // non-source-bounded operators (e.g., SOURCE), we need to do clip michael@0: // here after we've pushed the group, so that eventually popping michael@0: // the group and painting it will be able to clear the entire michael@0: // destination surface. michael@0: aThebesContext->Clip(); michael@0: aThebesContext->SetOperator(gfxContext::OPERATOR_OVER); michael@0: } michael@0: } michael@0: michael@0: aThebesContext->Translate(gfxPoint(-nsPresContext::AppUnitsToFloatCSSPixels(aRect.x), michael@0: -nsPresContext::AppUnitsToFloatCSSPixels(aRect.y))); michael@0: michael@0: nsDeviceContext* devCtx = mPresContext->DeviceContext(); michael@0: gfxFloat scale = gfxFloat(devCtx->AppUnitsPerDevPixel())/nsPresContext::AppUnitsPerCSSPixel(); michael@0: aThebesContext->Scale(scale, scale); michael@0: michael@0: // Since canvas APIs use floats to set up their matrices, we may have michael@0: // some slight inaccuracy here. Adjust matrix components that are michael@0: // integers up to the accuracy of floats to be those integers. michael@0: aThebesContext->NudgeCurrentMatrixToIntegers(); michael@0: michael@0: AutoSaveRestoreRenderingState _(this); michael@0: michael@0: nsRefPtr rc = new nsRenderingContext(); michael@0: rc->Init(devCtx, aThebesContext); michael@0: michael@0: bool wouldFlushRetainedLayers = false; michael@0: uint32_t flags = nsLayoutUtils::PAINT_IGNORE_SUPPRESSION; michael@0: if (aThebesContext->CurrentMatrix().HasNonIntegerTranslation()) { michael@0: flags |= nsLayoutUtils::PAINT_IN_TRANSFORM; michael@0: } michael@0: if (!(aFlags & RENDER_ASYNC_DECODE_IMAGES)) { michael@0: flags |= nsLayoutUtils::PAINT_SYNC_DECODE_IMAGES; michael@0: } michael@0: if (aFlags & RENDER_USE_WIDGET_LAYERS) { michael@0: // We only support using widget layers on display root's with widgets. michael@0: nsView* view = rootFrame->GetView(); michael@0: if (view && view->GetWidget() && michael@0: nsLayoutUtils::GetDisplayRootFrame(rootFrame) == rootFrame) { michael@0: flags |= nsLayoutUtils::PAINT_WIDGET_LAYERS; michael@0: } michael@0: } michael@0: if (!(aFlags & RENDER_CARET)) { michael@0: wouldFlushRetainedLayers = true; michael@0: flags |= nsLayoutUtils::PAINT_HIDE_CARET; michael@0: } michael@0: if (aFlags & RENDER_IGNORE_VIEWPORT_SCROLLING) { michael@0: wouldFlushRetainedLayers = !IgnoringViewportScrolling(); michael@0: mRenderFlags = ChangeFlag(mRenderFlags, true, STATE_IGNORING_VIEWPORT_SCROLLING); michael@0: } michael@0: if (aFlags & RENDER_DRAWWINDOW_NOT_FLUSHING) { michael@0: mRenderFlags = ChangeFlag(mRenderFlags, true, STATE_DRAWWINDOW_NOT_FLUSHING); michael@0: } michael@0: if (aFlags & RENDER_DOCUMENT_RELATIVE) { michael@0: // XXX be smarter about this ... drawWindow might want a rect michael@0: // that's "pretty close" to what our retained layer tree covers. michael@0: // In that case, it wouldn't disturb normal rendering too much, michael@0: // and we should allow it. michael@0: wouldFlushRetainedLayers = true; michael@0: flags |= nsLayoutUtils::PAINT_DOCUMENT_RELATIVE; michael@0: } michael@0: michael@0: // Don't let drawWindow blow away our retained layer tree michael@0: if ((flags & nsLayoutUtils::PAINT_WIDGET_LAYERS) && wouldFlushRetainedLayers) { michael@0: flags &= ~nsLayoutUtils::PAINT_WIDGET_LAYERS; michael@0: } michael@0: michael@0: nsLayoutUtils::PaintFrame(rc, rootFrame, nsRegion(aRect), michael@0: aBackgroundColor, flags); michael@0: michael@0: // if we had to use a group, paint it to the destination now michael@0: if (needsGroup) { michael@0: aThebesContext->Restore(); michael@0: aThebesContext->PopGroupToSource(); michael@0: aThebesContext->Paint(); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* michael@0: * Clip the display list aList to a range. Returns the clipped michael@0: * rectangle surrounding the range. michael@0: */ michael@0: nsRect michael@0: PresShell::ClipListToRange(nsDisplayListBuilder *aBuilder, michael@0: nsDisplayList* aList, michael@0: nsRange* aRange) michael@0: { michael@0: // iterate though the display items and add up the bounding boxes of each. michael@0: // This will allow the total area of the frames within the range to be michael@0: // determined. To do this, remove an item from the bottom of the list, check michael@0: // whether it should be part of the range, and if so, append it to the top michael@0: // of the temporary list tmpList. If the item is a text frame at the end of michael@0: // the selection range, clip it to the portion of the text frame that is michael@0: // part of the selection. Then, append the wrapper to the top of the list. michael@0: // Otherwise, just delete the item and don't append it. michael@0: nsRect surfaceRect; michael@0: nsDisplayList tmpList; michael@0: michael@0: nsDisplayItem* i; michael@0: while ((i = aList->RemoveBottom())) { michael@0: // itemToInsert indiciates the item that should be inserted into the michael@0: // temporary list. If null, no item should be inserted. michael@0: nsDisplayItem* itemToInsert = nullptr; michael@0: nsIFrame* frame = i->Frame(); michael@0: nsIContent* content = frame->GetContent(); michael@0: if (content) { michael@0: bool atStart = (content == aRange->GetStartParent()); michael@0: bool atEnd = (content == aRange->GetEndParent()); michael@0: if ((atStart || atEnd) && frame->GetType() == nsGkAtoms::textFrame) { michael@0: int32_t frameStartOffset, frameEndOffset; michael@0: frame->GetOffsets(frameStartOffset, frameEndOffset); michael@0: michael@0: int32_t hilightStart = michael@0: atStart ? std::max(aRange->StartOffset(), frameStartOffset) : frameStartOffset; michael@0: int32_t hilightEnd = michael@0: atEnd ? std::min(aRange->EndOffset(), frameEndOffset) : frameEndOffset; michael@0: if (hilightStart < hilightEnd) { michael@0: // determine the location of the start and end edges of the range. michael@0: nsPoint startPoint, endPoint; michael@0: frame->GetPointFromOffset(hilightStart, &startPoint); michael@0: frame->GetPointFromOffset(hilightEnd, &endPoint); michael@0: michael@0: // the clip rectangle is determined by taking the the start and michael@0: // end points of the range, offset from the reference frame. michael@0: // Because of rtl, the end point may be to the left of the michael@0: // start point, so x is set to the lowest value michael@0: nsRect textRect(aBuilder->ToReferenceFrame(frame), frame->GetSize()); michael@0: nscoord x = std::min(startPoint.x, endPoint.x); michael@0: textRect.x += x; michael@0: textRect.width = std::max(startPoint.x, endPoint.x) - x; michael@0: surfaceRect.UnionRect(surfaceRect, textRect); michael@0: michael@0: DisplayItemClip newClip; michael@0: newClip.SetTo(textRect); michael@0: newClip.IntersectWith(i->GetClip()); michael@0: i->SetClip(aBuilder, newClip); michael@0: itemToInsert = i; michael@0: } michael@0: } michael@0: // Don't try to descend into subdocuments. michael@0: // If this ever changes we'd need to add handling for subdocuments with michael@0: // different zoom levels. michael@0: else if (content->GetCurrentDoc() == michael@0: aRange->GetStartParent()->GetCurrentDoc()) { michael@0: // if the node is within the range, append it to the temporary list michael@0: bool before, after; michael@0: nsresult rv = michael@0: nsRange::CompareNodeToRange(content, aRange, &before, &after); michael@0: if (NS_SUCCEEDED(rv) && !before && !after) { michael@0: itemToInsert = i; michael@0: bool snap; michael@0: surfaceRect.UnionRect(surfaceRect, i->GetBounds(aBuilder, &snap)); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // insert the item into the list if necessary. If the item has a child michael@0: // list, insert that as well michael@0: nsDisplayList* sublist = i->GetSameCoordinateSystemChildren(); michael@0: if (itemToInsert || sublist) { michael@0: tmpList.AppendToTop(itemToInsert ? itemToInsert : i); michael@0: // if the item is a list, iterate over it as well michael@0: if (sublist) michael@0: surfaceRect.UnionRect(surfaceRect, michael@0: ClipListToRange(aBuilder, sublist, aRange)); michael@0: } michael@0: else { michael@0: // otherwise, just delete the item and don't readd it to the list michael@0: i->~nsDisplayItem(); michael@0: } michael@0: } michael@0: michael@0: // now add all the items back onto the original list again michael@0: aList->AppendToTop(&tmpList); michael@0: michael@0: return surfaceRect; michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: #include michael@0: michael@0: static bool gDumpRangePaintList = false; michael@0: #endif michael@0: michael@0: RangePaintInfo* michael@0: PresShell::CreateRangePaintInfo(nsIDOMRange* aRange, michael@0: nsRect& aSurfaceRect, michael@0: bool aForPrimarySelection) michael@0: { michael@0: RangePaintInfo* info = nullptr; michael@0: michael@0: nsRange* range = static_cast(aRange); michael@0: michael@0: nsIFrame* ancestorFrame; michael@0: nsIFrame* rootFrame = GetRootFrame(); michael@0: michael@0: // If the start or end of the range is the document, just use the root michael@0: // frame, otherwise get the common ancestor of the two endpoints of the michael@0: // range. michael@0: nsINode* startParent = range->GetStartParent(); michael@0: nsINode* endParent = range->GetEndParent(); michael@0: nsIDocument* doc = startParent->GetCurrentDoc(); michael@0: if (startParent == doc || endParent == doc) { michael@0: ancestorFrame = rootFrame; michael@0: } michael@0: else { michael@0: nsINode* ancestor = nsContentUtils::GetCommonAncestor(startParent, endParent); michael@0: NS_ASSERTION(!ancestor || ancestor->IsNodeOfType(nsINode::eCONTENT), michael@0: "common ancestor is not content"); michael@0: if (!ancestor || !ancestor->IsNodeOfType(nsINode::eCONTENT)) michael@0: return nullptr; michael@0: michael@0: nsIContent* ancestorContent = static_cast(ancestor); michael@0: ancestorFrame = ancestorContent->GetPrimaryFrame(); michael@0: michael@0: // use the nearest ancestor frame that includes all continuations as the michael@0: // root for building the display list michael@0: while (ancestorFrame && michael@0: nsLayoutUtils::GetNextContinuationOrIBSplitSibling(ancestorFrame)) michael@0: ancestorFrame = ancestorFrame->GetParent(); michael@0: } michael@0: michael@0: if (!ancestorFrame) michael@0: return nullptr; michael@0: michael@0: info = new RangePaintInfo(range, ancestorFrame); michael@0: michael@0: nsRect ancestorRect = ancestorFrame->GetVisualOverflowRect(); michael@0: michael@0: // get a display list containing the range michael@0: info->mBuilder.SetIncludeAllOutOfFlows(); michael@0: if (aForPrimarySelection) { michael@0: info->mBuilder.SetSelectedFramesOnly(); michael@0: } michael@0: info->mBuilder.EnterPresShell(ancestorFrame, ancestorRect); michael@0: ancestorFrame->BuildDisplayListForStackingContext(&info->mBuilder, michael@0: ancestorRect, &info->mList); michael@0: michael@0: #ifdef DEBUG michael@0: if (gDumpRangePaintList) { michael@0: fprintf(stderr, "CreateRangePaintInfo --- before ClipListToRange:\n"); michael@0: nsFrame::PrintDisplayList(&(info->mBuilder), info->mList); michael@0: } michael@0: #endif michael@0: michael@0: nsRect rangeRect = ClipListToRange(&info->mBuilder, &info->mList, range); michael@0: michael@0: info->mBuilder.LeavePresShell(ancestorFrame, ancestorRect); michael@0: michael@0: #ifdef DEBUG michael@0: if (gDumpRangePaintList) { michael@0: fprintf(stderr, "CreateRangePaintInfo --- after ClipListToRange:\n"); michael@0: nsFrame::PrintDisplayList(&(info->mBuilder), info->mList); michael@0: } michael@0: #endif michael@0: michael@0: // determine the offset of the reference frame for the display list michael@0: // to the root frame. This will allow the coordinates used when painting michael@0: // to all be offset from the same point michael@0: info->mRootOffset = ancestorFrame->GetOffsetTo(rootFrame); michael@0: rangeRect.MoveBy(info->mRootOffset); michael@0: aSurfaceRect.UnionRect(aSurfaceRect, rangeRect); michael@0: michael@0: return info; michael@0: } michael@0: michael@0: TemporaryRef michael@0: PresShell::PaintRangePaintInfo(nsTArray >* aItems, michael@0: nsISelection* aSelection, michael@0: nsIntRegion* aRegion, michael@0: nsRect aArea, michael@0: nsIntPoint& aPoint, michael@0: nsIntRect* aScreenRect) michael@0: { michael@0: nsPresContext* pc = GetPresContext(); michael@0: if (!pc || aArea.width == 0 || aArea.height == 0) michael@0: return nullptr; michael@0: michael@0: nsDeviceContext* deviceContext = pc->DeviceContext(); michael@0: michael@0: // use the rectangle to create the surface michael@0: nsIntRect pixelArea = aArea.ToOutsidePixels(pc->AppUnitsPerDevPixel()); michael@0: michael@0: // if the area of the image is larger than the maximum area, scale it down michael@0: float scale = 0.0; michael@0: nsIntRect rootScreenRect = michael@0: GetRootFrame()->GetScreenRectInAppUnits().ToNearestPixels( michael@0: pc->AppUnitsPerDevPixel()); michael@0: michael@0: // if the image is larger in one or both directions than half the size of michael@0: // the available screen area, scale the image down to that size. michael@0: nsRect maxSize; michael@0: deviceContext->GetClientRect(maxSize); michael@0: nscoord maxWidth = pc->AppUnitsToDevPixels(maxSize.width >> 1); michael@0: nscoord maxHeight = pc->AppUnitsToDevPixels(maxSize.height >> 1); michael@0: bool resize = (pixelArea.width > maxWidth || pixelArea.height > maxHeight); michael@0: if (resize) { michael@0: scale = 1.0; michael@0: // divide the maximum size by the image size in both directions. Whichever michael@0: // direction produces the smallest result determines how much should be michael@0: // scaled. michael@0: if (pixelArea.width > maxWidth) michael@0: scale = std::min(scale, float(maxWidth) / pixelArea.width); michael@0: if (pixelArea.height > maxHeight) michael@0: scale = std::min(scale, float(maxHeight) / pixelArea.height); michael@0: michael@0: pixelArea.width = NSToIntFloor(float(pixelArea.width) * scale); michael@0: pixelArea.height = NSToIntFloor(float(pixelArea.height) * scale); michael@0: michael@0: // adjust the screen position based on the rescaled size michael@0: nscoord left = rootScreenRect.x + pixelArea.x; michael@0: nscoord top = rootScreenRect.y + pixelArea.y; michael@0: aScreenRect->x = NSToIntFloor(aPoint.x - float(aPoint.x - left) * scale); michael@0: aScreenRect->y = NSToIntFloor(aPoint.y - float(aPoint.y - top) * scale); michael@0: } michael@0: else { michael@0: // move aScreenRect to the position of the surface in screen coordinates michael@0: aScreenRect->MoveTo(rootScreenRect.x + pixelArea.x, rootScreenRect.y + pixelArea.y); michael@0: } michael@0: aScreenRect->width = pixelArea.width; michael@0: aScreenRect->height = pixelArea.height; michael@0: michael@0: RefPtr dt = michael@0: gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget( michael@0: IntSize(pixelArea.width, pixelArea.height), michael@0: SurfaceFormat::B8G8R8A8); michael@0: if (!dt) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr ctx = new gfxContext(dt); michael@0: nsRefPtr rc = new nsRenderingContext(); michael@0: rc->Init(deviceContext, ctx); michael@0: michael@0: if (aRegion) { michael@0: // Convert aRegion from CSS pixels to dev pixels michael@0: nsIntRegion region = michael@0: aRegion->ToAppUnits(nsPresContext::AppUnitsPerCSSPixel()) michael@0: .ToOutsidePixels(pc->AppUnitsPerDevPixel()); michael@0: rc->SetClip(region); michael@0: } michael@0: michael@0: if (resize) michael@0: rc->Scale(scale, scale); michael@0: michael@0: // translate so that points are relative to the surface area michael@0: rc->Translate(-aArea.TopLeft()); michael@0: michael@0: // temporarily hide the selection so that text is drawn normally. If a michael@0: // selection is being rendered, use that, otherwise use the presshell's michael@0: // selection. michael@0: nsRefPtr frameSelection; michael@0: if (aSelection) { michael@0: frameSelection = static_cast(aSelection)->GetFrameSelection(); michael@0: } michael@0: else { michael@0: frameSelection = FrameSelection(); michael@0: } michael@0: int16_t oldDisplaySelection = frameSelection->GetDisplaySelection(); michael@0: frameSelection->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN); michael@0: michael@0: // next, paint each range in the selection michael@0: int32_t count = aItems->Length(); michael@0: for (int32_t i = 0; i < count; i++) { michael@0: RangePaintInfo* rangeInfo = (*aItems)[i]; michael@0: // the display lists paint relative to the offset from the reference michael@0: // frame, so translate the rendering context michael@0: nsRenderingContext::AutoPushTranslation michael@0: translate(rc, rangeInfo->mRootOffset); michael@0: michael@0: aArea.MoveBy(-rangeInfo->mRootOffset.x, -rangeInfo->mRootOffset.y); michael@0: nsRegion visible(aArea); michael@0: rangeInfo->mList.ComputeVisibilityForRoot(&rangeInfo->mBuilder, &visible); michael@0: rangeInfo->mList.PaintRoot(&rangeInfo->mBuilder, rc, nsDisplayList::PAINT_DEFAULT); michael@0: aArea.MoveBy(rangeInfo->mRootOffset.x, rangeInfo->mRootOffset.y); michael@0: } michael@0: michael@0: // restore the old selection display state michael@0: frameSelection->SetDisplaySelection(oldDisplaySelection); michael@0: michael@0: return dt->Snapshot(); michael@0: } michael@0: michael@0: TemporaryRef michael@0: PresShell::RenderNode(nsIDOMNode* aNode, michael@0: nsIntRegion* aRegion, michael@0: nsIntPoint& aPoint, michael@0: nsIntRect* aScreenRect) michael@0: { michael@0: // area will hold the size of the surface needed to draw the node, measured michael@0: // from the root frame. michael@0: nsRect area; michael@0: nsTArray > rangeItems; michael@0: michael@0: // nothing to draw if the node isn't in a document michael@0: nsCOMPtr node = do_QueryInterface(aNode); michael@0: if (!node->IsInDoc()) michael@0: return nullptr; michael@0: michael@0: nsRefPtr range = new nsRange(node); michael@0: if (NS_FAILED(range->SelectNode(aNode))) michael@0: return nullptr; michael@0: michael@0: RangePaintInfo* info = CreateRangePaintInfo(range, area, false); michael@0: if (info && !rangeItems.AppendElement(info)) { michael@0: delete info; michael@0: return nullptr; michael@0: } michael@0: michael@0: if (aRegion) { michael@0: // combine the area with the supplied region michael@0: nsIntRect rrectPixels = aRegion->GetBounds(); michael@0: michael@0: nsRect rrect = rrectPixels.ToAppUnits(nsPresContext::AppUnitsPerCSSPixel()); michael@0: area.IntersectRect(area, rrect); michael@0: michael@0: nsPresContext* pc = GetPresContext(); michael@0: if (!pc) michael@0: return nullptr; michael@0: michael@0: // move the region so that it is offset from the topleft corner of the surface michael@0: aRegion->MoveBy(-pc->AppUnitsToDevPixels(area.x), michael@0: -pc->AppUnitsToDevPixels(area.y)); michael@0: } michael@0: michael@0: return PaintRangePaintInfo(&rangeItems, nullptr, aRegion, area, aPoint, michael@0: aScreenRect); michael@0: } michael@0: michael@0: TemporaryRef michael@0: PresShell::RenderSelection(nsISelection* aSelection, michael@0: nsIntPoint& aPoint, michael@0: nsIntRect* aScreenRect) michael@0: { michael@0: // area will hold the size of the surface needed to draw the selection, michael@0: // measured from the root frame. michael@0: nsRect area; michael@0: nsTArray > rangeItems; michael@0: michael@0: // iterate over each range and collect them into the rangeItems array. michael@0: // This is done so that the size of selection can be determined so as michael@0: // to allocate a surface area michael@0: int32_t numRanges; michael@0: aSelection->GetRangeCount(&numRanges); michael@0: NS_ASSERTION(numRanges > 0, "RenderSelection called with no selection"); michael@0: michael@0: for (int32_t r = 0; r < numRanges; r++) michael@0: { michael@0: nsCOMPtr range; michael@0: aSelection->GetRangeAt(r, getter_AddRefs(range)); michael@0: michael@0: RangePaintInfo* info = CreateRangePaintInfo(range, area, true); michael@0: if (info && !rangeItems.AppendElement(info)) { michael@0: delete info; michael@0: return nullptr; michael@0: } michael@0: } michael@0: michael@0: return PaintRangePaintInfo(&rangeItems, aSelection, nullptr, area, aPoint, michael@0: aScreenRect); michael@0: } michael@0: michael@0: void michael@0: PresShell::AddPrintPreviewBackgroundItem(nsDisplayListBuilder& aBuilder, michael@0: nsDisplayList& aList, michael@0: nsIFrame* aFrame, michael@0: const nsRect& aBounds) michael@0: { michael@0: aList.AppendNewToBottom(new (&aBuilder) michael@0: nsDisplaySolidColor(&aBuilder, aFrame, aBounds, NS_RGB(115, 115, 115))); michael@0: } michael@0: michael@0: static bool michael@0: AddCanvasBackgroundColor(const nsDisplayList& aList, nsIFrame* aCanvasFrame, michael@0: nscolor aColor) michael@0: { michael@0: for (nsDisplayItem* i = aList.GetBottom(); i; i = i->GetAbove()) { michael@0: if (i->Frame() == aCanvasFrame && michael@0: i->GetType() == nsDisplayItem::TYPE_CANVAS_BACKGROUND_COLOR) { michael@0: nsDisplayCanvasBackgroundColor* bg = static_cast(i); michael@0: bg->SetExtraBackgroundColor(aColor); michael@0: return true; michael@0: } michael@0: nsDisplayList* sublist = i->GetSameCoordinateSystemChildren(); michael@0: if (sublist && AddCanvasBackgroundColor(*sublist, aCanvasFrame, aColor)) michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: void michael@0: PresShell::AddCanvasBackgroundColorItem(nsDisplayListBuilder& aBuilder, michael@0: nsDisplayList& aList, michael@0: nsIFrame* aFrame, michael@0: const nsRect& aBounds, michael@0: nscolor aBackstopColor, michael@0: uint32_t aFlags) michael@0: { michael@0: if (aBounds.IsEmpty()) { michael@0: return; michael@0: } michael@0: // We don't want to add an item for the canvas background color if the frame michael@0: // (sub)tree we are painting doesn't include any canvas frames. There isn't michael@0: // an easy way to check this directly, but if we check if the root of the michael@0: // (sub)tree we are painting is a canvas frame that should cover us in all michael@0: // cases (it will usually be a viewport frame when we have a canvas frame in michael@0: // the (sub)tree). michael@0: if (!(aFlags & nsIPresShell::FORCE_DRAW) && michael@0: !nsCSSRendering::IsCanvasFrame(aFrame)) { michael@0: return; michael@0: } michael@0: michael@0: nscolor bgcolor = NS_ComposeColors(aBackstopColor, mCanvasBackgroundColor); michael@0: if (NS_GET_A(bgcolor) == 0) michael@0: return; michael@0: michael@0: // To make layers work better, we want to avoid having a big non-scrolled michael@0: // color background behind a scrolled transparent background. Instead, michael@0: // we'll try to move the color background into the scrolled content michael@0: // by making nsDisplayCanvasBackground paint it. michael@0: if (!aFrame->GetParent()) { michael@0: nsIScrollableFrame* sf = michael@0: aFrame->PresContext()->PresShell()->GetRootScrollFrameAsScrollable(); michael@0: if (sf) { michael@0: nsCanvasFrame* canvasFrame = do_QueryFrame(sf->GetScrolledFrame()); michael@0: if (canvasFrame && canvasFrame->IsVisibleForPainting(&aBuilder)) { michael@0: if (AddCanvasBackgroundColor(aList, canvasFrame, bgcolor)) michael@0: return; michael@0: } michael@0: } michael@0: } michael@0: michael@0: aList.AppendNewToBottom( michael@0: new (&aBuilder) nsDisplaySolidColor(&aBuilder, aFrame, aBounds, bgcolor)); michael@0: } michael@0: michael@0: static bool IsTransparentContainerElement(nsPresContext* aPresContext) michael@0: { michael@0: nsCOMPtr docShellItem = aPresContext->GetDocShell(); michael@0: nsCOMPtr pwin(do_GetInterface(docShellItem)); michael@0: if (!pwin) michael@0: return false; michael@0: nsCOMPtr containerElement = michael@0: do_QueryInterface(pwin->GetFrameElementInternal()); michael@0: return containerElement && michael@0: containerElement->HasAttr(kNameSpaceID_None, nsGkAtoms::transparent); michael@0: } michael@0: michael@0: nscolor PresShell::GetDefaultBackgroundColorToDraw() michael@0: { michael@0: if (!mPresContext || !mPresContext->GetBackgroundColorDraw()) { michael@0: return NS_RGB(255,255,255); michael@0: } michael@0: return mPresContext->DefaultBackgroundColor(); michael@0: } michael@0: michael@0: void PresShell::UpdateCanvasBackground() michael@0: { michael@0: // If we have a frame tree and it has style information that michael@0: // specifies the background color of the canvas, update our local michael@0: // cache of that color. michael@0: nsIFrame* rootStyleFrame = FrameConstructor()->GetRootElementStyleFrame(); michael@0: if (rootStyleFrame) { michael@0: nsStyleContext* bgStyle = michael@0: nsCSSRendering::FindRootFrameBackground(rootStyleFrame); michael@0: // XXX We should really be passing the canvasframe, not the root element michael@0: // style frame but we don't have access to the canvasframe here. It isn't michael@0: // a problem because only a few frames can return something other than true michael@0: // and none of them would be a canvas frame or root element style frame. michael@0: bool drawBackgroundImage; michael@0: bool drawBackgroundColor; michael@0: michael@0: mCanvasBackgroundColor = michael@0: nsCSSRendering::DetermineBackgroundColor(mPresContext, bgStyle, michael@0: rootStyleFrame, michael@0: drawBackgroundImage, michael@0: drawBackgroundColor); michael@0: if (GetPresContext()->IsCrossProcessRootContentDocument() && michael@0: !IsTransparentContainerElement(mPresContext)) { michael@0: mCanvasBackgroundColor = michael@0: NS_ComposeColors(GetDefaultBackgroundColorToDraw(), mCanvasBackgroundColor); michael@0: } michael@0: } michael@0: michael@0: // If the root element of the document (ie html) has style 'display: none' michael@0: // then the document's background color does not get drawn; cache the michael@0: // color we actually draw. michael@0: if (!FrameConstructor()->GetRootElementFrame()) { michael@0: mCanvasBackgroundColor = GetDefaultBackgroundColorToDraw(); michael@0: } michael@0: if (XRE_GetProcessType() == GeckoProcessType_Content) { michael@0: if (TabChild* tabChild = TabChild::GetFrom(this)) { michael@0: tabChild->SetBackgroundColor(mCanvasBackgroundColor); michael@0: } michael@0: } michael@0: } michael@0: michael@0: nscolor PresShell::ComputeBackstopColor(nsView* aDisplayRoot) michael@0: { michael@0: nsIWidget* widget = aDisplayRoot->GetWidget(); michael@0: if (widget && (widget->GetTransparencyMode() != eTransparencyOpaque || michael@0: widget->WidgetPaintsBackground())) { michael@0: // Within a transparent widget, so the backstop color must be michael@0: // totally transparent. michael@0: return NS_RGBA(0,0,0,0); michael@0: } michael@0: // Within an opaque widget (or no widget at all), so the backstop michael@0: // color must be totally opaque. The user's default background michael@0: // as reported by the prescontext is guaranteed to be opaque. michael@0: return GetDefaultBackgroundColorToDraw(); michael@0: } michael@0: michael@0: struct PaintParams { michael@0: nscolor mBackgroundColor; michael@0: }; michael@0: michael@0: LayerManager* PresShell::GetLayerManager() michael@0: { michael@0: NS_ASSERTION(mViewManager, "Should have view manager"); michael@0: michael@0: nsView* rootView = mViewManager->GetRootView(); michael@0: if (rootView) { michael@0: if (nsIWidget* widget = rootView->GetWidget()) { michael@0: return widget->GetLayerManager(); michael@0: } michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: void PresShell::SetIgnoreViewportScrolling(bool aIgnore) michael@0: { michael@0: if (IgnoringViewportScrolling() == aIgnore) { michael@0: return; michael@0: } michael@0: RenderingState state(this); michael@0: state.mRenderFlags = ChangeFlag(state.mRenderFlags, aIgnore, michael@0: STATE_IGNORING_VIEWPORT_SCROLLING); michael@0: SetRenderingState(state); michael@0: } michael@0: michael@0: nsresult PresShell::SetResolution(float aXResolution, float aYResolution) michael@0: { michael@0: if (!(aXResolution > 0.0 && aYResolution > 0.0)) { michael@0: return NS_ERROR_ILLEGAL_VALUE; michael@0: } michael@0: if (aXResolution == mXResolution && aYResolution == mYResolution) { michael@0: return NS_OK; michael@0: } michael@0: RenderingState state(this); michael@0: state.mXResolution = aXResolution; michael@0: state.mYResolution = aYResolution; michael@0: SetRenderingState(state); michael@0: return NS_OK; michael@0: } michael@0: michael@0: gfxSize PresShell::GetCumulativeResolution() michael@0: { michael@0: gfxSize resolution = GetResolution(); michael@0: nsPresContext* parentCtx = GetPresContext()->GetParentPresContext(); michael@0: if (parentCtx) { michael@0: resolution = resolution * parentCtx->PresShell()->GetCumulativeResolution(); michael@0: } michael@0: return resolution; michael@0: } michael@0: michael@0: void PresShell::SetRenderingState(const RenderingState& aState) michael@0: { michael@0: if (mRenderFlags != aState.mRenderFlags) { michael@0: // Rendering state changed in a way that forces us to flush any michael@0: // retained layers we might already have. michael@0: LayerManager* manager = GetLayerManager(); michael@0: if (manager) { michael@0: FrameLayerBuilder::InvalidateAllLayers(manager); michael@0: } michael@0: } michael@0: michael@0: mRenderFlags = aState.mRenderFlags; michael@0: mXResolution = aState.mXResolution; michael@0: mYResolution = aState.mYResolution; michael@0: } michael@0: michael@0: void PresShell::SynthesizeMouseMove(bool aFromScroll) michael@0: { michael@0: if (!sSynthMouseMove) michael@0: return; michael@0: michael@0: if (mPaintingSuppressed || !mIsActive || !mPresContext) { michael@0: return; michael@0: } michael@0: michael@0: if (!mPresContext->IsRoot()) { michael@0: nsIPresShell* rootPresShell = GetRootPresShell(); michael@0: if (rootPresShell) { michael@0: rootPresShell->SynthesizeMouseMove(aFromScroll); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: if (mMouseLocation == nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE)) michael@0: return; michael@0: michael@0: if (!mSynthMouseMoveEvent.IsPending()) { michael@0: nsRefPtr ev = michael@0: new nsSynthMouseMoveEvent(this, aFromScroll); michael@0: michael@0: if (!GetPresContext()->RefreshDriver()->AddRefreshObserver(ev, michael@0: Flush_Display)) { michael@0: NS_WARNING("failed to dispatch nsSynthMouseMoveEvent"); michael@0: return; michael@0: } michael@0: michael@0: mSynthMouseMoveEvent = ev; michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Find the first floating view with a widget in a postorder traversal of the michael@0: * view tree that contains the point. Thus more deeply nested floating views michael@0: * are preferred over their ancestors, and floating views earlier in the michael@0: * view hierarchy (i.e., added later) are preferred over their siblings. michael@0: * This is adequate for finding the "topmost" floating view under a point, michael@0: * given that floating views don't supporting having a specific z-index. michael@0: * michael@0: * We cannot exit early when aPt is outside the view bounds, because floating michael@0: * views aren't necessarily included in their parent's bounds, so this could michael@0: * traverse the entire view hierarchy --- use carefully. michael@0: */ michael@0: static nsView* FindFloatingViewContaining(nsView* aView, nsPoint aPt) michael@0: { michael@0: if (aView->GetVisibility() == nsViewVisibility_kHide) michael@0: // No need to look into descendants. michael@0: return nullptr; michael@0: michael@0: nsIFrame* frame = aView->GetFrame(); michael@0: if (frame) { michael@0: if (!frame->IsVisibleConsideringAncestors(nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY) || michael@0: !frame->PresContext()->PresShell()->IsActive()) { michael@0: return nullptr; michael@0: } michael@0: } michael@0: michael@0: for (nsView* v = aView->GetFirstChild(); v; v = v->GetNextSibling()) { michael@0: nsView* r = FindFloatingViewContaining(v, v->ConvertFromParentCoords(aPt)); michael@0: if (r) michael@0: return r; michael@0: } michael@0: michael@0: if (aView->GetFloating() && aView->HasWidget() && michael@0: aView->GetDimensions().Contains(aPt)) michael@0: return aView; michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: /* michael@0: * This finds the first view containing the given point in a postorder michael@0: * traversal of the view tree that contains the point, assuming that the michael@0: * point is not in a floating view. It assumes that only floating views michael@0: * extend outside the bounds of their parents. michael@0: * michael@0: * This methods should only be called if FindFloatingViewContaining michael@0: * returns null. michael@0: */ michael@0: static nsView* FindViewContaining(nsView* aView, nsPoint aPt) michael@0: { michael@0: if (!aView->GetDimensions().Contains(aPt) || michael@0: aView->GetVisibility() == nsViewVisibility_kHide) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsIFrame* frame = aView->GetFrame(); michael@0: if (frame) { michael@0: if (!frame->IsVisibleConsideringAncestors(nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY) || michael@0: !frame->PresContext()->PresShell()->IsActive()) { michael@0: return nullptr; michael@0: } michael@0: } michael@0: michael@0: for (nsView* v = aView->GetFirstChild(); v; v = v->GetNextSibling()) { michael@0: nsView* r = FindViewContaining(v, v->ConvertFromParentCoords(aPt)); michael@0: if (r) michael@0: return r; michael@0: } michael@0: michael@0: return aView; michael@0: } michael@0: michael@0: void michael@0: PresShell::ProcessSynthMouseMoveEvent(bool aFromScroll) michael@0: { michael@0: // If drag session has started, we shouldn't synthesize mousemove event. michael@0: nsCOMPtr dragSession = nsContentUtils::GetDragSession(); michael@0: if (dragSession) { michael@0: mSynthMouseMoveEvent.Forget(); michael@0: return; michael@0: } michael@0: michael@0: // allow new event to be posted while handling this one only if the michael@0: // source of the event is a scroll (to prevent infinite reflow loops) michael@0: if (aFromScroll) { michael@0: mSynthMouseMoveEvent.Forget(); michael@0: } michael@0: michael@0: nsView* rootView = mViewManager ? mViewManager->GetRootView() : nullptr; michael@0: if (mMouseLocation == nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE) || michael@0: !rootView || !rootView->HasWidget() || !mPresContext) { michael@0: mSynthMouseMoveEvent.Forget(); michael@0: return; michael@0: } michael@0: michael@0: NS_ASSERTION(mPresContext->IsRoot(), "Only a root pres shell should be here"); michael@0: michael@0: // Hold a ref to ourselves so DispatchEvent won't destroy us (since michael@0: // we need to access members after we call DispatchEvent). michael@0: nsCOMPtr kungFuDeathGrip(this); michael@0: michael@0: #ifdef DEBUG_MOUSE_LOCATION michael@0: printf("[ps=%p]synthesizing mouse move to (%d,%d)\n", michael@0: this, mMouseLocation.x, mMouseLocation.y); michael@0: #endif michael@0: michael@0: int32_t APD = mPresContext->AppUnitsPerDevPixel(); michael@0: michael@0: // We need a widget to put in the event we are going to dispatch so we look michael@0: // for a view that has a widget and the mouse location is over. We first look michael@0: // for floating views, if there isn't one we use the root view. |view| holds michael@0: // that view. michael@0: nsView* view = nullptr; michael@0: michael@0: // The appunits per devpixel ratio of |view|. michael@0: int32_t viewAPD; michael@0: michael@0: // refPoint will be mMouseLocation relative to the widget of |view|, the michael@0: // widget we will put in the event we dispatch, in viewAPD appunits michael@0: nsPoint refpoint(0, 0); michael@0: michael@0: // We always dispatch the event to the pres shell that contains the view that michael@0: // the mouse is over. pointVM is the VM of that pres shell. michael@0: nsViewManager *pointVM = nullptr; michael@0: michael@0: // This could be a bit slow (traverses entire view hierarchy) michael@0: // but it's OK to do it once per synthetic mouse event michael@0: view = FindFloatingViewContaining(rootView, mMouseLocation); michael@0: if (!view) { michael@0: view = rootView; michael@0: nsView *pointView = FindViewContaining(rootView, mMouseLocation); michael@0: // pointView can be null in situations related to mouse capture michael@0: pointVM = (pointView ? pointView : view)->GetViewManager(); michael@0: refpoint = mMouseLocation + rootView->ViewToWidgetOffset(); michael@0: viewAPD = APD; michael@0: } else { michael@0: pointVM = view->GetViewManager(); michael@0: nsIFrame* frame = view->GetFrame(); michael@0: NS_ASSERTION(frame, "floating views can't be anonymous"); michael@0: viewAPD = frame->PresContext()->AppUnitsPerDevPixel(); michael@0: refpoint = mMouseLocation.ConvertAppUnits(APD, viewAPD); michael@0: refpoint -= view->GetOffsetTo(rootView); michael@0: refpoint += view->ViewToWidgetOffset(); michael@0: } michael@0: NS_ASSERTION(view->GetWidget(), "view should have a widget here"); michael@0: WidgetMouseEvent event(true, NS_MOUSE_MOVE, view->GetWidget(), michael@0: WidgetMouseEvent::eSynthesized); michael@0: event.refPoint = LayoutDeviceIntPoint::FromAppUnitsToNearest(refpoint, viewAPD); michael@0: event.time = PR_IntervalNow(); michael@0: // XXX set event.modifiers ? michael@0: // XXX mnakano I think that we should get the latest information from widget. michael@0: michael@0: nsCOMPtr shell = pointVM->GetPresShell(); michael@0: if (shell) { michael@0: shell->DispatchSynthMouseMove(&event, !aFromScroll); michael@0: } michael@0: michael@0: if (!aFromScroll) { michael@0: mSynthMouseMoveEvent.Forget(); michael@0: } michael@0: } michael@0: michael@0: /* static */ void michael@0: PresShell::MarkImagesInListVisible(const nsDisplayList& aList) michael@0: { michael@0: for (nsDisplayItem* item = aList.GetBottom(); item; item = item->GetAbove()) { michael@0: nsDisplayList* sublist = item->GetChildren(); michael@0: if (sublist) { michael@0: MarkImagesInListVisible(*sublist); michael@0: continue; michael@0: } michael@0: nsIFrame* f = item->Frame(); michael@0: // We could check the type of the display item, only a handful can hold an michael@0: // image loading content. michael@0: // dont bother nscomptr here, it is wasteful michael@0: nsCOMPtr content(do_QueryInterface(f->GetContent())); michael@0: if (content) { michael@0: // use the presshell containing the image michael@0: PresShell* presShell = static_cast(f->PresContext()->PresShell()); michael@0: uint32_t count = presShell->mVisibleImages.Count(); michael@0: presShell->mVisibleImages.PutEntry(content); michael@0: if (presShell->mVisibleImages.Count() > count) { michael@0: // content was added to mVisibleImages, so we need to increment its visible count michael@0: content->IncrementVisibleCount(); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: static PLDHashOperator michael@0: RemoveAndStore(nsRefPtrHashKey* aEntry, void* userArg) michael@0: { michael@0: nsTArray< nsRefPtr >* array = michael@0: static_cast< nsTArray< nsRefPtr >* >(userArg); michael@0: array->AppendElement(aEntry->GetKey()); michael@0: return PL_DHASH_REMOVE; michael@0: } michael@0: michael@0: void michael@0: PresShell::RebuildImageVisibility(const nsDisplayList& aList) michael@0: { michael@0: MOZ_ASSERT(!mImageVisibilityVisited, "already visited?"); michael@0: mImageVisibilityVisited = true; michael@0: // Remove the entries of the mVisibleImages hashtable and put them in the michael@0: // beforeImageList array. michael@0: nsTArray< nsRefPtr > beforeImageList; michael@0: beforeImageList.SetCapacity(mVisibleImages.Count()); michael@0: mVisibleImages.EnumerateEntries(RemoveAndStore, &beforeImageList); michael@0: MarkImagesInListVisible(aList); michael@0: for (uint32_t i = 0; i < beforeImageList.Length(); ++i) { michael@0: beforeImageList[i]->DecrementVisibleCount(); michael@0: } michael@0: } michael@0: michael@0: /* static */ void michael@0: PresShell::ClearImageVisibilityVisited(nsView* aView, bool aClear) michael@0: { michael@0: nsViewManager* vm = aView->GetViewManager(); michael@0: if (aClear) { michael@0: PresShell* presShell = static_cast(vm->GetPresShell()); michael@0: if (!presShell->mImageVisibilityVisited) { michael@0: presShell->ClearVisibleImagesList(); michael@0: } michael@0: presShell->mImageVisibilityVisited = false; michael@0: } michael@0: for (nsView* v = aView->GetFirstChild(); v; v = v->GetNextSibling()) { michael@0: ClearImageVisibilityVisited(v, v->GetViewManager() != vm); michael@0: } michael@0: } michael@0: michael@0: static PLDHashOperator michael@0: DecrementVisibleCount(nsRefPtrHashKey* aEntry, void* userArg) michael@0: { michael@0: aEntry->GetKey()->DecrementVisibleCount(); michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: void michael@0: PresShell::ClearVisibleImagesList() michael@0: { michael@0: mVisibleImages.EnumerateEntries(DecrementVisibleCount, nullptr); michael@0: mVisibleImages.Clear(); michael@0: } michael@0: michael@0: void michael@0: PresShell::UpdateImageVisibility() michael@0: { michael@0: MOZ_ASSERT(!mPresContext || mPresContext->IsRootContentDocument(), michael@0: "updating image visibility on a non-root content document?"); michael@0: michael@0: mUpdateImageVisibilityEvent.Revoke(); michael@0: michael@0: if (mHaveShutDown || mIsDestroying) { michael@0: return; michael@0: } michael@0: michael@0: // call update on that frame michael@0: nsIFrame* rootFrame = GetRootFrame(); michael@0: if (!rootFrame) { michael@0: ClearVisibleImagesList(); michael@0: return; michael@0: } michael@0: michael@0: // We could walk the frame tree directly and skip creating a display list for michael@0: // better perf. michael@0: nsRect updateRect(nsPoint(0, 0), rootFrame->GetSize()); michael@0: nsDisplayListBuilder builder(rootFrame, nsDisplayListBuilder::IMAGE_VISIBILITY, true); michael@0: builder.IgnorePaintSuppression(); michael@0: builder.EnterPresShell(rootFrame, updateRect); michael@0: nsDisplayList list; michael@0: rootFrame->BuildDisplayListForStackingContext(&builder, updateRect, &list); michael@0: builder.LeavePresShell(rootFrame, updateRect); michael@0: michael@0: RebuildImageVisibility(list); michael@0: michael@0: ClearImageVisibilityVisited(rootFrame->GetView(), true); michael@0: michael@0: list.DeleteAll(); michael@0: } michael@0: michael@0: bool michael@0: PresShell::AssumeAllImagesVisible() michael@0: { michael@0: static bool sImageVisibilityEnabled = true; michael@0: static bool sImageVisibilityEnabledForBrowserElementsOnly = false; michael@0: static bool sImageVisibilityPrefCached = false; michael@0: michael@0: if (!sImageVisibilityPrefCached) { michael@0: Preferences::AddBoolVarCache(&sImageVisibilityEnabled, michael@0: "layout.imagevisibility.enabled", true); michael@0: Preferences::AddBoolVarCache(&sImageVisibilityEnabledForBrowserElementsOnly, michael@0: "layout.imagevisibility.enabled_for_browser_elements_only", false); michael@0: sImageVisibilityPrefCached = true; michael@0: } michael@0: michael@0: if ((!sImageVisibilityEnabled && michael@0: !sImageVisibilityEnabledForBrowserElementsOnly) || michael@0: !mPresContext || !mDocument) { michael@0: return true; michael@0: } michael@0: michael@0: // We assume all images are visible in print, print preview, chrome, xul, and michael@0: // resource docs and don't keep track of them. michael@0: if (mPresContext->Type() == nsPresContext::eContext_PrintPreview || michael@0: mPresContext->Type() == nsPresContext::eContext_Print || michael@0: mPresContext->IsChrome() || michael@0: mDocument->IsResourceDoc() || michael@0: mDocument->IsXUL()) { michael@0: return true; michael@0: } michael@0: michael@0: if (!sImageVisibilityEnabled && michael@0: sImageVisibilityEnabledForBrowserElementsOnly) { michael@0: nsCOMPtr docshell(mPresContext->GetDocShell()); michael@0: if (!docshell || !docshell->GetIsInBrowserElement()) { michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: void michael@0: PresShell::ScheduleImageVisibilityUpdate() michael@0: { michael@0: if (AssumeAllImagesVisible()) michael@0: return; michael@0: michael@0: if (!mPresContext->IsRootContentDocument()) { michael@0: nsPresContext* presContext = mPresContext->GetToplevelContentDocumentPresContext(); michael@0: if (!presContext) michael@0: return; michael@0: MOZ_ASSERT(presContext->IsRootContentDocument(), michael@0: "Didn't get a root prescontext from GetToplevelContentDocumentPresContext?"); michael@0: presContext->PresShell()->ScheduleImageVisibilityUpdate(); michael@0: return; michael@0: } michael@0: michael@0: if (mHaveShutDown || mIsDestroying) michael@0: return; michael@0: michael@0: if (mUpdateImageVisibilityEvent.IsPending()) michael@0: return; michael@0: michael@0: nsRefPtr > ev = michael@0: NS_NewRunnableMethod(this, &PresShell::UpdateImageVisibility); michael@0: if (NS_SUCCEEDED(NS_DispatchToCurrentThread(ev))) { michael@0: mUpdateImageVisibilityEvent = ev; michael@0: } michael@0: } michael@0: michael@0: void michael@0: PresShell::EnsureImageInVisibleList(nsIImageLoadingContent* aImage) michael@0: { michael@0: if (AssumeAllImagesVisible()) { michael@0: aImage->IncrementVisibleCount(); michael@0: return; michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: // if it has a frame make sure its in this presshell michael@0: nsCOMPtr content = do_QueryInterface(aImage); michael@0: if (content) { michael@0: PresShell* shell = static_cast(content->OwnerDoc()->GetShell()); michael@0: MOZ_ASSERT(!shell || shell == this, "wrong shell"); michael@0: } michael@0: #endif michael@0: michael@0: if (!mVisibleImages.Contains(aImage)) { michael@0: mVisibleImages.PutEntry(aImage); michael@0: aImage->IncrementVisibleCount(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: PresShell::RemoveImageFromVisibleList(nsIImageLoadingContent* aImage) michael@0: { michael@0: #ifdef DEBUG michael@0: // if it has a frame make sure its in this presshell michael@0: nsCOMPtr content = do_QueryInterface(aImage); michael@0: if (content) { michael@0: PresShell* shell = static_cast(content->OwnerDoc()->GetShell()); michael@0: MOZ_ASSERT(!shell || shell == this, "wrong shell"); michael@0: } michael@0: #endif michael@0: michael@0: if (AssumeAllImagesVisible()) { michael@0: MOZ_ASSERT(mVisibleImages.Count() == 0, "shouldn't have any images in the table"); michael@0: return; michael@0: } michael@0: michael@0: uint32_t count = mVisibleImages.Count(); michael@0: mVisibleImages.RemoveEntry(aImage); michael@0: if (mVisibleImages.Count() < count) { michael@0: // aImage was in the hashtable, so we need to decrement its visible count michael@0: aImage->DecrementVisibleCount(); michael@0: } michael@0: } michael@0: michael@0: class nsAutoNotifyDidPaint michael@0: { michael@0: public: michael@0: nsAutoNotifyDidPaint(PresShell* aShell, uint32_t aFlags) michael@0: : mShell(aShell), mFlags(aFlags) michael@0: { michael@0: } michael@0: ~nsAutoNotifyDidPaint() michael@0: { michael@0: mShell->GetPresContext()->NotifyDidPaintForSubtree(mFlags); michael@0: } michael@0: michael@0: private: michael@0: PresShell* mShell; michael@0: uint32_t mFlags; michael@0: }; michael@0: michael@0: class AutoUpdateHitRegion michael@0: { michael@0: public: michael@0: AutoUpdateHitRegion(PresShell* aShell, nsIFrame* aFrame) michael@0: : mShell(aShell), mFrame(aFrame) michael@0: { michael@0: } michael@0: ~AutoUpdateHitRegion() michael@0: { michael@0: if (XRE_GetProcessType() != GeckoProcessType_Content || michael@0: !mFrame || !mShell) { michael@0: return; michael@0: } michael@0: TabChild* tabChild = TabChild::GetFrom(mShell); michael@0: if (!tabChild || !tabChild->GetUpdateHitRegion()) { michael@0: return; michael@0: } michael@0: nsRegion region; michael@0: nsDisplayListBuilder builder(mFrame, michael@0: nsDisplayListBuilder::EVENT_DELIVERY, michael@0: /* aBuildCert= */ false); michael@0: nsDisplayList list; michael@0: nsAutoTArray outFrames; michael@0: nsDisplayItem::HitTestState hitTestState; michael@0: nsRect bounds = mShell->GetPresContext()->GetVisibleArea(); michael@0: builder.EnterPresShell(mFrame, bounds); michael@0: mFrame->BuildDisplayListForStackingContext(&builder, bounds, &list); michael@0: builder.LeavePresShell(mFrame, bounds); michael@0: list.HitTest(&builder, bounds, &hitTestState, &outFrames); michael@0: list.DeleteAll(); michael@0: for (int32_t i = outFrames.Length() - 1; i >= 0; --i) { michael@0: region.Or(region, nsLayoutUtils::TransformFrameRectToAncestor( michael@0: outFrames[i], nsRect(nsPoint(0, 0), outFrames[i]->GetSize()), mFrame)); michael@0: } michael@0: tabChild->UpdateHitRegion(region); michael@0: } michael@0: private: michael@0: PresShell* mShell; michael@0: nsIFrame* mFrame; michael@0: }; michael@0: michael@0: void michael@0: PresShell::RestyleShadowRoot(ShadowRoot* aShadowRoot) michael@0: { michael@0: // Mark the children of the ShadowRoot as style changed but not michael@0: // the ShadowRoot itself because it is a document fragment and does not michael@0: // have a frame. michael@0: ExplicitChildIterator iterator(aShadowRoot); michael@0: for (nsIContent* child = iterator.GetNextChild(); michael@0: child; michael@0: child = iterator.GetNextChild()) { michael@0: if (child->IsElement()) { michael@0: mChangedScopeStyleRoots.AppendElement(child->AsElement()); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: PresShell::Paint(nsView* aViewToPaint, michael@0: const nsRegion& aDirtyRegion, michael@0: uint32_t aFlags) michael@0: { michael@0: PROFILER_LABEL("Paint", "PresShell::Paint"); michael@0: NS_ASSERTION(!mIsDestroying, "painting a destroyed PresShell"); michael@0: NS_ASSERTION(aViewToPaint, "null view"); michael@0: michael@0: MOZ_ASSERT(!mImageVisibilityVisited, "should have been cleared"); michael@0: michael@0: if (!mIsActive || mIsZombie) { michael@0: return; michael@0: } michael@0: michael@0: nsPresContext* presContext = GetPresContext(); michael@0: AUTO_LAYOUT_PHASE_ENTRY_POINT(presContext, Paint); michael@0: michael@0: nsIFrame* frame = aViewToPaint->GetFrame(); michael@0: michael@0: bool isRetainingManager; michael@0: LayerManager* layerManager = michael@0: aViewToPaint->GetWidget()->GetLayerManager(&isRetainingManager); michael@0: NS_ASSERTION(layerManager, "Must be in paint event"); michael@0: bool shouldInvalidate = layerManager->NeedsWidgetInvalidation(); michael@0: michael@0: nsAutoNotifyDidPaint notifyDidPaint(this, aFlags); michael@0: AutoUpdateHitRegion updateHitRegion(this, frame); michael@0: michael@0: // Whether or not we should set first paint when painting is michael@0: // suppressed is debatable. For now we'll do it because michael@0: // B2G relies on first paint to configure the viewport and michael@0: // we only want to do that when we have real content to paint. michael@0: // See Bug 798245 michael@0: if (mIsFirstPaint && !mPaintingSuppressed) { michael@0: layerManager->SetIsFirstPaint(); michael@0: mIsFirstPaint = false; michael@0: } michael@0: michael@0: layerManager->BeginTransaction(); michael@0: michael@0: if (frame && isRetainingManager) { michael@0: // Try to do an empty transaction, if the frame tree does not michael@0: // need to be updated. Do not try to do an empty transaction on michael@0: // a non-retained layer manager (like the BasicLayerManager that michael@0: // draws the window title bar on Mac), because a) it won't work michael@0: // and b) below we don't want to clear NS_FRAME_UPDATE_LAYER_TREE, michael@0: // that will cause us to forget to update the real layer manager! michael@0: michael@0: if (!(aFlags & PAINT_LAYERS)) { michael@0: if (layerManager->EndEmptyTransaction()) { michael@0: return; michael@0: } michael@0: NS_WARNING("Must complete empty transaction when compositing!"); michael@0: } michael@0: michael@0: if (!(frame->GetStateBits() & NS_FRAME_UPDATE_LAYER_TREE) && michael@0: !mNextPaintCompressed) { michael@0: NotifySubDocInvalidationFunc computeInvalidFunc = michael@0: presContext->MayHavePaintEventListenerInSubDocument() ? nsPresContext::NotifySubDocInvalidation : 0; michael@0: bool computeInvalidRect = computeInvalidFunc || michael@0: (layerManager->GetBackendType() == LayersBackend::LAYERS_BASIC); michael@0: michael@0: nsAutoPtr props(computeInvalidRect ? michael@0: LayerProperties::CloneFrom(layerManager->GetRoot()) : michael@0: nullptr); michael@0: michael@0: if (layerManager->EndEmptyTransaction((aFlags & PAINT_COMPOSITE) ? michael@0: LayerManager::END_DEFAULT : LayerManager::END_NO_COMPOSITE)) { michael@0: nsIntRegion invalid; michael@0: if (props) { michael@0: invalid = props->ComputeDifferences(layerManager->GetRoot(), computeInvalidFunc); michael@0: } else { michael@0: LayerProperties::ClearInvalidations(layerManager->GetRoot()); michael@0: } michael@0: if (props) { michael@0: if (!invalid.IsEmpty()) { michael@0: nsIntRect bounds = invalid.GetBounds(); michael@0: nsRect rect(presContext->DevPixelsToAppUnits(bounds.x), michael@0: presContext->DevPixelsToAppUnits(bounds.y), michael@0: presContext->DevPixelsToAppUnits(bounds.width), michael@0: presContext->DevPixelsToAppUnits(bounds.height)); michael@0: if (shouldInvalidate) { michael@0: aViewToPaint->GetViewManager()->InvalidateViewNoSuppression(aViewToPaint, rect); michael@0: } michael@0: presContext->NotifyInvalidation(bounds, 0); michael@0: } michael@0: } else if (shouldInvalidate) { michael@0: aViewToPaint->GetViewManager()->InvalidateView(aViewToPaint); michael@0: } michael@0: michael@0: frame->UpdatePaintCountForPaintedPresShells(); michael@0: return; michael@0: } michael@0: } michael@0: frame->RemoveStateBits(NS_FRAME_UPDATE_LAYER_TREE); michael@0: } michael@0: if (frame) { michael@0: frame->ClearPresShellsFromLastPaint(); michael@0: } michael@0: michael@0: nscolor bgcolor = ComputeBackstopColor(aViewToPaint); michael@0: uint32_t flags = nsLayoutUtils::PAINT_WIDGET_LAYERS | nsLayoutUtils::PAINT_EXISTING_TRANSACTION; michael@0: if (!(aFlags & PAINT_COMPOSITE)) { michael@0: flags |= nsLayoutUtils::PAINT_NO_COMPOSITE; michael@0: } michael@0: if (mNextPaintCompressed) { michael@0: flags |= nsLayoutUtils::PAINT_COMPRESSED; michael@0: mNextPaintCompressed = false; michael@0: } michael@0: michael@0: if (frame) { michael@0: // We can paint directly into the widget using its layer manager. michael@0: nsLayoutUtils::PaintFrame(nullptr, frame, aDirtyRegion, bgcolor, flags); michael@0: return; michael@0: } michael@0: michael@0: nsRefPtr root = layerManager->CreateColorLayer(); michael@0: if (root) { michael@0: nsPresContext* pc = GetPresContext(); michael@0: nsIntRect bounds = michael@0: pc->GetVisibleArea().ToOutsidePixels(pc->AppUnitsPerDevPixel()); michael@0: bgcolor = NS_ComposeColors(bgcolor, mCanvasBackgroundColor); michael@0: root->SetColor(bgcolor); michael@0: root->SetVisibleRegion(bounds); michael@0: layerManager->SetRoot(root); michael@0: } michael@0: layerManager->EndTransaction(nullptr, nullptr, (aFlags & PAINT_COMPOSITE) ? michael@0: LayerManager::END_DEFAULT : LayerManager::END_NO_COMPOSITE); michael@0: } michael@0: michael@0: // static michael@0: void michael@0: nsIPresShell::SetCapturingContent(nsIContent* aContent, uint8_t aFlags) michael@0: { michael@0: // If capture was set for pointer lock, don't unlock unless we are coming michael@0: // out of pointer lock explicitly. michael@0: if (!aContent && gCaptureInfo.mPointerLock && michael@0: !(aFlags & CAPTURE_POINTERLOCK)) { michael@0: return; michael@0: } michael@0: michael@0: NS_IF_RELEASE(gCaptureInfo.mContent); michael@0: michael@0: // only set capturing content if allowed or the CAPTURE_IGNOREALLOWED or michael@0: // CAPTURE_POINTERLOCK flags are used. michael@0: if ((aFlags & CAPTURE_IGNOREALLOWED) || gCaptureInfo.mAllowed || michael@0: (aFlags & CAPTURE_POINTERLOCK)) { michael@0: if (aContent) { michael@0: NS_ADDREF(gCaptureInfo.mContent = aContent); michael@0: } michael@0: // CAPTURE_POINTERLOCK is the same as CAPTURE_RETARGETTOELEMENT & CAPTURE_IGNOREALLOWED michael@0: gCaptureInfo.mRetargetToElement = ((aFlags & CAPTURE_RETARGETTOELEMENT) != 0) || michael@0: ((aFlags & CAPTURE_POINTERLOCK) != 0); michael@0: gCaptureInfo.mPreventDrag = (aFlags & CAPTURE_PREVENTDRAG) != 0; michael@0: gCaptureInfo.mPointerLock = (aFlags & CAPTURE_POINTERLOCK) != 0; michael@0: } michael@0: } michael@0: michael@0: /* static */ void michael@0: nsIPresShell::SetPointerCapturingContent(uint32_t aPointerId, nsIContent* aContent) michael@0: { michael@0: nsIContent* content = GetPointerCapturingContent(aPointerId); michael@0: michael@0: PointerInfo* pointerInfo = nullptr; michael@0: if (!content && gActivePointersIds->Get(aPointerId, &pointerInfo) && michael@0: pointerInfo && michael@0: nsIDOMMouseEvent::MOZ_SOURCE_MOUSE == pointerInfo->mPointerType) { michael@0: SetCapturingContent(aContent, CAPTURE_PREVENTDRAG); michael@0: } michael@0: michael@0: if (content) { michael@0: // Releasing capture for given pointer. michael@0: gPointerCaptureList->Remove(aPointerId); michael@0: DispatchGotOrLostPointerCaptureEvent(false, aPointerId, content); michael@0: // Need to check the state because a lostpointercapture listener michael@0: // may have called SetPointerCapture michael@0: if (GetPointerCapturingContent(aPointerId)) { michael@0: return; michael@0: } michael@0: } michael@0: michael@0: gPointerCaptureList->Put(aPointerId, aContent); michael@0: DispatchGotOrLostPointerCaptureEvent(true, aPointerId, aContent); michael@0: } michael@0: michael@0: /* static */ void michael@0: nsIPresShell::ReleasePointerCapturingContent(uint32_t aPointerId, nsIContent* aContent) michael@0: { michael@0: if (gActivePointersIds->Get(aPointerId)) { michael@0: SetCapturingContent(nullptr, CAPTURE_PREVENTDRAG); michael@0: } michael@0: michael@0: // Releasing capture for given pointer. michael@0: gPointerCaptureList->Remove(aPointerId); michael@0: michael@0: DispatchGotOrLostPointerCaptureEvent(false, aPointerId, aContent); michael@0: } michael@0: michael@0: /* static */ nsIContent* michael@0: nsIPresShell::GetPointerCapturingContent(uint32_t aPointerId) michael@0: { michael@0: return gPointerCaptureList->GetWeak(aPointerId); michael@0: } michael@0: michael@0: /* static */ bool michael@0: nsIPresShell::GetPointerInfo(uint32_t aPointerId, bool& aActiveState) michael@0: { michael@0: PointerInfo* pointerInfo = nullptr; michael@0: if (gActivePointersIds->Get(aPointerId, &pointerInfo) && pointerInfo) { michael@0: aActiveState = pointerInfo->mActiveState; michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: void michael@0: PresShell::UpdateActivePointerState(WidgetGUIEvent* aEvent) michael@0: { michael@0: switch (aEvent->message) { michael@0: case NS_MOUSE_ENTER: michael@0: // In this case we have to know information about available mouse pointers michael@0: if (WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent()) { michael@0: gActivePointersIds->Put(mouseEvent->pointerId, new PointerInfo(false, mouseEvent->inputSource)); michael@0: } michael@0: break; michael@0: case NS_POINTER_DOWN: michael@0: // In this case we switch pointer to active state michael@0: if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) { michael@0: gActivePointersIds->Put(pointerEvent->pointerId, new PointerInfo(true, pointerEvent->inputSource)); michael@0: } michael@0: break; michael@0: case NS_POINTER_UP: michael@0: // In this case we remove information about pointer or turn off active state michael@0: if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) { michael@0: if(pointerEvent->inputSource != nsIDOMMouseEvent::MOZ_SOURCE_TOUCH) { michael@0: gActivePointersIds->Put(pointerEvent->pointerId, new PointerInfo(false, pointerEvent->inputSource)); michael@0: } else { michael@0: gActivePointersIds->Remove(pointerEvent->pointerId); michael@0: } michael@0: } michael@0: break; michael@0: case NS_MOUSE_EXIT: michael@0: // In this case we have to remove information about disappeared mouse pointers michael@0: if (WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent()) { michael@0: gActivePointersIds->Remove(mouseEvent->pointerId); michael@0: } michael@0: break; michael@0: } michael@0: } michael@0: michael@0: nsIContent* michael@0: PresShell::GetCurrentEventContent() michael@0: { michael@0: if (mCurrentEventContent && michael@0: mCurrentEventContent->GetCurrentDoc() != mDocument) { michael@0: mCurrentEventContent = nullptr; michael@0: mCurrentEventFrame = nullptr; michael@0: } michael@0: return mCurrentEventContent; michael@0: } michael@0: michael@0: nsIFrame* michael@0: PresShell::GetCurrentEventFrame() michael@0: { michael@0: if (MOZ_UNLIKELY(mIsDestroying)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: // GetCurrentEventContent() makes sure the content is still in the michael@0: // same document that this pres shell belongs to. If not, then the michael@0: // frame shouldn't get an event, nor should we even assume its safe michael@0: // to try and find the frame. michael@0: nsIContent* content = GetCurrentEventContent(); michael@0: if (!mCurrentEventFrame && content) { michael@0: mCurrentEventFrame = content->GetPrimaryFrame(); michael@0: MOZ_ASSERT(!mCurrentEventFrame || michael@0: mCurrentEventFrame->PresContext()->GetPresShell() == this); michael@0: } michael@0: return mCurrentEventFrame; michael@0: } michael@0: michael@0: nsIFrame* michael@0: PresShell::GetEventTargetFrame() michael@0: { michael@0: return GetCurrentEventFrame(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: PresShell::GetEventTargetContent(WidgetEvent* aEvent) michael@0: { michael@0: nsCOMPtr content = GetCurrentEventContent(); michael@0: if (!content) { michael@0: nsIFrame* currentEventFrame = GetCurrentEventFrame(); michael@0: if (currentEventFrame) { michael@0: currentEventFrame->GetContentForEvent(aEvent, getter_AddRefs(content)); michael@0: NS_ASSERTION(!content || content->GetCurrentDoc() == mDocument, michael@0: "handing out content from a different doc"); michael@0: } michael@0: } michael@0: return content.forget(); michael@0: } michael@0: michael@0: void michael@0: PresShell::PushCurrentEventInfo(nsIFrame* aFrame, nsIContent* aContent) michael@0: { michael@0: if (mCurrentEventFrame || mCurrentEventContent) { michael@0: mCurrentEventFrameStack.InsertElementAt(0, mCurrentEventFrame); michael@0: mCurrentEventContentStack.InsertObjectAt(mCurrentEventContent, 0); michael@0: } michael@0: mCurrentEventFrame = aFrame; michael@0: mCurrentEventContent = aContent; michael@0: } michael@0: michael@0: void michael@0: PresShell::PopCurrentEventInfo() michael@0: { michael@0: mCurrentEventFrame = nullptr; michael@0: mCurrentEventContent = nullptr; michael@0: michael@0: if (0 != mCurrentEventFrameStack.Length()) { michael@0: mCurrentEventFrame = mCurrentEventFrameStack.ElementAt(0); michael@0: mCurrentEventFrameStack.RemoveElementAt(0); michael@0: mCurrentEventContent = mCurrentEventContentStack.ObjectAt(0); michael@0: mCurrentEventContentStack.RemoveObjectAt(0); michael@0: michael@0: // Don't use it if it has moved to a different document. michael@0: if (mCurrentEventContent && michael@0: mCurrentEventContent->GetCurrentDoc() != mDocument) { michael@0: mCurrentEventContent = nullptr; michael@0: mCurrentEventFrame = nullptr; michael@0: } michael@0: } michael@0: } michael@0: michael@0: bool PresShell::InZombieDocument(nsIContent *aContent) michael@0: { michael@0: // If a content node points to a null document, or the document is not michael@0: // attached to a window, then it is possibly in a zombie document, michael@0: // about to be replaced by a newly loading document. michael@0: // Such documents cannot handle DOM events. michael@0: // It might actually be in a node not attached to any document, michael@0: // in which case there is not parent presshell to retarget it to. michael@0: nsIDocument *doc = aContent->GetDocument(); michael@0: return !doc || !doc->GetWindow(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: PresShell::GetRootWindow() michael@0: { michael@0: nsCOMPtr window = michael@0: do_QueryInterface(mDocument->GetWindow()); michael@0: if (window) { michael@0: nsCOMPtr rootWindow = window->GetPrivateRoot(); michael@0: NS_ASSERTION(rootWindow, "nsPIDOMWindow::GetPrivateRoot() returns NULL"); michael@0: return rootWindow.forget(); michael@0: } michael@0: michael@0: // If we don't have DOM window, we're zombie, we should find the root window michael@0: // with our parent shell. michael@0: nsCOMPtr parent = GetParentPresShellForEventHandling(); michael@0: NS_ENSURE_TRUE(parent, nullptr); michael@0: return parent->GetRootWindow(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: PresShell::GetParentPresShellForEventHandling() michael@0: { michael@0: NS_ENSURE_TRUE(mPresContext, nullptr); michael@0: michael@0: // Now, find the parent pres shell and send the event there michael@0: nsCOMPtr treeItem = mPresContext->GetDocShell(); michael@0: if (!treeItem) { michael@0: treeItem = mForwardingContainer.get(); michael@0: } michael@0: michael@0: // Might have gone away, or never been around to start with michael@0: NS_ENSURE_TRUE(treeItem, nullptr); michael@0: michael@0: nsCOMPtr parentTreeItem; michael@0: treeItem->GetParent(getter_AddRefs(parentTreeItem)); michael@0: nsCOMPtr parentDocShell = do_QueryInterface(parentTreeItem); michael@0: NS_ENSURE_TRUE(parentDocShell && treeItem != parentTreeItem, nullptr); michael@0: michael@0: nsCOMPtr parentPresShell = parentDocShell->GetPresShell(); michael@0: return parentPresShell.forget(); michael@0: } michael@0: michael@0: nsresult michael@0: PresShell::RetargetEventToParent(WidgetGUIEvent* aEvent, michael@0: nsEventStatus* aEventStatus) michael@0: { michael@0: // Send this events straight up to the parent pres shell. michael@0: // We do this for keystroke events in zombie documents or if either a frame michael@0: // or a root content is not present. michael@0: // That way at least the UI key bindings can work. michael@0: michael@0: nsCOMPtr kungFuDeathGrip(this); michael@0: nsCOMPtr parentPresShell = GetParentPresShellForEventHandling(); michael@0: NS_ENSURE_TRUE(parentPresShell, NS_ERROR_FAILURE); michael@0: michael@0: // Fake the event as though it's from the parent pres shell's root frame. michael@0: return parentPresShell->HandleEvent(parentPresShell->GetRootFrame(), aEvent, true, aEventStatus); michael@0: } michael@0: michael@0: void michael@0: PresShell::DisableNonTestMouseEvents(bool aDisable) michael@0: { michael@0: sDisableNonTestMouseEvents = aDisable; michael@0: } michael@0: michael@0: already_AddRefed michael@0: PresShell::GetFocusedDOMWindowInOurWindow() michael@0: { michael@0: nsCOMPtr rootWindow = GetRootWindow(); michael@0: NS_ENSURE_TRUE(rootWindow, nullptr); michael@0: nsCOMPtr focusedWindow; michael@0: nsFocusManager::GetFocusedDescendant(rootWindow, true, michael@0: getter_AddRefs(focusedWindow)); michael@0: return focusedWindow.forget(); michael@0: } michael@0: michael@0: void michael@0: PresShell::RecordMouseLocation(WidgetGUIEvent* aEvent) michael@0: { michael@0: if (!mPresContext) michael@0: return; michael@0: michael@0: if (!mPresContext->IsRoot()) { michael@0: PresShell* rootPresShell = GetRootPresShell(); michael@0: if (rootPresShell) { michael@0: rootPresShell->RecordMouseLocation(aEvent); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: if ((aEvent->message == NS_MOUSE_MOVE && michael@0: aEvent->AsMouseEvent()->reason == WidgetMouseEvent::eReal) || michael@0: aEvent->message == NS_MOUSE_ENTER || michael@0: aEvent->message == NS_MOUSE_BUTTON_DOWN || michael@0: aEvent->message == NS_MOUSE_BUTTON_UP) { michael@0: nsIFrame* rootFrame = GetRootFrame(); michael@0: if (!rootFrame) { michael@0: nsView* rootView = mViewManager->GetRootView(); michael@0: mMouseLocation = nsLayoutUtils::TranslateWidgetToView(mPresContext, michael@0: aEvent->widget, LayoutDeviceIntPoint::ToUntyped(aEvent->refPoint), michael@0: rootView); michael@0: } else { michael@0: mMouseLocation = michael@0: nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, rootFrame); michael@0: } michael@0: #ifdef DEBUG_MOUSE_LOCATION michael@0: if (aEvent->message == NS_MOUSE_ENTER) michael@0: printf("[ps=%p]got mouse enter for %p\n", michael@0: this, aEvent->widget); michael@0: printf("[ps=%p]setting mouse location to (%d,%d)\n", michael@0: this, mMouseLocation.x, mMouseLocation.y); michael@0: #endif michael@0: if (aEvent->message == NS_MOUSE_ENTER) michael@0: SynthesizeMouseMove(false); michael@0: } else if (aEvent->message == NS_MOUSE_EXIT) { michael@0: // Although we only care about the mouse moving into an area for which this michael@0: // pres shell doesn't receive mouse move events, we don't check which widget michael@0: // the mouse exit was for since this seems to vary by platform. Hopefully michael@0: // this won't matter at all since we'll get the mouse move or enter after michael@0: // the mouse exit when the mouse moves from one of our widgets into another. michael@0: mMouseLocation = nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); michael@0: #ifdef DEBUG_MOUSE_LOCATION michael@0: printf("[ps=%p]got mouse exit for %p\n", michael@0: this, aEvent->widget); michael@0: printf("[ps=%p]clearing mouse location\n", michael@0: this); michael@0: #endif michael@0: } michael@0: } michael@0: michael@0: static void michael@0: EvictTouchPoint(nsRefPtr& aTouch, michael@0: nsIDocument* aLimitToDocument = nullptr) michael@0: { michael@0: nsCOMPtr node(do_QueryInterface(aTouch->mTarget)); michael@0: if (node) { michael@0: nsIDocument* doc = node->GetCurrentDoc(); michael@0: if (doc && (!aLimitToDocument || aLimitToDocument == doc)) { michael@0: nsIPresShell* presShell = doc->GetShell(); michael@0: if (presShell) { michael@0: nsIFrame* frame = presShell->GetRootFrame(); michael@0: if (frame) { michael@0: nsPoint pt(aTouch->mRefPoint.x, aTouch->mRefPoint.y); michael@0: nsCOMPtr widget = frame->GetView()->GetNearestWidget(&pt); michael@0: if (widget) { michael@0: WidgetTouchEvent event(true, NS_TOUCH_END, widget); michael@0: event.widget = widget; michael@0: event.time = PR_IntervalNow(); michael@0: event.touches.AppendElement(aTouch); michael@0: nsEventStatus status; michael@0: widget->DispatchEvent(&event, status); michael@0: return; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: } michael@0: if (!node || !aLimitToDocument || node->OwnerDoc() == aLimitToDocument) { michael@0: // We couldn't dispatch touchend. Remove the touch from gCaptureTouchList michael@0: // explicitly. michael@0: nsIPresShell::gCaptureTouchList->Remove(aTouch->Identifier()); michael@0: } michael@0: } michael@0: michael@0: static PLDHashOperator michael@0: AppendToTouchList(const uint32_t& aKey, nsRefPtr& aData, void *aTouchList) michael@0: { michael@0: nsTArray< nsRefPtr >* touches = michael@0: static_cast >*>(aTouchList); michael@0: aData->mChanged = false; michael@0: touches->AppendElement(aData); michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: void michael@0: PresShell::EvictTouches() michael@0: { michael@0: nsTArray< nsRefPtr > touches; michael@0: gCaptureTouchList->Enumerate(&AppendToTouchList, &touches); michael@0: for (uint32_t i = 0; i < touches.Length(); ++i) { michael@0: EvictTouchPoint(touches[i], mDocument); michael@0: } michael@0: } michael@0: michael@0: static PLDHashOperator michael@0: FindAnyTarget(const uint32_t& aKey, nsRefPtr& aData, michael@0: void* aAnyTarget) michael@0: { michael@0: if (aData) { michael@0: dom::EventTarget* target = aData->Target(); michael@0: if (target) { michael@0: nsCOMPtr* content = michael@0: static_cast*>(aAnyTarget); michael@0: *content = do_QueryInterface(target); michael@0: return PL_DHASH_STOP; michael@0: } michael@0: } michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: nsIFrame* GetNearestFrameContainingPresShell(nsIPresShell* aPresShell) michael@0: { michael@0: nsView* view = aPresShell->GetViewManager()->GetRootView(); michael@0: while (view && !view->GetFrame()) { michael@0: view = view->GetParent(); michael@0: } michael@0: michael@0: nsIFrame* frame = nullptr; michael@0: if (view) { michael@0: frame = view->GetFrame(); michael@0: } michael@0: michael@0: return frame; michael@0: } michael@0: michael@0: static bool michael@0: FlushThrottledStyles(nsIDocument *aDocument, void *aData) michael@0: { michael@0: nsIPresShell* shell = aDocument->GetShell(); michael@0: if (shell && shell->IsVisible()) { michael@0: nsPresContext* presContext = shell->GetPresContext(); michael@0: if (presContext) { michael@0: presContext->TransitionManager()->UpdateAllThrottledStyles(); michael@0: presContext->AnimationManager()->UpdateAllThrottledStyles(); michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: static nsresult michael@0: DispatchPointerFromMouseOrTouch(PresShell* aShell, michael@0: nsIFrame* aFrame, michael@0: WidgetGUIEvent* aEvent, michael@0: bool aDontRetargetEvents, michael@0: nsEventStatus* aStatus) michael@0: { michael@0: uint32_t pointerMessage = 0; michael@0: if (aEvent->eventStructType == NS_MOUSE_EVENT) { michael@0: WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent(); michael@0: // if it is not mouse then it is likely will come as touch event michael@0: if (!mouseEvent->convertToPointer) { michael@0: return NS_OK; michael@0: } michael@0: int16_t button = mouseEvent->button; michael@0: switch (mouseEvent->message) { michael@0: case NS_MOUSE_MOVE: michael@0: if (mouseEvent->buttons == 0) { michael@0: button = -1; michael@0: } michael@0: pointerMessage = NS_POINTER_MOVE; michael@0: break; michael@0: case NS_MOUSE_BUTTON_UP: michael@0: pointerMessage = NS_POINTER_UP; michael@0: break; michael@0: case NS_MOUSE_BUTTON_DOWN: michael@0: pointerMessage = NS_POINTER_DOWN; michael@0: break; michael@0: default: michael@0: return NS_OK; michael@0: } michael@0: michael@0: WidgetPointerEvent event(*mouseEvent); michael@0: event.message = pointerMessage; michael@0: event.button = button; michael@0: event.pressure = event.buttons ? michael@0: mouseEvent->pressure ? mouseEvent->pressure : 0.5f : michael@0: 0.0f; michael@0: event.convertToPointer = mouseEvent->convertToPointer = false; michael@0: aShell->HandleEvent(aFrame, &event, aDontRetargetEvents, aStatus); michael@0: } else if (aEvent->eventStructType == NS_TOUCH_EVENT) { michael@0: WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent(); michael@0: // loop over all touches and dispatch pointer events on each touch michael@0: // copy the event michael@0: switch (touchEvent->message) { michael@0: case NS_TOUCH_MOVE: michael@0: pointerMessage = NS_POINTER_MOVE; michael@0: break; michael@0: case NS_TOUCH_END: michael@0: pointerMessage = NS_POINTER_UP; michael@0: break; michael@0: case NS_TOUCH_START: michael@0: pointerMessage = NS_POINTER_DOWN; michael@0: break; michael@0: case NS_TOUCH_CANCEL: michael@0: pointerMessage = NS_POINTER_CANCEL; michael@0: break; michael@0: default: michael@0: return NS_OK; michael@0: } michael@0: michael@0: for (uint32_t i = 0; i < touchEvent->touches.Length(); ++i) { michael@0: mozilla::dom::Touch* touch = touchEvent->touches[i]; michael@0: if (!touch || !touch->convertToPointer) { michael@0: continue; michael@0: } michael@0: michael@0: WidgetPointerEvent event(touchEvent->mFlags.mIsTrusted, pointerMessage, touchEvent->widget); michael@0: event.isPrimary = i == 0; michael@0: event.pointerId = touch->Identifier(); michael@0: event.refPoint.x = touch->mRefPoint.x; michael@0: event.refPoint.y = touch->mRefPoint.y; michael@0: event.modifiers = touchEvent->modifiers; michael@0: event.width = touch->RadiusX(); michael@0: event.height = touch->RadiusY(); michael@0: event.tiltX = touch->tiltX; michael@0: event.tiltY = touch->tiltY; michael@0: event.time = touchEvent->time; michael@0: event.mFlags = touchEvent->mFlags; michael@0: event.button = WidgetMouseEvent::eLeftButton; michael@0: event.buttons = WidgetMouseEvent::eLeftButtonFlag; michael@0: event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH; michael@0: event.convertToPointer = touch->convertToPointer = false; michael@0: aShell->HandleEvent(aFrame, &event, aDontRetargetEvents, aStatus); michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: class ReleasePointerCaptureCaller michael@0: { michael@0: public: michael@0: ReleasePointerCaptureCaller() : michael@0: mPointerId(0), michael@0: mContent(nullptr) michael@0: { michael@0: } michael@0: ~ReleasePointerCaptureCaller() michael@0: { michael@0: if (mContent) { michael@0: nsIPresShell::ReleasePointerCapturingContent(mPointerId, mContent); michael@0: } michael@0: } michael@0: void SetTarget(uint32_t aPointerId, nsIContent* aContent) michael@0: { michael@0: mPointerId = aPointerId; michael@0: mContent = aContent; michael@0: } michael@0: michael@0: private: michael@0: int32_t mPointerId; michael@0: nsCOMPtr mContent; michael@0: }; michael@0: michael@0: nsresult michael@0: PresShell::HandleEvent(nsIFrame* aFrame, michael@0: WidgetGUIEvent* aEvent, michael@0: bool aDontRetargetEvents, michael@0: nsEventStatus* aEventStatus) michael@0: { michael@0: #ifdef MOZ_TASK_TRACER michael@0: // Make touch events, mouse events and hardware key events to be the source michael@0: // events of TaskTracer, and originate the rest correlation tasks from here. michael@0: SourceEventType type = SourceEventType::UNKNOWN; michael@0: if (WidgetTouchEvent* inputEvent = aEvent->AsTouchEvent()) { michael@0: type = SourceEventType::TOUCH; michael@0: } else if (WidgetMouseEvent* inputEvent = aEvent->AsMouseEvent()) { michael@0: type = SourceEventType::MOUSE; michael@0: } else if (WidgetKeyboardEvent* inputEvent = aEvent->AsKeyboardEvent()) { michael@0: type = SourceEventType::KEY; michael@0: } michael@0: AutoSourceEvent taskTracerEvent(type); michael@0: #endif michael@0: michael@0: if (sPointerEventEnabled) { michael@0: DispatchPointerFromMouseOrTouch(this, aFrame, aEvent, aDontRetargetEvents, aEventStatus); michael@0: } michael@0: michael@0: NS_ASSERTION(aFrame, "null frame"); michael@0: michael@0: if (mIsDestroying || michael@0: (sDisableNonTestMouseEvents && !aEvent->mFlags.mIsSynthesizedForTests && michael@0: aEvent->HasMouseEventMessage())) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: RecordMouseLocation(aEvent); michael@0: if (sPointerEventEnabled) { michael@0: UpdateActivePointerState(aEvent); michael@0: } michael@0: michael@0: if (!nsContentUtils::IsSafeToRunScript()) michael@0: return NS_OK; michael@0: michael@0: nsIContent* capturingContent = michael@0: (aEvent->HasMouseEventMessage() || michael@0: aEvent->eventStructType == NS_WHEEL_EVENT ? GetCapturingContent() : michael@0: nullptr); michael@0: michael@0: nsCOMPtr retargetEventDoc; michael@0: if (!aDontRetargetEvents) { michael@0: // key and IME related events should not cross top level window boundary. michael@0: // Basically, such input events should be fired only on focused widget. michael@0: // However, some IMEs might need to clean up composition after focused michael@0: // window is deactivated. And also some tests on MozMill want to test key michael@0: // handling on deactivated window because MozMill window can be activated michael@0: // during tests. So, there is no merit the events should be redirected to michael@0: // active window. So, the events should be handled on the last focused michael@0: // content in the last focused DOM window in same top level window. michael@0: // Note, if no DOM window has been focused yet, we can discard the events. michael@0: if (aEvent->IsTargetedAtFocusedWindow()) { michael@0: nsCOMPtr window = GetFocusedDOMWindowInOurWindow(); michael@0: // No DOM window in same top level window has not been focused yet, michael@0: // discard the events. michael@0: if (!window) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: retargetEventDoc = window->GetExtantDoc(); michael@0: if (!retargetEventDoc) michael@0: return NS_OK; michael@0: } else if (capturingContent) { michael@0: // if the mouse is being captured then retarget the mouse event at the michael@0: // document that is being captured. michael@0: retargetEventDoc = capturingContent->GetCurrentDoc(); michael@0: #ifdef ANDROID michael@0: } else if (aEvent->eventStructType == NS_TOUCH_EVENT) { michael@0: retargetEventDoc = GetTouchEventTargetDocument(); michael@0: #endif michael@0: } michael@0: michael@0: if (retargetEventDoc) { michael@0: nsCOMPtr presShell = retargetEventDoc->GetShell(); michael@0: if (!presShell) michael@0: return NS_OK; michael@0: michael@0: if (presShell != this) { michael@0: nsIFrame* frame = presShell->GetRootFrame(); michael@0: if (!frame) { michael@0: if (aEvent->message == NS_QUERY_TEXT_CONTENT || michael@0: aEvent->IsContentCommandEvent()) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: frame = GetNearestFrameContainingPresShell(presShell); michael@0: } michael@0: michael@0: if (!frame) michael@0: return NS_OK; michael@0: michael@0: nsCOMPtr shell = frame->PresContext()->GetPresShell(); michael@0: return shell->HandleEvent(frame, aEvent, true, aEventStatus); michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (aEvent->eventStructType == NS_KEY_EVENT && michael@0: mDocument && mDocument->EventHandlingSuppressed()) { michael@0: if (aEvent->message == NS_KEY_DOWN) { michael@0: mNoDelayedKeyEvents = true; michael@0: } else if (!mNoDelayedKeyEvents) { michael@0: DelayedEvent* event = new DelayedKeyEvent(aEvent->AsKeyboardEvent()); michael@0: if (!mDelayedEvents.AppendElement(event)) { michael@0: delete event; michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsIFrame* frame = aFrame; michael@0: michael@0: if (aEvent->IsUsingCoordinates()) { michael@0: ReleasePointerCaptureCaller releasePointerCaptureCaller; michael@0: if (nsLayoutUtils::AreAsyncAnimationsEnabled() && mDocument) { michael@0: if (aEvent->eventStructType == NS_TOUCH_EVENT) { michael@0: nsIDocument::UnlockPointer(); michael@0: } michael@0: michael@0: { // scope for scriptBlocker. michael@0: nsAutoScriptBlocker scriptBlocker; michael@0: GetRootPresShell()->GetDocument()-> michael@0: EnumerateSubDocuments(FlushThrottledStyles, nullptr); michael@0: } michael@0: frame = GetNearestFrameContainingPresShell(this); michael@0: } michael@0: michael@0: NS_WARN_IF_FALSE(frame, "Nothing to handle this event!"); michael@0: if (!frame) michael@0: return NS_OK; michael@0: michael@0: nsPresContext* framePresContext = frame->PresContext(); michael@0: nsPresContext* rootPresContext = framePresContext->GetRootPresContext(); michael@0: NS_ASSERTION(rootPresContext == mPresContext->GetRootPresContext(), michael@0: "How did we end up outside the connected prescontext/viewmanager hierarchy?"); michael@0: // If we aren't starting our event dispatch from the root frame of the root prescontext, michael@0: // then someone must be capturing the mouse. In that case we don't want to search the popup michael@0: // list. michael@0: if (framePresContext == rootPresContext && michael@0: frame == mFrameConstructor->GetRootFrame()) { michael@0: nsIFrame* popupFrame = michael@0: nsLayoutUtils::GetPopupFrameForEventCoordinates(rootPresContext, aEvent); michael@0: // If the popupFrame is an ancestor of the 'frame', the frame should michael@0: // handle the event, otherwise, the popup should handle it. michael@0: if (popupFrame && michael@0: !nsContentUtils::ContentIsCrossDocDescendantOf( michael@0: framePresContext->GetPresShell()->GetDocument(), michael@0: popupFrame->GetContent())) { michael@0: frame = popupFrame; michael@0: } michael@0: } michael@0: michael@0: bool captureRetarget = false; michael@0: if (capturingContent) { michael@0: // If a capture is active, determine if the docshell is visible. If not, michael@0: // clear the capture and target the mouse event normally instead. This michael@0: // would occur if the mouse button is held down while a tab change occurs. michael@0: // If the docshell is visible, look for a scrolling container. michael@0: bool vis; michael@0: nsCOMPtr baseWin = michael@0: do_QueryInterface(mPresContext->GetContainerWeak()); michael@0: if (baseWin && NS_SUCCEEDED(baseWin->GetVisibility(&vis)) && vis) { michael@0: captureRetarget = gCaptureInfo.mRetargetToElement; michael@0: if (!captureRetarget) { michael@0: // A check was already done above to ensure that capturingContent is michael@0: // in this presshell. michael@0: NS_ASSERTION(capturingContent->GetCurrentDoc() == GetDocument(), michael@0: "Unexpected document"); michael@0: nsIFrame* captureFrame = capturingContent->GetPrimaryFrame(); michael@0: if (captureFrame) { michael@0: if (capturingContent->Tag() == nsGkAtoms::select && michael@0: capturingContent->IsHTML()) { michael@0: // a dropdown