Fix for bug 512645 (Only clamp nested timeouts). r=jst.
authorPeter Van der Beken <peterv@propagandism.org>
Wed, 26 Aug 2009 11:07:39 -0700
changeset 34729 350ffc1d793a3f77bb015771917280091c92903f
parent 34728 2f5d97dd7e561d84b0c0007259b8761e6d40cf8b
child 34730 3e1290bba9029798016d7106237fea5850f8840c
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersjst
bugs512645
milestone1.9.3a1pre
Fix for bug 512645 (Only clamp nested timeouts). r=jst.
dom/base/nsGlobalWindow.cpp
dom/base/nsGlobalWindow.h
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -235,16 +235,20 @@ static PRBool               gDOMWindowDu
 
 #if defined(DEBUG_bryner) || defined(DEBUG_chb)
 #define DEBUG_PAGE_CACHE
 #endif
 
 // The shortest interval/timeout we permit
 #define DOM_MIN_TIMEOUT_VALUE 10 // 10ms
 
+// The number of nested timeouts before we start clamping. HTML5 says 1, WebKit
+// uses 5.
+#define DOM_CLAMP_TIMEOUT_NESTING_LEVEL 5
+
 // The longest interval (as PRIntervalTime) we permit, or that our
 // timer code can handle, really. See DELAY_INTERVAL_LIMIT in
 // nsTimerImpl.h for details.
 #define DOM_MAX_TIMEOUT_VALUE    PR_BIT(8 * sizeof(PRIntervalTime) - 1)
 
 #define FORWARD_TO_OUTER(method, args, err_rval)                              \
   PR_BEGIN_MACRO                                                              \
   if (IsInnerWindow()) {                                                      \
@@ -7660,31 +7664,35 @@ nsGlobalWindow::ClearWindowScope(nsISupp
     }
   }
 }
 
 //*****************************************************************************
 // nsGlobalWindow: Timeout Functions
 //*****************************************************************************
 
+PRUint32 sNestingLevel;
+
 nsresult
 nsGlobalWindow::SetTimeoutOrInterval(nsIScriptTimeoutHandler *aHandler,
                                      PRInt32 interval,
                                      PRBool aIsInterval, PRInt32 *aReturn)
 {
   FORWARD_TO_INNER(SetTimeoutOrInterval, (aHandler, interval, aIsInterval, aReturn),
                    NS_ERROR_NOT_INITIALIZED);
 
   // If we don't have a document (we could have been unloaded since
   // the call to setTimeout was made), do nothing.
   if (!mDocument) {
     return NS_OK;
   }
 
-  if (interval < DOM_MIN_TIMEOUT_VALUE) {
+  PRUint32 nestingLevel = sNestingLevel + 1;
+  if (interval < DOM_MIN_TIMEOUT_VALUE &&
+      (aIsInterval || nestingLevel >= DOM_CLAMP_TIMEOUT_NESTING_LEVEL)) {
     // Don't allow timeouts less than DOM_MIN_TIMEOUT_VALUE from
     // now...
 
     interval = DOM_MIN_TIMEOUT_VALUE;
   }
 
   NS_ASSERTION(interval >= 0, "DOM_MIN_TIMEOUT_VALUE lies");
   PRUint32 realInterval = interval;
@@ -7776,16 +7784,20 @@ nsGlobalWindow::SetTimeoutOrInterval(nsI
     // don't create a timer for it, since that will happen when we are thawed
     // and the timeout will then get a timer and run to completion.
 
     timeout->mWhen = delta;
   }
 
   timeout->mWindow = this;
 
+  if (!aIsInterval) {
+    timeout->mNestingLevel = nestingLevel;
+  }
+
   // No popups from timeouts by default
   timeout->mPopupState = openAbused;
 
   if (gRunningTimeoutDepth == 0 && gPopupControlState < openAbused) {
     // This timeout is *not* set from another timeout and it's set
     // while popups are enabled. Propagate the state to the timeout if
     // its delay (interval) is equal to or less than what
     // "dom.disable_open_click_delay" is set to (in ms).
@@ -7993,16 +8005,23 @@ nsGlobalWindow::RunTimeout(nsTimeout *aT
 
     // Hold on to the timeout in case mExpr or mFunObj releases its
     // doc.
     timeout->AddRef();
 
     ++gRunningTimeoutDepth;
     ++mTimeoutFiringDepth;
 
+    PRBool trackNestingLevel = !timeout->mInterval;
+    PRUint32 nestingLevel;
+    if (trackNestingLevel) {
+      nestingLevel = sNestingLevel;
+      sNestingLevel = timeout->mNestingLevel;
+    }
+
     nsCOMPtr<nsIScriptTimeoutHandler> handler(timeout->mScriptHandler);
     void *scriptObject = handler->GetScriptObject();
     if (!scriptObject) {
       // Evaluate the timeout expression.
       const PRUnichar *script = handler->GetHandlerText();
       NS_ASSERTION(script, "timeout has no script nor handler text!");
 
       const char *filename = nsnull;
@@ -8033,16 +8052,20 @@ nsGlobalWindow::RunTimeout(nsTimeout *aT
                             scriptObject, handler->GetArgv(),
                             // XXXmarkh - consider allowing CallEventHandler to
                             // accept nsnull?
                             getter_AddRefs(dummy));
 
     }
     handler = nsnull; // drop reference before dropping timeout refs.
 
+    if (trackNestingLevel) {
+      sNestingLevel = nestingLevel;
+    }
+
     --mTimeoutFiringDepth;
     --gRunningTimeoutDepth;
 
     mRunningTimeout = last_running_timeout;
     timeout->mRunning = PR_FALSE;
 
     // We ignore any failures from calling EvaluateString() or
     // CallEventHandler() on the context here since we're in a loop
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -180,16 +180,19 @@ struct nsTimeout : PRCList
   PRTime mWhen;
 
   // Principal with which to execute
   nsCOMPtr<nsIPrincipal> mPrincipal;
 
   // stack depth at which timeout is firing
   PRUint32 mFiringDepth;
 
+  // 
+  PRUint32 mNestingLevel;
+
   // The popup state at timeout creation time if not created from
   // another timeout
   PopupControlState mPopupState;
 
   // The language-specific information about the callback.
   nsCOMPtr<nsIScriptTimeoutHandler> mScriptHandler;
 
 private: