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:
martijn.martijn@gmail.com 2008-01-09 13:44:59 -08:00
Родитель 7ee592b381
Коммит 6e467a44be
6 изменённых файлов: 164 добавлений и 1 удалений

Просмотреть файл

@ -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);