mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-02 07:05:24 +00:00
1018 lines
39 KiB
C++
1018 lines
39 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
* vim: set ts=2 sw=2 et tw=78:
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* This Original Code has been modified by IBM Corporation.
|
|
* Modifications made by IBM described herein are
|
|
* Copyright (c) International Business Machines
|
|
* Corporation, 2000
|
|
*
|
|
* Modifications to Mozilla code or documentation
|
|
* identified per MPL Section 3.3
|
|
*
|
|
* Date Modified by Description of modification
|
|
* 05/03/2000 IBM Corp. Observer events for reflow states
|
|
*/
|
|
|
|
/* a presentation of a document, part 2 */
|
|
|
|
#ifndef nsPresShell_h_
|
|
#define nsPresShell_h_
|
|
|
|
#include "nsIPresShell.h"
|
|
#include "nsStubDocumentObserver.h"
|
|
#include "nsISelectionController.h"
|
|
#include "nsIObserver.h"
|
|
#include "nsWeakReference.h"
|
|
#include "nsCRT.h"
|
|
#include "nsAutoPtr.h"
|
|
#include "nsIWidget.h"
|
|
#include "nsContentUtils.h" // For AddScriptBlocker().
|
|
#include "nsRefreshDriver.h"
|
|
#include "TouchManager.h"
|
|
#include "mozilla/Attributes.h"
|
|
#include "mozilla/EventForwards.h"
|
|
#include "mozilla/MemoryReporting.h"
|
|
#include "mozilla/StyleSetHandle.h"
|
|
#include "mozilla/UniquePtr.h"
|
|
#include "MobileViewportManager.h"
|
|
#include "ZoomConstraintsClient.h"
|
|
|
|
class nsRange;
|
|
|
|
struct RangePaintInfo;
|
|
struct nsCallbackEventRequest;
|
|
#ifdef MOZ_REFLOW_PERF
|
|
class ReflowCountMgr;
|
|
#endif
|
|
|
|
class nsPresShellEventCB;
|
|
class nsAutoCauseReflowNotifier;
|
|
|
|
namespace mozilla {
|
|
|
|
class EventDispatchingCallback;
|
|
|
|
// A set type for tracking visible frames, for use by the visibility code in
|
|
// PresShell. The set contains nsIFrame* pointers.
|
|
typedef nsTHashtable<nsPtrHashKey<nsIFrame>> VisibleFrames;
|
|
|
|
// A hash table type for tracking visible regions, for use by the visibility
|
|
// code in PresShell. The mapping is from view IDs to regions in the
|
|
// coordinate system of that view's scrolled frame.
|
|
typedef nsClassHashtable<nsUint64HashKey, mozilla::CSSIntRegion> VisibleRegions;
|
|
|
|
} // namespace mozilla
|
|
|
|
// 250ms. This is actually pref-controlled, but we use this value if we fail
|
|
// to get the pref for any reason.
|
|
#define PAINTLOCK_EVENT_DELAY 250
|
|
|
|
class PresShell final : public nsIPresShell,
|
|
public nsStubDocumentObserver,
|
|
public nsISelectionController,
|
|
public nsIObserver,
|
|
public nsSupportsWeakReference
|
|
{
|
|
template <typename T> using Maybe = mozilla::Maybe<T>;
|
|
using Nothing = mozilla::Nothing;
|
|
using OnNonvisible = mozilla::OnNonvisible;
|
|
template <typename T> using UniquePtr = mozilla::UniquePtr<T>;
|
|
using VisibilityCounter = mozilla::VisibilityCounter;
|
|
using VisibleFrames = mozilla::VisibleFrames;
|
|
using VisibleRegions = mozilla::VisibleRegions;
|
|
|
|
public:
|
|
PresShell();
|
|
|
|
NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW
|
|
|
|
// nsISupports
|
|
NS_DECL_ISUPPORTS
|
|
|
|
static bool AccessibleCaretEnabled();
|
|
|
|
// BeforeAfterKeyboardEvent preference
|
|
static bool BeforeAfterKeyboardEventEnabled();
|
|
|
|
static bool IsTargetIframe(nsINode* aTarget);
|
|
|
|
void Init(nsIDocument* aDocument, nsPresContext* aPresContext,
|
|
nsViewManager* aViewManager, mozilla::StyleSetHandle aStyleSet);
|
|
virtual void Destroy() override;
|
|
virtual void MakeZombie() override;
|
|
|
|
virtual void UpdatePreferenceStyles() override;
|
|
|
|
NS_IMETHOD GetSelection(SelectionType aType, nsISelection** aSelection) override;
|
|
virtual mozilla::dom::Selection* GetCurrentSelection(SelectionType aType) override;
|
|
|
|
NS_IMETHOD SetDisplaySelection(int16_t aToggle) override;
|
|
NS_IMETHOD GetDisplaySelection(int16_t *aToggle) override;
|
|
NS_IMETHOD ScrollSelectionIntoView(SelectionType aType, SelectionRegion aRegion,
|
|
int16_t aFlags) override;
|
|
NS_IMETHOD RepaintSelection(SelectionType aType) override;
|
|
|
|
virtual void BeginObservingDocument() override;
|
|
virtual void EndObservingDocument() override;
|
|
virtual nsresult Initialize(nscoord aWidth, nscoord aHeight) override;
|
|
virtual nsresult ResizeReflow(nscoord aWidth, nscoord aHeight, nscoord aOldWidth = 0, nscoord aOldHeight = 0) override;
|
|
virtual nsresult ResizeReflowIgnoreOverride(nscoord aWidth, nscoord aHeight, nscoord aOldWidth, nscoord aOldHeight) override;
|
|
virtual nsIPageSequenceFrame* GetPageSequenceFrame() const override;
|
|
virtual nsCanvasFrame* GetCanvasFrame() const override;
|
|
|
|
virtual nsIFrame* GetPlaceholderFrameFor(nsIFrame* aFrame) const override;
|
|
virtual void FrameNeedsReflow(nsIFrame *aFrame, IntrinsicDirty aIntrinsicDirty,
|
|
nsFrameState aBitToAdd,
|
|
ReflowRootHandling aRootHandling =
|
|
eInferFromBitToAdd) override;
|
|
virtual void FrameNeedsToContinueReflow(nsIFrame *aFrame) override;
|
|
virtual void CancelAllPendingReflows() override;
|
|
virtual bool IsSafeToFlush() const override;
|
|
virtual void FlushPendingNotifications(mozFlushType aType) override;
|
|
virtual void FlushPendingNotifications(mozilla::ChangesToFlush aType) override;
|
|
virtual void DestroyFramesFor(nsIContent* aContent,
|
|
nsIContent** aDestroyedFramesFor) override;
|
|
virtual void CreateFramesFor(nsIContent* aContent) override;
|
|
|
|
/**
|
|
* Recreates the frames for a node
|
|
*/
|
|
virtual nsresult RecreateFramesFor(nsIContent* aContent) override;
|
|
|
|
/**
|
|
* Post a callback that should be handled after reflow has finished.
|
|
*/
|
|
virtual nsresult PostReflowCallback(nsIReflowCallback* aCallback) override;
|
|
virtual void CancelReflowCallback(nsIReflowCallback* aCallback) override;
|
|
|
|
virtual void ClearFrameRefs(nsIFrame* aFrame) override;
|
|
virtual already_AddRefed<gfxContext> CreateReferenceRenderingContext() override;
|
|
virtual nsresult GoToAnchor(const nsAString& aAnchorName, bool aScroll,
|
|
uint32_t aAdditionalScrollFlags = 0) override;
|
|
virtual nsresult ScrollToAnchor() override;
|
|
|
|
virtual nsresult ScrollContentIntoView(nsIContent* aContent,
|
|
ScrollAxis aVertical,
|
|
ScrollAxis aHorizontal,
|
|
uint32_t aFlags) override;
|
|
virtual bool ScrollFrameRectIntoView(nsIFrame* aFrame,
|
|
const nsRect& aRect,
|
|
ScrollAxis aVertical,
|
|
ScrollAxis aHorizontal,
|
|
uint32_t aFlags) override;
|
|
virtual nsRectVisibility GetRectVisibility(nsIFrame *aFrame,
|
|
const nsRect &aRect,
|
|
nscoord aMinTwips) const override;
|
|
|
|
virtual void SetIgnoreFrameDestruction(bool aIgnore) override;
|
|
virtual void NotifyDestroyingFrame(nsIFrame* aFrame) override;
|
|
|
|
virtual nsresult CaptureHistoryState(nsILayoutHistoryState** aLayoutHistoryState) override;
|
|
|
|
virtual void UnsuppressPainting() override;
|
|
|
|
virtual nsresult GetAgentStyleSheets(
|
|
nsTArray<mozilla::StyleSheetHandle::RefPtr>& aSheets) override;
|
|
virtual nsresult SetAgentStyleSheets(
|
|
const nsTArray<mozilla::StyleSheetHandle::RefPtr>& aSheets) override;
|
|
|
|
virtual nsresult AddOverrideStyleSheet(mozilla::StyleSheetHandle aSheet) override;
|
|
virtual nsresult RemoveOverrideStyleSheet(mozilla::StyleSheetHandle aSheet) override;
|
|
|
|
virtual nsresult HandleEventWithTarget(
|
|
mozilla::WidgetEvent* aEvent,
|
|
nsIFrame* aFrame,
|
|
nsIContent* aContent,
|
|
nsEventStatus* aStatus) override;
|
|
virtual nsIFrame* GetEventTargetFrame() override;
|
|
virtual already_AddRefed<nsIContent> GetEventTargetContent(
|
|
mozilla::WidgetEvent* aEvent) override;
|
|
|
|
virtual void NotifyCounterStylesAreDirty() override;
|
|
|
|
virtual nsresult ReconstructFrames(void) override;
|
|
virtual void Freeze() override;
|
|
virtual void Thaw() override;
|
|
virtual void FireOrClearDelayedEvents(bool aFireEvents) override;
|
|
|
|
virtual nsresult RenderDocument(const nsRect& aRect, uint32_t aFlags,
|
|
nscolor aBackgroundColor,
|
|
gfxContext* aThebesContext) override;
|
|
|
|
virtual already_AddRefed<SourceSurface>
|
|
RenderNode(nsIDOMNode* aNode,
|
|
nsIntRegion* aRegion,
|
|
nsIntPoint& aPoint,
|
|
nsIntRect* aScreenRect,
|
|
uint32_t aFlags) override;
|
|
|
|
virtual already_AddRefed<SourceSurface>
|
|
RenderSelection(nsISelection* aSelection,
|
|
nsIntPoint& aPoint,
|
|
nsIntRect* aScreenRect,
|
|
uint32_t aFlags) override;
|
|
|
|
virtual already_AddRefed<nsPIDOMWindowOuter> GetRootWindow() override;
|
|
|
|
virtual LayerManager* GetLayerManager() override;
|
|
|
|
virtual bool AsyncPanZoomEnabled() override;
|
|
|
|
virtual void SetIgnoreViewportScrolling(bool aIgnore) override;
|
|
|
|
virtual nsresult SetResolution(float aResolution) override {
|
|
return SetResolutionImpl(aResolution, /* aScaleToResolution = */ false);
|
|
}
|
|
virtual nsresult SetResolutionAndScaleTo(float aResolution) override {
|
|
return SetResolutionImpl(aResolution, /* aScaleToResolution = */ true);
|
|
}
|
|
virtual bool ScaleToResolution() const override;
|
|
virtual float GetCumulativeResolution() override;
|
|
virtual float GetCumulativeNonRootScaleResolution() override;
|
|
virtual void SetRestoreResolution(float aResolution) override;
|
|
|
|
//nsIViewObserver interface
|
|
|
|
virtual void Paint(nsView* aViewToPaint, const nsRegion& aDirtyRegion,
|
|
uint32_t aFlags) override;
|
|
virtual nsresult HandleEvent(nsIFrame* aFrame,
|
|
mozilla::WidgetGUIEvent* aEvent,
|
|
bool aDontRetargetEvents,
|
|
nsEventStatus* aEventStatus,
|
|
nsIContent** aTargetContent) override;
|
|
virtual nsresult HandleDOMEventWithTarget(
|
|
nsIContent* aTargetContent,
|
|
mozilla::WidgetEvent* aEvent,
|
|
nsEventStatus* aStatus) override;
|
|
virtual nsresult HandleDOMEventWithTarget(nsIContent* aTargetContent,
|
|
nsIDOMEvent* aEvent,
|
|
nsEventStatus* aStatus) override;
|
|
virtual bool ShouldIgnoreInvalidation() override;
|
|
virtual void WillPaint() override;
|
|
virtual void WillPaintWindow() override;
|
|
virtual void DidPaintWindow() override;
|
|
virtual void ScheduleViewManagerFlush(PaintType aType = PAINT_DEFAULT) override;
|
|
virtual void DispatchSynthMouseMove(mozilla::WidgetGUIEvent* aEvent,
|
|
bool aFlushOnHoverChange) override;
|
|
virtual void ClearMouseCaptureOnView(nsView* aView) override;
|
|
virtual bool IsVisible() override;
|
|
|
|
virtual already_AddRefed<mozilla::AccessibleCaretEventHub> GetAccessibleCaretEventHub() const override;
|
|
|
|
// caret handling
|
|
virtual already_AddRefed<nsCaret> GetCaret() const override;
|
|
NS_IMETHOD SetCaretEnabled(bool aInEnable) override;
|
|
NS_IMETHOD SetCaretReadOnly(bool aReadOnly) override;
|
|
NS_IMETHOD GetCaretEnabled(bool *aOutEnabled) override;
|
|
NS_IMETHOD SetCaretVisibilityDuringSelection(bool aVisibility) override;
|
|
NS_IMETHOD GetCaretVisible(bool *_retval) override;
|
|
virtual void SetCaret(nsCaret *aNewCaret) override;
|
|
virtual void RestoreCaret() override;
|
|
|
|
NS_IMETHOD SetSelectionFlags(int16_t aInEnable) override;
|
|
NS_IMETHOD GetSelectionFlags(int16_t *aOutEnable) override;
|
|
|
|
// nsISelectionController
|
|
|
|
NS_IMETHOD PhysicalMove(int16_t aDirection, int16_t aAmount, bool aExtend) override;
|
|
NS_IMETHOD CharacterMove(bool aForward, bool aExtend) override;
|
|
NS_IMETHOD CharacterExtendForDelete() override;
|
|
NS_IMETHOD CharacterExtendForBackspace() override;
|
|
NS_IMETHOD WordMove(bool aForward, bool aExtend) override;
|
|
NS_IMETHOD WordExtendForDelete(bool aForward) override;
|
|
NS_IMETHOD LineMove(bool aForward, bool aExtend) override;
|
|
NS_IMETHOD IntraLineMove(bool aForward, bool aExtend) override;
|
|
NS_IMETHOD PageMove(bool aForward, bool aExtend) override;
|
|
NS_IMETHOD ScrollPage(bool aForward) override;
|
|
NS_IMETHOD ScrollLine(bool aForward) override;
|
|
NS_IMETHOD ScrollCharacter(bool aRight) override;
|
|
NS_IMETHOD CompleteScroll(bool aForward) override;
|
|
NS_IMETHOD CompleteMove(bool aForward, bool aExtend) override;
|
|
NS_IMETHOD SelectAll() override;
|
|
NS_IMETHOD CheckVisibility(nsIDOMNode *node, int16_t startOffset, int16_t EndOffset, bool *_retval) override;
|
|
virtual nsresult CheckVisibilityContent(nsIContent* aNode, int16_t aStartOffset,
|
|
int16_t aEndOffset, bool* aRetval) override;
|
|
|
|
// nsIDocumentObserver
|
|
NS_DECL_NSIDOCUMENTOBSERVER_BEGINUPDATE
|
|
NS_DECL_NSIDOCUMENTOBSERVER_ENDUPDATE
|
|
NS_DECL_NSIDOCUMENTOBSERVER_BEGINLOAD
|
|
NS_DECL_NSIDOCUMENTOBSERVER_ENDLOAD
|
|
NS_DECL_NSIDOCUMENTOBSERVER_CONTENTSTATECHANGED
|
|
NS_DECL_NSIDOCUMENTOBSERVER_DOCUMENTSTATESCHANGED
|
|
NS_DECL_NSIDOCUMENTOBSERVER_STYLESHEETADDED
|
|
NS_DECL_NSIDOCUMENTOBSERVER_STYLESHEETREMOVED
|
|
NS_DECL_NSIDOCUMENTOBSERVER_STYLESHEETAPPLICABLESTATECHANGED
|
|
NS_DECL_NSIDOCUMENTOBSERVER_STYLERULECHANGED
|
|
NS_DECL_NSIDOCUMENTOBSERVER_STYLERULEADDED
|
|
NS_DECL_NSIDOCUMENTOBSERVER_STYLERULEREMOVED
|
|
|
|
// nsIMutationObserver
|
|
NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
|
|
NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTEWILLCHANGE
|
|
NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
|
|
NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
|
|
NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
|
|
NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
|
|
|
|
NS_DECL_NSIOBSERVER
|
|
|
|
#ifdef MOZ_REFLOW_PERF
|
|
virtual void DumpReflows() override;
|
|
virtual void CountReflows(const char * aName, nsIFrame * aFrame) override;
|
|
virtual void PaintCount(const char * aName,
|
|
nsRenderingContext* aRenderingContext,
|
|
nsPresContext* aPresContext,
|
|
nsIFrame * aFrame,
|
|
const nsPoint& aOffset,
|
|
uint32_t aColor) override;
|
|
virtual void SetPaintFrameCount(bool aOn) override;
|
|
virtual bool IsPaintingFrameCounts() override;
|
|
#endif
|
|
|
|
#ifdef DEBUG
|
|
virtual void ListStyleContexts(nsIFrame *aRootFrame, FILE *out,
|
|
int32_t aIndent = 0) override;
|
|
|
|
virtual void ListStyleSheets(FILE *out, int32_t aIndent = 0) override;
|
|
virtual void VerifyStyleTree() override;
|
|
#endif
|
|
|
|
static mozilla::LazyLogModule gLog;
|
|
|
|
virtual void DisableNonTestMouseEvents(bool aDisable) override;
|
|
|
|
virtual void UpdateCanvasBackground() override;
|
|
|
|
virtual void AddCanvasBackgroundColorItem(nsDisplayListBuilder& aBuilder,
|
|
nsDisplayList& aList,
|
|
nsIFrame* aFrame,
|
|
const nsRect& aBounds,
|
|
nscolor aBackstopColor,
|
|
uint32_t aFlags) override;
|
|
|
|
virtual void AddPrintPreviewBackgroundItem(nsDisplayListBuilder& aBuilder,
|
|
nsDisplayList& aList,
|
|
nsIFrame* aFrame,
|
|
const nsRect& aBounds) override;
|
|
|
|
virtual nscolor ComputeBackstopColor(nsView* aDisplayRoot) override;
|
|
|
|
virtual void SetIsActive(bool aIsActive, bool aIsHidden = true) override;
|
|
|
|
virtual bool GetIsViewportOverridden() override {
|
|
return (mMobileViewportManager != nullptr);
|
|
}
|
|
|
|
virtual bool IsLayoutFlushObserver() override
|
|
{
|
|
return GetPresContext()->RefreshDriver()->
|
|
IsLayoutFlushObserver(this);
|
|
}
|
|
|
|
virtual void LoadComplete() override;
|
|
|
|
void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
|
|
nsArenaMemoryStats *aArenaObjectsSize,
|
|
size_t *aPresShellSize,
|
|
size_t *aStyleSetsSize,
|
|
size_t *aTextRunsSize,
|
|
size_t *aPresContextSize) override;
|
|
size_t SizeOfTextRuns(mozilla::MallocSizeOf aMallocSizeOf) const;
|
|
|
|
virtual void AddInvalidateHiddenPresShellObserver(nsRefreshDriver *aDriver) override;
|
|
|
|
// This data is stored as a content property (nsGkAtoms::scrolling) on
|
|
// mContentToScrollTo when we have a pending ScrollIntoView.
|
|
struct ScrollIntoViewData {
|
|
ScrollAxis mContentScrollVAxis;
|
|
ScrollAxis mContentScrollHAxis;
|
|
uint32_t mContentToScrollToFlags;
|
|
};
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Approximate frame visibility tracking public API.
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
void ScheduleApproximateFrameVisibilityUpdateSoon() override;
|
|
void ScheduleApproximateFrameVisibilityUpdateNow() override;
|
|
|
|
void RebuildApproximateFrameVisibilityDisplayList(const nsDisplayList& aList) override;
|
|
void RebuildApproximateFrameVisibility(nsRect* aRect = nullptr,
|
|
bool aRemoveOnly = false) override;
|
|
|
|
void MarkFrameVisible(nsIFrame* aFrame, VisibilityCounter aCounter) override;
|
|
void MarkFrameNonvisible(nsIFrame* aFrame) override;
|
|
|
|
bool AssumeAllFramesVisible() override;
|
|
|
|
|
|
virtual void RecordShadowStyleChange(mozilla::dom::ShadowRoot* aShadowRoot) override;
|
|
|
|
virtual void DispatchAfterKeyboardEvent(nsINode* aTarget,
|
|
const mozilla::WidgetKeyboardEvent& aEvent,
|
|
bool aEmbeddedCancelled) override;
|
|
|
|
virtual bool CanDispatchEvent(
|
|
const mozilla::WidgetGUIEvent* aEvent = nullptr) const override;
|
|
|
|
void SetNextPaintCompressed() { mNextPaintCompressed = true; }
|
|
|
|
protected:
|
|
virtual ~PresShell();
|
|
|
|
void HandlePostedReflowCallbacks(bool aInterruptible);
|
|
void CancelPostedReflowCallbacks();
|
|
|
|
void ScheduleBeforeFirstPaint();
|
|
void UnsuppressAndInvalidate();
|
|
|
|
void WillCauseReflow() {
|
|
nsContentUtils::AddScriptBlocker();
|
|
++mChangeNestCount;
|
|
}
|
|
nsresult DidCauseReflow();
|
|
friend class nsAutoCauseReflowNotifier;
|
|
|
|
nsresult DispatchEventToDOM(mozilla::WidgetEvent* aEvent,
|
|
nsEventStatus* aStatus,
|
|
nsPresShellEventCB* aEventCB);
|
|
void DispatchTouchEventToDOM(mozilla::WidgetEvent* aEvent,
|
|
nsEventStatus* aStatus,
|
|
nsPresShellEventCB* aEventCB,
|
|
bool aTouchIsNew);
|
|
|
|
void WillDoReflow();
|
|
|
|
/**
|
|
* Callback handler for whether reflow happened.
|
|
*
|
|
* @param aInterruptible Whether or not reflow interruption is allowed.
|
|
*/
|
|
void DidDoReflow(bool aInterruptible);
|
|
// ProcessReflowCommands returns whether we processed all our dirty roots
|
|
// without interruptions.
|
|
bool ProcessReflowCommands(bool aInterruptible);
|
|
// MaybeScheduleReflow checks if posting a reflow is needed, then checks if
|
|
// the last reflow was interrupted. In the interrupted case ScheduleReflow is
|
|
// called off a timer, otherwise it is called directly.
|
|
void MaybeScheduleReflow();
|
|
// Actually schedules a reflow. This should only be called by
|
|
// MaybeScheduleReflow and the reflow timer ScheduleReflowOffTimer
|
|
// sets up.
|
|
void ScheduleReflow();
|
|
|
|
// DoReflow returns whether the reflow finished without interruption
|
|
bool DoReflow(nsIFrame* aFrame, bool aInterruptible);
|
|
#ifdef DEBUG
|
|
void DoVerifyReflow();
|
|
void VerifyHasDirtyRootAncestor(nsIFrame* aFrame);
|
|
#endif
|
|
|
|
// Helper for ScrollContentIntoView
|
|
void DoScrollContentIntoView();
|
|
|
|
/**
|
|
* Initialize cached font inflation preference values and do an initial
|
|
* computation to determine if font inflation is enabled.
|
|
*
|
|
* @see nsLayoutUtils::sFontSizeInflationEmPerLine
|
|
* @see nsLayoutUtils::sFontSizeInflationMinTwips
|
|
* @see nsLayoutUtils::sFontSizeInflationLineThreshold
|
|
*/
|
|
void SetupFontInflation();
|
|
|
|
friend struct AutoRenderingStateSaveRestore;
|
|
friend struct RenderingState;
|
|
|
|
struct RenderingState {
|
|
explicit RenderingState(PresShell* aPresShell)
|
|
: mResolution(aPresShell->mResolution)
|
|
, mRenderFlags(aPresShell->mRenderFlags)
|
|
{ }
|
|
Maybe<float> mResolution;
|
|
RenderFlags mRenderFlags;
|
|
};
|
|
|
|
struct AutoSaveRestoreRenderingState {
|
|
explicit AutoSaveRestoreRenderingState(PresShell* aPresShell)
|
|
: mPresShell(aPresShell)
|
|
, mOldState(aPresShell)
|
|
{}
|
|
|
|
~AutoSaveRestoreRenderingState()
|
|
{
|
|
mPresShell->mRenderFlags = mOldState.mRenderFlags;
|
|
mPresShell->mResolution = mOldState.mResolution;
|
|
}
|
|
|
|
PresShell* mPresShell;
|
|
RenderingState mOldState;
|
|
};
|
|
static RenderFlags ChangeFlag(RenderFlags aFlags, bool aOnOff,
|
|
eRenderFlag aFlag)
|
|
{
|
|
return aOnOff ? (aFlags | aFlag) : (aFlag & ~aFlag);
|
|
}
|
|
|
|
|
|
void SetRenderingState(const RenderingState& aState);
|
|
|
|
friend class nsPresShellEventCB;
|
|
|
|
bool mCaretEnabled;
|
|
|
|
bool mIsHidden;
|
|
#ifdef DEBUG
|
|
nsStyleSet* CloneStyleSet(nsStyleSet* aSet);
|
|
bool VerifyIncrementalReflow();
|
|
bool mInVerifyReflow;
|
|
void ShowEventTargetDebug();
|
|
#endif
|
|
|
|
void RecordStyleSheetChange(mozilla::StyleSheetHandle aStyleSheet);
|
|
|
|
void RemovePreferenceStyles();
|
|
|
|
// methods for painting a range to an offscreen buffer
|
|
|
|
// given a display list, clip the items within the list to
|
|
// the range
|
|
nsRect ClipListToRange(nsDisplayListBuilder *aBuilder,
|
|
nsDisplayList* aList,
|
|
nsRange* aRange);
|
|
|
|
// create a RangePaintInfo for the range aRange containing the
|
|
// display list needed to paint the range to a surface
|
|
mozilla::UniquePtr<RangePaintInfo>
|
|
CreateRangePaintInfo(nsIDOMRange* aRange,
|
|
nsRect& aSurfaceRect,
|
|
bool aForPrimarySelection);
|
|
|
|
/*
|
|
* Paint the items to a new surface and return it.
|
|
*
|
|
* aSelection - selection being painted, if any
|
|
* aRegion - clip region, if any
|
|
* aArea - area that the surface occupies, relative to the root frame
|
|
* aPoint - reference point, typically the mouse position
|
|
* aScreenRect - [out] set to the area of the screen the painted area should
|
|
* be displayed at
|
|
* aFlags - set RENDER_AUTO_SCALE to scale down large images, but it must not
|
|
* be set if a custom image was specified
|
|
*/
|
|
already_AddRefed<SourceSurface>
|
|
PaintRangePaintInfo(const nsTArray<mozilla::UniquePtr<RangePaintInfo>>& aItems,
|
|
nsISelection* aSelection,
|
|
nsIntRegion* aRegion,
|
|
nsRect aArea,
|
|
nsIntPoint& aPoint,
|
|
nsIntRect* aScreenRect,
|
|
uint32_t aFlags);
|
|
|
|
/**
|
|
* Methods to handle changes to user and UA sheet lists that we get
|
|
* notified about.
|
|
*/
|
|
void AddUserSheet(nsISupports* aSheet);
|
|
void AddAgentSheet(nsISupports* aSheet);
|
|
void AddAuthorSheet(nsISupports* aSheet);
|
|
void RemoveSheet(mozilla::SheetType aType, nsISupports* aSheet);
|
|
|
|
/// @return the LayerManager at the root of the view tree.
|
|
LayerManager* GetRootLayerManager();
|
|
|
|
// Hide a view if it is a popup
|
|
void HideViewIfPopup(nsView* aView);
|
|
|
|
// Utility method to restore the root scrollframe state
|
|
void RestoreRootScrollPosition();
|
|
|
|
void MaybeReleaseCapturingContent();
|
|
|
|
nsresult HandleRetargetedEvent(mozilla::WidgetEvent* aEvent,
|
|
nsEventStatus* aStatus,
|
|
nsIContent* aTarget)
|
|
{
|
|
PushCurrentEventInfo(nullptr, nullptr);
|
|
mCurrentEventContent = aTarget;
|
|
nsresult rv = NS_OK;
|
|
if (GetCurrentEventFrame()) {
|
|
rv = HandleEventInternal(aEvent, aStatus, true);
|
|
}
|
|
PopCurrentEventInfo();
|
|
return rv;
|
|
}
|
|
|
|
class DelayedEvent
|
|
{
|
|
public:
|
|
virtual ~DelayedEvent() { }
|
|
virtual void Dispatch() { }
|
|
};
|
|
|
|
class DelayedInputEvent : public DelayedEvent
|
|
{
|
|
public:
|
|
virtual void Dispatch() override;
|
|
|
|
protected:
|
|
DelayedInputEvent();
|
|
virtual ~DelayedInputEvent();
|
|
|
|
mozilla::WidgetInputEvent* mEvent;
|
|
};
|
|
|
|
class DelayedMouseEvent : public DelayedInputEvent
|
|
{
|
|
public:
|
|
explicit DelayedMouseEvent(mozilla::WidgetMouseEvent* aEvent);
|
|
};
|
|
|
|
class DelayedKeyEvent : public DelayedInputEvent
|
|
{
|
|
public:
|
|
explicit DelayedKeyEvent(mozilla::WidgetKeyboardEvent* aEvent);
|
|
};
|
|
|
|
// Check if aEvent is a mouse event and record the mouse location for later
|
|
// synth mouse moves.
|
|
void RecordMouseLocation(mozilla::WidgetGUIEvent* aEvent);
|
|
class nsSynthMouseMoveEvent final : public nsARefreshObserver {
|
|
public:
|
|
nsSynthMouseMoveEvent(PresShell* aPresShell, bool aFromScroll)
|
|
: mPresShell(aPresShell), mFromScroll(aFromScroll) {
|
|
NS_ASSERTION(mPresShell, "null parameter");
|
|
}
|
|
|
|
private:
|
|
// Private destructor, to discourage deletion outside of Release():
|
|
~nsSynthMouseMoveEvent() {
|
|
Revoke();
|
|
}
|
|
|
|
public:
|
|
NS_INLINE_DECL_REFCOUNTING(nsSynthMouseMoveEvent, override)
|
|
|
|
void Revoke() {
|
|
if (mPresShell) {
|
|
mPresShell->GetPresContext()->RefreshDriver()->
|
|
RemoveRefreshObserver(this, Flush_Display);
|
|
mPresShell = nullptr;
|
|
}
|
|
}
|
|
virtual void WillRefresh(mozilla::TimeStamp aTime) override {
|
|
if (mPresShell) {
|
|
RefPtr<PresShell> shell = mPresShell;
|
|
shell->ProcessSynthMouseMoveEvent(mFromScroll);
|
|
}
|
|
}
|
|
private:
|
|
PresShell* mPresShell;
|
|
bool mFromScroll;
|
|
};
|
|
void ProcessSynthMouseMoveEvent(bool aFromScroll);
|
|
|
|
void QueryIsActive();
|
|
|
|
bool InZombieDocument(nsIContent *aContent);
|
|
already_AddRefed<nsIPresShell> GetParentPresShellForEventHandling();
|
|
nsIContent* GetCurrentEventContent();
|
|
nsIFrame* GetCurrentEventFrame();
|
|
nsresult RetargetEventToParent(mozilla::WidgetGUIEvent* aEvent,
|
|
nsEventStatus* aEventStatus);
|
|
void PushCurrentEventInfo(nsIFrame* aFrame, nsIContent* aContent);
|
|
void PopCurrentEventInfo();
|
|
/**
|
|
* @param aIsHandlingNativeEvent true when the caller (perhaps) handles
|
|
* an event which is caused by native
|
|
* event. Otherwise, false.
|
|
*/
|
|
nsresult HandleEventInternal(mozilla::WidgetEvent* aEvent,
|
|
nsEventStatus* aStatus,
|
|
bool aIsHandlingNativeEvent);
|
|
nsresult HandlePositionedEvent(nsIFrame* aTargetFrame,
|
|
mozilla::WidgetGUIEvent* aEvent,
|
|
nsEventStatus* aEventStatus);
|
|
// This returns the focused DOM window under our top level window.
|
|
// I.e., when we are deactive, this returns the *last* focused DOM window.
|
|
already_AddRefed<nsPIDOMWindowOuter> GetFocusedDOMWindowInOurWindow();
|
|
|
|
/*
|
|
* This and the next two helper methods are used to target and position the
|
|
* context menu when the keyboard shortcut is used to open it.
|
|
*
|
|
* If another menu is open, the context menu is opened relative to the
|
|
* active menuitem within the menu, or the menu itself if no item is active.
|
|
* Otherwise, if the caret is visible, the menu is opened near the caret.
|
|
* Otherwise, if a selectable list such as a listbox is focused, the
|
|
* current item within the menu is opened relative to this item.
|
|
* Otherwise, the context menu is opened at the topleft corner of the
|
|
* view.
|
|
*
|
|
* Returns true if the context menu event should fire and false if it should
|
|
* not.
|
|
*/
|
|
bool AdjustContextMenuKeyEvent(mozilla::WidgetMouseEvent* aEvent);
|
|
|
|
//
|
|
bool PrepareToUseCaretPosition(nsIWidget* aEventWidget,
|
|
mozilla::LayoutDeviceIntPoint& aTargetPt);
|
|
|
|
// Get the selected item and coordinates in device pixels relative to root
|
|
// document's root view for element, first ensuring the element is onscreen
|
|
void GetCurrentItemAndPositionForElement(nsIDOMElement *aCurrentEl,
|
|
nsIContent **aTargetToUse,
|
|
mozilla::LayoutDeviceIntPoint& aTargetPt,
|
|
nsIWidget *aRootWidget);
|
|
|
|
void FireResizeEvent();
|
|
static void AsyncResizeEventCallback(nsITimer* aTimer, void* aPresShell);
|
|
|
|
virtual void SynthesizeMouseMove(bool aFromScroll) override;
|
|
|
|
PresShell* GetRootPresShell();
|
|
|
|
nscolor GetDefaultBackgroundColorToDraw();
|
|
|
|
DOMHighResTimeStamp GetPerformanceNow();
|
|
|
|
// The callback for the mPaintSuppressionTimer timer.
|
|
static void sPaintSuppressionCallback(nsITimer* aTimer, void* aPresShell);
|
|
|
|
// The callback for the mReflowContinueTimer timer.
|
|
static void sReflowContinueCallback(nsITimer* aTimer, void* aPresShell);
|
|
bool ScheduleReflowOffTimer();
|
|
|
|
// Widget notificiations
|
|
virtual void WindowSizeMoveDone() override;
|
|
virtual void SysColorChanged() override { mPresContext->SysColorChanged(); }
|
|
virtual void ThemeChanged() override { mPresContext->ThemeChanged(); }
|
|
virtual void BackingScaleFactorChanged() override { mPresContext->UIResolutionChanged(); }
|
|
#ifdef ANDROID
|
|
virtual nsIDocument* GetTouchEventTargetDocument();
|
|
#endif
|
|
|
|
virtual void PausePainting() override;
|
|
virtual void ResumePainting() override;
|
|
|
|
void UpdateActivePointerState(mozilla::WidgetGUIEvent* aEvent);
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Approximate frame visibility tracking implementation.
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
void UpdateApproximateFrameVisibility();
|
|
void DoUpdateApproximateFrameVisibility(bool aRemoveOnly);
|
|
|
|
void ClearVisibleFramesSets(Maybe<OnNonvisible> aNonvisibleAction = Nothing());
|
|
static void ClearVisibleFramesForUnvisitedPresShells(nsView* aView, bool aClear);
|
|
static void MarkFramesInListApproximatelyVisible(const nsDisplayList& aList);
|
|
void MarkFramesInSubtreeApproximatelyVisible(nsIFrame* aFrame,
|
|
const nsRect& aRect,
|
|
bool aRemoveOnly = false);
|
|
void UpdateFrameVisibilityOnActiveStateChange();
|
|
|
|
void InitVisibleRegionsIfVisualizationEnabled(VisibilityCounter aForCounter);
|
|
void AddFrameToVisibleRegions(nsIFrame* aFrame, VisibilityCounter aForCounter);
|
|
void NotifyCompositorOfVisibleRegionsChange();
|
|
|
|
nsRevocableEventPtr<nsRunnableMethod<PresShell>> mUpdateApproximateFrameVisibilityEvent;
|
|
nsRevocableEventPtr<nsRunnableMethod<PresShell>> mNotifyCompositorOfVisibleRegionsChangeEvent;
|
|
|
|
struct VisibleFramesContainer
|
|
{
|
|
VisibleFramesContainer() : mSuppressingVisibility(false) { }
|
|
|
|
void AddFrame(nsIFrame* aFrame, VisibilityCounter aCounter);
|
|
void RemoveFrame(nsIFrame* aFrame, VisibilityCounter aCounter);
|
|
|
|
bool IsVisibilitySuppressed() const { return mSuppressingVisibility; }
|
|
void SuppressVisibility();
|
|
void UnsuppressVisibility();
|
|
|
|
VisibleFrames& ForCounter(VisibilityCounter aCounter)
|
|
{
|
|
switch (aCounter)
|
|
{
|
|
case VisibilityCounter::MAY_BECOME_VISIBLE: return mApproximate;
|
|
case VisibilityCounter::IN_DISPLAYPORT: return mInDisplayPort;
|
|
}
|
|
MOZ_CRASH();
|
|
}
|
|
|
|
// A set of frames that were visible or could be visible soon at the time
|
|
// that we last did an approximate frame visibility update.
|
|
VisibleFrames mApproximate;
|
|
|
|
// A set of frames that were visible in the displayport the last time we painted.
|
|
VisibleFrames mInDisplayPort;
|
|
|
|
bool mSuppressingVisibility;
|
|
};
|
|
|
|
VisibleFramesContainer mVisibleFrames;
|
|
|
|
struct VisibleRegionsContainer
|
|
{
|
|
VisibleRegions& ForCounter(VisibilityCounter aCounter)
|
|
{
|
|
switch (aCounter)
|
|
{
|
|
case VisibilityCounter::MAY_BECOME_VISIBLE: return mApproximate;
|
|
case VisibilityCounter::IN_DISPLAYPORT: return mInDisplayPort;
|
|
}
|
|
MOZ_CRASH();
|
|
}
|
|
|
|
// The approximately visible regions calculated during the last update to
|
|
// approximate frame visibility.
|
|
VisibleRegions mApproximate;
|
|
|
|
// The in-displayport visible regions calculated during the last paint.
|
|
VisibleRegions mInDisplayPort;
|
|
};
|
|
|
|
// The most recent visible regions we've computed. Only non-null if the APZ
|
|
// minimap visibility visualization was enabled during the last visibility
|
|
// update.
|
|
UniquePtr<VisibleRegionsContainer> mVisibleRegions;
|
|
|
|
friend struct AutoUpdateVisibility;
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Methods for dispatching KeyboardEvent and BeforeAfterKeyboardEvent.
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
void HandleKeyboardEvent(nsINode* aTarget,
|
|
mozilla::WidgetKeyboardEvent& aEvent,
|
|
bool aEmbeddedCancelled,
|
|
nsEventStatus* aStatus,
|
|
mozilla::EventDispatchingCallback* aEventCB);
|
|
void DispatchBeforeKeyboardEventInternal(
|
|
const nsTArray<nsCOMPtr<mozilla::dom::Element> >& aChain,
|
|
const mozilla::WidgetKeyboardEvent& aEvent,
|
|
size_t& aChainIndex,
|
|
bool& aDefaultPrevented);
|
|
void DispatchAfterKeyboardEventInternal(
|
|
const nsTArray<nsCOMPtr<mozilla::dom::Element> >& aChain,
|
|
const mozilla::WidgetKeyboardEvent& aEvent,
|
|
bool aEmbeddedCancelled,
|
|
size_t aChainIndex = 0);
|
|
|
|
#ifdef MOZ_B2G
|
|
// This method is used to forward the keyboard event to the input-method-app
|
|
// before the event is dispatched to its event target.
|
|
// Return true if it's successfully forwarded. Otherwise, return false.
|
|
bool ForwardKeyToInputMethodApp(nsINode* aTarget,
|
|
mozilla::WidgetKeyboardEvent& aEvent,
|
|
nsEventStatus* aStatus);
|
|
#endif // MOZ_B2G
|
|
|
|
// This method tries forwarding key events to the input-method-editor(IME).
|
|
// If the event isn't be forwarded, then it will be dispathed to its target.
|
|
// Return true when event is successfully forwarded to the input-method-editor.
|
|
// Otherwise, return false.
|
|
bool ForwardKeyToInputMethodAppOrDispatch(bool aIsTargetRemote,
|
|
nsINode* aTarget,
|
|
mozilla::WidgetKeyboardEvent& aEvent,
|
|
nsEventStatus* aStatus,
|
|
mozilla::EventDispatchingCallback* aEventCB);
|
|
|
|
nsresult SetResolutionImpl(float aResolution, bool aScaleToResolution);
|
|
|
|
#ifdef DEBUG
|
|
// The reflow root under which we're currently reflowing. Null when
|
|
// not in reflow.
|
|
nsIFrame* mCurrentReflowRoot;
|
|
uint32_t mUpdateCount;
|
|
#endif
|
|
|
|
#ifdef MOZ_REFLOW_PERF
|
|
ReflowCountMgr* mReflowCountMgr;
|
|
#endif
|
|
|
|
// This is used for synthetic mouse events that are sent when what is under
|
|
// the mouse pointer may have changed without the mouse moving (eg scrolling,
|
|
// change to the document contents).
|
|
// It is set only on a presshell for a root document, this value represents
|
|
// the last observed location of the mouse relative to that root document. It
|
|
// is set to (NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE) if the mouse isn't
|
|
// over our window or there is no last observed mouse location for some
|
|
// reason.
|
|
nsPoint mMouseLocation;
|
|
// This is an APZ state variable that tracks the target guid for the last
|
|
// mouse event that was processed (corresponding to mMouseLocation). This is
|
|
// needed for the synthetic mouse events.
|
|
mozilla::layers::ScrollableLayerGuid mMouseEventTargetGuid;
|
|
|
|
// mStyleSet owns it but we maintain a ref, may be null
|
|
mozilla::StyleSheetHandle::RefPtr mPrefStyleSheet;
|
|
|
|
// Set of frames that we should mark with NS_FRAME_HAS_DIRTY_CHILDREN after
|
|
// we finish reflowing mCurrentReflowRoot.
|
|
nsTHashtable<nsPtrHashKey<nsIFrame> > mFramesToDirty;
|
|
|
|
// Reflow roots that need to be reflowed.
|
|
nsTArray<nsIFrame*> mDirtyRoots;
|
|
|
|
nsTArray<nsAutoPtr<DelayedEvent> > mDelayedEvents;
|
|
nsRevocableEventPtr<nsRunnableMethod<PresShell> > mResizeEvent;
|
|
nsCOMPtr<nsITimer> mAsyncResizeEventTimer;
|
|
private:
|
|
nsIFrame* mCurrentEventFrame;
|
|
nsCOMPtr<nsIContent> mCurrentEventContent;
|
|
nsTArray<nsIFrame*> mCurrentEventFrameStack;
|
|
nsCOMArray<nsIContent> mCurrentEventContentStack;
|
|
protected:
|
|
nsRevocableEventPtr<nsSynthMouseMoveEvent> mSynthMouseMoveEvent;
|
|
nsCOMPtr<nsIContent> mLastAnchorScrolledTo;
|
|
RefPtr<nsCaret> mCaret;
|
|
RefPtr<nsCaret> mOriginalCaret;
|
|
nsCallbackEventRequest* mFirstCallbackEventRequest;
|
|
nsCallbackEventRequest* mLastCallbackEventRequest;
|
|
|
|
mozilla::TouchManager mTouchManager;
|
|
|
|
RefPtr<ZoomConstraintsClient> mZoomConstraintsClient;
|
|
RefPtr<MobileViewportManager> mMobileViewportManager;
|
|
|
|
RefPtr<mozilla::AccessibleCaretEventHub> mAccessibleCaretEventHub;
|
|
|
|
// This timer controls painting suppression. Until it fires
|
|
// or all frames are constructed, we won't paint anything but
|
|
// our <body> background and scrollbars.
|
|
nsCOMPtr<nsITimer> mPaintSuppressionTimer;
|
|
|
|
nsCOMPtr<nsITimer> mDelayedPaintTimer;
|
|
|
|
// The `performance.now()` value when we last started to process reflows.
|
|
DOMHighResTimeStamp mLastReflowStart;
|
|
|
|
mozilla::TimeStamp mLoadBegin; // used to time loads
|
|
|
|
// Information needed to properly handle scrolling content into view if the
|
|
// pre-scroll reflow flush can be interrupted. mContentToScrollTo is
|
|
// non-null between the initial scroll attempt and the first time we finish
|
|
// processing all our dirty roots. mContentToScrollTo has a content property
|
|
// storing the details for the scroll operation, see ScrollIntoViewData above.
|
|
nsCOMPtr<nsIContent> mContentToScrollTo;
|
|
|
|
nscoord mLastAnchorScrollPositionY;
|
|
|
|
// Information about live content (which still stay in DOM tree).
|
|
// Used in case we need re-dispatch event after sending pointer event,
|
|
// when target of pointer event was deleted during executing user handlers.
|
|
nsCOMPtr<nsIContent> mPointerEventTarget;
|
|
|
|
// This is used to protect ourselves from triggering reflow while in the
|
|
// middle of frame construction and the like... it really shouldn't be
|
|
// needed, one hopes, but it is for now.
|
|
uint16_t mChangeNestCount;
|
|
|
|
bool mDocumentLoading : 1;
|
|
bool mIgnoreFrameDestruction : 1;
|
|
bool mHaveShutDown : 1;
|
|
bool mLastRootReflowHadUnconstrainedBSize : 1;
|
|
bool mNoDelayedMouseEvents : 1;
|
|
bool mNoDelayedKeyEvents : 1;
|
|
|
|
// We've been disconnected from the document. We will refuse to paint the
|
|
// document until either our timer fires or all frames are constructed.
|
|
bool mIsDocumentGone : 1;
|
|
|
|
// Indicates that it is safe to unlock painting once all pending reflows
|
|
// have been processed.
|
|
bool mShouldUnsuppressPainting : 1;
|
|
|
|
bool mAsyncResizeTimerIsActive : 1;
|
|
bool mInResize : 1;
|
|
|
|
bool mApproximateFrameVisibilityVisited : 1;
|
|
|
|
bool mNextPaintCompressed : 1;
|
|
|
|
bool mHasCSSBackgroundColor : 1;
|
|
|
|
// Whether content should be scaled by the resolution amount. If this is
|
|
// not set, a transform that scales by the inverse of the resolution is
|
|
// applied to rendered layers.
|
|
bool mScaleToResolution : 1;
|
|
|
|
// Whether the last chrome-only escape key event is consumed.
|
|
bool mIsLastChromeOnlyEscapeKeyConsumed : 1;
|
|
|
|
// Whether the widget has received a paint message yet.
|
|
bool mHasReceivedPaintMessage : 1;
|
|
|
|
static bool sDisableNonTestMouseEvents;
|
|
};
|
|
|
|
#endif /* !defined(nsPresShell_h_) */
|