зеркало из https://github.com/mozilla/pjs.git
Bug 399852 - Crash [@ nsCSSFrameConstructor::FindFrameWithContent] with position: fixed, focusing and contenteditable, patch by Chris Pearce, r+sr=roc, a=blocking1.9+
This commit is contained in:
Родитель
7ee592b381
Коммит
6e467a44be
|
@ -66,6 +66,7 @@
|
||||||
#include "nsIPresShell.h"
|
#include "nsIPresShell.h"
|
||||||
#include "nsStyleSet.h"
|
#include "nsStyleSet.h"
|
||||||
#include "nsIViewManager.h"
|
#include "nsIViewManager.h"
|
||||||
|
#include "nsViewManager.h"
|
||||||
#include "nsIEventStateManager.h"
|
#include "nsIEventStateManager.h"
|
||||||
#include "nsIScrollableView.h"
|
#include "nsIScrollableView.h"
|
||||||
#include "nsStyleConsts.h"
|
#include "nsStyleConsts.h"
|
||||||
|
@ -379,6 +380,26 @@ static nsresult
|
||||||
DeletingFrameSubtree(nsFrameManager* aFrameManager,
|
DeletingFrameSubtree(nsFrameManager* aFrameManager,
|
||||||
nsIFrame* aFrame);
|
nsIFrame* aFrame);
|
||||||
|
|
||||||
|
void nsFocusEventSuppressor::Suppress(nsIPresShell *aPresShell)
|
||||||
|
{
|
||||||
|
NS_ASSERTION(aPresShell, "Need non-null nsIPresShell!");
|
||||||
|
NS_ASSERTION(!mViewManager, "Suppress before a pending UnSuppress()");
|
||||||
|
nsFrameManager *frameManager = aPresShell->FrameManager();
|
||||||
|
mViewManager = frameManager->GetPresContext()->GetViewManager();
|
||||||
|
if (mViewManager) {
|
||||||
|
mOldSuppressState = mViewManager->GetSuppressFocusEvents();
|
||||||
|
mViewManager->SetSuppressFocusEvents(PR_TRUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void nsFocusEventSuppressor::Unsuppress()
|
||||||
|
{
|
||||||
|
if (mViewManager) {
|
||||||
|
mViewManager->SetSuppressFocusEvents(mOldSuppressState);
|
||||||
|
mViewManager = nsnull;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef MOZ_SVG
|
#ifdef MOZ_SVG
|
||||||
|
|
||||||
static nsIFrame *
|
static nsIFrame *
|
||||||
|
@ -10237,6 +10258,14 @@ nsCSSFrameConstructor::AttributeChanged(nsIContent* aContent,
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsCSSFrameConstructor::BeginUpdate() {
|
||||||
|
if (!mUpdateCount) {
|
||||||
|
mFocusSuppressor.Suppress(mPresShell);
|
||||||
|
}
|
||||||
|
++mUpdateCount;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsCSSFrameConstructor::EndUpdate()
|
nsCSSFrameConstructor::EndUpdate()
|
||||||
{
|
{
|
||||||
|
@ -10246,6 +10275,8 @@ nsCSSFrameConstructor::EndUpdate()
|
||||||
|
|
||||||
RecalcQuotesAndCounters();
|
RecalcQuotesAndCounters();
|
||||||
NS_ASSERTION(mUpdateCount == 1, "Odd update count");
|
NS_ASSERTION(mUpdateCount == 1, "Odd update count");
|
||||||
|
|
||||||
|
mFocusSuppressor.Unsuppress();
|
||||||
}
|
}
|
||||||
|
|
||||||
--mUpdateCount;
|
--mUpdateCount;
|
||||||
|
|
|
@ -52,6 +52,7 @@
|
||||||
#include "nsHashKeys.h"
|
#include "nsHashKeys.h"
|
||||||
#include "nsThreadUtils.h"
|
#include "nsThreadUtils.h"
|
||||||
#include "nsPageContentFrame.h"
|
#include "nsPageContentFrame.h"
|
||||||
|
#include "nsIViewManager.h"
|
||||||
|
|
||||||
class nsIDocument;
|
class nsIDocument;
|
||||||
struct nsFrameItems;
|
struct nsFrameItems;
|
||||||
|
@ -73,6 +74,20 @@ struct nsFindFrameHint
|
||||||
nsFindFrameHint() : mPrimaryFrameForPrevSibling(nsnull) { }
|
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;
|
||||||
|
PRBool mOldSuppressState;
|
||||||
|
};
|
||||||
|
|
||||||
typedef void (PR_CALLBACK nsLazyFrameConstructionCallback)
|
typedef void (PR_CALLBACK nsLazyFrameConstructionCallback)
|
||||||
(nsIContent* aContent, nsIFrame* aFrame, void* aArg);
|
(nsIContent* aContent, nsIFrame* aFrame, void* aArg);
|
||||||
|
|
||||||
|
@ -148,7 +163,7 @@ public:
|
||||||
PRInt32 aModType,
|
PRInt32 aModType,
|
||||||
PRUint32 aStateMask);
|
PRUint32 aStateMask);
|
||||||
|
|
||||||
void BeginUpdate() { ++mUpdateCount; }
|
void BeginUpdate();
|
||||||
void EndUpdate();
|
void EndUpdate();
|
||||||
void RecalcQuotesAndCounters();
|
void RecalcQuotesAndCounters();
|
||||||
|
|
||||||
|
@ -162,6 +177,9 @@ public:
|
||||||
nsresult ProcessRestyledFrames(nsStyleChangeList& aRestyleArray);
|
nsresult ProcessRestyledFrames(nsStyleChangeList& aRestyleArray);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
nsFocusEventSuppressor mFocusSuppressor;
|
||||||
|
|
||||||
// Note: It's the caller's responsibility to make sure to wrap a
|
// Note: It's the caller's responsibility to make sure to wrap a
|
||||||
// ProcessOneRestyle call in a view update batch.
|
// ProcessOneRestyle call in a view update batch.
|
||||||
// This function does not call ProcessAttachedQueue() on the binding manager.
|
// This function does not call ProcessAttachedQueue() on the binding manager.
|
||||||
|
|
|
@ -440,6 +440,18 @@ public:
|
||||||
* (aFromScroll is false) or scrolled (aFromScroll is true).
|
* (aFromScroll is false) or scrolled (aFromScroll is true).
|
||||||
*/
|
*/
|
||||||
NS_IMETHOD SynthesizeMouseMove(PRBool aFromScroll)=0;
|
NS_IMETHOD SynthesizeMouseMove(PRBool aFromScroll)=0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggles global suppression of focus/blur events. When suppression
|
||||||
|
* is on, focus/blur events will not be sent to their target widgets/views.
|
||||||
|
* Note that when called with aSuppress as false, blur/focus events are
|
||||||
|
* fired to reset the focus. This can run arbitrary code, and could
|
||||||
|
* even destroy the view manager.
|
||||||
|
*/
|
||||||
|
virtual void SetSuppressFocusEvents(PRBool aSuppress)=0;
|
||||||
|
|
||||||
|
virtual PRBool GetSuppressFocusEvents()=0;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
NS_DEFINE_STATIC_IID_ACCESSOR(nsIViewManager, NS_IVIEWMANAGER_IID)
|
NS_DEFINE_STATIC_IID_ACCESSOR(nsIViewManager, NS_IVIEWMANAGER_IID)
|
||||||
|
|
|
@ -200,6 +200,13 @@ nsView::~nsView()
|
||||||
{
|
{
|
||||||
MOZ_COUNT_DTOR(nsView);
|
MOZ_COUNT_DTOR(nsView);
|
||||||
|
|
||||||
|
if (this == nsViewManager::GetViewFocusedBeforeSuppression()) {
|
||||||
|
nsViewManager::SetViewFocusedBeforeSuppression(nsnull);
|
||||||
|
}
|
||||||
|
if (this == nsViewManager::GetCurrentlyFocusedView()) {
|
||||||
|
nsViewManager::SetCurrentlyFocusedView(nsnull);
|
||||||
|
}
|
||||||
|
|
||||||
while (GetFirstChild())
|
while (GetFirstChild())
|
||||||
{
|
{
|
||||||
nsView* child = GetFirstChild();
|
nsView* child = GetFirstChild();
|
||||||
|
|
|
@ -935,6 +935,63 @@ void nsViewManager::UpdateViews(nsView *aView, PRUint32 aUpdateFlags)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PRBool nsViewManager::sSuppressFocusEvents = PR_FALSE;
|
||||||
|
nsView *nsViewManager::sCurrentlyFocusView = nsnull;
|
||||||
|
nsView *nsViewManager::sViewFocusedBeforeSuppression = nsnull;
|
||||||
|
|
||||||
|
// Enables/disables focus/blur event suppression. When suppression
|
||||||
|
// is disabled, we "reboot" 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. The suppression should be enabled
|
||||||
|
// when we're messing with the frame tree, so focus/blur handlers
|
||||||
|
// don't mess with stuff while we're trying too. See Bug 399852.
|
||||||
|
void nsViewManager::SetSuppressFocusEvents(PRBool aSuppress)
|
||||||
|
{
|
||||||
|
if (sSuppressFocusEvents && !aSuppress) {
|
||||||
|
// We're turning off suppression, synthesize LOSTFOCUS/GOTFOCUS.
|
||||||
|
if (GetCurrentlyFocusedView() != GetViewFocusedBeforeSuppression()) {
|
||||||
|
|
||||||
|
// Turn off suppresion before we send blur/focus events.
|
||||||
|
sSuppressFocusEvents = aSuppress;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (!sSuppressFocusEvents && aSuppress) {
|
||||||
|
// We're turning on focus/blur suppression, remember what had
|
||||||
|
// the focus.
|
||||||
|
SetViewFocusedBeforeSuppression(GetCurrentlyFocusedView());
|
||||||
|
sSuppressFocusEvents = aSuppress;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP nsViewManager::DispatchEvent(nsGUIEvent *aEvent, nsEventStatus *aStatus)
|
NS_IMETHODIMP nsViewManager::DispatchEvent(nsGUIEvent *aEvent, nsEventStatus *aStatus)
|
||||||
{
|
{
|
||||||
*aStatus = nsEventStatus_eIgnore;
|
*aStatus = nsEventStatus_eIgnore;
|
||||||
|
@ -1138,6 +1195,12 @@ NS_IMETHODIMP nsViewManager::DispatchEvent(nsGUIEvent *aEvent, nsEventStatus *aS
|
||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
|
if (aEvent->message == NS_GOTFOCUS)
|
||||||
|
SetCurrentlyFocusedView(nsView::GetViewFor(aEvent->widget));
|
||||||
|
if ((aEvent->message == NS_GOTFOCUS || aEvent->message == NS_LOSTFOCUS) &&
|
||||||
|
nsViewManager::GetSuppressFocusEvents())
|
||||||
|
break;
|
||||||
|
|
||||||
if ((NS_IS_MOUSE_EVENT(aEvent) &&
|
if ((NS_IS_MOUSE_EVENT(aEvent) &&
|
||||||
// Ignore moves that we synthesize.
|
// Ignore moves that we synthesize.
|
||||||
static_cast<nsMouseEvent*>(aEvent)->reason ==
|
static_cast<nsMouseEvent*>(aEvent)->reason ==
|
||||||
|
|
|
@ -201,10 +201,42 @@ public:
|
||||||
/* Update the cached RootViewManager pointer on this view manager. */
|
/* Update the cached RootViewManager pointer on this view manager. */
|
||||||
void InvalidateHierarchy();
|
void InvalidateHierarchy();
|
||||||
|
|
||||||
|
virtual void SetSuppressFocusEvents(PRBool aSuppress);
|
||||||
|
|
||||||
|
virtual PRBool GetSuppressFocusEvents()
|
||||||
|
{
|
||||||
|
return sSuppressFocusEvents;
|
||||||
|
}
|
||||||
|
|
||||||
|
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:
|
protected:
|
||||||
virtual ~nsViewManager();
|
virtual ~nsViewManager();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
static nsView *sCurrentlyFocusView;
|
||||||
|
static nsView *sViewFocusedBeforeSuppression;
|
||||||
|
static PRBool sSuppressFocusEvents;
|
||||||
|
|
||||||
void FlushPendingInvalidates();
|
void FlushPendingInvalidates();
|
||||||
void ProcessPendingUpdates(nsView *aView, PRBool aDoInvalidate);
|
void ProcessPendingUpdates(nsView *aView, PRBool aDoInvalidate);
|
||||||
void ReparentChildWidgets(nsIView* aView, nsIWidget *aNewWidget);
|
void ReparentChildWidgets(nsIView* aView, nsIWidget *aNewWidget);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче