зеркало из https://github.com/mozilla/pjs.git
Bug 399852. Suppress focus changes during content updates, reboot focus after the update has finished. patch by Chris Pearce, r+sr=roc
This commit is contained in:
Родитель
5a4d5f2857
Коммит
2d662517f2
|
@ -379,6 +379,23 @@ static nsresult
|
|||
DeletingFrameSubtree(nsFrameManager* aFrameManager,
|
||||
nsIFrame* aFrame);
|
||||
|
||||
void nsFocusEventSuppressor::Suppress(nsIPresShell *aPresShell)
|
||||
{
|
||||
NS_ASSERTION(aPresShell, "Need non-null nsIPresShell!");
|
||||
if (!mViewManager) {
|
||||
nsFrameManager *frameManager = aPresShell->FrameManager();
|
||||
mViewManager = frameManager->GetPresContext()->GetViewManager();
|
||||
NS_ASSERTION(mViewManager, "We must have an mViewManager here");
|
||||
}
|
||||
mViewManager->SuppressFocusEvents();
|
||||
}
|
||||
|
||||
void nsFocusEventSuppressor::Unsuppress()
|
||||
{
|
||||
NS_ASSERTION(mViewManager, "We must have an mViewManager here");
|
||||
mViewManager->UnsuppressFocusEvents();
|
||||
}
|
||||
|
||||
#ifdef MOZ_SVG
|
||||
|
||||
static nsIFrame *
|
||||
|
@ -10233,6 +10250,12 @@ nsCSSFrameConstructor::AttributeChanged(nsIContent* aContent,
|
|||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
nsCSSFrameConstructor::BeginUpdate() {
|
||||
mFocusSuppressor.Suppress(mPresShell);
|
||||
++mUpdateCount;
|
||||
}
|
||||
|
||||
void
|
||||
nsCSSFrameConstructor::EndUpdate()
|
||||
{
|
||||
|
@ -10243,7 +10266,7 @@ nsCSSFrameConstructor::EndUpdate()
|
|||
RecalcQuotesAndCounters();
|
||||
NS_ASSERTION(mUpdateCount == 1, "Odd update count");
|
||||
}
|
||||
|
||||
mFocusSuppressor.Unsuppress();
|
||||
--mUpdateCount;
|
||||
}
|
||||
|
||||
|
|
|
@ -52,6 +52,7 @@
|
|||
#include "nsHashKeys.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsPageContentFrame.h"
|
||||
#include "nsIViewManager.h"
|
||||
|
||||
class nsIDocument;
|
||||
struct nsFrameItems;
|
||||
|
@ -73,6 +74,19 @@ struct nsFindFrameHint
|
|||
nsFindFrameHint() : mPrimaryFrameForPrevSibling(nsnull) { }
|
||||
};
|
||||
|
||||
// Class which makes an nsIPresShell's ViewManager supress
|
||||
// focus/blur events. This prevents the frame tree from being changed
|
||||
// by focus handlers etc while *we* are trying to change it.
|
||||
// Fix for bug 399852.
|
||||
class nsFocusEventSuppressor
|
||||
{
|
||||
public:
|
||||
void Suppress(nsIPresShell *aPresShell);
|
||||
void Unsuppress();
|
||||
private:
|
||||
nsCOMPtr<nsIViewManager> mViewManager;
|
||||
};
|
||||
|
||||
typedef void (PR_CALLBACK nsLazyFrameConstructionCallback)
|
||||
(nsIContent* aContent, nsIFrame* aFrame, void* aArg);
|
||||
|
||||
|
@ -148,7 +162,7 @@ public:
|
|||
PRInt32 aModType,
|
||||
PRUint32 aStateMask);
|
||||
|
||||
void BeginUpdate() { ++mUpdateCount; }
|
||||
void BeginUpdate();
|
||||
void EndUpdate();
|
||||
void RecalcQuotesAndCounters();
|
||||
|
||||
|
@ -162,6 +176,9 @@ public:
|
|||
nsresult ProcessRestyledFrames(nsStyleChangeList& aRestyleArray);
|
||||
|
||||
private:
|
||||
|
||||
nsFocusEventSuppressor mFocusSuppressor;
|
||||
|
||||
// Note: It's the caller's responsibility to make sure to wrap a
|
||||
// ProcessOneRestyle call in a view update batch.
|
||||
// This function does not call ProcessAttachedQueue() on the binding manager.
|
||||
|
|
|
@ -481,6 +481,31 @@ public:
|
|||
* (aFromScroll is false) or scrolled (aFromScroll is true).
|
||||
*/
|
||||
NS_IMETHOD SynthesizeMouseMove(PRBool aFromScroll)=0;
|
||||
|
||||
/**
|
||||
* Enables focus/blur event suppression. This stops focus/blur
|
||||
* events from reaching the widgets. This should be enabled
|
||||
* when we're messing with the frame tree, so focus/blur handlers
|
||||
* don't mess with stuff while we are. See Bug 399852.
|
||||
*/
|
||||
virtual void SuppressFocusEvents()=0;
|
||||
|
||||
/**
|
||||
* Disables focus/blur event suppression. This "reboots" the focus
|
||||
* by sending a blur to what was focused before suppression began,
|
||||
* and by sending a focus event to what should be currently focused.
|
||||
* Note this can run arbitrary code, and could even destroy the view
|
||||
* manager. The suppression should be enabled when we're messing with
|
||||
* the frame tree, so focus/blur handlers don't mess with stuff while
|
||||
* we are. See Bug 399852.
|
||||
*/
|
||||
virtual void UnsuppressFocusEvents()=0;
|
||||
|
||||
/**
|
||||
* Returns true when focus suppression is on.
|
||||
*/
|
||||
virtual PRBool IsFocusSuppressed()=0;
|
||||
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(nsIViewManager, NS_IVIEWMANAGER_IID)
|
||||
|
|
|
@ -200,6 +200,13 @@ nsView::~nsView()
|
|||
{
|
||||
MOZ_COUNT_DTOR(nsView);
|
||||
|
||||
if (this == nsViewManager::GetViewFocusedBeforeSuppression()) {
|
||||
nsViewManager::SetViewFocusedBeforeSuppression(nsnull);
|
||||
}
|
||||
if (this == nsViewManager::GetCurrentlyFocusedView()) {
|
||||
nsViewManager::SetCurrentlyFocusedView(nsnull);
|
||||
}
|
||||
|
||||
while (GetFirstChild())
|
||||
{
|
||||
nsView* child = GetFirstChild();
|
||||
|
|
|
@ -935,6 +935,59 @@ void nsViewManager::UpdateViews(nsView *aView, PRUint32 aUpdateFlags)
|
|||
}
|
||||
}
|
||||
|
||||
nsView *nsViewManager::sCurrentlyFocusView = nsnull;
|
||||
nsView *nsViewManager::sViewFocusedBeforeSuppression = nsnull;
|
||||
PRInt32 nsViewManager::sSuppressCount = 0;
|
||||
|
||||
void nsViewManager::SuppressFocusEvents()
|
||||
{
|
||||
sSuppressCount++;
|
||||
if (sSuppressCount == 1) {
|
||||
// We're turning on focus/blur suppression, remember what had
|
||||
// the focus.
|
||||
SetViewFocusedBeforeSuppression(GetCurrentlyFocusedView());
|
||||
}
|
||||
}
|
||||
|
||||
void nsViewManager::UnsuppressFocusEvents()
|
||||
{
|
||||
sSuppressCount--;
|
||||
if (sSuppressCount > 0 ||
|
||||
GetCurrentlyFocusedView() == GetViewFocusedBeforeSuppression())
|
||||
return;
|
||||
|
||||
// We're turning off suppression, synthesize LOSTFOCUS/GOTFOCUS.
|
||||
nsIWidget *widget = nsnull;
|
||||
nsEventStatus status;
|
||||
|
||||
// Backup what is focused before we send the blur. If the
|
||||
// blur causes a focus change, keep that new focus change,
|
||||
// don't overwrite with the old "currently focused view".
|
||||
nsIView *currentFocusBeforeBlur = GetCurrentlyFocusedView();
|
||||
|
||||
// Send NS_LOSTFOCUS to widget that was focused before
|
||||
// focus/blur suppression.
|
||||
if (GetViewFocusedBeforeSuppression()) {
|
||||
widget = GetViewFocusedBeforeSuppression()->GetWidget();
|
||||
if (widget) {
|
||||
nsGUIEvent event(PR_TRUE, NS_LOSTFOCUS, widget);
|
||||
widget->DispatchEvent(&event, status);
|
||||
}
|
||||
}
|
||||
|
||||
// Send NS_GOTFOCUS to the widget that we think should be focused.
|
||||
if (GetCurrentlyFocusedView() &&
|
||||
currentFocusBeforeBlur == GetCurrentlyFocusedView())
|
||||
{
|
||||
widget = GetCurrentlyFocusedView()->GetWidget();
|
||||
if (widget) {
|
||||
nsGUIEvent event(PR_TRUE, NS_GOTFOCUS, widget);
|
||||
widget->DispatchEvent(&event, status);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsViewManager::DispatchEvent(nsGUIEvent *aEvent, nsEventStatus *aStatus)
|
||||
{
|
||||
*aStatus = nsEventStatus_eIgnore;
|
||||
|
@ -1138,6 +1191,13 @@ NS_IMETHODIMP nsViewManager::DispatchEvent(nsGUIEvent *aEvent, nsEventStatus *aS
|
|||
|
||||
default:
|
||||
{
|
||||
if (aEvent->message == NS_GOTFOCUS) {
|
||||
SetCurrentlyFocusedView(nsView::GetViewFor(aEvent->widget));
|
||||
}
|
||||
if ((aEvent->message == NS_GOTFOCUS || aEvent->message == NS_LOSTFOCUS) &&
|
||||
nsViewManager::IsFocusSuppressed())
|
||||
break;
|
||||
|
||||
if ((NS_IS_MOUSE_EVENT(aEvent) &&
|
||||
// Ignore moves that we synthesize.
|
||||
static_cast<nsMouseEvent*>(aEvent)->reason ==
|
||||
|
|
|
@ -201,10 +201,43 @@ public:
|
|||
/* Update the cached RootViewManager pointer on this view manager. */
|
||||
void InvalidateHierarchy();
|
||||
|
||||
virtual void SuppressFocusEvents();
|
||||
virtual void UnsuppressFocusEvents();
|
||||
|
||||
virtual PRBool IsFocusSuppressed()
|
||||
{
|
||||
return sSuppressCount > 0;
|
||||
}
|
||||
|
||||
static void SetCurrentlyFocusedView(nsView *aView)
|
||||
{
|
||||
sCurrentlyFocusView = aView;
|
||||
}
|
||||
|
||||
static nsView* GetCurrentlyFocusedView()
|
||||
{
|
||||
return sCurrentlyFocusView;
|
||||
}
|
||||
|
||||
static void SetViewFocusedBeforeSuppression(nsView *aView)
|
||||
{
|
||||
sViewFocusedBeforeSuppression = aView;
|
||||
}
|
||||
|
||||
static nsView* GetViewFocusedBeforeSuppression()
|
||||
{
|
||||
return sViewFocusedBeforeSuppression;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~nsViewManager();
|
||||
|
||||
private:
|
||||
|
||||
static nsView *sCurrentlyFocusView;
|
||||
static nsView *sViewFocusedBeforeSuppression;
|
||||
static PRInt32 sSuppressCount;
|
||||
|
||||
void FlushPendingInvalidates();
|
||||
void ProcessPendingUpdates(nsView *aView, PRBool aDoInvalidate);
|
||||
void ReparentChildWidgets(nsIView* aView, nsIWidget *aNewWidget);
|
||||
|
|
Загрузка…
Ссылка в новой задаче