From e9b8a89f91ab05f1855c24b4dbdaae23e22002b4 Mon Sep 17 00:00:00 2001 From: "Olli.Pettay%helsinki.fi" Date: Thu, 20 Mar 2008 20:24:39 +0000 Subject: [PATCH] Bug 421209, r=cpearce, sr=jst, a=beltzner git-svn-id: svn://10.0.0.236/trunk@248307 18797224-902f-48f8-a5cc-f745e15eee43 --- .../events/src/nsEventStateManager.cpp | 17 ++- mozilla/docshell/base/nsDocShell.cpp | 10 +- mozilla/layout/base/Makefile.in | 4 +- mozilla/layout/base/nsCSSFrameConstructor.cpp | 23 +--- mozilla/layout/base/nsCSSFrameConstructor.h | 15 --- .../layout/base/nsFocusEventSuppressor.cpp | 123 ++++++++++++++++++ mozilla/layout/base/nsIFocusEventSuppressor.h | 74 +++++++++++ mozilla/layout/build/nsLayoutModule.cpp | 9 ++ mozilla/layout/build/nsLayoutStatics.cpp | 2 + mozilla/view/public/nsIViewManager.h | 30 +---- mozilla/view/src/nsViewManager.cpp | 35 ++--- mozilla/view/src/nsViewManager.h | 21 ++- .../xpfe/appshell/src/nsWebShellWindow.cpp | 79 ++++++++++- mozilla/xpfe/appshell/src/nsWebShellWindow.h | 1 + 14 files changed, 354 insertions(+), 89 deletions(-) create mode 100644 mozilla/layout/base/nsFocusEventSuppressor.cpp create mode 100644 mozilla/layout/base/nsIFocusEventSuppressor.h diff --git a/mozilla/content/events/src/nsEventStateManager.cpp b/mozilla/content/events/src/nsEventStateManager.cpp index 4c838dfcbef..284b2d031c4 100644 --- a/mozilla/content/events/src/nsEventStateManager.cpp +++ b/mozilla/content/events/src/nsEventStateManager.cpp @@ -868,6 +868,9 @@ nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext, break; case NS_GOTFOCUS: { +#ifdef DEBUG_smaug + printf("nsEventStateManager::PreHandleEvent, NS_GOTFOCUS \n"); +#endif // This is called when a child widget has received focus. // We need to take care of sending a blur event for the previously // focused content and document, then dispatching a focus @@ -1031,6 +1034,9 @@ nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext, case NS_LOSTFOCUS: { +#ifdef DEBUG_smaug + printf("nsEventStateManager::PreHandleEvent, NS_LOSTFOCUS \n"); +#endif // Hide the caret if it's visible. if (mPresContext) { nsIPresShell *presShell = mPresContext->GetPresShell(); @@ -1052,7 +1058,10 @@ nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext, // This allows "-moz-user-focus: ignore" to work. #if defined(XP_WIN) || defined(XP_OS2) - if (!static_cast(aEvent)->isMozWindowTakingFocus) { + //XXXsmaug Change all the NS_LOSTFOCUS/GOTFOCUS events to use + // the same event struct type! + if (aEvent->eventStructType == NS_FOCUS_EVENT && + !static_cast(aEvent)->isMozWindowTakingFocus) { // This situation occurs when focus goes to a non-gecko child window // in an embedding application. In this case we do fire a blur @@ -1124,6 +1133,9 @@ nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext, case NS_ACTIVATE: { +#ifdef DEBUG_smaug + printf("nsEventStateManager::PreHandleEvent, NS_ACTIVATE \n"); +#endif // If we have a focus controller, and if it has a focused window and a // focused element in its focus memory, then restore the focus to those // objects. @@ -1211,6 +1223,9 @@ nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext, case NS_DEACTIVATE: { +#ifdef DEBUG_smaug + printf("nsEventStateManager::PreHandleEvent, NS_DEACTIVATE \n"); +#endif EnsureDocument(aPresContext); nsIMEStateManager::OnDeactivate(aPresContext); diff --git a/mozilla/docshell/base/nsDocShell.cpp b/mozilla/docshell/base/nsDocShell.cpp index f21b5ddcc6d..09437641fec 100644 --- a/mozilla/docshell/base/nsDocShell.cpp +++ b/mozilla/docshell/base/nsDocShell.cpp @@ -201,6 +201,7 @@ static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID); #endif #include "nsContentErrors.h" +#include "nsIFocusEventSuppressor.h" // Number of documents currently loading static PRInt32 gNumberOfDocumentsLoading = 0; @@ -3675,7 +3676,12 @@ nsDocShell::Destroy() if (docShellParentAsItem) docShellParentAsItem->RemoveChild(this); + nsCOMPtr suppressor; if (mContentViewer) { + suppressor = + do_GetService(NS_NSIFOCUSEVENTSUPPRESSORSERVICE_CONTRACTID); + NS_ENSURE_STATE(suppressor); + suppressor->Suppress(); mContentViewer->Close(nsnull); mContentViewer->Destroy(); mContentViewer = nsnull; @@ -3702,7 +3708,9 @@ nsDocShell::Destroy() // Cancel any timers that were set for this docshell; this is needed // to break the cycle between us and the timers. CancelRefreshURITimers(); - + if (suppressor) { + suppressor->Unsuppress(); + } return NS_OK; } diff --git a/mozilla/layout/base/Makefile.in b/mozilla/layout/base/Makefile.in index 9db0ff8cfd7..bc3c181a9dd 100644 --- a/mozilla/layout/base/Makefile.in +++ b/mozilla/layout/base/Makefile.in @@ -109,6 +109,7 @@ EXPORTS = \ nsIReflowCallback.h \ nsLayoutErrors.h \ nsLayoutUtils.h \ + nsIFocusEventSuppressor.h \ nsPresContext.h \ nsPresState.h \ nsStyleChangeList.h \ @@ -122,7 +123,7 @@ CPPSRCS = \ nsCaret.cpp \ nsChildIterator.cpp \ nsCounterManager.cpp \ - nsDisplayList.cpp \ + nsDisplayList.cpp \ nsDocumentViewer.cpp \ nsFrameManager.cpp \ nsFrameTraversal.cpp \ @@ -137,6 +138,7 @@ CPPSRCS = \ nsQuoteList.cpp \ nsStyleChangeList.cpp \ nsStyleSheetService.cpp \ + nsFocusEventSuppressor.cpp \ $(NULL) ifndef MOZ_XUL diff --git a/mozilla/layout/base/nsCSSFrameConstructor.cpp b/mozilla/layout/base/nsCSSFrameConstructor.cpp index 8617446ed80..35c783aa814 100644 --- a/mozilla/layout/base/nsCSSFrameConstructor.cpp +++ b/mozilla/layout/base/nsCSSFrameConstructor.cpp @@ -121,7 +121,7 @@ #include "nsIPrincipal.h" #include "nsIDOMWindowInternal.h" #include "nsStyleUtil.h" - +#include "nsIFocusEventSuppressor.h" #include "nsBox.h" #ifdef MOZ_XUL @@ -379,23 +379,6 @@ 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 * @@ -10252,7 +10235,7 @@ nsCSSFrameConstructor::AttributeChanged(nsIContent* aContent, void nsCSSFrameConstructor::BeginUpdate() { - mFocusSuppressor.Suppress(mPresShell); + NS_SuppressFocusEvent(); ++mUpdateCount; } @@ -10266,7 +10249,7 @@ nsCSSFrameConstructor::EndUpdate() RecalcQuotesAndCounters(); NS_ASSERTION(mUpdateCount == 1, "Odd update count"); } - mFocusSuppressor.Unsuppress(); + NS_UnsuppressFocusEvent(); --mUpdateCount; } diff --git a/mozilla/layout/base/nsCSSFrameConstructor.h b/mozilla/layout/base/nsCSSFrameConstructor.h index a92a0f79746..7590becb517 100644 --- a/mozilla/layout/base/nsCSSFrameConstructor.h +++ b/mozilla/layout/base/nsCSSFrameConstructor.h @@ -74,19 +74,6 @@ 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 mViewManager; -}; - typedef void (PR_CALLBACK nsLazyFrameConstructionCallback) (nsIContent* aContent, nsIFrame* aFrame, void* aArg); @@ -177,8 +164,6 @@ public: 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. diff --git a/mozilla/layout/base/nsFocusEventSuppressor.cpp b/mozilla/layout/base/nsFocusEventSuppressor.cpp new file mode 100644 index 00000000000..bdb06be6be2 --- /dev/null +++ b/mozilla/layout/base/nsFocusEventSuppressor.cpp @@ -0,0 +1,123 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Olli Pettay (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsIFocusEventSuppressor.h" +#include "nsTArray.h" + +class nsFocusEventSuppressorService : public nsIFocusEventSuppressorService +{ +public: + NS_DECL_ISUPPORTS + virtual void AddObserverCallback(nsFocusEventSuppressorCallback aCallback) + { + NS_AddFocusSuppressorCallback(aCallback); + } + virtual void Suppress() + { + NS_SuppressFocusEvent(); + } + virtual void Unsuppress() + { + NS_UnsuppressFocusEvent(); + } +}; + +static nsTArray* sCallbacks = nsnull; +static PRUint32 sFocusSuppressCount = 0; + +NS_IMPL_ADDREF(nsFocusEventSuppressorService) +NS_IMPL_RELEASE(nsFocusEventSuppressorService) + +NS_INTERFACE_MAP_BEGIN(nsFocusEventSuppressorService) + NS_INTERFACE_MAP_ENTRY(nsIFocusEventSuppressorService) + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +void +NS_AddFocusSuppressorCallback(nsFocusEventSuppressorCallback aCallback) +{ + if (aCallback) { + if (!sCallbacks) { + sCallbacks = new nsTArray(2); + if (!sCallbacks) { + NS_WARNING("Out of memory!"); + return; + } + } else if (sCallbacks->Contains(aCallback)) { + return; + } + sCallbacks->AppendElement(aCallback); + } +} + +void +NS_SuppressFocusEvent() +{ + ++sFocusSuppressCount; + if (sFocusSuppressCount == 1 && sCallbacks) { + for (PRUint32 i = 0; i < sCallbacks->Length(); ++i) { + sCallbacks->ElementAt(i)(PR_TRUE); + } + } +} + +void +NS_UnsuppressFocusEvent() +{ + --sFocusSuppressCount; + if (sFocusSuppressCount == 0 && sCallbacks) { + for (PRUint32 i = 0; i < sCallbacks->Length(); ++i) { + sCallbacks->ElementAt(i)(PR_FALSE); + } + } +} + +void +NS_ShutdownFocusSuppressor() +{ + delete sCallbacks; + sCallbacks = nsnull; +} + +nsresult +NS_NewFocusEventSuppressorService(nsIFocusEventSuppressorService** aResult) +{ + nsIFocusEventSuppressorService* it = new nsFocusEventSuppressorService(); + NS_ENSURE_TRUE(it, NS_ERROR_OUT_OF_MEMORY); + NS_ADDREF(*aResult = it); + return NS_OK; +} diff --git a/mozilla/layout/base/nsIFocusEventSuppressor.h b/mozilla/layout/base/nsIFocusEventSuppressor.h new file mode 100644 index 00000000000..079fa6f7afc --- /dev/null +++ b/mozilla/layout/base/nsIFocusEventSuppressor.h @@ -0,0 +1,74 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Olli Pettay (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef nsIFocusEventSuppressor_h___ +#define nsIFocusEventSuppressor_h___ +#include "nsISupports.h" + +typedef void (*PR_CALLBACK nsFocusEventSuppressorCallback)(PRBool aSuppress); + +#define NS_NSIFOCUSEVENTSUPPRESSORSERVICE_IID \ + { 0x8aae5cee, 0x59ab, 0x42d4, \ + { 0xa3, 0x76, 0xbf, 0x63, 0x54, 0x04, 0xc7, 0x98 } } + +class nsIFocusEventSuppressorService : public nsISupports +{ +public: + NS_DECLARE_STATIC_IID_ACCESSOR(NS_NSIFOCUSEVENTSUPPRESSORSERVICE_IID) + virtual void AddObserverCallback(nsFocusEventSuppressorCallback aCallback) = 0; + virtual void Suppress() = 0; + virtual void Unsuppress() = 0; +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsIFocusEventSuppressorService, + NS_NSIFOCUSEVENTSUPPRESSORSERVICE_IID) + +#if defined(_IMPL_NS_LAYOUT) || defined(NS_STATIC_FOCUS_SUPPRESSOR) +void NS_SuppressFocusEvent(); +void NS_UnsuppressFocusEvent(); +void NS_AddFocusSuppressorCallback(nsFocusEventSuppressorCallback aCallback); +void NS_ShutdownFocusSuppressor(); +#endif + +#define NS_NSIFOCUSEVENTSUPPRESSORSERVICE_CID \ + { 0x35b2656c, 0x4102, 0x4bc1, \ + { 0x87, 0x6a, 0xfd, 0x6c, 0xb8, 0x30, 0x78, 0x7b } } + +#define NS_NSIFOCUSEVENTSUPPRESSORSERVICE_CONTRACTID \ + "@mozilla.org/focus-event-suppressor-service;1" + +#endif diff --git a/mozilla/layout/build/nsLayoutModule.cpp b/mozilla/layout/build/nsLayoutModule.cpp index 71efb23de1c..77d383428fd 100644 --- a/mozilla/layout/build/nsLayoutModule.cpp +++ b/mozilla/layout/build/nsLayoutModule.cpp @@ -127,6 +127,8 @@ #include "nsDOMStorage.h" #include "nsJSON.h" +#include "nsIFocusEventSuppressor.h" + // Editor stuff #include "nsEditorCID.h" #include "nsEditor.h" @@ -416,6 +418,7 @@ nsresult NS_NewTextEncoder(nsIDocumentEncoder** aResult); nsresult NS_NewXBLService(nsIXBLService** aResult); nsresult NS_NewContentPolicy(nsIContentPolicy** aResult); nsresult NS_NewDOMEventGroup(nsIDOMEventGroup** aResult); +nsresult NS_NewFocusEventSuppressorService(nsIFocusEventSuppressorService** aResult); NS_IMETHODIMP NS_NewXULControllers(nsISupports* aOuter, REFNSIID aIID, void** aResult); @@ -503,6 +506,7 @@ MAKE_CTOR(CreateSanitizingHTMLSerializer, nsIContentSerializer, NS_NewSan MAKE_CTOR(CreateXBLService, nsIXBLService, NS_NewXBLService) MAKE_CTOR(CreateContentPolicy, nsIContentPolicy, NS_NewContentPolicy) MAKE_CTOR(CreateComputedDOMStyle, nsIComputedDOMStyle, NS_NewComputedDOMStyle) +MAKE_CTOR(CreateFocusEventSuppressorService,nsIFocusEventSuppressorService,NS_NewFocusEventSuppressorService) #ifdef MOZ_XUL MAKE_CTOR(CreateXULSortService, nsIXULSortService, NS_NewXULSortService) // NS_NewXULContentBuilder @@ -1262,6 +1266,11 @@ static const nsModuleComponentInfo gComponents[] = { { "View Manager", NS_VIEW_MANAGER_CID, "@mozilla.org/view-manager;1", nsViewManagerConstructor }, + { "Focus Event Suppressor", + NS_NSIFOCUSEVENTSUPPRESSORSERVICE_CID, + NS_NSIFOCUSEVENTSUPPRESSORSERVICE_CONTRACTID, + CreateFocusEventSuppressorService }, + { "Plugin Document Loader Factory", NS_PLUGINDOCLOADERFACTORY_CID, "@mozilla.org/content/plugin/document-loader-factory;1", diff --git a/mozilla/layout/build/nsLayoutStatics.cpp b/mozilla/layout/build/nsLayoutStatics.cpp index 9e0620f9e5e..9a48f2b54c6 100644 --- a/mozilla/layout/build/nsLayoutStatics.cpp +++ b/mozilla/layout/build/nsLayoutStatics.cpp @@ -82,6 +82,7 @@ #include "nsTextFragment.h" #include "nsCSSRuleProcessor.h" #include "nsXMLHttpRequest.h" +#include "nsIFocusEventSuppressor.h" #ifdef MOZ_XUL #include "nsXULPopupManager.h" @@ -310,6 +311,7 @@ nsLayoutStatics::Shutdown() #endif nsXMLHttpRequest::ShutdownACCache(); + NS_ShutdownFocusSuppressor(); } void diff --git a/mozilla/view/public/nsIViewManager.h b/mozilla/view/public/nsIViewManager.h index 16eae946f60..20f13f91ea0 100644 --- a/mozilla/view/public/nsIViewManager.h +++ b/mozilla/view/public/nsIViewManager.h @@ -60,10 +60,10 @@ enum nsRectVisibility { nsRectVisibility_kZeroAreaRect }; -// 5a1e80e1-b51c-4f43-8950-1064bd1f39ee +// 855e75b8-32cf-4e16-bc50-4e04c53f6cbc #define NS_IVIEWMANAGER_IID \ -{ 0x5a1e80e1, 0xb51c, 0x4f43, \ - { 0x89, 0x50, 0x10, 0x64, 0xbd, 0x1f, 0x39, 0xee } } +{ 0x855e75b8, 0x32cf, 0x4e16, \ + { 0xbc, 0x50, 0x4e, 0x04, 0xc5, 0x3f, 0x6c, 0xbc } } class nsIViewManager : public nsISupports { @@ -481,30 +481,6 @@ 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; }; diff --git a/mozilla/view/src/nsViewManager.cpp b/mozilla/view/src/nsViewManager.cpp index 8ca479517d2..1660671ae2b 100644 --- a/mozilla/view/src/nsViewManager.cpp +++ b/mozilla/view/src/nsViewManager.cpp @@ -62,8 +62,9 @@ #include "nsCOMArray.h" #include "nsThreadUtils.h" #include "nsContentUtils.h" - #include "gfxContext.h" +#define NS_STATIC_FOCUS_SUPPRESSOR +#include "nsIFocusEventSuppressor.h" static NS_DEFINE_IID(kBlenderCID, NS_BLENDER_CID); static NS_DEFINE_IID(kRegionCID, NS_REGION_CID); @@ -93,6 +94,10 @@ static NS_DEFINE_IID(kRenderingContextCID, NS_RENDERING_CONTEXT_CID); #include "nsITimeRecorder.h" #endif +#ifdef DEBUG_smaug +#define DEBUG_FOCUS_SUPPRESSION +#endif + //-------------- Begin Invalidate Event Definition ------------------------ class nsInvalidateEvent : public nsViewManagerEvent { @@ -169,7 +174,9 @@ nsViewManager::nsViewManager() gViewManagers->AppendElement(this); - mVMCount++; + if (++mVMCount == 1) { + NS_AddFocusSuppressorCallback(&nsViewManager::SuppressFocusEvents); + } // NOTE: we use a zeroing operator new, so all data members are // assumed to be cleared here. mDefaultBackgroundColor = NS_RGBA(0, 0, 0, 0); @@ -942,24 +949,20 @@ void nsViewManager::UpdateViews(nsView *aView, PRUint32 aUpdateFlags) nsView *nsViewManager::sCurrentlyFocusView = nsnull; nsView *nsViewManager::sViewFocusedBeforeSuppression = nsnull; -PRInt32 nsViewManager::sSuppressCount = 0; +PRBool nsViewManager::sFocusSuppressed = PR_FALSE; -void nsViewManager::SuppressFocusEvents() +void nsViewManager::SuppressFocusEvents(PRBool aSuppress) { - sSuppressCount++; - if (sSuppressCount == 1) { - // We're turning on focus/blur suppression, remember what had - // the focus. + if (aSuppress) { + sFocusSuppressed = PR_TRUE; SetViewFocusedBeforeSuppression(GetCurrentlyFocusedView()); - } -} + return; + } -void nsViewManager::UnsuppressFocusEvents() -{ - sSuppressCount--; - if (sSuppressCount > 0 || - GetCurrentlyFocusedView() == GetViewFocusedBeforeSuppression()) - return; + sFocusSuppressed = PR_FALSE; + if (GetCurrentlyFocusedView() == GetViewFocusedBeforeSuppression()) { + return; + } // We're turning off suppression, synthesize LOSTFOCUS/GOTFOCUS. nsIWidget *widget = nsnull; diff --git a/mozilla/view/src/nsViewManager.h b/mozilla/view/src/nsViewManager.h index eae248ef08d..ec28ed3cce4 100644 --- a/mozilla/view/src/nsViewManager.h +++ b/mozilla/view/src/nsViewManager.h @@ -201,12 +201,23 @@ public: /* Update the cached RootViewManager pointer on this view manager. */ void InvalidateHierarchy(); - virtual void SuppressFocusEvents(); - virtual void UnsuppressFocusEvents(); + /** + * Enables/disables focus/blur event suppression. + * Enabling 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. + * + * Disabling "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. + * See Bug 399852. + */ + static void SuppressFocusEvents(PRBool aSuppress); - virtual PRBool IsFocusSuppressed() + PRBool IsFocusSuppressed() { - return sSuppressCount > 0; + return sFocusSuppressed; } static void SetCurrentlyFocusedView(nsView *aView) @@ -236,7 +247,7 @@ private: static nsView *sCurrentlyFocusView; static nsView *sViewFocusedBeforeSuppression; - static PRInt32 sSuppressCount; + static PRBool sFocusSuppressed; void FlushPendingInvalidates(); void ProcessPendingUpdates(nsView *aView, PRBool aDoInvalidate); diff --git a/mozilla/xpfe/appshell/src/nsWebShellWindow.cpp b/mozilla/xpfe/appshell/src/nsWebShellWindow.cpp index c937f0eeb01..f83abc82259 100644 --- a/mozilla/xpfe/appshell/src/nsWebShellWindow.cpp +++ b/mozilla/xpfe/appshell/src/nsWebShellWindow.cpp @@ -107,12 +107,18 @@ #include "nsIDocShellTreeNode.h" #include "nsIMarkupDocumentViewer.h" +#include "nsIFocusEventSuppressor.h" #if defined(XP_MACOSX) #include "nsIMenuBar.h" #define USE_NATIVE_MENUS #endif +static nsWebShellWindow* gCurrentlyFocusedWindow = nsnull; +static nsWebShellWindow* gFocusedWindowBeforeSuppression = nsnull; +static PRBool gFocusSuppressed = PR_FALSE; +static PRUint32 gWebShellWindowCount = 0; + /* Define Class IDs */ static NS_DEFINE_CID(kWindowCID, NS_WINDOW_CID); @@ -124,11 +130,25 @@ static NS_DEFINE_CID(kMenuBarCID, NS_MENUBAR_CID); nsWebShellWindow::nsWebShellWindow() : nsXULWindow() { mSPTimerLock = PR_NewLock(); + if (++gWebShellWindowCount == 1) { + nsCOMPtr suppressor = + do_GetService(NS_NSIFOCUSEVENTSUPPRESSORSERVICE_CONTRACTID); + if (suppressor) { + suppressor->AddObserverCallback(&nsWebShellWindow::SuppressFocusEvents); + } + } } nsWebShellWindow::~nsWebShellWindow() { + --gWebShellWindowCount; + if (gCurrentlyFocusedWindow == this) { + gCurrentlyFocusedWindow = nsnull; + } + if (gFocusedWindowBeforeSuppression == this) { + gFocusedWindowBeforeSuppression = nsnull; + } if (mWindow) mWindow->SetClientData(0); mWindow = nsnull; // Force release here. @@ -422,7 +442,7 @@ nsWebShellWindow::HandleEvent(nsGUIEvent *aEvent) } case NS_ACTIVATE: { -#ifdef DEBUG_saari +#if defined(DEBUG_saari) || defined(DEBUG_smaug) printf("nsWebShellWindow::NS_ACTIVATE\n"); #endif nsCOMPtr privateDOMWindow = do_GetInterface(docShell); @@ -433,7 +453,7 @@ nsWebShellWindow::HandleEvent(nsGUIEvent *aEvent) } case NS_DEACTIVATE: { -#ifdef DEBUG_saari +#if defined(DEBUG_saari) || defined(DEBUG_smaug) printf("nsWebShellWindow::NS_DEACTIVATE\n"); #endif @@ -450,9 +470,13 @@ nsWebShellWindow::HandleEvent(nsGUIEvent *aEvent) } case NS_GOTFOCUS: { -#ifdef DEBUG_saari +#if defined(DEBUG_saari) || defined(DEBUG_smaug) printf("nsWebShellWindow::GOTFOCUS\n"); #endif + gCurrentlyFocusedWindow = eventWindow; + if (gFocusSuppressed) { + break; + } nsCOMPtr domDocument; nsCOMPtr piWin = do_GetInterface(docShell); if (!piWin) { @@ -502,6 +526,15 @@ nsWebShellWindow::HandleEvent(nsGUIEvent *aEvent) } break; } + case NS_LOSTFOCUS: { +#if defined(DEBUG_saari) || defined(DEBUG_smaug) + printf("nsWebShellWindow::LOSTFOCUS\n"); +#endif + if (gCurrentlyFocusedWindow == eventWindow) { + gCurrentlyFocusedWindow = nsnull; + } + break; + } default: break; @@ -840,3 +873,43 @@ NS_IMETHODIMP nsWebShellWindow::Destroy() return nsXULWindow::Destroy(); } +void +nsWebShellWindow::SuppressFocusEvents(PRBool aSuppress) +{ + if (aSuppress) { + gFocusSuppressed = PR_TRUE; + gFocusedWindowBeforeSuppression = gCurrentlyFocusedWindow; + return; + } + + gFocusSuppressed = PR_FALSE; + if (gFocusedWindowBeforeSuppression == gCurrentlyFocusedWindow) { + return; + } + + // 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 window". + nsWebShellWindow* currentFocusBeforeBlur = gCurrentlyFocusedWindow; + + if (gFocusedWindowBeforeSuppression) { + nsCOMPtr widget = gFocusedWindowBeforeSuppression->mWindow; + if (widget) { + nsRefPtr window = gFocusedWindowBeforeSuppression; + nsGUIEvent lostfocus(PR_TRUE, NS_LOSTFOCUS, widget); + window->HandleEvent(&lostfocus); + } + } + + // Send NS_GOTFOCUS to the widget that we think should be focused. + if (gCurrentlyFocusedWindow && + gCurrentlyFocusedWindow == currentFocusBeforeBlur) { + nsCOMPtr widget = gCurrentlyFocusedWindow->mWindow; + if (widget) { + nsRefPtr window = gCurrentlyFocusedWindow; + nsGUIEvent gotfocus(PR_TRUE, NS_GOTFOCUS, widget); + window->HandleEvent(&gotfocus); + } + } +} + diff --git a/mozilla/xpfe/appshell/src/nsWebShellWindow.h b/mozilla/xpfe/appshell/src/nsWebShellWindow.h index 483fe67f21c..73c9e344707 100644 --- a/mozilla/xpfe/appshell/src/nsWebShellWindow.h +++ b/mozilla/xpfe/appshell/src/nsWebShellWindow.h @@ -76,6 +76,7 @@ public: // nsIBaseWindow NS_IMETHOD Destroy(); + static void SuppressFocusEvents(PRBool aSuppress); protected: virtual ~nsWebShellWindow();