Bug 333198 - 'Suppress Input events for web content during synchronous XMLHttpRequest loads'. r=bz, sr=jst, a=blocking1.9.1+
authorOlli Pettay <opettay@mozilla.com>
Thu, 26 Feb 2009 14:00:30 -0800
changeset 25570 7184f7d69ff0ab8ba79fbd4237cf41059ea9b5c5
parent 25569 cdfbe284c2071f62d17ae0ba7104ec281f9f709e
child 25571 fee69bd1bfa7b18a196645a3b57752b9f14bb48d
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz, jst, blocking1
bugs333198
milestone1.9.2a1pre
Bug 333198 - 'Suppress Input events for web content during synchronous XMLHttpRequest loads'. r=bz, sr=jst, a=blocking1.9.1+
content/base/public/nsIDocument.h
content/base/src/nsDocument.cpp
content/base/src/nsDocument.h
content/base/src/nsXMLHttpRequest.cpp
content/base/test/Makefile.in
content/base/test/test_bug333198.html
content/events/src/nsEventStateManager.cpp
docshell/base/nsDocShell.cpp
dom/public/idl/base/nsIDOMWindowUtils.idl
dom/src/base/nsDOMWindowUtils.cpp
dom/src/base/nsGlobalWindow.cpp
dom/src/base/nsGlobalWindow.h
layout/base/nsDocumentViewer.cpp
layout/base/nsIPresShell.h
layout/base/nsPresShell.cpp
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -95,18 +95,18 @@ class nsIDocumentObserver;
 class nsBindingManager;
 class nsIDOMNodeList;
 class mozAutoSubtreeModified;
 struct JSObject;
 class nsFrameLoader;
 
 // IID for the nsIDocument interface
 #define NS_IDOCUMENT_IID      \
-{ 0x29f7a5d7, 0xb217, 0x4ea2, \
-  {0x95, 0x40, 0x46, 0x41, 0xb9, 0xf5, 0x99, 0xd9 } }
+{ 0xdd9bd470, 0x6315, 0x4e67, \
+  { 0xa8, 0x8a, 0x78, 0xbf, 0x92, 0xb4, 0x5a, 0xdf } }
 
 // Flag for AddStyleSheet().
 #define NS_STYLESHEET_FROM_CATALOG                (1 << 0)
 
 //----------------------------------------------------------------------
 
 // Document interface.  This is implemented by all document objects in
 // Gecko.
@@ -1112,16 +1112,30 @@ public:
    */
   PRBool IsShowing() { return mIsShowing; }
 
 #ifdef MOZ_SMIL
   // Getter for this document's SMIL Animation Controller
   virtual nsSMILAnimationController* GetAnimationController() = 0;
 #endif // MOZ_SMIL
 
+  /**
+   * Prevents user initiated events from being dispatched to the document and
+   * subdocuments.
+   */
+  virtual void SuppressEventHandling(PRUint32 aIncrease = 1) = 0;
+
+  virtual void UnsuppressEventHandlingAndFireEvents(PRBool aFireEvents) = 0;
+
+  void UnsuppressEventHandling()
+  {
+    UnsuppressEventHandlingAndFireEvents(PR_TRUE);
+  }
+
+  PRUint32 EventHandlingSuppressed() { return mEventsSuppressed; }
 protected:
   ~nsIDocument()
   {
     // XXX The cleanup of mNodeInfoManager (calling DropDocumentReference and
     //     releasing it) happens in the nsDocument destructor. We'd prefer to
     //     do it here but nsNodeInfoManager is a concrete class that we don't
     //     want to expose to users of the nsIDocument API outside of Gecko.
   }
@@ -1216,16 +1230,18 @@ protected:
   nsCOMArray<nsINode> mSubtreeModifiedTargets;
   PRUint32            mSubtreeModifiedDepth;
 
   // If we're an external resource document, this will be non-null and will
   // point to our "display document": the one that all resource lookups should
   // go to.
   nsCOMPtr<nsIDocument> mDisplayDocument;
 
+  PRUint32 mEventsSuppressed;
+
 private:
   // JSObject cache. Only to be used for performance
   // optimizations. This will be set once this document is touched
   // from JS, and it will be unset once the JSObject is finalized.
   JSObject *mJSObject;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocument, NS_IDOCUMENT_IID)
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -6883,16 +6883,20 @@ CanCacheSubDocument(PLDHashTable *table,
 
 #ifdef DEBUG_bryner
 #define DEBUG_PAGE_CACHE
 #endif
 
 PRBool
 nsDocument::CanSavePresentation(nsIRequest *aNewRequest)
 {
+  if (EventHandlingSuppressed()) {
+    return PR_FALSE;
+  }
+
   // Check our event listener manager for unload/beforeunload listeners.
   nsCOMPtr<nsPIDOMEventTarget> piTarget = do_QueryInterface(mScriptGlobalObject);
   if (piTarget) {
     nsCOMPtr<nsIEventListenerManager> manager;
     piTarget->GetListenerManager(PR_FALSE, getter_AddRefs(manager));
     if (manager && manager->HasUnloadListeners()) {
       return PR_FALSE;
     }
@@ -7491,8 +7495,56 @@ nsDocument::GetReadyState(nsAString& aRe
   case READYSTATE_COMPLETE :
     aReadyState.Assign(NS_LITERAL_STRING("complete"));
     break;  
   default:
     aReadyState.Assign(NS_LITERAL_STRING("uninitialized"));
   }
   return NS_OK;
 }
+
+static PRBool
+SuppressEventHandlingInDocument(nsIDocument* aDocument, void* aData)
+{
+  aDocument->SuppressEventHandling(*static_cast<PRUint32*>(aData));
+  return PR_TRUE;
+}
+
+void
+nsDocument::SuppressEventHandling(PRUint32 aIncrease)
+{
+  mEventsSuppressed += aIncrease;
+  EnumerateSubDocuments(SuppressEventHandlingInDocument, &aIncrease);
+}
+
+static PRBool
+GetAndUnsuppressSubDocuments(nsIDocument* aDocument, void* aData)
+{
+  PRUint32 suppression = aDocument->EventHandlingSuppressed();
+  if (suppression > 0) {
+    static_cast<nsDocument*>(aDocument)->DecreaseEventSuppression();
+  }
+  nsCOMArray<nsIDocument>* docs = static_cast<nsCOMArray<nsIDocument>* >(aData);
+  docs->AppendObject(aDocument);
+  aDocument->EnumerateSubDocuments(GetAndUnsuppressSubDocuments, docs);
+  return PR_TRUE;
+}
+
+void
+nsDocument::UnsuppressEventHandlingAndFireEvents(PRBool aFireEvents)
+{
+  if (mEventsSuppressed > 0) {
+    --mEventsSuppressed;
+  }
+  nsCOMArray<nsIDocument> documents;
+  documents.AppendObject(this);
+  EnumerateSubDocuments(GetAndUnsuppressSubDocuments, &documents);
+  for (PRInt32 i = 0; i < documents.Count(); ++i) {
+    if (!documents[i]->EventHandlingSuppressed()) {
+      nsPresShellIterator iter(documents[i]);
+      nsCOMPtr<nsIPresShell> shell;
+      while ((shell = iter.GetNextShell())) {
+        shell->FireOrClearDelayedEvents(aFireEvents);
+      }
+    }
+  }
+}
+
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -981,16 +981,22 @@ public:
                             ExternalResourceLoad** aPendingLoad);
   virtual NS_HIDDEN_(void)
     EnumerateExternalResources(nsSubDocEnumFunc aCallback, void* aData);
 
 #ifdef MOZ_SMIL
   nsSMILAnimationController* GetAnimationController();
 #endif // MOZ_SMIL
 
+  virtual void SuppressEventHandling(PRUint32 aIncrease);
+
+  virtual void UnsuppressEventHandlingAndFireEvents(PRBool aFireEvents);
+  
+  void DecreaseEventSuppression() { --mEventsSuppressed; }
+
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsDocument, nsIDocument)
 
   /**
    * Utility method for getElementsByClassName.  aRootNode is the node (either
    * document or element), which getElementsByClassName was called on.
    */
   static nsresult GetElementsByClassNameHelper(nsINode* aRootNode,
                                                const nsAString& aClasses,
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -2155,17 +2155,18 @@ nsXMLHttpRequest::OnStartRequest(nsIRequ
   mState |= XML_HTTP_REQUEST_PARSEBODY;
   mState &= ~XML_HTTP_REQUEST_MPART_HEADERS;
   ChangeState(XML_HTTP_REQUEST_LOADED);
 
   nsresult status;
   request->GetStatus(&status);
   mErrorLoad = mErrorLoad || NS_FAILED(status);
 
-  if (mUpload && !mUploadComplete && !mErrorLoad) {
+  if (mUpload && !mUploadComplete && !mErrorLoad &&
+      (mState & XML_HTTP_REQUEST_ASYNC)) {
     mUploadComplete = PR_TRUE;
     DispatchProgressEvent(mUpload, NS_LITERAL_STRING(LOAD_STR),
                           PR_TRUE, mUploadTotal, mUploadTotal);
   }
 
   // Reset responseBody
   mResponseBody.Truncate();
 
@@ -2781,38 +2782,49 @@ nsXMLHttpRequest::Send(nsIVariant *aBody
   // can run script that would try to restart this request, and that could end
   // up doing our AsyncOpen on a null channel if the reentered AsyncOpen fails.
   ChangeState(XML_HTTP_REQUEST_SENT);
 
   // If we're synchronous, spin an event loop here and wait
   if (!(mState & XML_HTTP_REQUEST_ASYNC)) {
     mState |= XML_HTTP_REQUEST_SYNCLOOPING;
 
+    nsCOMPtr<nsIDocument> suspendedDoc;
     nsCOMPtr<nsIRunnable> resumeTimeoutRunnable;
     if (mOwner) {
       nsCOMPtr<nsIDOMWindow> topWindow;
       if (NS_SUCCEEDED(mOwner->GetTop(getter_AddRefs(topWindow)))) {
         nsCOMPtr<nsPIDOMWindow> suspendedWindow(do_QueryInterface(topWindow));
         if (suspendedWindow) {
+           suspendedDoc = do_QueryInterface(suspendedWindow->GetExtantDocument());
+           if (suspendedDoc) {
+             suspendedDoc->SuppressEventHandling();
+           }
           suspendedWindow->SuspendTimeouts();
           resumeTimeoutRunnable = NS_NEW_RUNNABLE_METHOD(nsPIDOMWindow,
                                                          suspendedWindow.get(),
                                                          ResumeTimeouts);
         }
       }
     }
 
     nsIThread *thread = NS_GetCurrentThread();
     while (mState & XML_HTTP_REQUEST_SYNCLOOPING) {
       if (!NS_ProcessNextEvent(thread)) {
         rv = NS_ERROR_UNEXPECTED;
         break;
       }
     }
 
+    if (suspendedDoc) {
+      NS_DispatchToCurrentThread(
+        NS_NEW_RUNNABLE_METHOD(nsIDocument, suspendedDoc.get(),
+                               UnsuppressEventHandling));
+    }
+
     if (resumeTimeoutRunnable) {
       NS_DispatchToCurrentThread(resumeTimeoutRunnable);
     }
   } else {
     if (!mUploadComplete &&
         HasListenersFor(NS_LITERAL_STRING(UPLOADPROGRESS_STR)) ||
         (mUpload && mUpload->HasListenersFor(NS_LITERAL_STRING(PROGRESS_STR)))) {
       StartProgressEventTimer();
@@ -3217,17 +3229,17 @@ nsXMLHttpRequest::OnProgress(nsIRequest 
   }
 
   if (mTimerIsActive) {
     // The progress event will be dispatched when the notifier calls Notify().
     mProgressEventWasDelayed = PR_TRUE;
     return NS_OK;
   }
 
-  if (!mErrorLoad) {
+  if (!mErrorLoad && (mState & XML_HTTP_REQUEST_ASYNC)) {
     StartProgressEventTimer();
     NS_NAMED_LITERAL_STRING(progress, PROGRESS_STR);
     NS_NAMED_LITERAL_STRING(uploadprogress, UPLOADPROGRESS_STR);
     DispatchProgressEvent(this, upload ? uploadprogress : progress, PR_TRUE,
                           lengthComputable, loaded, lengthComputable ? total : 0,
                           aProgress, aProgressMax);
 
     if (upload && mUpload && !mUploadComplete) {
@@ -3343,17 +3355,18 @@ nsXMLHttpRequest::GetUpload(nsIXMLHttpRe
   NS_ADDREF(*aUpload = mUpload);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsXMLHttpRequest::Notify(nsITimer* aTimer)
 {
   mTimerIsActive = PR_FALSE;
-  if (NS_SUCCEEDED(CheckInnerWindowCorrectness()) && !mErrorLoad) {
+  if (NS_SUCCEEDED(CheckInnerWindowCorrectness()) && !mErrorLoad &&
+      (mState & XML_HTTP_REQUEST_ASYNC)) {
     if (mProgressEventWasDelayed) {
       mProgressEventWasDelayed = PR_FALSE;
       if (!(XML_HTTP_REQUEST_MPART_HEADERS & mState)) {
         StartProgressEventTimer();
         // We're uploading if our state is XML_HTTP_REQUEST_OPENED or
         // XML_HTTP_REQUEST_SENT
         if ((XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT) & mState) {
           DispatchProgressEvent(this, NS_LITERAL_STRING(UPLOADPROGRESS_STR),
--- a/content/base/test/Makefile.in
+++ b/content/base/test/Makefile.in
@@ -141,16 +141,17 @@ include $(topsrcdir)/config/rules.mk
 		formReset.html \
 		bug382113_object.html \
 		test_bug326337.html \
 		file_bug326337_inner.html \
 		file_bug326337_outer.html \
 		file_bug326337.xml \
 		file_bug326337_multipart.txt \
 		file_bug326337_multipart.txt^headers^ \
+		test_bug333198.html \
 		test_bug402150.html \
 		test_bug402150.html^headers^ \
 		test_bug401662.html \
 		test_bug403852.html \
 		test_bug403868.xml \
 		test_bug405182.html \
 		test_bug403841.html \
 		test_bug409380.html \
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_bug333198.html
@@ -0,0 +1,73 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=333198
+-->
+<head>
+  <title>Test for Bug 333198</title>
+  <script type="application/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<iframe id="ifr"></iframe><br>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=333198">Mozilla Bug 333198</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 333198 **/
+
+var eventCount = 0;
+function clickHandler() {
+  ++eventCount;
+}
+
+function suppressEvents(suppress) {
+  netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+  window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+        .getInterface(Components.interfaces.nsIDOMWindowUtils)
+        .suppressEventHandling(suppress);
+}
+
+function sendEvents() {
+  netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+  windowUtils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+                      .getInterface(Components.interfaces.nsIDOMWindowUtils);
+  windowUtils.sendMouseEvent("mousedown", 1, 1, 0, 1, 0);
+  windowUtils.sendMouseEvent("mouseup", 1, 1, 0, 1, 0);
+
+  iframeUtils = document.getElementById("ifr").contentWindow
+                        .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+                        .getInterface(Components.interfaces.nsIDOMWindowUtils);
+  iframeUtils.sendMouseEvent("mousedown", 1, 1, 0, 1, 0);
+  iframeUtils.sendMouseEvent("mouseup", 1, 1, 0, 1, 0);
+}
+
+function runTest() {
+  window.addEventListener("click", clickHandler, true);
+  var ifr = document.getElementById("ifr")
+  ifr.contentWindow.addEventListener("click", clickHandler, true);
+  sendEvents();
+  is(eventCount, 2, "Wrong event count(1)");
+  suppressEvents(true);
+  sendEvents();
+  is(eventCount, 2, "Wrong event count(2)");
+  suppressEvents(false);
+  sendEvents();
+  is(eventCount, 4, "Wrong event count(2)");
+  if (eventCount != 4)
+  alert(eventCount);
+  SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(runTest);
+
+</script>
+</pre>
+</body>
+</html>
--- a/content/events/src/nsEventStateManager.cpp
+++ b/content/events/src/nsEventStateManager.cpp
@@ -255,16 +255,50 @@ PrintDocTreeAll(nsIDocShellTreeItem* aIt
       break;
     item = parent;
   }
 
   PrintDocTree(item, 0);
 }
 #endif
 
+static nsIDocument*
+EventHandlingSuppressed(nsPIDOMEventTarget* aTarget)
+{
+  nsCOMPtr<nsINode> node = do_QueryInterface(aTarget);
+  nsCOMPtr<nsIDocument> doc;
+  if (node) {
+    doc = node->GetOwnerDoc();
+  } else {
+    nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aTarget);
+    if (window) {
+      doc = do_QueryInterface(window->GetExtantDocument());
+    }
+  }
+
+  return (doc && doc->EventHandlingSuppressed()) ? doc : nsnull;
+}
+
+static void
+FireBlurEvent(nsPIDOMEventTarget* aTarget, nsEvent* aEvent, nsPresContext* aContext)
+{
+  NS_ASSERTION(aEvent->message == NS_BLUR_CONTENT, "Wrong event!");
+  nsIDocument* doc = EventHandlingSuppressed(aTarget);
+  if (doc) {
+    if (aContext) {
+      nsIPresShell* shell = aContext->GetPresShell();
+      if (shell) {
+        shell->NeedsBlurAfterSuppression(aTarget);
+      }
+    }
+  } else if (aTarget) {
+    nsEventDispatcher::Dispatch(aTarget, aContext, aEvent);
+  }
+}
+
 class nsUITimerCallback : public nsITimerCallback
 {
 public:
   nsUITimerCallback() : mPreviousCount(0) {}
   NS_DECL_ISUPPORTS
   NS_DECL_NSITIMERCALLBACK
 private:
   PRUint32 mPreviousCount;
@@ -1087,24 +1121,21 @@ nsEventStateManager::PreHandleEvent(nsPr
               focusController->SetSuppressFocus(PR_TRUE,
                                                 "NS_GOTFOCUS ESM Suppression");
             }
           }
 
           if (!isAlreadySuppressed) {
 
             // Fire the blur event on the previously focused document.
-
-            nsEventStatus blurstatus = nsEventStatus_eIgnore;
-            nsEvent blurevent(PR_TRUE, NS_BLUR_CONTENT);
-            blurevent.flags |= NS_EVENT_FLAG_CANT_BUBBLE;
-
-            nsEventDispatcher::Dispatch(gLastFocusedDocument,
-                                        gLastFocusedPresContextWeak,
-                                        &blurevent, nsnull, &blurstatus);
+            nsEvent blurEvent(PR_TRUE, NS_BLUR_CONTENT);
+            blurEvent.flags |= NS_EVENT_FLAG_CANT_BUBBLE;
+
+            FireBlurEvent(gLastFocusedDocument, &blurEvent,
+                          gLastFocusedPresContextWeak);
 
             nsCOMPtr<nsIEventStateManager> esm;
             if (!mCurrentFocus && gLastFocusedContent) {
               // We also need to blur the previously focused content node here,
               // if we don't have a focused content node in this document.
               // (SendFocusBlur isn't called in this case).
               if (gLastFocusedContent) {
                 nsCOMPtr<nsIDocument> doc = gLastFocusedContent->GetCurrentDoc();
@@ -1115,26 +1146,25 @@ nsEventStateManager::PreHandleEvent(nsPr
                     esm = oldPresContext->EventStateManager();
                     if (esm) {
                       esm->SetFocusedContent(gLastFocusedContent);
                     }
                   }
                 }
               }
               nsCOMPtr<nsIContent> blurContent = gLastFocusedContent;
-              blurevent.target = nsnull;
-              nsEventDispatcher::Dispatch(gLastFocusedContent,
-                                          gLastFocusedPresContextWeak,
-                                          &blurevent, nsnull, &blurstatus);
+              blurEvent.target = nsnull;
+              FireBlurEvent(gLastFocusedContent, &blurEvent,
+                            gLastFocusedPresContextWeak);
             }
             if (ourWindow) {
               // Clear the target so that Dispatch can set it back correctly.
-              blurevent.target = nsnull;
-              nsEventDispatcher::Dispatch(ourWindow, gLastFocusedPresContextWeak,
-                                          &blurevent, nsnull, &blurstatus);
+              blurEvent.target = nsnull;
+              nsCOMPtr<nsPIDOMEventTarget> win = do_QueryInterface(ourWindow);
+              FireBlurEvent(win, &blurEvent, gLastFocusedPresContextWeak);
             }
 
             if (esm) {
               esm->SetFocusedContent(nsnull);
             }
             NS_IF_RELEASE(gLastFocusedContent);
           }
 
@@ -1254,37 +1284,34 @@ nsEventStateManager::PreHandleEvent(nsPr
         if (gLastFocusedContent && !gLastFocusedContent->IsInDoc()) {
           NS_RELEASE(gLastFocusedContent);
         }
 
         nsIMEStateManager::OnTextStateBlur(nsnull, nsnull);
 
         // Now fire blurs.  We fire a blur on the focused document, element,
         // and window.
-
-        nsEventStatus status = nsEventStatus_eIgnore;
-        nsEvent event(PR_TRUE, NS_BLUR_CONTENT);
-        event.flags |= NS_EVENT_FLAG_CANT_BUBBLE;
+        nsEvent blurEvent(PR_TRUE, NS_BLUR_CONTENT);
+        blurEvent.flags |= NS_EVENT_FLAG_CANT_BUBBLE;
 
         if (gLastFocusedDocument && gLastFocusedPresContextWeak) {
           if (gLastFocusedContent) {
             // Retrieve this content node's pres context. it can be out of sync
             // in the Ender widget case.
             nsCOMPtr<nsIDocument> doc = gLastFocusedContent->GetDocument();
             if (doc) {
               nsIPresShell *shell = doc->GetPrimaryShell();
               if (shell) {
                 nsCOMPtr<nsPresContext> oldPresContext =
                   shell->GetPresContext();
 
                 nsCOMPtr<nsIEventStateManager> esm =
                   oldPresContext->EventStateManager();
                 esm->SetFocusedContent(gLastFocusedContent);
-                nsEventDispatcher::Dispatch(gLastFocusedContent, oldPresContext,
-                                            &event, nsnull, &status);
+                FireBlurEvent(gLastFocusedContent, &blurEvent, oldPresContext);
                 esm->SetFocusedContent(nsnull);
                 NS_IF_RELEASE(gLastFocusedContent);
               }
             }
           }
 
           // Clear our global variables before firing the event to prevent
           // duplicate blur events (bug 112294).
@@ -1296,25 +1323,23 @@ nsEventStateManager::PreHandleEvent(nsPr
 
           // fire blur on document and window
           if (lastFocusedDocument) {
             // get the window here, in case the event causes
             // gLastFocusedDocument to change.
 
             nsCOMPtr<nsPIDOMWindow> window(lastFocusedDocument->GetWindow());
 
-            event.target = nsnull;
-            nsEventDispatcher::Dispatch(lastFocusedDocument,
-                                        lastFocusedPresContext,
-                                        &event, nsnull, &status);
+            blurEvent.target = nsnull;
+            FireBlurEvent(lastFocusedDocument, &blurEvent, lastFocusedPresContext);
 
             if (window) {
-              event.target = nsnull;
-              nsEventDispatcher::Dispatch(window, lastFocusedPresContext,
-                                          &event, nsnull, &status);
+              blurEvent.target = nsnull;
+              nsCOMPtr<nsPIDOMEventTarget> win = do_QueryInterface(window);
+              FireBlurEvent(win, &blurEvent ,lastFocusedPresContext);
             }
           }
         }
       }
 #endif
     }
     break;
 
@@ -1437,20 +1462,19 @@ nsEventStateManager::PreHandleEvent(nsPr
       if (gLastFocusedDocument && gLastFocusedDocument == mDocument &&
           gLastFocusedDocument != mFirstDocumentBlurEvent) {
 
         PRBool clearFirstDocumentBlurEvent = PR_FALSE;
         if (!mFirstDocumentBlurEvent) {
           mFirstDocumentBlurEvent = gLastFocusedDocument;
           clearFirstDocumentBlurEvent = PR_TRUE;
         }
-          
-        nsEventStatus status = nsEventStatus_eIgnore;
-        nsEvent event(PR_TRUE, NS_BLUR_CONTENT);
-        event.flags |= NS_EVENT_FLAG_CANT_BUBBLE;
+
+        nsEvent blurEvent(PR_TRUE, NS_BLUR_CONTENT);
+        blurEvent.flags |= NS_EVENT_FLAG_CANT_BUBBLE;
 
         if (gLastFocusedContent) {
           nsIPresShell *shell = gLastFocusedDocument->GetPrimaryShell();
           if (shell) {
             nsCOMPtr<nsPresContext> oldPresContext = shell->GetPresContext();
 
             nsCOMPtr<nsIDOMElement> focusedElement;
             if (focusController)
@@ -1458,40 +1482,38 @@ nsEventStateManager::PreHandleEvent(nsPr
 
             nsCOMPtr<nsIEventStateManager> esm;
             esm = oldPresContext->EventStateManager();
             esm->SetFocusedContent(gLastFocusedContent);
 
             nsCOMPtr<nsIContent> focusedContent = do_QueryInterface(focusedElement);
             if (focusedContent) {
               // Blur the element.
-              nsEventDispatcher::Dispatch(focusedContent, oldPresContext,
-                                          &event, nsnull, &status);
+              FireBlurEvent(focusedContent, &blurEvent, oldPresContext);
             }
 
             esm->SetFocusedContent(nsnull);
             NS_IF_RELEASE(gLastFocusedContent);
           }
         }
 
         // Clear our global variables before firing the event to prevent
         // duplicate blur events (bug 112294).
         mCurrentTarget = nsnull;
         NS_IF_RELEASE(gLastFocusedDocument);
         gLastFocusedPresContextWeak = nsnull;
 
         // fire blur on document and window
-        event.target = nsnull;
-        nsEventDispatcher::Dispatch(mDocument, aPresContext, &event, nsnull,
-                                    &status);
+        blurEvent.target = nsnull;
+        FireBlurEvent(mDocument, &blurEvent, aPresContext);
 
         if (ourWindow) {
-          event.target = nsnull;
-          nsEventDispatcher::Dispatch(ourWindow, aPresContext, &event, nsnull,
-                                      &status);
+          blurEvent.target = nsnull;
+          nsCOMPtr<nsPIDOMEventTarget> win = do_QueryInterface(ourWindow);
+          FireBlurEvent(win, &blurEvent, aPresContext);
         }
         if (clearFirstDocumentBlurEvent) {
           mFirstDocumentBlurEvent = nsnull;
         }
       }
 
       if (focusController) {
         focusController->SetActive(PR_FALSE);
@@ -4317,17 +4339,17 @@ nsEventStateManager::ShiftFocusInternal(
     // does, we send focus into the subshell.
 
     nsCOMPtr<nsIDocShell> sub_shell;
     nsCOMPtr<nsIDocument> doc = nextFocus->GetDocument();
 
     if (doc) {
       nsIDocument *sub_doc = doc->GetSubDocumentFor(nextFocus);
 
-      if (sub_doc) {
+      if (sub_doc && !sub_doc->EventHandlingSuppressed()) {
         nsCOMPtr<nsISupports> container = sub_doc->GetContainer();
         sub_shell = do_QueryInterface(container);
       }
     }
 
     if (sub_shell) {
       // Make sure to scroll before possibly dispatching focus/blur events.
       presShell->ScrollContentIntoView(nextFocus,
@@ -5179,19 +5201,18 @@ nsEventStateManager::SendFocusBlur(nsPre
         nsCOMPtr<nsIViewManager> kungFuDeathGrip;
         nsIPresShell *shell = doc->GetPrimaryShell();
         if (shell) {
           kungFuDeathGrip = shell->GetViewManager();
 
           nsCOMPtr<nsPresContext> oldPresContext = shell->GetPresContext();
 
           //fire blur
-          nsEventStatus status = nsEventStatus_eIgnore;
-          nsEvent event(PR_TRUE, NS_BLUR_CONTENT);
-          event.flags |= NS_EVENT_FLAG_CANT_BUBBLE;
+          nsEvent blurEvent(PR_TRUE, NS_BLUR_CONTENT);
+          blurEvent.flags |= NS_EVENT_FLAG_CANT_BUBBLE;
 
           EnsureDocument(presShell);
 
           // Make sure we're not switching command dispatchers, if so,
           // surpress the blurred one
           if(gLastFocusedDocument && mDocument) {
             nsPIDOMWindow *newWindow = mDocument->GetWindow();
             if (newWindow) {
@@ -5212,18 +5233,17 @@ nsEventStateManager::SendFocusBlur(nsPre
           nsCOMPtr<nsIEventStateManager> esm;
           esm = oldPresContext->EventStateManager();
           esm->SetFocusedContent(gLastFocusedContent);
           nsCOMPtr<nsIContent> temp = gLastFocusedContent;
           NS_RELEASE(gLastFocusedContent); // nulls out gLastFocusedContent
 
           nsCxPusher pusher;
           if (pusher.Push(temp)) {
-            nsEventDispatcher::Dispatch(temp, oldPresContext, &event, nsnull,
-                                        &status);
+            FireBlurEvent(temp, &blurEvent, oldPresContext);
             pusher.Pop();
           }
 
           focusAfterBlur = mCurrentFocus;
           if (!previousFocus || previousFocus == focusAfterBlur)
             esm->SetFocusedContent(nsnull);
         }
       }
@@ -5245,19 +5265,18 @@ nsEventStateManager::SendFocusBlur(nsPre
 
     if(gLastFocusedDocument)
       window = gLastFocusedDocument->GetWindow();
 
     EnsureDocument(presShell);
 
     if (gLastFocusedDocument && (gLastFocusedDocument != mDocument) &&
         window) {
-      nsEventStatus status = nsEventStatus_eIgnore;
-      nsEvent event(PR_TRUE, NS_BLUR_CONTENT);
-      event.flags |= NS_EVENT_FLAG_CANT_BUBBLE;
+      nsEvent blurEvent(PR_TRUE, NS_BLUR_CONTENT);
+      blurEvent.flags |= NS_EVENT_FLAG_CANT_BUBBLE;
 
       // Make sure we're not switching command dispatchers, if so,
       // suppress the blurred one if it isn't already suppressed
       if (mDocument && !oldFocusSuppressor.Suppressing()) {
         nsCOMPtr<nsPIDOMWindow> newWindow(mDocument->GetWindow());
 
         if (newWindow) {
           nsCOMPtr<nsPIDOMWindow> oldWindow(gLastFocusedDocument->GetWindow());
@@ -5275,35 +5294,34 @@ nsEventStateManager::SendFocusBlur(nsPre
 
       gLastFocusedPresContextWeak->EventStateManager()->SetFocusedContent(nsnull);
       nsCOMPtr<nsIDocument> temp = gLastFocusedDocument;
       NS_RELEASE(gLastFocusedDocument);
       gLastFocusedDocument = nsnull;
 
       nsCxPusher pusher;
       if (pusher.Push(temp)) {
-        nsEventDispatcher::Dispatch(temp, gLastFocusedPresContextWeak, &event,
-                                    nsnull, &status);
+        FireBlurEvent(temp, &blurEvent, gLastFocusedPresContextWeak);
         pusher.Pop();
       }
 
       if (previousFocus && mCurrentFocus != previousFocus) {
         // The document's blur handler focused something else.
         // Abort firing any additional blur or focus events, and make sure
         // nsFocusController:mFocusedElement is not nulled out, but agrees
         // with our current concept of focus.
         EnsureFocusSynchronization();
         return NS_OK;
       }
 
       nsCOMPtr<nsPIDOMEventTarget> target = do_QueryInterface(window);
       if (pusher.Push(target)) {
-        nsEventDispatcher::Dispatch(window, gLastFocusedPresContextWeak, &event,
-                                    nsnull, &status);
-
+        blurEvent.target = nsnull;
+        FireBlurEvent(target, &blurEvent, gLastFocusedPresContextWeak);
+  
         if (previousFocus && mCurrentFocus != previousFocus) {
           // The window's blur handler focused something else.
           // Abort firing any additional blur or focus events.
           EnsureFocusSynchronization();
           return NS_OK;
         }
       }
     }
@@ -5346,17 +5364,18 @@ nsEventStateManager::SendFocusBlur(nsPre
       if (vm) {
         vm->GetWidget(getter_AddRefs(widget));
       }
     }
     if (widget)
       widget->SetFocus(PR_TRUE);
   }
 
-  if (nsnull != aContent && aContent != mFirstFocusEvent) {
+  if (nsnull != aContent && aContent != mFirstFocusEvent &&
+      !EventHandlingSuppressed(aContent)) {
 
     //Store the first focus event we fire and don't refire focus
     //to that element while the first focus is still ongoing.
     PRBool clearFirstFocusEvent = PR_FALSE;
     if (!mFirstFocusEvent) {
       mFirstFocusEvent = aContent;
       clearFirstFocusEvent = PR_TRUE;
     }
@@ -5381,17 +5400,17 @@ nsEventStateManager::SendFocusBlur(nsPre
       mCurrentTabIndex = val;
     }
 
     if (clearFirstFocusEvent) {
       mFirstFocusEvent = nsnull;
     }
 
     nsIMEStateManager::OnTextStateFocus(mPresContext, mCurrentFocus);
-  } else if (!aContent) {
+  } else if (!aContent && !EventHandlingSuppressed(mDocument)) {
     //fire focus on document even if the content isn't focusable (ie. text)
     //see bugzilla bug 93521
     nsEventStatus status = nsEventStatus_eIgnore;
     nsEvent event(PR_TRUE, NS_FOCUS_CONTENT);
     event.flags |= NS_EVENT_FLAG_CANT_BUBBLE;
 
     if (nsnull != mPresContext && mDocument) {
       nsCxPusher pusher;
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -6016,16 +6016,24 @@ nsDocShell::RestoreFromHistory()
 
     if (oldMUDV && newMUDV) {
         newMUDV->SetTextZoom(textZoom);
         newMUDV->SetFullZoom(pageZoom);
     }
 
     nsCOMPtr<nsIDocument> document = do_QueryInterface(domDoc);
     if (document) {
+        nsCOMPtr<nsIDocShellTreeItem> parent;
+        GetParent(getter_AddRefs(parent));
+        nsCOMPtr<nsIDOMDocument> parentDoc = do_GetInterface(parent);
+        nsCOMPtr<nsIDocument> d = do_QueryInterface(parentDoc);
+        if (d && d->EventHandlingSuppressed()) {
+            document->SuppressEventHandling(d->EventHandlingSuppressed());
+        }
+
         // Use the uri from the mLSHE we had when we entered this function
         // (which need not match the document's URI if anchors are involved),
         // since that's the history entry we're loading.  Note that if we use
         // origLSHE we don't have to worry about whether the entry in question
         // is still mLSHE or whether it's now mOSHE.
         nsCOMPtr<nsIURI> uri;
         origLSHE->GetURI(getter_AddRefs(uri));
         SetCurrentURI(uri, document->GetChannel(), PR_TRUE);
--- a/dom/public/idl/base/nsIDOMWindowUtils.idl
+++ b/dom/public/idl/base/nsIDOMWindowUtils.idl
@@ -43,17 +43,17 @@
  * elevated privileges; the method implementations should contain the
  * necessary security checks.  Access this interface by calling
  * getInterface on a DOMWindow.
  */
 
 interface nsIDOMElement;
 interface nsIDOMHTMLCanvasElement;
 
-[scriptable, uuid(8C6263C9-F3EF-419d-80EF-D5D716635FAA)]
+[scriptable, uuid(6e3510b9-806d-4a2a-be79-73d2a495b4b8)]
 interface nsIDOMWindowUtils : nsISupports {
 
   /**
    * Image animation mode of the window. When this attribute's value
    * is changed, the implementation should set all images in the window
    * to the given value. That is, when set to kDontAnimMode, all images
    * will stop animating. The attribute's value must be one of the
    * animationMode values from imgIContainer.
@@ -294,16 +294,25 @@ interface nsIDOMWindowUtils : nsISupport
 
   /**
    * Returns true if a MozAfterPaint event has been queued but not yet
    * fired.
    */
   readonly attribute boolean isMozAfterPaintPending;
 
   /**
+   * Suppresses/unsuppresses user initiated event handling in window's document
+   * and subdocuments.
+   *
+   * @throw NS_ERROR_DOM_SECURITY_ERR if called without UniversalXPConnect
+   *        privileges and NS_ERROR_FAILURE if window doesn't have a document.
+   */
+  void suppressEventHandling(in boolean aSuppress);
+
+  /**
    * Disable or enable non synthetic test mouse events on *all* windows.
    *
    * Cannot be accessed from unprivileged context (not content-accessible).
    * Will throw a DOM security error if called without UniversalXPConnect
    * privileges.
    *
    * @param aDisable  If true, disable all non synthetic test mouse events
    *               on all windows.  Otherwise, enable them.
--- a/dom/src/base/nsDOMWindowUtils.cpp
+++ b/dom/src/base/nsDOMWindowUtils.cpp
@@ -695,8 +695,27 @@ nsDOMWindowUtils::DisableNonTestMouseEve
   NS_ENSURE_TRUE(mWindow, NS_ERROR_FAILURE);
   nsIDocShell *docShell = mWindow->GetDocShell();
   NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
   nsCOMPtr<nsIPresShell> presShell;
   docShell->GetPresShell(getter_AddRefs(presShell));
   NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
   return presShell->DisableNonTestMouseEvents(aDisable);
 }
+
+NS_IMETHODIMP
+nsDOMWindowUtils::SuppressEventHandling(PRBool aSuppress)
+{
+  PRBool hasCap = PR_FALSE;
+  if (NS_FAILED(nsContentUtils::GetSecurityManager()->IsCapabilityEnabled("UniversalXPConnect", &hasCap)) || !hasCap)
+    return NS_ERROR_DOM_SECURITY_ERR;
+
+  nsCOMPtr<nsIDocument> doc(do_QueryInterface(mWindow->GetExtantDocument()));
+  NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
+
+  if (aSuppress) {
+    doc->SuppressEventHandling();
+  } else {
+    doc->UnsuppressEventHandling();
+  }
+  return NS_OK;
+}
+
--- a/dom/src/base/nsGlobalWindow.cpp
+++ b/dom/src/base/nsGlobalWindow.cpp
@@ -5606,19 +5606,27 @@ nsGlobalWindow::EnterModalState()
   GetTop(getter_AddRefs(top));
 
   if (!top) {
     NS_ERROR("Uh, EnterModalState() called w/o a reachable top window?");
 
     return;
   }
 
-  static_cast<nsGlobalWindow *>
-             (static_cast<nsIDOMWindow *>
-                         (top.get()))->mModalStateDepth++;
+  nsGlobalWindow* topWin =
+    static_cast<nsGlobalWindow*>(static_cast<nsIDOMWindow *>(top.get()));
+  if (topWin->mModalStateDepth == 0) {
+    NS_ASSERTION(!mSuspendedDoc, "Shouldn't have mSuspendedDoc here!");
+
+    mSuspendedDoc = do_QueryInterface(topWin->GetExtantDocument());
+    if (mSuspendedDoc) {
+      mSuspendedDoc->SuppressEventHandling();
+    }
+  }
+  topWin->mModalStateDepth++;
 }
 
 // static
 void
 nsGlobalWindow::RunPendingTimeoutsRecursive(nsGlobalWindow *aTopWindow,
                                             nsGlobalWindow *aWindow)
 {
   nsGlobalWindow *inner;
@@ -5704,16 +5712,32 @@ nsGlobalWindow::LeaveModalState()
                            (top.get()));
 
   topWin->mModalStateDepth--;
 
   if (topWin->mModalStateDepth == 0) {
     nsCOMPtr<nsIRunnable> runner = new nsPendingTimeoutRunner(topWin);
     if (NS_FAILED(NS_DispatchToCurrentThread(runner)))
       NS_WARNING("failed to dispatch pending timeout runnable");
+
+    if (mSuspendedDoc) {
+      nsCOMPtr<nsIDocument> currentDoc =
+        do_QueryInterface(topWin->GetExtantDocument());
+      if (currentDoc == mSuspendedDoc) {
+        NS_DispatchToCurrentThread(
+          NS_NEW_RUNNABLE_METHOD(nsIDocument, mSuspendedDoc.get(),
+                                 UnsuppressEventHandling));
+      } else {
+        // Somehow the document was changed.
+        // Unsuppress event handling in the document but don't even
+        // try to fire events.
+        mSuspendedDoc->UnsuppressEventHandlingAndFireEvents(PR_FALSE);
+      }
+      mSuspendedDoc = nsnull;
+    }
   }
 }
 
 PRBool
 nsGlobalWindow::IsInModalState()
 {
   nsCOMPtr<nsIDOMWindow> top;
   GetTop(getter_AddRefs(top));
--- a/dom/src/base/nsGlobalWindow.h
+++ b/dom/src/base/nsGlobalWindow.h
@@ -739,16 +739,18 @@ protected:
   PRUint32 mSerial;
   nsCOMPtr<nsIURI> mLastOpenedURI;
 #endif
 
   nsCOMPtr<nsIDOMOfflineResourceList> mApplicationCache;
 
   nsDataHashtable<nsVoidPtrHashKey, void*> mCachedXBLPrototypeHandlers;
 
+  nsCOMPtr<nsIDocument> mSuspendedDoc;
+
   friend class nsDOMScriptableHelper;
   friend class nsDOMWindowUtils;
   friend class PostMessageEvent;
   static nsIFactory *sComputedDOMStyleFactory;
   static nsIDOMStorageList* sGlobalStorageList;
 };
 
 /*
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -624,16 +624,19 @@ DocumentViewerImpl::SyncParentSubDocMap(
 
     if (parent_win) {
       nsCOMPtr<nsIDOMDocument> dom_doc;
       parent_win->GetDocument(getter_AddRefs(dom_doc));
 
       nsCOMPtr<nsIDocument> parent_doc(do_QueryInterface(dom_doc));
 
       if (parent_doc) {
+        if (mDocument && parent_doc->GetSubDocumentFor(content) != mDocument) {
+          mDocument->SuppressEventHandling(parent_doc->EventHandlingSuppressed());
+        }
         return parent_doc->SetSubDocumentFor(content, mDocument);
       }
     }
   }
 
   return NS_OK;
 }
 
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -92,24 +92,25 @@ class nsIStyleFrameConstruction;
 class nsIStyleSheet;
 class nsCSSFrameConstructor;
 class nsISelection;
 template<class E> class nsCOMArray;
 class nsWeakFrame;
 class nsIScrollableFrame;
 class gfxASurface;
 class gfxContext;
+class nsPIDOMEventTarget;
 
 typedef short SelectionType;
 typedef PRUint32 nsFrameState;
 
-// 445e6184-5e7e-4a9b-97f7-c9391e6773d2
-#define NS_IPRESSHELL_IID     \
-{ 0x445e6184, 0x5e7e, 0x4a9b, \
-  { 0x97, 0xf7, 0xc9, 0x39, 0x1e, 0x67, 0x73, 0xd2 } }
+// 8355e7a9-4118-47dc-97e3-a3c251332e86
+#define NS_IPRESSHELL_IID \
+{ 0x8355e7a9, 0x4118, 0x47dc, \
+  { 0x97, 0xe3, 0xa3, 0xc2, 0x51, 0x33, 0x2e, 0x86 } }
 
 // Constants for ScrollContentIntoView() function
 #define NS_PRESSHELL_SCROLL_TOP      0
 #define NS_PRESSHELL_SCROLL_BOTTOM   100
 #define NS_PRESSHELL_SCROLL_LEFT     0
 #define NS_PRESSHELL_SCROLL_RIGHT    100
 #define NS_PRESSHELL_SCROLL_CENTER   50
 #define NS_PRESSHELL_SCROLL_ANYWHERE -1
@@ -687,16 +688,19 @@ public:
   virtual void Freeze() = 0;
 
   /**
    * Restarts active elements (plugins) in this presentation and in the
    * presentations of subdocuments, then do a full invalidate of the content area.
    */
   virtual void Thaw() = 0;
 
+  virtual void NeedsBlurAfterSuppression(nsPIDOMEventTarget* aTarget) = 0;
+  virtual void FireOrClearDelayedEvents(PRBool aFireEvents) = 0;
+
   /**
    * When this shell is disconnected from its containing docshell, we
    * lose our container pointer.  However, we'd still like to be able to target
    * user events at the docshell's parent.  This pointer allows us to do that.
    * It should not be used for any other purpose.
    */
   void SetForwardingContainer(nsWeakPtr aContainer)
   {
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -875,16 +875,18 @@ public:
   NS_IMETHOD GetEventTargetFrame(nsIFrame** aFrame);
   NS_IMETHOD GetEventTargetContent(nsEvent* aEvent, nsIContent** aContent);
 
   NS_IMETHOD IsReflowLocked(PRBool* aIsLocked);  
 
   virtual nsresult ReconstructFrames(void);
   virtual void Freeze();
   virtual void Thaw();
+  virtual void NeedsBlurAfterSuppression(nsPIDOMEventTarget* aTarget);
+  virtual void FireOrClearDelayedEvents(PRBool aFireEvents);
 
   virtual nsIFrame* GetFrameForPoint(nsIFrame* aFrame, nsPoint aPt);
 
   NS_IMETHOD RenderDocument(const nsRect& aRect, PRUint32 aFlags,
                             nscolor aBackgroundColor,
                             gfxContext* aThebesContext);
 
   virtual already_AddRefed<gfxASurface> RenderNode(nsIDOMNode* aNode,
@@ -1147,16 +1149,23 @@ protected:
   nsRefPtr<nsCaret>             mOriginalCaret;
   PRInt16                       mSelectionFlags;
   FrameArena                    mFrameArena;
   StackArena                    mStackArena;
   nsCOMPtr<nsIDragService>      mDragService;
   
   nsRevocableEventPtr<ReflowEvent> mReflowEvent;
 
+  PRPackedBool                   mNeedsGotFocus;
+  PRPackedBool                   mNeedsLostFocus;
+  PRPackedBool                   mMozTakingFocus;
+  PRPackedBool                   mNeedsActivate;
+  PRPackedBool                   mNeedsDeactivate;
+  nsCOMArray<nsPIDOMEventTarget> mDelayedBlurTargets;
+
   nsCallbackEventRequest* mFirstCallbackEventRequest;
   nsCallbackEventRequest* mLastCallbackEventRequest;
 
   PRPackedBool      mIsThemeSupportDisabled;  // Whether or not form controls should use nsITheme in this shell.
 
   PRPackedBool      mIsDocumentGone;      // We've been disconnected from the document.
   PRPackedBool      mPaintingSuppressed;  // For all documents we initially lock down painting.
                                           // We will refuse to paint the document until either
@@ -5616,16 +5625,47 @@ PresShell::HandleEvent(nsIView         *
         *aEventStatus = nsEventStatus_eConsumeDoDefault;
         mPresContext->SysColorChanged();
         return NS_OK;
       }
     }
     return NS_OK;
   }
   
+  if (mDocument && mDocument->EventHandlingSuppressed()) {
+    switch (aEvent->message) {
+      case NS_GOTFOCUS:
+        mNeedsGotFocus = PR_TRUE;
+        mNeedsLostFocus = PR_FALSE;
+        mNeedsDeactivate = PR_FALSE;
+        break;
+      case NS_LOSTFOCUS:
+        mNeedsLostFocus = PR_TRUE;
+        mNeedsGotFocus = PR_FALSE;
+        mNeedsActivate = PR_FALSE;
+        mMozTakingFocus =
+          (aEvent->eventStructType == NS_FOCUS_EVENT &&
+           static_cast<nsFocusEvent*>(aEvent)->isMozWindowTakingFocus);
+        break;
+      case NS_ACTIVATE:
+        mNeedsActivate = PR_TRUE;
+        mNeedsDeactivate = PR_FALSE;
+        mNeedsLostFocus = PR_FALSE;
+        break;
+      case NS_DEACTIVATE:
+        mNeedsDeactivate = PR_TRUE;
+        mNeedsActivate = PR_FALSE;
+        mNeedsGotFocus = PR_FALSE;
+        break;
+      default:
+        break;
+    }
+    return NS_OK;
+  }
+
   nsIFrame* frame = static_cast<nsIFrame*>(aView->GetClientData());
 
   PRBool dispatchUsingCoordinates =
       !NS_IS_KEY_EVENT(aEvent) && !NS_IS_IME_EVENT(aEvent) &&
       !NS_IS_CONTEXT_MENU_KEY(aEvent) && !NS_IS_FOCUS_EVENT(aEvent) &&
       !NS_IS_PLUGIN_EVENT(aEvent);
 
   // if this event has no frame, we need to retarget it at a parent
@@ -6472,16 +6512,89 @@ PresShell::Freeze()
     mCaret->SetCaretVisible(PR_FALSE);
 
   mPaintingSuppressed = PR_TRUE;
 
   if (mDocument)
     mDocument->EnumerateSubDocuments(FreezeSubDocument, nsnull);
 }
 
+void
+PresShell::FireOrClearDelayedEvents(PRBool aFireEvents)
+{
+  if (!aFireEvents) {
+    mDelayedBlurTargets.Clear();
+    mNeedsGotFocus = mNeedsLostFocus = mMozTakingFocus = mNeedsActivate =
+      mNeedsDeactivate = PR_FALSE;
+    return;
+  }
+
+  if (!mIsDestroying && mDocument) {
+    nsCOMPtr<nsIDocument> doc = mDocument;
+    for (PRInt32 i = 0;
+         (i < mDelayedBlurTargets.Count()) &&
+         !doc->EventHandlingSuppressed(); ++i) {
+      nsEvent blurevent(PR_TRUE, NS_BLUR_CONTENT);
+      blurevent.flags |= NS_EVENT_FLAG_CANT_BUBBLE;
+      nsEventDispatcher::Dispatch(mDelayedBlurTargets[i], mPresContext, &blurevent);
+    }
+    mDelayedBlurTargets.Clear();
+
+    nsFocusEvent firstEvent(PR_TRUE, NS_EVENT_NULL, nsnull);
+    nsFocusEvent secondEvent(PR_TRUE, NS_EVENT_NULL, nsnull);
+    if (mNeedsGotFocus) {
+      firstEvent.message = NS_GOTFOCUS;
+    } else if (mNeedsDeactivate) {
+      firstEvent.message = NS_DEACTIVATE;
+    }
+    if (mNeedsActivate) {
+      secondEvent.message = NS_ACTIVATE;
+    } else if (mNeedsLostFocus) {
+      secondEvent.message = NS_LOSTFOCUS;
+      secondEvent.isMozWindowTakingFocus = mMozTakingFocus;
+    }
+    mNeedsGotFocus = mNeedsLostFocus = mMozTakingFocus = mNeedsActivate =
+      mNeedsDeactivate = PR_FALSE;
+
+    if (firstEvent.message && !mIsDestroying &&
+        !doc->EventHandlingSuppressed()) {
+      nsIViewManager* vm = GetViewManager();
+      if (vm) {
+        nsIView* view = nsnull;
+        vm->GetRootView(view);
+        if (view) {
+          nsEventStatus status = nsEventStatus_eIgnore;
+          HandleEvent(view, &firstEvent, &status);
+        }
+      }
+    }
+    if (secondEvent.message && !mIsDestroying &&
+        !doc->EventHandlingSuppressed()) {
+      nsIViewManager* vm = GetViewManager();
+      if (vm) {
+        nsIView* view = nsnull;
+        vm->GetRootView(view);
+        if (view) {
+          nsEventStatus status = nsEventStatus_eIgnore;
+          HandleEvent(view, &secondEvent, &status);
+        }
+      }
+    }
+  }
+}
+
+void
+PresShell::NeedsBlurAfterSuppression(nsPIDOMEventTarget* aTarget)
+{
+  if (mDocument && mDocument->EventHandlingSuppressed()) {
+    mDelayedBlurTargets.RemoveObject(aTarget);
+    mDelayedBlurTargets.AppendObject(aTarget);
+  }
+}
+
 static void
 StartPluginInstance(PresShell *aShell, nsIContent *aContent)
 {
   nsCOMPtr<nsIObjectLoadingContent> objlc(do_QueryInterface(aContent));
   if (!objlc)
     return;
 
   nsCOMPtr<nsIPluginInstance> inst;