Commit ef535192 authored by mrbkap%gmail.com's avatar mrbkap%gmail.com
Browse files

bug 305452: Don't run timeouts that were set after the page was put into the...

bug 305452: Don't run timeouts that were set after the page was put into the bfcache (e.g., from a mousemove handler). Instead, store them in the timer list and wait for the page to be pulled out of the bfcache to run them. Also fix a couple of refcounting botches so that timeouts suspended in a page put into the bfcache get cleaned up instead of leaking. r=bryner sr=jst
parent 52d0c531
Loading
Loading
Loading
Loading
+51 −18
Original line number Diff line number Diff line
@@ -5801,9 +5801,10 @@ nsGlobalWindow::SetTimeoutOrInterval(PRBool aIsInterval, PRInt32 *aReturn)
  if (!timeout)
    return NS_ERROR_OUT_OF_MEMORY;

  // Increment the timeout's reference count to indicate that this
  // timeout struct will be held as the closure of a timer.
  // Increment the timeout's reference count to represent this function's hold
  // on the timeout.
  timeout->AddRef();

  if (aIsInterval) {
    timeout->mInterval = (PRInt32)interval;
  }
@@ -5872,8 +5873,14 @@ nsGlobalWindow::SetTimeoutOrInterval(PRBool aIsInterval, PRInt32 *aReturn)
    return NS_ERROR_FAILURE;
  }

  timeout->mWhen = PR_IntervalNow() +
                     PR_MillisecondsToInterval((PRUint32)interval);
  PRIntervalTime delta = PR_MillisecondsToInterval((PRUint32)interval);

  if (!IsFrozen()) {
    // If we're not currently frozen, then we set timeout->mWhen to be the
    // actual firing time of the timer (i.e., now + delta). We also actually
    // create a timer and fire it off.

    timeout->mWhen = PR_IntervalNow() + delta;

    timeout->mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
    if (NS_FAILED(rv)) {
@@ -5891,6 +5898,17 @@ nsGlobalWindow::SetTimeoutOrInterval(PRBool aIsInterval, PRInt32 *aReturn)
      return rv;
    }

    // The timeout is now also held in the timer's closure.
    timeout->AddRef();
  } else {
    // If we are frozen, however, then we instead simply set timeout->mWhen to
    // be the "time remaining" in the timeout (i.e., the interval itself). We
    // 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;
  NS_ADDREF(timeout->mWindow);

@@ -5912,9 +5930,14 @@ nsGlobalWindow::SetTimeoutOrInterval(PRBool aIsInterval, PRInt32 *aReturn)
  }

  InsertTimeoutIntoList(mTimeoutInsertionPoint, timeout);

  timeout->mPublicId = ++mTimeoutPublicIdCounter;
  *aReturn = timeout->mPublicId;

  // Our hold on the timeout is expiring. Note that this should not actually
  // free the timeout (since the list should have taken ownership as well).
  timeout->Release(scx);

  return NS_OK;
}

@@ -5923,6 +5946,7 @@ void
nsGlobalWindow::RunTimeout(nsTimeout *aTimeout)
{
  NS_ASSERTION(IsInnerWindow(), "Timeout running on outer window!");
  NS_ASSERTION(!IsFrozen(), "Timeout running on a window in the bfcache!");

  // Make sure that the script context doesn't go away as a result of
  // running timeouts
@@ -6234,7 +6258,6 @@ nsTimeout::Release(nsIScriptContext *aContext)
      // window having a context. It would be good to remedy this
      // workable but clumsy situation someday.

      NS_WARNING("nsTimeout::Release() proceeding without context.");
      nsCOMPtr<nsIJSRuntimeService> rtsvc =
        do_GetService("@mozilla.org/js/xpc/RuntimeService;1");

@@ -6746,7 +6769,11 @@ nsGlobalWindow::SuspendTimeouts()
      t->mTimer = nsnull;
    }

    // We don't Release() the timeout because we still need it.
    // Drop the reference that the timer's closure had on this timeout, we'll
    // add it back in ResumeTimeouts. Note that it shouldn't matter that we're
    // passing null for the context, since this shouldn't actually release this
    // timeout.
    t->Release(nsnull);
  }

  // Suspend our children as well.
@@ -6793,7 +6820,13 @@ nsGlobalWindow::ResumeTimeouts()

    rv = t->mTimer->InitWithFuncCallback(TimerCallback, t, delay,
                                         nsITimer::TYPE_ONE_SHOT);
    NS_ENSURE_SUCCESS(rv, rv);
    if (NS_FAILED(rv)) {
      t->mTimer = nsnull;
      return rv;
    }

    // Add a reference for the new timer's closure.
    t->AddRef();
  }

  // Resume our children as well.