diff --git a/mozilla/layout/base/nsCSSFrameConstructor.cpp b/mozilla/layout/base/nsCSSFrameConstructor.cpp index d78803b2b14..d853cbe4a87 100644 --- a/mozilla/layout/base/nsCSSFrameConstructor.cpp +++ b/mozilla/layout/base/nsCSSFrameConstructor.cpp @@ -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 * @@ -10232,6 +10249,12 @@ nsCSSFrameConstructor::AttributeChanged(nsIContent* aContent, return result; } +void +nsCSSFrameConstructor::BeginUpdate() { + mFocusSuppressor.Suppress(mPresShell); + ++mUpdateCount; +} + void nsCSSFrameConstructor::EndUpdate() { @@ -10242,7 +10265,7 @@ nsCSSFrameConstructor::EndUpdate() RecalcQuotesAndCounters(); NS_ASSERTION(mUpdateCount == 1, "Odd update count"); } - + mFocusSuppressor.Unsuppress(); --mUpdateCount; } diff --git a/mozilla/toolkit/components/passwordmgr/test/test_basic_form_autocomplete.html b/mozilla/toolkit/components/passwordmgr/test/test_basic_form_autocomplete.html index 2fe365aae8e..278ee314e12 100644 --- a/mozilla/toolkit/components/passwordmgr/test/test_basic_form_autocomplete.html +++ b/mozilla/toolkit/components/passwordmgr/test/test_basic_form_autocomplete.html @@ -84,6 +84,7 @@ function restoreForm() { uname.value = ""; pword.value = ""; uname.focus(); + ok(document.activeElement == uname, "Username should be focused."); } @@ -126,6 +127,7 @@ function runTest(testNum) { // Seems we need to enable this again, or sendKeyEvent() complaints. netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect'); ok(true, "Starting test #" + testNum); + ok(document.hasFocus(), "Document should be focused"); switch(testNum) { case 1: @@ -417,12 +419,31 @@ function runTest(testNum) { } +function __log(message) { + var m = '[CPEARCE] ' + message; + dump(m); + ok(true, m); +} + + function startTest() { - runTest(1); + __log('JS: onload() document.hasFocus=' + document.hasFocus() + +' document.activeElement=[tag=' + document.activeElement.tagName +' name=' +document.activeElement.name +']\n'); + runTest(1); } window.onload = startTest; +window.onfocus = function() { + __log('JS: onfocus() document.hasFocus=' + document.hasFocus() + +' document.activeElement=[tag=' + document.activeElement.tagName +' name=' +document.activeElement.name +']\n'); +} + +window.onblur = function() { + __log('JS: onblur() document.hasFocus=' + document.hasFocus() + +' document.activeElement=[tag=' + document.activeElement.tagName +' name=' +document.activeElement.name +']\n'); +} + SimpleTest.waitForExplicitFinish(); diff --git a/mozilla/view/public/nsIViewManager.h b/mozilla/view/public/nsIViewManager.h index 14f5d65d508..16eae946f60 100644 --- a/mozilla/view/public/nsIViewManager.h +++ b/mozilla/view/public/nsIViewManager.h @@ -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) diff --git a/mozilla/view/src/nsView.cpp b/mozilla/view/src/nsView.cpp index f2bcbd31ab4..8645f4237eb 100644 --- a/mozilla/view/src/nsView.cpp +++ b/mozilla/view/src/nsView.cpp @@ -200,6 +200,19 @@ nsView::~nsView() { MOZ_COUNT_DTOR(nsView); + if (this == nsViewManager::GetViewFocusedBeforeSuppression()) { + if (GetViewManager()->IsFocusSuppressed()) { + printf("*** 0 INFO TODO [CPEARCE] destroying view focused before suppression, while suppressed\n"); + } + nsViewManager::SetViewFocusedBeforeSuppression(nsnull); + } + if (this == nsViewManager::GetCurrentlyFocusedView()) { + if (GetViewManager()->IsFocusSuppressed()) { + printf("*** 0 INFO TODO [CPEARCE] destroying view currently focused, while suppressed\n"); + } + nsViewManager::SetCurrentlyFocusedView(nsnull); + } + while (GetFirstChild()) { nsView* child = GetFirstChild(); diff --git a/mozilla/view/src/nsViewManager.cpp b/mozilla/view/src/nsViewManager.cpp index 8746a94d718..e1b05eb7c0f 100644 --- a/mozilla/view/src/nsViewManager.cpp +++ b/mozilla/view/src/nsViewManager.cpp @@ -935,6 +935,61 @@ 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) { + printf("*** 0 INFO TODO [CPEARCE] Unsuppressing, dispatching NS_LOSTFOCUS\n"); + 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) { + printf("*** 0 INFO TODO [CPEARCE] Unsuppressing, dispatching NS_GOTFOCUS\n"); + nsGUIEvent event(PR_TRUE, NS_GOTFOCUS, widget); + widget->DispatchEvent(&event, status); + } + } + +} + NS_IMETHODIMP nsViewManager::DispatchEvent(nsGUIEvent *aEvent, nsEventStatus *aStatus) { *aStatus = nsEventStatus_eIgnore; @@ -1138,6 +1193,20 @@ NS_IMETHODIMP nsViewManager::DispatchEvent(nsGUIEvent *aEvent, nsEventStatus *aS default: { + if (aEvent->message == NS_GOTFOCUS) { + printf("*** 0 INFO TODO [CPEARCE] Focus changing%s\n", + (nsViewManager::IsFocusSuppressed() ? " while suppressed" : "")); + SetCurrentlyFocusedView(nsView::GetViewFor(aEvent->widget)); + } + if ((aEvent->message == NS_GOTFOCUS || aEvent->message == NS_LOSTFOCUS) && + nsViewManager::IsFocusSuppressed()) + { + printf("*** 0 INFO TODO [CPEARCE] Suppressing %s\n", + (aEvent->message == NS_GOTFOCUS ? "NS_GOTFOCUS" : "NS_LOSTFOCUS")); + + break; + } + if ((NS_IS_MOUSE_EVENT(aEvent) && // Ignore moves that we synthesize. static_cast(aEvent)->reason == diff --git a/mozilla/view/src/nsViewManager.h b/mozilla/view/src/nsViewManager.h index d0c69b50584..eae248ef08d 100644 --- a/mozilla/view/src/nsViewManager.h +++ b/mozilla/view/src/nsViewManager.h @@ -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);