mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 67752. Implement interruptible reflow. r=roc,dbaron
This commit is contained in:
parent
98015bb24b
commit
05780cc568
@ -48,8 +48,12 @@ enum mozFlushType {
|
||||
notifications. */
|
||||
Flush_Style = 3, /* As above, plus flush style reresolution */
|
||||
Flush_Frames = Flush_Style,
|
||||
Flush_Layout = 4, /* As above, plus flush reflow */
|
||||
Flush_Display = 5 /* As above, plus flush painting */
|
||||
Flush_InterruptibleLayout = 4, /* As above, plus flush reflow,
|
||||
but allow it to be interrupted (so
|
||||
an incomplete layout may result) */
|
||||
Flush_Layout = 5, /* As above, but layout must run to
|
||||
completion */
|
||||
Flush_Display = 6 /* As above, plus flush painting */
|
||||
};
|
||||
|
||||
#endif /* mozFlushType_h___ */
|
||||
|
@ -6257,8 +6257,8 @@ nsDocument::FlushPendingNotifications(mozFlushType aType)
|
||||
// correct size to determine the correct style.
|
||||
if (mParentDocument && IsSafeToFlush()) {
|
||||
mozFlushType parentType = aType;
|
||||
if (aType == Flush_Style)
|
||||
parentType = Flush_Layout;
|
||||
if (aType >= Flush_Style)
|
||||
parentType = PR_MAX(Flush_Layout, aType);
|
||||
mParentDocument->FlushPendingNotifications(parentType);
|
||||
}
|
||||
|
||||
|
@ -5749,7 +5749,7 @@ nsEventStateManager::FlushPendingEvents(nsPresContext* aPresContext)
|
||||
NS_PRECONDITION(nsnull != aPresContext, "nsnull ptr");
|
||||
nsIPresShell *shell = aPresContext->GetPresShell();
|
||||
if (shell) {
|
||||
shell->FlushPendingNotifications(Flush_Display);
|
||||
shell->FlushPendingNotifications(Flush_InterruptibleLayout);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1308,9 +1308,8 @@ nsGenericHTMLElement::GetFormControlFrameFor(nsIContent* aContent,
|
||||
PRBool aFlushContent)
|
||||
{
|
||||
if (aFlushContent) {
|
||||
// Cause a flush of content, so we get up-to-date frame
|
||||
// information
|
||||
aDocument->FlushPendingNotifications(Flush_Layout);
|
||||
// Cause a flush of the frames, so we get up-to-date frame information
|
||||
aDocument->FlushPendingNotifications(Flush_Frames);
|
||||
}
|
||||
nsIFrame* frame = GetPrimaryFrameFor(aContent, aDocument);
|
||||
if (frame) {
|
||||
@ -2696,7 +2695,10 @@ nsGenericHTMLFormElement::SetFocusAndScrollIntoView(nsPresContext* aPresContext)
|
||||
{
|
||||
nsIEventStateManager *esm = aPresContext->EventStateManager();
|
||||
if (esm->SetContentState(this, NS_EVENT_STATE_FOCUS)) {
|
||||
nsIFormControlFrame* formControlFrame = GetFormControlFrame(PR_TRUE);
|
||||
// XXXldb once bug 43114 is fixed, we don't need to flush here.
|
||||
aPresContext->Document()->
|
||||
FlushPendingNotifications(Flush_InterruptibleLayout);
|
||||
nsIFormControlFrame* formControlFrame = GetFormControlFrame(PR_FALSE);
|
||||
if (formControlFrame) {
|
||||
formControlFrame->SetFocus(PR_TRUE, PR_TRUE);
|
||||
nsCOMPtr<nsIPresShell> presShell = aPresContext->GetPresShell();
|
||||
|
@ -3200,7 +3200,7 @@ HTMLContentSink::FlushPendingNotifications(mozFlushType aType)
|
||||
else if (mCurrentContext) {
|
||||
mCurrentContext->FlushText();
|
||||
}
|
||||
if (aType >= Flush_Layout) {
|
||||
if (aType >= Flush_InterruptibleLayout) {
|
||||
// Make sure that layout has started so that the reflow flush
|
||||
// will actually happen.
|
||||
StartLayout(PR_TRUE);
|
||||
|
@ -1653,7 +1653,7 @@ nsXMLContentSink::FlushPendingNotifications(mozFlushType aType)
|
||||
else {
|
||||
FlushText(PR_FALSE);
|
||||
}
|
||||
if (aType >= Flush_Layout) {
|
||||
if (aType >= Flush_InterruptibleLayout) {
|
||||
// Make sure that layout has started so that the reflow flush
|
||||
// will actually happen.
|
||||
MaybeStartLayout(PR_TRUE);
|
||||
|
@ -101,10 +101,10 @@ class nsPIDOMEventTarget;
|
||||
typedef short SelectionType;
|
||||
typedef PRUint32 nsFrameState;
|
||||
|
||||
// fa1bf801-9fb6-4d19-8d33-698e9961fc10
|
||||
// 4fb87dae-8986-429f-b6ba-f040750e3ee8
|
||||
#define NS_IPRESSHELL_IID \
|
||||
{ 0xfa1bf801, 0x9fb6, 0x4d19, \
|
||||
{ 0x8d, 0x33, 0x69, 0x8e, 0x99, 0x61, 0xfc, 0x10 } }
|
||||
{ 0x4fb87dae, 0x8986, 0x429f, \
|
||||
{ 0xb6, 0xba, 0xf0, 0x40, 0x75, 0x0e, 0x3e, 0xe8 } }
|
||||
|
||||
// Constants for ScrollContentIntoView() function
|
||||
#define NS_PRESSHELL_SCROLL_TOP 0
|
||||
@ -124,6 +124,8 @@ typedef PRUint32 nsFrameState;
|
||||
#define VERIFY_REFLOW_REALLY_NOISY_RC 0x20
|
||||
#define VERIFY_REFLOW_DURING_RESIZE_REFLOW 0x40
|
||||
|
||||
#undef NOISY_INTERRUPTIBLE_REFLOW
|
||||
|
||||
/**
|
||||
* Presentation shell interface. Presentation shells are the
|
||||
* controlling point for managing the presentation of a document. The
|
||||
@ -367,6 +369,19 @@ public:
|
||||
IntrinsicDirty aIntrinsicDirty,
|
||||
nsFrameState aBitToAdd) = 0;
|
||||
|
||||
/**
|
||||
* Tell the presshell that the given frame's reflow was interrupted. This
|
||||
* will mark as having dirty children a path from the given frame (inclusive)
|
||||
* to the nearest ancestor with a dirty subtree, or to the reflow root
|
||||
* currently being reflowed if no such ancestor exists (inclusive). This is
|
||||
* to be done immediately after reflow of the current reflow root completes.
|
||||
* This method must only be called during reflow, and the frame it's being
|
||||
* called on must be in the process of being reflowed when it's called. This
|
||||
* method doesn't mark any intrinsic widths dirty and doesn't add any bits
|
||||
* other than NS_FRAME_HAS_DIRTY_CHILDREN.
|
||||
*/
|
||||
NS_IMETHOD_(void) FrameNeedsToContinueReflow(nsIFrame *aFrame) = 0;
|
||||
|
||||
NS_IMETHOD CancelAllPendingReflows() = 0;
|
||||
|
||||
/**
|
||||
@ -461,7 +476,7 @@ public:
|
||||
*/
|
||||
NS_IMETHOD ScrollContentIntoView(nsIContent* aContent,
|
||||
PRIntn aVPercent,
|
||||
PRIntn aHPercent) const = 0;
|
||||
PRIntn aHPercent) = 0;
|
||||
|
||||
/**
|
||||
* Suppress notification of the frame manager that frames are
|
||||
|
@ -90,6 +90,8 @@
|
||||
#include "nsFontFaceLoader.h"
|
||||
#include "nsIEventListenerManager.h"
|
||||
#include "nsStyleStructInlines.h"
|
||||
#include "nsIAppShell.h"
|
||||
#include "prenv.h"
|
||||
|
||||
#ifdef MOZ_SMIL
|
||||
#include "nsSMILAnimationController.h"
|
||||
@ -2027,3 +2029,125 @@ nsPresContext::HasCachedStyleData()
|
||||
{
|
||||
return mShell && mShell->StyleSet()->HasCachedStyleData();
|
||||
}
|
||||
|
||||
static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
|
||||
static PRBool sGotInterruptEnv = PR_FALSE;
|
||||
static PRUint32 sInterruptSeed = 1;
|
||||
static PRUint32 sInterruptChecksToSkip = 200;
|
||||
enum InterruptMode {
|
||||
ModeRandom,
|
||||
ModeCounter,
|
||||
ModeEvent
|
||||
};
|
||||
static InterruptMode sInterruptMode = ModeEvent;
|
||||
static PRUint32 sInterruptCounter;
|
||||
static PRUint32 sInterruptMaxCounter = 10;
|
||||
|
||||
static void GetInterruptEnv()
|
||||
{
|
||||
char *ev = PR_GetEnv("GECKO_REFLOW_INTERRUPT_MODE");
|
||||
if (ev) {
|
||||
#ifndef XP_WIN
|
||||
if (PL_strcasecmp(ev, "random") == 0) {
|
||||
ev = PR_GetEnv("GECKO_REFLOW_INTERRUPT_SEED");
|
||||
if (ev) {
|
||||
sInterruptSeed = atoi(ev);
|
||||
}
|
||||
srandom(sInterruptSeed);
|
||||
sInterruptMode = ModeRandom;
|
||||
} else
|
||||
#endif
|
||||
if (PL_strcasecmp(ev, "counter") == 0) {
|
||||
ev = PR_GetEnv("GECKO_REFLOW_INTERRUPT_FREQUENCY");
|
||||
if (ev) {
|
||||
sInterruptMaxCounter = atoi(ev);
|
||||
}
|
||||
sInterruptCounter = 0;
|
||||
sInterruptMode = ModeCounter;
|
||||
}
|
||||
}
|
||||
ev = PR_GetEnv("GECKO_REFLOW_INTERRUPT_CHECKS_TO_SKIP");
|
||||
if (ev) {
|
||||
sInterruptChecksToSkip = atoi(ev);
|
||||
}
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsPresContext::HavePendingInputEvent()
|
||||
{
|
||||
switch (sInterruptMode) {
|
||||
#ifndef XP_WIN
|
||||
case ModeRandom:
|
||||
return (random() & 1);
|
||||
#endif
|
||||
case ModeCounter:
|
||||
if (sInterruptCounter < sInterruptMaxCounter) {
|
||||
++sInterruptCounter;
|
||||
return PR_FALSE;
|
||||
}
|
||||
sInterruptCounter = 0;
|
||||
return PR_TRUE;
|
||||
default:
|
||||
case ModeEvent: {
|
||||
nsIFrame* f = PresShell()->GetRootFrame();
|
||||
if (f) {
|
||||
nsIWidget* w = f->GetWindow();
|
||||
if (w) {
|
||||
return w->HasPendingInputEvent();
|
||||
}
|
||||
}
|
||||
return PR_FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsPresContext::ReflowStarted(PRBool aInterruptible)
|
||||
{
|
||||
// We don't support interrupting in paginated contexts, since page
|
||||
// sequences only handle initial reflow
|
||||
mInterruptsEnabled = aInterruptible && !IsPaginated();
|
||||
|
||||
// Don't set mHasPendingInterrupt based on HavePendingInputEvent() here. If
|
||||
// we ever change that, then we need to update the code in
|
||||
// PresShell::DoReflow to only add the just-reflown root to dirty roots if
|
||||
// it's actually dirty. Otherwise we can end up adding a root that has no
|
||||
// interruptible descendants, just because we detected an interrupt at reflow
|
||||
// start.
|
||||
mHasPendingInterrupt = PR_FALSE;
|
||||
|
||||
mInterruptChecksToSkip = sInterruptChecksToSkip;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsPresContext::CheckForInterrupt(nsIFrame* aFrame)
|
||||
{
|
||||
if (mHasPendingInterrupt) {
|
||||
mShell->FrameNeedsToContinueReflow(aFrame);
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
if (!sGotInterruptEnv) {
|
||||
sGotInterruptEnv = PR_TRUE;
|
||||
GetInterruptEnv();
|
||||
}
|
||||
|
||||
if (!mInterruptsEnabled) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
if (mInterruptChecksToSkip > 0) {
|
||||
--mInterruptChecksToSkip;
|
||||
return PR_FALSE;
|
||||
}
|
||||
mInterruptChecksToSkip = sInterruptChecksToSkip;
|
||||
|
||||
mHasPendingInterrupt = HavePendingInputEvent();
|
||||
if (mHasPendingInterrupt) {
|
||||
#ifdef NOISY_INTERRUPTIBLE_REFLOW
|
||||
printf("*** DETECTED pending interrupt (time=%lld)\n", PR_Now());
|
||||
#endif /* NOISY_INTERRUPTIBLE_REFLOW */
|
||||
mShell->FrameNeedsToContinueReflow(aFrame);
|
||||
}
|
||||
return mHasPendingInterrupt;
|
||||
}
|
||||
|
@ -806,6 +806,58 @@ public:
|
||||
mCrossDocDirtyRegion.SetEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the prescontext that the presshell is about to reflow a reflow root.
|
||||
* The single argument indicates whether this reflow should be interruptible.
|
||||
* If aInterruptible is false then CheckForInterrupt and HasPendingInterrupt
|
||||
* will always return false. If aInterruptible is true then CheckForInterrupt
|
||||
* will return true when a pending event is detected. This is for use by the
|
||||
* presshell only. Reflow code wanting to prevent interrupts should use
|
||||
* InterruptPreventer.
|
||||
*/
|
||||
void ReflowStarted(PRBool aInterruptible);
|
||||
|
||||
/**
|
||||
* A class that can be used to temporarily disable reflow interruption.
|
||||
*/
|
||||
class InterruptPreventer;
|
||||
friend class InterruptPreventer;
|
||||
class NS_STACK_CLASS InterruptPreventer {
|
||||
public:
|
||||
InterruptPreventer(nsPresContext* aCtx) :
|
||||
mCtx(aCtx),
|
||||
mInterruptsEnabled(aCtx->mInterruptsEnabled),
|
||||
mHasPendingInterrupt(aCtx->mHasPendingInterrupt)
|
||||
{
|
||||
mCtx->mInterruptsEnabled = PR_FALSE;
|
||||
mCtx->mHasPendingInterrupt = PR_FALSE;
|
||||
}
|
||||
~InterruptPreventer() {
|
||||
mCtx->mInterruptsEnabled = mInterruptsEnabled;
|
||||
mCtx->mHasPendingInterrupt = mHasPendingInterrupt;
|
||||
}
|
||||
|
||||
private:
|
||||
nsPresContext* mCtx;
|
||||
PRBool mInterruptsEnabled;
|
||||
PRBool mHasPendingInterrupt;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check for interrupts. This may return true if a pending event is
|
||||
* detected. Once it has returned true, it will keep returning true until
|
||||
* SetInterruptState is called again. In all cases where returns true, the
|
||||
* passed-in frame (which should be the frame whose reflow will be
|
||||
* interrupted if true is returend) will be passed to
|
||||
* nsIPresShell::FrameNeedsToContinueReflow.
|
||||
*/
|
||||
PRBool CheckForInterrupt(nsIFrame* aFrame);
|
||||
/**
|
||||
* Returns true if CheckForInterrupt has returned true since the last
|
||||
* SetInterruptState. Cannot itself trigger an interrupt check.
|
||||
*/
|
||||
PRBool HasPendingInterrupt() { return mHasPendingInterrupt; }
|
||||
|
||||
protected:
|
||||
friend class nsRunnableMethod<nsPresContext>;
|
||||
NS_HIDDEN_(void) ThemeChangedInternal();
|
||||
@ -834,6 +886,8 @@ protected:
|
||||
FlushUserFontSet();
|
||||
}
|
||||
|
||||
PRBool HavePendingInputEvent();
|
||||
|
||||
// Can't be inline because we can't include nsStyleSet.h.
|
||||
PRBool HasCachedStyleData();
|
||||
|
||||
@ -921,6 +975,10 @@ protected:
|
||||
|
||||
nscoord mBorderWidthTable[3];
|
||||
|
||||
PRUint32 mInterruptChecksToSkip;
|
||||
|
||||
unsigned mHasPendingInterrupt : 1;
|
||||
unsigned mInterruptsEnabled : 1;
|
||||
unsigned mUseDocumentFonts : 1;
|
||||
unsigned mUseDocumentColors : 1;
|
||||
unsigned mUnderlineLinks : 1;
|
||||
|
@ -821,6 +821,7 @@ public:
|
||||
nsIFrame** aPlaceholderFrame) const;
|
||||
NS_IMETHOD FrameNeedsReflow(nsIFrame *aFrame, IntrinsicDirty aIntrinsicDirty,
|
||||
nsFrameState aBitToAdd);
|
||||
NS_IMETHOD_(void) FrameNeedsToContinueReflow(nsIFrame *aFrame);
|
||||
NS_IMETHOD CancelAllPendingReflows();
|
||||
NS_IMETHOD IsSafeToFlush(PRBool& aIsSafeToFlush);
|
||||
NS_IMETHOD FlushPendingNotifications(mozFlushType aType);
|
||||
@ -844,7 +845,7 @@ public:
|
||||
|
||||
NS_IMETHOD ScrollContentIntoView(nsIContent* aContent,
|
||||
PRIntn aVPercent,
|
||||
PRIntn aHPercent) const;
|
||||
PRIntn aHPercent);
|
||||
|
||||
NS_IMETHOD SetIgnoreFrameDestruction(PRBool aIgnore);
|
||||
NS_IMETHOD NotifyDestroyingFrame(nsIFrame* aFrame);
|
||||
@ -1022,23 +1023,31 @@ public:
|
||||
protected:
|
||||
virtual ~PresShell();
|
||||
|
||||
void HandlePostedReflowCallbacks();
|
||||
void HandlePostedReflowCallbacks(PRBool aInterruptible);
|
||||
void CancelPostedReflowCallbacks();
|
||||
|
||||
void UnsuppressAndInvalidate();
|
||||
|
||||
void WillDoReflow();
|
||||
void DidDoReflow();
|
||||
nsresult ProcessReflowCommands(PRBool aInterruptible);
|
||||
void DidDoReflow(PRBool aInterruptible);
|
||||
// ProcessReflowCommands returns whether we processed all our dirty roots
|
||||
// without interruptions.
|
||||
PRBool ProcessReflowCommands(PRBool aInterruptible);
|
||||
void ClearReflowEventStatus();
|
||||
void PostReflowEvent();
|
||||
|
||||
void DoReflow(nsIFrame* aFrame);
|
||||
|
||||
// DoReflow returns whether the reflow finished without interruption
|
||||
PRBool DoReflow(nsIFrame* aFrame, PRBool aInterruptible);
|
||||
#ifdef DEBUG
|
||||
void DoVerifyReflow();
|
||||
void VerifyHasDirtyRootAncestor(nsIFrame* aFrame);
|
||||
#endif
|
||||
|
||||
// Helper for ScrollContentIntoView
|
||||
nsresult DoScrollContentIntoView(nsIContent* aContent,
|
||||
PRIntn aVPercent,
|
||||
PRIntn aHPercent);
|
||||
|
||||
friend class nsPresShellEventCB;
|
||||
|
||||
class ReflowEvent;
|
||||
@ -1122,11 +1131,6 @@ protected:
|
||||
// Utility method to restore the root scrollframe state
|
||||
void RestoreRootScrollPosition();
|
||||
|
||||
// Method to handle actually flushing. This allows the caller to control
|
||||
// whether the reflow flush (if any) should be interruptible.
|
||||
nsresult DoFlushPendingNotifications(mozFlushType aType,
|
||||
PRBool aInterruptibleReflow);
|
||||
|
||||
nsCOMPtr<nsICSSStyleSheet> mPrefStyleSheet; // mStyleSet owns it but we
|
||||
// maintain a ref, may be null
|
||||
#ifdef DEBUG
|
||||
@ -1157,6 +1161,25 @@ protected:
|
||||
|
||||
nsRevocableEventPtr<ReflowEvent> mReflowEvent;
|
||||
|
||||
#ifdef DEBUG
|
||||
// The reflow root under which we're currently reflowing. Null when
|
||||
// not in reflow.
|
||||
nsIFrame* mCurrentReflowRoot;
|
||||
#endif
|
||||
|
||||
// Set of frames that we should mark with NS_FRAME_HAS_DIRTY_CHILDREN after
|
||||
// we finish reflowing mCurrentReflowRoot.
|
||||
nsTHashtable< nsPtrHashKey<nsIFrame> > mFramesToDirty;
|
||||
|
||||
// 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. mContentScrollVPosition and
|
||||
// mContentScrollHPosition are only used when it's non-null.
|
||||
nsCOMPtr<nsIContent> mContentToScrollTo;
|
||||
PRIntn mContentScrollVPosition;
|
||||
PRIntn mContentScrollHPosition;
|
||||
|
||||
struct nsBlurOrFocusTarget
|
||||
{
|
||||
nsBlurOrFocusTarget(nsPIDOMEventTarget* aTarget, PRUint32 aEventType)
|
||||
@ -1173,6 +1196,8 @@ protected:
|
||||
nsCallbackEventRequest* mFirstCallbackEventRequest;
|
||||
nsCallbackEventRequest* mLastCallbackEventRequest;
|
||||
|
||||
PRPackedBool mSuppressInterruptibleReflows;
|
||||
|
||||
PRPackedBool mIsThemeSupportDisabled; // Whether or not form controls should use nsITheme in this shell.
|
||||
|
||||
PRPackedBool mIsDocumentGone; // We've been disconnected from the document.
|
||||
@ -1624,6 +1649,10 @@ PresShell::Init(nsIDocument* aDocument,
|
||||
result = mStackArena.Init();
|
||||
NS_ENSURE_SUCCESS(result, result);
|
||||
|
||||
if (!mFramesToDirty.Init()) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
mDocument = aDocument;
|
||||
NS_ADDREF(mDocument);
|
||||
mViewManager = aViewManager;
|
||||
@ -1755,6 +1784,8 @@ PresShell::Destroy()
|
||||
if (mHaveShutDown)
|
||||
return NS_OK;
|
||||
|
||||
mContentToScrollTo = nsnull;
|
||||
|
||||
if (mPresContext) {
|
||||
// We need to notify the destroying the nsPresContext to ESM for
|
||||
// suppressing to use from ESM.
|
||||
@ -2700,14 +2731,12 @@ PresShell::ResizeReflow(nscoord aWidth, nscoord aHeight)
|
||||
|
||||
// Kick off a top-down reflow
|
||||
AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Reflow);
|
||||
mIsReflowing = PR_TRUE;
|
||||
|
||||
mDirtyRoots.RemoveElement(rootFrame);
|
||||
DoReflow(rootFrame);
|
||||
mIsReflowing = PR_FALSE;
|
||||
DoReflow(rootFrame, PR_TRUE);
|
||||
}
|
||||
|
||||
DidDoReflow();
|
||||
DidDoReflow(PR_TRUE);
|
||||
}
|
||||
|
||||
batch.EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC);
|
||||
@ -3417,6 +3446,20 @@ PresShell::FrameNeedsReflow(nsIFrame *aFrame, IntrinsicDirty aIntrinsicDirty,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(void)
|
||||
PresShell::FrameNeedsToContinueReflow(nsIFrame *aFrame)
|
||||
{
|
||||
NS_ASSERTION(mIsReflowing, "Must be in reflow when marking path dirty.");
|
||||
NS_PRECONDITION(mCurrentReflowRoot, "Must have a current reflow root here");
|
||||
NS_ASSERTION(aFrame == mCurrentReflowRoot ||
|
||||
nsLayoutUtils::IsProperAncestorFrame(mCurrentReflowRoot, aFrame),
|
||||
"Frame passed in is not the descendant of mCurrentReflowRoot");
|
||||
NS_ASSERTION(aFrame->GetStateBits() & NS_FRAME_IN_REFLOW,
|
||||
"Frame passed in not in reflow?");
|
||||
|
||||
mFramesToDirty.PutEntry(aFrame);
|
||||
}
|
||||
|
||||
nsIScrollableView*
|
||||
PresShell::GetViewToScroll(nsLayoutUtils::Direction aDirection)
|
||||
{
|
||||
@ -3550,6 +3593,8 @@ PresShell::ClearFrameRefs(nsIFrame* aFrame)
|
||||
}
|
||||
}
|
||||
|
||||
mFramesToDirty.RemoveEntry(aFrame);
|
||||
|
||||
nsWeakFrame* weakFrame = mWeakFrames;
|
||||
while (weakFrame) {
|
||||
nsWeakFrame* prev = weakFrame->GetPreviousWeakFrame();
|
||||
@ -4088,17 +4133,21 @@ static void ScrollViewToShowRect(nsIScrollableView* aScrollingView,
|
||||
NS_IMETHODIMP
|
||||
PresShell::ScrollContentIntoView(nsIContent* aContent,
|
||||
PRIntn aVPercent,
|
||||
PRIntn aHPercent) const
|
||||
PRIntn aHPercent)
|
||||
{
|
||||
mContentToScrollTo = aContent;
|
||||
mContentScrollVPosition = aVPercent;
|
||||
mContentScrollHPosition = aHPercent;
|
||||
|
||||
nsCOMPtr<nsIContent> content = aContent; // Keep content alive while flushing.
|
||||
NS_ENSURE_TRUE(content, NS_ERROR_NULL_POINTER);
|
||||
nsCOMPtr<nsIDocument> currentDoc = content->GetCurrentDoc();
|
||||
NS_ENSURE_STATE(currentDoc);
|
||||
currentDoc->FlushPendingNotifications(Flush_Layout);
|
||||
nsIFrame* frame = GetPrimaryFrameFor(content);
|
||||
if (!frame) {
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
}
|
||||
currentDoc->FlushPendingNotifications(Flush_InterruptibleLayout);
|
||||
|
||||
// If mContentToScrollTo is non-null, that means we interrupted the reflow
|
||||
// and won't necessarily get the position correct, but do a best-effort
|
||||
// scroll.
|
||||
|
||||
// Before we scroll the frame into view, ask the command dispatcher
|
||||
// if we're resetting focus because a window just got an activate
|
||||
@ -4115,11 +4164,26 @@ PresShell::ScrollContentIntoView(nsIContent* aContent,
|
||||
PRBool dontScroll = PR_FALSE;
|
||||
focusController->GetSuppressFocusScroll(&dontScroll);
|
||||
if (dontScroll) {
|
||||
mContentToScrollTo = nsnull;
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return DoScrollContentIntoView(content, aVPercent, aHPercent);
|
||||
}
|
||||
|
||||
nsresult
|
||||
PresShell::DoScrollContentIntoView(nsIContent* aContent,
|
||||
PRIntn aVPercent,
|
||||
PRIntn aHPercent)
|
||||
{
|
||||
nsIFrame* frame = GetPrimaryFrameFor(aContent);
|
||||
if (!frame) {
|
||||
mContentToScrollTo = nsnull;
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
}
|
||||
|
||||
// This is a two-step process.
|
||||
// Step 1: Find the bounds of the rect we want to scroll into view. For
|
||||
// example, for an inline frame we may want to scroll in the whole
|
||||
@ -4582,7 +4646,7 @@ PresShell::CancelPostedReflowCallbacks()
|
||||
}
|
||||
|
||||
void
|
||||
PresShell::HandlePostedReflowCallbacks()
|
||||
PresShell::HandlePostedReflowCallbacks(PRBool aInterruptible)
|
||||
{
|
||||
PRBool shouldFlush = PR_FALSE;
|
||||
|
||||
@ -4601,8 +4665,10 @@ PresShell::HandlePostedReflowCallbacks()
|
||||
}
|
||||
}
|
||||
|
||||
mozFlushType flushType =
|
||||
aInterruptible ? Flush_InterruptibleLayout : Flush_Layout;
|
||||
if (shouldFlush)
|
||||
FlushPendingNotifications(Flush_Layout);
|
||||
FlushPendingNotifications(flushType);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -4627,15 +4693,8 @@ PresShell::IsSafeToFlush(PRBool& aIsSafeToFlush)
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
NS_IMETHODIMP
|
||||
PresShell::FlushPendingNotifications(mozFlushType aType)
|
||||
{
|
||||
return DoFlushPendingNotifications(aType, PR_FALSE);
|
||||
}
|
||||
|
||||
nsresult
|
||||
PresShell::DoFlushPendingNotifications(mozFlushType aType,
|
||||
PRBool aInterruptibleReflow)
|
||||
{
|
||||
NS_ASSERTION(aType >= Flush_Frames, "Why did we get called?");
|
||||
|
||||
@ -4704,11 +4763,17 @@ PresShell::DoFlushPendingNotifications(mozFlushType aType,
|
||||
// There might be more pending constructors now, but we're not going to
|
||||
// worry about them. They can't be triggered during reflow, so we should
|
||||
// be good.
|
||||
|
||||
if (aType >= Flush_Layout && !mIsDestroying) {
|
||||
|
||||
if (aType >= (mSuppressInterruptibleReflows ? Flush_Layout : Flush_InterruptibleLayout) &&
|
||||
!mIsDestroying) {
|
||||
mFrameConstructor->RecalcQuotesAndCounters();
|
||||
mViewManager->FlushDelayedResize();
|
||||
ProcessReflowCommands(aInterruptibleReflow);
|
||||
if (ProcessReflowCommands(aType < Flush_Layout) && mContentToScrollTo) {
|
||||
// We didn't get interrupted. Go ahead and scroll to our content
|
||||
DoScrollContentIntoView(mContentToScrollTo, mContentScrollVPosition,
|
||||
mContentScrollHPosition);
|
||||
mContentToScrollTo = nsnull;
|
||||
}
|
||||
}
|
||||
|
||||
PRUint32 updateFlags = NS_VMREFRESH_NO_SYNC;
|
||||
@ -4717,7 +4782,7 @@ PresShell::DoFlushPendingNotifications(mozFlushType aType,
|
||||
// immediately
|
||||
updateFlags = NS_VMREFRESH_IMMEDIATE;
|
||||
}
|
||||
else if (aType < Flush_Layout) {
|
||||
else if (aType < Flush_InterruptibleLayout) {
|
||||
// Not flushing reflows, so do deferred invalidates. This will keep us
|
||||
// from possibly flushing out reflows due to invalidates being processed
|
||||
// at the end of this view batch.
|
||||
@ -6556,7 +6621,7 @@ PresShell::WillPaint()
|
||||
// reflow being interspersed. Note that we _do_ allow this to be
|
||||
// interruptible; if we can't do all the reflows it's better to flicker a bit
|
||||
// than to freeze up.
|
||||
DoFlushPendingNotifications(Flush_Layout, PR_TRUE);
|
||||
FlushPendingNotifications(Flush_InterruptibleLayout);
|
||||
}
|
||||
|
||||
nsresult
|
||||
@ -6766,7 +6831,17 @@ PresShell::ReflowEvent::Run() {
|
||||
// before processing that pres shell's reflow commands. Fixes bug 54868.
|
||||
nsCOMPtr<nsIViewManager> viewManager = ps->GetViewManager();
|
||||
|
||||
ps->DoFlushPendingNotifications(Flush_Layout, PR_TRUE);
|
||||
ps->mSuppressInterruptibleReflows = PR_FALSE;
|
||||
|
||||
#ifdef NOISY_INTERRUPTIBLE_REFLOW
|
||||
printf("*** Entering reflow event (time=%lld)\n", PR_Now());
|
||||
#endif /* NOISY_INTERRUPTIBLE_REFLOW */
|
||||
|
||||
ps->FlushPendingNotifications(Flush_InterruptibleLayout);
|
||||
|
||||
#ifdef NOISY_INTERRUPTIBLE_REFLOW
|
||||
printf("*** Returning from reflow event (time=%lld)\n", PR_Now());
|
||||
#endif /* NOISY_INTERRUPTIBLE_REFLOW */
|
||||
|
||||
// Now, explicitly release the pres shell before the view manager
|
||||
ps = nsnull;
|
||||
@ -6813,11 +6888,11 @@ PresShell::WillDoReflow()
|
||||
}
|
||||
|
||||
void
|
||||
PresShell::DidDoReflow()
|
||||
PresShell::DidDoReflow(PRBool aInterruptible)
|
||||
{
|
||||
mFrameConstructor->EndUpdate();
|
||||
|
||||
HandlePostedReflowCallbacks();
|
||||
HandlePostedReflowCallbacks(aInterruptible);
|
||||
// Null-check mViewManager in case this happens during Destroy. See
|
||||
// bugs 244435 and 238546.
|
||||
if (!mPaintingSuppressed && mViewManager)
|
||||
@ -6830,8 +6905,24 @@ PresShell::DidDoReflow()
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PresShell::DoReflow(nsIFrame* target)
|
||||
static PLDHashOperator
|
||||
MarkFramesDirtyToRoot(nsPtrHashKey<nsIFrame>* p, void* closure)
|
||||
{
|
||||
nsIFrame* target = static_cast<nsIFrame*>(closure);
|
||||
for (nsIFrame* f = p->GetKey(); f && !NS_SUBTREE_DIRTY(f);
|
||||
f = f->GetParent()) {
|
||||
f->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
|
||||
|
||||
if (f == target) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
PRBool
|
||||
PresShell::DoReflow(nsIFrame* target, PRBool aInterruptible)
|
||||
{
|
||||
nsIFrame* rootFrame = FrameManager()->GetRootFrame();
|
||||
|
||||
@ -6840,9 +6931,13 @@ PresShell::DoReflow(nsIFrame* target)
|
||||
// reflow; otherwise, it crashes on the mac (I'm not quite sure why)
|
||||
nsresult rv = CreateRenderingContext(rootFrame, getter_AddRefs(rcx));
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_NOTREACHED("CreateRenderingContext failure");
|
||||
return;
|
||||
}
|
||||
NS_NOTREACHED("CreateRenderingContext failure");
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
mCurrentReflowRoot = target;
|
||||
#endif
|
||||
|
||||
target->WillReflow(mPresContext);
|
||||
|
||||
@ -6877,6 +6972,9 @@ PresShell::DoReflow(nsIFrame* target)
|
||||
reflowState.mComputedBorderPadding.LeftRight(),
|
||||
"reflow state computed incorrect width");
|
||||
|
||||
mPresContext->ReflowStarted(aInterruptible);
|
||||
mIsReflowing = PR_TRUE;
|
||||
|
||||
nsReflowStatus status;
|
||||
nsHTMLReflowMetrics desiredSize;
|
||||
target->Reflow(mPresContext, desiredSize, reflowState, status);
|
||||
@ -6908,6 +7006,38 @@ PresShell::DoReflow(nsIFrame* target)
|
||||
mPresContext->SetVisibleArea(nsRect(0, 0, desiredSize.width,
|
||||
desiredSize.height));
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
mCurrentReflowRoot = nsnull;
|
||||
#endif
|
||||
|
||||
NS_ASSERTION(mPresContext->HasPendingInterrupt() ||
|
||||
mFramesToDirty.Count() == 0,
|
||||
"Why do we need to dirty anything if not interrupted?");
|
||||
|
||||
mIsReflowing = PR_FALSE;
|
||||
PRBool interrupted = mPresContext->HasPendingInterrupt();
|
||||
if (interrupted) {
|
||||
// Make sure target gets reflowed again.
|
||||
mFramesToDirty.EnumerateEntries(&MarkFramesDirtyToRoot, target);
|
||||
NS_ASSERTION(NS_SUBTREE_DIRTY(target), "Why is the target not dirty?");
|
||||
mDirtyRoots.AppendElement(target);
|
||||
|
||||
// Clear mFramesToDirty after we've done the NS_SUBTREE_DIRTY(target)
|
||||
// assertion so that if it fails it's easier to see what's going on.
|
||||
#ifdef NOISY_INTERRUPTIBLE_REFLOW
|
||||
printf("mFramesToDirty.Count() == %u\n", mFramesToDirty.Count());
|
||||
#endif /* NOISY_INTERRUPTIBLE_REFLOW */
|
||||
mFramesToDirty.Clear();
|
||||
|
||||
// Any FlushPendingNotifications with interruptible reflows
|
||||
// should be suppressed now. We don't want to do extra reflow work
|
||||
// before our reflow event happens.
|
||||
mSuppressInterruptibleReflows = PR_TRUE;
|
||||
PostReflowEvent();
|
||||
}
|
||||
|
||||
return !interrupted;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
@ -6944,12 +7074,13 @@ PresShell::DoVerifyReflow()
|
||||
}
|
||||
#endif
|
||||
|
||||
nsresult
|
||||
PRBool
|
||||
PresShell::ProcessReflowCommands(PRBool aInterruptible)
|
||||
{
|
||||
MOZ_TIMER_DEBUGLOG(("Start: Reflow: PresShell::ProcessReflowCommands(), this=%p\n", this));
|
||||
MOZ_TIMER_START(mReflowWatch);
|
||||
|
||||
PRBool interrupted = PR_FALSE;
|
||||
if (0 != mDirtyRoots.Length()) {
|
||||
|
||||
#ifdef DEBUG
|
||||
@ -6969,7 +7100,6 @@ PresShell::ProcessReflowCommands(PRBool aInterruptible)
|
||||
{
|
||||
nsAutoScriptBlocker scriptBlocker;
|
||||
AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Reflow);
|
||||
mIsReflowing = PR_TRUE;
|
||||
|
||||
do {
|
||||
// Send an incremental reflow notification to the target frame.
|
||||
@ -6984,22 +7114,19 @@ PresShell::ProcessReflowCommands(PRBool aInterruptible)
|
||||
continue;
|
||||
}
|
||||
|
||||
DoReflow(target);
|
||||
interrupted = !DoReflow(target, aInterruptible);
|
||||
|
||||
// Keep going until we're out of reflow commands, or we've run
|
||||
// past our deadline.
|
||||
} while (mDirtyRoots.Length() &&
|
||||
// past our deadline, or we're interrupted.
|
||||
} while (!interrupted && mDirtyRoots.Length() &&
|
||||
(!aInterruptible || PR_IntervalNow() < deadline));
|
||||
|
||||
// XXXwaterson for interruptible reflow, examine the tree and
|
||||
// re-enqueue any unflowed reflow targets.
|
||||
|
||||
mIsReflowing = PR_FALSE;
|
||||
interrupted = mDirtyRoots.Length() != 0;
|
||||
}
|
||||
|
||||
// Exiting the scriptblocker might have killed us
|
||||
if (!mIsDestroying) {
|
||||
DidDoReflow();
|
||||
DidDoReflow(aInterruptible);
|
||||
}
|
||||
|
||||
// DidDoReflow might have killed us
|
||||
@ -7035,7 +7162,7 @@ PresShell::ProcessReflowCommands(PRBool aInterruptible)
|
||||
UnsuppressAndInvalidate();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
return !interrupted;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -147,9 +147,9 @@ nsAbsoluteContainingBlock::Reflow(nsContainerFrame* aDelegatingFrame,
|
||||
nsIFrame* kidFrame;
|
||||
nsOverflowContinuationTracker tracker(aPresContext, aDelegatingFrame, PR_TRUE);
|
||||
for (kidFrame = mAbsoluteFrames.FirstChild(); kidFrame; kidFrame = kidFrame->GetNextSibling()) {
|
||||
if (reflowAll ||
|
||||
NS_SUBTREE_DIRTY(kidFrame) ||
|
||||
FrameDependsOnContainer(kidFrame, aCBWidthChanged, aCBHeightChanged)) {
|
||||
PRBool kidNeedsReflow = reflowAll || NS_SUBTREE_DIRTY(kidFrame) ||
|
||||
FrameDependsOnContainer(kidFrame, aCBWidthChanged, aCBHeightChanged);
|
||||
if (kidNeedsReflow && !aPresContext->HasPendingInterrupt()) {
|
||||
// Reflow the frame
|
||||
nsReflowStatus kidStatus = NS_FRAME_COMPLETE;
|
||||
ReflowAbsoluteFrame(aDelegatingFrame, aPresContext, aReflowState,
|
||||
@ -188,7 +188,16 @@ nsAbsoluteContainingBlock::Reflow(nsContainerFrame* aDelegatingFrame,
|
||||
kidFrame->GetPosition());
|
||||
}
|
||||
}
|
||||
|
||||
if (kidNeedsReflow && aPresContext->HasPendingInterrupt()) {
|
||||
if (aDelegatingFrame->GetStateBits() & NS_FRAME_IS_DIRTY) {
|
||||
kidFrame->AddStateBits(NS_FRAME_IS_DIRTY);
|
||||
} else {
|
||||
kidFrame->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Abspos frames can't cause their parent to be incomplete,
|
||||
// only overflow incomplete.
|
||||
if (NS_FRAME_IS_NOT_COMPLETE(reflowStatus))
|
||||
@ -334,11 +343,25 @@ nsAbsoluteContainingBlock::DestroyFrames(nsIFrame* aDelegatingFrame)
|
||||
|
||||
void
|
||||
nsAbsoluteContainingBlock::MarkSizeDependentFramesDirty()
|
||||
{
|
||||
DoMarkFramesDirty(PR_FALSE);
|
||||
}
|
||||
|
||||
void
|
||||
nsAbsoluteContainingBlock::MarkAllFramesDirty()
|
||||
{
|
||||
DoMarkFramesDirty(PR_TRUE);
|
||||
}
|
||||
|
||||
void
|
||||
nsAbsoluteContainingBlock::DoMarkFramesDirty(PRBool aMarkAllDirty)
|
||||
{
|
||||
for (nsIFrame* kidFrame = mAbsoluteFrames.FirstChild();
|
||||
kidFrame;
|
||||
kidFrame = kidFrame->GetNextSibling()) {
|
||||
if (FrameDependsOnContainer(kidFrame, PR_TRUE, PR_TRUE)) {
|
||||
if (aMarkAllDirty) {
|
||||
kidFrame->AddStateBits(NS_FRAME_IS_DIRTY);
|
||||
} else if (FrameDependsOnContainer(kidFrame, PR_TRUE, PR_TRUE)) {
|
||||
// Add the weakest flags that will make sure we reflow this frame later
|
||||
kidFrame->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
|
||||
}
|
||||
|
@ -127,8 +127,13 @@ public:
|
||||
|
||||
PRBool HasAbsoluteFrames() {return mAbsoluteFrames.NotEmpty();}
|
||||
|
||||
// Mark our size-dependent absolute frames with NS_FRAME_HAS_DIRTY_CHILDREN
|
||||
// so that we'll make sure to reflow them.
|
||||
void MarkSizeDependentFramesDirty();
|
||||
|
||||
// Mark all our absolute frames with NS_FRAME_IS_DIRTY
|
||||
void MarkAllFramesDirty();
|
||||
|
||||
protected:
|
||||
// Returns PR_TRUE if the position of f depends on the position of
|
||||
// its placeholder or if the position or size of f depends on a
|
||||
@ -146,6 +151,11 @@ protected:
|
||||
nsReflowStatus& aStatus,
|
||||
nsRect* aChildBounds);
|
||||
|
||||
// Mark our absolute frames dirty. If aMarkAllDirty is true, all will be
|
||||
// marked with NS_FRAME_IS_DIRTY. Otherwise, the size-dependant ones will be
|
||||
// marked with NS_FRAME_HAS_DIRTY_CHILDREN.
|
||||
void DoMarkFramesDirty(PRBool aMarkAllDirty);
|
||||
|
||||
protected:
|
||||
nsFrameList mAbsoluteFrames; // additional named child list
|
||||
|
||||
|
@ -1122,12 +1122,20 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext,
|
||||
// in that situation --- what we think is our "new size"
|
||||
// will not be our real new size. This also happens to be more efficient.
|
||||
if (mAbsoluteContainer.HasAbsoluteFrames()) {
|
||||
if (aReflowState.WillReflowAgainForClearance()) {
|
||||
PRBool haveInterrupt = aPresContext->HasPendingInterrupt();
|
||||
if (aReflowState.WillReflowAgainForClearance() ||
|
||||
haveInterrupt) {
|
||||
// Make sure that when we reflow again we'll actually reflow all the abs
|
||||
// pos frames that might conceivably depend on our size. Sadly, we can't
|
||||
// do much better than that, because we don't really know what our size
|
||||
// will be, and it might in fact not change on the followup reflow!
|
||||
mAbsoluteContainer.MarkSizeDependentFramesDirty();
|
||||
// pos frames that might conceivably depend on our size (or all of them,
|
||||
// if we're dirty right now and interrupted; in that case we also need
|
||||
// to mark them all with NS_FRAME_IS_DIRTY). Sadly, we can't do much
|
||||
// better than that, because we don't really know what our size will be,
|
||||
// and it might in fact not change on the followup reflow!
|
||||
if (haveInterrupt && (GetStateBits() & NS_FRAME_IS_DIRTY)) {
|
||||
mAbsoluteContainer.MarkAllFramesDirty();
|
||||
} else {
|
||||
mAbsoluteContainer.MarkSizeDependentFramesDirty();
|
||||
}
|
||||
} else {
|
||||
nsRect childBounds;
|
||||
nsSize containingBlockSize =
|
||||
@ -2016,6 +2024,18 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
|
||||
}
|
||||
|
||||
DumpLine(aState, line, deltaY, -1);
|
||||
|
||||
if (aState.mPresContext->CheckForInterrupt(this)) {
|
||||
willReflowAgain = PR_TRUE;
|
||||
// Another option here might be to leave |line| clean if
|
||||
// !HasPendingInterrupt() before the CheckForInterrupt() call, since in
|
||||
// that case the line really did reflow as it should have. Not sure
|
||||
// whether that would be safe, so doing this for now instead. Also not
|
||||
// sure whether we really want to mark all lines dirty after an
|
||||
// interrupt, but until we get better at propagating float damage we
|
||||
// really do need to do it this way; see comments inside MarkLineDirty.
|
||||
MarkLineDirtyForInterrupt(line);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle BR-clearance from the last line of the block
|
||||
@ -2037,8 +2057,9 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
|
||||
if (repositionViews)
|
||||
::PlaceFrameView(this);
|
||||
|
||||
// We can skip trying to pull up the next line if there is no next
|
||||
// in flow or we were told not to or we know it will be futile, i.e.,
|
||||
// We can skip trying to pull up the next line if our height is constrained
|
||||
// (so we can report being incomplete) and there is no next in flow or we
|
||||
// were told not to or we know it will be futile, i.e.,
|
||||
// -- the next in flow is not changing
|
||||
// -- and we cannot have added more space for its first line to be
|
||||
// pulled up into,
|
||||
@ -2047,8 +2068,10 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
|
||||
// didn't change)
|
||||
// -- my chain of next-in-flows either has no first line, or its first
|
||||
// line isn't dirty.
|
||||
PRBool skipPull = willReflowAgain;
|
||||
if (aState.mNextInFlow &&
|
||||
PRBool heightConstrained =
|
||||
aState.mReflowState.availableHeight != NS_UNCONSTRAINEDSIZE;
|
||||
PRBool skipPull = willReflowAgain && heightConstrained;
|
||||
if (!skipPull && heightConstrained && aState.mNextInFlow &&
|
||||
(aState.mReflowState.mFlags.mNextInFlowUntouched &&
|
||||
!lastLineMovedUp &&
|
||||
!(GetStateBits() & NS_FRAME_IS_DIRTY) &&
|
||||
@ -2067,14 +2090,18 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
|
||||
// (First, see if there is such a line, and second, see if it's clean)
|
||||
if (!bifLineIter.Next() ||
|
||||
!bifLineIter.GetLine()->IsDirty()) {
|
||||
if (IS_TRUE_OVERFLOW_CONTAINER(aState.mNextInFlow))
|
||||
NS_FRAME_SET_OVERFLOW_INCOMPLETE(aState.mReflowStatus);
|
||||
else
|
||||
NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus);
|
||||
skipPull=PR_TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (skipPull && aState.mNextInFlow) {
|
||||
NS_ASSERTION(heightConstrained, "Height should be constrained here\n");
|
||||
if (IS_TRUE_OVERFLOW_CONTAINER(aState.mNextInFlow))
|
||||
NS_FRAME_SET_OVERFLOW_INCOMPLETE(aState.mReflowStatus);
|
||||
else
|
||||
NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus);
|
||||
}
|
||||
|
||||
if (!skipPull && aState.mNextInFlow) {
|
||||
// Pull data from a next-in-flow if there's still room for more
|
||||
@ -2157,28 +2184,38 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
|
||||
AutoNoisyIndenter indent2(gNoisyReflow);
|
||||
#endif
|
||||
|
||||
// Now reflow it and any lines that it makes during it's reflow
|
||||
// (we have to loop here because reflowing the line may case a new
|
||||
// line to be created; see SplitLine's callers for examples of
|
||||
// when this happens).
|
||||
while (line != end_lines()) {
|
||||
rv = ReflowLine(aState, line, &keepGoing);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
DumpLine(aState, line, deltaY, -1);
|
||||
if (!keepGoing) {
|
||||
if (0 == line->GetChildCount()) {
|
||||
DeleteLine(aState, line, line_end);
|
||||
if (aState.mPresContext->HasPendingInterrupt()) {
|
||||
MarkLineDirtyForInterrupt(line);
|
||||
} else {
|
||||
// Now reflow it and any lines that it makes during it's reflow
|
||||
// (we have to loop here because reflowing the line may case a new
|
||||
// line to be created; see SplitLine's callers for examples of
|
||||
// when this happens).
|
||||
while (line != end_lines()) {
|
||||
rv = ReflowLine(aState, line, &keepGoing);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
DumpLine(aState, line, deltaY, -1);
|
||||
if (!keepGoing) {
|
||||
if (0 == line->GetChildCount()) {
|
||||
DeleteLine(aState, line, line_end);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (LineHasClear(line.get())) {
|
||||
foundAnyClears = PR_TRUE;
|
||||
}
|
||||
if (LineHasClear(line.get())) {
|
||||
foundAnyClears = PR_TRUE;
|
||||
}
|
||||
|
||||
// If this is an inline frame then its time to stop
|
||||
++line;
|
||||
aState.AdvanceToNextLine();
|
||||
if (aState.mPresContext->CheckForInterrupt(this)) {
|
||||
willReflowAgain = PR_TRUE;
|
||||
MarkLineDirtyForInterrupt(line);
|
||||
break;
|
||||
}
|
||||
|
||||
// If this is an inline frame then its time to stop
|
||||
++line;
|
||||
aState.AdvanceToNextLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2216,6 +2253,58 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
|
||||
return rv;
|
||||
}
|
||||
|
||||
static void MarkAllDescendantLinesDirty(nsBlockFrame* aBlock)
|
||||
{
|
||||
nsLineList::iterator line = aBlock->begin_lines();
|
||||
nsLineList::iterator endLine = aBlock->end_lines();
|
||||
while (line != endLine) {
|
||||
if (line->IsBlock()) {
|
||||
nsIFrame* f = line->mFirstChild;
|
||||
nsBlockFrame* bf = nsLayoutUtils::GetAsBlock(f);
|
||||
if (bf) {
|
||||
MarkAllDescendantLinesDirty(bf);
|
||||
}
|
||||
}
|
||||
line->MarkDirty();
|
||||
++line;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsBlockFrame::MarkLineDirtyForInterrupt(nsLineBox* aLine)
|
||||
{
|
||||
aLine->MarkDirty();
|
||||
|
||||
// Just checking NS_FRAME_IS_DIRTY is ok, because we've already
|
||||
// marked the lines that need to be marked dirty based on our
|
||||
// vertical resize stuff. So we'll definitely reflow all those kids;
|
||||
// the only question is how they should behave.
|
||||
if (GetStateBits() & NS_FRAME_IS_DIRTY) {
|
||||
// Mark all our child frames dirty so we make sure to reflow them
|
||||
// later.
|
||||
PRInt32 n = aLine->GetChildCount();
|
||||
for (nsIFrame* f = aLine->mFirstChild; n > 0;
|
||||
f = f->GetNextSibling(), --n) {
|
||||
f->AddStateBits(NS_FRAME_IS_DIRTY);
|
||||
nsIFrame* oof = nsPlaceholderFrame::GetRealFrameFor(f);
|
||||
if (oof != f && oof->GetParent() == this) {
|
||||
oof->AddStateBits(NS_FRAME_IS_DIRTY);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Dirty all the descendant lines of block kids to handle float damage,
|
||||
// since our nsFloatManager will go away by the next time we're reflowing.
|
||||
// XXXbz Can we do something more like what PropagateFloatDamage does?
|
||||
// Would need to sort out the exact business with mBlockDelta for that....
|
||||
// This marks way too much dirty. If we ever make this better, revisit
|
||||
// which lines we mark dirty in the interrupt case in ReflowDirtyLines.
|
||||
nsBlockFrame* bf = nsLayoutUtils::GetAsBlock(aLine->mFirstChild);
|
||||
if (bf) {
|
||||
MarkAllDescendantLinesDirty(bf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsBlockFrame::DeleteLine(nsBlockReflowState& aState,
|
||||
nsLineList::iterator aLine,
|
||||
@ -4958,23 +5047,6 @@ nsBlockFrame::RemoveFloat(nsIFrame* aFloat) {
|
||||
return line_end;
|
||||
}
|
||||
|
||||
static void MarkAllDescendantLinesDirty(nsBlockFrame* aBlock)
|
||||
{
|
||||
nsLineList::iterator line = aBlock->begin_lines();
|
||||
nsLineList::iterator endLine = aBlock->end_lines();
|
||||
while (line != endLine) {
|
||||
if (line->IsBlock()) {
|
||||
nsIFrame* f = line->mFirstChild;
|
||||
nsBlockFrame* bf = nsLayoutUtils::GetAsBlock(f);
|
||||
if (bf) {
|
||||
MarkAllDescendantLinesDirty(bf);
|
||||
}
|
||||
}
|
||||
line->MarkDirty();
|
||||
++line;
|
||||
}
|
||||
}
|
||||
|
||||
static void MarkSameFloatManagerLinesDirty(nsBlockFrame* aBlock)
|
||||
{
|
||||
nsBlockFrame* blockWithFloatMgr = aBlock;
|
||||
|
@ -464,6 +464,9 @@ protected:
|
||||
/** reflow all lines that have been marked dirty */
|
||||
nsresult ReflowDirtyLines(nsBlockReflowState& aState);
|
||||
|
||||
/** Mark a given line dirty due to reflow being interrupted on or before it */
|
||||
void MarkLineDirtyForInterrupt(nsLineBox* aLine);
|
||||
|
||||
//----------------------------------------
|
||||
// Methods for line reflow
|
||||
/**
|
||||
|
@ -755,6 +755,16 @@ nsColumnSetFrame::ReflowChildren(nsHTMLReflowMetrics& aDesiredSize,
|
||||
}
|
||||
}
|
||||
|
||||
if (PresContext()->HasPendingInterrupt()) {
|
||||
// Stop the loop now while |child| still points to the frame that bailed
|
||||
// out. We could keep going here and condition a bunch of the code in
|
||||
// this loop on whether there's an interrupt, or even just keep going and
|
||||
// trying to reflow the blocks (even though we know they'll interrupt
|
||||
// right after their first line), but stopping now is conceptually the
|
||||
// simplest (and probably fastest) thing.
|
||||
break;
|
||||
}
|
||||
|
||||
// Advance to the next column
|
||||
child = child->GetNextSibling();
|
||||
|
||||
@ -770,6 +780,14 @@ nsColumnSetFrame::ReflowChildren(nsHTMLReflowMetrics& aDesiredSize,
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (PresContext()->HasPendingInterrupt() &&
|
||||
(GetStateBits() & NS_FRAME_IS_DIRTY)) {
|
||||
// Mark all our kids starting with |child| dirty
|
||||
for (; child; child = child->GetNextSibling()) {
|
||||
child->AddStateBits(NS_FRAME_IS_DIRTY);
|
||||
}
|
||||
}
|
||||
|
||||
// If we're doing RTL, we need to make sure our last column is at the left-hand side of the frame.
|
||||
if (RTL && childOrigin.x != targetX) {
|
||||
@ -897,7 +915,7 @@ nsColumnSetFrame::Reflow(nsPresContext* aPresContext,
|
||||
PRBool feasible = ReflowChildren(aDesiredSize, aReflowState,
|
||||
aStatus, config, unboundedLastColumn, &carriedOutBottomMargin, colData);
|
||||
|
||||
if (isBalancing) {
|
||||
if (isBalancing && !aPresContext->HasPendingInterrupt()) {
|
||||
nscoord availableContentHeight = GetAvailableContentHeight(aReflowState);
|
||||
|
||||
// Termination of the algorithm below is guaranteed because
|
||||
@ -910,7 +928,7 @@ nsColumnSetFrame::Reflow(nsPresContext* aPresContext,
|
||||
// search)
|
||||
PRBool maybeContinuousBreakingDetected = PR_FALSE;
|
||||
|
||||
while (1) {
|
||||
while (!aPresContext->HasPendingInterrupt()) {
|
||||
nscoord lastKnownFeasibleHeight = knownFeasibleHeight;
|
||||
|
||||
// Record what we learned from the last reflow
|
||||
@ -1002,7 +1020,7 @@ nsColumnSetFrame::Reflow(nsPresContext* aPresContext,
|
||||
&carriedOutBottomMargin, colData);
|
||||
}
|
||||
|
||||
if (!feasible) {
|
||||
if (!feasible && !aPresContext->HasPendingInterrupt()) {
|
||||
// We may need to reflow one more time at the feasible height to
|
||||
// get a valid layout.
|
||||
PRBool skip = PR_FALSE;
|
||||
@ -1021,6 +1039,13 @@ nsColumnSetFrame::Reflow(nsPresContext* aPresContext,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (aPresContext->HasPendingInterrupt() &&
|
||||
aReflowState.availableHeight == NS_UNCONSTRAINEDSIZE) {
|
||||
// In this situation, we might be lying about our reflow status, because
|
||||
// our last kid (the one that got interrupted) was incomplete. Fix that.
|
||||
aStatus = NS_FRAME_COMPLETE;
|
||||
}
|
||||
|
||||
CheckInvalidateSizeChange(aDesiredSize);
|
||||
|
||||
|
@ -1926,7 +1926,7 @@ nsGfxScrollFrameInner::AsyncScrollPortEvent::Run()
|
||||
{
|
||||
if (mInner) {
|
||||
mInner->mOuter->PresContext()->GetPresShell()->
|
||||
FlushPendingNotifications(Flush_Layout);
|
||||
FlushPendingNotifications(Flush_InterruptibleLayout);
|
||||
}
|
||||
return mInner ? mInner->FireScrollPortEvent() : NS_OK;
|
||||
}
|
||||
|
@ -2055,6 +2055,9 @@ nsLineLayout::VerticalAlignFrames(PerSpanData* psd)
|
||||
|
||||
#ifdef NOISY_VERTICAL_ALIGN
|
||||
printf(" new values: %d,%d\n", minY, maxY);
|
||||
#endif
|
||||
#ifdef NOISY_VERTICAL_ALIGN
|
||||
printf(" Used mMinLineHeight: %d, fontHeight: %d, fontAscent: %d\n", mMinLineHeight, fontHeight, fontAscent);
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
|
15
layout/reftests/bugs/67752-1-ref.html
Normal file
15
layout/reftests/bugs/67752-1-ref.html
Normal file
@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
span { display: inline-block; width: 1em; height: 10px; background: green; }
|
||||
</style>
|
||||
</head>
|
||||
<body style="position: relative; font-size: 20px">
|
||||
Test
|
||||
<div style="position: absolute; left: 0; width: 25px; height: 0; top: 0;">
|
||||
<span></span>
|
||||
<span></span>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
23
layout/reftests/bugs/67752-1.html
Normal file
23
layout/reftests/bugs/67752-1.html
Normal file
@ -0,0 +1,23 @@
|
||||
<!DOCTYPE html>
|
||||
<html class="reftest-wait">
|
||||
<head>
|
||||
<script>
|
||||
function f() {
|
||||
document.body.style.fontSize = "20px";
|
||||
// End the test asynchronously so we get a chance to do a reflow before
|
||||
// that happens.
|
||||
setTimeout("document.documentElement.className = ''", 0);
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
span { display: inline-block; width: 1em; height: 10px; background: green; }
|
||||
</style>
|
||||
</head>
|
||||
<body onload="f()" style="position: relative; font-size: 10px">
|
||||
Test
|
||||
<div style="position: absolute; left: 0; width: 25px; height: 0; top: 0;">
|
||||
<span></span>
|
||||
<span></span>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
15
layout/reftests/bugs/67752-2-ref.html
Normal file
15
layout/reftests/bugs/67752-2-ref.html
Normal file
@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
span { display: inline-block; width: 1em; height: 10px; background: green; }
|
||||
</style>
|
||||
</head>
|
||||
<body style="font-size: 20px">
|
||||
Test
|
||||
<div style="position: absolute; left: 0; width: 25px; height: 0; top: 0;">
|
||||
<span></span>
|
||||
<span></span>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
23
layout/reftests/bugs/67752-2.html
Normal file
23
layout/reftests/bugs/67752-2.html
Normal file
@ -0,0 +1,23 @@
|
||||
<!DOCTYPE html>
|
||||
<html class="reftest-wait">
|
||||
<head>
|
||||
<script>
|
||||
function f() {
|
||||
document.body.style.fontSize = "20px";
|
||||
// End the test asynchronously so we get a chance to do a reflow before
|
||||
// that happens.
|
||||
setTimeout("document.documentElement.className = ''", 0);
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
span { display: inline-block; width: 1em; height: 10px; background: green; }
|
||||
</style>
|
||||
</head>
|
||||
<body onload="f()" style="font-size: 10px">
|
||||
Test
|
||||
<div style="position: absolute; left: 0; width: 25px; height: 0; top: 0;">
|
||||
<span></span>
|
||||
<span></span>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -66,6 +66,8 @@ fails == 25888-3r.html 25888-3r-ref.html # bug 25888
|
||||
== 50630-4.html 50630-4-ref.html
|
||||
== 50630-4.html 50630-4-ref2.html
|
||||
== 50630-5.html 50630-5-ref.html
|
||||
== 67752-1.html 67752-1-ref.html
|
||||
== 67752-2.html 67752-2-ref.html
|
||||
== 68061-1.xml 68061-1-ref.xml
|
||||
== 68061-2.xml 68061-2-ref.xml
|
||||
== 76331-1.html 76331-1-ref.html
|
||||
|
@ -379,6 +379,9 @@ nsSVGForeignObjectFrame::InitialUpdate()
|
||||
"before our nsSVGOuterSVGFrame's initial Reflow()!!!");
|
||||
|
||||
UpdateCoveredRegion();
|
||||
|
||||
// Make sure to not allow interrupts if we're not being reflown as a root
|
||||
nsPresContext::InterruptPreventer noInterrupts(PresContext());
|
||||
DoReflow();
|
||||
|
||||
NS_ASSERTION(!(mState & NS_FRAME_IN_REFLOW),
|
||||
@ -583,6 +586,9 @@ nsSVGForeignObjectFrame::MaybeReflowFromOuterSVGFrame()
|
||||
if (kid->GetStateBits() & NS_FRAME_HAS_DIRTY_CHILDREN) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure to not allow interrupts if we're not being reflown as a root
|
||||
nsPresContext::InterruptPreventer noInterrupts(PresContext());
|
||||
DoReflow();
|
||||
}
|
||||
|
||||
|
@ -100,10 +100,10 @@ typedef nsEventStatus (* EVENT_CALLBACK)(nsGUIEvent *event);
|
||||
#define NS_NATIVE_TSF_DISPLAY_ATTR_MGR 102
|
||||
#endif
|
||||
|
||||
// af70b716-2e34-463f-8f1c-273dbddd845b
|
||||
// 3d277f04-93f4-4384-9fdc-e1e2d1fc4e33
|
||||
#define NS_IWIDGET_IID \
|
||||
{ 0xaf70b716, 0x2e34, 0x463f, \
|
||||
{ 0x8f, 0x1c, 0x27, 0x3d, 0xbd, 0xdd, 0x84, 0x5b } }
|
||||
{ 0x3d277f04, 0x93f4, 0x4384, \
|
||||
{ 0x9f, 0xdc, 0xe1, 0xe2, 0xd1, 0xfc, 0x4e, 0x33 } }
|
||||
|
||||
/*
|
||||
* Window shadow styles
|
||||
@ -816,6 +816,12 @@ class nsIWidget : public nsISupports {
|
||||
*/
|
||||
NS_IMETHOD GetLastInputEventTime(PRUint32& aTime) = 0;
|
||||
|
||||
/**
|
||||
* Ask whether there user input events pending. All input events are
|
||||
* included, including those not targeted at this nsIwidget instance.
|
||||
*/
|
||||
virtual PRBool HasPendingInputEvent() = 0;
|
||||
|
||||
/**
|
||||
* Called when when we need to begin secure keyboard input, such as when a password field
|
||||
* gets focus.
|
||||
|
@ -668,6 +668,10 @@ nsAppShell::ProcessNextNativeEvent(PRBool aMayWait)
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK;
|
||||
|
||||
if (!moreEvents) {
|
||||
nsChildView::UpdateCurrentInputEventCount();
|
||||
}
|
||||
|
||||
return moreEvents;
|
||||
}
|
||||
|
||||
|
@ -369,6 +369,8 @@ public:
|
||||
|
||||
NS_IMETHOD GetAttention(PRInt32 aCycleCount);
|
||||
|
||||
virtual PRBool HasPendingInputEvent();
|
||||
|
||||
NS_IMETHOD ActivateNativeMenuItemAt(const nsAString& indexString);
|
||||
NS_IMETHOD ForceUpdateNativeMenuAt(const nsAString& indexString);
|
||||
|
||||
@ -411,6 +413,9 @@ public:
|
||||
|
||||
void ResetParent();
|
||||
|
||||
static PRBool DoHasPendingInputEvent();
|
||||
static PRUint32 GetCurrentInputEventCount();
|
||||
static void UpdateCurrentInputEventCount();
|
||||
protected:
|
||||
|
||||
PRBool ReportDestroyEvent();
|
||||
@ -456,6 +461,8 @@ protected:
|
||||
|
||||
nsPluginPort mPluginPort;
|
||||
nsIPluginInstanceOwner* mPluginInstanceOwner; // [WEAK]
|
||||
|
||||
static PRUint32 sLastInputEventCount;
|
||||
};
|
||||
|
||||
void NS_InstallPluginKeyEventsHandler();
|
||||
|
@ -82,6 +82,8 @@
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
|
||||
#undef DEBUG_IME
|
||||
#undef DEBUG_UPDATE
|
||||
#undef INVALIDATE_DEBUGGING // flash areas as they are invalidated
|
||||
@ -149,6 +151,8 @@ PRUint32 gLastModifierState = 0;
|
||||
|
||||
PRBool gUserCancelledDrag = PR_FALSE;
|
||||
|
||||
PRUint32 nsChildView::sLastInputEventCount = 0;
|
||||
|
||||
@interface ChildView(Private)
|
||||
|
||||
// sets up our view, attaching it to its owning gecko view
|
||||
@ -2059,6 +2063,54 @@ NS_IMETHODIMP nsChildView::GetAttention(PRInt32 aCycleCount)
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
||||
}
|
||||
|
||||
/* static */
|
||||
PRBool nsChildView::DoHasPendingInputEvent()
|
||||
{
|
||||
return sLastInputEventCount != GetCurrentInputEventCount();
|
||||
}
|
||||
|
||||
/* static */
|
||||
PRUint32 nsChildView::GetCurrentInputEventCount()
|
||||
{
|
||||
// Can't use kCGAnyInputEventType because that updates too rarely for us (and
|
||||
// always in increments of 30+!) and because apparently it's sort of broken
|
||||
// on Tiger. So just go ahead and query the counters we care about.
|
||||
static const CGEventType eventTypes[] = {
|
||||
kCGEventLeftMouseDown,
|
||||
kCGEventLeftMouseUp,
|
||||
kCGEventRightMouseDown,
|
||||
kCGEventRightMouseUp,
|
||||
kCGEventMouseMoved,
|
||||
kCGEventLeftMouseDragged,
|
||||
kCGEventRightMouseDragged,
|
||||
kCGEventKeyDown,
|
||||
kCGEventKeyUp,
|
||||
kCGEventScrollWheel,
|
||||
kCGEventTabletPointer,
|
||||
kCGEventOtherMouseDown,
|
||||
kCGEventOtherMouseUp,
|
||||
kCGEventOtherMouseDragged
|
||||
};
|
||||
|
||||
PRUint32 eventCount = 0;
|
||||
for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(eventTypes); ++i) {
|
||||
eventCount +=
|
||||
CGEventSourceCounterForEventType(kCGEventSourceStateCombinedSessionState,
|
||||
eventTypes[i]);
|
||||
}
|
||||
return eventCount;
|
||||
}
|
||||
|
||||
/* static */
|
||||
void nsChildView::UpdateCurrentInputEventCount()
|
||||
{
|
||||
sLastInputEventCount = GetCurrentInputEventCount();
|
||||
}
|
||||
|
||||
PRBool nsChildView::HasPendingInputEvent()
|
||||
{
|
||||
return DoHasPendingInputEvent();
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
|
@ -247,6 +247,7 @@ public:
|
||||
NS_IMETHOD DispatchEvent(nsGUIEvent* event, nsEventStatus & aStatus) ;
|
||||
NS_IMETHOD CaptureRollupEvents(nsIRollupListener * aListener, PRBool aDoCapture, PRBool aConsumeRollupEvent);
|
||||
NS_IMETHOD GetAttention(PRInt32 aCycleCount);
|
||||
virtual PRBool HasPendingInputEvent();
|
||||
virtual nsTransparencyMode GetTransparencyMode();
|
||||
virtual void SetTransparencyMode(nsTransparencyMode aMode);
|
||||
NS_IMETHOD SetWindowShadowStyle(PRInt32 aStyle);
|
||||
|
@ -62,6 +62,7 @@
|
||||
#include "nsMenuUtilsX.h"
|
||||
#include "nsStyleConsts.h"
|
||||
#include "nsNativeThemeColors.h"
|
||||
#include "nsChildView.h"
|
||||
|
||||
#include "gfxPlatform.h"
|
||||
#include "qcms.h"
|
||||
@ -1321,6 +1322,11 @@ NS_IMETHODIMP nsCocoaWindow::GetAttention(PRInt32 aCycleCount)
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsCocoaWindow::HasPendingInputEvent()
|
||||
{
|
||||
return nsChildView::DoHasPendingInputEvent();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsCocoaWindow::SetWindowShadowStyle(PRInt32 aStyle)
|
||||
{
|
||||
|
@ -1911,6 +1911,39 @@ nsWindow::GetAttention(PRInt32 aCycleCount)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsWindow::HasPendingInputEvent()
|
||||
{
|
||||
// This sucks, but gtk/gdk has no way to answer the question we want while
|
||||
// excluding paint events, and there's no X API that will let us peek
|
||||
// without blocking or removing. To prevent event reordering, peek
|
||||
// anything except expose events. Reordering expose and others should be
|
||||
// ok, hopefully.
|
||||
PRBool haveEvent;
|
||||
#ifdef MOZ_X11
|
||||
XEvent ev;
|
||||
haveEvent =
|
||||
XCheckMaskEvent(GDK_DISPLAY(),
|
||||
KeyPressMask | KeyReleaseMask | ButtonPressMask |
|
||||
ButtonReleaseMask | EnterWindowMask | LeaveWindowMask |
|
||||
PointerMotionMask | PointerMotionHintMask |
|
||||
Button1MotionMask | Button2MotionMask |
|
||||
Button3MotionMask | Button4MotionMask |
|
||||
Button5MotionMask | ButtonMotionMask | KeymapStateMask |
|
||||
VisibilityChangeMask | StructureNotifyMask |
|
||||
ResizeRedirectMask | SubstructureNotifyMask |
|
||||
SubstructureRedirectMask | FocusChangeMask |
|
||||
PropertyChangeMask | ColormapChangeMask |
|
||||
OwnerGrabButtonMask, &ev);
|
||||
if (haveEvent) {
|
||||
XPutBackEvent(GDK_DISPLAY(), &ev);
|
||||
}
|
||||
#else
|
||||
haveEvent = PR_FALSE;
|
||||
#endif
|
||||
return haveEvent;
|
||||
}
|
||||
|
||||
void
|
||||
nsWindow::LoseFocus(void)
|
||||
{
|
||||
|
@ -204,6 +204,9 @@ public:
|
||||
PRBool aDoCapture,
|
||||
PRBool aConsumeRollupEvent);
|
||||
NS_IMETHOD GetAttention(PRInt32 aCycleCount);
|
||||
|
||||
virtual PRBool HasPendingInputEvent();
|
||||
|
||||
NS_IMETHOD MakeFullScreen(PRBool aFullScreen);
|
||||
NS_IMETHOD HideWindowChrome(PRBool aShouldHide);
|
||||
|
||||
|
@ -3393,16 +3393,20 @@ NS_METHOD nsWindow::SetIcon(const nsAString& aIconSpec)
|
||||
NS_IMETHODIMP
|
||||
nsWindow::GetLastInputEventTime(PRUint32& aTime)
|
||||
{
|
||||
ULONG ulStatus = WinQueryQueueStatus(HWND_DESKTOP);
|
||||
// If there is pending input then return the current time.
|
||||
if (HasPendingInputEvent()) {
|
||||
gLastInputEventTime = PR_IntervalToMicroseconds(PR_IntervalNow());
|
||||
}
|
||||
|
||||
// If there is pending input then return the current time.
|
||||
if (ulStatus & (QS_KEY | QS_MOUSE)) {
|
||||
gLastInputEventTime = PR_IntervalToMicroseconds(PR_IntervalNow());
|
||||
}
|
||||
aTime = gLastInputEventTime;
|
||||
|
||||
aTime = gLastInputEventTime;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
PRBool
|
||||
nsWindow::HasPendingInputEvent()
|
||||
{
|
||||
return (WinQueryQueueStatus(HWND_DESKTOP) & (QS_KEY | QS_MOUSE)) != 0;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
|
@ -161,6 +161,7 @@ class nsWindow : public nsBaseWidget,
|
||||
NS_IMETHOD CaptureRollupEvents(nsIRollupListener * aListener, PRBool aDoCapture, PRBool aConsumeRollupEvent);
|
||||
|
||||
NS_IMETHOD GetLastInputEventTime(PRUint32& aTime);
|
||||
virtual PRBool HasPendingInputEvent();
|
||||
|
||||
// Widget appearance
|
||||
NS_IMETHOD SetCursor( nsCursor aCursor);
|
||||
|
@ -6990,15 +6990,7 @@ void nsWindow::StopFlashing()
|
||||
NS_IMETHODIMP
|
||||
nsWindow::GetLastInputEventTime(PRUint32& aTime)
|
||||
{
|
||||
WORD qstatus = HIWORD(GetQueueStatus(QS_INPUT));
|
||||
|
||||
// If there is pending input or the user is currently
|
||||
// moving the window then return the current time.
|
||||
// Note: When the user is moving the window WIN32 spins
|
||||
// a separate event loop and input events are not
|
||||
// reported to the application.
|
||||
nsToolkit* toolkit = (nsToolkit *)mToolkit;
|
||||
if (qstatus || (toolkit && toolkit->UserIsMovingWindow())) {
|
||||
if (HasPendingInputEvent()) {
|
||||
gLastInputEventTime = PR_IntervalToMicroseconds(PR_IntervalNow());
|
||||
}
|
||||
|
||||
@ -7007,6 +6999,19 @@ nsWindow::GetLastInputEventTime(PRUint32& aTime)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsWindow::HasPendingInputEvent()
|
||||
{
|
||||
// If there is pending input or the user is currently
|
||||
// moving the window then return true.
|
||||
// Note: When the user is moving the window WIN32 spins
|
||||
// a separate event loop and input events are not
|
||||
// reported to the application.
|
||||
WORD qstatus = HIWORD(GetQueueStatus(QS_INPUT));
|
||||
nsToolkit* toolkit = (nsToolkit *)mToolkit;
|
||||
return qstatus || (toolkit && toolkit->UserIsMovingWindow());
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------
|
||||
//-- NOTE!!! These hook functions can be removed when we migrate to
|
||||
|
@ -211,6 +211,7 @@ public:
|
||||
|
||||
NS_IMETHOD GetAttention(PRInt32 aCycleCount);
|
||||
NS_IMETHOD GetLastInputEventTime(PRUint32& aTime);
|
||||
virtual PRBool HasPendingInputEvent();
|
||||
|
||||
// Note that the result of GetTopLevelWindow method can be different from the
|
||||
// result of GetTopLevelHWND method. The result can be non-floating window.
|
||||
|
@ -798,6 +798,12 @@ nsBaseWidget::GetLastInputEventTime(PRUint32& aTime) {
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsBaseWidget::HasPendingInputEvent()
|
||||
{
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsBaseWidget::SetIcon(const nsAString&)
|
||||
{
|
||||
|
@ -122,6 +122,7 @@ public:
|
||||
NS_IMETHOD EnableDragDrop(PRBool aEnable);
|
||||
NS_IMETHOD GetAttention(PRInt32 aCycleCount);
|
||||
NS_IMETHOD GetLastInputEventTime(PRUint32& aTime);
|
||||
virtual PRBool HasPendingInputEvent();
|
||||
NS_IMETHOD SetIcon(const nsAString &anIconSpec);
|
||||
NS_IMETHOD BeginSecureKeyboardInput();
|
||||
NS_IMETHOD EndSecureKeyboardInput();
|
||||
|
Loading…
Reference in New Issue
Block a user