Bug 421209, r=cpearce, sr=jst, a=beltzner
authorOlli.Pettay@helsinki.fi
Thu, 20 Mar 2008 13:24:36 -0700
changeset 13389 452972ff9bf46abe86dd14034fbed7f9f711b623
parent 13388 a67e2065c4bd07cad17f8930a96a2b28975c4762
child 13390 ce51c6330d14eed2f811a359f016960f8f5f89a7
push id2
push userjorendorff@mozilla.com
push dateWed, 26 Mar 2008 17:39:06 +0000
treeherdermozilla-central@1d969540aa4e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce, jst, beltzner
bugs421209
milestone1.9b5pre
Bug 421209, r=cpearce, sr=jst, a=beltzner
content/events/src/nsEventStateManager.cpp
docshell/base/nsDocShell.cpp
layout/base/Makefile.in
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsCSSFrameConstructor.h
layout/base/nsFocusEventSuppressor.cpp
layout/base/nsIFocusEventSuppressor.h
layout/build/nsLayoutModule.cpp
layout/build/nsLayoutStatics.cpp
view/public/nsIViewManager.h
view/src/nsViewManager.cpp
view/src/nsViewManager.h
xpfe/appshell/src/nsWebShellWindow.cpp
xpfe/appshell/src/nsWebShellWindow.h
--- a/content/events/src/nsEventStateManager.cpp
+++ b/content/events/src/nsEventStateManager.cpp
@@ -863,16 +863,19 @@ nsEventStateManager::PreHandleEvent(nsPr
     KillClickHoldTimer();
     break;
 #endif
   case NS_DRAGDROP_OVER:
     GenerateDragDropEnterExit(aPresContext, (nsGUIEvent*)aEvent);
     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
       // event to the target content, its document, and its window.
 
       EnsureDocument(aPresContext);
 
       // If the document didn't change, then the only thing that could have
@@ -1026,16 +1029,19 @@ nsEventStateManager::PreHandleEvent(nsPr
 
       ResetBrowseWithCaret();
     }
 
     break;
 
   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();
         if (presShell) {
            nsCOMPtr<nsICaret> caret;
            presShell->GetCaret(getter_AddRefs(caret));
            if (caret) {
              PRBool caretVisible = PR_FALSE;
@@ -1047,17 +1053,20 @@ nsEventStateManager::PreHandleEvent(nsPr
         }
       }
 
       // If focus is going to another mozilla window, we wait for the
       // focus event and fire a blur on the old focused content at that time.
       // This allows "-moz-user-focus: ignore" to work.
 
 #if defined(XP_WIN) || defined(XP_OS2)
-      if (!static_cast<nsFocusEvent*>(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<nsFocusEvent*>(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
         // immediately.
 
         EnsureDocument(aPresContext);
 
         if (gLastFocusedContent && !gLastFocusedContent->IsInDoc()) {
@@ -1119,16 +1128,19 @@ nsEventStateManager::PreHandleEvent(nsPr
         }
       }
 #endif
     }
     break;
 
  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.
 
       EnsureDocument(aPresContext);
 
       nsCOMPtr<nsPIDOMWindow> win = mDocument->GetWindow();
 
@@ -1206,16 +1218,19 @@ nsEventStateManager::PreHandleEvent(nsPr
           focusController->GetSuppressFocus(&isSuppressed);
         }
       }
     }
     break;
 
  case NS_DEACTIVATE:
     {
+#ifdef DEBUG_smaug
+      printf("nsEventStateManager::PreHandleEvent, NS_DEACTIVATE \n");
+#endif
       EnsureDocument(aPresContext);
 
       nsIMEStateManager::OnDeactivate(aPresContext);
 
       nsCOMPtr<nsPIDOMWindow> ourWindow(mDocument->GetWindow());
 
       // Suppress the focus controller for the duration of the
       // de-activation.  This will cause it to remember the last
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -196,16 +196,17 @@ static NS_DEFINE_CID(kDOMScriptObjectFac
 static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
 
 #if defined(DEBUG_bryner) || defined(DEBUG_chb)
 //#define DEBUG_DOCSHELL_FOCUS
 #define DEBUG_PAGE_CACHE
 #endif
 
 #include "nsContentErrors.h"
+#include "nsIFocusEventSuppressor.h"
 
 // Number of documents currently loading
 static PRInt32 gNumberOfDocumentsLoading = 0;
 
 // Global count of existing docshells.
 static PRInt32 gDocShellCount = 0;
 
 // Global reference to the URI fixup service.
@@ -3670,17 +3671,22 @@ nsDocShell::Destroy()
     PersistLayoutHistoryState();
 
     // Remove this docshell from its parent's child list
     nsCOMPtr<nsIDocShellTreeItem> docShellParentAsItem =
         do_QueryInterface(GetAsSupports(mParent));
     if (docShellParentAsItem)
         docShellParentAsItem->RemoveChild(this);
 
+    nsCOMPtr<nsIFocusEventSuppressorService> suppressor;
     if (mContentViewer) {
+        suppressor =
+          do_GetService(NS_NSIFOCUSEVENTSUPPRESSORSERVICE_CONTRACTID);
+        NS_ENSURE_STATE(suppressor);
+        suppressor->Suppress();
         mContentViewer->Close(nsnull);
         mContentViewer->Destroy();
         mContentViewer = nsnull;
     }
 
     nsDocLoader::Destroy();
     
     mParentWidget = nsnull;
@@ -3697,17 +3703,19 @@ nsDocShell::Destroy()
     SetTreeOwner(nsnull);
 
     // required to break ref cycle
     mSecurityUI = nsnull;
 
     // 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;
 }
 
 NS_IMETHODIMP
 nsDocShell::SetPosition(PRInt32 x, PRInt32 y)
 {
     mBounds.x = x;
     mBounds.y = y;
--- a/layout/base/Makefile.in
+++ b/layout/base/Makefile.in
@@ -104,44 +104,46 @@ EXPORTS		= \
 		nsIImageMap.h \
 		nsILayoutDebugger.h \
 		nsILayoutHistoryState.h \
 		nsIPercentHeightObserver.h  \
 		nsIPresShell.h \
 		nsIReflowCallback.h \
 		nsLayoutErrors.h \
 		nsLayoutUtils.h \
+		nsIFocusEventSuppressor.h \
 		nsPresContext.h \
 		nsPresState.h \
 		nsStyleChangeList.h \
 		nsStyleConsts.h \
 		$(NULL)
 
 CPPSRCS		= \
 		nsCSSColorUtils.cpp \
 		nsCSSFrameConstructor.cpp \
 		nsCSSRendering.cpp \
 		nsCaret.cpp \
 		nsChildIterator.cpp \
 		nsCounterManager.cpp \
-                nsDisplayList.cpp \
+		nsDisplayList.cpp \
 		nsDocumentViewer.cpp \
 		nsFrameManager.cpp \
 		nsFrameTraversal.cpp \
 		nsGenConList.cpp \
 		nsImageLoader.cpp \
 		nsLayoutDebugger.cpp \
 		nsLayoutHistoryState.cpp \
 		nsLayoutUtils.cpp \
 		nsPresContext.cpp \
 		nsPresShell.cpp \
 		nsPresState.cpp \
 		nsQuoteList.cpp \
 		nsStyleChangeList.cpp \
 		nsStyleSheetService.cpp \
+		nsFocusEventSuppressor.cpp \
 		$(NULL)
 
 ifndef MOZ_XUL
 XPIDLSRCS  += \
 		nsIBoxObject.idl \
 		$(NULL)
 EXPORTS    += \
 		nsPIBoxObject.h \
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -116,17 +116,17 @@
 #include "nsBoxFrame.h"
 #include "nsIBoxLayout.h"
 #include "nsImageFrame.h"
 #include "nsIObjectLoadingContent.h"
 #include "nsContentErrors.h"
 #include "nsIPrincipal.h"
 #include "nsIDOMWindowInternal.h"
 #include "nsStyleUtil.h"
-
+#include "nsIFocusEventSuppressor.h"
 #include "nsBox.h"
 
 #ifdef MOZ_XUL
 #include "nsIRootBox.h"
 #include "nsIDOMXULCommandDispatcher.h"
 #include "nsIDOMXULDocument.h"
 #include "nsIXULDocument.h"
 #endif
@@ -374,33 +374,16 @@ static PRInt32 FFWC_recursions=0;
 static PRInt32 FFWC_nextInFlows=0;
 static PRInt32 FFWC_slowSearchForText=0;
 #endif
 
 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 *
 SVG_GetFirstNonAAncestorFrame(nsIFrame *aParentFrame)
 {
   for (nsIFrame *ancestorFrame = aParentFrame; ancestorFrame != nsnull;
        ancestorFrame = ancestorFrame->GetParent()) {
     if (ancestorFrame->GetType() != nsGkAtoms::svgAFrame) {
@@ -10247,31 +10230,31 @@ nsCSSFrameConstructor::AttributeChanged(
 
   PostRestyleEvent(aContent, rshint, hint);
 
   return result;
 }
 
 void
 nsCSSFrameConstructor::BeginUpdate() {
-  mFocusSuppressor.Suppress(mPresShell);
+  NS_SuppressFocusEvent();
   ++mUpdateCount;
 }
 
 void
 nsCSSFrameConstructor::EndUpdate()
 {
   if (mUpdateCount == 1) {
     // This is the end of our last update.  Before we decrement
     // mUpdateCount, recalc quotes and counters as needed.
 
     RecalcQuotesAndCounters();
     NS_ASSERTION(mUpdateCount == 1, "Odd update count");
   }
-  mFocusSuppressor.Unsuppress();
+  NS_UnsuppressFocusEvent();
   --mUpdateCount;
 }
 
 void
 nsCSSFrameConstructor::RecalcQuotesAndCounters()
 {
   if (mQuotesDirty) {
     mQuotesDirty = PR_FALSE;
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -69,29 +69,16 @@ class nsStyleChangeList;
 class nsIFrame;
 
 struct nsFindFrameHint
 {
   nsIFrame *mPrimaryFrameForPrevSibling;  // weak ref to the primary frame for the content for which we need a frame
   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);
 
 class nsFrameConstructorState;
 class nsFrameConstructorSaveState;
   
 class nsCSSFrameConstructor
 {
@@ -172,18 +159,16 @@ public:
   // ProcessRestyledFrames call in a view update batch.
   // This function does not call ProcessAttachedQueue() on the binding manager.
   // If the caller wants that to happen synchronously, it needs to handle that
   // itself.
   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.
   // If the caller wants that to happen synchronously, it needs to handle that
   // itself.
   void ProcessOneRestyle(nsIContent* aContent, nsReStyleHint aRestyleHint,
                          nsChangeHint aChangeHint);
 
new file mode 100644
--- /dev/null
+++ b/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 <Olli.Pettay@helsinki.fi> (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<nsFocusEventSuppressorCallback>* 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<nsFocusEventSuppressorCallback>(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;
+}
new file mode 100644
--- /dev/null
+++ b/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 <Olli.Pettay@helsinki.fi> (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
--- a/layout/build/nsLayoutModule.cpp
+++ b/layout/build/nsLayoutModule.cpp
@@ -122,16 +122,18 @@
 #include "nsIControllerCommandTable.h"
 #include "nsJSProtocolHandler.h"
 #include "nsScriptNameSpaceManager.h"
 #include "nsIControllerContext.h"
 #include "nsDOMScriptObjectFactory.h"
 #include "nsDOMStorage.h"
 #include "nsJSON.h"
 
+#include "nsIFocusEventSuppressor.h"
+
 // Editor stuff
 #include "nsEditorCID.h"
 #include "nsEditor.h"
 #include "nsPlaintextEditor.h"
 #include "nsEditorController.h" //CID
 #include "nsIController.h"
 #include "nsIControllerContext.h"
 #include "nsIControllerCommandTable.h"
@@ -411,16 +413,17 @@ nsresult NS_NewGenRegularIterator(nsICon
 nsresult NS_NewContentSubtreeIterator(nsIContentIterator** aResult);
 nsresult NS_NewGenSubtreeIterator(nsIContentIterator** aInstancePtrResult);
 nsresult NS_NewContentDocumentLoaderFactory(nsIDocumentLoaderFactory** aResult);
 nsresult NS_NewHTMLCopyTextEncoder(nsIDocumentEncoder** aResult);
 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);
 
 #define MAKE_CTOR(ctor_, iface_, func_)                   \
 static NS_IMETHODIMP                                      \
 ctor_(nsISupports* aOuter, REFNSIID aIID, void** aResult) \
 {                                                         \
   *aResult = nsnull;                                      \
@@ -498,16 +501,17 @@ MAKE_CTOR(CreateHTMLFragmentSink2,      
 MAKE_CTOR(CreateHTMLParanoidFragmentSink, nsIFragmentContentSink,      NS_NewHTMLParanoidFragmentSink)
 MAKE_CTOR(CreateXMLFragmentSink,          nsIFragmentContentSink,      NS_NewXMLFragmentContentSink)
 MAKE_CTOR(CreateXMLFragmentSink2,         nsIFragmentContentSink,      NS_NewXMLFragmentContentSink2)
 MAKE_CTOR(CreateXHTMLParanoidFragmentSink,nsIFragmentContentSink,      NS_NewXHTMLParanoidFragmentSink)
 MAKE_CTOR(CreateSanitizingHTMLSerializer, nsIContentSerializer,        NS_NewSanitizingHTMLSerializer)
 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
 // NS_NewXULTreeBuilder
 MAKE_CTOR(CreateXULDocument,              nsIXULDocument,              NS_NewXULDocument)
 // NS_NewXULControllers
 // NS_NewXULPrototypeCache
 MAKE_CTOR(CreateXULPopupManager,      nsISupports,      NS_NewXULPopupManager)
@@ -1257,16 +1261,21 @@ static const nsModuleComponentInfo gComp
     NS_WINDOWCONTROLLER_CONTRACTID,
     CreateWindowControllerWithSingletonCommandTable
   },
 
   // view stuff
   { "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",
     CreateContentDLF },
 
   { "Plugin Document",
     NS_PLUGINDOCUMENT_CID,
     nsnull,
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -77,16 +77,17 @@
 #include "txMozillaXSLTProcessor.h"
 #include "nsDOMStorage.h"
 #include "nsCellMap.h"
 #include "nsTextFrameTextRunCache.h"
 #include "nsCCUncollectableMarker.h"
 #include "nsTextFragment.h"
 #include "nsCSSRuleProcessor.h"
 #include "nsXMLHttpRequest.h"
+#include "nsIFocusEventSuppressor.h"
 
 #ifdef MOZ_XUL
 #include "nsXULPopupManager.h"
 #include "nsXULContentUtils.h"
 #include "nsXULElement.h"
 #include "nsXULPrototypeCache.h"
 #include "nsXULTooltipListener.h"
 
@@ -305,16 +306,17 @@ nsLayoutStatics::Shutdown()
   nsAutoCopyListener::Shutdown();
 
 #ifndef MOZILLA_PLAINTEXT_EDITOR_ONLY
   nsHTMLEditor::Shutdown();
   nsTextServicesDocument::Shutdown();
 #endif
 
   nsXMLHttpRequest::ShutdownACCache();
+  NS_ShutdownFocusSuppressor();
 }
 
 void
 nsLayoutStatics::AddRef()
 {
   NS_ASSERTION(sLayoutStaticRefcnt,
                "nsLayoutStatics already dropped to zero!");
 
--- a/view/public/nsIViewManager.h
+++ b/view/public/nsIViewManager.h
@@ -55,20 +55,20 @@ enum nsRectVisibility {
   nsRectVisibility_kVisible, 
   nsRectVisibility_kAboveViewport, 
   nsRectVisibility_kBelowViewport, 
   nsRectVisibility_kLeftOfViewport, 
   nsRectVisibility_kRightOfViewport, 
   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
 {
 public:
 
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_IVIEWMANAGER_IID)
   /**
    * Initialize the ViewManager
@@ -476,40 +476,16 @@ public:
                                nsRectVisibility *aRectVisibility)=0;
 
   /**
    * Dispatch a mouse move event based on the most recent mouse
    * position.  This is used when the contents of the page moved
    * (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)
 
 // Paint timing mode flags
 
 // intermediate: do no special timing processing; repaint when the
--- a/view/src/nsViewManager.cpp
+++ b/view/src/nsViewManager.cpp
@@ -57,18 +57,19 @@
 #include "nsIPrefService.h"
 #include "nsRegion.h"
 #include "nsInt64.h"
 #include "nsScrollPortView.h"
 #include "nsHashtable.h"
 #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);
 static NS_DEFINE_IID(kRenderingContextCID, NS_RENDERING_CONTEXT_CID);
 
 /**
    XXX TODO XXX
 
@@ -88,16 +89,20 @@ static NS_DEFINE_IID(kRenderingContextCI
 */
 
 #define NSCOORD_NONE      PR_INT32_MIN
 
 #ifdef NS_VM_PERF_METRICS
 #include "nsITimeRecorder.h"
 #endif
 
+#ifdef DEBUG_smaug
+#define DEBUG_FOCUS_SUPPRESSION
+#endif
+
 //-------------- Begin Invalidate Event Definition ------------------------
 
 class nsInvalidateEvent : public nsViewManagerEvent {
 public:
   nsInvalidateEvent(nsViewManager *vm) : nsViewManagerEvent(vm) {}
 
   NS_IMETHOD Run() {
     if (mViewManager)
@@ -164,17 +169,19 @@ nsViewManager::nsViewManager()
     /* XXX: This should use a device to create a matching |nsIRenderingContext| object */
     CallCreateInstance(kRenderingContextCID, &gCleanupContext);
     NS_ASSERTION(gCleanupContext,
                  "Wasn't able to create a graphics context for cleanup");
   }
 
   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);
   mHasPendingUpdates = PR_FALSE;
   mRecursiveRefreshPending = PR_FALSE;
   mUpdateBatchFlags = 0;
 }
 
@@ -937,34 +944,30 @@ void nsViewManager::UpdateViews(nsView *
   while (nsnull != childView)  {
     UpdateViews(childView, aUpdateFlags);
     childView = childView->GetNextSibling();
   }
 }
 
 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;
   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".
--- a/view/src/nsViewManager.h
+++ b/view/src/nsViewManager.h
@@ -196,22 +196,33 @@ public:
                                nsRectVisibility *aRectVisibility);
 
   NS_IMETHOD SynthesizeMouseMove(PRBool aFromScroll);
   void ProcessSynthMouseMoveEvent(PRBool aFromScroll);
 
   /* 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)
   {
     sCurrentlyFocusView = aView;
   }
   
   static nsView* GetCurrentlyFocusedView()
@@ -231,17 +242,17 @@ public:
 
 protected:
   virtual ~nsViewManager();
 
 private:
 
   static nsView *sCurrentlyFocusView;
   static nsView *sViewFocusedBeforeSuppression;
-  static PRInt32 sSuppressCount;
+  static PRBool sFocusSuppressed;
 
   void FlushPendingInvalidates();
   void ProcessPendingUpdates(nsView *aView, PRBool aDoInvalidate);
   void ReparentChildWidgets(nsIView* aView, nsIWidget *aNewWidget);
   void ReparentWidgets(nsIView* aView, nsIView *aParent);
   already_AddRefed<nsIRenderingContext> CreateRenderingContext(nsView &aView);
   void UpdateWidgetArea(nsView *aWidgetView, const nsRegion &aDamagedRegion,
                         nsView* aIgnoreWidgetView);
--- a/xpfe/appshell/src/nsWebShellWindow.cpp
+++ b/xpfe/appshell/src/nsWebShellWindow.cpp
@@ -102,38 +102,58 @@
 #include "nsIPresShell.h"
 #include "nsPresContext.h"
 
 #include "nsIBaseWindow.h"
 #include "nsIDocShellTreeItem.h"
 #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);
 
 #include "nsWidgetsCID.h"
 static NS_DEFINE_CID(kMenuBarCID,          NS_MENUBAR_CID);
 
 #define SIZE_PERSISTENCE_TIMEOUT 500 // msec
 
 nsWebShellWindow::nsWebShellWindow() : nsXULWindow()
 {
   mSPTimerLock = PR_NewLock();
+  if (++gWebShellWindowCount == 1) {
+    nsCOMPtr<nsIFocusEventSuppressorService> 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.
 
   if (mSPTimerLock) {
     PR_Lock(mSPTimerLock);
     if (mSPTimer)
       mSPTimer->Cancel();
@@ -417,47 +437,51 @@ nsWebShellWindow::HandleEvent(nsGUIEvent
         break;
       }
 
       case NS_MOUSE_ACTIVATE:{
         break;
       }
       
       case NS_ACTIVATE: {
-#ifdef DEBUG_saari
+#if defined(DEBUG_saari) || defined(DEBUG_smaug)
         printf("nsWebShellWindow::NS_ACTIVATE\n");
 #endif
         nsCOMPtr<nsPIDOMWindow> privateDOMWindow = do_GetInterface(docShell);
         if (privateDOMWindow)
           privateDOMWindow->Activate();
 
         break;
       }
 
       case NS_DEACTIVATE: {
-#ifdef DEBUG_saari
+#if defined(DEBUG_saari) || defined(DEBUG_smaug)
         printf("nsWebShellWindow::NS_DEACTIVATE\n");
 #endif
 
         nsCOMPtr<nsPIDOMWindow> privateDOMWindow = do_GetInterface(docShell);
         if (privateDOMWindow) {
           nsIFocusController *focusController =
             privateDOMWindow->GetRootFocusController();
           if (focusController)
             focusController->SetActive(PR_FALSE);
 
           privateDOMWindow->Deactivate();
         }
         break;
       }
       
       case NS_GOTFOCUS: {
-#ifdef DEBUG_saari
+#if defined(DEBUG_saari) || defined(DEBUG_smaug)
         printf("nsWebShellWindow::GOTFOCUS\n");
 #endif
+        gCurrentlyFocusedWindow = eventWindow;
+        if (gFocusSuppressed) {
+          break;
+        }
         nsCOMPtr<nsIDOMDocument> domDocument;
         nsCOMPtr<nsPIDOMWindow> piWin = do_GetInterface(docShell);
         if (!piWin) {
           break;
         }
         nsIFocusController *focusController = piWin->GetRootFocusController();
         if (focusController) {
           // This is essentially the first stage of activation (NS_GOTFOCUS is
@@ -497,16 +521,25 @@ nsWebShellWindow::HandleEvent(nsGUIEvent
               eventWindow->SavePersistentAttributes();
             }
 
             break;
           }
         }
         break;
       }
+      case NS_LOSTFOCUS: {
+#if defined(DEBUG_saari) || defined(DEBUG_smaug)
+        printf("nsWebShellWindow::LOSTFOCUS\n");
+#endif
+        if (gCurrentlyFocusedWindow == eventWindow) {
+          gCurrentlyFocusedWindow = nsnull;
+        }
+        break;
+      }
       default:
         break;
 
     }
   }
   return result;
 }
 
@@ -835,8 +868,48 @@ NS_IMETHODIMP nsWebShellWindow::Destroy(
   }
   PR_Unlock(mSPTimerLock);
   PR_DestroyLock(mSPTimerLock);
   mSPTimerLock = nsnull;
   }
   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<nsIWidget> widget = gFocusedWindowBeforeSuppression->mWindow;
+    if (widget) {
+      nsRefPtr<nsWebShellWindow> 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<nsIWidget> widget = gCurrentlyFocusedWindow->mWindow;
+    if (widget) {
+      nsRefPtr<nsWebShellWindow> window = gCurrentlyFocusedWindow;
+      nsGUIEvent gotfocus(PR_TRUE, NS_GOTFOCUS, widget);
+      window->HandleEvent(&gotfocus);
+    }
+  }
+}
+
--- a/xpfe/appshell/src/nsWebShellWindow.h
+++ b/xpfe/appshell/src/nsWebShellWindow.h
@@ -71,16 +71,17 @@ public:
   nsresult Toolbar();
 
   // nsIWebProgressListener
   NS_DECL_NSIWEBPROGRESSLISTENER
 
   // nsIBaseWindow
   NS_IMETHOD Destroy();
 
+  static void SuppressFocusEvents(PRBool aSuppress);
 protected:
   
   virtual ~nsWebShellWindow();
 
   nsCOMPtr<nsIDOMDocument> GetNamedDOMDoc(const nsAString & aWebShellName);
 
   void                     LoadContentAreas();
   PRBool                   ExecuteCloseHandler();